master
h 282 lines 10.6 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssHttpProxy.h
8
9 Abstract:
10
11 This file contains HTTP proxy related classes and helper functions for proxy queries. Based on implementation in WSA.
12
13 --*/
14
15 #pragma once
16
17 #include <windows.h>
18 #include <winhttp.h>
19
20 #include <wil/resource.h>
21
22 #include <string>
23 #include <vector>
24 #include <optional>
25
26 #include "WslCoreConfig.h"
27 #include "WslCoreMessageQueue.h"
28
29 static constexpr auto c_winhttpModuleName = L"Winhttp.dll";
30 static constexpr auto c_httpProxyLower = "http_proxy";
31 static constexpr auto c_httpProxyUpper = "HTTP_PROXY";
32 static constexpr auto c_httpsProxyLower = "https_proxy";
33 static constexpr auto c_httpsProxyUpper = "HTTPS_PROXY";
34 static constexpr auto c_proxyBypassLower = "no_proxy";
35 static constexpr auto c_proxyBypassUpper = "NO_PROXY";
36 static constexpr auto c_pacProxy = "WSL_PAC_URL";
37 static constexpr auto c_loopback = L"loopback";
38 static constexpr auto c_localhost = L"localhost";
39
40 void FreeHttpProxySettings(WINHTTP_PROXY_SETTINGS_EX* proxySettings) noexcept;
41 using unique_winhttp_proxy_settings = wil::unique_struct<WINHTTP_PROXY_SETTINGS_EX, decltype(&FreeHttpProxySettings), FreeHttpProxySettings>;
42
43 // Function declarations used for dynamically loading in Winhttp APIs.
44 WINHTTPAPI
45 DWORD
46 WINAPI
47 RegisterProxyChangeNotification(_In_ ULONGLONG ullFlags, _In_ WINHTTP_PROXY_CHANGE_CALLBACK pfnCallback, _In_ PVOID pvContext, _Out_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE* hRegistration);
48
49 WINHTTPAPI
50 DWORD
51 WINAPI
52 UnregisterProxyChangeNotification(_In_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE hRegistration);
53
54 WINHTTPAPI
55 DWORD
56 WINAPI
57 GetProxySettingsEx(
58 _In_ HINTERNET hResolver,
59 _In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType,
60 _In_opt_ PWINHTTP_PROXY_SETTINGS_PARAM pProxySettingsParam,
61 _In_opt_ DWORD_PTR pContext);
62
63 WINHTTPAPI
64 DWORD
65 WINAPI
66 GetProxySettingsResultEx(_In_ HINTERNET hResolver, _Out_ PVOID pProxySettingsEx);
67
68 WINHTTPAPI
69 DWORD
70 WINAPI
71 FreeProxySettingsEx(_In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType, _In_ PVOID pProxySettingsEx);
72
73 enum class UnsupportedProxyReason
74 {
75 Supported,
76 LoopbackNotMirrored,
77 Ipv6NotMirrored,
78 LoopbackV6,
79 UnsupportedError
80 };
81
82 constexpr auto ToString(UnsupportedProxyReason config) noexcept
83 {
84 switch (config)
85 {
86 case UnsupportedProxyReason::Supported:
87 return "Supported";
88 case UnsupportedProxyReason::LoopbackNotMirrored:
89 return "LoopbackNotMirrored";
90 case UnsupportedProxyReason::Ipv6NotMirrored:
91 return "Ipv6NotMirrored";
92 case UnsupportedProxyReason::LoopbackV6:
93 return "LoopbackV6";
94 case UnsupportedProxyReason::UnsupportedError:
95 return "UnsupportedError";
96 default:
97 return "<unknown UnsupportedProxyReason>";
98 }
99 }
100
101 struct HttpProxySettings
102 {
103 HttpProxySettings() = default;
104 HttpProxySettings(const WINHTTP_PROXY_SETTINGS_EX& ProxySettings);
105 HttpProxySettings(const HttpProxySettings&) = default;
106 HttpProxySettings(HttpProxySettings&&) = default;
107 HttpProxySettings& operator=(const HttpProxySettings&) = default;
108 HttpProxySettings& operator=(HttpProxySettings&&) = default;
109
110 std::string PacUrl{};
111 std::string Proxy{};
112 std::string SecureProxy{};
113 std::vector<std::string> ProxyBypasses{};
114 std::string ProxyBypassesComma{};
115 UnsupportedProxyReason UnsupportedProxyDropReason = UnsupportedProxyReason::Supported;
116
117 std::string ToString() const;
118 bool HasSettingsConfigured() const;
119 };
120
121 class HttpProxyStateTracker
122 {
123 enum class QueryState
124 {
125 NoQuery,
126 Pending,
127 PendingAndQueueAdditional
128 };
129
130 public:
131 HttpProxyStateTracker(int ProxyTimeout, HANDLE UserToken, wsl::core::NetworkingMode configuration);
132 ~HttpProxyStateTracker();
133
134 HttpProxyStateTracker(const HttpProxyStateTracker&) = delete;
135 HttpProxyStateTracker(HttpProxyStateTracker&&) = delete;
136 HttpProxyStateTracker& operator=(const HttpProxyStateTracker&) = delete;
137 HttpProxyStateTracker& operator=(HttpProxyStateTracker&&) = delete;
138
139 /// <summary>
140 /// If no proxy queries have completed, wait for timeout for result.
141 /// Otherwise, return the proxy settings.
142 /// </summary>
143 std::optional<HttpProxySettings> WaitForInitialProxySettings();
144
145 /// <summary>
146 /// This needs to be called after the VM is created so actual selected configuration is set.
147 /// </summary>
148 void ConfigureNetworkingMode(wsl::core::NetworkingMode mode) noexcept;
149
150 /// <summary>
151 /// Loads necessary WinHttpProxy APIs into static dynamic functions from DLL if they exist.
152 /// </summary>
153 static HRESULT s_LoadWinHttpProxyMethods() noexcept;
154
155 /// Static dynamic function for loading in necessary WinHttpProxy APIs
156 static std::optional<LxssDynamicFunction<decltype(FreeProxySettingsEx)>> s_WinHttpFreeProxySettingsEx;
157
158 private:
159 /// <summary>
160 /// Invoked via WslCoreMessageQueue. Uses WinHttpProxy APIs to start proxy query.
161 /// </summary>
162 void QueryProxySettingsAsync();
163
164 /// <summary>
165 /// Invoked via WslCoreMessageQueue when a proxy request completes.
166 /// </summary>
167 /// <param name="error"> Error status of request. </param>
168 /// <param name="proxySettings"> The current proxy settings. </param>
169 void RequestCompleted(_In_ DWORD error, _In_ HttpProxySettings&& newProxySettings) noexcept;
170
171 /// <summary>
172 /// Invoked via WslCoreMessageQueue when a proxy request closes.
173 /// </summary>
174 void RequestClosed() noexcept;
175
176 /// <summary>
177 /// Checks if two proxy settings are identical.
178 /// </summary>
179 bool AreProxyStringsIdentical(const HttpProxySettings& newSettings) const;
180
181 /// <summary>
182 /// Memory barrier for reading/writing to m_proxySettings.
183 /// </summary>
184 wil::critical_section m_proxySettingsLock{};
185
186 /// <summary>
187 /// Current http proxy settings. If no queries have completed it is std::nullopt.
188 /// </summary>
189 _Guarded_by_(m_proxySettingsLock) std::optional<HttpProxySettings> m_proxySettings {};
190
191 /// <summary>
192 /// Current network mode. Used to determine some cases when we should send toast notification.
193 /// </summary>
194 _Guarded_by_(m_proxySettingsLock) wsl::core::NetworkingMode m_networkMode = wsl::core::NetworkingMode::Nat;
195
196 /// <summary>
197 /// Indicates if we need to start another query after current one.
198 /// </summary>
199 QueryState m_queryState{QueryState::NoQuery};
200
201 /// <summary>
202 /// Synchronizes request startup and teardown.
203 /// </summary>
204 wil::critical_section m_requestLock{};
205 _Guarded_by_(m_requestLock) bool m_stopping = false;
206
207 /// <summary>
208 /// Used to impersonate user, as it is required for the proxy queries to run as the user; otherwise, the results will be incorrect.
209 /// </summary>
210 wil::unique_handle m_userToken{};
211
212 /// <summary>
213 /// Handle for tracking http proxy setting changes.
214 /// </summary>
215 WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE m_proxyRegistrationHandle{};
216
217 /// <summary>
218 /// Amount of time WSL will wait for proxy settings if no proxy settings have been detected by time we attempt to launch process.
219 /// </summary>
220 const int m_initialQueryTimeout = 1000;
221
222 /// <summary>
223 /// We resolve and store the localized proxy change string for notifications to this object, as we can't resolve it in callback from proxy query.
224 /// </summary>
225 const std::wstring m_localizedProxyChangeString{};
226
227 /// <summary>
228 /// Event that is set when m_proxySettings has a value.
229 /// </summary>
230 wil::slim_event_manual_reset m_initialProxyQueryCompleted{false};
231
232 /// <summary>
233 /// Event that is set when all tracked requests have completed.
234 /// </summary>
235 wil::slim_event_manual_reset m_requestFinished{true};
236
237 // Handles associated with the request
238 _Guarded_by_(m_requestLock) wil::unique_winhttp_hinternet m_session {};
239 _Guarded_by_(m_requestLock) wil::unique_winhttp_hinternet m_resolver {};
240
241 /// <summary>
242 /// Single-threaded queue to trigger work from winhttp callbacks.
243 /// </summary>
244 wsl::core::WslCoreMessageQueue m_callbackQueue{};
245
246 /// Static dynamic functions for loading in necessary WinHttpProxy APIs
247 static std::optional<LxssDynamicFunction<decltype(GetProxySettingsEx)>> s_WinHttpGetProxySettingsEx;
248 static std::optional<LxssDynamicFunction<decltype(GetProxySettingsResultEx)>> s_WinHttpGetProxySettingsResultEx;
249 static std::optional<LxssDynamicFunction<decltype(RegisterProxyChangeNotification)>> s_WinHttpRegisterProxyChangeNotification;
250 static std::optional<LxssDynamicFunction<decltype(UnregisterProxyChangeNotification)>> s_WinHttpUnregisterProxyChangeNotification;
251
252 /// <summary>
253 /// Callback that returns results from proxy queries.
254 /// </summary>
255 /// <param name="resolver"> Resolver associated with this callback. </param>
256 /// <param name="context"> Pointer to ProxyCallbackContext associated with callback. </param>
257 /// <param name="internetStatus"> Status of callback. </param>
258 /// <param name="statusInformation"> Pointer to WINHTTP_ASYNC_RESULT used for error info. </param>
259 static void CALLBACK s_GetProxySettingsExCallback(
260 _In_ HINTERNET resolver, _In_ DWORD_PTR context, _In_ DWORD internetStatus, _In_ PVOID statusInformation, _In_ DWORD) noexcept;
261
262 /// <summary>
263 /// Callback that notifies that an Http proxy setting change has been detected.
264 /// </summary>
265 /// <param name="flags"> Flags used to verify the type of callback received. </param>
266 /// <param name="pContext"> Pointer to ProxyStateTracker associated with callback. </param>
267 static void CALLBACK s_OnProxyChange(_In_ ULONGLONG flags, _In_ void* pContext) noexcept;
268
269 /// <summary>
270 /// Determines if a proxy setting string is localhost.
271 /// </summary>
272 /// <param name="proxyString"> ProxyString to check if it is localhost. </param>
273 /// <param name="configuration"> Current network configuration. </param>
274 static UnsupportedProxyReason IsUnsupportedProxy(LPCWSTR proxyString, wsl::core::NetworkingMode mode) noexcept;
275
276 /// <summary>
277 /// Remove invalid proxy configurations depending on network mode.
278 /// </summary>
279 /// <param name="settings"> HttpProxySettings to be filtered. </param>
280 /// <param name="configuration"> Current network configuration. </param>
281 static void FilterProxySettingsByNetworkConfiguration(HttpProxySettings& settings, wsl::core::NetworkingMode mode) noexcept;
282 };