xref: /curl/lib/version_win32.c (revision bcec0840)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #if defined(_WIN32)
28 
29 #include <curl/curl.h>
30 #include "version_win32.h"
31 #include "warnless.h"
32 
33 /* The last 2 #include files should be in this order */
34 #ifdef BUILDING_LIBCURL
35 #include "curl_memory.h"
36 #endif
37 #include "memdebug.h"
38 
39 /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
40    and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
41 struct OUR_OSVERSIONINFOEXW {
42   ULONG  dwOSVersionInfoSize;
43   ULONG  dwMajorVersion;
44   ULONG  dwMinorVersion;
45   ULONG  dwBuildNumber;
46   ULONG  dwPlatformId;
47   WCHAR  szCSDVersion[128];
48   USHORT wServicePackMajor;
49   USHORT wServicePackMinor;
50   USHORT wSuiteMask;
51   UCHAR  wProductType;
52   UCHAR  wReserved;
53 };
54 
55 /*
56  * curlx_verify_windows_version()
57  *
58  * This is used to verify if we are running on a specific Windows version.
59  *
60  * Parameters:
61  *
62  * majorVersion [in] - The major version number.
63  * minorVersion [in] - The minor version number.
64  * buildVersion [in] - The build version number. If 0, this parameter is
65  *                     ignored.
66  * platform     [in] - The optional platform identifier.
67  * condition    [in] - The test condition used to specifier whether we are
68  *                     checking a version less than, equal to or greater than
69  *                     what is specified in the major and minor version
70  *                     numbers.
71  *
72  * Returns TRUE if matched; otherwise FALSE.
73  */
curlx_verify_windows_version(const unsigned int majorVersion,const unsigned int minorVersion,const unsigned int buildVersion,const PlatformIdentifier platform,const VersionCondition condition)74 bool curlx_verify_windows_version(const unsigned int majorVersion,
75                                   const unsigned int minorVersion,
76                                   const unsigned int buildVersion,
77                                   const PlatformIdentifier platform,
78                                   const VersionCondition condition)
79 {
80   bool matched = FALSE;
81 
82 #if defined(CURL_WINDOWS_UWP)
83   /* We have no way to determine the Windows version from Windows apps,
84      so let's assume we are running on the target Windows version. */
85   const WORD fullVersion = MAKEWORD(minorVersion, majorVersion);
86   const WORD targetVersion = (WORD)_WIN32_WINNT;
87 
88   (void)buildVersion;
89 
90   switch(condition) {
91   case VERSION_LESS_THAN:
92     matched = targetVersion < fullVersion;
93     break;
94 
95   case VERSION_LESS_THAN_EQUAL:
96     matched = targetVersion <= fullVersion;
97     break;
98 
99   case VERSION_EQUAL:
100     matched = targetVersion == fullVersion;
101     break;
102 
103   case VERSION_GREATER_THAN_EQUAL:
104     matched = targetVersion >= fullVersion;
105     break;
106 
107   case VERSION_GREATER_THAN:
108     matched = targetVersion > fullVersion;
109     break;
110   }
111 
112   if(matched && (platform == PLATFORM_WINDOWS)) {
113     /* we are always running on PLATFORM_WINNT */
114     matched = FALSE;
115   }
116 #elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
117     (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
118   OSVERSIONINFO osver;
119 
120   memset(&osver, 0, sizeof(osver));
121   osver.dwOSVersionInfoSize = sizeof(osver);
122 
123   /* Find out Windows version */
124   if(GetVersionEx(&osver)) {
125     /* Verify the Operating System version number */
126     switch(condition) {
127     case VERSION_LESS_THAN:
128       if(osver.dwMajorVersion < majorVersion ||
129         (osver.dwMajorVersion == majorVersion &&
130          osver.dwMinorVersion < minorVersion) ||
131         (buildVersion != 0 &&
132          (osver.dwMajorVersion == majorVersion &&
133           osver.dwMinorVersion == minorVersion &&
134           osver.dwBuildNumber < buildVersion)))
135         matched = TRUE;
136       break;
137 
138     case VERSION_LESS_THAN_EQUAL:
139       if(osver.dwMajorVersion < majorVersion ||
140         (osver.dwMajorVersion == majorVersion &&
141          osver.dwMinorVersion < minorVersion) ||
142         (osver.dwMajorVersion == majorVersion &&
143          osver.dwMinorVersion == minorVersion &&
144          (buildVersion == 0 ||
145           osver.dwBuildNumber <= buildVersion)))
146         matched = TRUE;
147       break;
148 
149     case VERSION_EQUAL:
150       if(osver.dwMajorVersion == majorVersion &&
151          osver.dwMinorVersion == minorVersion &&
152         (buildVersion == 0 ||
153          osver.dwBuildNumber == buildVersion))
154         matched = TRUE;
155       break;
156 
157     case VERSION_GREATER_THAN_EQUAL:
158       if(osver.dwMajorVersion > majorVersion ||
159         (osver.dwMajorVersion == majorVersion &&
160          osver.dwMinorVersion > minorVersion) ||
161         (osver.dwMajorVersion == majorVersion &&
162          osver.dwMinorVersion == minorVersion &&
163          (buildVersion == 0 ||
164           osver.dwBuildNumber >= buildVersion)))
165         matched = TRUE;
166       break;
167 
168     case VERSION_GREATER_THAN:
169       if(osver.dwMajorVersion > majorVersion ||
170         (osver.dwMajorVersion == majorVersion &&
171          osver.dwMinorVersion > minorVersion) ||
172         (buildVersion != 0 &&
173          (osver.dwMajorVersion == majorVersion &&
174           osver.dwMinorVersion == minorVersion &&
175           osver.dwBuildNumber > buildVersion)))
176         matched = TRUE;
177       break;
178     }
179 
180     /* Verify the platform identifier (if necessary) */
181     if(matched) {
182       switch(platform) {
183       case PLATFORM_WINDOWS:
184         if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
185           matched = FALSE;
186         break;
187 
188       case PLATFORM_WINNT:
189         if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
190           matched = FALSE;
191         break;
192 
193       default: /* like platform == PLATFORM_DONT_CARE */
194         break;
195       }
196     }
197   }
198 #else
199   ULONGLONG cm = 0;
200   struct OUR_OSVERSIONINFOEXW osver;
201   BYTE majorCondition;
202   BYTE minorCondition;
203   BYTE buildCondition;
204   BYTE spMajorCondition;
205   BYTE spMinorCondition;
206   DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION |
207                      VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
208 
209   typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN)
210     (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG);
211   static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
212   static bool onetime = TRUE; /* safe because first call is during init */
213 
214   if(onetime) {
215     pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN,
216       (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo")));
217     onetime = FALSE;
218   }
219 
220   switch(condition) {
221   case VERSION_LESS_THAN:
222     majorCondition = VER_LESS;
223     minorCondition = VER_LESS;
224     buildCondition = VER_LESS;
225     spMajorCondition = VER_LESS_EQUAL;
226     spMinorCondition = VER_LESS_EQUAL;
227     break;
228 
229   case VERSION_LESS_THAN_EQUAL:
230     majorCondition = VER_LESS_EQUAL;
231     minorCondition = VER_LESS_EQUAL;
232     buildCondition = VER_LESS_EQUAL;
233     spMajorCondition = VER_LESS_EQUAL;
234     spMinorCondition = VER_LESS_EQUAL;
235     break;
236 
237   case VERSION_EQUAL:
238     majorCondition = VER_EQUAL;
239     minorCondition = VER_EQUAL;
240     buildCondition = VER_EQUAL;
241     spMajorCondition = VER_GREATER_EQUAL;
242     spMinorCondition = VER_GREATER_EQUAL;
243     break;
244 
245   case VERSION_GREATER_THAN_EQUAL:
246     majorCondition = VER_GREATER_EQUAL;
247     minorCondition = VER_GREATER_EQUAL;
248     buildCondition = VER_GREATER_EQUAL;
249     spMajorCondition = VER_GREATER_EQUAL;
250     spMinorCondition = VER_GREATER_EQUAL;
251     break;
252 
253   case VERSION_GREATER_THAN:
254     majorCondition = VER_GREATER;
255     minorCondition = VER_GREATER;
256     buildCondition = VER_GREATER;
257     spMajorCondition = VER_GREATER_EQUAL;
258     spMinorCondition = VER_GREATER_EQUAL;
259     break;
260 
261   default:
262     return FALSE;
263   }
264 
265   memset(&osver, 0, sizeof(osver));
266   osver.dwOSVersionInfoSize = sizeof(osver);
267   osver.dwMajorVersion = majorVersion;
268   osver.dwMinorVersion = minorVersion;
269   osver.dwBuildNumber = buildVersion;
270   if(platform == PLATFORM_WINDOWS)
271     osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
272   else if(platform == PLATFORM_WINNT)
273     osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
274 
275   cm = VerSetConditionMask(cm, VER_MAJORVERSION, majorCondition);
276   cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition);
277   cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition);
278   cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition);
279 
280   if(platform != PLATFORM_DONT_CARE) {
281     cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL);
282     dwTypeMask |= VER_PLATFORMID;
283   }
284 
285   /* Later versions of Windows have version functions that may not return the
286      real version of Windows unless the application is so manifested. We prefer
287      the real version always, so we use the Rtl variant of the function when
288      possible. Note though the function signatures have underlying fundamental
289      types that are the same, the return values are different. */
290   if(pRtlVerifyVersionInfo)
291     matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
292   else
293     matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm);
294 
295   /* Compare the build number separately. VerifyVersionInfo normally compares
296      major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not
297      do the same for build (eg 1.9 build 222 is not less than 2.0 build 111).
298      Build comparison is only needed when build numbers are equal (eg 1.9 is
299      always less than 2.0 so build comparison is not needed). */
300   if(matched && buildVersion &&
301      (condition == VERSION_EQUAL ||
302       ((condition == VERSION_GREATER_THAN_EQUAL ||
303         condition == VERSION_LESS_THAN_EQUAL) &&
304         curlx_verify_windows_version(majorVersion, minorVersion, 0,
305                                      platform, VERSION_EQUAL)))) {
306 
307     cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition);
308     dwTypeMask = VER_BUILDNUMBER;
309     if(pRtlVerifyVersionInfo)
310       matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
311     else
312       matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver,
313                                       dwTypeMask, cm);
314   }
315 
316 #endif
317 
318   return matched;
319 }
320 
321 #endif /* _WIN32 */
322