public bool GetProxyForUrl( SafeWinHttpHandle sessionHandle, Uri uri, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; if (!_useProxy) { return(false); } bool useProxy = false; Interop.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; autoProxyOptions.AutoConfigUrl = AutoConfigUrl; autoProxyOptions.AutoDetectFlags = AutoDetect ? (Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DHCP | Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DNS_A) : 0; autoProxyOptions.AutoLoginIfChallenged = false; autoProxyOptions.Flags = (AutoDetect ? Interop.WinHttp.WINHTTP_AUTOPROXY_AUTO_DETECT : 0) | (!string.IsNullOrEmpty(AutoConfigUrl) ? Interop.WinHttp.WINHTTP_AUTOPROXY_CONFIG_URL : 0); autoProxyOptions.Reserved1 = IntPtr.Zero; autoProxyOptions.Reserved2 = 0; // AutoProxy Cache. // https://docs.microsoft.com/en-us/windows/desktop/WinHttp/autoproxy-cache // If the out-of-process service is active when WinHttpGetProxyForUrl is called, the cached autoproxy // URL and script are available to the whole computer. However, if the out-of-process service is used, // and the fAutoLogonIfChallenged flag in the pAutoProxyOptions structure is true, then the autoproxy // URL and script are not cached. Therefore, calling WinHttpGetProxyForUrl with the fAutoLogonIfChallenged // member set to TRUE results in additional overhead operations that may affect performance. // The following steps can be used to improve performance: // 1. Call WinHttpGetProxyForUrl with the fAutoLogonIfChallenged parameter set to false. The autoproxy // URL and script are cached for future calls to WinHttpGetProxyForUrl. // 2. If Step 1 fails, with ERROR_WINHTTP_LOGIN_FAILURE, then call WinHttpGetProxyForUrl with the // fAutoLogonIfChallenged member set to TRUE. // // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. var repeat = false; do { _autoDetectionFailed = false; if (Interop.WinHttp.WinHttpGetProxyForUrl( sessionHandle, uri.AbsoluteUri, ref autoProxyOptions, out proxyInfo)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Using autoconfig proxy settings"); } useProxy = true; break; } else { var lastError = Marshal.GetLastWin32Error(); if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"error={lastError}"); } if (lastError == Interop.WinHttp.ERROR_WINHTTP_LOGIN_FAILURE) { if (repeat) { // We don't retry more than once. break; } else { repeat = true; autoProxyOptions.AutoLoginIfChallenged = true; } } else { if (lastError == Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED) { _autoDetectionFailed = true; _lastTimeAutoDetectionFailed = Environment.TickCount; } break; } } } while (repeat); // Fall back to manual settings if available. if (!useProxy && !string.IsNullOrEmpty(Proxy)) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxyInfo.Proxy = Marshal.StringToHGlobalUni(Proxy); proxyInfo.ProxyBypass = string.IsNullOrEmpty(ProxyBypass) ? IntPtr.Zero : Marshal.StringToHGlobalUni(ProxyBypass); if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Fallback to Proxy={Proxy}, ProxyBypass={ProxyBypass}"); } useProxy = true; } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"useProxy={useProxy}"); } return(useProxy); }
private static void OnRequestSendingRequest(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle is null"); if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https) { // Not SSL/TLS. return; } // Grab the channel binding token (CBT) information from the request handle and put it into // the TransportContext object. state.TransportContext.SetChannelBinding(state.RequestHandle); if (state.ServerCertificateValidationCallback != null) { IntPtr certHandle = IntPtr.Zero; uint certHandleSize = (uint)IntPtr.Size; if (!Interop.WinHttp.WinHttpQueryOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CERT_CONTEXT, ref certHandle, ref certHandleSize)) { int lastError = Marshal.GetLastWin32Error(); WinHttpTraceHelper.Trace( "OnRequestSendingRequest: Error getting WINHTTP_OPTION_SERVER_CERT_CONTEXT, {0}", lastError); if (lastError == Interop.WinHttp.ERROR_WINHTTP_INCORRECT_HANDLE_STATE) { // Not yet an SSL/TLS connection. This occurs while connecting thru a proxy where the // CONNECT verb hasn't yet been processed due to the proxy requiring authentication. // We need to ignore this notification. Another notification will be sent once the final // connection thru the proxy is completed. return; } throw WinHttpException.CreateExceptionUsingError(lastError); } // Create a managed wrapper around the certificate handle. Since this results in duplicating // the handle, we will close the original handle after creating the wrapper. var serverCertificate = new X509Certificate2(certHandle); Interop.Crypt32.CertFreeCertificateContext(certHandle); X509Chain chain = null; SslPolicyErrors sslPolicyErrors; try { WinHttpCertificateHelper.BuildChain( serverCertificate, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); bool result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } finally { if (chain != null) { chain.Dispose(); } serverCertificate.Dispose(); } } }