Beispiel #1
0
        public void SendAsync(MailMessage message, object?userToken)
        {
            ObjectDisposedException.ThrowIf(_disposed, this);


            try
            {
                if (InCall)
                {
                    throw new InvalidOperationException(SR.net_inasync);
                }

                if (message == null)
                {
                    throw new ArgumentNullException(nameof(message));
                }

                if (DeliveryMethod == SmtpDeliveryMethod.Network)
                {
                    CheckHostAndPort();
                }

                _recipients = new MailAddressCollection();

                if (message.From == null)
                {
                    throw new InvalidOperationException(SR.SmtpFromRequired);
                }

                if (message.To != null)
                {
                    foreach (MailAddress address in message.To)
                    {
                        _recipients.Add(address);
                    }
                }
                if (message.Bcc != null)
                {
                    foreach (MailAddress address in message.Bcc)
                    {
                        _recipients.Add(address);
                    }
                }
                if (message.CC != null)
                {
                    foreach (MailAddress address in message.CC)
                    {
                        _recipients.Add(address);
                    }
                }

                if (_recipients.Count == 0)
                {
                    throw new InvalidOperationException(SR.SmtpRecipientRequired);
                }

                InCall     = true;
                _cancelled = false;
                _message   = message;
                string?pickupDirectory = PickupDirectoryLocation;

                CredentialCache?cache;
                // Skip token capturing if no credentials are used or they don't include a default one.
                // Also do capture the token if ICredential is not of CredentialCache type so we don't know what the exact credential response will be.
                _transport.IdentityRequired = Credentials != null && (ReferenceEquals(Credentials, CredentialCache.DefaultNetworkCredentials) || (cache = Credentials as CredentialCache) == null || IsSystemNetworkCredentialInCache(cache));

                _asyncOp = AsyncOperationManager.CreateOperation(userToken);
                switch (DeliveryMethod)
                {
                case SmtpDeliveryMethod.PickupDirectoryFromIis:
                    throw new NotSupportedException(SR.SmtpGetIisPickupDirectoryNotSupported);

                case SmtpDeliveryMethod.SpecifiedPickupDirectory:
                {
                    if (EnableSsl)
                    {
                        throw new SmtpException(SR.SmtpPickupDirectoryDoesnotSupportSsl);
                    }

                    _writer = GetFileMailWriter(pickupDirectory);
                    bool allowUnicode = IsUnicodeSupported();
                    ValidateUnicodeRequirement(message, _recipients, allowUnicode);
                    message.Send(_writer, true, allowUnicode);

                    if (_writer != null)
                    {
                        _writer.Close();
                    }

                    AsyncCompletedEventArgs eventArgs = new AsyncCompletedEventArgs(null, false, _asyncOp.UserSuppliedState);
                    InCall = false;
                    _asyncOp.PostOperationCompleted(_onSendCompletedDelegate, eventArgs);
                    break;
                }

                case SmtpDeliveryMethod.Network:
                default:
                    _operationCompletedResult = new ContextAwareResult(_transport.IdentityRequired, true, null, this, s_contextSafeCompleteCallback);
                    lock (_operationCompletedResult.StartPostingAsyncOp())
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, $"Calling BeginConnect. Transport: {_transport}");
                        }
                        _transport.BeginGetConnection(_operationCompletedResult, ConnectCallback, _operationCompletedResult, Host !, Port);
                        _operationCompletedResult.FinishPostingAsyncOp();
                    }
                    break;
                }
            }
            catch (Exception e)
            {
                InCall = false;

                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(this, e);
                }

                if (e is SmtpFailedRecipientException && !((SmtpFailedRecipientException)e).fatal)
                {
                    throw;
                }

                Abort();

                if (e is SecurityException ||
                    e is AuthenticationException ||
                    e is SmtpException)
                {
                    throw;
                }

                throw new SmtpException(SR.SmtpSendMailFailure, e);
            }
        }
Beispiel #2
0
        protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response;
            uint redirectCount = 0;

            while (true)
            {
                // Just as with WinHttpHandler and CurlHandler, for security reasons, we drop the server credential if it is
                // anything other than a CredentialCache on redirection. We allow credentials in a CredentialCache since they
                // are specifically tied to URIs.
                ICredentials currentCredential = redirectCount > 0 ? _credentials as CredentialCache : _credentials;

                if (currentCredential != null && _preAuthenticate)
                {
                    AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential);
                }

                response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);

                if (currentCredential != null && response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    AuthenticationHeaderValue selectedAuth = GetSupportedAuthScheme(response.Headers.WwwAuthenticate);
                    if (selectedAuth != null)
                    {
                        switch (selectedAuth.Scheme)
                        {
                        case AuthenticationHelper.Digest:
                            // Update digest response with new parameter from WWWAuthenticate
                            var digestResponse = new AuthenticationHelper.DigestResponse(selectedAuth.Parameter);
                            if (await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false))
                            {
                                response.Dispose();
                                response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);

                                // Retry in case of nonce timeout in server.
                                if (response.StatusCode == HttpStatusCode.Unauthorized)
                                {
                                    foreach (AuthenticationHeaderValue ahv in response.Headers.WwwAuthenticate)
                                    {
                                        if (ahv.Scheme == AuthenticationHelper.Digest)
                                        {
                                            digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter);
                                            if (AuthenticationHelper.IsServerNonceStale(digestResponse) &&
                                                await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false))
                                            {
                                                response.Dispose();
                                                response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);
                                            }

                                            break;
                                        }
                                    }
                                }
                            }
                            break;

                        case AuthenticationHelper.Basic:
                            if (_preAuthenticate)
                            {
                                // We already tried these credentials via preauthentication, so no need to try again
                                break;
                            }

                            if (AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential))
                            {
                                response.Dispose();
                                response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);
                            }
                            break;
                        }
                    }
                }

                if (!RequestNeedsRedirect(response))
                {
                    break;
                }

                // Clear the authorization header, if the request requires redirect.
                request.Headers.Authorization = null;

                Uri location = response.Headers.Location;
                if (location == null)
                {
                    // No location header. Nothing to redirect to.
                    break;
                }

                if (!location.IsAbsoluteUri)
                {
                    location = new Uri(request.RequestUri, location);
                }

                // Disallow automatic redirection from secure to non-secure schemes
                bool allowed =
                    (HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedScheme(location.Scheme)) ||
                    (HttpUtilities.IsSupportedSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedSecureScheme(location.Scheme));
                if (!allowed)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"Insecure https to http redirect from {request.RequestUri} to {location} blocked.");
                    }
                    break;
                }

                redirectCount++;
                if (redirectCount > _maxAutomaticRedirections)
                {
                    throw new HttpRequestException(SR.net_http_max_redirects);
                }

                // Set up for the automatic redirect
                request.RequestUri = location;

                if (RequestRequiresForceGet(response.StatusCode, request.Method))
                {
                    request.Method  = HttpMethod.Get;
                    request.Content = null;
                }

                // Do the redirect.
                response.Dispose();
            }

            return(response);
        }
Beispiel #3
0
        private void Initialize()
        {
            _transport = new SmtpTransport(this);
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Associate(this, _transport);
            }
            _onSendCompletedDelegate = new SendOrPostCallback(SendCompletedWaitCallback);

            if (_host != null && _host.Length != 0)
            {
                _host = _host.Trim();
            }

            if (_port == 0)
            {
                _port = DefaultPort;
            }

            if (_targetName == null)
            {
                _targetName = "SMTPSVC/" + _host;
            }

            if (_clientDomain == null)
            {
                // We use the local host name as the default client domain
                // for the client's EHLO or HELO message. This limits the
                // information about the host that we share. Additionally, the
                // FQDN is not available to us or useful to the server (internal
                // machine connecting to public server).

                // SMTP RFC's require ASCII only host names in the HELO/EHLO message.
                string clientDomainRaw = IPGlobalProperties.GetIPGlobalProperties().HostName;

                IdnMapping mapping = new IdnMapping();
                try
                {
                    clientDomainRaw = mapping.GetAscii(clientDomainRaw);
                }
                catch (ArgumentException) { }

                // For some inputs GetAscii may fail (bad Unicode, etc).  If that happens
                // we must strip out any non-ASCII characters.
                // If we end up with no characters left, we use the string "LocalHost".  This
                // matches Outlook behavior.
                StringBuilder sb = new StringBuilder();
                char          ch;
                for (int i = 0; i < clientDomainRaw.Length; i++)
                {
                    ch = clientDomainRaw[i];
                    if ((ushort)ch <= 0x7F)
                    {
                        sb.Append(ch);
                    }
                }
                if (sb.Length > 0)
                {
                    _clientDomain = sb.ToString();
                }
                else
                {
                    _clientDomain = "LocalHost";
                }
            }
        }
Beispiel #4
0
        public void Send(MailMessage message)
        {
            ObjectDisposedException.ThrowIf(_disposed, this);

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, $"DeliveryMethod={DeliveryMethod}");
                NetEventSource.Associate(this, message);
            }

            SmtpFailedRecipientException?recipientException = null;

            if (InCall)
            {
                throw new InvalidOperationException(SR.net_inasync);
            }

            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (DeliveryMethod == SmtpDeliveryMethod.Network)
            {
                CheckHostAndPort();
            }

            MailAddressCollection recipients = new MailAddressCollection();

            if (message.From == null)
            {
                throw new InvalidOperationException(SR.SmtpFromRequired);
            }

            if (message.To != null)
            {
                foreach (MailAddress address in message.To)
                {
                    recipients.Add(address);
                }
            }
            if (message.Bcc != null)
            {
                foreach (MailAddress address in message.Bcc)
                {
                    recipients.Add(address);
                }
            }
            if (message.CC != null)
            {
                foreach (MailAddress address in message.CC)
                {
                    recipients.Add(address);
                }
            }

            if (recipients.Count == 0)
            {
                throw new InvalidOperationException(SR.SmtpRecipientRequired);
            }

            _transport.IdentityRequired = false;  // everything completes on the same thread.

            try
            {
                InCall    = true;
                _timedOut = false;
                _timer    = new Timer(new TimerCallback(TimeOutCallback), null, Timeout, Timeout);
                bool   allowUnicode    = false;
                string?pickupDirectory = PickupDirectoryLocation;

                MailWriter writer;
                switch (DeliveryMethod)
                {
                case SmtpDeliveryMethod.PickupDirectoryFromIis:
                    throw new NotSupportedException(SR.SmtpGetIisPickupDirectoryNotSupported);

                case SmtpDeliveryMethod.SpecifiedPickupDirectory:
                    if (EnableSsl)
                    {
                        throw new SmtpException(SR.SmtpPickupDirectoryDoesnotSupportSsl);
                    }

                    allowUnicode = IsUnicodeSupported();     // Determend by the DeliveryFormat paramiter
                    ValidateUnicodeRequirement(message, recipients, allowUnicode);
                    writer = GetFileMailWriter(pickupDirectory);
                    break;

                case SmtpDeliveryMethod.Network:
                default:
                    GetConnection();
                    // Detected durring GetConnection(), restrictable using the DeliveryFormat paramiter
                    allowUnicode = IsUnicodeSupported();
                    ValidateUnicodeRequirement(message, recipients, allowUnicode);
                    writer = _transport.SendMail(message.Sender ?? message.From, recipients,
                                                 message.BuildDeliveryStatusNotificationString(), allowUnicode, out recipientException);
                    break;
                }
                _message = message;
                message.Send(writer, DeliveryMethod != SmtpDeliveryMethod.Network, allowUnicode);
                writer.Close();

                //throw if we couldn't send to any of the recipients
                if (DeliveryMethod == SmtpDeliveryMethod.Network && recipientException != null)
                {
                    throw recipientException;
                }
            }
            catch (Exception e)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(this, e);
                }

                if (e is SmtpFailedRecipientException && !((SmtpFailedRecipientException)e).fatal)
                {
                    throw;
                }

                Abort();
                if (_timedOut)
                {
                    throw new SmtpException(SR.net_timeout);
                }

                if (e is SecurityException ||
                    e is AuthenticationException ||
                    e is SmtpException)
                {
                    throw;
                }

                throw new SmtpException(SR.SmtpSendMailFailure, e);
            }
            finally
            {
                InCall = false;
                if (_timer != null)
                {
                    _timer.Dispose();
                }
            }
        }
        public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection?additionalCertificates, bool offline = false)
        {
            if (!target.HasPrivateKey)
            {
                throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
            }

            X509Certificate2[] intermediates = Array.Empty <X509Certificate2>();
            using (X509Chain chain = new X509Chain())
            {
                if (additionalCertificates != null)
                {
                    foreach (X509Certificate cert in additionalCertificates)
                    {
                        chain.ChainPolicy.ExtraStore.Add(cert);
                    }
                }

                chain.ChainPolicy.VerificationFlags           = X509VerificationFlags.AllFlags;
                chain.ChainPolicy.RevocationMode              = X509RevocationMode.NoCheck;
                chain.ChainPolicy.DisableCertificateDownloads = offline;
                bool chainStatus = chain.Build(target);

                if (!chainStatus && NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(null, $"Failed to build chain for {target.Subject}");
                }

                int count = chain.ChainElements.Count - (TrimRootCertificate ? 1 : 2);
                foreach (X509ChainStatus status in chain.ChainStatus)
                {
                    if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain))
                    {
                        // The last cert isn't a root cert
                        count++;
                        break;
                    }
                }

                // Count can be zero for a self-signed certificate, or a cert issued directly from a root.
                if (count > 0 && chain.ChainElements.Count > 1)
                {
                    intermediates = new X509Certificate2[count];
                    for (int i = 0; i < count; i++)
                    {
                        intermediates[i] = chain.ChainElements[i + 1].Certificate;
                    }
                }

                // Dispose the copy of the target cert.
                chain.ChainElements[0].Certificate.Dispose();

                // Dispose the last cert, if we didn't include it.
                for (int i = count + 1; i < chain.ChainElements.Count; i++)
                {
                    chain.ChainElements[i].Certificate.Dispose();
                }
            }

            return(new SslStreamCertificateContext(target, intermediates));
        }
        // We need at least 5 bytes to determine what we have.
        private Framing DetectFraming(byte[] bytes, int length)
        {
            /* PCTv1.0 Hello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * PCT1_CLIENT_HELLO  (must be equal)
             * PCT1_CLIENT_VERSION_MSB (if version greater than PCTv1)
             * PCT1_CLIENT_VERSION_LSB (if version greater than PCTv1)
             *
             * ... PCT hello ...
             */

            /* Microsoft Unihello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 Compatible Hello ...
             */

            /* SSLv2 CLIENT_HELLO starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 CLIENT_HELLO ...
             */

            /* SSLv2 SERVER_HELLO starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_SERVER_HELLO  (must be equal)
             * SSL2_SESSION_ID_HIT (ignore)
             * SSL2_CERTIFICATE_TYPE (ignore)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3)
             *
             * ... SSLv2 SERVER_HELLO ...
             */

            /* SSLv3 Type 2 Hello starts with
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * SSL2_CLIENT_HELLO  (must be equal)
             * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv3)
             * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv3)
             *
             * ... SSLv2 Compatible Hello ...
             */

            /* SSLv3 Type 3 Hello starts with
             * 22 (HANDSHAKE MESSAGE)
             * VERSION MSB
             * VERSION LSB
             * RECORD_LENGTH_MSB  (ignore)
             * RECORD_LENGTH_LSB  (ignore)
             * HS TYPE (CLIENT_HELLO)
             * 3 bytes HS record length
             * HS Version
             * HS Version
             */

            /* SSLv2 message codes
             * SSL_MT_ERROR                0
             * SSL_MT_CLIENT_HELLO         1
             * SSL_MT_CLIENT_MASTER_KEY    2
             * SSL_MT_CLIENT_FINISHED      3
             * SSL_MT_SERVER_HELLO         4
             * SSL_MT_SERVER_VERIFY        5
             * SSL_MT_SERVER_FINISHED      6
             * SSL_MT_REQUEST_CERTIFICATE  7
             * SSL_MT_CLIENT_CERTIFICATE   8
             */

            int version = -1;

            if ((bytes == null || bytes.Length <= 0))
            {
                NetEventSource.Fail(this, "Header buffer is not allocated.");
            }

            // If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello.
            if (bytes[0] == (byte)FrameType.Handshake || bytes[0] == (byte)FrameType.AppData ||
                bytes[0] == (byte)FrameType.Alert)
            {
                if (length < 3)
                {
                    return(Framing.Invalid);
                }

#if TRACE_VERBOSE
                if (bytes[1] != 3 && NetEventSource.IsEnabled)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[1]:x}");
                    }
                }
#endif

                version = (bytes[1] << 8) | bytes[2];
                if (version < 0x300 || version >= 0x500)
                {
                    return(Framing.Invalid);
                }

                //
                // This is an SSL3 Framing
                //
                return(Framing.SinceSSL3);
            }

#if TRACE_VERBOSE
            if ((bytes[0] & 0x80) == 0 && NetEventSource.IsEnabled)
            {
                // We have a three-byte header format
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL v <=2 HELLO has no high bit set for 3 bytes header, we are broken, received byte = {bytes[0]:x}");
                }
            }
#endif

            if (length < 3)
            {
                return(Framing.Invalid);
            }

            if (bytes[2] > 8)
            {
                return(Framing.Invalid);
            }

            if (bytes[2] == 0x1)  // SSL_MT_CLIENT_HELLO
            {
                if (length >= 5)
                {
                    version = (bytes[3] << 8) | bytes[4];
                }
            }
            else if (bytes[2] == 0x4) // SSL_MT_SERVER_HELLO
            {
                if (length >= 7)
                {
                    version = (bytes[5] << 8) | bytes[6];
                }
            }

            if (version != -1)
            {
                // If this is the first packet, the client may start with an SSL2 packet
                // but stating that the version is 3.x, so check the full range.
                // For the subsequent packets we assume that an SSL2 packet should have a 2.x version.
                if (_framing == Framing.Unknown)
                {
                    if (version != 0x0002 && (version < 0x200 || version >= 0x500))
                    {
                        return(Framing.Invalid);
                    }
                }
                else
                {
                    if (version != 0x0002)
                    {
                        return(Framing.Invalid);
                    }
                }
            }

            // When server has replied the framing is already fixed depending on the prior client packet
            if (!_context.IsServer || _framing == Framing.Unified)
            {
                return(Framing.BeforeSSL3);
            }

            return(Framing.Unified); // Will use Ssl2 just for this frame.
        }
Beispiel #7
0
        // Connects the Client to the specified port on the specified host.
        public void Connect(string hostname, int port)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, hostname);
            }

            ThrowIfDisposed();

            if (hostname == null)
            {
                throw new ArgumentNullException(nameof(hostname));
            }
            if (!TcpValidationHelpers.ValidatePortNumber(port))
            {
                throw new ArgumentOutOfRangeException(nameof(port));
            }

            // Check for already connected and throw here. This check
            // is not required in the other connect methods as they
            // will throw from WinSock. Here, the situation is more
            // complex since we have to resolve a hostname so it's
            // easier to simply block the request up front.
            if (_active)
            {
                throw new SocketException((int)SocketError.IsConnected);
            }

            // IPv6: We need to process each of the addresses returned from
            //       DNS when trying to connect. Use of AddressList[0] is
            //       bad form.
            IPAddress[]           addresses = Dns.GetHostAddresses(hostname);
            ExceptionDispatchInfo lastex    = null;

            try
            {
                foreach (IPAddress address in addresses)
                {
                    try
                    {
                        if (_clientSocket == null)
                        {
                            // We came via the <hostname,port> constructor. Set the address family appropriately,
                            // create the socket and try to connect.
                            Debug.Assert(address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6);
                            if ((address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4) || Socket.OSSupportsIPv6)
                            {
                                var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                                // Use of Interlocked.Exchanged ensures _clientSocket is written before Disposed is read.
                                Interlocked.Exchange(ref _clientSocket, socket);
                                if (Disposed)
                                {
                                    // Dispose the socket so it throws ObjectDisposedException when we Connect.
                                    socket.Dispose();
                                }

                                try
                                {
                                    socket.Connect(address, port);
                                }
                                catch
                                {
                                    _clientSocket = null;
                                    throw;
                                }
                            }

                            _family = address.AddressFamily;
                            _active = true;
                            break;
                        }
                        else if (address.AddressFamily == _family || _family == AddressFamily.Unknown)
                        {
                            // Only use addresses with a matching family
                            Connect(new IPEndPoint(address, port));
                            _active = true;
                            break;
                        }
                    }
                    catch (Exception ex) when(!(ex is OutOfMemoryException))
                    {
                        lastex = ExceptionDispatchInfo.Capture(ex);
                    }
                }
            }
            finally
            {
                if (!_active)
                {
                    // The connect failed - rethrow the last error we had
                    lastex?.Throw();
                    throw new SocketException((int)SocketError.NotConnected);
                }
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
        }
        // return value indicates sync vs async completion
        // false: sync completion
        // true: async completion or with error
        private unsafe bool WriteAsyncFast(HttpListenerAsyncEventArgs eventArgs)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }

            Interop.HttpApi.HTTP_FLAGS flags = Interop.HttpApi.HTTP_FLAGS.NONE;

            eventArgs.StartOperationCommon(this, _outputStream.InternalHttpContext.RequestQueueBoundHandle);
            eventArgs.StartOperationSend();

            uint statusCode;
            bool completedAsynchronouslyOrWithError = false;

            try
            {
                if (_outputStream.Closed ||
                    (eventArgs.Buffer != null && eventArgs.Count == 0))
                {
                    eventArgs.FinishOperationSuccess(eventArgs.Count, true);
                    return(false);
                }

                if (eventArgs.ShouldCloseOutput)
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }
                else
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
                    // When using HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA HTTP.SYS will copy the payload to
                    // kernel memory (Non-Paged Pool). Http.Sys will buffer up to
                    // Math.Min(16 MB, current TCP window size)
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA;
                }

                uint bytesSent;
                statusCode =
                    Interop.HttpApi.HttpSendResponseEntityBody(
                        _outputStream.InternalHttpContext.RequestQueueHandle,
                        _outputStream.InternalHttpContext.RequestId,
                        (uint)flags,
                        eventArgs.EntityChunkCount,
                        (Interop.HttpApi.HTTP_DATA_CHUNK *)eventArgs.EntityChunks,
                        &bytesSent,
                        SafeLocalAllocHandle.Zero,
                        0,
                        eventArgs.NativeOverlapped,
                        null);

                if (statusCode != Interop.HttpApi.ERROR_SUCCESS &&
                    statusCode != Interop.HttpApi.ERROR_IO_PENDING)
                {
                    throw new HttpListenerException((int)statusCode);
                }
                else if (statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                         HttpListener.SkipIOCPCallbackOnSuccess)
                {
                    // IO operation completed synchronously - callback won't be called to signal completion.
                    eventArgs.FinishOperationSuccess((int)bytesSent, true);
                    completedAsynchronouslyOrWithError = false;
                }
                else
                {
                    completedAsynchronouslyOrWithError = true;
                }
            }
            catch (Exception e)
            {
                _writeEventArgs.FinishOperationFailure(e, true);
                _outputStream.SetClosedFlag();
                _outputStream.InternalHttpContext.Abort();

                completedAsynchronouslyOrWithError = true;
            }
            finally
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, completedAsynchronouslyOrWithError);
                }
            }

            return(completedAsynchronouslyOrWithError);
        }
Beispiel #9
0
        public static async Task <string> GetDigestTokenForCredential(NetworkCredential credential, HttpRequestMessage request, DigestResponse digestResponse)
        {
            StringBuilder sb = StringBuilderCache.Acquire();

            // It is mandatory for servers to implement sha-256 per RFC 7616
            // Keep MD5 for backward compatibility.
            string algorithm;
            bool   isAlgorithmSpecified = digestResponse.Parameters.TryGetValue(Algorithm, out algorithm);

            if (isAlgorithmSpecified)
            {
                if (!algorithm.Equals(Sha256, StringComparison.OrdinalIgnoreCase) &&
                    !algorithm.Equals(Md5, StringComparison.OrdinalIgnoreCase) &&
                    !algorithm.Equals(Sha256Sess, StringComparison.OrdinalIgnoreCase) &&
                    !algorithm.Equals(MD5Sess, StringComparison.OrdinalIgnoreCase))
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Error(digestResponse, "Algorithm not supported: {algorithm}");
                    }
                    return(null);
                }
            }
            else
            {
                algorithm = Md5;
            }

            // Check if nonce is there in challenge
            string nonce;

            if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(digestResponse, "Nonce missing");
                }
                return(null);
            }

            // opaque token may or may not exist
            string opaque;

            digestResponse.Parameters.TryGetValue(Opaque, out opaque);

            string realm;

            if (!digestResponse.Parameters.TryGetValue(Realm, out realm))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(digestResponse, "Realm missing");
                }
                return(null);
            }

            // Add username
            string userhash;

            if (digestResponse.Parameters.TryGetValue(UserHash, out userhash) && userhash == "true")
            {
                sb.AppendKeyValue(Username, ComputeHash(credential.UserName + ":" + realm, algorithm));
                sb.AppendKeyValue(UserHash, userhash, includeQuotes: false);
            }
            else
            {
                if (HeaderUtilities.ContainsNonAscii(credential.UserName))
                {
                    string usernameStar = HeaderUtilities.Encode5987(credential.UserName);
                    sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false);
                }
                else
                {
                    sb.AppendKeyValue(Username, credential.UserName);
                }
            }

            // Add realm
            if (realm != string.Empty)
            {
                sb.AppendKeyValue(Realm, realm);
            }

            // Add nonce
            sb.AppendKeyValue(Nonce, nonce);

            // Add uri
            sb.AppendKeyValue(Uri, request.RequestUri.PathAndQuery);

            // Set qop, default is auth
            string qop            = Auth;
            bool   isQopSpecified = digestResponse.Parameters.ContainsKey(Qop);

            if (isQopSpecified)
            {
                // Check if auth-int present in qop string
                int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt, StringComparison.Ordinal);
                if (index1 != -1)
                {
                    // Get index of auth if present in qop string
                    int index2 = digestResponse.Parameters[Qop].IndexOf(Auth, StringComparison.Ordinal);

                    // If index2 < index1, auth option is available
                    // If index2 == index1, check if auth option available later in string after auth-int.
                    if (index2 == index1)
                    {
                        index2 = digestResponse.Parameters[Qop].IndexOf(Auth, index1 + AuthInt.Length, StringComparison.Ordinal);
                        if (index2 == -1)
                        {
                            qop = AuthInt;
                        }
                    }
                }
            }

            // Set cnonce
            string cnonce = GetRandomAlphaNumericString();

            // Calculate response
            string a1 = credential.UserName + ":" + realm + ":" + credential.Password;

            if (algorithm.EndsWith("sess", StringComparison.OrdinalIgnoreCase))
            {
                a1 = ComputeHash(a1, algorithm) + ":" + nonce + ":" + cnonce;
            }

            string a2 = request.Method.Method + ":" + request.RequestUri.PathAndQuery;

            if (qop == AuthInt)
            {
                string content = request.Content == null ? string.Empty : await request.Content.ReadAsStringAsync().ConfigureAwait(false);

                a2 = a2 + ":" + ComputeHash(content, algorithm);
            }

            string response;

            if (isQopSpecified)
            {
                response = ComputeHash(ComputeHash(a1, algorithm) + ":" +
                                       nonce + ":" +
                                       DigestResponse.NonceCount + ":" +
                                       cnonce + ":" +
                                       qop + ":" +
                                       ComputeHash(a2, algorithm), algorithm);
            }
            else
            {
                response = ComputeHash(ComputeHash(a1, algorithm) + ":" +
                                       nonce + ":" +
                                       ComputeHash(a2, algorithm), algorithm);
            }

            // Add response
            sb.AppendKeyValue(Response, response, includeComma: opaque != null || isAlgorithmSpecified || isQopSpecified);

            // Add opaque
            if (opaque != null)
            {
                sb.AppendKeyValue(Opaque, opaque, includeComma: isAlgorithmSpecified || isQopSpecified);
            }

            if (isAlgorithmSpecified)
            {
                // Add algorithm
                sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false, includeComma: isQopSpecified);
            }

            if (isQopSpecified)
            {
                // Add qop
                sb.AppendKeyValue(Qop, qop, includeQuotes: false);

                // Add nc
                sb.AppendKeyValue(NC, DigestResponse.NonceCount, includeQuotes: false);

                // Add cnonce
                sb.AppendKeyValue(CNonce, cnonce, includeComma: false);
            }

            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Beispiel #10
0
        private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);

            if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response))
            {
                // Proxy didn't indicate that it supports connection-based auth, so we can't proceed.
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}");
                }
                return(response);
            }

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                if (challenge.AuthenticationType == AuthenticationType.Negotiate ||
                    challenge.AuthenticationType == AuthenticationType.Ntlm)
                {
                    bool isNewConnection = false;
                    bool needDrain       = true;
                    try
                    {
                        if (response.Headers.ConnectionClose.GetValueOrDefault())
                        {
                            // Server is closing the connection and asking us to authenticate on a new connection.

                            // First, detach the current connection from the pool. This means it will no longer count against the connection limit.
                            // Instead, it will be replaced by the new connection below.
                            connection.DetachFromPool();

                            connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false);

                            connection !.Acquire();
                            isNewConnection = true;
                            needDrain       = false;
                        }

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri}");
                        }

                        // Calculate SPN (Service Principal Name) using the host name of the request.
                        // Use the request's 'Host' header if available. Otherwise, use the request uri.
                        // Ignore the 'Host' header if this is proxy authentication since we need to use
                        // the host name of the proxy itself for SPN calculation.
                        string hostName;
                        if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null)
                        {
                            // Use the host name without any normalization.
                            hostName = request.Headers.Host;
                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}");
                            }
                        }
                        else
                        {
                            // Need to use FQDN normalized host so that CNAME's are traversed.
                            // Use DNS to do the forward lookup to an A (host) record.
                            // But skip DNS lookup on IP literals. Otherwise, we would end up
                            // doing an unintended reverse DNS lookup.
                            UriHostNameType hnt = authUri.HostNameType;
                            if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4)
                            {
                                hostName = authUri.IdnHost;
                            }
                            else
                            {
                                IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost, cancellationToken).ConfigureAwait(false);

                                hostName = result.HostName;
                            }

                            if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn)
                            {
                                hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}");
                            }
                        }

                        string spn = "HTTP/" + hostName;
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}");
                        }

                        ChannelBinding?  channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
                        NTAuthentication authContext    = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, channelBinding);
                        string?          challengeData  = challenge.ChallengeData;
                        try
                        {
                            while (true)
                            {
                                string?challengeResponse = authContext.GetOutgoingBlob(challengeData);
                                if (challengeResponse == null)
                                {
                                    // Response indicated denial even after login, so stop processing and return current response.
                                    break;
                                }

                                if (needDrain)
                                {
                                    await connection.DrainResponseAsync(response !, cancellationToken).ConfigureAwait(false);
                                }

                                SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);

                                response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);

                                if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
                                {
                                    break;
                                }

                                needDrain = true;
                            }
                        }
                        finally
                        {
                            authContext.CloseContext();
                        }
                    }
                    finally
                    {
                        if (isNewConnection)
                        {
                            connection !.Release();
                        }
                    }
                }
            }

            return(response !);
        }
Beispiel #11
0
        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.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "Using autoconfig proxy settings");
                    }
                    useProxy = true;

                    break;
                }
                else
                {
                    var lastError = Marshal.GetLastWin32Error();
                    if (NetEventSource.Log.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);
            private SocketError InnerReleaseHandle()
            {
                SocketError errorCode;

                // If _blockable was set in BlockingRelease, it's safe to block here, which means
                // we can honor the linger options set on the socket.  It also means closesocket() might return WSAEWOULDBLOCK, in which
                // case we need to do some recovery.
                if (_blockable)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"handle:{handle}, Following 'blockable' branch");
                    }
                    errorCode = Interop.Winsock.closesocket(handle);
#if DEBUG
                    _closeSocketHandle = handle;
                    _closeSocketResult = errorCode;
#endif
                    if (errorCode == SocketError.SocketError)
                    {
                        errorCode = (SocketError)Marshal.GetLastWin32Error();
                    }

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"handle:{handle}, closesocket()#1:{errorCode}");
                    }

                    // If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed.
                    if (errorCode != SocketError.WouldBlock)
                    {
                        return(errorCode);
                    }

                    // The socket must be non-blocking with a linger timeout set.
                    // We have to set the socket to blocking.
                    int nonBlockCmd = 0;
                    errorCode = Interop.Winsock.ioctlsocket(
                        handle,
                        Interop.Winsock.IoctlSocketConstants.FIONBIO,
                        ref nonBlockCmd);
                    if (errorCode == SocketError.SocketError)
                    {
                        errorCode = (SocketError)Marshal.GetLastWin32Error();
                    }

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, $"handle:{handle}, ioctlsocket()#1:{errorCode}");
                    }

                    // If that succeeded, try again.
                    if (errorCode == SocketError.Success)
                    {
                        errorCode = Interop.Winsock.closesocket(handle);
#if DEBUG
                        _closeSocketHandle = handle;
                        _closeSocketResult = errorCode;
#endif
                        if (errorCode == SocketError.SocketError)
                        {
                            errorCode = (SocketError)Marshal.GetLastWin32Error();
                        }
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(this, $"handle:{handle}, closesocket#2():{errorCode}");
                        }

                        // If it's not WSAEWOULDBLOCK, there's no more recourse - we either succeeded or failed.
                        if (errorCode != SocketError.WouldBlock)
                        {
                            return(errorCode);
                        }
                    }

                    // It failed.  Fall through to the regular abortive close.
                }

                // By default or if CloseAsIs() path failed, set linger timeout to zero to get an abortive close (RST).
                Interop.Winsock.Linger lingerStruct;
                lingerStruct.OnOff = 1;
                lingerStruct.Time  = 0;

                errorCode = Interop.Winsock.setsockopt(
                    handle,
                    SocketOptionLevel.Socket,
                    SocketOptionName.Linger,
                    ref lingerStruct,
                    4);
#if DEBUG
                _closeSocketLinger = errorCode;
#endif
                if (errorCode == SocketError.SocketError)
                {
                    errorCode = (SocketError)Marshal.GetLastWin32Error();
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"handle:{handle}, setsockopt():{errorCode}");
                }

                if (errorCode != SocketError.Success && errorCode != SocketError.InvalidArgument && errorCode != SocketError.ProtocolOption)
                {
                    // Too dangerous to try closesocket() - it might block!
                    return(errorCode);
                }

                errorCode = Interop.Winsock.closesocket(handle);
#if DEBUG
                _closeSocketHandle = handle;
                _closeSocketResult = errorCode;
#endif
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"handle:{handle}, closesocket#3():{(errorCode == SocketError.SocketError ? (SocketError)Marshal.GetLastWin32Error() : errorCode)}");
                }

                return(errorCode);
            }
        private async ValueTask <int> ReadAsyncInternal <TReadAdapter>(TReadAdapter adapter, Memory <byte> buffer)
            where TReadAdapter : ISslReadAdapter
        {
            if (Interlocked.Exchange(ref _nestedRead, 1) == 1)
            {
                throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(SslStream.ReadAsync), "read"));
            }

            try
            {
                while (true)
                {
                    int copyBytes;
                    if (_decryptedBytesCount != 0)
                    {
                        copyBytes = CopyDecryptedData(buffer);

                        return(copyBytes);
                    }

                    copyBytes = await adapter.LockAsync(buffer).ConfigureAwait(false);

                    if (copyBytes > 0)
                    {
                        return(copyBytes);
                    }

                    ResetReadBuffer();
                    int readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false);

                    if (readBytes == 0)
                    {
                        return(0);
                    }

                    int payloadBytes = GetRemainingFrameSize(_internalBuffer, _internalOffset, readBytes);
                    if (payloadBytes < 0)
                    {
                        throw new IOException(SR.net_frame_read_size);
                    }

                    readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize + payloadBytes).ConfigureAwait(false);

                    Debug.Assert(readBytes >= 0);
                    if (readBytes == 0)
                    {
                        throw new IOException(SR.net_io_eof);
                    }

                    // At this point, readBytes contains the size of the header plus body.
                    // Set _decrytpedBytesOffset/Count to the current frame we have (including header)
                    // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller.
                    _decryptedBytesOffset = _internalOffset;
                    _decryptedBytesCount  = readBytes;
                    SecurityStatusPal status = DecryptData();

                    // Treat the bytes we just decrypted as consumed
                    // Note, we won't do another buffer read until the decrypted bytes are processed
                    ConsumeBufferedBytes(readBytes);

                    if (status.ErrorCode != SecurityStatusPalErrorCode.OK)
                    {
                        byte[] extraBuffer = null;
                        if (_decryptedBytesCount != 0)
                        {
                            extraBuffer = new byte[_decryptedBytesCount];
                            Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount);

                            _decryptedBytesCount = 0;
                        }

                        ProtocolToken message = new ProtocolToken(null, status);
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(null, $"***Processing an error Status = {message.Status}");
                        }

                        if (message.Renegotiate)
                        {
                            if (!_sslAuthenticationOptions.AllowRenegotiation)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    NetEventSource.Fail(this, "Renegotiation was requested but it is disallowed");
                                }
                                throw new IOException(SR.net_ssl_io_renego);
                            }

                            await ReplyOnReAuthenticationAsync(extraBuffer, adapter.CancellationToken).ConfigureAwait(false);

                            // Loop on read.
                            continue;
                        }

                        if (message.CloseConnection)
                        {
                            return(0);
                        }

                        throw new IOException(SR.net_io_decrypt, message.GetException());
                    }
                }
            }
            catch (Exception e)
            {
                if (e is IOException || (e is OperationCanceledException && adapter.CancellationToken.IsCancellationRequested))
                {
                    throw;
                }

                throw new IOException(SR.net_io_read, e);
            }
            finally
            {
                _nestedRead = 0;
            }
        }
        private async Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, HttpWebSocket.GetTraceMsgForParameters(offset, count, cancellationToken));
            }

            CancellationTokenRegistration cancellationTokenRegistration = default;

            int bytesRead = 0;

            try
            {
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationTokenRegistration = cancellationToken.Register(s_OnCancel, this, false);
                }

                if (!_inOpaqueMode)
                {
                    bytesRead = await _inputStream.ReadAsync(buffer, offset, count, cancellationToken).SuppressContextFlow <int>();
                }
                else
                {
#if DEBUG
                    // When using fast path only one outstanding read is permitted. By switching into opaque mode
                    // via IWebSocketStream.SwitchToOpaqueMode (see more detailed comments in interface definition)
                    // caller takes responsibility for enforcing this constraint.
                    Debug.Assert(Interlocked.Increment(ref _outstandingOperations._reads) == 1,
                                 "Only one outstanding read allowed at any given time.");
#endif
                    _readTaskCompletionSource = new TaskCompletionSource <int>();
                    _readEventArgs.SetBuffer(buffer, offset, count);
                    if (!ReadAsyncFast(_readEventArgs))
                    {
                        if (_readEventArgs.Exception != null)
                        {
                            throw _readEventArgs.Exception;
                        }

                        bytesRead = _readEventArgs.BytesTransferred;
                    }
                    else
                    {
                        bytesRead = await _readTaskCompletionSource.Task.SuppressContextFlow <int>();
                    }
                }
            }
            catch (Exception error)
            {
                if (s_CanHandleException(error))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                throw;
            }
            finally
            {
                cancellationTokenRegistration.Dispose();

                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, bytesRead);
                }
            }

            return(bytesRead);
        }
Beispiel #15
0
    static MsQuicApi()
    {
        IntPtr msQuicHandle;

        if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) &&
            !NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
        {
            return;
        }

        try
        {
            if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
            {
                return;
            }

            QUIC_API_TABLE *apiTable = null;
            delegate * unmanaged[Cdecl] < uint, QUIC_API_TABLE **, int > msQuicOpenVersion = (delegate * unmanaged[Cdecl] < uint, QUIC_API_TABLE **, int >)msQuicOpenVersionAddress;
            if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable)))
            {
                return;
            }

            try
            {
                int   arraySize  = 4;
                uint *libVersion = stackalloc uint[arraySize];
                uint  size       = (uint)arraySize * sizeof(uint);
                if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
                {
                    return;
                }

                var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
                if (version < MsQuicVersion)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
                    }
                    return;
                }

                // Assume SChannel is being used on windows and query for the actual provider from the library
                QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
                size = sizeof(QUIC_TLS_PROVIDER);
                apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
                UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;

                if (UsesSChannelBackend)
                {
                    // Implies windows platform, check TLS1.3 availability
                    if (!IsWindowsVersionSupported())
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
                        }

                        return;
                    }

                    Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
                    Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
                }

                Api             = new MsQuicApi(apiTable);
                IsQuicSupported = true;
            }
            finally
            {
                if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
                {
                    // Gracefully close the API table
                    ((delegate * unmanaged[Cdecl] < QUIC_API_TABLE *, void >)msQuicClose)(apiTable);
                }
            }
        }
        finally
        {
            if (!IsQuicSupported)
            {
                NativeLibrary.Free(msQuicHandle);
            }
        }
    }
        // return value indicates sync vs async completion
        // false: sync completion
        // true: async completion or error
        private unsafe bool ReadAsyncFast(HttpListenerAsyncEventArgs eventArgs)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }

            eventArgs.StartOperationCommon(this, _inputStream.InternalHttpContext.RequestQueueBoundHandle);
            eventArgs.StartOperationReceive();

            uint statusCode = 0;
            bool completedAsynchronouslyOrWithError = false;

            try
            {
                Debug.Assert(eventArgs.Buffer != null, "'BufferList' is not supported for read operations.");
                if (eventArgs.Count == 0 || _inputStream.Closed)
                {
                    eventArgs.FinishOperationSuccess(0, true);
                    return(false);
                }

                uint dataRead       = 0;
                int  offset         = eventArgs.Offset;
                int  remainingCount = eventArgs.Count;

                if (_inputStream.BufferedDataChunksAvailable)
                {
                    dataRead = _inputStream.GetChunks(eventArgs.Buffer, eventArgs.Offset, eventArgs.Count);
                    if (_inputStream.BufferedDataChunksAvailable && dataRead == eventArgs.Count)
                    {
                        eventArgs.FinishOperationSuccess(eventArgs.Count, true);
                        return(false);
                    }
                }

                Debug.Assert(!_inputStream.BufferedDataChunksAvailable, "'m_InputStream.BufferedDataChunksAvailable' MUST BE 'FALSE' at this point.");
                Debug.Assert(dataRead <= eventArgs.Count, "'dataRead' MUST NOT be bigger than 'eventArgs.Count'.");

                if (dataRead != 0)
                {
                    offset         += (int)dataRead;
                    remainingCount -= (int)dataRead;
                    //the http.sys team recommends that we limit the size to 128kb
                    if (remainingCount > HttpRequestStream.MaxReadSize)
                    {
                        remainingCount = HttpRequestStream.MaxReadSize;
                    }

                    eventArgs.SetBuffer(eventArgs.Buffer, offset, remainingCount);
                }
                else if (remainingCount > HttpRequestStream.MaxReadSize)
                {
                    remainingCount = HttpRequestStream.MaxReadSize;
                    eventArgs.SetBuffer(eventArgs.Buffer, offset, remainingCount);
                }

                uint flags         = 0;
                uint bytesReturned = 0;
                statusCode =
                    Interop.HttpApi.HttpReceiveRequestEntityBody(
                        _inputStream.InternalHttpContext.RequestQueueHandle,
                        _inputStream.InternalHttpContext.RequestId,
                        flags,
                        (byte *)_webSocket.InternalBuffer.ToIntPtr(eventArgs.Offset),
                        (uint)eventArgs.Count,
                        out bytesReturned,
                        eventArgs.NativeOverlapped);

                if (statusCode != Interop.HttpApi.ERROR_SUCCESS &&
                    statusCode != Interop.HttpApi.ERROR_IO_PENDING &&
                    statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
                {
                    throw new HttpListenerException((int)statusCode);
                }
                else if (statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                         HttpListener.SkipIOCPCallbackOnSuccess)
                {
                    // IO operation completed synchronously. No IO completion port callback is used because
                    // it was disabled in SwitchToOpaqueMode()
                    eventArgs.FinishOperationSuccess((int)bytesReturned, true);
                    completedAsynchronouslyOrWithError = false;
                }
                else if (statusCode == Interop.HttpApi.ERROR_HANDLE_EOF)
                {
                    eventArgs.FinishOperationSuccess(0, true);
                    completedAsynchronouslyOrWithError = false;
                }
                else
                {
                    completedAsynchronouslyOrWithError = true;
                }
            }
            catch (Exception e)
            {
                _readEventArgs.FinishOperationFailure(e, true);
                _outputStream.SetClosedFlag();
                _outputStream.InternalHttpContext.Abort();

                completedAsynchronouslyOrWithError = true;
            }
            finally
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this, completedAsynchronouslyOrWithError);
                }
            }

            return(completedAsynchronouslyOrWithError);
        }
Beispiel #17
0
        private unsafe SocketError DoCloseHandle(bool abortive)
        {
            Interop.Error errorCode = Interop.Error.SUCCESS;

            // If abortive is not set, we're not running on the finalizer thread, so it's safe to block here.
            // We can honor the linger options set on the socket.  It also means closesocket() might return
            // EWOULDBLOCK, in which case we need to do some recovery.
            if (!abortive)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, $"handle:{handle} Following 'non-abortive' branch.");
                }

                // Close, and if its errno is other than EWOULDBLOCK, there's nothing more to do - we either succeeded or failed.
                errorCode = CloseHandle(handle);
                if (errorCode != Interop.Error.EWOULDBLOCK)
                {
                    return(SocketPal.GetSocketErrorForErrorCode(errorCode));
                }

                // The socket must be non-blocking with a linger timeout set.
                // We have to set the socket to blocking.
                if (Interop.Sys.Fcntl.DangerousSetIsNonBlocking(handle, 0) == 0)
                {
                    // The socket successfully made blocking; retry the close().
                    return(SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)));
                }

                // The socket could not be made blocking; fall through to the regular abortive close.
            }

            // By default or if the non-abortive path failed, set linger timeout to zero to get an abortive close (RST).
            var linger = new Interop.Sys.LingerOption
            {
                OnOff   = 1,
                Seconds = 0
            };

            errorCode = Interop.Sys.SetLingerOption(handle, &linger);
#if DEBUG
            _closeSocketLinger = SocketPal.GetSocketErrorForErrorCode(errorCode);
#endif
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"handle:{handle}, setsockopt():{errorCode}");
            }

            switch (errorCode)
            {
            case Interop.Error.SUCCESS:
            case Interop.Error.EINVAL:
            case Interop.Error.ENOPROTOOPT:
                errorCode = CloseHandle(handle);
                break;

                // For other errors, it's too dangerous to try closesocket() - it might block!
            }

            return(SocketPal.GetSocketErrorForErrorCode(errorCode));
        }
        private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle sessionHandle)
        {
            _proxyHelper   = proxyHelper;
            _sessionHandle = sessionHandle;

            if (proxyHelper.ManualSettingsUsed)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(proxyHelper, $"ManualSettingsUsed, {proxyHelper.Proxy}");
                }
                ParseProxyConfig(proxyHelper.Proxy, out _insecureProxyUri, out _secureProxyUri);
                if (_insecureProxyUri == null && _secureProxyUri == null)
                {
                    // If advanced parsing by protocol fails, fall-back to simplified parsing.
                    _insecureProxyUri = _secureProxyUri = GetUriFromString(proxyHelper.Proxy);
                }

                if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass))
                {
                    int    idx   = 0;
                    int    start = 0;
                    string tmp;

                    // Process bypass list for manual setting.
                    // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average.
                    _bypass = new List <Regex>(proxyHelper.ProxyBypass.Length / 5);

                    while (idx < proxyHelper.ProxyBypass.Length)
                    {
                        // Strip leading spaces and scheme if any.
                        while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ')
                        {
                            idx += 1;
                        }
                        ;
                        if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            idx += 7;
                        }
                        else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            idx += 8;
                        }

                        if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[')
                        {
                            // Strip [] from IPv6 so we can use IdnHost laster for matching.
                            idx += 1;
                        }

                        start = idx;
                        while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']')
                        {
                            idx += 1;
                        }
                        ;

                        if (idx == start)
                        {
                            // Empty string.
                            tmp = null;
                        }
                        else if (string.Compare(proxyHelper.ProxyBypass, start, "<local>", 0, 7, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            _bypassLocal = true;
                            tmp          = null;
                        }
                        else
                        {
                            tmp = proxyHelper.ProxyBypass.Substring(start, idx - start);
                        }

                        // Skip trailing characters if any.
                        if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';')
                        {
                            // Got stopped at space or ']'. Strip until next ';' or end.
                            while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';')
                            {
                                idx += 1;
                            }
                            ;
                        }
                        if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';')
                        {
                            idx++;
                        }
                        if (tmp == null)
                        {
                            continue;
                        }

                        try
                        {
                            // Escape any special characters and unescape * to get wildcard pattern match.
                            Regex re = new Regex(Regex.Escape(tmp).Replace("\\*", ".*?") + "$",
                                                 RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
                            _bypass.Add(re);
                        }
                        catch (Exception ex)
                        {
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.Error(this, $"Failed to process {tmp} from bypass list: {ex}");
                            }
                        }
                    }
                    if (_bypass.Count == 0)
                    {
                        // Bypass string only had garbage we did not parse.
                        _bypass = null;
                    }
                }

                if (_bypassLocal)
                {
                    _localIp = new List <IPAddress>();
                    foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
                    {
                        IPInterfaceProperties ipProps = netInterface.GetIPProperties();
                        foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
                        {
                            _localIp.Add(addr.Address);
                        }
                    }
                }
            }
        }
        protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, request, cancellationToken);
            }

            HttpResponseMessage response = await _initialInnerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);

            uint redirectCount = 0;
            Uri  redirectUri;

            while ((redirectUri = GetUriForRedirect(request.RequestUri, response)) != null)
            {
                redirectCount++;

                if (redirectCount > _maxAutomaticRedirections)
                {
                    // If we exceed the maximum number of redirects
                    // then just return the 3xx response.
                    if (NetEventSource.IsEnabled)
                    {
                        TraceError($"Exceeded max number of redirects. Redirect from {request.RequestUri} to {redirectUri} blocked.", request.GetHashCode());
                    }

                    break;
                }

                response.Dispose();

                // Clear the authorization header.
                request.Headers.Authorization = null;

                if (NetEventSource.IsEnabled)
                {
                    Trace($"Redirecting from {request.RequestUri} to {redirectUri} in response to status code {(int)response.StatusCode} '{response.StatusCode}'.", request.GetHashCode());
                }

                // Set up for the redirect
                request.RequestUri = redirectUri;
                if (RequestRequiresForceGet(response.StatusCode, request.Method))
                {
                    if (NetEventSource.IsEnabled)
                    {
                        Trace($"Modified request from {request.Method} to {HttpMethod.Get} in response to status code {(int)response.StatusCode} '{response.StatusCode}'.", request.GetHashCode());
                    }

                    request.Method  = HttpMethod.Get;
                    request.Content = null;
                    request.Headers.TransferEncodingChunked = false;
                }

                // Issue the redirected request.
                response = await _redirectInnerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false);
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }

            return(response);
        }
Beispiel #20
0
        internal static unsafe int InitializeSecurityContext(
            ref SafeFreeCredentials?inCredentials,
            ref SafeDeleteSslContext?refContext,
            string?targetName,
            Interop.SspiCli.ContextFlags inFlags,
            Interop.SspiCli.Endianness endianness,
            InputSecurityBuffers inSecBuffers,
            ref SecurityBuffer outSecBuffer,
            ref Interop.SspiCli.ContextFlags outFlags)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(null, $"credential:{inCredentials}, crefContext:{refContext}, targetName:{targetName}, inFlags:{inFlags}, endianness:{endianness}");
            }

            if (inCredentials == null)
            {
                throw new ArgumentNullException(nameof(inCredentials));
            }

            Debug.Assert(inSecBuffers.Count <= 3);
            Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor  = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Count);
            Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);

            // Actually, this is returned in outFlags.
            bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false;

            int errorCode = -1;

            bool isContextAbsent = true;

            if (refContext != null)
            {
                isContextAbsent = refContext._handle.IsZero;
            }

            // Optional output buffer that may need to be freed.
            SafeFreeContextBuffer?outFreeContextBuffer = null;

            try
            {
                Span <Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3];

                fixed(void *inUnmanagedBufferPtr = inUnmanagedBuffer)
                fixed(void *pinnedToken0 = inSecBuffers._item0.Token)
                fixed(void *pinnedToken1 = inSecBuffers._item1.Token)
                fixed(void *pinnedToken2 = inSecBuffers._item2.Token)
                {
                    // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
                    inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr;
                    // Updated pvBuffer with pinned address. UnmanagedToken takes precedence.
                    if (inSecBuffers.Count > 2)
                    {
                        inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type;
                        inUnmanagedBuffer[2].cbBuffer   = inSecBuffers._item2.Token.Length;
                        inUnmanagedBuffer[2].pvBuffer   = inSecBuffers._item2.UnmanagedToken != null ?
                                                          (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandle() :
                                                          (IntPtr)pinnedToken2;
                    }

                    if (inSecBuffers.Count > 1)
                    {
                        inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type;
                        inUnmanagedBuffer[1].cbBuffer   = inSecBuffers._item1.Token.Length;
                        inUnmanagedBuffer[1].pvBuffer   = inSecBuffers._item1.UnmanagedToken != null ?
                                                          (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandle() :
                                                          (IntPtr)pinnedToken1;
                    }

                    if (inSecBuffers.Count > 0)
                    {
                        inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type;
                        inUnmanagedBuffer[0].cbBuffer   = inSecBuffers._item0.Token.Length;
                        inUnmanagedBuffer[0].pvBuffer   = inSecBuffers._item0.UnmanagedToken != null ?
                                                          (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandle() :
                                                          (IntPtr)pinnedToken0;
                    }

                    fixed(byte *pinnedOutBytes = outSecBuffer.token)
                    {
                        // Fix Descriptor pointer that points to unmanaged SecurityBuffers.
                        Interop.SspiCli.SecBuffer outUnmanagedBuffer = default;
                        outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer;
                        outUnmanagedBuffer.cbBuffer          = outSecBuffer.size;
                        outUnmanagedBuffer.BufferType        = outSecBuffer.type;
                        outUnmanagedBuffer.pvBuffer          = outSecBuffer.token == null || outSecBuffer.token.Length == 0 ?
                                                               IntPtr.Zero :
                                                               (IntPtr)(pinnedOutBytes + outSecBuffer.offset);

                        if (isSspiAllocated)
                        {
                            outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle();
                        }

                        if (refContext == null || refContext.IsInvalid)
                        {
                            // Previous versions unconditionally built a new "refContext" here, but would pass
                            // incorrect arguments to InitializeSecurityContextW in cases where an "contextHandle" was
                            // already present and non-zero.
                            if (isContextAbsent)
                            {
                                refContext = new SafeDeleteSslContext();
                            }
                        }

                        if (targetName == null || targetName.Length == 0)
                        {
                            targetName = dummyStr;
                        }

                        string punyCode = s_idnMapping.GetAscii(targetName);

                        fixed(char *namePtr = punyCode)
                        {
                            errorCode = MustRunInitializeSecurityContext(
                                ref inCredentials,
                                isContextAbsent,
                                (byte *)(((object)targetName == (object)dummyStr) ? null : namePtr),
                                inFlags,
                                endianness,
                                &inSecurityBufferDescriptor,
                                refContext !,
                                ref outSecurityBufferDescriptor,
                                ref outFlags,
                                outFreeContextBuffer);
                        }

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(null, "Marshalling OUT buffer");
                        }

                        // Get unmanaged buffer with index 0 as the only one passed into PInvoke.
                        outSecBuffer.size  = outUnmanagedBuffer.cbBuffer;
                        outSecBuffer.type  = outUnmanagedBuffer.BufferType;
                        outSecBuffer.token = outSecBuffer.size > 0 ?
                                             new Span <byte>((byte *)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).ToArray() :
                                             null;
                    }
                }
            }
            finally
            {
                outFreeContextBuffer?.Dispose();
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}");
            }
            return(errorCode);
        }
Beispiel #21
0
        private static unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped *nativeOverlapped)
        {
#if DEBUG
            DebugThreadTracking.SetThreadSource(ThreadKinds.CompletionPort);
            using (DebugThreadTracking.SetThreadKind(ThreadKinds.System))
            {
#endif
            BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped);

            if (asyncResult.InternalPeekCompleted)
            {
                NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
            }
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(null, $"errorCode:{errorCode} numBytes:{numBytes} nativeOverlapped:{(IntPtr)nativeOverlapped}");
            }

            // Complete the IO and invoke the user's callback.
            SocketError socketError = (SocketError)errorCode;

            if (socketError != SocketError.Success && socketError != SocketError.OperationAborted)
            {
                // There are cases where passed errorCode does not reflect the details of the underlined socket error.
                // "So as of today, the key is the difference between WSAECONNRESET and ConnectionAborted,
                //  .e.g remote party or network causing the connection reset or something on the local host (e.g. closesocket
                // or receiving data after shutdown (SD_RECV)).  With Winsock/TCP stack rewrite in longhorn, there may
                // be other differences as well."

                Socket socket = asyncResult.AsyncObject as Socket;
                if (socket == null)
                {
                    socketError = SocketError.NotSocket;
                }
                else if (socket.CleanedUp)
                {
                    socketError = SocketError.OperationAborted;
                }
                else
                {
                    try
                    {
                        // The async IO completed with a failure.
                        // Here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error.
                        SocketFlags ignore;
                        bool        success = Interop.Winsock.WSAGetOverlappedResult(
                            socket.SafeHandle,
                            asyncResult._nativeOverlapped,
                            out numBytes,
                            false,
                            out ignore);
                        if (!success)
                        {
                            socketError = SocketPal.GetLastSocketError();
                        }
                        if (success)
                        {
                            NetEventSource.Fail(asyncResult, $"Unexpectedly succeeded. errorCode:{errorCode} numBytes:{numBytes}");
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // CleanedUp check above does not always work since this code is subject to race conditions
                        socketError = SocketError.OperationAborted;
                    }
                }
            }

            // Set results and invoke callback
            asyncResult.CompletionCallback((int)numBytes, socketError);
#if DEBUG
        }
#endif
        }