[filesystem.c] Add a cast to aviod a warning with VisualStudio 2017.
[metze/wireshark/wip.git] / wsutil / os_version_info.c
1 /* os_version_info.c
2  * Routines to report operating system version information
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26 #include <errno.h>
27
28 #ifdef HAVE_SYS_UTSNAME_H
29 #include <sys/utsname.h>
30 #endif
31
32 #ifdef HAVE_MACOS_FRAMEWORKS
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <wsutil/cfutils.h>
35 #endif
36
37 #include <glib.h>
38
39 #include <wsutil/unicode-utils.h>
40
41 #include <wsutil/os_version_info.h>
42
43 #ifdef _WIN32
44 typedef void (WINAPI *nativesi_func_ptr)(LPSYSTEM_INFO);
45 #endif
46
47 /*
48  * Handles the rather elaborate process of getting OS version information
49  * from macOS (we want the macOS version, not the Darwin version, the latter
50  * being easy to get with uname()).
51  */
52 #ifdef HAVE_MACOS_FRAMEWORKS
53
54 /*
55  * Fetch a string, as a UTF-8 C string, from a dictionary, given a key.
56  */
57 static char *
58 get_string_from_dictionary(CFPropertyListRef dict, CFStringRef key)
59 {
60         CFStringRef cfstring;
61
62         cfstring = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)dict,
63             (const void *)key);
64         if (cfstring == NULL)
65                 return NULL;
66         if (CFGetTypeID(cfstring) != CFStringGetTypeID()) {
67                 /* It isn't a string.  Punt. */
68                 return NULL;
69         }
70         return CFString_to_C_string(cfstring);
71 }
72
73 /*
74  * Get the macOS version information, and append it to the GString.
75  * Return TRUE if we succeed, FALSE if we fail.
76  *
77  * XXX - this gives the OS name as "Mac OS X" even if Apple called/calls
78  * it "OS X" or "macOS".
79  */
80 static gboolean
81 get_macos_version_info(GString *str)
82 {
83         static const UInt8 server_version_plist_path[] =
84             "/System/Library/CoreServices/ServerVersion.plist";
85         static const UInt8 system_version_plist_path[] =
86             "/System/Library/CoreServices/SystemVersion.plist";
87         CFURLRef version_plist_file_url;
88         CFReadStreamRef version_plist_stream;
89         CFDictionaryRef version_dict;
90         char *string;
91
92         /*
93          * On macOS, report the macOS version number as the OS, and put
94          * the Darwin information in parentheses.
95          *
96          * Alas, Gestalt() is deprecated in Mountain Lion, so the build
97          * fails if you treat deprecation warnings as fatal.  I don't
98          * know of any replacement API, so we fall back on reading
99          * /System/Library/CoreServices/ServerVersion.plist if it
100          * exists, otherwise /System/Library/CoreServices/SystemVersion.plist,
101          * and using ProductUserVisibleVersion.  We also get the build
102          * version from ProductBuildVersion and the product name from
103          * ProductName.
104          */
105         version_plist_file_url = CFURLCreateFromFileSystemRepresentation(NULL,
106             server_version_plist_path, sizeof server_version_plist_path - 1,
107             false);
108         if (version_plist_file_url == NULL)
109                 return FALSE;
110         version_plist_stream = CFReadStreamCreateWithFile(NULL,
111             version_plist_file_url);
112         CFRelease(version_plist_file_url);
113         if (version_plist_stream == NULL)
114                 return FALSE;
115         if (!CFReadStreamOpen(version_plist_stream)) {
116                 CFRelease(version_plist_stream);
117
118                 /*
119                  * Try SystemVersion.plist.
120                  */
121                 version_plist_file_url = CFURLCreateFromFileSystemRepresentation(NULL,
122                     system_version_plist_path, sizeof system_version_plist_path - 1,
123                     false);
124                 if (version_plist_file_url == NULL)
125                         return FALSE;
126                 version_plist_stream = CFReadStreamCreateWithFile(NULL,
127                     version_plist_file_url);
128                 CFRelease(version_plist_file_url);
129                 if (version_plist_stream == NULL)
130                         return FALSE;
131                 if (!CFReadStreamOpen(version_plist_stream)) {
132                         CFRelease(version_plist_stream);
133                         return FALSE;
134                 }
135         }
136 #ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
137         version_dict = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL,
138             version_plist_stream, 0, kCFPropertyListImmutable,
139             NULL, NULL);
140 #else
141         version_dict = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL,
142             version_plist_stream, 0, kCFPropertyListImmutable,
143             NULL, NULL);
144 #endif
145         if (version_dict == NULL) {
146                 CFRelease(version_plist_stream);
147                 return FALSE;
148         }
149         if (CFGetTypeID(version_dict) != CFDictionaryGetTypeID()) {
150                 /* This is *supposed* to be a dictionary.  Punt. */
151                 CFRelease(version_dict);
152                 CFReadStreamClose(version_plist_stream);
153                 CFRelease(version_plist_stream);
154                 return FALSE;
155         }
156         /* Get the product name string. */
157         string = get_string_from_dictionary(version_dict,
158             CFSTR("ProductName"));
159         if (string == NULL) {
160                 CFRelease(version_dict);
161                 CFReadStreamClose(version_plist_stream);
162                 CFRelease(version_plist_stream);
163                 return FALSE;
164         }
165         g_string_append_printf(str, "%s", string);
166         g_free(string);
167
168         /* Get the OS version string. */
169         string = get_string_from_dictionary(version_dict,
170             CFSTR("ProductUserVisibleVersion"));
171         if (string == NULL) {
172                 CFRelease(version_dict);
173                 CFReadStreamClose(version_plist_stream);
174                 CFRelease(version_plist_stream);
175                 return FALSE;
176         }
177         g_string_append_printf(str, " %s", string);
178         g_free(string);
179
180         /* Get the build string */
181         string = get_string_from_dictionary(version_dict,
182             CFSTR("ProductBuildVersion"));
183         if (string == NULL) {
184                 CFRelease(version_dict);
185                 CFReadStreamClose(version_plist_stream);
186                 CFRelease(version_plist_stream);
187                 return FALSE;
188         }
189         g_string_append_printf(str, ", build %s", string);
190         g_free(string);
191         CFRelease(version_dict);
192         CFReadStreamClose(version_plist_stream);
193         CFRelease(version_plist_stream);
194         return TRUE;
195 }
196 #endif
197
198 /*
199  * Get the OS version, and append it to the GString
200  */
201 void
202 get_os_version_info(GString *str)
203 {
204 #if defined(_WIN32)
205         HMODULE kernel_dll_handle;
206         OSVERSIONINFOEX info;
207         SYSTEM_INFO system_info;
208         nativesi_func_ptr nativesi_func;
209 #elif defined(HAVE_SYS_UTSNAME_H)
210         struct utsname name;
211 #endif
212
213 #if defined(_WIN32)
214         /*
215          * See
216          *
217          *      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getting_the_system_version.asp
218          *
219          * for more than you ever wanted to know about determining the
220          * flavor of Windows on which you're running.  Implementing more
221          * of that is left as an exercise to the reader - who should
222          * check any copyright information about code samples on MSDN
223          * before cutting and pasting into Wireshark.
224          *
225          * They should also note that you need an OSVERSIONINFOEX structure
226          * to get some of that information, and that not only is that
227          * structure not supported on older versions of Windows, you might
228          * not even be able to compile code that *uses* that structure with
229          * older versions of the SDK.
230          */
231
232         memset(&info, '\0', sizeof info);
233         info.dwOSVersionInfoSize = sizeof info;
234         if (!GetVersionEx((OSVERSIONINFO *)&info)) {
235                 /*
236                  * XXX - get the failure reason.
237                  */
238                 g_string_append(str, "unknown Windows version");
239                 return;
240         }
241
242         memset(&system_info, '\0', sizeof system_info);
243         /* Look for and use the GetNativeSystemInfo() function if available to get the correct processor
244          * architecture even when running 32-bit Wireshark in WOW64 (x86 emulation on 64-bit Windows) */
245         kernel_dll_handle = GetModuleHandle(_T("kernel32.dll"));
246         if (kernel_dll_handle == NULL) {
247                 /*
248                  * XXX - get the failure reason.
249                  */
250                 g_string_append(str, "unknown Windows version");
251                 return;
252         }
253         nativesi_func = (nativesi_func_ptr)GetProcAddress(kernel_dll_handle, "GetNativeSystemInfo");
254         if(nativesi_func)
255                 nativesi_func(&system_info);
256         else
257                 GetSystemInfo(&system_info);
258
259         switch (info.dwPlatformId) {
260
261         case VER_PLATFORM_WIN32s:
262                 /* Shyeah, right. */
263                 g_string_append_printf(str, "Windows 3.1 with Win32s");
264                 break;
265
266         case VER_PLATFORM_WIN32_WINDOWS:
267                 /* Windows OT */
268                 switch (info.dwMajorVersion) {
269
270                 case 4:
271                         /* 3 cheers for Microsoft marketing! */
272                         switch (info.dwMinorVersion) {
273
274                         case 0:
275                                 g_string_append_printf(str, "Windows 95");
276                                 break;
277
278                         case 10:
279                                 g_string_append_printf(str, "Windows 98");
280                                 break;
281
282                         case 90:
283                                 g_string_append_printf(str, "Windows Me");
284                                 break;
285
286                         default:
287                                 g_string_append_printf(str, "Windows OT, unknown version %lu.%lu",
288                                     info.dwMajorVersion, info.dwMinorVersion);
289                                 break;
290                         }
291                         break;
292
293                 default:
294                         g_string_append_printf(str, "Windows OT, unknown version %lu.%lu",
295                             info.dwMajorVersion, info.dwMinorVersion);
296                         break;
297                 }
298                 break;
299
300         case VER_PLATFORM_WIN32_NT:
301                 /* Windows NT */
302                 switch (info.dwMajorVersion) {
303
304                 case 3:
305                 case 4:
306                         g_string_append_printf(str, "Windows NT %lu.%lu",
307                             info.dwMajorVersion, info.dwMinorVersion);
308                         break;
309
310                 case 5:
311                         /* 3 cheers for Microsoft marketing! */
312                         switch (info.dwMinorVersion) {
313
314                         case 0:
315                                 g_string_append_printf(str, "Windows 2000");
316                                 break;
317
318                         case 1:
319                                 g_string_append_printf(str, "Windows XP");
320                                 break;
321
322                         case 2:
323                                 if ((info.wProductType == VER_NT_WORKSTATION) &&
324                                     (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) {
325                                         g_string_append_printf(str, "Windows XP Professional x64 Edition");
326                                 } else {
327                                         g_string_append_printf(str, "Windows Server 2003");
328                                         if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
329                                                 g_string_append_printf(str, " x64 Edition");
330                                 }
331                                 break;
332
333                         default:
334                                 g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
335                                                        info.dwMajorVersion, info.dwMinorVersion);
336                                 break;
337                         }
338                         break;
339
340                 case 6: {
341                         gboolean is_nt_workstation;
342
343                         if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
344                                 g_string_append(str, "64-bit ");
345                         else if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
346                                 g_string_append(str, "32-bit ");
347 #ifndef VER_NT_WORKSTATION
348 #define VER_NT_WORKSTATION 0x01
349                         is_nt_workstation = ((info.wReserved[1] & 0xff) == VER_NT_WORKSTATION);
350 #else
351                         is_nt_workstation = (info.wProductType == VER_NT_WORKSTATION);
352 #endif
353                         switch (info.dwMinorVersion) {
354                         case 0:
355                                 g_string_append_printf(str, is_nt_workstation ? "Windows Vista" : "Windows Server 2008");
356                                 break;
357                         case 1:
358                                 g_string_append_printf(str, is_nt_workstation ? "Windows 7" : "Windows Server 2008 R2");
359                                 break;
360                         case 2:
361                                 g_string_append_printf(str, is_nt_workstation ? "Windows 8" : "Windows Server 2012");
362                                 break;
363                         case 3:
364                                 g_string_append_printf(str, is_nt_workstation ? "Windows 8.1" : "Windows Server 2012 R2");
365                                 break;
366                         default:
367                                 g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
368                                                        info.dwMajorVersion, info.dwMinorVersion);
369                                 break;
370                         }
371                         break;
372                 }  /* case 6 */
373
374                 case 10: {
375                         gboolean is_nt_workstation;
376
377                         if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
378                                 g_string_append(str, "64-bit ");
379                         else if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
380                                 g_string_append(str, "32-bit ");
381                         is_nt_workstation = (info.wProductType == VER_NT_WORKSTATION);
382                         switch (info.dwMinorVersion) {
383                         case 0:
384                                 g_string_append_printf(str, is_nt_workstation ? "Windows 10" : "Windows Server 2016");
385                                 break;
386                         default:
387                                 g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
388                                                        info.dwMajorVersion, info.dwMinorVersion);
389                                 break;
390                         }
391                         break;
392                 }  /* case 10 */
393
394                 default:
395                         g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
396                             info.dwMajorVersion, info.dwMinorVersion);
397                         break;
398                 } /* info.dwMajorVersion */
399                 break;
400
401         default:
402                 g_string_append_printf(str, "Unknown Windows platform %lu version %lu.%lu",
403                     info.dwPlatformId, info.dwMajorVersion, info.dwMinorVersion);
404                 break;
405         }
406         if (info.szCSDVersion[0] != '\0')
407                 g_string_append_printf(str, " %s", utf_16to8(info.szCSDVersion));
408         g_string_append_printf(str, ", build %lu", info.dwBuildNumber);
409 #elif defined(HAVE_SYS_UTSNAME_H)
410         /*
411          * We have <sys/utsname.h>, so we assume we have "uname()".
412          */
413         if (uname(&name) < 0) {
414                 g_string_append_printf(str, "unknown OS version (uname failed - %s)",
415                     g_strerror(errno));
416                 return;
417         }
418
419         if (strcmp(name.sysname, "AIX") == 0) {
420                 /*
421                  * Yay, IBM!  Thanks for doing something different
422                  * from most of the other UNIXes out there, and
423                  * making "name.version" apparently be the major
424                  * version number and "name.release" be the minor
425                  * version number.
426                  */
427                 g_string_append_printf(str, "%s %s.%s", name.sysname, name.version,
428                     name.release);
429         } else {
430                 /*
431                  * XXX - get "version" on any other platforms?
432                  *
433                  * On Digital/Tru64 UNIX, it's something unknown.
434                  * On Solaris, it's some kind of build information.
435                  * On HP-UX, it appears to be some sort of subrevision
436                  * thing.
437                  * On *BSD and Darwin/macOS, it's a long string giving
438                  * a build date, config file name, etc., etc., etc..
439                  */
440 #ifdef HAVE_MACOS_FRAMEWORKS
441                 /*
442                  * On macOS, report the macOS version number as the OS
443                  * version if we can, and put the Darwin information
444                  * in parentheses.
445                  */
446                 if (get_macos_version_info(str)) {
447                         /* Success - append the Darwin information. */
448                         g_string_append_printf(str, " (%s %s)", name.sysname, name.release);
449                 } else {
450                         /* Failure - just use the Darwin information. */
451                         g_string_append_printf(str, "%s %s", name.sysname, name.release);
452                 }
453 #else /* HAVE_MACOS_FRAMEWORKS */
454                 /*
455                  * XXX - on Linux, are there any APIs to get the distribution
456                  * name and version number?  I think some distributions have
457                  * that.
458                  *
459                  * At least on Linux Standard Base-compliant distributions,
460                  * there's an "lsb_release" command.  However:
461                  *
462                  *      http://forums.fedoraforum.org/showthread.php?t=220885
463                  *
464                  * seems to suggest that if you don't have the redhat-lsb
465                  * package installed, you don't have lsb_release, and that
466                  * /etc/fedora-release has the release information on
467                  * Fedora.
468                  *
469                  *      http://linux.die.net/man/1/lsb_release
470                  *
471                  * suggests that there's an /etc/distrib-release file, but
472                  * it doesn't indicate whether "distrib" is literally
473                  * "distrib" or is the name for the distribution, and
474                  * also speaks of an /etc/debian_version file.
475                  *
476                  * "lsb_release" apparently parses /etc/lsb-release, which
477                  * has shell-style assignments, assigning to, among other
478                  * values, DISTRIB_ID (distributor/distribution name),
479                  * DISTRIB_RELEASE (release number of the distribution),
480                  * DISTRIB_DESCRIPTION (*might* be name followed by version,
481                  * but the manpage for lsb_release seems to indicate that's
482                  * not guaranteed), and DISTRIB_CODENAME (code name, e.g.
483                  * "licentious" for the Ubuntu Licentious Lemur release).
484                  * the lsb_release man page also speaks of the distrib-release
485                  * file, but Debian doesn't have one, and Ubuntu 7's
486                  * lsb_release command doesn't look for one.
487                  *
488                  * I've seen references to /etc/redhat-release as well.
489                  *
490                  * At least on my Ubuntu 7 system, /etc/debian_version
491                  * doesn't contain anything interesting (just some Debian
492                  * codenames).
493                  *
494                  * See also
495                  *
496                  *      http://bugs.python.org/issue1322
497                  *
498                  *      http://www.novell.com/coolsolutions/feature/11251.html
499                  *
500                  *      http://linuxmafia.com/faq/Admin/release-files.html
501                  *
502                  * and the Lib/Platform.py file in recent Python 2.x
503                  * releases.
504                  */
505                 g_string_append_printf(str, "%s %s", name.sysname, name.release);
506 #endif /* HAVE_MACOS_FRAMEWORKS */
507         }
508 #else
509         g_string_append(str, "an unknown OS");
510 #endif
511 }
512
513 #if defined(_WIN32)
514 /*
515  * Get the Windows major OS version.
516  *
517  * XXX - Should this return the minor version as well, e.g. 0x00050002?
518  *
519  * XXX - I think Microsoft have now stuck it at 6 forever, so it really
520  * distinguishes, on the versions of Windows we currently support and
521  * future Windows versions between Windows 2000/XP (major version 5) and
522  * everything after it (major version 6).
523  */
524 guint32
525 get_windows_major_version(void)
526 {
527         OSVERSIONINFO info;
528
529         info.dwOSVersionInfoSize = sizeof info;
530         if (GetVersionEx(&info)) {
531                 return info.dwMajorVersion;
532         }
533         return 0;
534 }
535 #endif
536
537 /*
538  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
539  *
540  * Local variables:
541  * c-basic-offset: 8
542  * tab-width: 8
543  * indent-tabs-mode: t
544  * End:
545  *
546  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
547  * :indentSize=8:tabSize=8:noTabs=false:
548  */