/*++ Copyright (c) Microsoft. All rights reserved. Module Name: LxssHttpProxy.h Abstract: This file contains HTTP proxy related classes and helper functions for proxy queries. Based on implementation in WSA. --*/ #pragma once #include #include #include #include #include #include #include "WslCoreConfig.h" #include "WslCoreMessageQueue.h" static constexpr auto c_winhttpModuleName = L"Winhttp.dll"; static constexpr auto c_httpProxyLower = "http_proxy"; static constexpr auto c_httpProxyUpper = "HTTP_PROXY"; static constexpr auto c_httpsProxyLower = "https_proxy"; static constexpr auto c_httpsProxyUpper = "HTTPS_PROXY"; static constexpr auto c_proxyBypassLower = "no_proxy"; static constexpr auto c_proxyBypassUpper = "NO_PROXY"; static constexpr auto c_pacProxy = "WSL_PAC_URL"; static constexpr auto c_loopback = L"loopback"; static constexpr auto c_localhost = L"localhost"; void FreeHttpProxySettings(WINHTTP_PROXY_SETTINGS_EX* proxySettings) noexcept; using unique_winhttp_proxy_settings = wil::unique_struct; // Function declarations used for dynamically loading in Winhttp APIs. WINHTTPAPI DWORD WINAPI RegisterProxyChangeNotification(_In_ ULONGLONG ullFlags, _In_ WINHTTP_PROXY_CHANGE_CALLBACK pfnCallback, _In_ PVOID pvContext, _Out_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE* hRegistration); WINHTTPAPI DWORD WINAPI UnregisterProxyChangeNotification(_In_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE hRegistration); WINHTTPAPI DWORD WINAPI GetProxySettingsEx( _In_ HINTERNET hResolver, _In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType, _In_opt_ PWINHTTP_PROXY_SETTINGS_PARAM pProxySettingsParam, _In_opt_ DWORD_PTR pContext); WINHTTPAPI DWORD WINAPI GetProxySettingsResultEx(_In_ HINTERNET hResolver, _Out_ PVOID pProxySettingsEx); WINHTTPAPI DWORD WINAPI FreeProxySettingsEx(_In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType, _In_ PVOID pProxySettingsEx); enum class UnsupportedProxyReason { Supported, LoopbackNotMirrored, Ipv6NotMirrored, LoopbackV6, UnsupportedError }; constexpr auto ToString(UnsupportedProxyReason config) noexcept { switch (config) { case UnsupportedProxyReason::Supported: return "Supported"; case UnsupportedProxyReason::LoopbackNotMirrored: return "LoopbackNotMirrored"; case UnsupportedProxyReason::Ipv6NotMirrored: return "Ipv6NotMirrored"; case UnsupportedProxyReason::LoopbackV6: return "LoopbackV6"; case UnsupportedProxyReason::UnsupportedError: return "UnsupportedError"; default: return ""; } } struct HttpProxySettings { HttpProxySettings() = default; HttpProxySettings(const WINHTTP_PROXY_SETTINGS_EX& ProxySettings); HttpProxySettings(const HttpProxySettings&) = default; HttpProxySettings(HttpProxySettings&&) = default; HttpProxySettings& operator=(const HttpProxySettings&) = default; HttpProxySettings& operator=(HttpProxySettings&&) = default; std::string PacUrl{}; std::string Proxy{}; std::string SecureProxy{}; std::vector ProxyBypasses{}; std::string ProxyBypassesComma{}; UnsupportedProxyReason UnsupportedProxyDropReason = UnsupportedProxyReason::Supported; std::string ToString() const; bool HasSettingsConfigured() const; }; class HttpProxyStateTracker { enum class QueryState { NoQuery, Pending, PendingAndQueueAdditional }; public: HttpProxyStateTracker(int ProxyTimeout, HANDLE UserToken, wsl::core::NetworkingMode configuration); ~HttpProxyStateTracker(); HttpProxyStateTracker(const HttpProxyStateTracker&) = delete; HttpProxyStateTracker(HttpProxyStateTracker&&) = delete; HttpProxyStateTracker& operator=(const HttpProxyStateTracker&) = delete; HttpProxyStateTracker& operator=(HttpProxyStateTracker&&) = delete; /// /// If no proxy queries have completed, wait for timeout for result. /// Otherwise, return the proxy settings. /// std::optional WaitForInitialProxySettings(); /// /// This needs to be called after the VM is created so actual selected configuration is set. /// void ConfigureNetworkingMode(wsl::core::NetworkingMode mode) noexcept; /// /// Loads necessary WinHttpProxy APIs into static dynamic functions from DLL if they exist. /// static HRESULT s_LoadWinHttpProxyMethods() noexcept; /// Static dynamic function for loading in necessary WinHttpProxy APIs static std::optional> s_WinHttpFreeProxySettingsEx; private: /// /// Invoked via WslCoreMessageQueue. Uses WinHttpProxy APIs to start proxy query. /// void QueryProxySettingsAsync(); /// /// Invoked via WslCoreMessageQueue when a proxy request completes. /// /// Error status of request. /// The current proxy settings. void RequestCompleted(_In_ DWORD error, _In_ HttpProxySettings&& newProxySettings) noexcept; /// /// Invoked via WslCoreMessageQueue when a proxy request closes. /// void RequestClosed() noexcept; /// /// Checks if two proxy settings are identical. /// bool AreProxyStringsIdentical(const HttpProxySettings& newSettings) const; /// /// Memory barrier for reading/writing to m_proxySettings. /// wil::critical_section m_proxySettingsLock{}; /// /// Current http proxy settings. If no queries have completed it is std::nullopt. /// _Guarded_by_(m_proxySettingsLock) std::optional m_proxySettings {}; /// /// Current network mode. Used to determine some cases when we should send toast notification. /// _Guarded_by_(m_proxySettingsLock) wsl::core::NetworkingMode m_networkMode = wsl::core::NetworkingMode::Nat; /// /// Indicates if we need to start another query after current one. /// QueryState m_queryState{QueryState::NoQuery}; /// /// Used to impersonate user, as it is required for the proxy queries to run as the user; otherwise, the results will be incorrect. /// wil::unique_handle m_userToken{}; /// /// Handle for tracking http proxy setting changes. /// WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE m_proxyRegistrationHandle{}; /// /// Amount of time WSL will wait for proxy settings if no proxy settings have been detected by time we attempt to launch process. /// const int m_initialQueryTimeout = 1000; /// /// 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. /// const std::wstring m_localizedProxyChangeString{}; /// /// Event that is set when m_proxySettings has a value. /// wil::slim_event_manual_reset m_initialProxyQueryCompleted{false}; /// /// Event that is set when all tracked requests have completed. /// wil::slim_event_manual_reset m_requestFinished{true}; // Handles associated with the request wil::unique_winhttp_hinternet m_session{}; wil::unique_winhttp_hinternet m_resolver{}; /// /// Single-threaded queue to trigger work from winhttp callbacks. /// wsl::core::WslCoreMessageQueue m_callbackQueue{}; /// Static dynamic functions for loading in necessary WinHttpProxy APIs static std::optional> s_WinHttpGetProxySettingsEx; static std::optional> s_WinHttpGetProxySettingsResultEx; static std::optional> s_WinHttpRegisterProxyChangeNotification; static std::optional> s_WinHttpUnregisterProxyChangeNotification; /// /// Callback that returns results from proxy queries. /// /// Resolver associated with this callback. /// Pointer to ProxyCallbackContext associated with callback. /// Status of callback. /// Pointer to WINHTTP_ASYNC_RESULT used for error info. static void CALLBACK s_GetProxySettingsExCallback( _In_ HINTERNET resolver, _In_ DWORD_PTR context, _In_ DWORD internetStatus, _In_ PVOID statusInformation, _In_ DWORD) noexcept; /// /// Callback that notifies that an Http proxy setting change has been detected. /// /// Flags used to verify the type of callback received. /// Pointer to ProxyStateTracker associated with callback. static void CALLBACK s_OnProxyChange(_In_ ULONGLONG flags, _In_ void* pContext) noexcept; /// /// Determines if a proxy setting string is localhost. /// /// ProxyString to check if it is localhost. /// Current network configuration. static UnsupportedProxyReason IsUnsupportedProxy(LPCWSTR proxyString, wsl::core::NetworkingMode mode) noexcept; /// /// Remove invalid proxy configurations depending on network mode. /// /// HttpProxySettings to be filtered. /// Current network configuration. static void FilterProxySettingsByNetworkConfiguration(HttpProxySettings& settings, wsl::core::NetworkingMode mode) noexcept; };