public WinInetProxyHelper() { var proxyConfig = new Interop.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(); try { if (Interop.WinHttp.WinHttpGetIEProxyConfigForCurrentUser(out proxyConfig)) { AutoConfigUrl = Marshal.PtrToStringUni(proxyConfig.AutoConfigUrl); AutoDetect = proxyConfig.AutoDetect; Proxy = Marshal.PtrToStringUni(proxyConfig.Proxy); ProxyBypass = Marshal.PtrToStringUni(proxyConfig.ProxyBypass); } else { var lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_FILE_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError); } } } finally { // FreeHGlobal already checks for null pointer before freeing the memory. Marshal.FreeHGlobal(proxyConfig.AutoConfigUrl); Marshal.FreeHGlobal(proxyConfig.Proxy); Marshal.FreeHGlobal(proxyConfig.ProxyBypass); } }
/// <summary> /// Returns the size of the char array buffer. /// </summary> public static unsafe int GetResponseHeaderCharBufferLength(SafeWinHttpHandle requestHandle, bool isTrailingHeaders) { char *buffer = null; int bufferLength = 0; uint index = 0; uint infoLevel = Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF; if (isTrailingHeaders) { infoLevel |= Interop.WinHttp.WINHTTP_QUERY_FLAG_TRAILERS; } if (!QueryHeaders(requestHandle, infoLevel, buffer, ref bufferLength, ref index)) { int lastError = Marshal.GetLastWin32Error(); Debug.Assert(isTrailingHeaders || lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND); if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER && (!isTrailingHeaders || lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND)) { throw WinHttpException.CreateExceptionUsingError(lastError, nameof(Interop.WinHttp.WinHttpQueryHeaders)); } } return(bufferLength); }
/// <summary> /// Fills the buffer with the header value and returns the length, or returns 0 if the header isn't found. /// </summary> private static unsafe int GetResponseHeader(SafeWinHttpHandle requestHandle, uint infoLevel, char[] buffer) { Debug.Assert(buffer != null, "buffer must not be null."); Debug.Assert(buffer.Length > 0, "buffer must not be empty."); int bufferLength = buffer.Length; uint index = 0; fixed(char *pBuffer = &buffer[0]) { if (!QueryHeaders(requestHandle, infoLevel, pBuffer, ref bufferLength, ref index)) { int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { return(0); } Debug.Assert(lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER, "buffer must be of sufficient size."); throw WinHttpException.CreateExceptionUsingError(lastError); } } return(bufferLength); }
public static unsafe bool GetResponseHeader( SafeWinHttpHandle requestHandle, uint infoLevel, ref char[] buffer, ref uint index, out string headerValue) { const int StackLimit = 128; Debug.Assert(buffer == null || (buffer != null && buffer.Length > StackLimit)); int bufferLength; uint originalIndex = index; if (buffer == null) { bufferLength = StackLimit; char *pBuffer = stackalloc char[bufferLength]; if (QueryHeaders(requestHandle, infoLevel, pBuffer, ref bufferLength, ref index)) { headerValue = new string(pBuffer, 0, bufferLength); return(true); } } else { bufferLength = buffer.Length; fixed(char *pBuffer = &buffer[0]) { if (QueryHeaders(requestHandle, infoLevel, pBuffer, ref bufferLength, ref index)) { headerValue = new string(pBuffer, 0, bufferLength); return(true); } } } int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { headerValue = null; return(false); } if (lastError == Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { // WinHttpQueryHeaders may advance the index even when it fails due to insufficient buffer, // so we set the index back to its original value so we can retry retrieving the same // index again with a larger buffer. index = originalIndex; buffer = new char[bufferLength]; return(GetResponseHeader(requestHandle, infoLevel, ref buffer, ref index, out headerValue)); } throw WinHttpException.CreateExceptionUsingError(lastError); }
public unsafe static bool GetResponseHeader( SafeWinHttpHandle requestHandle, uint infoLevel, ref char[] buffer, ref uint index, out string headerValue) { const int StackLimit = 128; Debug.Assert(buffer == null || (buffer != null && buffer.Length > StackLimit)); int bufferLength; if (buffer == null) { bufferLength = StackLimit; char *pBuffer = stackalloc char[bufferLength]; if (QueryHeaders(requestHandle, infoLevel, pBuffer, ref bufferLength, ref index)) { headerValue = new string(pBuffer, 0, bufferLength); return(true); } } else { bufferLength = buffer.Length; fixed(char *pBuffer = buffer) { if (QueryHeaders(requestHandle, infoLevel, pBuffer, ref bufferLength, ref index)) { headerValue = new string(pBuffer, 0, bufferLength); return(true); } } } int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { headerValue = null; return(false); } if (lastError == Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { buffer = new char[bufferLength]; return(GetResponseHeader(requestHandle, infoLevel, ref buffer, ref index, out headerValue)); } throw WinHttpException.CreateExceptionUsingError(lastError); }
private static string GetResponseHeaderStringHelper( SafeWinHttpHandle requestHandle, uint infoLevel, ref uint index) { uint bytesNeeded = 0; bool results = false; // Call WinHttpQueryHeaders once to obtain the size of the buffer needed. The size is returned in // bytes but the API actually returns Unicode characters. if (!Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, null, ref bytesNeeded, ref index)) { int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { return(null); } if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { throw WinHttpException.CreateExceptionUsingError(lastError); } } // Allocate space for the buffer. int charsNeeded = (int)bytesNeeded / sizeof(char); var buffer = new StringBuilder(charsNeeded, charsNeeded); results = Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, buffer, ref bytesNeeded, ref index); if (!results) { WinHttpException.ThrowExceptionUsingLastError(); } return(buffer.ToString()); }
private void SetStatusCallback( SafeWinHttpHandle requestHandle, Interop.WinHttp.WINHTTP_STATUS_CALLBACK callback) { IntPtr oldCallback = Interop.WinHttp.WinHttpSetStatusCallback( requestHandle, callback, Interop.WinHttp.WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, IntPtr.Zero); if (oldCallback == new IntPtr(Interop.WinHttp.WINHTTP_INVALID_STATUS_CALLBACK)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_INVALID_HANDLE) // Ignore error if handle was already closed. { throw WinHttpException.CreateExceptionUsingError(lastError); } } }
private static void ResetAuthRequestHeaders(WinHttpRequestState state) { const string AuthHeaderNameWithColon = "Authorization:"; SafeWinHttpHandle requestHandle = state.RequestHandle; // Clear auth headers. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, AuthHeaderNameWithColon, (uint)AuthHeaderNameWithColon.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError, "WINHTTP_CALLBACK_STATUS_REDIRECT/WinHttpAddRequestHeaders"); } } }
/// <summary> /// Returns the size of the char array buffer. /// </summary> private static unsafe int GetResponseHeaderCharBufferLength(SafeWinHttpHandle requestHandle, uint infoLevel) { char *buffer = null; int bufferLength = 0; uint index = 0; if (!QueryHeaders(requestHandle, infoLevel, buffer, ref bufferLength, ref index)) { int lastError = Marshal.GetLastWin32Error(); Debug.Assert(lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND); if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { throw WinHttpException.CreateExceptionUsingError(lastError); } } return(bufferLength); }
private void SetStatusCallback( SafeWinHttpHandle requestHandle, Interop.WinHttp.WINHTTP_STATUS_CALLBACK callback) { // TODO: Issue #5036. Having the status callback use WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS // isn't strictly necessary. However, some of the notification flags are required. // This will be addressed this as part of WinHttpHandler performance improvements. IntPtr oldCallback = Interop.WinHttp.WinHttpSetStatusCallback( requestHandle, callback, Interop.WinHttp.WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, IntPtr.Zero); if (oldCallback == new IntPtr(Interop.WinHttp.WINHTTP_INVALID_STATUS_CALLBACK)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_INVALID_HANDLE) // Ignore error if handle was already closed. { throw WinHttpException.CreateExceptionUsingError(lastError); } } }
public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri) { SafeWinHttpHandle?requestHandle = state.RequestHandle; Debug.Assert(state.Handler != null); Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); Debug.Assert(state.Handler.CookieContainer != null); Debug.Assert(requestHandle != null); // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, CookieHeaderNameWithColon, (uint)CookieHeaderNameWithColon.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError, nameof(Interop.WinHttp.WinHttpAddRequestHeaders)); } } // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. string?cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, cookieHeader, (uint)cookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(nameof(Interop.WinHttp.WinHttpAddRequestHeaders)); } } }
private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { Debug.Assert(state != null, "OnRequestError: state is null"); if (NetEventSource.Log.IsEnabled()) { WinHttpTraceHelper.TraceAsyncError(state, asyncResult); } Exception innerException = WinHttpException.CreateExceptionUsingError(unchecked ((int)asyncResult.dwError), "WINHTTP_CALLBACK_STATUS_REQUEST_ERROR"); switch (unchecked ((uint)asyncResult.dwResult.ToInt32())) { case Interop.WinHttp.API_SEND_REQUEST: state.LifecycleAwaitable.SetException(innerException); break; case Interop.WinHttp.API_RECEIVE_RESPONSE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_RESEND_REQUEST) { state.RetryRequest = true; state.LifecycleAwaitable.SetResult(0); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) { // WinHttp will automatically drop any client SSL certificates that we // have pre-set into the request handle including the NULL certificate // (which means we have no certs to send). For security reasons, we don't // allow the certificate to be re-applied. But we need to tell WinHttp // explicitly that we don't have any certificate to send. Debug.Assert(state.RequestHandle != null, "OnRequestError: state.RequestHandle is null"); WinHttpHandler.SetNoClientCertificate(state.RequestHandle); state.RetryRequest = true; state.LifecycleAwaitable.SetResult(0); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { state.LifecycleAwaitable.SetCanceled(state.CancellationToken); } else { state.LifecycleAwaitable.SetException(innerException); } break; case Interop.WinHttp.API_QUERY_DATA_AVAILABLE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(state, "QUERY_DATA_AVAILABLE - ERROR_WINHTTP_OPERATION_CANCELLED"); } state.LifecycleAwaitable.SetCanceled(); } else { state.LifecycleAwaitable.SetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_READ_DATA: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(state, "API_READ_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); } state.LifecycleAwaitable.SetCanceled(); } else { state.LifecycleAwaitable.SetException(new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_WRITE_DATA: Debug.Assert(state.TcsInternalWriteDataToRequestStream != null); if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(state, "API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); } state.TcsInternalWriteDataToRequestStream.TrySetCanceled(); } else { state.TcsInternalWriteDataToRequestStream.TrySetException( new IOException(SR.net_http_io_write, innerException)); } break; default: Debug.Fail( "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.", "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")"); break; } }
private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { WinHttpTraceHelper.TraceAsyncError("OnRequestError", asyncResult); Debug.Assert(state != null, "OnRequestError: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)asyncResult.dwError).InitializeStackTrace(); switch ((uint)asyncResult.dwResult.ToInt32()) { case Interop.WinHttp.API_SEND_REQUEST: state.TcsSendRequest.TrySetException(innerException); break; case Interop.WinHttp.API_RECEIVE_RESPONSE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_RESEND_REQUEST) { state.RetryRequest = true; state.TcsReceiveResponseHeaders.TrySetResult(false); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) { // WinHttp will automatically drop any client SSL certificates that we // have pre-set into the request handle including the NULL certificate // (which means we have no certs to send). For security reasons, we don't // allow the certificate to be re-applied. But we need to tell WinHttp // explicitly that we don't have any certificate to send. Debug.Assert(state.RequestHandle != null, "OnRequestError: state.RequestHandle is null"); WinHttpHandler.SetNoClientCertificate(state.RequestHandle); state.RetryRequest = true; state.TcsReceiveResponseHeaders.TrySetResult(false); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { state.TcsReceiveResponseHeaders.TrySetCanceled(state.CancellationToken); } else { state.TcsReceiveResponseHeaders.TrySetException(innerException); } break; case Interop.WinHttp.API_QUERY_DATA_AVAILABLE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: QUERY_DATA_AVAILABLE - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsQueryDataAvailable.TrySetCanceled(); } else { state.TcsQueryDataAvailable.TrySetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_READ_DATA: state.DisposeCtrReadFromResponseStream(); if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: API_READ_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsReadFromResponseStream.TrySetCanceled(); } else { state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_WRITE_DATA: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's WriteAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsInternalWriteDataToRequestStream.TrySetCanceled(); } else { state.TcsInternalWriteDataToRequestStream.TrySetException( new IOException(SR.net_http_io_write, innerException)); } break; default: Debug.Fail( "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.", "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")"); break; } }
private static void OnRequestRedirect(WinHttpRequestState state, Uri redirectUri) { const string EmptyCookieHeader = "Cookie:"; Debug.Assert(state != null, "OnRequestRedirect: state is null"); Debug.Assert(redirectUri != null, "OnRequestRedirect: redirectUri is null"); Debug.Assert(state.TcsReceiveResponseHeaders != null, "TcsReceiveResponseHeaders is null"); Debug.Assert(!state.TcsReceiveResponseHeaders.Task.IsCompleted, "TcsReceiveResponseHeaders.Task is completed"); // If we're manually handling cookies, we need to reset them based on the new URI. if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( state.RequestHandle, EmptyCookieHeader, (uint)EmptyCookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError); } } // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. string cookieHeader = WinHttpHandler.GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( state.RequestHandle, cookieHeader, (uint)cookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(); } } } state.RequestMessage.RequestUri = redirectUri; // Redirection to a new uri may require a new connection through a potentially different proxy. // If so, we will need to respond to additional 407 proxy auth demands and re-attach any // proxy credentials. The ProcessResponse() method looks at the state.LastStatusCode // before attaching proxy credentials and marking the HTTP request to be re-submitted. // So we need to reset the LastStatusCode remembered. Otherwise, it will see additional 407 // responses as an indication that proxy auth failed and won't retry the HTTP request. if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired) { state.LastStatusCode = 0; } // For security reasons, we drop the server credential if it is a // NetworkCredential. But we allow credentials in a CredentialCache // since they are specifically tied to URI's. if (!(state.ServerCredentials is CredentialCache)) { state.ServerCredentials = null; } }
public void GetProxyForUrl(SafeWinHttpHandle sessionHandle, Uri uri, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { 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. // http://msdn.microsoft.com/en-us/library/windows/desktop/aa383153(v=vs.85).aspx // 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. var repeat = false; do { if (Interop.WinHttp.WinHttpGetProxyForUrl(sessionHandle, uri.AbsoluteUri, ref autoProxyOptions, out proxyInfo)) { repeat = false; } else { var lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED) { // Fall back to manual settings if available. if (!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); } else { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; } repeat = false; } else if (lastError == Interop.WinHttp.ERROR_WINHTTP_LOGIN_FAILURE) { if (repeat) { throw WinHttpException.CreateExceptionUsingError(lastError); } repeat = true; autoProxyOptions.AutoLoginIfChallenged = true; } else { WinHttpException.ThrowExceptionUsingLastError(); } } } while (repeat); }
private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { WinHttpTraceHelper.TraceAsyncError("OnRequestError", asyncResult); Debug.Assert(state != null, "OnRequestError: state is null"); Debug.Assert((unchecked ((int)asyncResult.dwError) != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER && unchecked ((int)asyncResult.dwError) != unchecked ((int)0x80090321)), // SEC_E_BUFFER_TOO_SMALL $"Unexpected async error in WinHttpRequestCallback: {unchecked((int)asyncResult.dwError)}, WinHttp API: {unchecked((uint)asyncResult.dwResult.ToInt32())}"); Exception innerException = WinHttpException.CreateExceptionUsingError(unchecked ((int)asyncResult.dwError)); switch (unchecked ((uint)asyncResult.dwResult.ToInt32())) { case Interop.WinHttp.API_SEND_REQUEST: state.LifecycleAwaitable.SetException(innerException); break; case Interop.WinHttp.API_RECEIVE_RESPONSE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_RESEND_REQUEST) { state.RetryRequest = true; state.LifecycleAwaitable.SetResult(0); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) { // WinHttp will automatically drop any client SSL certificates that we // have pre-set into the request handle including the NULL certificate // (which means we have no certs to send). For security reasons, we don't // allow the certificate to be re-applied. But we need to tell WinHttp // explicitly that we don't have any certificate to send. Debug.Assert(state.RequestHandle != null, "OnRequestError: state.RequestHandle is null"); WinHttpHandler.SetNoClientCertificate(state.RequestHandle); state.RetryRequest = true; state.LifecycleAwaitable.SetResult(0); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { state.LifecycleAwaitable.SetCanceled(state.CancellationToken); } else { state.LifecycleAwaitable.SetException(innerException); } break; case Interop.WinHttp.API_QUERY_DATA_AVAILABLE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). WinHttpTraceHelper.Trace("RequestCallback: QUERY_DATA_AVAILABLE - ERROR_WINHTTP_OPERATION_CANCELLED"); state.LifecycleAwaitable.SetCanceled(); } else { state.LifecycleAwaitable.SetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_READ_DATA: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). WinHttpTraceHelper.Trace("RequestCallback: API_READ_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.LifecycleAwaitable.SetCanceled(); } else { state.LifecycleAwaitable.SetException(new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_WRITE_DATA: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's WriteAsync() call into the TrySetCanceled(). WinHttpTraceHelper.Trace("RequestCallback: API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsInternalWriteDataToRequestStream.TrySetCanceled(); } else { state.TcsInternalWriteDataToRequestStream.TrySetException( new IOException(SR.net_http_io_write, innerException)); } break; default: Debug.Fail( "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.", "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")"); break; } }
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); } // Get any additional certificates sent from the remote server during the TLS/SSL handshake. X509Certificate2Collection remoteCertificateStore = UnmanagedCertificateContext.GetRemoteCertificatesFromStoreContext(certHandle); // 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; bool result = false; try { WinHttpCertificateHelper.BuildChain( serverCertificate, remoteCertificateStore, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); } catch (Exception ex) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE, ex); } finally { if (chain != null) { chain.Dispose(); } serverCertificate.Dispose(); } if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } }
private void EnsureSessionHandleExists(WinHttpRequestState state) { if (_sessionHandle == null) { lock (_lockObject) { if (_sessionHandle == null) { uint accessType; // If a custom proxy is specified and it is really the system web proxy // (initial WebRequest.DefaultWebProxy) then we need to update the settings // since that object is only a sentinel. if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy) { Debug.Assert(state.Proxy != null); try { state.Proxy.GetProxy(state.RequestMessage.RequestUri); } catch (PlatformNotSupportedException) { // This is the system web proxy. state.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseWinInetProxy; state.Proxy = null; } } if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.DoNotUseProxy || state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy) { // Either no proxy at all or a custom IWebProxy proxy is specified. // For a custom IWebProxy, we'll need to calculate and set the proxy // on a per request handle basis using the request Uri. For now, // we set the session handle to have no proxy. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; } else if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseWinHttpProxy) { // Use WinHTTP per-machine proxy settings which are set using the "netsh winhttp" command. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; } else { // Use WinInet per-user proxy settings. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; } _sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, accessType, Interop.WinHttp.WINHTTP_NO_PROXY_NAME, Interop.WinHttp.WINHTTP_NO_PROXY_BYPASS, (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC); if (_sessionHandle.IsInvalid) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_INVALID_PARAMETER) { ThrowOnInvalidHandle(_sessionHandle); } // We must be running on a platform earlier than Win8.1/Win2K12R2 which doesn't support // WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY. So, we'll need to read the Wininet style proxy // settings ourself using our WinInetProxyHelper object. _proxyHelper = new WinInetProxyHelper(); _sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, _proxyHelper.ManualSettingsOnly ? Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY : Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY, _proxyHelper.ManualSettingsOnly ? _proxyHelper.Proxy : Interop.WinHttp.WINHTTP_NO_PROXY_NAME, _proxyHelper.ManualSettingsOnly ? _proxyHelper.ProxyBypass : Interop.WinHttp.WINHTTP_NO_PROXY_BYPASS, (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC); ThrowOnInvalidHandle(_sessionHandle); } uint optionAssuredNonBlockingTrue = 1; // TRUE if (!Interop.WinHttp.WinHttpSetOption( _sessionHandle, Interop.WinHttp.WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS, ref optionAssuredNonBlockingTrue, (uint)Marshal.SizeOf <uint>())) { // This option is not available on downlevel Windows versions. While it improves // performance, we can ignore the error that the option is not available. int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_INVALID_OPTION) { throw WinHttpException.CreateExceptionUsingError(lastError); } } } } } }
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(); 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(); } } }