Exemplo n.º 1
0
    private async Task RunAsync()
    {
        try
        {
            var address = BindingAddress.Parse(Options.Url);

            if (!IPAddress.TryParse(address.Host, out var ip))
            {
                ip = Dns.GetHostEntry(address.Host).AddressList.First();
            }

            var endpoint = new IPEndPoint(ip, address.Port);

            _logger.LogInformation($"Connecting to '{endpoint}'.");

            await using var context = await _connectionFactory.ConnectAsync(endpoint);

            _logger.LogInformation($"Connected to '{endpoint}'.");

            var originalTransport     = context.Transport;
            IAsyncDisposable sslState = null;
            if (address.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
            {
                _logger.LogInformation("Starting TLS handshake.");

                var memoryPool        = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool;
                var inputPipeOptions  = new StreamPipeReaderOptions(memoryPool, memoryPool.GetMinimumSegmentSize(), memoryPool.GetMinimumAllocSize(), leaveOpen: true);
                var outputPipeOptions = new StreamPipeWriterOptions(pool: memoryPool, leaveOpen: true);

                var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions);
                var sslStream     = sslDuplexPipe.Stream;
                sslState = sslDuplexPipe;

                context.Transport = sslDuplexPipe;

                await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
                {
                    TargetHost = address.Host,
                    RemoteCertificateValidationCallback = (_, __, ___, ____) => true,
                    ApplicationProtocols = new List <SslApplicationProtocol> {
                        SslApplicationProtocol.Http2
                    },
                    EnabledSslProtocols = SslProtocols.Tls12,
                }, CancellationToken.None);

                _logger.LogInformation($"TLS handshake completed successfully.");
            }

            var http2Utilities = new Http2Utilities(context, _logger, _stopTokenSource.Token);

            try
            {
                await Options.Scenaro(http2Utilities);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "App error");
                throw;
            }
            finally
            {
                // Unwind Https for shutdown. This must happen before the context goes out of scope or else DisposeAsync will never complete
                context.Transport = originalTransport;

                if (sslState != null)
                {
                    await sslState.DisposeAsync();
                }
            }
        }
        finally
        {
            HostApplicationLifetime.StopApplication();
        }
    }
        private async Task InnerOnConnectionAsync(ConnectionContext context)
        {
            bool certificateRequired;
            var  feature = new TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool;

            var inputPipeOptions = new StreamPipeReaderOptions
                                   (
                pool: memoryPool,
                bufferSize: memoryPool.GetMinimumSegmentSize(),
                minimumReadSize: memoryPool.GetMinimumAllocSize(),
                leaveOpen: true
                                   );

            var outputPipeOptions = new StreamPipeWriterOptions
                                    (
                pool: memoryPool,
                leaveOpen: true
                                    );

            SslDuplexPipe sslDuplexPipe = null;

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslDuplexPipe       = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions);
                certificateRequired = false;
            }
            else
            {
                sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream(s,
                                                                                                                             leaveInnerStreamOpen: false,
                                                                                                                             userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                }));

                certificateRequired = true;
            }

            var sslStream = sslDuplexPipe.Stream;

            using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout))
                using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context)) {
                    try {
                        // Adapt to the SslStream signature
                        ServerCertificateSelectionCallback selector = null;
                        if (_serverCertificateSelector != null)
                        {
                            selector = (sender, name) => {
                                context.Features.Set(sslStream);
                                var cert = _serverCertificateSelector(context, name);
                                if (cert != null)
                                {
                                    EnsureCertificateIsAllowedForServerAuth(cert);
                                }
                                return(cert);
                            };
                        }

                        var sslOptions = new SslServerAuthenticationOptions {
                            ServerCertificate = _serverCertificate,
                            ServerCertificateSelectionCallback = selector,
                            ClientCertificateRequired          = certificateRequired,
                            EnabledSslProtocols            = _options.SslProtocols,
                            CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                            ApplicationProtocols           = new List <SslApplicationProtocol>()
                        };

                        // This is order sensitive
                        if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                            // https://tools.ietf.org/html/rfc7540#section-9.2.1
                            sslOptions.AllowRenegotiation = false;
                        }

                        if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                        }

                        _options.OnAuthenticate?.Invoke(context, sslOptions);

                        await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
                    } catch (OperationCanceledException) {
                        _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                        await sslStream.DisposeAsync();

                        return;
                    } catch (IOException ex) {
                        _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        await sslStream.DisposeAsync();

                        return;
                    } catch (AuthenticationException ex) {
                        if (_serverCertificate == null ||
                            !CertificateManager.IsHttpsDevelopmentCertificate(_serverCertificate) ||
                            CertificateManager.CheckDeveloperCertificateKey(_serverCertificate))
                        {
                            _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        }
                        else
                        {
                            _logger?.LogError(3, ex, CoreStrings.BadDeveloperCertificateState);
                        }

                        await sslStream.DisposeAsync();

                        return;
                    }
                }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = ConvertToX509Certificate2(sslStream.RemoteCertificate);
            feature.CipherAlgorithm      = sslStream.CipherAlgorithm;
            feature.CipherStrength       = sslStream.CipherStrength;
            feature.HashAlgorithm        = sslStream.HashAlgorithm;
            feature.HashStrength         = sslStream.HashStrength;
            feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
            feature.KeyExchangeStrength  = sslStream.KeyExchangeStrength;
            feature.Protocol             = sslStream.SslProtocol;

            var originalTransport = context.Transport;

            try {
                context.Transport = sslDuplexPipe;

                // Disposing the stream will dispose the sslDuplexPipe
                await using (sslStream)
                    await using (sslDuplexPipe) {
                        await _next(context);

                        // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream
                        // as the duplex pipe can hit an ODE as it still may be writing.
                    }
            } finally {
                // Restore the original so that it gets closed appropriately
                context.Transport = originalTransport;
            }
        }
Exemplo n.º 3
0
            public async Task RunAsync()
            {
                var endpoint = new IPEndPoint(IPAddress.Loopback, 5001);

                _logger.LogInformation($"Connecting to '{endpoint}'.");

                await using var context = await _connectionFactory.ConnectAsync(endpoint);

                _logger.LogInformation($"Connected to '{endpoint}'. Starting TLS handshake.");

                var memoryPool        = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool;
                var inputPipeOptions  = new StreamPipeReaderOptions(memoryPool, memoryPool.GetMinimumSegmentSize(), memoryPool.GetMinimumAllocSize(), leaveOpen: true);
                var outputPipeOptions = new StreamPipeWriterOptions(pool: memoryPool, leaveOpen: true);

                await using var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions);
                await using var sslStream     = sslDuplexPipe.Stream;

                var originalTransport = context.Transport;

                context.Transport = sslDuplexPipe;

                try
                {
                    await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
                    {
                        TargetHost = "localhost",
                        RemoteCertificateValidationCallback = (_, __, ___, ____) => true,
                        ApplicationProtocols = new List <SslApplicationProtocol> {
                            SslApplicationProtocol.Http2
                        },
                        EnabledSslProtocols = SslProtocols.Tls12,
                    }, CancellationToken.None);

                    _logger.LogInformation($"TLS handshake completed successfully.");

                    var http2Utilities = new Http2Utilities(context);

                    await http2Utilities.InitializeConnectionAsync();

                    _logger.LogInformation("Initialized http2 connection. Starting stream 1.");

                    await http2Utilities.StartStreamAsync(1, Http2Utilities._browserRequestHeaders, endStream : true);

                    var headersFrame = await http2Utilities.ReceiveFrameAsync();

                    Trace.Assert(headersFrame.Type == Http2FrameType.HEADERS);
                    Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
                    Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) == 0);

                    _logger.LogInformation("Received headers in a single frame.");

                    var decodedHeaders = http2Utilities.DecodeHeaders(headersFrame);

                    foreach (var header in decodedHeaders)
                    {
                        _logger.LogInformation($"{header.Key}: {header.Value}");
                    }

                    var dataFrame = await http2Utilities.ReceiveFrameAsync();

                    Trace.Assert(dataFrame.Type == Http2FrameType.DATA);
                    Trace.Assert((dataFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 0);

                    _logger.LogInformation("Received data in a single frame.");

                    _logger.LogInformation(Encoding.UTF8.GetString(dataFrame.Payload.ToArray()));

                    var trailersFrame = await http2Utilities.ReceiveFrameAsync();

                    Trace.Assert(trailersFrame.Type == Http2FrameType.HEADERS);
                    Trace.Assert((trailersFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 1);

                    _logger.LogInformation("Received trailers in a single frame.");

                    http2Utilities._decodedHeaders.Clear();

                    var decodedTrailers = http2Utilities.DecodeHeaders(trailersFrame);

                    foreach (var header in decodedHeaders)
                    {
                        _logger.LogInformation($"{header.Key}: {header.Value}");
                    }

                    await http2Utilities.StopConnectionAsync(expectedLastStreamId : 1, ignoreNonGoAwayFrames : false);

                    _logger.LogInformation("Connection stopped.");
                }
                finally
                {
                    context.Transport = originalTransport;
                }
            }