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