Exemple #1
0
        /// <summary>
        /// Handle dispose of a client/server session
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="args"></param>
        private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader,
                             StreamWriter clientStreamWriter, IDisposable args)
        {
            if (clientStream != null)
            {
                clientStream.Close();
                clientStream.Dispose();
            }

            if (args != null)
            {
                args.Dispose();
            }

            if (clientStreamReader != null)
            {
                clientStreamReader.Dispose();
            }

            if (clientStreamWriter != null)
            {
                clientStreamWriter.Close();
                clientStreamWriter.Dispose();
            }
        }
        /// <summary>
        ///  Handle dispose of a client/server session
        /// </summary>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="serverConnection"></param>
        private void Dispose(CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, TcpConnection serverConnection)
        {
            clientStream?.Dispose();

            clientStreamReader?.Dispose();
            clientStreamWriter?.Dispose();

            if (serverConnection != null)
            {
                serverConnection.Dispose();
                UpdateServerConnectionCount(false);
            }
        }
        /// <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"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
        {
            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);

            try
            {
                if (endPoint.EnableSsl)
                {
                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream);

                    if (clientHelloInfo != null)
                    {
                        var sslStream = new SslStream(clientStream);
                        clientStream = new CustomBufferedStream(sslStream, BufferSize);

                        string sniHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                        string certName    = HttpHelper.GetWildCardDomainName(sniHostName);
                        var    certificate = await CertificateManager.CreateCertificateAsync(certName);

                        try
                        {
                            //Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);
                        }
                        catch (Exception e)
                        {
                            ExceptionFunc(new Exception($"Could'nt authenticate client '{sniHostName}' with fake certificate.", e));
                            return;
                        }
                    }

                    //HTTPS server created - we can now decrypt the client's traffic
                }

                //Now create the request
                await HandleHttpSessionRequest(tcpClient, clientStream, clientStreamReader, clientStreamWriter,
                                               endPoint.EnableSsl?endPoint.GenericCertificateName : null, endPoint, null, true);
            }
            finally
            {
                clientStreamReader.Dispose();
                clientStream.Dispose();
            }
        }
Exemple #4
0
        /// <summary>
        ///  Handle dispose of a client/server session
        /// </summary>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="serverConnection"></param>
        private void Dispose(Stream clientStream,
                             CustomBinaryReader clientStreamReader,
                             StreamWriter clientStreamWriter,
                             TcpConnection serverConnection)
        {
            clientStream?.Close();
            clientStream?.Dispose();

            clientStreamReader?.Dispose();
            clientStreamWriter?.Dispose();

            if (serverConnection != null)
            {
                serverConnection.Dispose();
                Interlocked.Decrement(ref serverConnectionCount);
            }
        }
Exemple #5
0
        /// <summary>
        /// Handle dispose of a client/server session
        /// </summary>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="args"></param>
        private void Dispose(Stream clientStream, CustomBinaryReader clientStreamReader,
                             StreamWriter clientStreamWriter, IDisposable args)
        {
            if (clientStream != null)
            {
                clientStream.Close();
                clientStream.Dispose();
            }

            args?.Dispose();

            clientStreamReader?.Dispose();

            if (clientStreamWriter == null)
            {
                return;
            }
            clientStreamWriter.Close();
            clientStreamWriter.Dispose();
        }
        /// <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"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            bool disposed = false;

            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream);

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command
                string httpCmd = await clientStreamReader.ReadLineAsync();

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

                string  httpMethod;
                string  httpUrl;
                Version version;
                Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);

                httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl);

                //filter out excluded host names
                bool excluded = false;

                if (endPoint.ExcludedHttpsHostNameRegex != null)
                {
                    excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                if (endPoint.IncludedHttpsHostNameRegex != null)
                {
                    excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                ConnectRequest connectRequest = null;

                //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (httpMethod == "CONNECT")
                {
                    connectRequest = new ConnectRequest
                    {
                        RequestUri         = httpRemoteUri,
                        OriginalRequestUrl = httpUrl,
                        HttpVersion        = version,
                        Method             = httpMethod,
                    };

                    await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders);

                    var connectArgs = new TunnelConnectSessionEventArgs(endPoint);
                    connectArgs.WebSession.Request       = connectRequest;
                    connectArgs.ProxyClient.TcpClient    = tcpClient;
                    connectArgs.ProxyClient.ClientStream = clientStream;

                    if (TunnelConnectRequest != null)
                    {
                        await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                    }

                    if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false)
                    {
                        if (TunnelConnectResponse != null)
                        {
                            await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                        }

                        return;
                    }

                    //write back successfull CONNECT response
                    connectArgs.WebSession.Response = ConnectResponse.CreateSuccessfullConnectResponse(version);
                    await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response);

                    var clientHelloInfo = await SslTools.GetClientHelloInfo(clientStream);

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

                    if (TunnelConnectResponse != null)
                    {
                        connectArgs.IsHttpsConnect = isClientHello;
                        await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                    }

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

                        SslStream sslStream = null;

                        try
                        {
                            var alpnStream = AlpnEnabled ? (Stream) new ServerHelloAlpnAdderStream(clientStream) : clientStream;
                            sslStream = new SslStream(alpnStream);

                            string certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host);

                            var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(certName, false);

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

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

                            clientStreamReader.Dispose();
                            clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream);
                        }
                        catch
                        {
                            sslStream?.Dispose();
                            return;
                        }

                        //Now read the actual HTTPS request line
                        httpCmd = await clientStreamReader.ReadLineAsync();
                    }
                    //Hostname is excluded or it is not an HTTPS connect
                    else
                    {
                        //create new connection
                        using (var connection = await GetServerConnection(connectArgs, true))
                        {
                            if (isClientHello)
                            {
                                if (clientStream.Available > 0)
                                {
                                    //send the buffered data
                                    var data = new byte[clientStream.Available];
                                    await clientStream.ReadAsync(data, 0, data.Length);

                                    await connection.Stream.WriteAsync(data, 0, data.Length);

                                    await connection.Stream.FlushAsync();
                                }

                                var serverHelloInfo = await SslTools.GetServerHelloInfo(connection.Stream);

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

                            await TcpHelper.SendRaw(clientStream, connection.Stream,
                                                    (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });

                            UpdateServerConnectionCount(false);
                        }

                        return;
                    }
                }

                //Now create the request
                disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                                          httpRemoteUri.Scheme == UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequest);
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error whilst authorizing request", e));
            }
            finally
            {
                if (!disposed)
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                }
            }
        }
        /// <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"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            var disposed = false;

            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new StreamWriter(clientStream)
            {
                NewLine = ProxyConstants.NewLine
            };

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command
                var httpCmd = await clientStreamReader.ReadLineAsync();

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

                //break up the line into three components (method, remote URL & Http Version)
                var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                //Find the request Verb
                var httpVerb = httpCmdSplit[0].ToUpper();

                httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);

                //parse the HTTP version
                var version = HttpHeader.Version11;
                if (httpCmdSplit.Length == 3)
                {
                    var httpVersion = httpCmdSplit[2].Trim();

                    if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
                    {
                        version = HttpHeader.Version10;
                    }
                }

                //filter out excluded host names
                bool excluded = false;

                if (endPoint.ExcludedHttpsHostNameRegex != null)
                {
                    excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                if (endPoint.IncludedHttpsHostNameRegex != null)
                {
                    excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                List <HttpHeader> connectRequestHeaders = null;

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (httpVerb == "CONNECT" && !excluded &&
                    endPoint.RemoteHttpsPorts.Contains(httpRemoteUri.Port))
                {
                    httpRemoteUri         = new Uri("https://" + httpCmdSplit[1]);
                    connectRequestHeaders = new List <HttpHeader>();
                    string tmpLine;
                    while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
                    {
                        var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

                        var newHeader = new HttpHeader(header[0], header[1]);
                        connectRequestHeaders.Add(newHeader);
                    }

                    if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false)
                    {
                        return;
                    }

                    await WriteConnectResponse(clientStreamWriter, version);

                    SslStream sslStream = null;

                    try
                    {
                        sslStream = new SslStream(clientStream);

                        var certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host);

                        var certificate = endPoint.GenericCertificate ??
                                          CertificateManager.CreateCertificate(certName, false);

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

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

                        clientStreamReader.Dispose();
                        clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                        clientStreamWriter = new StreamWriter(clientStream)
                        {
                            NewLine = ProxyConstants.NewLine
                        };
                    }
                    catch
                    {
                        sslStream?.Dispose();
                        return;
                    }

                    //Now read the actual HTTPS request line
                    httpCmd = await clientStreamReader.ReadLineAsync();
                }
                //Sorry cannot do a HTTPS request decrypt to port 80 at this time
                else if (httpVerb == "CONNECT")
                {
                    //Siphon out CONNECT request headers
                    await clientStreamReader.ReadAndIgnoreAllLinesAsync();

                    //write back successfull CONNECT response
                    await WriteConnectResponse(clientStreamWriter, version);

                    await TcpHelper.SendRaw(this,
                                            httpRemoteUri.Host, httpRemoteUri.Port,
                                            null, version, null,
                                            false,
                                            clientStream, tcpConnectionFactory);

                    return;
                }

                //Now create the request
                disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                                          httpRemoteUri.Scheme == Uri.UriSchemeHttps?httpRemoteUri.Host : null, endPoint,
                                                          connectRequestHeaders);
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error whilst authorizing request", e));
            }
            finally
            {
                if (!disposed)
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                }
            }
        }
        /// <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"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);

            try
            {
                string connectHostname = null;

                ConnectRequest connectRequest = null;

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

                    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;

                    //filter out excluded host names
                    bool excluded = false;

                    if (endPoint.ExcludedHttpsHostNameRegex != null)
                    {
                        excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
                    }

                    if (endPoint.IncludedHttpsHostNameRegex != null)
                    {
                        excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
                    }

                    if (endPoint.BeforeTunnelConnect != null)
                    {
                        excluded = await endPoint.BeforeTunnelConnect(connectHostname);
                    }

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

                    await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers);

                    var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, ExceptionFunc);
                    connectArgs.ProxyClient.TcpClient    = tcpClient;
                    connectArgs.ProxyClient.ClientStream = clientStream;

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


                    if (await CheckAuthorization(clientStreamWriter, connectArgs) == false)
                    {
                        await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc);

                        return;
                    }

                    //write back successfull CONNECT response
                    var response = ConnectResponse.CreateSuccessfullConnectResponse(version);
                    response.Headers.FixProxyHeaders();
                    connectArgs.WebSession.Response = response;

                    await clientStreamWriter.WriteResponseAsync(response);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream);

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

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

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

                        SslStream sslStream = null;

                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);

                            var certificate = endPoint.GenericCertificate ?? await CertificateManager.CreateCertificateAsync(certName);

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

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

                            clientStreamReader.Dispose();
                            clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
                        }
                        catch
                        {
                            sslStream?.Dispose();
                            return;
                        }

                        if (await HttpHelper.IsConnectMethod(clientStream) == -1)
                        {
                            // It can be for example some Google (Cloude Messaging for Chrome) magic
                            excluded = true;
                        }
                    }

                    //Hostname is excluded or it is not an HTTPS connect
                    if (excluded || !isClientHello)
                    {
                        //create new connection
                        using (var connection = await GetServerConnection(connectArgs, true))
                        {
                            if (isClientHello)
                            {
                                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);

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

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

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

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

                        return;
                    }
                }

                //Now create the request
                await HandleHttpSessionRequest(tcpClient, clientStream, clientStreamReader, clientStreamWriter, connectHostname, endPoint, connectRequest);
            }
            catch (ProxyHttpException e)
            {
                ExceptionFunc(e);
            }
            catch (IOException e)
            {
                ExceptionFunc(new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                ExceptionFunc(new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                clientStreamReader.Dispose();
                clientStream.Dispose();
            }
        }
Exemple #9
0
 public void Dispose()
 {
     reader.Dispose();
 }