Exemple #1
0
        //Send chunked response
        private static void WriteResponseBodyChunked(CustomBinaryReader inStreamReader, Stream outStream)
        {
            while (true)
            {
                var chuchkHead = inStreamReader.ReadLine();
                var chunkSize  = int.Parse(chuchkHead, NumberStyles.HexNumber);

                if (chunkSize != 0)
                {
                    var buffer = inStreamReader.ReadBytes(chunkSize);

                    var chunkHead = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));

                    outStream.Write(chunkHead, 0, chunkHead.Length);
                    outStream.Write(NewLineBytes, 0, NewLineBytes.Length);

                    outStream.Write(buffer, 0, chunkSize);
                    outStream.Write(NewLineBytes, 0, NewLineBytes.Length);

                    inStreamReader.ReadLine();
                }
                else
                {
                    inStreamReader.ReadLine();
                    outStream.Write(ChunkEnd, 0, ChunkEnd.Length);
                    break;
                }
            }
        }
Exemple #2
0
        //This is called when client is aware of proxy
        private static void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client)
        {
            Stream clientStream       = client.GetStream();
            var    clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
            var    clientStreamWriter = new StreamWriter(clientStream);

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command

                var httpCmd = clientStreamReader.ReadLine();

                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(SpaceSplit, 3);

                var httpVerb = httpCmdSplit[0];

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

                var httpVersion = httpCmdSplit[2];

                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]);
                    clientStreamReader.ReadAllLines();

                    WriteConnectResponse(clientStreamWriter, httpVersion);

                    var certificate = CertManager.CreateCertificate(httpRemoteUri.Host);

                    SslStream sslStream = null;

                    try
                    {
                        sslStream = new SslStream(clientStream, true);

                        //Successfully managed to authenticate the client using the fake certificate
                        sslStream.AuthenticateAsServer(certificate, false,
                                                       SupportedProtocols, false);

                        clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
                        clientStreamWriter = new StreamWriter(sslStream);
                        //HTTPS server created - we can now decrypt the client's traffic
                        clientStream = sslStream;
                    }

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

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


                    httpCmd = clientStreamReader.ReadLine();
                }
                else if (httpVerb.ToUpper() == "CONNECT")
                {
                    clientStreamReader.ReadAllLines();
                    WriteConnectResponse(clientStreamWriter, httpVersion);

                    TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
                                      false);

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

                //Now create the request

                HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                         httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false);
            }
            catch
            {
                Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
            }
        }
Exemple #3
0
        private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
                                                     CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool IsHttps)
        {
            TcpConnection connection          = null;
            string        lastRequestHostName = null;

            while (true)
            {
                if (string.IsNullOrEmpty(httpCmd))
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                    break;
                }

                var args = new SessionEventArgs();
                args.Client.TcpClient = client;

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

                    var httpMethod = httpCmdSplit[0];


                    var httpVersion = httpCmdSplit[2];

                    Version version;
                    if (httpVersion == "HTTP/1.1")
                    {
                        version = new Version(1, 1);
                    }
                    else
                    {
                        version = new Version(1, 0);
                    }


                    args.ProxySession.Request.RequestHeaders = new List <HttpHeader>();

                    string tmpLine;
                    while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                    {
                        var header = tmpLine.Split(new char[] { ':' }, 2);
                        args.ProxySession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
                    }



                    var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.ProxySession.Request.Host, httpCmdSplit[1])));
                    args.IsHttps = IsHttps;



                    args.ProxySession.Request.RequestUri = httpRemoteUri;

                    args.ProxySession.Request.Method      = httpMethod;
                    args.ProxySession.Request.HttpVersion = httpVersion;
                    args.Client.ClientStream       = clientStream;
                    args.Client.ClientStreamReader = clientStreamReader;
                    args.Client.ClientStreamWriter = clientStreamWriter;

                    //If requested interception
                    if (BeforeRequest != null)
                    {
                        BeforeRequest(null, args);
                    }

                    args.ProxySession.Request.RequestLocked = true;

                    if (args.ProxySession.Request.CancelRequest)
                    {
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        break;
                    }

                    if (args.ProxySession.Request.UpgradeToWebSocket)
                    {
                        TcpHelper.SendRaw(clientStream, httpCmd, args.ProxySession.Request.RequestHeaders,
                                          httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps);
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        return;
                    }

                    PrepareRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession);
                    //construct the web request that we are going to issue on behalf of the client.
                    connection = connection == null?
                                 TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps)
                                     : lastRequestHostName != args.ProxySession.Request.RequestUri.Host ? TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps)
                            : connection;

                    lastRequestHostName            = args.ProxySession.Request.RequestUri.Host;
                    args.ProxySession.Request.Host = args.ProxySession.Request.RequestUri.Host;
                    args.ProxySession.SetConnection(connection);
                    args.ProxySession.SendRequest();

                    //If request was modified by user
                    if (args.ProxySession.Request.RequestBodyRead)
                    {
                        args.ProxySession.Request.ContentLength = args.ProxySession.Request.RequestBody.Length;
                        var newStream = args.ProxySession.ProxyClient.ServerStreamReader.BaseStream;
                        newStream.Write(args.ProxySession.Request.RequestBody, 0, args.ProxySession.Request.RequestBody.Length);
                    }
                    else
                    {
                        //If its a post/put request, then read the client html body and send it to server
                        if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
                        {
                            SendClientRequestBody(args);
                        }
                    }

                    HandleHttpSessionResponse(args);

                    //if connection is closing exit
                    if (args.ProxySession.Response.ResponseKeepAlive == false)
                    {
                        connection.TcpClient.Close();
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        return;
                    }

                    // read the next request
                    httpCmd = clientStreamReader.ReadLine();
                }
                catch
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                    break;
                }
            }

            if (connection != null)
            {
                TcpConnectionManager.ReleaseClient(connection);
            }
        }
Exemple #4
0
        private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
                                                     CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName)
        {
            if (string.IsNullOrEmpty(httpCmd))
            {
                Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                return;
            }

            var args = new SessionEventArgs(BUFFER_SIZE);

            args.Client = client;


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

                var httpMethod    = httpCmdSplit[0];
                var httpRemoteUri =
                    new Uri(secureTunnelHostName == null ? httpCmdSplit[1] : (secureTunnelHostName + httpCmdSplit[1]));
                var httpVersion = httpCmdSplit[2];

                Version version;
                if (httpVersion == "HTTP/1.1")
                {
                    version = new Version(1, 1);
                }
                else
                {
                    version = new Version(1, 0);
                }

                if (httpRemoteUri.Scheme == Uri.UriSchemeHttps)
                {
                    args.IsHttps = true;
                }

                args.RequestHeaders = new List <HttpHeader>();

                string tmpLine;

                while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                {
                    var header = tmpLine.Split(ColonSpaceSplit, 2, StringSplitOptions.None);
                    args.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
                }

                for (var i = 0; i < args.RequestHeaders.Count; i++)
                {
                    var rawHeader = args.RequestHeaders[i];


                    //if request was upgrade to web-socket protocol then relay the request without proxying
                    if ((rawHeader.Name.ToLower() == "upgrade") && (rawHeader.Value.ToLower() == "websocket"))
                    {
                        TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.RequestHeaders,
                                          httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps);
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        return;
                    }
                }

                //验证服务器证书回调自动验证
                System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
                //construct the web request that we are going to issue on behalf of the client.
                args.ProxyRequest       = (HttpWebRequest)WebRequest.Create(httpRemoteUri);
                args.ProxyRequest.Proxy = null;
                args.ProxyRequest.UseDefaultCredentials = true;
                args.ProxyRequest.Method          = httpMethod;
                args.ProxyRequest.ProtocolVersion = version;
                args.ClientStream                        = clientStream;
                args.ClientStreamReader                  = clientStreamReader;
                args.ClientStreamWriter                  = clientStreamWriter;
                args.ProxyRequest.AllowAutoRedirect      = false;
                args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None;
                args.RequestHostname                     = args.ProxyRequest.RequestUri.Host;
                args.RequestUrl         = args.ProxyRequest.RequestUri.OriginalString;
                args.ClientPort         = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
                args.ClientIpAddress    = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
                args.RequestHttpVersion = version;
                args.RequestIsAlive     = args.ProxyRequest.KeepAlive;
                args.ProxyRequest.ConnectionGroupName       = args.RequestHostname;
                args.ProxyRequest.AllowWriteStreamBuffering = true;


                //If requested interception
                if (BeforeRequest != null)
                {
                    args.RequestEncoding = args.ProxyRequest.GetEncoding();
                    BeforeRequest(null, args);
                }

                args.RequestLocked = true;

                if (args.CancelRequest)
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                    return;
                }

                SetRequestHeaders(args.RequestHeaders, args.ProxyRequest);

                //If request was modified by user
                if (args.RequestBodyRead)
                {
                    args.ProxyRequest.ContentLength = args.RequestBody.Length;
                    var newStream = args.ProxyRequest.GetRequestStream();
                    newStream.Write(args.RequestBody, 0, args.RequestBody.Length);

                    args.ProxyRequest.BeginGetResponse(HandleHttpSessionResponse, args);
                }
                else
                {
                    //If its a post/put request, then read the client html body and send it to server
                    if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
                    {
                        SendClientRequestBody(args);
                    }
                    //Http request body sent, now wait asynchronously for response
                    args.ProxyRequest.BeginGetResponse(HandleHttpSessionResponse, args);
                }

                //Now read the next request (if keep-Alive is enabled, otherwise exit this thread)
                //If client is pipeling the request, this will be immediately hit before response for previous request was made
                httpCmd = clientStreamReader.ReadLine();
                //Http request body sent, now wait for next request
                Task.Factory.StartNew(
                    () =>
                    HandleHttpSessionRequest(args.Client, httpCmd, args.ClientStream, args.ClientStreamReader,
                                             args.ClientStreamWriter, secureTunnelHostName));
            }
            catch
            {
                Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
            }
        }
Exemple #5
0
        private static void HandleClientRequest(TcpClient Client)
        {
            string connectionGroup = null;

            Stream             clientStream        = null;
            CustomBinaryReader clientStreamReader  = null;
            StreamWriter       connectStreamWriter = null;
            string             tunnelHostName      = null;
            int tunnelPort = 0;

            try
            {
                connectionGroup = Dns.GetHostEntry(((IPEndPoint)Client.Client.RemoteEndPoint).Address).HostName;


                clientStream       = Client.GetStream();
                clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
                string securehost = null;

                List <string> requestLines = new List <string>();
                string        tmpLine;
                while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                {
                    requestLines.Add(tmpLine);
                }

                //read the first line HTTP command
                String httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                if (String.IsNullOrEmpty(httpCmd))
                {
                    throw new EndOfStreamException();
                }
                //break up the line into three components
                String[] splitBuffer = httpCmd.Split(spaceSplit, 3);

                String  method    = splitBuffer[0];
                String  remoteUri = splitBuffer[1];
                Version version;
                string  RequestVersion;
                if (splitBuffer[2] == "HTTP/1.1")
                {
                    version        = new Version(1, 1);
                    RequestVersion = "HTTP/1.1";
                }
                else
                {
                    version        = new Version(1, 0);
                    RequestVersion = "HTTP/1.0";
                }

                if (splitBuffer[0].ToUpper() == "CONNECT")
                {
                    //Browser wants to create a secure tunnel
                    //instead = we are going to perform a man in the middle "attack"
                    //the user's browser should warn them of the certification errors,
                    //so we need to install our root certficate in users machine as Certificate Authority.
                    remoteUri      = "https://" + splitBuffer[1];
                    tunnelHostName = splitBuffer[1].Split(':')[0];
                    int.TryParse(splitBuffer[1].Split(':')[1], out tunnelPort);
                    if (tunnelPort == 0)
                    {
                        tunnelPort = 80;
                    }
                    var isSecure = true;
                    for (int i = 1; i < requestLines.Count; i++)
                    {
                        var      rawHeader = requestLines[i];
                        String[] header    = rawHeader.ToLower().Trim().Split(colonSpaceSplit, 2, StringSplitOptions.None);

                        if ((header[0] == "host"))
                        {
                            var hostDetails = header[1].ToLower().Trim().Split(':');
                            if (hostDetails.Length > 1)
                            {
                                isSecure = false;
                            }
                        }
                    }
                    requestLines.Clear();

                    connectStreamWriter = new StreamWriter(clientStream);
                    connectStreamWriter.WriteLine(RequestVersion + " 200 Connection established");
                    connectStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
                    connectStreamWriter.WriteLine(String.Format("connection:close"));
                    connectStreamWriter.WriteLine();
                    connectStreamWriter.Flush();



                    if (tunnelPort != 443)
                    {
                        TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);

                        if (clientStream != null)
                        {
                            clientStream.Close();
                        }

                        return;
                    }
                    Monitor.Enter(certificateAccessLock);
                    var _certificate = ProxyServer.CertManager.CreateCertificate(tunnelHostName); //CertificateHelper.GetCertificate(RootCertificateName, tunnelHostName);
                    Monitor.Exit(certificateAccessLock);

                    SslStream sslStream = null;
                    if (!pinnedCertificateClients.Contains(tunnelHostName) && isSecure)
                    {
                        sslStream = new SslStream(clientStream, true);
                        try
                        {
                            sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
                        }

                        catch (AuthenticationException ex)
                        {
                            if (pinnedCertificateClients.Contains(tunnelHostName) == false)
                            {
                                pinnedCertificateClients.Add(tunnelHostName);
                            }
                            throw ex;
                        }
                    }
                    else
                    {
                        TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);

                        if (clientStream != null)
                        {
                            clientStream.Close();
                        }

                        return;
                    }
                    clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
                    //HTTPS server created - we can now decrypt the client's traffic
                    clientStream = sslStream;

                    while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                    {
                        requestLines.Add(tmpLine);
                    }

                    //read the new http command.
                    httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                    if (String.IsNullOrEmpty(httpCmd))
                    {
                        throw new EndOfStreamException();
                    }

                    securehost = remoteUri;
                }

                int count = 0;


                SessionEventArgs args = new SessionEventArgs(BUFFER_SIZE);

                while (!String.IsNullOrEmpty(httpCmd))
                {
                    count++;

                    MemoryStream mw = null;
                    StreamWriter sw = null;
                    args = new SessionEventArgs(BUFFER_SIZE);

                    try
                    {
                        splitBuffer = httpCmd.Split(spaceSplit, 3);

                        if (splitBuffer.Length != 3)
                        {
                            TcpHelper.SendRaw(httpCmd, tunnelHostName, ref requestLines, args.IsSecure, clientStreamReader.BaseStream);

                            if (clientStream != null)
                            {
                                clientStream.Close();
                            }

                            return;
                        }
                        method    = splitBuffer[0];
                        remoteUri = splitBuffer[1];

                        if (splitBuffer[2] == "HTTP/1.1")
                        {
                            version = new Version(1, 1);
                        }
                        else
                        {
                            version = new Version(1, 0);
                        }

                        if (securehost != null)
                        {
                            remoteUri     = securehost + remoteUri;
                            args.IsSecure = true;
                        }

                        //construct the web request that we are going to issue on behalf of the client.
                        args.ProxyRequest       = (HttpWebRequest)HttpWebRequest.Create(remoteUri.Trim());
                        args.ProxyRequest.Proxy = null;
                        args.ProxyRequest.UseDefaultCredentials = true;
                        args.ProxyRequest.Method          = method;
                        args.ProxyRequest.ProtocolVersion = version;
                        args.ClientStream       = clientStream;
                        args.ClientStreamReader = clientStreamReader;

                        for (int i = 1; i < requestLines.Count; i++)
                        {
                            var      rawHeader = requestLines[i];
                            String[] header    = rawHeader.ToLower().Trim().Split(colonSpaceSplit, 2, StringSplitOptions.None);

                            if ((header[0] == "upgrade") && (header[1] == "websocket"))
                            {
                                TcpHelper.SendRaw(httpCmd, tunnelHostName, ref requestLines, args.IsSecure, clientStreamReader.BaseStream);

                                if (clientStream != null)
                                {
                                    clientStream.Close();
                                }

                                return;
                            }
                        }

                        ReadRequestHeaders(ref requestLines, args.ProxyRequest);


                        int contentLen = (int)args.ProxyRequest.ContentLength;

                        args.ProxyRequest.AllowAutoRedirect      = false;
                        args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None;

                        if (BeforeRequest != null)
                        {
                            args.Hostname   = args.ProxyRequest.RequestUri.Host;
                            args.RequestURL = args.ProxyRequest.RequestUri.OriginalString;

                            args.RequestLength = contentLen;

                            args.HttpVersion = version;
                            args.Port        = ((IPEndPoint)Client.Client.RemoteEndPoint).Port;
                            args.ipAddress   = ((IPEndPoint)Client.Client.RemoteEndPoint).Address;
                            args.IsAlive     = args.ProxyRequest.KeepAlive;

                            BeforeRequest(null, args);
                        }
                        if (args.Cancel)
                        {
                            if (args.IsAlive)
                            {
                                requestLines.Clear();
                                while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                                {
                                    requestLines.Add(tmpLine);
                                }

                                httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }

                        args.ProxyRequest.ConnectionGroupName       = connectionGroup;
                        args.ProxyRequest.AllowWriteStreamBuffering = true;

                        args.FinishedRequestEvent = new ManualResetEvent(false);


                        if (method.ToUpper() == "POST" || method.ToUpper() == "PUT")
                        {
                            args.ProxyRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), args);
                        }
                        else
                        {
                            args.ProxyRequest.BeginGetResponse(new AsyncCallback(HandleServerResponse), args);
                        }


                        if (args.IsSecure)
                        {
                            if (args.ProxyRequest.Method == "POST" || args.ProxyRequest.Method == "PUT")
                            {
                                args.FinishedRequestEvent.WaitOne();
                            }
                            else
                            {
                                args.FinishedRequestEvent.Set();
                            }
                        }
                        else
                        {
                            args.FinishedRequestEvent.WaitOne();
                        }

                        httpCmd = null;
                        if (args.ProxyRequest.KeepAlive)
                        {
                            requestLines.Clear();
                            while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                            {
                                requestLines.Add(tmpLine);
                            }
                            httpCmd = requestLines.Count() > 0 ? requestLines[0] : null;
                        }


                        if (args.ServerResponse != null)
                        {
                            args.ServerResponse.Close();
                        }
                    }
                    catch (IOException ex)
                    {
                        throw ex;
                    }
                    catch (UriFormatException ex)
                    {
                        throw ex;
                    }
                    catch (WebException ex)
                    {
                        throw ex;
                    }
                    finally
                    {
                        if (sw != null)
                        {
                            sw.Close();
                        }
                        if (mw != null)
                        {
                            mw.Close();
                        }

                        if (args.ProxyRequest != null)
                        {
                            args.ProxyRequest.Abort();
                        }
                        if (args.ServerResponseStream != null)
                        {
                            args.ServerResponseStream.Close();
                        }
                    }
                }
            }
            catch (AuthenticationException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            catch (EndOfStreamException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            catch (IOException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            catch (UriFormatException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            catch (WebException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (connectStreamWriter != null)
                {
                    connectStreamWriter.Close();
                }

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

                if (clientStream != null)
                {
                    clientStream.Close();
                }
            }
        }
Exemple #6
0
        private static void HandleClient(TcpClient Client)
        {
            Stream             clientStream        = null;
            CustomBinaryReader clientStreamReader  = null;
            StreamWriter       connectStreamWriter = null;
            string             tunnelHostName      = null;
            int tunnelPort = 0;

            try
            {
                clientStream = Client.GetStream();

                clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
                string securehost = null;

                List <string> requestLines = new List <string>();
                string        tmpLine;
                while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                {
                    requestLines.Add(tmpLine);
                }

                //read the first line HTTP command
                String httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                if (String.IsNullOrEmpty(httpCmd))
                {
                    throw new EndOfStreamException();
                }
                //break up the line into three components (method, remote URL & Http Version)
                String[] splitBuffer = httpCmd.Split(spaceSplit, 3);

                String  method    = splitBuffer[0];
                String  remoteUri = splitBuffer[1];
                Version version;
                string  RequestVersion;
                if (splitBuffer[2] == "HTTP/1.1")
                {
                    version        = new Version(1, 1);
                    RequestVersion = "HTTP/1.1";
                }
                else
                {
                    version        = new Version(1, 0);
                    RequestVersion = "HTTP/1.0";
                }

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (splitBuffer[0].ToUpper() == "CONNECT")
                {
                    //Browser wants to create a secure tunnel
                    //instead = we are going to perform a man in the middle "attack"
                    //the user's browser should warn them of the certification errors,
                    //to avoid that we need to install our root certficate in users machine as Certificate Authority.
                    remoteUri      = "https://" + splitBuffer[1];
                    tunnelHostName = splitBuffer[1].Split(':')[0];
                    int.TryParse(splitBuffer[1].Split(':')[1], out tunnelPort);
                    if (tunnelPort == 0)
                    {
                        tunnelPort = 80;
                    }
                    var isSecure = true;
                    for (int i = 1; i < requestLines.Count; i++)
                    {
                        var      rawHeader = requestLines[i];
                        String[] header    = rawHeader.ToLower().Trim().Split(colonSpaceSplit, 2, StringSplitOptions.None);

                        if ((header[0] == "host"))
                        {
                            var hostDetails = header[1].ToLower().Trim().Split(':');
                            if (hostDetails.Length > 1)
                            {
                                isSecure = false;
                            }
                        }
                    }
                    requestLines.Clear();

                    connectStreamWriter = new StreamWriter(clientStream);
                    connectStreamWriter.WriteLine(RequestVersion + " 200 Connection established");
                    connectStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
                    connectStreamWriter.WriteLine(String.Format("connection:close"));
                    connectStreamWriter.WriteLine();
                    connectStreamWriter.Flush();


                    //If port is not 443 its not a HTTP request, so just relay
                    if (tunnelPort != 443)
                    {
                        TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);

                        if (clientStream != null)
                        {
                            clientStream.Close();
                        }

                        return;
                    }

                    //Create the fake certificate signed using our fake certificate authority
                    Monitor.Enter(certificateAccessLock);
                    var _certificate = ProxyServer.CertManager.CreateCertificate(tunnelHostName);
                    Monitor.Exit(certificateAccessLock);

                    SslStream sslStream = null;
                    //Pinned certificate clients cannot be proxied
                    //Example dropbox.com uses certificate pinning
                    //So just relay the request after identifying it by first failure
                    if (!pinnedCertificateClients.Contains(tunnelHostName) && isSecure)
                    {
                        sslStream = new SslStream(clientStream, true);
                        try
                        {
                            //Successfully managed to authenticate the client using the fake certificate
                            sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
                        }

                        catch (AuthenticationException ex)
                        {
                            //if authentication failed it could be because client uses pinned certificates
                            //So add the hostname to this list so that next time we can relay without touching it (tunnel the request)
                            if (pinnedCertificateClients.Contains(tunnelHostName) == false)
                            {
                                pinnedCertificateClients.Add(tunnelHostName);
                            }
                            throw ex;
                        }
                    }
                    else
                    {
                        //Hostname was a previously failed request due to certificate pinning, just relay (tunnel the request)
                        TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);

                        if (clientStream != null)
                        {
                            clientStream.Close();
                        }

                        return;
                    }
                    clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
                    //HTTPS server created - we can now decrypt the client's traffic
                    clientStream = sslStream;

                    while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                    {
                        requestLines.Add(tmpLine);
                    }

                    //read the new http command.
                    httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                    if (String.IsNullOrEmpty(httpCmd))
                    {
                        throw new EndOfStreamException();
                    }

                    securehost = remoteUri;
                }

                //Now create the request
                HandleHttpSessionRequest(Client, httpCmd, clientStream, tunnelHostName, requestLines, clientStreamReader, securehost);
            }
            catch (AuthenticationException)
            {
                return;
            }
            catch (EndOfStreamException)
            {
                return;
            }
            catch (IOException)
            {
                return;
            }
            catch (UriFormatException)
            {
                return;
            }
            catch (WebException)
            {
                return;
            }
            finally
            {
            }
        }
Exemple #7
0
        private static void HandleHttpSessionRequest(TcpClient Client, string httpCmd, Stream clientStream, string tunnelHostName, List <string> requestLines, CustomBinaryReader clientStreamReader, string securehost)
        {
            if (httpCmd == null)
            {
                return;
            }


            var args = new SessionEventArgs(BUFFER_SIZE);

            args.Client         = Client;
            args.tunnelHostName = tunnelHostName;
            args.securehost     = securehost;
            try
            {
                //break up the line into three components (method, remote URL & Http Version)
                var splitBuffer = httpCmd.Split(spaceSplit, 3);

                if (splitBuffer.Length != 3)
                {
                    TcpHelper.SendRaw(httpCmd, tunnelHostName, ref requestLines, args.IsSSLRequest, clientStreamReader.BaseStream);

                    if (clientStream != null)
                    {
                        clientStream.Close();
                    }

                    return;
                }
                var     method    = splitBuffer[0];
                var     remoteUri = splitBuffer[1];
                Version version;
                if (splitBuffer[2] == "HTTP/1.1")
                {
                    version = new Version(1, 1);
                }
                else
                {
                    version = new Version(1, 0);
                }

                if (securehost != null)
                {
                    remoteUri         = securehost + remoteUri;
                    args.IsSSLRequest = true;
                }

                //construct the web request that we are going to issue on behalf of the client.
                args.ProxyRequest       = (HttpWebRequest)HttpWebRequest.Create(remoteUri.Trim());
                args.ProxyRequest.Proxy = null;
                args.ProxyRequest.UseDefaultCredentials = true;
                args.ProxyRequest.Method          = method;
                args.ProxyRequest.ProtocolVersion = version;
                args.ClientStream       = clientStream;
                args.ClientStreamReader = clientStreamReader;

                for (int i = 1; i < requestLines.Count; i++)
                {
                    var      rawHeader = requestLines[i];
                    String[] header    = rawHeader.ToLower().Trim().Split(colonSpaceSplit, 2, StringSplitOptions.None);

                    //if request was upgrade to web-socket protocol then relay the request without proxying
                    if ((header[0] == "upgrade") && (header[1] == "websocket"))
                    {
                        TcpHelper.SendRaw(httpCmd, tunnelHostName, ref requestLines, args.IsSSLRequest, clientStreamReader.BaseStream);

                        if (clientStream != null)
                        {
                            clientStream.Close();
                        }

                        return;
                    }
                }

                ReadRequestHeaders(ref requestLines, args.ProxyRequest);


                int contentLen = (int)args.ProxyRequest.ContentLength;

                args.ProxyRequest.AllowAutoRedirect      = false;
                args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None;

                //If requested interception
                if (BeforeRequest != null)
                {
                    args.RequestHostname = args.ProxyRequest.RequestUri.Host;
                    args.RequestURL      = args.ProxyRequest.RequestUri.OriginalString;

                    args.RequestLength = contentLen;

                    args.RequestHttpVersion = version;
                    args.ClientPort         = ((IPEndPoint)Client.Client.RemoteEndPoint).Port;
                    args.ClientIpAddress    = ((IPEndPoint)Client.Client.RemoteEndPoint).Address;
                    args.RequestIsAlive     = args.ProxyRequest.KeepAlive;

                    BeforeRequest(null, args);
                }

                string tmpLine;
                if (args.CancelRequest)
                {
                    if (args.RequestIsAlive)
                    {
                        requestLines.Clear();
                        while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                        {
                            requestLines.Add(tmpLine);
                        }

                        httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
                        return;
                    }
                    else
                    {
                        return;
                    }
                }

                args.ProxyRequest.ConnectionGroupName       = args.RequestHostname;
                args.ProxyRequest.AllowWriteStreamBuffering = true;

                //If request was modified by user
                if (args.RequestWasModified)
                {
                    ASCIIEncoding encoding     = new ASCIIEncoding();
                    byte[]        requestBytes = encoding.GetBytes(args.RequestHtmlBody);
                    args.ProxyRequest.ContentLength = requestBytes.Length;
                    Stream newStream = args.ProxyRequest.GetRequestStream();
                    newStream.Write(requestBytes, 0, requestBytes.Length);
                    args.ProxyRequest.BeginGetResponse(new AsyncCallback(HandleHttpSessionResponse), args);
                }
                else
                {
                    //If its a post/put request, then read the client html body and send it to server
                    if (method.ToUpper() == "POST" || method.ToUpper() == "PUT")
                    {
                        args.ProxyRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), args);
                    }
                    else
                    {
                        //otherwise wait for response asynchronously
                        args.ProxyRequest.BeginGetResponse(new AsyncCallback(HandleHttpSessionResponse), args);

                        //Now read the next request (if keep-Alive is enabled, otherwise exit thus thread)
                        //If client is pipeling the request, this will be immediately hit before response for previous request was made
                        if (args.ProxyRequest.KeepAlive)
                        {
                            requestLines = new List <string>();
                            requestLines.Clear();
                            while (!String.IsNullOrEmpty(tmpLine = args.ClientStreamReader.ReadLine()))
                            {
                                requestLines.Add(tmpLine);
                            }
                            httpCmd = requestLines.Count() > 0 ? requestLines[0] : null;
                            HandleHttpSessionRequest(Client, httpCmd, args.ClientStream, args.tunnelHostName, requestLines, args.ClientStreamReader, args.securehost);
                        }
                    }
                }
            }
            catch (IOException)
            {
                return;
            }
            catch (UriFormatException)
            {
                return;
            }
            catch (WebException)
            {
                return;
            }
            finally
            {
            }
        }