/// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            return(handleClient(endPoint, clientConnection, endPoint.Port, cancellationTokenSource, cancellationToken));
        }
        public IBinaryConnection Create(BinaryConnectionConfig config, IMessageValidator validator)
        {
            IBinaryConnection connection = null;

            if (config is TcpClientConfig)
            {
                connection = new TcpClientConnection(validator)
                {
                    Logger = _logger
                }
            }
            ;

            if (config is TcpListenerConfig)
            {
                connection = new TcpListenerConnection(validator)
                {
                    Logger = _logger
                }
            }
            ;

            connection?.Initialize(config);

            return(connection);
        }
Ejemplo n.º 3
0
            protected override async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
            {
                tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
                tcpClient.SendTimeout    = ConnectionTimeOutSeconds * 1000;

                tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);


                using (var requestState = new TRequestState())
                {
                    await InvokeClientConnectionCreateEvent(tcpClient);

                    var clientConnection = new TcpClientConnection(this, tcpClient);
                    requestState.server           = this;
                    requestState.clientConnection = clientConnection;

                    if (endPoint is TransparentProxyEndPoint tep)
                    {
                        await handleClient(tep, requestState);
                    }
                    else
                    {
                        await handleClient((ExplicitProxyEndPoint)endPoint, requestState);
                    }
                }
            }
Ejemplo n.º 4
0
        public async Task WhenReceivingData_TriggerDataReceivedEvent()
        {
            var pause        = new ManualResetEvent(false);
            var data         = "test";
            var dataReceived = string.Empty;

            using (var tcpClient = new TcpClientConnection())
            {
                tcpClient.DataReceived += (s, e) =>
                {
                    dataReceived = e.Data;
                    pause.Set();
                };
                await tcpClient.ConnectAsync(HOST, PORT);

                using (var server = await connectionFixture.TcpListener.AcceptTcpClientAsync())
                {
                    using (var stream = new StreamWriter(server.GetStream()))
                    {
                        await stream.WriteLineAsync(data);

                        await stream.FlushAsync();

                        // Wait for the client to receive the data if necessary
                        Assert.True(pause.WaitOne(60000));
                    }
                }
            }

            Assert.Equal(data, dataReceived);
        }
Ejemplo n.º 5
0
        private void ProcessRequest(TcpClientConnection client)
        {
            EventHandler <TcpClientEventArgs> handler = OnDataRecieved;

            if (handler == null)
            {
                client.Close();
            }
            else
            {
                if (client.BytesDesired <= client.BytesAvailable)
                {
                    client.BytesDesired = 0;
                    handler(this, client);
                }
            }

            if (client.ReadOffset < ushort.MaxValue / 2 && client.ReadBuffer.Length > ushort.MaxValue)
            {
                Array.Resize(ref client.ReadBuffer, client.ReadOffset + 1024);
            }

            if (!client.IsClosed)
            {
                try
                {
                    client.FlushWrite();
                }
                catch
                { client.Close(); }
            }
        }
Ejemplo n.º 6
0
        public async Task WhenServerDisconnects_TrigerDisconnectedEvent()
        {
            var pauseConnected    = new ManualResetEvent(false);
            var pauseDisconnected = new ManualResetEvent(false);
            var pauseDataReceived = new ManualResetEvent(false);

            using (var tcpClient = new TcpClientConnection())
            {
                tcpClient.Connected    += (s, e) => pauseConnected.Set();
                tcpClient.Disconnected += (s, e) => pauseDisconnected.Set();
                tcpClient.DataReceived += (s, e) => pauseDataReceived.Set();

                await tcpClient.ConnectAsync(HOST, PORT);

                using (var server = await connectionFixture.TcpListener.AcceptTcpClientAsync())
                {
                    // Wait for the client to be connected if necessary
                    Assert.True(pauseConnected.WaitOne(60000));

                    using (var stream = new StreamWriter(server.GetStream()))
                    {
                        await stream.WriteLineAsync("test");

                        await stream.FlushAsync();

                        // Wait for the client to receive the data if necessary
                        Assert.True(pauseDataReceived.WaitOne(60000));
                    }
                }
            }

            Assert.True(pauseDisconnected.WaitOne(60000));
        }
Ejemplo n.º 7
0
        private Boolean CloseConnection()
        {
            StopRequested = true;
            TcpClientConnection.GetStream().Close();

            return(true);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Read all CURRENTLY available data (in the kernel) from the Stream. Keep in mind, that there might be more data than this method returns.
        /// </summary>
        /// <returns>A bunch of data which arrived at the kernel.</returns>
        public Byte[] GetAllAvailableData()
        {
            #region Data Definition

            NetworkStream networkStream = TcpClientConnection.GetStream();

            Byte[] ReadBuffer = new Byte[1024];

            Int32       BytesRead      = 0;
            List <Byte> FirstBytesList = new List <Byte>();

            #endregion

            #region Read the FirstBytes until no Data is available or we read more than we can store in a List of Bytes

            do
            {
                BytesRead = networkStream.Read(ReadBuffer, 0, ReadBuffer.Length);
                if (BytesRead == ReadBuffer.Length)
                {
                    FirstBytesList.AddRange(ReadBuffer);
                }
                else
                {
                    Byte[] Temp = new Byte[BytesRead];
                    Array.Copy(ReadBuffer, 0, Temp, 0, BytesRead);
                    FirstBytesList.AddRange(Temp);
                }
            } while (networkStream.DataAvailable && BytesRead > 0 && FirstBytesList.Count < (Int32.MaxValue - ReadBuffer.Length));

            #endregion

            return(FirstBytesList.ToArray());
        }
Ejemplo n.º 9
0
 private void conn_DataReceived(TcpClientConnection<HttpConnectionState> conn, HttpConnectionState state, BufferData data)
 {
     HttpResponseParser parser = state.ResponseParser;
     string response = parser.Parse(data);//info:noexception
     if (response == null) return;
     conn.Close();//info:noexception
     TriggerCompleted(new HttpFetchResult(state.Url, response: response));
 }
Ejemplo n.º 10
0
 /// <summary>
 /// constructor of SettingModel
 /// </summary>
 public SettingModel()
 {
     handler          = new ObservableCollection <string>();
     clientConnection = TcpClientConnection.Instance;
     Thread.Sleep(1000);
     BindingOperations.EnableCollectionSynchronization(handler, new Object());
     clientConnection.MessageReceived += onGetConfig;
 }
        public void TcpClientConnection_Constructor_WithParameters()
        {
            var connection = new TcpClientConnection(ip, 3030);

            //Assert.AreEqual(100, connection.ReadTimeout);
            //Assert.AreEqual(100, connection.WriteTimeout);
            Assert.AreEqual(ip + ":3030", connection.Name);
        }
        /// <summary>
        /// Creates a connection for TCP network services.
        /// </summary>
        public override TcpConnection Create()
        {
            var connection = new TcpClientConnection();

            connection.TcpClient.NoDelay           = true;
            connection.TcpClient.SendBufferSize    = 1024 * 8;
            connection.TcpClient.ReceiveBufferSize = 1024 * 8;
            return(connection);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Creates a new connection for the tunnel.
        /// </summary>
        /// <param name="session">The session that will own the connection.</param>
        /// <param name="connectionId"></param>
        /// <returns>
        /// The <see cref="T:BlueBoxMoon.LocalSubway.Connections.Connection" /> that can now be added to the tunnel.
        /// </returns>
        public override async Task <Connection> CreateConnectionAsync(ClientSession session, Guid connectionId)
        {
            var socket = new SubwayTcpClient(_hostname, _port);

            var connection = new TcpClientConnection(connectionId, Id, session, socket);
            await connection.StartAsync();

            return(connection);
        }
        public void TcpClientConnection_OpenAndClose()
        {
            var connection = new TcpClientConnection(ip);

            connection.Open();
            connection.Close();
            connection.Open();
            connection.Close();
            connection.Dispose();
        }
Ejemplo n.º 15
0
 /// <summary>
 /// when we click on the button, this function happen.
 /// in our case- the choose itrm remove from the list
 /// </summary>
 /// <param name="obj"></param>
 private void OnRemove(object obj)
 {
     if (chooseItem != null)
     {
         DataInfo            remove = new DataInfo(CommandEnum.CloseCommand, this.chooseItem);
         TcpClientConnection client = TcpClientConnection.Instance;
         client.WriteToServer(remove.toJson());
         this.chooseItem = null;
     }
 }
Ejemplo n.º 16
0
        /// <summary>
        ///     Starts the TCP server loop listening for client connections on the Server Socket, then
        ///     communicating with the client on that client's TCP Connection Socket.
        /// </summary>
        public override void Start()
        {
            ListenSocket = Transport.GetSocket(IPAddress.Any.AddressFamily);
            ListenSocket.Bind(IpEndPoint);
            ListenSocket.Listen(10);
            Console.WriteLine("TCP Server Socket awaiting connection from client");
            Console.WriteLine("Press ESC to stop this server");

            var shutdownToken = new CancellationTokenSource();
            var socketTasks = new ConcurrentBag<Task>();

            var serverSocketTask = Task.Run(() =>
            {
                //loop to listen for TCP connections while token isn't
                while (!shutdownToken.IsCancellationRequested)
                {
                    try
                    {
                        Console.WriteLine("\r\nTCP Server Socket waiting at {0}", ListenSocket.LocalEndPoint);

                        var newClientSocket = ListenSocket.Accept();

                        Connections++;
                        Console.WriteLine("\r\nConnections: {0}", Connections);
                        var client = new TcpClientConnection(newClientSocket, Transport.BufferSize);
                        var clientTask = Task.Factory.StartNew(() =>
                        {
                            client.Execute();
                            Task toRemove;
                            socketTasks.TryTake(out toRemove); //remove from concurrent bag
                            Connections--;
                            Console.WriteLine("\r\nConnections: {0}", Connections);
                        }, shutdownToken.Token);
                        socketTasks.Add(clientTask);
                    }
                    catch (OperationCanceledException)
                    {
                        //time to shutdown
                    }
                }
            }, shutdownToken.Token); //cancel this task when token is flagged
            socketTasks.Add(serverSocketTask);

            //wait for connections
            while (Console.ReadKey(true).Key != ConsoleKey.Escape)
            {
            }

            //no more waiting... shutdown
            Console.WriteLine("Stopping... closing open TCP connections");
            shutdownToken.CancelAfter(1000); //give tasks 1000ms to finish - then throw them if necessary
            Task.WaitAll(socketTasks.ToArray(), 1000); //wait up to 5 seconds for all tasks to end, then give up

            Stop();
        }
Ejemplo n.º 17
0
        public SimpleClient(string host, int port)
        {
            Contract.Requires(!string.IsNullOrEmpty(host));
            Contract.Requires(0 < port && port < 65536);

            var address = host == "localhost" ? IPAddress.Loopback : IPAddress.Parse(host);

            connection = new TcpClientConnection(new IPEndPoint(address, port));
            connection.SocketDisconnected += SocketDisconnected;
            connection.DataReceived       += DataReceived;
        }
        /// <summary>
        ///     Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
        /// </summary>
        private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
                                               TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest?connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
        {
            BufferPool    = server.BufferPool;
            ExceptionFunc = server.ExceptionFunc;
            TimeLine["Session Created"] = DateTime.Now;

            CancellationTokenSource = cancellationTokenSource;

            ClientConnection = clientConnection;
            ClientStream     = clientStream;
            HttpClient       = new HttpWebClient(connectRequest, request, new Lazy <int>(() => clientConnection.GetProcessId(endPoint)));
            LocalEndPoint    = endPoint;
            EnableWinAuth    = server.EnableWinAuth && isWindowsAuthenticationSupported;
        }
Ejemplo n.º 19
0
        public async Task WhenConnected_TriggerConnectedEvent()
        {
            var pause = new ManualResetEvent(false);

            using (var tcpClient = new TcpClientConnection())
            {
                tcpClient.Connected += (s, e) => pause.Set();

                await tcpClient.ConnectAsync(HOST, PORT);

                await connectionFixture.TcpListener.AcceptTcpClientAsync();
            }

            Assert.True(pause.WaitOne(500));
        }
Ejemplo n.º 20
0
        public async Task WhenReceivingDataWithNoDataReceivedHandler_ItShouldBeOK()
        {
            using (var tcpClient = new TcpClientConnection())
            {
                await tcpClient.ConnectAsync(HOST, PORT);

                using (var server = await connectionFixture.TcpListener.AcceptTcpClientAsync())
                {
                    using (var stream = new StreamWriter(server.GetStream()))
                    {
                        await stream.WriteLineAsync("test");

                        await stream.FlushAsync();
                    }
                }
            }
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Wait until new StreamData is available timeout or server shutdown
        /// </summary>
        /// <returns>True: if new StreamData is available. False: if timeout or server shutdown</returns>
        public Boolean WaitForStreamDataAvailable()
        {
            #region Timeout

            var Start = DateTime.Now;

            if (TcpClientConnection == null)
            {
                return(false);
            }

            var stream = TcpClientConnection.GetStream();

            if (stream == null)
            {
                return(false);
            }

            while (!StopRequested && TcpClientConnection.Connected &&
                   !stream.DataAvailable &&
                   ((Timeout == System.Threading.Timeout.Infinite) || (DateTime.Now.Subtract(Start).TotalMilliseconds < Timeout)))
            {
                Thread.Sleep(1);
            }

            #endregion

            if (StopRequested || !TcpClientConnection.Connected)
            {
                Debug.WriteLine("[ATcpSocketConnection][StreamDataAvailableTimeout] Stop requested");
                return(false);
            }

            // If we have any DataAvailable than proceed, even if StopRequested is true
            if (stream.DataAvailable)
            {
                return(true);
            }

            if (DateTime.Now.Subtract(Start).TotalMilliseconds >= Timeout)
            {
                Debug.WriteLine("[ATcpSocketConnection][StreamDataAvailableTimeout] timedout after " + Timeout + "ms");
            }

            return(false);
        }
Ejemplo n.º 22
0
        private void DataRecieved(IAsyncResult ar)
        {
            TcpClientConnection client = ar.AsyncState as TcpClientConnection;

            if (client == null)
            {
                return;
            }
            bool close = false;

            try
            {
                int count = client.Stream.EndRead(ar);
                if (count <= 0)
                {
                    close = true;
                }
                else
                {
                    client.ReadOffset += count;
                    client.AsyncRead   = null;
                    lock (_queue)
                    {
                        _queue.Enqueue(client);
                        _ready.Set();
                    }
                }
            }
            catch
            {
                close = true;
            }

            if (close)
            {
                using (client.Client)
                {
                    lock (_clients)
                        _clients.Remove(client);
                }
            }
        }
Ejemplo n.º 23
0
        public async Task WhenSendingData_ServerShouldReceiveIt()
        {
            var data         = "test";
            var dataReceived = string.Empty;

            using (var tcpClient = new TcpClientConnection())
            {
                await tcpClient.ConnectAsync(HOST, PORT);

                using (var server = await connectionFixture.TcpListener.AcceptTcpClientAsync())
                {
                    using (var stream = new StreamReader(server.GetStream()))
                    {
                        await tcpClient.SendAsync(data);

                        dataReceived = await stream.ReadLineAsync();
                    }
                }
            }

            Assert.Equal(data, dataReceived);
        }
Ejemplo n.º 24
0
        private void button1_Click(object sender, EventArgs e)
        {
            using (TcpClientConnection client = new TcpClientConnection()) {
                client.NetworkStream = new NetworkStream();

                client.TimeOut = 60000;
                client.Open(HostResolver.GetIPAddress("127.0.0.1"), 2110);
                MessageBox.Show("Client connected");

                using (StringStream data = new StringStream("Data to be sent")) {
                    client.WriteData(data);
                    data.SetLength(0);
                    client.IsReadUntilClose = true;
                    client.ReadData(data);

                    MessageBox.Show(data.DataString);
                }

                client.Close(true);
                MessageBox.Show("Client disconnected");
            }
        }
Ejemplo n.º 25
0
        /// <summary>
        ///     Handle the client.
        /// </summary>
        /// <param name="tcpClient">The client.</param>
        /// <param name="endPoint">The proxy endpoint.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
        {
            tcpClient.ReceiveTimeout    = ConnectionTimeOutSeconds * 1000;
            tcpClient.SendTimeout       = ConnectionTimeOutSeconds * 1000;
            tcpClient.SendBufferSize    = BufferSize;
            tcpClient.ReceiveBufferSize = BufferSize;

            await InvokeConnectionCreateEvent(tcpClient, true);

            using (var clientConnection = new TcpClientConnection(this, tcpClient))
            {
                if (endPoint is TransparentProxyEndPoint)
                {
                    TransparentProxyEndPoint tep = endPoint as TransparentProxyEndPoint;
                    await handleClient(tep, clientConnection);
                }
                else
                {
                    await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection);
                }
            }
        }
Ejemplo n.º 26
0
        private void HandleRequests()
        {
            try
            {
                while (!_stop.WaitOne(0, false))
                {
                    var context = _listener.BeginAcceptTcpClient(null, null);

                    if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
                    {
                        return;
                    }

                    try
                    {
                        TcpClientConnection client = new TcpClientConnection(this, _listener.EndAcceptTcpClient(context));

                        EventHandler <TcpClientEventArgs> handler = OnClientConnect;
                        if (handler != null)
                        {
                            handler(this, client);
                        }

                        lock (_clients)
                        {
                            _clients.Add(client);
                        }
                        lock (_queue)
                        {
                            _queue.Enqueue(client);
                            _ready.Set();
                        }
                    }
                    catch { return; }
                }
            }
            catch { _stop.Set(); }
        }
Ejemplo n.º 27
0
        public MainWindow()
        {
            ModalWindow modalWindow = new ModalWindow();

            modalWindow.ShowDialog();
            _currentСlient = ModalWindow._currentСlient;
            if (_currentСlient == null)
            {
                System.Windows.Application.Current.Shutdown();
            }
            else
            {
                InitializeComponent();
                _common                      = new BLL.Common();
                Conversations                = new ObservableCollection <ConversationModel>();
                Messages                     = new ObservableCollection <MessageEventArgsModel>();
                _clientTcp                   = new TcpClientConnection();
                _clientTcp.OnMessage        += SetMessage;
                _clientTcp.SentStartMessage += SentStartMessage;
                UpdateСlientData();
                _clientTcp.StartTcpClient(_portNumber);
            }
        }
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            Task <TcpServerConnection> prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            SslStream sslStream = null;

            try
            {
                string connectHostname = null;
                TunnelConnectSessionEventArgs connectArgs = null;

                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, BufferSize, cancellationToken) == 1)
                {
                    // read the first line HTTP command
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (string.IsNullOrEmpty(httpCmd))
                    {
                        return;
                    }

                    Request.ParseRequestLine(httpCmd, out string _, out string httpUrl, out var version);

                    var httpRemoteUri = new Uri("http://" + httpUrl);
                    connectHostname = httpRemoteUri.Host;

                    var connectRequest = new ConnectRequest
                    {
                        RequestUri  = httpRemoteUri,
                        OriginalUrl = httpUrl,
                        HttpVersion = version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
                                                                    cancellationTokenSource);
                    connectArgs.ProxyClient.Connection   = clientConnection;
                    connectArgs.ProxyClient.ClientStream = clientStream;

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl = endPoint.DecryptSsl && connectArgs.DecryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.HttpClient.Response.StatusCode == 0)
                        {
                            connectArgs.HttpClient.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    // write back successful CONNECT response
                    var response = ConnectResponse.CreateSuccessfulConnectResponse(version);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.HttpClient.Response = response;

                    await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    bool isClientHello = clientHelloInfo != null;
                    if (isClientHello)
                    {
                        connectRequest.TunnelType      = TunnelType.Https;
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && isClientHello)
                    {
                        connectRequest.RequestUri = new Uri("https://" + httpUrl);

                        bool http2Supported = false;

                        var alpn = clientHelloInfo.GetAlpn();
                        if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                        {
                            // test server HTTP/2 support
                            try
                            {
                                // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                                var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                                noCache : true, cancellationToken : cancellationToken);

                                http2Supported = connection.NegotiatedApplicationProtocol ==
                                                 SslApplicationProtocol.Http2;
                                //release connection back to pool instead of closing when connection pool is enabled.
                                await tcpConnectionFactory.Release(connection, true);
                            }
                            catch (Exception)
                            {
                                // ignore
                            }
                        }

                        if (EnableTcpServerConnectionPrefetch)
                        {
                            IPAddress[] ipAddresses = null;
                            try
                            {
                                //make sure the host can be resolved before creating the prefetch task
                                ipAddresses = await Dns.GetHostAddressesAsync(connectArgs.HttpClient.Request.RequestUri.Host);
                            }
                            catch (SocketException) { }

                            if (ipAddresses != null && ipAddresses.Length > 0)
                            {
                                //don't pass cancellation token here
                                //it could cause floating server connections when client exits
                                prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                  isConnect: true, applicationProtocols: null, noCache: false,
                                                                                                  cancellationToken: CancellationToken.None);
                            }
                        }

                        X509Certificate2 certificate = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (EnableHttp2 && http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETCOREAPP2_1
                            clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream       = new CustomBufferedStream(sslStream, BufferPool, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
                        }

                        if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, BufferSize, cancellationToken) == -1)
                        {
                            decryptSsl = false;
                        }

                        if (!decryptSsl)
                        {
                            await tcpConnectionFactory.Release(prefetchConnectionTask, true);

                            prefetchConnectionTask = null;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (!decryptSsl || !isClientHello)
                    {
                        if (!isClientHello)
                        {
                            connectRequest.TunnelType = TunnelType.Websocket;
                        }

                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer(BufferSize);

                                    try
                                    {
                                        await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                        await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
                                                    (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                    (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                    connectArgs.CancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream, BufferPool, BufferSize, cancellationToken) == 1)
                {
                    // todo
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        connectArgs.HttpClient.ConnectRequest.TunnelType = TunnelType.Http2;

                        // HTTP/2 Connection Preface
                        string line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

                            await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

#if NETCOREAPP2_1
                            await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
                                                        (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                        (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                        () => new SessionEventArgs(this, endPoint, cancellationTokenSource)
                            {
                                ProxyClient = { Connection = clientConnection },
                                HttpClient  = { ConnectRequest = connectArgs?.HttpClient.ConnectRequest },
                                UserData    = connectArgs?.UserData
                            },
                                                        async args => { await invokeBeforeRequest(args); },
                                                        async args => { await invokeBeforeResponse(args); },
                                                        connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                calledRequestHandler = true;
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, connectHostname, connectArgs, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!calledRequestHandler)
                {
                    await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
                }

                sslStream?.Dispose();
                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
Ejemplo n.º 29
0
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);

            Task <TcpServerConnection>?prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            try
            {
                TunnelConnectSessionEventArgs?connectArgs = null;

                var method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                if (clientStream.IsClosed)
                {
                    return;
                }

                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (method == KnownMethod.Connect)
                {
                    // read the first line HTTP command
                    var requestLine = await clientStream.ReadRequestLine(cancellationToken);

                    if (requestLine.IsEmpty())
                    {
                        return;
                    }

                    var connectRequest = new ConnectRequest(requestLine.RequestUri)
                    {
                        RequestUriString8 = requestLine.RequestUri,
                        HttpVersion       = requestLine.Version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs             = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, clientStream, cancellationTokenSource);
                    clientStream.DataRead  += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
                    clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl  = endPoint.DecryptSsl && connectArgs.DecryptSsl;
                    bool sendRawData = !decryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.HttpClient.Response.StatusCode == 0)
                        {
                            connectArgs.HttpClient.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    // write back successful CONNECT response
                    var response = ConnectResponse.CreateSuccessfulConnectResponse(requestLine.Version);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.HttpClient.Response = response;

                    await clientStream.WriteResponseAsync(response, cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    if (clientStream.IsClosed)
                    {
                        return;
                    }

                    bool isClientHello = clientHelloInfo != null;
                    if (clientHelloInfo != null)
                    {
                        connectRequest.TunnelType      = TunnelType.Https;
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && clientHelloInfo != null)
                    {
                        connectRequest.IsHttps = true; // todo: move this line to the previous "if"
                        clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;

                        bool http2Supported = false;

                        if (EnableHttp2)
                        {
                            var alpn = clientHelloInfo.GetAlpn();
                            if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                            {
                                // test server HTTP/2 support
                                try
                                {
                                    // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                                    var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                    true, SslExtensions.Http2ProtocolAsList,
                                                                                                    true, cancellationToken);

                                    http2Supported = connection.NegotiatedApplicationProtocol ==
                                                     SslApplicationProtocol.Http2;

                                    // release connection back to pool instead of closing when connection pool is enabled.
                                    await tcpConnectionFactory.Release(connection, true);
                                }
                                catch (Exception)
                                {
                                    // ignore
                                }
                            }
                        }

                        if (EnableTcpServerConnectionPrefetch)
                        {
                            IPAddress[]? ipAddresses = null;
                            try
                            {
                                // make sure the host can be resolved before creating the prefetch task
                                ipAddresses = await Dns.GetHostAddressesAsync(connectArgs.HttpClient.Request.RequestUri.Host);
                            }
                            catch (SocketException) { }

                            if (ipAddresses != null && ipAddresses.Length > 0)
                            {
                                // don't pass cancellation token here
                                // it could cause floating server connections when client exits
                                prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                  true, null, false,
                                                                                                  CancellationToken.None);
                            }
                        }

                        string connectHostname = requestLine.RequestUri.GetString();
                        int    idx             = connectHostname.IndexOf(":");
                        if (idx >= 0)
                        {
                            connectHostname = connectHostname.Substring(0, idx);
                        }

                        X509Certificate2?certificate = null;
                        SslStream?       sslStream   = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (EnableHttp2 && http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETSTANDARD2_1
                            clientStream.Connection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken);
                            sslStream    = null; // clientStream was created, no need to keep SSL stream reference

                            clientStream.DataRead  += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
                            clientStream.DataWrite += (o, args) => connectArgs.OnDecryptedDataReceived(args.Buffer, args.Offset, args.Count);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();

                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
                        }

                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }

                        if (method == KnownMethod.Invalid)
                        {
                            sendRawData = true;
                            await tcpConnectionFactory.Release(prefetchConnectionTask, true);

                            prefetchConnectionTask = null;
                        }
                    }
                    else if (clientHelloInfo == null)
                    {
                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (method == KnownMethod.Invalid)
                    {
                        sendRawData = true;
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (sendRawData)
                    {
                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        true, null,
                                                                                        true, cancellationToken);

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer();

                                    try
                                    {
                                        // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                        int read = await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        if (read != available)
                                        {
                                            throw new Exception("Internal error.");
                                        }

                                        await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            if (!clientStream.IsClosed && !connection.Stream.IsClosed)
                            {
                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
                                                        null, null, connectArgs.CancellationTokenSource, ExceptionFunc);
                            }
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && method == KnownMethod.Pri)
                {
                    // todo
                    string?httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        connectArgs.HttpClient.ConnectRequest !.TunnelType = TunnelType.Http2;

                        // HTTP/2 Connection Preface
                        string?line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        true, SslExtensions.Http2ProtocolAsList,
                                                                                        true, cancellationToken);

                        try
                        {
#if NETSTANDARD2_1
                            var connectionPreface = new ReadOnlyMemory <byte>(Http2Helper.ConnectionPreface);
                            await connection.Stream.WriteAsync(connectionPreface, cancellationToken);

                            await Http2Helper.SendHttp2(clientStream, connection.Stream,
                                                        () => new SessionEventArgs(this, endPoint, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
                            {
                                UserData = connectArgs?.UserData
                            },
                                                        async args => { await onBeforeRequest(args); },
                                                        async args => { await onBeforeResponse(args); },
                                                        connectArgs.CancellationTokenSource, clientStream.Connection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                calledRequestHandler = true;

                // Now create the request
                await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }

                if (!calledRequestHandler)
                {
                    await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
                }

                clientStream.Dispose();
            }
        }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            Task <TcpServerConnection> prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                bool   isHttps       = clientHelloInfo != null;
                string httpsHostName = null;

                if (isHttps)
                {
                    httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource)
                    {
                        SniHostName = httpsHostName
                    };

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        if (EnableTcpServerConnectionPrefetch)
                        {
                            //don't pass cancellation token here
                            //it could cause floating server connections when client exits
                            prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
                                                                                              httpVersion: null, isHttps: true, applicationProtocols: null, isConnect: false,
                                                                                              proxyServer: this, session: null, upStreamEndPoint: UpStreamEndPoint, externalProxy: UpStreamHttpsProxy,
                                                                                              noCache: false, cancellationToken: CancellationToken.None);
                        }

                        SslStream sslStream = null;

                        //do client authentication using fake certificate
                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName    = HttpHelper.GetWildCardDomainName(httpsHostName);
                            var    certificate = endPoint.GenericCertificate ??
                                                 await CertificateManager.CreateCertificateAsync(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize);

                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();
                            throw new ProxyConnectException(
                                      $"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e, null);
                        }
                    }
                    else
                    {
                        var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
                                                                                        httpVersion : null, isHttps : false, applicationProtocols : null,
                                                                                        isConnect : true, proxyServer : this, session : null, upStreamEndPoint : UpStreamEndPoint,
                                                                                        externalProxy : UpStreamHttpsProxy, noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            CustomBufferedStream serverStream = null;
                            int available = clientStream.Available;

                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer(BufferSize);
                                try
                                {
                                    // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    serverStream = connection.Stream;
                                    await serverStream.WriteAsync(data, 0, available, cancellationToken);

                                    await serverStream.FlushAsync(cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
                                                    null, null, cancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }
                calledRequestHandler = true;
                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, isHttps?httpsHostName : null, null, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!calledRequestHandler)
                {
                    await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
                }

                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
        /// <summary>
        ///     This is the core request handler method for a particular connection from client.
        ///     Will create new session (request/response) sequence until
        ///     client/server abruptly terminates connection or by normal HTTP termination.
        /// </summary>
        /// <param name="endPoint">The proxy endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <param name="clientStream">The client stream.</param>
        /// <param name="clientStreamWriter">The client stream writer.</param>
        /// <param name="cancellationTokenSource">The cancellation token source for this async task.</param>
        /// <param name="httpsConnectHostname">
        ///     The https hostname as appeared in CONNECT request if this is a HTTPS request from
        ///     explicit endpoint.
        /// </param>
        /// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param>
        private async Task HandleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
                                                    CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
                                                    CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest)
        {
            var cancellationToken = cancellationTokenSource.Token;
            TcpServerConnection serverConnection = null;
            bool serverConnectionClose           = false;

            try
            {
                // Loop through each subsequest request on this particular client connection
                // (assuming HTTP connection is kept alive by client)
                while (true)
                {
                    // read the request line
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (string.IsNullOrEmpty(httpCmd))
                    {
                        return;
                    }

                    var args = new SessionEventArgs(BufferSize, endPoint, cancellationTokenSource, ExceptionFunc)
                    {
                        ProxyClient = { ClientConnection = clientConnection },
                        WebSession  = { ConnectRequest = connectRequest }
                    };

                    try
                    {
                        try
                        {
                            Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl,
                                                     out var version);

                            // Read the request headers in to unique and non-unique header collections
                            await HeaderParser.ReadHeaders(clientStream, args.WebSession.Request.Headers,
                                                           cancellationToken);

                            Uri httpRemoteUri;
                            if (uriSchemeRegex.IsMatch(httpUrl))
                            {
                                try
                                {
                                    httpRemoteUri = new Uri(httpUrl);
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception($"Invalid URI: '{httpUrl}'", ex);
                                }
                            }
                            else
                            {
                                string host        = args.WebSession.Request.Host ?? httpsConnectHostname;
                                string hostAndPath = host;
                                if (httpUrl.StartsWith("/"))
                                {
                                    hostAndPath += httpUrl;
                                }

                                string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://",
                                                           hostAndPath);
                                try
                                {
                                    httpRemoteUri = new Uri(url);
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception($"Invalid URI: '{url}'", ex);
                                }
                            }

                            var request = args.WebSession.Request;
                            request.RequestUri  = httpRemoteUri;
                            request.OriginalUrl = httpUrl;

                            request.Method                      = httpMethod;
                            request.HttpVersion                 = version;
                            args.ProxyClient.ClientStream       = clientStream;
                            args.ProxyClient.ClientStreamWriter = clientStreamWriter;

                            if (!args.IsTransparent)
                            {
                                // proxy authorization check
                                if (httpsConnectHostname == null && await CheckAuthorization(args) == false)
                                {
                                    await InvokeBeforeResponse(args);

                                    // send the response
                                    await clientStreamWriter.WriteResponseAsync(args.WebSession.Response,
                                                                                cancellationToken : cancellationToken);

                                    return;
                                }

                                PrepareRequestHeaders(request.Headers);
                                request.Host = request.RequestUri.Authority;
                            }

                            // if win auth is enabled
                            // we need a cache of request body
                            // so that we can send it after authentication in WinAuthHandler.cs
                            if (isWindowsAuthenticationEnabledAndSupported && request.HasBody)
                            {
                                await args.GetRequestBody(cancellationToken);
                            }

                            request.OriginalHasBody = request.HasBody;

                            // If user requested interception do it
                            await InvokeBeforeRequest(args);

                            var response = args.WebSession.Response;

                            if (request.CancelRequest)
                            {
                                // syphon out the request body from client before setting the new body
                                await args.SyphonOutBodyAsync(true, cancellationToken);

                                await HandleHttpSessionResponse(args);

                                if (!response.KeepAlive)
                                {
                                    return;
                                }

                                continue;
                            }

                            // create a new connection if hostname/upstream end point changes
                            if (serverConnection != null &&
                                (!serverConnection.HostName.EqualsIgnoreCase(request.RequestUri.Host) ||
                                 args.WebSession.UpStreamEndPoint?.Equals(serverConnection.UpStreamEndPoint) ==
                                 false))
                            {
                                tcpConnectionFactory.Release(serverConnection, true);
                                serverConnection = null;
                            }

                            if (serverConnection == null)
                            {
                                serverConnection = await GetServerConnection(args, false, clientConnection.NegotiatedApplicationProtocol, cancellationToken);
                            }

                            // if upgrading to websocket then relay the requet without reading the contents
                            if (request.UpgradeToWebSocket)
                            {
                                // prepare the prefix content
                                await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);

                                await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
                                                                                      cancellationToken : cancellationToken);

                                string httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken);

                                Response.ParseResponseLine(httpStatus, out var responseVersion,
                                                           out int responseStatusCode,
                                                           out string responseStatusDescription);
                                response.HttpVersion       = responseVersion;
                                response.StatusCode        = responseStatusCode;
                                response.StatusDescription = responseStatusDescription;

                                await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers,
                                                               cancellationToken);

                                if (!args.IsTransparent)
                                {
                                    await clientStreamWriter.WriteResponseAsync(response,
                                                                                cancellationToken : cancellationToken);
                                }

                                // If user requested call back then do it
                                if (!args.WebSession.Response.Locked)
                                {
                                    await InvokeBeforeResponse(args);
                                }

                                await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferSize,
                                                        (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); },
                                                        (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
                                                        cancellationTokenSource, ExceptionFunc);

                                return;
                            }

                            // construct the web request that we are going to issue on behalf of the client.
                            await HandleHttpSessionRequestInternal(serverConnection, args);

                            if (args.WebSession.ServerConnection == null)
                            {
                                return;
                            }

                            // if connection is closing exit
                            if (!response.KeepAlive)
                            {
                                serverConnectionClose = true;
                                return;
                            }

                            if (cancellationTokenSource.IsCancellationRequested)
                            {
                                throw new Exception("Session was terminated by user.");
                            }
                        }
                        catch (Exception e) when(!(e is ProxyHttpException))
                        {
                            throw new ProxyHttpException("Error occured whilst handling session request", e, args);
                        }
                    }
                    catch (Exception e)
                    {
                        args.Exception        = e;
                        serverConnectionClose = true;
                        throw;
                    }
                    finally
                    {
                        await InvokeAfterResponse(args);

                        args.Dispose();
                    }
                }
            }
            finally
            {
                tcpConnectionFactory.Release(serverConnection, serverConnectionClose || !EnableConnectionPool);
            }
        }
 public static void CheckIn(TcpClientConnection<HttpConnectionState> conn)
 {
     //todo:把链接入池
 }
Ejemplo n.º 33
0
 public ClientDisconnectedState(TcpClientConnection context, StateMap stateMap) : base(context, stateMap, BinaryConnectionState.Disconnected)
 {
 }
Ejemplo n.º 34
0
 private void conn_Connected(TcpClientConnection<HttpConnectionState> conn, HttpConnectionState state)
 {
     HttpRequest request = state.Request;
     conn.SendData(request.GetBytes());//info:noexception
 }
Ejemplo n.º 35
0
 private void conn_Error(TcpClientConnection<HttpConnectionState> conn, HttpConnectionState state, Exception error)
 {
     TriggerCompleted(new HttpFetchResult(state.Url, error: error));
 }
Ejemplo n.º 36
0
        private void HandleRequests()
        {
            try
            {
                while (!_stop.WaitOne(0, false))
                {
                    var context = _listener.BeginAcceptTcpClient(null, null);

                    if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
                        return;

                    try
                    {
                        TcpClientConnection client = new TcpClientConnection(this, _listener.EndAcceptTcpClient(context));

                        EventHandler<TcpClientEventArgs> handler = OnClientConnect;
                        if (handler != null)
                            handler(this, client);

                        lock (_clients)
                        {
                            _clients.Add(client);
                        }
                        lock (_queue)
                        {
                            _queue.Enqueue(client);
                            _ready.Set();
                        }
                    }
                    catch { return; }
                }
            }
            catch { _stop.Set(); }
        }
Ejemplo n.º 37
0
        private void ProcessRequest(TcpClientConnection client)
        {
            EventHandler<TcpClientEventArgs> handler = OnDataRecieved;
            if (handler == null)
                client.Close();
            else
            {
                if (client.BytesDesired <= client.BytesAvailable)
                {
                    client.BytesDesired = 0;
                    handler(this, client);
                }
            }

            if (client.ReadOffset < ushort.MaxValue / 2 && client.ReadBuffer.Length > ushort.MaxValue)
                Array.Resize(ref client.ReadBuffer, client.ReadOffset + 1024);

            if (!client.IsClosed)
            {
                try
                {
                    client.FlushWrite();
                }
                catch
                { client.Close(); }
            }
        }