Пример #1
0
        /// <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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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));
        }
Пример #4
0
        /// <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);
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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();
            }
        }
Пример #8
0
        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();
                }
            }
        }
Пример #9
0
        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;
            }
        }
Пример #10
0
        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();
                }
            }
        }
Пример #11
0
        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);
        }
Пример #12
0
        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);
        }