//This is called when client is aware of proxy private async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client) { Stream clientStream = client.GetStream(); var clientStreamReader = new CustomBinaryReader(clientStream); var clientStreamWriter = new StreamWriter(clientStream); Uri httpRemoteUri; try { //read the first line HTTP command var httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); return; } //break up the line into three components (method, remote URL & Http Version) var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); //Find the request Verb var httpVerb = httpCmdSplit[0]; if (httpVerb.ToUpper() == "CONNECT") { httpRemoteUri = new Uri("http://" + httpCmdSplit[1]); } else { httpRemoteUri = new Uri(httpCmdSplit[1]); } //parse the HTTP version Version version = new Version(1, 1); if (httpCmdSplit.Length == 3) { string httpVersion = httpCmdSplit[2].Trim(); if (httpVersion == "http/1.0") { version = new Version(1, 0); } } //filter out excluded host names var excluded = endPoint.ExcludedHttpsHostNameRegex != null? endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false; //Client wants to create a secure tcp tunnel (its a HTTPS request) if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80) { httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); await clientStreamReader.ReadAllLinesAsync(); await WriteConnectResponse(clientStreamWriter, version); SslStream sslStream = null; try { //create the Tcp Connection to server and then release it to connection cache //Just doing what CONNECT request is asking as to do var tunnelClient = await tcpConnectionCacheManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version, UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectClientCertificate)); await tcpConnectionCacheManager.ReleaseClient(tunnelClient); sslStream = new SslStream(clientStream, true); var certificate = await certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); //HTTPS server created - we can now decrypt the client's traffic clientStream = sslStream; clientStreamReader = new CustomBinaryReader(sslStream); clientStreamWriter = new StreamWriter(sslStream); } catch { if (sslStream != null) { sslStream.Dispose(); } Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); return; } //Now read the actual HTTPS request line httpCmd = await clientStreamReader.ReadLineAsync(); } //Sorry cannot do a HTTPS request decrypt to port 80 at this time else if (httpVerb.ToUpper() == "CONNECT") { //Cyphen out CONNECT request headers await clientStreamReader.ReadAllLinesAsync(); //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); //Just relay the request/response without decrypting it await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port, false, SupportedSslProtocols); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); return; } //Now create the request await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == Uri.UriSchemeHttps?true : false); } catch { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); } }
/// <summary> /// Creates a TCP connection to server /// </summary> /// <param name="bufferSize"></param> /// <param name="connectionTimeOutSeconds"></param> /// <param name="remoteHostName"></param> /// <param name="httpCmd"></param> /// <param name="httpVersion"></param> /// <param name="isHttps"></param> /// <param name="remotePort"></param> /// <param name="supportedSslProtocols"></param> /// <param name="remoteCertificateValidationCallback"></param> /// <param name="localCertificateSelectionCallback"></param> /// <param name="externalHttpProxy"></param> /// <param name="externalHttpsProxy"></param> /// <param name="clientStream"></param> /// <param name="upStreamEndPoint"></param> /// <returns></returns> internal async Task <TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds, string remoteHostName, int remotePort, Version httpVersion, bool isHttps, SslProtocols supportedSslProtocols, RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy, Stream clientStream, EndPoint upStreamEndPoint) { TcpClient client; Stream stream; if (isHttps) { SslStream sslStream = null; //If this proxy uses another external proxy then create a tunnel request for HTTPS connections if (externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName) { client = new TcpClient(); client.Client.Bind(upStreamEndPoint); await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); stream = client.GetStream(); using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine }) { await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}"); await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}"); await writer.WriteLineAsync("Connection: Keep-Alive"); if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null) { await writer.WriteLineAsync("Proxy-Connection: keep-alive"); await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password))); } await writer.WriteLineAsync(); await writer.FlushAsync(); writer.Close(); } using (var reader = new CustomBinaryReader(stream)) { var result = await reader.ReadLineAsync(); if (!new string[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower()))) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } await reader.ReadAllLinesAsync(); } } else { client = new TcpClient(); client.Client.Bind(upStreamEndPoint); await client.ConnectAsync(remoteHostName, remotePort); stream = client.GetStream(); } try { sslStream = new SslStream(stream, true, remoteCertificateValidationCallback, localCertificateSelectionCallback); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false); stream = sslStream; } catch { sslStream?.Dispose(); throw; } } else { if (externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName) { client = new TcpClient(); client.Client.Bind(upStreamEndPoint); await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port); stream = client.GetStream(); } else { client = new TcpClient(); client.Client.Bind(upStreamEndPoint); await client.ConnectAsync(remoteHostName, remotePort); stream = client.GetStream(); } } client.ReceiveTimeout = connectionTimeOutSeconds * 1000; client.SendTimeout = connectionTimeOutSeconds * 1000; stream.ReadTimeout = connectionTimeOutSeconds * 1000; stream.WriteTimeout = connectionTimeOutSeconds * 1000; return(new TcpConnection() { UpStreamHttpProxy = externalHttpProxy, UpStreamHttpsProxy = externalHttpsProxy, HostName = remoteHostName, Port = remotePort, IsHttps = isHttps, TcpClient = client, StreamReader = new CustomBinaryReader(stream), Stream = stream, Version = httpVersion }); }
private static async Task <TcpConnection> CreateClient(string hostname, int port, bool isHttps, Version version) { TcpClient client; Stream stream; if (isHttps) { CustomSslStream sslStream = null; if (ProxyServer.UpStreamHttpsProxy != null) { client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port); stream = (Stream)client.GetStream(); using (var writer = new StreamWriter(stream, Encoding.ASCII, Constants.BUFFER_SIZE, true)) { await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", hostname, port, version)).ConfigureAwait(false); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", hostname, port)).ConfigureAwait(false); await writer.WriteLineAsync("Connection: Keep-Alive").ConfigureAwait(false); await writer.WriteLineAsync().ConfigureAwait(false); await writer.FlushAsync().ConfigureAwait(false); writer.Close(); } using (var reader = new CustomBinaryReader(stream)) { var result = await reader.ReadLineAsync().ConfigureAwait(false); if (!result.ToLower().Contains("200 connection established")) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } await reader.ReadAllLinesAsync().ConfigureAwait(false); } } else { client = new TcpClient(hostname, port); stream = (Stream)client.GetStream(); } try { sslStream = new CustomSslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate), new LocalCertificateSelectionCallback(ProxyServer.SelectClientCertificate)); await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false).ConfigureAwait(false); stream = (Stream)sslStream; } catch { if (sslStream != null) { sslStream.Dispose(); } throw; } } else { if (ProxyServer.UpStreamHttpProxy != null) { client = new TcpClient(ProxyServer.UpStreamHttpProxy.HostName, ProxyServer.UpStreamHttpProxy.Port); stream = (Stream)client.GetStream(); } else { client = new TcpClient(hostname, port); stream = (Stream)client.GetStream(); } } return(new TcpConnection() { HostName = hostname, port = port, IsHttps = isHttps, TcpClient = client, StreamReader = new CustomBinaryReader(stream), Stream = stream, Version = version }); }
/// <summary> /// Create connection to a particular host/port optionally with SSL and a particular HTTP version /// </summary> /// <param name="hostname"></param> /// <param name="port"></param> /// <param name="isHttps"></param> /// <param name="version"></param> /// <returns></returns> private async Task <TcpConnectionCache> CreateClient(string hostname, int port, bool isHttps, Version version, ExternalProxy upStreamHttpProxy, ExternalProxy upStreamHttpsProxy, int bufferSize, SslProtocols supportedSslProtocols, RemoteCertificateValidationCallback remoteCertificateValidationCallBack, LocalCertificateSelectionCallback localCertificateSelectionCallback) { TcpClient client; Stream stream; if (isHttps) { SslStream sslStream = null; //If this proxy uses another external proxy then create a tunnel request for HTTPS connections if (upStreamHttpsProxy != null) { client = new TcpClient(upStreamHttpsProxy.HostName, upStreamHttpsProxy.Port); stream = (Stream)client.GetStream(); using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)) { await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", hostname, port, version)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", hostname, port)); await writer.WriteLineAsync("Connection: Keep-Alive"); await writer.WriteLineAsync(); await writer.FlushAsync(); writer.Close(); } using (var reader = new CustomBinaryReader(stream)) { var result = await reader.ReadLineAsync(); if (!result.ToLower().Contains("200 connection established")) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } await reader.ReadAllLinesAsync(); } } else { client = new TcpClient(hostname, port); stream = (Stream)client.GetStream(); } try { sslStream = new SslStream(stream, true, remoteCertificateValidationCallBack, localCertificateSelectionCallback); await sslStream.AuthenticateAsClientAsync(hostname, null, supportedSslProtocols, false); stream = (Stream)sslStream; } catch { if (sslStream != null) { sslStream.Dispose(); } throw; } } else { if (upStreamHttpProxy != null) { client = new TcpClient(upStreamHttpProxy.HostName, upStreamHttpProxy.Port); stream = (Stream)client.GetStream(); } else { client = new TcpClient(hostname, port); stream = (Stream)client.GetStream(); } } return(new TcpConnectionCache() { HostName = hostname, port = port, IsHttps = isHttps, TcpClient = client, StreamReader = new CustomBinaryReader(stream), Stream = stream, Version = version }); }
/// <summary> /// Creates a TCP connection to server /// </summary> /// <param name="bufferSize"></param> /// <param name="connectionTimeOutSeconds"></param> /// <param name="remoteHostName"></param> /// <param name="httpCmd"></param> /// <param name="httpVersion"></param> /// <param name="isHttps"></param> /// <param name="remotePort"></param> /// <param name="supportedSslProtocols"></param> /// <param name="remoteCertificateValidationCallback"></param> /// <param name="localCertificateSelectionCallback"></param> /// <param name="externalHttpProxy"></param> /// <param name="externalHttpsProxy"></param> /// <param name="clientStream"></param> /// <returns></returns> internal async Task <TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds, string remoteHostName, int remotePort, Version httpVersion, bool isHttps, SslProtocols supportedSslProtocols, RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy, Stream clientStream) { TcpClient client; Stream stream; if (isHttps) { SslStream sslStream = null; //If this proxy uses another external proxy then create a tunnel request for HTTPS connections if (externalHttpsProxy != null) { client = new TcpClient(externalHttpsProxy.HostName, externalHttpsProxy.Port); stream = client.GetStream(); using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)) { await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", remoteHostName, remotePort, httpVersion)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort)); await writer.WriteLineAsync("Connection: Keep-Alive"); await writer.WriteLineAsync(); await writer.FlushAsync(); writer.Close(); } using (var reader = new CustomBinaryReader(stream)) { var result = await reader.ReadLineAsync(); if (!result.ToLower().Contains("200 connection established")) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } await reader.ReadAllLinesAsync(); } } else { client = new TcpClient(remoteHostName, remotePort); stream = client.GetStream(); } try { sslStream = new SslStream(stream, true, remoteCertificateValidationCallback, localCertificateSelectionCallback); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false); stream = sslStream; } catch { if (sslStream != null) { sslStream.Dispose(); } throw; } } else { if (externalHttpProxy != null) { client = new TcpClient(externalHttpProxy.HostName, externalHttpProxy.Port); stream = client.GetStream(); } else { client = new TcpClient(remoteHostName, remotePort); stream = client.GetStream(); } } client.ReceiveTimeout = connectionTimeOutSeconds * 1000; client.SendTimeout = connectionTimeOutSeconds * 1000; stream.ReadTimeout = connectionTimeOutSeconds * 1000; stream.WriteTimeout = connectionTimeOutSeconds * 1000; return(new TcpConnection() { HostName = remoteHostName, port = remotePort, IsHttps = isHttps, TcpClient = client, StreamReader = new CustomBinaryReader(stream), Stream = stream }); }