//This is called when client is aware of proxy
        private async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client)
        {
            Stream clientStream       = client.GetStream();
            var    clientStreamReader = new CustomBinaryReader(clientStream);
            var    clientStreamWriter = new StreamWriter(clientStream);

            Uri httpRemoteUri;

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

                if (string.IsNullOrEmpty(httpCmd))
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                    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];

                if (httpVerb.ToUpper() == "CONNECT")
                {
                    httpRemoteUri = new Uri("http://" + httpCmdSplit[1]);
                }
                else
                {
                    httpRemoteUri = new Uri(httpCmdSplit[1]);
                }

                //parse the HTTP version
                Version version = new Version(1, 1);
                if (httpCmdSplit.Length == 3)
                {
                    string httpVersion = httpCmdSplit[2].Trim();

                    if (httpVersion == "http/1.0")
                    {
                        version = new Version(1, 0);
                    }
                }
                //filter out excluded host names
                var excluded = endPoint.ExcludedHttpsHostNameRegex != null?
                               endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80)
                {
                    httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
                    await clientStreamReader.ReadAllLinesAsync();

                    await WriteConnectResponse(clientStreamWriter, version);

                    SslStream sslStream = null;

                    try
                    {
                        //create the Tcp Connection to server and then release it to connection cache
                        //Just doing what CONNECT request is asking as to do
                        var tunnelClient = await tcpConnectionCacheManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version,
                                                                                     UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate),
                                                                                     new LocalCertificateSelectionCallback(SelectClientCertificate));

                        await tcpConnectionCacheManager.ReleaseClient(tunnelClient);

                        sslStream = new SslStream(clientStream, true);
                        var certificate = await certificateCacheManager.CreateCertificate(httpRemoteUri.Host, 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 = sslStream;

                        clientStreamReader = new CustomBinaryReader(sslStream);
                        clientStreamWriter = new StreamWriter(sslStream);
                    }
                    catch
                    {
                        if (sslStream != null)
                        {
                            sslStream.Dispose();
                        }

                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                        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.ToUpper() == "CONNECT")
                {
                    //Cyphen out CONNECT request headers
                    await clientStreamReader.ReadAllLinesAsync();

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

                    //Just relay the request/response without decrypting it
                    await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
                                            false, SupportedSslProtocols);

                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                    return;
                }

                //Now create the request
                await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                               httpRemoteUri.Scheme == Uri.UriSchemeHttps?true : false);
            }
            catch
            {
                Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
            }
        }
        /// <summary>
        /// Creates a TCP connection to server
        /// </summary>
        /// <param name="bufferSize"></param>
        /// <param name="connectionTimeOutSeconds"></param>
        /// <param name="remoteHostName"></param>
        /// <param name="httpCmd"></param>
        /// <param name="httpVersion"></param>
        /// <param name="isHttps"></param>
        /// <param name="remotePort"></param>
        /// <param name="supportedSslProtocols"></param>
        /// <param name="remoteCertificateValidationCallback"></param>
        /// <param name="localCertificateSelectionCallback"></param>
        /// <param name="externalHttpProxy"></param>
        /// <param name="externalHttpsProxy"></param>
        /// <param name="clientStream"></param>
        /// <param name="upStreamEndPoint"></param>
        /// <returns></returns>
        internal async Task <TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds,
                                                         string remoteHostName, int remotePort, Version httpVersion,
                                                         bool isHttps, SslProtocols supportedSslProtocols,
                                                         RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback,
                                                         ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy,
                                                         Stream clientStream, EndPoint upStreamEndPoint)
        {
            TcpClient client;
            Stream    stream;

            if (isHttps)
            {
                SslStream sslStream = null;

                //If this proxy uses another external proxy then create a tunnel request for HTTPS connections
                if (externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName)
                {
                    client = new TcpClient();
                    client.Client.Bind(upStreamEndPoint);
                    await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port);

                    stream = client.GetStream();

                    using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)
                    {
                        NewLine = ProxyConstants.NewLine
                    })
                    {
                        await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}");

                        await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}");

                        await writer.WriteLineAsync("Connection: Keep-Alive");

                        if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null)
                        {
                            await writer.WriteLineAsync("Proxy-Connection: keep-alive");

                            await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
                        }
                        await writer.WriteLineAsync();

                        await writer.FlushAsync();

                        writer.Close();
                    }

                    using (var reader = new CustomBinaryReader(stream))
                    {
                        var result = await reader.ReadLineAsync();


                        if (!new string[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower())))
                        {
                            throw new Exception("Upstream proxy failed to create a secure tunnel");
                        }

                        await reader.ReadAllLinesAsync();
                    }
                }
                else
                {
                    client = new TcpClient();
                    client.Client.Bind(upStreamEndPoint);
                    await client.ConnectAsync(remoteHostName, remotePort);

                    stream = client.GetStream();
                }

                try
                {
                    sslStream = new SslStream(stream, true, remoteCertificateValidationCallback,
                                              localCertificateSelectionCallback);

                    await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false);

                    stream = sslStream;
                }
                catch
                {
                    sslStream?.Dispose();

                    throw;
                }
            }
            else
            {
                if (externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName)
                {
                    client = new TcpClient();
                    client.Client.Bind(upStreamEndPoint);
                    await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port);

                    stream = client.GetStream();
                }
                else
                {
                    client = new TcpClient();
                    client.Client.Bind(upStreamEndPoint);
                    await client.ConnectAsync(remoteHostName, remotePort);

                    stream = client.GetStream();
                }
            }

            client.ReceiveTimeout = connectionTimeOutSeconds * 1000;
            client.SendTimeout    = connectionTimeOutSeconds * 1000;

            stream.ReadTimeout  = connectionTimeOutSeconds * 1000;
            stream.WriteTimeout = connectionTimeOutSeconds * 1000;


            return(new TcpConnection()
            {
                UpStreamHttpProxy = externalHttpProxy,
                UpStreamHttpsProxy = externalHttpsProxy,
                HostName = remoteHostName,
                Port = remotePort,
                IsHttps = isHttps,
                TcpClient = client,
                StreamReader = new CustomBinaryReader(stream),
                Stream = stream,
                Version = httpVersion
            });
        }
Esempio n. 3
0
        private static async Task <TcpConnection> CreateClient(string hostname, int port, bool isHttps, Version version)
        {
            TcpClient client;
            Stream    stream;

            if (isHttps)
            {
                CustomSslStream sslStream = null;

                if (ProxyServer.UpStreamHttpsProxy != null)
                {
                    client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port);
                    stream = (Stream)client.GetStream();

                    using (var writer = new StreamWriter(stream, Encoding.ASCII, Constants.BUFFER_SIZE, true))
                    {
                        await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", hostname, port, version)).ConfigureAwait(false);

                        await writer.WriteLineAsync(string.Format("Host: {0}:{1}", hostname, port)).ConfigureAwait(false);

                        await writer.WriteLineAsync("Connection: Keep-Alive").ConfigureAwait(false);

                        await writer.WriteLineAsync().ConfigureAwait(false);

                        await writer.FlushAsync().ConfigureAwait(false);

                        writer.Close();
                    }

                    using (var reader = new CustomBinaryReader(stream))
                    {
                        var result = await reader.ReadLineAsync().ConfigureAwait(false);

                        if (!result.ToLower().Contains("200 connection established"))
                        {
                            throw new Exception("Upstream proxy failed to create a secure tunnel");
                        }

                        await reader.ReadAllLinesAsync().ConfigureAwait(false);
                    }
                }
                else
                {
                    client = new TcpClient(hostname, port);
                    stream = (Stream)client.GetStream();
                }

                try
                {
                    sslStream = new CustomSslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate),
                                                    new LocalCertificateSelectionCallback(ProxyServer.SelectClientCertificate));
                    await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false).ConfigureAwait(false);

                    stream = (Stream)sslStream;
                }
                catch
                {
                    if (sslStream != null)
                    {
                        sslStream.Dispose();
                    }
                    throw;
                }
            }
            else
            {
                if (ProxyServer.UpStreamHttpProxy != null)
                {
                    client = new TcpClient(ProxyServer.UpStreamHttpProxy.HostName, ProxyServer.UpStreamHttpProxy.Port);
                    stream = (Stream)client.GetStream();
                }
                else
                {
                    client = new TcpClient(hostname, port);
                    stream = (Stream)client.GetStream();
                }
            }

            return(new TcpConnection()
            {
                HostName = hostname,
                port = port,
                IsHttps = isHttps,
                TcpClient = client,
                StreamReader = new CustomBinaryReader(stream),
                Stream = stream,
                Version = version
            });
        }
Esempio n. 4
0
        /// <summary>
        /// Create connection to a particular host/port optionally with SSL and a particular HTTP version
        /// </summary>
        /// <param name="hostname"></param>
        /// <param name="port"></param>
        /// <param name="isHttps"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        private async Task <TcpConnectionCache> CreateClient(string hostname, int port, bool isHttps, Version version,
                                                             ExternalProxy upStreamHttpProxy, ExternalProxy upStreamHttpsProxy, int bufferSize, SslProtocols supportedSslProtocols,
                                                             RemoteCertificateValidationCallback remoteCertificateValidationCallBack, LocalCertificateSelectionCallback localCertificateSelectionCallback)
        {
            TcpClient client;
            Stream    stream;

            if (isHttps)
            {
                SslStream sslStream = null;

                //If this proxy uses another external proxy then create a tunnel request for HTTPS connections
                if (upStreamHttpsProxy != null)
                {
                    client = new TcpClient(upStreamHttpsProxy.HostName, upStreamHttpsProxy.Port);
                    stream = (Stream)client.GetStream();

                    using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true))
                    {
                        await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", hostname, port, version));

                        await writer.WriteLineAsync(string.Format("Host: {0}:{1}", hostname, port));

                        await writer.WriteLineAsync("Connection: Keep-Alive");

                        await writer.WriteLineAsync();

                        await writer.FlushAsync();

                        writer.Close();
                    }

                    using (var reader = new CustomBinaryReader(stream))
                    {
                        var result = await reader.ReadLineAsync();

                        if (!result.ToLower().Contains("200 connection established"))
                        {
                            throw new Exception("Upstream proxy failed to create a secure tunnel");
                        }

                        await reader.ReadAllLinesAsync();
                    }
                }
                else
                {
                    client = new TcpClient(hostname, port);
                    stream = (Stream)client.GetStream();
                }

                try
                {
                    sslStream = new SslStream(stream, true, remoteCertificateValidationCallBack,
                                              localCertificateSelectionCallback);
                    await sslStream.AuthenticateAsClientAsync(hostname, null, supportedSslProtocols, false);

                    stream = (Stream)sslStream;
                }
                catch
                {
                    if (sslStream != null)
                    {
                        sslStream.Dispose();
                    }
                    throw;
                }
            }
            else
            {
                if (upStreamHttpProxy != null)
                {
                    client = new TcpClient(upStreamHttpProxy.HostName, upStreamHttpProxy.Port);
                    stream = (Stream)client.GetStream();
                }
                else
                {
                    client = new TcpClient(hostname, port);
                    stream = (Stream)client.GetStream();
                }
            }

            return(new TcpConnectionCache()
            {
                HostName = hostname,
                port = port,
                IsHttps = isHttps,
                TcpClient = client,
                StreamReader = new CustomBinaryReader(stream),
                Stream = stream,
                Version = version
            });
        }
        /// <summary>
        /// Creates a TCP connection to server
        /// </summary>
        /// <param name="bufferSize"></param>
        /// <param name="connectionTimeOutSeconds"></param>
        /// <param name="remoteHostName"></param>
        /// <param name="httpCmd"></param>
        /// <param name="httpVersion"></param>
        /// <param name="isHttps"></param>
        /// <param name="remotePort"></param>
        /// <param name="supportedSslProtocols"></param>
        /// <param name="remoteCertificateValidationCallback"></param>
        /// <param name="localCertificateSelectionCallback"></param>
        /// <param name="externalHttpProxy"></param>
        /// <param name="externalHttpsProxy"></param>
        /// <param name="clientStream"></param>
        /// <returns></returns>
        internal async Task <TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds,
                                                         string remoteHostName, int remotePort, Version httpVersion,
                                                         bool isHttps, SslProtocols supportedSslProtocols,
                                                         RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback,
                                                         ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy,
                                                         Stream clientStream)
        {
            TcpClient client;
            Stream    stream;

            if (isHttps)
            {
                SslStream sslStream = null;

                //If this proxy uses another external proxy then create a tunnel request for HTTPS connections
                if (externalHttpsProxy != null)
                {
                    client = new TcpClient(externalHttpsProxy.HostName, externalHttpsProxy.Port);
                    stream = client.GetStream();

                    using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true))
                    {
                        await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", remoteHostName, remotePort, httpVersion));

                        await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort));

                        await writer.WriteLineAsync("Connection: Keep-Alive");

                        await writer.WriteLineAsync();

                        await writer.FlushAsync();

                        writer.Close();
                    }

                    using (var reader = new CustomBinaryReader(stream))
                    {
                        var result = await reader.ReadLineAsync();

                        if (!result.ToLower().Contains("200 connection established"))
                        {
                            throw new Exception("Upstream proxy failed to create a secure tunnel");
                        }

                        await reader.ReadAllLinesAsync();
                    }
                }
                else
                {
                    client = new TcpClient(remoteHostName, remotePort);
                    stream = client.GetStream();
                }

                try
                {
                    sslStream = new SslStream(stream, true, remoteCertificateValidationCallback,
                                              localCertificateSelectionCallback);

                    await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false);

                    stream = sslStream;
                }
                catch
                {
                    if (sslStream != null)
                    {
                        sslStream.Dispose();
                    }

                    throw;
                }
            }
            else
            {
                if (externalHttpProxy != null)
                {
                    client = new TcpClient(externalHttpProxy.HostName, externalHttpProxy.Port);
                    stream = client.GetStream();
                }
                else
                {
                    client = new TcpClient(remoteHostName, remotePort);
                    stream = client.GetStream();
                }
            }

            client.ReceiveTimeout = connectionTimeOutSeconds * 1000;
            client.SendTimeout    = connectionTimeOutSeconds * 1000;

            stream.ReadTimeout  = connectionTimeOutSeconds * 1000;
            stream.WriteTimeout = connectionTimeOutSeconds * 1000;

            return(new TcpConnection()
            {
                HostName = remoteHostName,
                port = remotePort,
                IsHttps = isHttps,
                TcpClient = client,
                StreamReader = new CustomBinaryReader(stream),
                Stream = stream
            });
        }