installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / shared / WindowsVersion.cc
1 // Copyright (c) 2016 Ryan Prichard
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 // IN THE SOFTWARE.
20
21 #include "WindowsVersion.h"
22
23 #include <windows.h>
24 #include <stdint.h>
25
26 #include <memory>
27 #include <string>
28 #include <tuple>
29
30 #include "DebugClient.h"
31 #include "OsModule.h"
32 #include "StringBuilder.h"
33 #include "StringUtil.h"
34 #include "WinptyAssert.h"
35 #include "WinptyException.h"
36
37 namespace {
38
39 typedef std::tuple<DWORD, DWORD> Version;
40
41 // This function can only return a version up to 6.2 unless the executable is
42 // manifested for a newer version of Windows.  See the MSDN documentation for
43 // GetVersionEx.
44 OSVERSIONINFOEX getWindowsVersionInfo() {
45     // Allow use of deprecated functions (i.e. GetVersionEx).  We need to use
46     // GetVersionEx for the old MinGW toolchain and with MSVC when it targets XP.
47     // Having two code paths makes code harder to test, and it's not obvious how
48     // to detect the presence of a new enough SDK.  (Including ntverp.h and
49     // examining VER_PRODUCTBUILD apparently works, but even then, MinGW-w64 and
50     // MSVC seem to use different version numbers.)
51 #ifdef _MSC_VER
52 #pragma warning(push)
53 #pragma warning(disable:4996)
54 #endif
55     OSVERSIONINFOEX info = {};
56     info.dwOSVersionInfoSize = sizeof(info);
57     const auto success = GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
58     ASSERT(success && "GetVersionEx failed");
59     return info;
60 #ifdef _MSC_VER
61 #pragma warning(pop)
62 #endif
63 }
64
65 Version getWindowsVersion() {
66     const auto info = getWindowsVersionInfo();
67     return Version(info.dwMajorVersion, info.dwMinorVersion);
68 }
69
70 struct ModuleNotFound : WinptyException {
71     virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
72         return L"ModuleNotFound";
73     }
74 };
75
76 // Throws WinptyException on error.
77 std::wstring getSystemDirectory() {
78     wchar_t systemDirectory[MAX_PATH];
79     const UINT size = GetSystemDirectoryW(systemDirectory, MAX_PATH);
80     if (size == 0) {
81         throwWindowsError(L"GetSystemDirectory failed");
82     } else if (size >= MAX_PATH) {
83         throwWinptyException(
84             L"GetSystemDirectory: path is longer than MAX_PATH");
85     }
86     return systemDirectory;
87 }
88
89 #define GET_VERSION_DLL_API(name) \
90     const auto p ## name =                                  \
91         reinterpret_cast<decltype(name)*>(                  \
92             versionDll.proc(#name));                        \
93     if (p ## name == nullptr) {                             \
94         throwWinptyException(L ## #name L" is missing");    \
95     }
96
97 // Throws WinptyException on error.
98 VS_FIXEDFILEINFO getFixedFileInfo(const std::wstring &path) {
99     // version.dll is not a conventional KnownDll, so if we link to it, there's
100     // a danger of accidentally loading a malicious DLL.  In a more typical
101     // application, perhaps we'd guard against this security issue by
102     // controlling which directories this code runs in (e.g. *not* the
103     // "Downloads" directory), but that's harder for the winpty library.
104     OsModule versionDll(
105         (getSystemDirectory() + L"\\version.dll").c_str(),
106         OsModule::LoadErrorBehavior::Throw);
107     GET_VERSION_DLL_API(GetFileVersionInfoSizeW);
108     GET_VERSION_DLL_API(GetFileVersionInfoW);
109     GET_VERSION_DLL_API(VerQueryValueW);
110     DWORD size = pGetFileVersionInfoSizeW(path.c_str(), nullptr);
111     if (!size) {
112         // I see ERROR_FILE_NOT_FOUND on Win7 and
113         // ERROR_RESOURCE_DATA_NOT_FOUND on WinXP.
114         if (GetLastError() == ERROR_FILE_NOT_FOUND ||
115                 GetLastError() == ERROR_RESOURCE_DATA_NOT_FOUND) {
116             throw ModuleNotFound();
117         } else {
118             throwWindowsError(
119                 (L"GetFileVersionInfoSizeW failed on " + path).c_str());
120         }
121     }
122     std::unique_ptr<char[]> versionBuffer(new char[size]);
123     if (!pGetFileVersionInfoW(path.c_str(), 0, size, versionBuffer.get())) {
124         throwWindowsError((L"GetFileVersionInfoW failed on " + path).c_str());
125     }
126     VS_FIXEDFILEINFO *versionInfo = nullptr;
127     UINT versionInfoSize = 0;
128     if (!pVerQueryValueW(
129                 versionBuffer.get(), L"\\",
130                 reinterpret_cast<void**>(&versionInfo), &versionInfoSize) ||
131             versionInfo == nullptr ||
132             versionInfoSize != sizeof(VS_FIXEDFILEINFO) ||
133             versionInfo->dwSignature != 0xFEEF04BD) {
134         throwWinptyException((L"VerQueryValueW failed on " + path).c_str());
135     }
136     return *versionInfo;
137 }
138
139 uint64_t productVersionFromInfo(const VS_FIXEDFILEINFO &info) {
140     return (static_cast<uint64_t>(info.dwProductVersionMS) << 32) |
141            (static_cast<uint64_t>(info.dwProductVersionLS));
142 }
143
144 uint64_t fileVersionFromInfo(const VS_FIXEDFILEINFO &info) {
145     return (static_cast<uint64_t>(info.dwFileVersionMS) << 32) |
146            (static_cast<uint64_t>(info.dwFileVersionLS));
147 }
148
149 std::string versionToString(uint64_t version) {
150     StringBuilder b(32);
151     b << ((uint16_t)(version >> 48));
152     b << '.';
153     b << ((uint16_t)(version >> 32));
154     b << '.';
155     b << ((uint16_t)(version >> 16));
156     b << '.';
157     b << ((uint16_t)(version >> 0));
158     return b.str_moved();
159 }
160
161 } // anonymous namespace
162
163 // Returns true for Windows Vista (or Windows Server 2008) or newer.
164 bool isAtLeastWindowsVista() {
165     return getWindowsVersion() >= Version(6, 0);
166 }
167
168 // Returns true for Windows 7 (or Windows Server 2008 R2) or newer.
169 bool isAtLeastWindows7() {
170     return getWindowsVersion() >= Version(6, 1);
171 }
172
173 // Returns true for Windows 8 (or Windows Server 2012) or newer.
174 bool isAtLeastWindows8() {
175     return getWindowsVersion() >= Version(6, 2);
176 }
177
178 #define WINPTY_IA32     1
179 #define WINPTY_X64      2
180
181 #if defined(_M_IX86) || defined(__i386__)
182 #define WINPTY_ARCH WINPTY_IA32
183 #elif defined(_M_X64) || defined(__x86_64__)
184 #define WINPTY_ARCH WINPTY_X64
185 #endif
186
187 typedef BOOL WINAPI IsWow64Process_t(HANDLE hProcess, PBOOL Wow64Process);
188
189 void dumpWindowsVersion() {
190     if (!isTracingEnabled()) {
191         return;
192     }
193     const auto info = getWindowsVersionInfo();
194     StringBuilder b;
195     b << info.dwMajorVersion << '.' << info.dwMinorVersion
196       << '.' << info.dwBuildNumber << ' '
197       << "SP" << info.wServicePackMajor << '.' << info.wServicePackMinor
198       << ' ';
199     switch (info.wProductType) {
200         case VER_NT_WORKSTATION:        b << "Client"; break;
201         case VER_NT_DOMAIN_CONTROLLER:  b << "DomainController"; break;
202         case VER_NT_SERVER:             b << "Server"; break;
203         default:
204             b << "product=" << info.wProductType; break;
205     }
206     b << ' ';
207 #if WINPTY_ARCH == WINPTY_IA32
208     b << "IA32";
209     OsModule kernel32(L"kernel32.dll");
210     IsWow64Process_t *pIsWow64Process =
211         reinterpret_cast<IsWow64Process_t*>(
212             kernel32.proc("IsWow64Process"));
213     if (pIsWow64Process != nullptr) {
214         BOOL result = false;
215         const BOOL success = pIsWow64Process(GetCurrentProcess(), &result);
216         if (!success) {
217             b << " WOW64:error";
218         } else if (success && result) {
219             b << " WOW64";
220         }
221     } else {
222         b << " WOW64:missingapi";
223     }
224 #elif WINPTY_ARCH == WINPTY_X64
225     b << "X64";
226 #endif
227     const auto dllVersion = [](const wchar_t *dllPath) -> std::string {
228         try {
229             const auto info = getFixedFileInfo(dllPath);
230             StringBuilder fb(64);
231             fb << utf8FromWide(dllPath) << ':';
232             fb << "F:" << versionToString(fileVersionFromInfo(info)) << '/'
233                << "P:" << versionToString(productVersionFromInfo(info));
234             return fb.str_moved();
235         } catch (const ModuleNotFound&) {
236             return utf8FromWide(dllPath) + ":none";
237         } catch (const WinptyException &e) {
238             trace("Error getting %s version: %s",
239                 utf8FromWide(dllPath).c_str(), utf8FromWide(e.what()).c_str());
240             return utf8FromWide(dllPath) + ":error";
241         }
242     };
243     b << ' ' << dllVersion(L"kernel32.dll");
244     // ConEmu provides a DLL that hooks many Windows APIs, especially console
245     // APIs.  Its existence and version number could be useful in debugging.
246 #if WINPTY_ARCH == WINPTY_IA32
247     b << ' ' << dllVersion(L"ConEmuHk.dll");
248 #elif WINPTY_ARCH == WINPTY_X64
249     b << ' ' << dllVersion(L"ConEmuHk64.dll");
250 #endif
251     trace("Windows version: %s", b.c_str());
252 }