//This is called when requests are routed through router to this endpoint
        //For ssl requests
        private static void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
        {
            Stream clientStream = tcpClient.GetStream();
            CustomBinaryReader clientStreamReader = null;
            StreamWriter clientStreamWriter = null;
            X509Certificate2 certificate = null;

            if (endPoint.EnableSsl)
            {
                var sslStream = new SslStream(clientStream, true);
                //if(endPoint.UseServerNameIndication)
                //{
                //   //implement in future once SNI supported by SSL stream
                //    certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);
                //}
                //else
                certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);

                try
                {
                    //Successfully managed to authenticate the client using the fake certificate
                    sslStream.AuthenticateAsServer(certificate, false,
                       SslProtocols.Tls, false);

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

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

                    Dispose(tcpClient, sslStream, clientStreamReader, clientStreamWriter, null);
                    return;
                }
                clientStream = sslStream;
            }
            else
            {
                clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
            }

            var httpCmd = clientStreamReader.ReadLine();

            //Now create the request
            HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                true);
        }
Esempio n. 2
0
        /// <summary>
        /// Copies the specified bytes to the stream from the input stream
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="count"></param>
        /// <param name="onCopy"></param>
        /// <returns></returns>
        private async Task CopyBytesFromStream(CustomBinaryReader reader, long count, Action <byte[], int, int> onCopy)
        {
            var  buffer         = reader.Buffer;
            long remainingBytes = count;

            while (remainingBytes > 0)
            {
                int bytesToRead = buffer.Length;
                if (remainingBytes < bytesToRead)
                {
                    bytesToRead = (int)remainingBytes;
                }

                int bytesRead = await reader.ReadBytesAsync(buffer, bytesToRead);

                if (bytesRead == 0)
                {
                    break;
                }

                remainingBytes -= bytesRead;

                await WriteAsync(buffer, 0, bytesRead);

                onCopy?.Invoke(buffer, 0, bytesRead);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Copies the streams chunked
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="removeChunkedEncoding"></param>
        /// <param name="onCopy"></param>
        /// <returns></returns>
        private async Task CopyBodyChunkedAsync(CustomBinaryReader reader, bool removeChunkedEncoding, Action <byte[], int, int> onCopy)
        {
            while (true)
            {
                string chunkHead = await reader.ReadLineAsync();

                int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);

                if (!removeChunkedEncoding)
                {
                    await WriteLineAsync(chunkHead);
                }

                if (chunkSize != 0)
                {
                    await CopyBytesFromStream(reader, chunkSize, onCopy);
                }

                if (!removeChunkedEncoding)
                {
                    await WriteLineAsync();
                }

                //chunk trail
                await reader.ReadLineAsync();

                if (chunkSize == 0)
                {
                    break;
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Copies the streams chunked
        /// </summary>
        /// <param name="inStreamReader"></param>
        /// <returns></returns>
        internal async Task WriteResponseBodyChunkedAsync(CustomBinaryReader inStreamReader)
        {
            while (true)
            {
                string chunkHead = await inStreamReader.ReadLineAsync();

                int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);

                if (chunkSize != 0)
                {
                    var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
                    await BaseStream.WriteAsync(chunkHeadBytes, 0, chunkHeadBytes.Length);

                    await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);

                    await inStreamReader.CopyBytesToStream(BaseStream, chunkSize);

                    await BaseStream.WriteAsync(ProxyConstants.NewLineBytes, 0, ProxyConstants.NewLineBytes.Length);

                    await inStreamReader.ReadLineAsync();
                }
                else
                {
                    await inStreamReader.ReadLineAsync();

                    await BaseStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);

                    break;
                }
            }
        }
Esempio n. 5
0
        private static void Dispose(TcpClient client, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, SessionEventArgs args)
        {
            if (args != null)
                args.Dispose();

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

            if (clientStreamWriter != null)
                clientStreamWriter.Dispose();

            if (clientStream != null)
                clientStream.Dispose();

            if (client != null)
                client.Close();
        }
Esempio n. 6
0
        /// <summary>
        /// Copies the specified content length number of bytes to the output stream from the given inputs stream
        /// optionally chunked
        /// </summary>
        /// <param name="bufferSize"></param>
        /// <param name="inStreamReader"></param>
        /// <param name="isChunked"></param>
        /// <param name="contentLength"></param>
        /// <returns></returns>
        internal async Task WriteResponseBodyAsync(int bufferSize, CustomBinaryReader inStreamReader, bool isChunked,
                                                   long contentLength)
        {
            if (!isChunked)
            {
                //http 1.0
                if (contentLength == -1)
                {
                    contentLength = long.MaxValue;
                }

                await inStreamReader.CopyBytesToStream(BaseStream, contentLength);
            }
            else
            {
                await WriteResponseBodyChunkedAsync(inStreamReader);
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Copies the specified content length number of bytes to the output stream from the given inputs stream
        /// optionally chunked
        /// </summary>
        /// <param name="streamReader"></param>
        /// <param name="isChunked"></param>
        /// <param name="contentLength"></param>
        /// <param name="removeChunkedEncoding"></param>
        /// <param name="onCopy"></param>
        /// <returns></returns>
        internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength, bool removeChunkedEncoding, Action <byte[], int, int> onCopy)
        {
            //For chunked request we need to read data as they arrive, until we reach a chunk end symbol
            if (isChunked)
            {
                //Need to revist, find any potential bugs
                //send the body bytes to server in chunks
                return(CopyBodyChunkedAsync(streamReader, removeChunkedEncoding, onCopy));
            }

            //http 1.0
            if (contentLength == -1)
            {
                contentLength = long.MaxValue;
            }

            //If not chunked then its easy just read the amount of bytes mentioned in content length header
            return(CopyBytesFromStream(streamReader, contentLength, onCopy));
        }
Esempio n. 8
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 = 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();

            }
        }
        private static void HandleClient(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 = ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443)
                {
                    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,
                            SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, 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(clientStreamReader.BaseStream, 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 ? httpRemoteUri.OriginalString : null);
            }
            catch
            {
                Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
            }
        }
        private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
            CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName)
        {
            TcpConnection connection = null;
            string lastRequestHostName = null;

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

                var args = new SessionEventArgs(BUFFER_SIZE);
                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 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.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]));
                    }

                    SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession);

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

                    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;
                    args.ProxySession.Request.Hostname = args.ProxySession.Request.RequestUri.Host;
                    args.ProxySession.Request.Url = args.ProxySession.Request.RequestUri.OriginalString;
                    args.Client.ClientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
                    args.Client.ClientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address;


                    //If requested interception
                    if (BeforeRequest != null)
                    {
                        args.ProxySession.Request.Encoding = args.ProxySession.GetEncoding();
                        BeforeRequest(null, args);
                    }

                    args.ProxySession.Request.RequestLocked = true;

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


                    //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.Hostname ? TcpConnectionManager.GetClient(args.ProxySession.Request.RequestUri.Host, args.ProxySession.Request.RequestUri.Port, args.IsHttps)
                            : connection;

                    lastRequestHostName = args.ProxySession.Request.Hostname;

                    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);
        }
        //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;
                }
            }


        }
        private static void WriteResponseBody(CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, int BodyLength)
        {
            if (!isChunked)
            {
                int bytesToRead = BUFFER_SIZE;

                if (BodyLength < BUFFER_SIZE)
                    bytesToRead = BodyLength;

                var buffer = new byte[BUFFER_SIZE];

                var bytesRead = 0;
                var totalBytesRead = 0;

                while ((bytesRead += inStreamReader.BaseStream.Read(buffer, 0, bytesToRead)) > 0)
                {
                    outStream.Write(buffer, 0, bytesRead);
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == BodyLength)
                        break;

                    bytesRead = 0;
                    var remainingBytes = (BodyLength - totalBytesRead);
                    bytesToRead = remainingBytes > BUFFER_SIZE ? BUFFER_SIZE : remainingBytes;
                }
            }
            else
                WriteResponseBodyChunked(inStreamReader, outStream);
        }
Esempio n. 13
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)
                String[] 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 = null;

                while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
                {
                    String[] header = tmpLine.Split(colonSpaceSplit, 2, StringSplitOptions.None);
                    args.requestHeaders.Add(new HttpHeader(header[0], header[1]));
                }

                for (int 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;
                    }
                }

                //construct the web request that we are going to issue on behalf of the client.
                args.proxyRequest = (HttpWebRequest)HttpWebRequest.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;
                    Stream newStream = args.proxyRequest.GetRequestStream();
                    newStream.Write(args.requestBody, 0, args.requestBody.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 (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
                    {
                        SendClientRequestBody(args);
                    }
                    //Http request body sent, now wait asynchronously for response
                    args.proxyRequest.BeginGetResponse(new AsyncCallback(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);
            }
        }
Esempio n. 14
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
            {

            }
        }
Esempio n. 15
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
            {

            }
        }