Esempio n. 1
0
        public Http3Connection(HttpConnectionPool pool, HttpAuthority?origin, HttpAuthority authority, QuicConnection connection)
        {
            _pool       = pool;
            _origin     = origin;
            _authority  = authority;
            _connection = connection;

            bool   altUsedDefaultPort = pool.Kind == HttpConnectionKind.Http && authority.Port == HttpConnectionPool.DefaultHttpPort || pool.Kind == HttpConnectionKind.Https && authority.Port == HttpConnectionPool.DefaultHttpsPort;
            string altUsedValue       = altUsedDefaultPort ? authority.IdnHost : authority.IdnHost + ":" + authority.Port.ToString(Globalization.CultureInfo.InvariantCulture);

            _altUsedEncodedHeader = QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(KnownHeaders.AltUsed.Name, altUsedValue);

            _maximumRequestStreams = _requestStreamsRemaining = connection.GetRemoteAvailableBidirectionalStreamCount();

            // Errors are observed via Abort().
            _ = SendSettingsAsync();

            // This process is cleaned up when _connection is disposed, and errors are observed via Abort().
            _ = AcceptStreamsAsync();
        }
Esempio n. 2
0
        public Http2Connection(HttpConnectionPool pool, SslStream stream)
        {
            _pool           = pool;
            _stream         = stream;
            _syncObject     = new object();
            _incomingBuffer = new ArrayBuffer(InitialConnectionBufferSize);
            _outgoingBuffer = new ArrayBuffer(InitialConnectionBufferSize);
            _headerBuffer   = new ArrayBuffer(InitialConnectionBufferSize);

            _hpackDecoder = new HPackDecoder();

            _httpStreams = new Dictionary <int, Http2Stream>();

            _writerLock        = new SemaphoreSlim(1, 1);
            _connectionWindow  = new CreditManager(DefaultInitialWindowSize);
            _concurrentStreams = new CreditManager(int.MaxValue);

            _nextStream           = 1;
            _initialWindowSize    = DefaultInitialWindowSize;
            _maxConcurrentStreams = int.MaxValue;
        }
Esempio n. 3
0
        public ValueTask <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri?proxyUri, bool async, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken)
        {
            HttpConnectionKey key = GetConnectionKey(request, proxyUri, isProxyConnect);

            HttpConnectionPool?pool;

            while (!_pools.TryGetValue(key, out pool))
            {
                pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer);

                if (_cleaningTimer == null)
                {
                    // There's no cleaning timer, which means we're not adding connections into pools, but we still need
                    // the pool object for this request.  We don't need or want to add the pool to the pools, though,
                    // since we don't want it to sit there forever, which it would without the cleaning timer.
                    break;
                }

                if (_pools.TryAdd(key, pool))
                {
                    // We need to ensure the cleanup timer is running if it isn't
                    // already now that we added a new connection pool.
                    lock (SyncObj)
                    {
                        if (!_timerIsRunning)
                        {
                            SetCleaningTimer(_cleanPoolTimeout);
                        }
                    }
                    break;
                }

                // We created a pool and tried to add it to our pools, but some other thread got there before us.
                // We don't need to Dispose the pool, as that's only needed when it contains connections
                // that need to be closed.
            }

            return(pool.SendAsync(request, async, doRequestAuth, cancellationToken));
        }
Esempio n. 4
0
        public HttpConnection(
            HttpConnectionPool pool,
            HttpConnectionKey key,
            Stream stream,
            TransportContext transportContext,
            bool usingProxy)
        {
            _pool             = pool;
            _key              = key;
            _stream           = stream;
            _transportContext = transportContext;
            _usingProxy       = usingProxy;

            _sb = new StringBuilder();

            _writeBuffer = new byte[BufferSize];
            _writeOffset = 0;

            _readBuffer = new byte[BufferSize];
            _readLength = 0;
            _readOffset = 0;

            _pool.AddConnection(this);
        }
Esempio n. 5
0
        public Http3Connection(HttpConnectionPool pool, HttpAuthority?origin, HttpAuthority authority, QuicConnection connection)
        {
            _pool       = pool;
            _origin     = origin;
            _authority  = authority;
            _connection = connection;

            bool   altUsedDefaultPort = pool.Kind == HttpConnectionKind.Http && authority.Port == HttpConnectionPool.DefaultHttpPort || pool.Kind == HttpConnectionKind.Https && authority.Port == HttpConnectionPool.DefaultHttpsPort;
            string altUsedValue       = altUsedDefaultPort ? authority.IdnHost : string.Create(CultureInfo.InvariantCulture, $"{authority.IdnHost}:{authority.Port}");

            _altUsedEncodedHeader = QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(KnownHeaders.AltUsed.Name, altUsedValue);

            if (HttpTelemetry.Log.IsEnabled())
            {
                HttpTelemetry.Log.Http30ConnectionEstablished();
                _markedByTelemetryStatus = TelemetryStatus_Opened;
            }

            // Errors are observed via Abort().
            _ = SendSettingsAsync();

            // This process is cleaned up when _connection is disposed, and errors are observed via Abort().
            _ = AcceptStreamsAsync();
        }
Esempio n. 6
0
 public static Task <HttpResponseMessage> SendWithRequestAuthAsync(HttpRequestMessage request, ICredentials credentials, bool preAuthenticate, HttpConnectionPool pool, CancellationToken cancellationToken)
 {
     return(SendWithAuthAsync(request, request.RequestUri, credentials, preAuthenticate, false, true, pool, cancellationToken));
 }
Esempio n. 7
0
 public static Task <HttpResponseMessage> SendWithProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
 {
     return(SendWithAuthAsync(request, proxyUri, proxyCredentials, false, true, doRequestAuth, pool, cancellationToken));
 }
Esempio n. 8
0
        private static async Task <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
        {
            if (preAuthenticate)
            {
                NetworkCredential credential = credentials.GetCredential(authUri, BasicScheme);
                if (credential != null)
                {
                    SetBasicAuthToken(request, credential, isProxyAuth);
                }
            }

            HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                if (challenge.AuthenticationType == AuthenticationType.Digest)
                {
                    var digestResponse = new DigestResponse(challenge.ChallengeData);
                    if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                    {
                        response.Dispose();
                        response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                        // Retry in case of nonce timeout in server.
                        if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string challengeData))
                        {
                            digestResponse = new DigestResponse(challengeData);
                            if (IsServerNonceStale(digestResponse) &&
                                await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                            {
                                response.Dispose();
                                response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }
                else if (challenge.AuthenticationType == AuthenticationType.Basic)
                {
                    if (!preAuthenticate)
                    {
                        SetBasicAuthToken(request, challenge.Credential, isProxyAuth);

                        response.Dispose();
                        response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                    }
                }
            }

            return(response);
        }
Esempio n. 9
0
 private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
 {
     return(isProxyAuth ?
            pool.SendWithRetryAsync(request, doRequestAuth, cancellationToken) :
            pool.SendWithProxyAuthAsync(request, doRequestAuth, cancellationToken));
 }
Esempio n. 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}");
                        }

                        ProtectionLevel requiredProtectionLevel = ProtectionLevel.None;
                        // When connecting to proxy server don't enforce the integrity to avoid
                        // compatibility issues. The assumption is that the proxy server comes
                        // from a trusted source. On macOS we always need to enforce the integrity
                        // to avoid the GSSAPI implementation generating corrupted authentication
                        // tokens.
                        if (!isProxyAuth || OperatingSystem.IsMacOS())
                        {
                            requiredProtectionLevel = ProtectionLevel.Sign;
                        }

                        NegotiateAuthenticationClientOptions authClientOptions = new NegotiateAuthenticationClientOptions
                        {
                            Package    = challenge.SchemeName,
                            Credential = challenge.Credential,
                            TargetName = spn,
                            RequiredProtectionLevel = requiredProtectionLevel,
                            Binding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint)
                        };

                        using NegotiateAuthentication authContext = new NegotiateAuthentication(authClientOptions);
                        string?challengeData = challenge.ChallengeData;
                        NegotiateAuthenticationStatusCode statusCode;
                        while (true)
                        {
                            string?challengeResponse = authContext.GetOutgoingBlob(challengeData, out statusCode);
                            if (statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded || 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.IsAuthenticated || !TryGetChallengeDataForScheme(challenge.SchemeName, GetResponseAuthenticationHeaderValues(response, isProxyAuth), out challengeData))
                            {
                                break;
                            }

                            if (!IsAuthenticationChallenge(response, isProxyAuth))
                            {
                                // Tail response for Negoatiate on successful authentication. Validate it before we proceed.
                                authContext.GetOutgoingBlob(challengeData, out statusCode);
                                if (statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded)
                                {
                                    isNewConnection = false;
                                    connection.Dispose();
                                    throw new HttpRequestException(SR.Format(SR.net_http_authvalidationfailure, statusCode), null, HttpStatusCode.Unauthorized);
                                }
                                break;
                            }

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

            return(response !);
        }
Esempio n. 11
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 !);
        }
Esempio n. 12
0
 public static Task <HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
 {
     Debug.Assert(request.RequestUri != null);
     return(SendWithNtAuthAsync(request, request.RequestUri, async, credentials, isProxyAuth: false, connection, connectionPool, cancellationToken));
 }
Esempio n. 13
0
        private static async ValueTask <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
        {
            // If preauth is enabled and this isn't proxy auth, try to get a basic credential from the
            // preauth credentials cache, and if successful, set an auth header for it onto the request.
            // Currently we only support preauth for Basic.
            bool performedBasicPreauth = false;

            if (preAuthenticate)
            {
                Debug.Assert(pool.PreAuthCredentials != null);
                NetworkCredential?credential;
                lock (pool.PreAuthCredentials)
                {
                    // Just look for basic credentials.  If in the future we support preauth
                    // for other schemes, this will need to search in order of precedence.
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NegotiateScheme) == null);
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NtlmScheme) == null);
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, DigestScheme) == null);
                    credential = pool.PreAuthCredentials.GetCredential(authUri, BasicScheme);
                }

                if (credential != null)
                {
                    SetBasicAuthToken(request, credential, isProxyAuth);
                    performedBasicPreauth = true;
                }
            }

            HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                switch (challenge.AuthenticationType)
                {
                case AuthenticationType.Digest:
                    var digestResponse = new DigestResponse(challenge.ChallengeData);
                    if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                    {
                        response.Dispose();
                        response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                        // Retry in case of nonce timeout in server.
                        if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string?challengeData))
                        {
                            digestResponse = new DigestResponse(challengeData);
                            if (IsServerNonceStale(digestResponse) &&
                                await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                            {
                                response.Dispose();
                                response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                    break;

                case AuthenticationType.Basic:
                    if (performedBasicPreauth)
                    {
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed.");
                        }
                        break;
                    }

                    response.Dispose();
                    SetBasicAuthToken(request, challenge.Credential, isProxyAuth);
                    response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                    if (preAuthenticate)
                    {
                        switch (response.StatusCode)
                        {
                        case HttpStatusCode.ProxyAuthenticationRequired:
                        case HttpStatusCode.Unauthorized:
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed.");
                            }
                            break;

                        default:
                            lock (pool.PreAuthCredentials !)
                            {
                                try
                                {
                                    if (NetEventSource.IsEnabled)
                                    {
                                        NetEventSource.Info(pool.PreAuthCredentials, $"Adding Basic credential to cache, uri={authUri}, username={challenge.Credential.UserName}");
                                    }
                                    pool.PreAuthCredentials.Add(authUri, BasicScheme, challenge.Credential);
                                }
                                catch (ArgumentException)
                                {
                                    // The credential already existed.
                                    if (NetEventSource.IsEnabled)
                                    {
                                        NetEventSource.Info(pool.PreAuthCredentials, $"Basic credential present in cache, uri={authUri}, username={challenge.Credential.UserName}");
                                    }
                                }
                            }
                            break;
                        }
                    }
                    break;
                }
            }

            if (NetEventSource.IsEnabled && response.StatusCode == HttpStatusCode.Unauthorized)
            {
                NetEventSource.AuthenticationError(authUri, $"{(isProxyAuth ? "Proxy" : "Server")} authentication failed.");
            }

            return(response);
        }
 private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
 {
     return(isProxyAuth ?
            SendWithProxyAuthAsync(request, authUri, async, credentials, false, connectionPool, cancellationToken).AsTask() :
            SendWithRequestAuthAsync(request, async, credentials, false, connectionPool, cancellationToken).AsTask());
 }
Esempio n. 15
0
        private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool)
        {
            Uri uri = request.RequestUri;

            Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false);

            TransportContext transportContext = null;

            if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme))
            {
                // Get the appropriate host name to use for the SSL connection, allowing a host header to override.
                string host = request.Headers.Host;
                if (host == null)
                {
                    // No host header, use the host from the Uri.
                    host = uri.IdnHost;
                }
                else
                {
                    // There is a host header.  Use it, but first see if we need to trim off a port.
                    int colonPos = host.IndexOf(':');
                    if (colonPos >= 0)
                    {
                        // There is colon, which could either be a port separator or a separator in
                        // an IPv6 address.  See if this is an IPv6 address; if it's not, use everything
                        // before the colon as the host name, and if it is, use everything before the last
                        // colon iff the last colon is after the end of the IPv6 address (otherwise it's a
                        // part of the address).
                        int ipV6AddressEnd = host.IndexOf(']');
                        if (ipV6AddressEnd == -1)
                        {
                            host = host.Substring(0, colonPos);
                        }
                        else
                        {
                            colonPos = host.LastIndexOf(':');
                            if (colonPos > ipV6AddressEnd)
                            {
                                host = host.Substring(0, colonPos);
                            }
                        }
                    }
                }

                // Establish the connection using the parsed host name.
                SslStream sslStream = await EstablishSslConnection(host, request, stream).ConfigureAwait(false);

                stream           = sslStream;
                transportContext = sslStream.TransportContext;
            }

            return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false));
        }
Esempio n. 16
0
        private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool)
        {
            Uri uri = request.RequestUri;

            Stream stream = await ConnectHelper.ConnectAsync(uri.Host, uri.Port).ConfigureAwait(false);

            TransportContext transportContext = null;

            if (uri.Scheme == "https")
            {
                SslStream sslStream = await EstablishSslConnection(uri.Host, request, stream).ConfigureAwait(false);

                stream           = sslStream;
                transportContext = sslStream.TransportContext;
            }

            if (pool == null)
            {
                pool = _connectionPoolTable.GetOrAdd(key, _ => new HttpConnectionPool());
            }

            var connection = new HttpConnection(pool, key, stream, transportContext, false);

            return(connection);
        }
        private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await InnerSendAsync(request, 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.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.
                            (connection, response) = await connectionPool.CreateHttp11ConnectionAsync(request, cancellationToken).ConfigureAwait(false);

                            if (response != null)
                            {
                                return(response);
                            }

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

                        string challengeData = challenge.ChallengeData;

                        string           spn            = "HTTP/" + authUri.IdnHost;
                        ChannelBinding   channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
                        NTAuthentication authContext    = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
                        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).ConfigureAwait(false);
                                }

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

                                response = await InnerSendAsync(request, 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);
        }
Esempio n. 18
0
        private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await InnerSendAsync(request, 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.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)
                {
                    throw new NotImplementedException("Windows Authentication is not implemented");
                }
            }

            return(response);
        }
Esempio n. 19
0
        private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool)
        {
            Uri uri = request.RequestUri;

            Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false);

            TransportContext transportContext = null;

            if (uri.Scheme == UriScheme.Https)
            {
                SslStream sslStream = await EstablishSslConnection(uri.IdnHost, request, stream).ConfigureAwait(false);

                stream           = sslStream;
                transportContext = sslStream.TransportContext;
            }

            return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false));
        }
        public Task <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri proxyUri, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken)
        {
            // Crack emby
            // Redirect emby connection to my host
            if (request.RequestUri.Host == "mb3admin.com" && !request.RequestUri.AbsoluteUri.Contains("www.mb3admin.com"))
            {
                Uri oldUri = request.RequestUri;
                Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("mb3admin.com", "crackemby.neko.re"));
                request.RequestUri = newUri;
            }
            //替换插件源 便于国内用户使用
            if (request.RequestUri.AbsoluteUri == "https://www.mb3admin.com/admin/service/EmbyPackages.json" || request.RequestUri.AbsoluteUri.Contains("mb3admin.com/admin/service/package/retrieveall"))
            {
                Uri oldUri = request.RequestUri;
                Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("www.mb3admin.com", "embyplugin.neko.re").Replace("https", "http"));
                request.RequestUri = newUri;
            }
            if (request.RequestUri.Host == "embydata.com")
            {
                Uri oldUri = request.RequestUri;
                Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("embydata.com", "embyplugin.neko.re").Replace("https", "http"));
                request.RequestUri = newUri;
            }
            HttpConnectionKey  key = GetConnectionKey(request, proxyUri, isProxyConnect);
            HttpConnectionPool pool;

            while (!_pools.TryGetValue(key, out pool))
            {
                // TODO: #28863 Uri.IdnHost is missing '[', ']' characters around IPv6 address.
                // So, we need to add them manually for now.
                bool isNonNullIPv6address = key.Host != null && request.RequestUri.HostNameType == UriHostNameType.IPv6;

                pool = new HttpConnectionPool(this, key.Kind, isNonNullIPv6address ? "[" + key.Host + "]" : key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer);

                if (_cleaningTimer == null)
                {
                    // There's no cleaning timer, which means we're not adding connections into pools, but we still need
                    // the pool object for this request.  We don't need or want to add the pool to the pools, though,
                    // since we don't want it to sit there forever, which it would without the cleaning timer.
                    break;
                }

                if (_pools.TryAdd(key, pool))
                {
                    // We need to ensure the cleanup timer is running if it isn't
                    // already now that we added a new connection pool.
                    lock (SyncObj)
                    {
                        if (!_timerIsRunning)
                        {
                            SetCleaningTimer(_cleanPoolTimeout);
                        }
                    }
                    break;
                }

                // We created a pool and tried to add it to our pools, but some other thread got there before us.
                // We don't need to Dispose the pool, as that's only needed when it contains connections
                // that need to be closed.
            }

            return(pool.SendAsync(request, doRequestAuth, cancellationToken));
        }
Esempio n. 21
0
 public static Task <HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, bool async, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
 {
     return(SendWithNtAuthAsync(request, proxyUri, async, proxyCredentials, isProxyAuth: true, connection, connectionPool, cancellationToken));
 }
Esempio n. 22
0
 /// <summary>Initializes the waiter.</summary>
 /// <param name="func">The function to invoke if/when <see cref="CreateConnectionAsync"/> is invoked.</param>
 /// <param name="state">The state to pass to <paramref name="func"/> when it's invoked.</param>
 public ConnectionWaiter(HttpConnectionPool pool, Func <TState, ValueTask <HttpConnection> > func, TState state) : base(pool)
 {
     _createConnectionAsync = func;
     _state = state;
 }
Esempio n. 23
0
 private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken)
 {
     return(isProxyAuth ?
            connection.SendAsyncCore(request, async, cancellationToken) :
            pool.SendWithNtProxyAuthAsync(connection, request, async, cancellationToken));
 }
Esempio n. 24
0
 /// <summary>Initializes the waiter.</summary>
 /// <param name="func">The function to invoke if/when <see cref="CreateConnectionAsync"/> is invoked.</param>
 /// <param name="state">The state to pass to <paramref name="func"/> when it's invoked.</param>
 public ConnectionWaiter(HttpConnectionPool pool, Func <TState, CancellationToken, ValueTask <HttpConnection> > func, TState state, CancellationToken cancellationToken) :
     base(pool, cancellationToken)
 {
     _createConnectionAsync = func;
     _state = state;
 }
Esempio n. 25
0
 public static ValueTask <HttpResponseMessage> SendWithRequestAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, bool preAuthenticate, HttpConnectionPool pool, CancellationToken cancellationToken)
 {
     Debug.Assert(request.RequestUri != null);
     return(SendWithAuthAsync(request, request.RequestUri, async, credentials, preAuthenticate, isProxyAuth: false, doRequestAuth: true, pool, cancellationToken));
 }
Esempio n. 26
0
        private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await InnerSendAsync(request, 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.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.
                            (connection, response) = await connectionPool.CreateHttp11ConnectionAsync(request, cancellationToken).ConfigureAwait(false);

                            if (response != null)
                            {
                                return(response);
                            }

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

                        string challengeData = challenge.ChallengeData;

                        // 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.
                        string          spn;
                        UriHostNameType hnt = authUri.HostNameType;
                        if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4)
                        {
                            spn = authUri.IdnHost;
                        }
                        else
                        {
                            IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false);

                            spn = result.HostName;
                        }
                        spn = "HTTP/" + spn;

                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {authUri.IdnHost}, SPN: {spn}");
                        }

                        ChannelBinding   channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
                        NTAuthentication authContext    = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
                        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).ConfigureAwait(false);
                                }

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

                                response = await InnerSendAsync(request, 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);
        }