/// <summary> /// Fills the buffer with the header value and returns the length, or returns 0 if the header isn't found. /// </summary> private unsafe static 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) { 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 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; 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) { 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 static void TraceAsyncError(string message, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { if (!IsTraceEnabled()) { return; } uint apiIndex = (uint)asyncResult.dwResult.ToInt32(); uint error = asyncResult.dwError; WriteLine( "{0}: api={1}, error={2}({3}) \"{4}\"", message, GetNameFromApiIndex(apiIndex), GetNameFromError(error), error, WinHttpException.GetErrorMessage((int)error)); }
/// <summary> /// Returns the first header or throws if the header isn't found. /// </summary> public static uint GetResponseHeaderNumberInfo(SafeWinHttpHandle requestHandle, uint infoLevel) { uint result = 0; uint resultSize = sizeof(uint); if (!Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel | Interop.WinHttp.WINHTTP_QUERY_FLAG_NUMBER, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, ref result, ref resultSize, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(); } return(result); }
/// <summary> /// Returns the size of the char array buffer. /// </summary> private unsafe static 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 Task <bool> InternalWriteDataAsync(byte[] buffer, int offset, int count, CancellationToken token) { Debug.Assert(count >= 0); if (count == 0) { return(Task.FromResult <bool>(true)); } // TODO (Issue 2505): replace with PinnableBufferCache. if (!_cachedSendPinnedBuffer.IsAllocated || _cachedSendPinnedBuffer.Target != buffer) { if (_cachedSendPinnedBuffer.IsAllocated) { _cachedSendPinnedBuffer.Free(); } _cachedSendPinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); } _state.TcsInternalWriteDataToRequestStream = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); lock (_state.Lock) { if (!Interop.WinHttp.WinHttpWriteData( _state.RequestHandle, Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset), (uint)count, IntPtr.Zero)) { _state.TcsInternalWriteDataToRequestStream.TrySetException( new IOException("net_http_io_write", WinHttpException.CreateExceptionUsingLastError())); } } // TODO: Issue #2165. Register callback on cancellation token to cancel WinHTTP operation. return(_state.TcsInternalWriteDataToRequestStream.Task); }
public void ChangeDefaultCredentialsPolicy( SafeWinHttpHandle requestHandle, uint authTarget, bool allowDefaultCredentials) { Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); uint optionData = allowDefaultCredentials ? (authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY ? Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM : Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW) : Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; if (!Interop.WinHttp.WinHttpSetOption( requestHandle, Interop.WinHttp.WINHTTP_OPTION_AUTOLOGON_POLICY, ref optionData)) { WinHttpException.ThrowExceptionUsingLastError(); } }
public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri) { SafeWinHttpHandle requestHandle = state.RequestHandle; Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); // 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); } } // 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(); } } }
private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { WinHttpTraceHelper.TraceAsyncError("OnRequestError", asyncResult); Debug.Assert(state != null, "OnRequestError: state is null"); Exception innerException = WinHttpException.CreateExceptionUsingError(unchecked ((int)asyncResult.dwError)); switch (unchecked ((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("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("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("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(); 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(); } } }
public override Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (count > buffer.Length - offset) { throw new ArgumentException("net_http_buffer_insufficient_length", nameof(buffer)); } if (token.IsCancellationRequested) { return(Task.FromCanceled <int>(token)); } CheckDisposed(); if (_state.TcsReadFromResponseStream != null && !_state.TcsReadFromResponseStream.Task.IsCompleted) { throw new InvalidOperationException("net_http_no_concurrent_io_allowed"); } _state.PinReceiveBuffer(buffer); _state.TcsReadFromResponseStream = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable.Task.ContinueWith((previousTask) => { if (previousTask.IsFaulted) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException(previousTask.Exception.InnerException); } else if (previousTask.IsCanceled || token.IsCancellationRequested) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetCanceled(token); } else { int bytesToRead; int bytesAvailable = previousTask.Result; if (bytesAvailable > count) { bytesToRead = count; } else { bytesToRead = bytesAvailable; } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpReadData( _requestHandle, Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset), (uint)bytesToRead, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException("net_http_io_read", WinHttpException.CreateExceptionUsingLastError())); } } } }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); // Register callback on cancellation token to cancel any pending WinHTTP operation. if (token.CanBeCanceled) { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: registering for cancellation token request"); _state.CtrReadFromResponseStream = token.Register(s => ((WinHttpResponseStream)s).CancelPendingResponseStreamReadOperation(), this); } else { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: received no cancellation token"); } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpQueryDataAvailable(_requestHandle, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException("net_http_io_read", WinHttpException.CreateExceptionUsingLastError())); } } return(_state.TcsReadFromResponseStream.Task); }
private bool SetWinHttpCredential( SafeWinHttpHandle requestHandle, ICredentials credentials, Uri uri, uint authScheme, uint authTarget) { string userName; string password; Debug.Assert(credentials != null); Debug.Assert(authScheme != 0); Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); NetworkCredential networkCredential = credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]); if (networkCredential == null) { return(false); } if (networkCredential == CredentialCache.DefaultNetworkCredentials) { // Only Negotiate and NTLM can use default credentials. Otherwise, // behave as-if there were no credentials. if (authScheme == Interop.WinHttp.WINHTTP_AUTH_SCHEME_NEGOTIATE || authScheme == Interop.WinHttp.WINHTTP_AUTH_SCHEME_NTLM) { // Allow WinHTTP to transmit the default credentials. ChangeDefaultCredentialsPolicy(requestHandle, authTarget, allowDefaultCredentials: true); userName = null; password = null; } else { return(false); } } else { userName = networkCredential.UserName; password = networkCredential.Password; string domain = networkCredential.Domain; // WinHTTP does not support a blank username. So, we will throw an exception. if (string.IsNullOrEmpty(userName)) { throw new InvalidOperationException("net_http_username_empty_string"); } if (!string.IsNullOrEmpty(domain)) { userName = domain + "\\" + userName; } } if (!Interop.WinHttp.WinHttpSetCredentials( requestHandle, authTarget, authScheme, userName, password, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(); } return(true); }