/// <summary> /// This is the core request handler method for a particular connection from client /// </summary> /// <param name="client"></param> /// <param name="httpCmd"></param> /// <param name="clientStream"></param> /// <param name="clientStreamReader"></param> /// <param name="clientStreamWriter"></param> /// <param name="httpsHostName"></param> /// <returns></returns> private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List <HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null) { TcpConnection connection = null; //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { if (string.IsNullOrEmpty(httpCmd)) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); break; } var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse); args.ProxyClient.TcpClient = client; args.WebSession.ConnectHeaders = connectHeaders; args.WebSession.ProcessId = new Lazy <int>(() => { var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint; //If client is localhost get the process id if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address)) { return(NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled)); } //can't access process Id of remote request from remote machine return(-1); }); try { //break up the line into three components (method, remote URL & Http Version) var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); var httpMethod = httpCmdSplit[0]; //find the request HTTP version Version httpVersion = new Version(1, 1); if (httpCmdSplit.Length == 3) { var httpVersionString = httpCmdSplit[2].ToLower().Trim(); if (httpVersionString == "http/1.0") { httpVersion = new Version(1, 0); } } //Read the request headers in to unique and non-unique header collections string tmpLine; while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); var newHeader = new HttpHeader(header[0], header[1]); //if header exist in non-unique header collection add it there if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name)) { args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader); } //if header is alread in unique header collection then move both to non-unique collection else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name)) { var existing = args.WebSession.Request.RequestHeaders[newHeader.Name]; var nonUniqueHeaders = new List <HttpHeader>(); nonUniqueHeaders.Add(existing); nonUniqueHeaders.Add(newHeader); args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders); args.WebSession.Request.RequestHeaders.Remove(newHeader.Name); } //add to unique header collection else { args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader); } } var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host == null ? httpsHostName : args.WebSession.Request.Host, httpCmdSplit[1]))); args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.Method = httpMethod.Trim().ToUpper(); args.WebSession.Request.HttpVersion = httpVersion; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; if (httpsHostName == null && (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false)) { Dispose(clientStream, clientStreamReader, clientStreamWriter, args); break; } PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession); args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; //If user requested interception do it if (BeforeRequest != null) { Delegate[] invocationList = BeforeRequest.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ((Func <object, SessionEventArgs, Task>)invocationList[i])(null, args); } await Task.WhenAll(handlerTasks); } //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectClientCertificate), clientStream, tcpConnectionFactory); Dispose(clientStream, clientStreamReader, clientStreamWriter, args); break; } //construct the web request that we are going to issue on behalf of the client. await HandleHttpSessionRequestInternal(connection, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false); if (args.WebSession.Request.CancelRequest) { break; } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { break; } // read the next request httpCmd = await clientStreamReader.ReadLineAsync(); } catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args)); Dispose(clientStream, clientStreamReader, clientStreamWriter, args); break; } } if (connection != null) { //dispose connection.Dispose(); } }
//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); } }
//This is called when client is aware of proxy //So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { Stream clientStream = tcpClient.GetStream(); clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; 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(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; List <HttpHeader> connectRequestHeaders = null; //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]); string tmpLine = null; connectRequestHeaders = new List <HttpHeader>(); while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); var newHeader = new HttpHeader(header[0], header[1]); connectRequestHeaders.Add(newHeader); } if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } await WriteConnectResponse(clientStreamWriter, version); SslStream sslStream = null; try { sslStream = new SslStream(clientStream, true); var certificate = 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(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); await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, httpCmd, version, null, false, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectClientCertificate), clientStream, tcpConnectionFactory); Dispose(clientStream, clientStreamReader, clientStreamWriter, null); return; } //Now create the request await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == Uri.UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequestHeaders, null, null); } catch (Exception) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } }
/// <summary> /// This is the core request handler method for a particular connection from client. /// Will create new session (request/response) sequence until /// client/server abruptly terminates connection or by normal HTTP termination. /// </summary> /// <param name="endPoint">The proxy endpoint.</param> /// <param name="clientConnection">The client connection.</param> /// <param name="clientStream">The client stream.</param> /// <param name="clientStreamWriter">The client stream writer.</param> /// <param name="cancellationTokenSource">The cancellation token source for this async task.</param> /// <param name="httpsConnectHostname"> /// The https hostname as appeared in CONNECT request if this is a HTTPS request from /// explicit endpoint. /// </param> /// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param> private async Task HandleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest) { var cancellationToken = cancellationTokenSource.Token; TcpServerConnection serverConnection = null; try { // Loop through each subsequest request on this particular client connection // (assuming HTTP connection is kept alive by client) while (true) { // read the request line string httpCmd = await clientStream.ReadLineAsync(cancellationToken); if (string.IsNullOrEmpty(httpCmd)) { return; } var args = new SessionEventArgs(BufferSize, endPoint, cancellationTokenSource, ExceptionFunc) { ProxyClient = { ClientConnection = clientConnection }, WebSession = { ConnectRequest = connectRequest } }; try { try { Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version); // Read the request headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(clientStream, args.WebSession.Request.Headers, cancellationToken); Uri httpRemoteUri; if (uriSchemeRegex.IsMatch(httpUrl)) { try { httpRemoteUri = new Uri(httpUrl); } catch (Exception ex) { throw new Exception($"Invalid URI: '{httpUrl}'", ex); } } else { string host = args.WebSession.Request.Host ?? httpsConnectHostname; string hostAndPath = host; if (httpUrl.StartsWith("/")) { hostAndPath += httpUrl; } string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://", hostAndPath); try { httpRemoteUri = new Uri(url); } catch (Exception ex) { throw new Exception($"Invalid URI: '{url}'", ex); } } var request = args.WebSession.Request; request.RequestUri = httpRemoteUri; request.OriginalUrl = httpUrl; request.Method = httpMethod; request.HttpVersion = version; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamWriter = clientStreamWriter; if (!args.IsTransparent) { // proxy authorization check if (httpsConnectHostname == null && await CheckAuthorization(args) == false) { await InvokeBeforeResponse(args); // send the response await clientStreamWriter.WriteResponseAsync(args.WebSession.Response, cancellationToken : cancellationToken); return; } PrepareRequestHeaders(request.Headers); request.Host = request.RequestUri.Authority; } // if win auth is enabled // we need a cache of request body // so that we can send it after authentication in WinAuthHandler.cs if (isWindowsAuthenticationEnabledAndSupported && request.HasBody) { await args.GetRequestBody(cancellationToken); } request.OriginalHasBody = request.HasBody; // If user requested interception do it await InvokeBeforeRequest(args); var response = args.WebSession.Response; if (request.CancelRequest) { // syphon out the request body from client before setting the new body await args.SyphonOutBodyAsync(true, cancellationToken); await HandleHttpSessionResponse(args); if (!response.KeepAlive) { return; } continue; } // create a new connection if hostname/upstream end point changes if (serverConnection != null && (!serverConnection.HostName.EqualsIgnoreCase(request.RequestUri.Host) || args.WebSession.UpStreamEndPoint?.Equals(serverConnection.UpStreamEndPoint) == false)) { serverConnection.Dispose(); serverConnection = null; } if (serverConnection == null) { serverConnection = await GetServerConnection(args, false, clientConnection.NegotiatedApplicationProtocol, cancellationToken); } // if upgrading to websocket then relay the requet without reading the contents if (request.UpgradeToWebSocket) { // prepare the prefix content await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken); await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers, cancellationToken : cancellationToken); string httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken); Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, out string responseStatusDescription); response.HttpVersion = responseVersion; response.StatusCode = responseStatusCode; response.StatusDescription = responseStatusDescription; await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers, cancellationToken); if (!args.IsTransparent) { await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken); } // If user requested call back then do it if (!args.WebSession.Response.Locked) { await InvokeBeforeResponse(args); } await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferSize, (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }, cancellationTokenSource, ExceptionFunc); return; } // construct the web request that we are going to issue on behalf of the client. await HandleHttpSessionRequestInternal(serverConnection, args); if (args.WebSession.ServerConnection == null) { return; } // if connection is closing exit if (!response.KeepAlive) { return; } if (cancellationTokenSource.IsCancellationRequested) { throw new Exception("Session was terminated by user."); } } catch (Exception e) when(!(e is ProxyHttpException)) { throw new ProxyHttpException("Error occured whilst handling session request", e, args); } } catch (Exception e) { args.Exception = e; throw; } finally { await InvokeAfterResponse(args); args.Dispose(); } } } finally { serverConnection?.Dispose(); } }
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(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 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])); } SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession); var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.ProxySession.Request.Hostname, httpCmdSplit[1]))); args.IsHttps = IsHttps; 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; } 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); } }
/// <summary> /// 构造函数 /// </summary> /// <param name="ip">服务IP</param> /// <param name="port">服务端口</param> public TcpContextBase(string ip, int port) { tcp = new TcpHelper(ip, port); }
/// <summary> /// This is called when client is aware of proxy /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); try { string connectHostname = null; ConnectRequest connectRequest = null; //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) if (await HttpHelper.IsConnectMethod(clientStream) == 1) { //read the first line HTTP command string httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { return; } Request.ParseRequestLine(httpCmd, out string _, out string httpUrl, out var version); var httpRemoteUri = new Uri("http://" + httpUrl); connectHostname = httpRemoteUri.Host; //filter out excluded host names bool excluded = false; if (endPoint.ExcludedHttpsHostNameRegex != null) { excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname)); } if (endPoint.IncludedHttpsHostNameRegex != null) { excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname)); } if (endPoint.BeforeTunnelConnect != null) { excluded = await endPoint.BeforeTunnelConnect(connectHostname); } connectRequest = new ConnectRequest { RequestUri = httpRemoteUri, OriginalUrl = httpUrl, HttpVersion = version, }; await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers); var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, ExceptionFunc); connectArgs.ProxyClient.TcpClient = tcpClient; connectArgs.ProxyClient.ClientStream = clientStream; await endPoint.InvokeTunnectConnectRequest(this, connectArgs, ExceptionFunc); if (await CheckAuthorization(clientStreamWriter, connectArgs) == false) { await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc); return; } //write back successfull CONNECT response var response = ConnectResponse.CreateSuccessfullConnectResponse(version); response.Headers.FixProxyHeaders(); connectArgs.WebSession.Response = response; await clientStreamWriter.WriteResponseAsync(response); var clientHelloInfo = await SslTools.PeekClientHello(clientStream); bool isClientHello = clientHelloInfo != null; if (isClientHello) { connectRequest.ClientHelloInfo = clientHelloInfo; } await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello); if (!excluded && isClientHello) { connectRequest.RequestUri = new Uri("https://" + httpUrl); SslStream sslStream = null; try { sslStream = new SslStream(clientStream); string certName = HttpHelper.GetWildCardDomainName(connectHostname); var certificate = endPoint.GenericCertificate ?? await CertificateManager.CreateCertificateAsync(certName); //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 = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); } catch { sslStream?.Dispose(); return; } if (await HttpHelper.IsConnectMethod(clientStream) == -1) { // It can be for example some Google (Cloude Messaging for Chrome) magic excluded = true; } } //Hostname is excluded or it is not an HTTPS connect if (excluded || !isClientHello) { //create new connection using (var connection = await GetServerConnection(connectArgs, true)) { if (isClientHello) { int available = clientStream.Available; if (available > 0) { //send the buffered data var data = BufferPool.GetBuffer(BufferSize); try { // clientStream.Available sbould be at most BufferSize because it is using the same buffer size await clientStream.ReadAsync(data, 0, available); await connection.StreamWriter.WriteAsync(data, 0, available, true); } finally { BufferPool.ReturnBuffer(data); } } var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream); ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo; } await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, ExceptionFunc); } return; } } //Now create the request await HandleHttpSessionRequest(tcpClient, clientStream, clientStreamReader, clientStreamWriter, connectHostname, endPoint, connectRequest); } catch (ProxyHttpException e) { ExceptionFunc(e); } catch (IOException e) { ExceptionFunc(new Exception("Connection was aborted", e)); } catch (SocketException e) { ExceptionFunc(new Exception("Could not connect", e)); } catch (Exception e) { ExceptionFunc(new Exception("Error occured in whilst handling the client", e)); } finally { clientStreamReader.Dispose(); clientStream.Dispose(); } }
public void HandshakeTest() { TcpClient tcpClient = new TcpClient(); tcpClient.Client.Connect(new IPEndPoint(IPAddress.Parse(SERVER_ADDRESS), SERVER_PORT)); Assert.IsTrue(tcpClient.Connected); using (NetworkStream tcpStream = tcpClient.GetStream()) { RSAParameters clientPK = CryptographyHelper.GetKeyPair(); RSAParameters serverPK = new RSAParameters(); // Client >> HELLO <public-key> byte[] outdata = Encoding.UTF8.GetBytes(String.Format("HELLO {0}\r\n", CryptographyHelper.ToBase64(clientPK))); tcpStream.Write(outdata, 0, outdata.Length); tcpStream.Flush(); // Server >> OK <public-key> TcpHelper.WaitForDataAvailable(tcpStream); if (tcpStream.DataAvailable) { byte[] indata = new byte[512]; int count = tcpStream.Read(indata, 0, indata.Length); Assert.IsTrue(count > 0); string action = Encoding.UTF8.GetString(indata).Substring(0, 2); string base64 = Encoding.UTF8.GetString(indata).Substring(3, (count - (action.Length + 3))); Assert.AreEqual <string>("OK", action); Assert.IsNotNull(base64); serverPK = CryptographyHelper.FromBase64(base64); Assert.IsNotNull(serverPK); Assert.IsNotNull(serverPK.Exponent); Assert.IsNotNull(serverPK.Modulus); Assert.IsTrue(serverPK.Exponent.Length == 3); Assert.IsTrue(serverPK.Modulus.Length == 256); } byte[] clientCK = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N")); RSA rsa = CryptographyHelper.CreateAsymmetricCryptographyProvider(); rsa.ImportParameters(serverPK); byte[] encClientCK = rsa.Encrypt(clientCK, RSAEncryptionPadding.OaepSHA1); // Client >> ENCRYPT <crypto-key> outdata = Encoding.UTF8.GetBytes(String.Format("ENCRYPT {0}\r\n", Convert.ToBase64String(encClientCK))); tcpStream.Write(outdata, 0, outdata.Length); tcpStream.Flush(); // Server >> OK <crypto-key> TcpHelper.WaitForDataAvailable(tcpStream); if (tcpStream.DataAvailable) { byte[] indata = new byte[512]; int count = tcpStream.Read(indata, 0, indata.Length); Assert.IsTrue(count > 0); string action = Encoding.UTF8.GetString(indata).Substring(0, 2); string base64 = Encoding.UTF8.GetString(indata).Substring(3, (count - (action.Length + 3))); Assert.AreEqual <string>("OK", action); Assert.IsNotNull(base64); byte[] encServerCK = Convert.FromBase64String(base64); Assert.IsNotNull(encServerCK); Assert.IsNotNull(encServerCK.Length == 256); rsa = CryptographyHelper.CreateAsymmetricCryptographyProvider(); rsa.ImportParameters(clientPK); byte[] serverCK = rsa.Decrypt(encServerCK, RSAEncryptionPadding.OaepSHA1); Assert.IsNotNull(serverCK); Assert.IsNotNull(serverCK.Length == 32); } Assert.IsTrue(tcpClient.Connected); } if (tcpClient.Connected) { tcpClient.Dispose(); } }
/// <summary> /// TODO: needs a big rewrite /// </summary> /// <param name="inputDataItems"></param> /// <returns></returns> protected override PropertyBagDataItem[] GetOutputData(DataItemBase[] inputDataItems) { var result = new List <PropertyBagDataItem>(); TcpClient tcpClient; if (Array.IndexOf(TcpHelper.SecureSchemes, _testUri.Scheme.ToLowerInvariant()) == -1) { return(result.ToArray()); // skip checks if scheme is not secure (nothing to check) } bool authSuccess = false; try { if (_customDnsServers.Count > 0) { tcpClient = new TcpClient(TcpHelper.ResolveHostName(_testUri.DnsSafeHost, _customDnsServers.ToArray())[0].ToString(), _testUri.Port); // it always return and address OR exception } else { tcpClient = new TcpClient(_testUri.DnsSafeHost, _testUri.Port); } } catch (Exception ex) { _logger.WriteLog(TraceEventType.Warning, 175, $"Unable to test certificate. Can't connect to {_testUri.Host}. \n\n {ex}"); return(result.ToArray()); } X509Certificate2 remoteCert = null; using (var sslStream = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(RecordCertificateProperties))) { foreach (SslProtocols protocolType in Enum.GetValues(typeof(SslProtocols))) { if (protocolType == SslProtocols.None) { continue; } sslStream.ReadTimeout = 15000; sslStream.WriteTimeout = 15000; try { sslStream.AuthenticateAsClient(_testUri.Host, null, protocolType, false); } catch { continue; }; authSuccess = true; break; } if (!authSuccess) { return(result.ToArray()); } remoteCert = new X509Certificate2(sslStream.RemoteCertificate.GetRawCertData()); } try { // dump some cert properties in a property bag var bagItem = new Dictionary <string, object> { { "DaysToExpire", Math.Round(remoteCert.NotAfter.Subtract(DateTime.UtcNow).TotalDays, 2) }, { "StartDate", remoteCert.NotBefore.ToString("yyyy-MM-dd HH:mm:ss") }, { "EndDate", remoteCert.NotAfter.ToString("yyyy-MM-dd HH:mm:ss") }, { "Issuer", remoteCert.Issuer }, { "Subject", remoteCert.Subject }, { "SerialNumber", remoteCert.SerialNumber }, { "SignatureAlgorithm", remoteCert.SignatureAlgorithm.FriendlyName } }; if (_disabledHashes.TrueForAll(x => x.Value != remoteCert.SignatureAlgorithm.Value)) { bagItem.Add("SignatureAlgorithmDisabled", "false"); } else { bagItem.Add("SignatureAlgorithmDisabled", "true"); } bagItem.Add("Thumbprint", remoteCert.Thumbprint); if (_policyErrors == SslPolicyErrors.None) { bagItem.Add("PolicyErrors", "NONE"); } else { bagItem.Add("PolicyErrors", _policyErrors.ToString()); } // Verify the cert chain if (_validateCertificate) { var certificateChain = new X509Chain(true); // Use Machine Context certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; certificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; certificateChain.Build(remoteCert); if (certificateChain.ChainStatus.Length == 0) { bagItem.Add("CertificateIsValid", "true"); bagItem.Add("CertificateStatus", ""); } else { bagItem.Add("CertificateIsValid", "false"); string strStatusMessage = string.Empty; foreach (var status in certificateChain.ChainStatus) { strStatusMessage += $"{status.StatusInformation}; "; } bagItem.Add("CertificateStatus", strStatusMessage); } } else { bagItem.Add("CertificateIsValid", "true"); bagItem.Add("CertificateStatus", ""); } result.Add(new PropertyBagDataItem(null, new Dictionary <string, Dictionary <string, object> > { { string.Empty, bagItem } })); } catch (Exception ex) { //_logger.TraceEvent(TraceEventType.Warning) } return(result.ToArray()); }
private void Send() { switch (Const.Locator.ParameterSetting.CommModel) { case Commom.CommModel.NetPort: //网口通讯 if (string.IsNullOrWhiteSpace(Cmd)) { throw new Exception("Request cmd can not be null."); } if (string.IsNullOrWhiteSpace(Request)) { throw new Exception("Request can not be null."); } _netClient = new TcpHelper(); if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SdcUrl)) { MessageBoxEx.Show("EFD URL can not be null.", MessageBoxButton.OK); return; } string[] sdc = Const.Locator.ParameterSetting.SdcUrl.Split(':'); if (sdc != null && sdc.Count() != 2) { MessageBoxEx.Show("EFD URL is not in the right format.", MessageBoxButton.OK); return; } bool isConn = _netClient.Connect(IPAddress.Parse(sdc[0]), int.Parse(sdc[1])); if (!isConn) { MessageBoxEx.Show("Failed to connect to EFD.", MessageBoxButton.OK); return; } _netClient.Complated -= _client_Complated; _netClient.Complated += _client_Complated; switch (Cmd) { case "Status": _netClient.Send(0x01, Request); break; case "Sign": _netClient.Send(0x02, Request); break; case "Report": _netClient.Send(0x04, Request); break; default: break; } _netClient.ReciveAsync(); break; case Commom.CommModel.SerialPort: //串口通讯 if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SelectedPort)) { throw new Exception("Port can not be null."); } if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SelectedDataBits)) { throw new Exception("DataBits can not be null."); } if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SelectedBaudRate)) { throw new Exception("BaudRate can not be null."); } if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SelectedParity)) { throw new Exception("Parity can not be null."); } if (string.IsNullOrWhiteSpace(Const.Locator.ParameterSetting.SelectedStopBits)) { throw new Exception("StopBits can not be null."); } if (string.IsNullOrWhiteSpace(Cmd)) { throw new Exception("Request cmd can not be null."); } if (string.IsNullOrWhiteSpace(Request)) { throw new Exception("Request can not be null."); } _client = new SerialClient(Const.Locator.ParameterSetting.SelectedPort, int.Parse(Const.Locator.ParameterSetting.SelectedBaudRate), (Parity)Enum.Parse(typeof(Parity), Const.Locator.ParameterSetting.SelectedParity), int.Parse(Const.Locator.ParameterSetting.SelectedDataBits), (StopBits)Enum.Parse(typeof(StopBits), Const.Locator.ParameterSetting.SelectedStopBits)); _client.Open(); _isCanSend = false; _client.Complated += _client_Complated; switch (Cmd) { case "Status": _client.Send(0x01, Request); break; case "Sign": _client.Send(0x02, Request); break; case "Report": _client.Send(0x04, Request); break; default: break; } break; default: break; } }
/// <summary> /// This is called when this proxy acts as a reverse proxy (like a real http server). /// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client /// </summary> /// <param name="endPoint">The transparent endpoint.</param> /// <param name="clientConnection">The client connection.</param> /// <returns></returns> private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection) { var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize); var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize); Task <TcpServerConnection> prefetchConnectionTask = null; bool closeServerConnection = false; bool calledRequestHandler = false; try { var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken); bool isHttps = clientHelloInfo != null; string httpsHostName = null; if (isHttps) { httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName; var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource) { SniHostName = httpsHostName }; await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc); if (cancellationTokenSource.IsCancellationRequested) { throw new Exception("Session was terminated by user."); } if (endPoint.DecryptSsl && args.DecryptSsl) { //don't pass cancellation token here //it could cause floating server connections when client exits prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port, null, true, null, false, this, UpStreamEndPoint, UpStreamHttpsProxy, false, CancellationToken.None); SslStream sslStream = null; //do client authentication using fake certificate try { sslStream = new SslStream(clientStream); string certName = HttpHelper.GetWildCardDomainName(httpsHostName); var certificate = await CertificateManager.CreateCertificateAsync(certName); // Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); // HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize); } catch (Exception e) { sslStream?.Dispose(); throw new ProxyConnectException( $"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e, null); } } else { var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port, null, false, null, true, this, UpStreamEndPoint, UpStreamHttpsProxy, true, cancellationToken); try { CustomBufferedStream serverStream = null; int available = clientStream.Available; if (available > 0) { // send the buffered data var data = BufferPool.GetBuffer(BufferSize); try { // clientStream.Available sbould be at most BufferSize because it is using the same buffer size await clientStream.ReadAsync(data, 0, available, cancellationToken); serverStream = connection.Stream; await serverStream.WriteAsync(data, 0, available, cancellationToken); await serverStream.FlushAsync(cancellationToken); } finally { BufferPool.ReturnBuffer(data); } } await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize, null, null, cancellationTokenSource, ExceptionFunc); } finally { await tcpConnectionFactory.Release(connection, true); } return; } } calledRequestHandler = true; // HTTPS server created - we can now decrypt the client's traffic // Now create the request await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, cancellationTokenSource, isHttps?httpsHostName : null, null, prefetchConnectionTask); } catch (ProxyException e) { closeServerConnection = true; onException(clientStream, e); } catch (IOException e) { closeServerConnection = true; onException(clientStream, new Exception("Connection was aborted", e)); } catch (SocketException e) { closeServerConnection = true; onException(clientStream, new Exception("Could not connect", e)); } catch (Exception e) { closeServerConnection = true; onException(clientStream, new Exception("Error occured in whilst handling the client", e)); } finally { if (!calledRequestHandler && prefetchConnectionTask != null) { var connection = await prefetchConnectionTask; await tcpConnectionFactory.Release(connection, closeServerConnection); } clientStream.Dispose(); if (!cancellationTokenSource.IsCancellationRequested) { cancellationTokenSource.Cancel(); } } }
private void OnKick(int pos) { TcpHelper.CtosMessage_HsKick(pos); }
public void AddChatMsg(string msg, int player) { string result = ""; switch (player) { case -1: //local name result += Program.I().selectServer.name; result += ":"; break; case 0: //from host result += Program.I().ocgcore.name_0; result += ":"; break; case 1: //from client result += Program.I().ocgcore.name_1; result += ":"; break; case 2: //host tag result += Program.I().ocgcore.name_0_tag; result += ":"; break; case 3: //client tag result += Program.I().ocgcore.name_1_tag; result += ":"; break; case 7: //--- result += "[---]"; result += ":"; break; case 8: //system custom message, no prefix. result += "[System]"; result += ":"; break; case 9: //error message result += "[Script error]"; result += ":"; break; default: //from watcher or unknown result += "[---]"; result += ":"; break; } result += msg; string res = "[888888]" + result + "[-]"; Program.I().book.add(res); Package p = new Package(); p.Fuction = (int)YGOSharp.OCGWrapper.Enums.GameMessage.sibyl_chat; p.Data = new BinaryMaster(); p.Data.writer.WriteUnicode(res, res.Length + 1); TcpHelper.AddRecordLine(p); switch ((YGOSharp.Network.Enums.PlayerType)player) { case YGOSharp.Network.Enums.PlayerType.Red: result = "[FF3030]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.Green: result = "[7CFC00]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.Blue: result = "[4876FF]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.BabyBlue: result = "[63B8FF]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.Pink: result = "[EED2EE]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.Yellow: result = "[EEEE00]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.White: result = "[FAF0E6]" + result + "[-]"; break; case YGOSharp.Network.Enums.PlayerType.Gray: result = "[CDC9C9]" + result + "[-]"; break; } RMSshow_none(result); }
void handler(byte[] buffer) { TcpHelper.CtosMessage_Response(buffer); }
void Update() { if (preWid != Screen.width || preheight != Screen.height) { Resources.UnloadUnusedAssets(); onRESIZED(); } fixALLcamerasPreFrame(); wheelValue = UICamera.GetAxis("Mouse ScrollWheel") * 50; pointedGameObject = null; pointedCollider = null; Ray line = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(line, out hit, (float)1000, rayFilter)) { pointedGameObject = hit.collider.gameObject; pointedCollider = hit.collider; } GameObject hoverobject = UICamera.Raycast(Input.mousePosition) ? UICamera.lastHit.collider.gameObject : null; if (hoverobject != null) { if (hoverobject.layer == 11 || pointedGameObject == null) { pointedGameObject = hoverobject; pointedCollider = UICamera.lastHit.collider; } } InputGetMouseButtonDown_0 = Input.GetMouseButtonDown(0); InputGetMouseButtonUp_0 = Input.GetMouseButtonUp(0); InputGetMouseButtonDown_1 = Input.GetMouseButtonDown(1); InputGetMouseButtonUp_1 = Input.GetMouseButtonUp(1); InputEnterDown = Input.GetKeyDown(KeyCode.Return); InputGetMouseButton_0 = Input.GetMouseButton(0); for (int i = 0; i < servants.Count; i++) { servants[i].Update(); } TcpHelper.preFrameFunction(); delayedTask remove = null; while (true) { remove = null; for (int i = 0; i < delayedTasks.Count; i++) { if (Program.TimePassed() > delayedTasks[i].timeToBeDone) { remove = delayedTasks[i]; try { remove.act(); } catch (System.Exception e) { UnityEngine.Debug.Log(e); } break; } } if (remove != null) { delayedTasks.Remove(remove); } else { break; } } }
/// <summary> /// This is the core request handler method for a particular connection from client /// Will create new session (request/response) sequence until /// client/server abruptly terminates connection or by normal HTTP termination /// </summary> /// <param name="client"></param> /// <param name="clientStream"></param> /// <param name="clientStreamReader"></param> /// <param name="clientStreamWriter"></param> /// <param name="httpsConnectHostname"></param> /// <param name="endPoint"></param> /// <param name="connectRequest"></param> /// <param name="isTransparentEndPoint"></param> /// <returns></returns> private async Task HandleHttpSessionRequest(TcpClient client, CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, string httpsConnectHostname, ProxyEndPoint endPoint, ConnectRequest connectRequest, bool isTransparentEndPoint = false) { TcpConnection connection = null; try { //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { // read the request line string httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { break; } var args = new SessionEventArgs(BufferSize, endPoint, ExceptionFunc) { ProxyClient = { TcpClient = client }, WebSession = { ConnectRequest = connectRequest } }; try { Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version); //Read the request headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers); Uri httpRemoteUri; if (uriSchemeRegex.IsMatch(httpUrl)) { try { httpRemoteUri = new Uri(httpUrl); } catch (Exception ex) { throw new Exception($"Invalid URI: '{httpUrl}'", ex); } } else { string host = args.WebSession.Request.Host ?? httpsConnectHostname; string hostAndPath = host; if (httpUrl.StartsWith("/")) { hostAndPath += httpUrl; } string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://", hostAndPath); try { httpRemoteUri = new Uri(url); } catch (Exception ex) { throw new Exception($"Invalid URI: '{url}'", ex); } } args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.OriginalUrl = httpUrl; args.WebSession.Request.Method = httpMethod; args.WebSession.Request.HttpVersion = version; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; //proxy authorization check if (!args.IsTransparent && httpsConnectHostname == null && await CheckAuthorization(clientStreamWriter, args) == false) { break; } if (!isTransparentEndPoint) { PrepareRequestHeaders(args.WebSession.Request.Headers); args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; } //if win auth is enabled //we need a cache of request body //so that we can send it after authentication in WinAuthHandler.cs if (isWindowsAuthenticationEnabledAndSupported && args.WebSession.Request.HasBody) { await args.GetRequestBody(); } //If user requested interception do it if (BeforeRequest != null) { await BeforeRequest.InvokeAsync(this, args, ExceptionFunc); } var response = args.WebSession.Response; if (args.WebSession.Request.CancelRequest) { await HandleHttpSessionResponse(args); if (!response.KeepAlive) { break; } continue; } //create a new connection if hostname/upstream end point changes if (connection != null && (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase) || (args.WebSession.UpStreamEndPoint != null && !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint)))) { connection.Dispose(); connection = null; } if (connection == null) { connection = await GetServerConnection(args, false); } //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { //prepare the prefix content var requestHeaders = args.WebSession.Request.Headers; await connection.StreamWriter.WriteLineAsync(httpCmd); await connection.StreamWriter.WriteHeadersAsync(requestHeaders); string httpStatus = await connection.StreamReader.ReadLineAsync(); Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, out string responseStatusDescription); response.HttpVersion = responseVersion; response.StatusCode = responseStatusCode; response.StatusDescription = responseStatusDescription; await HeaderParser.ReadHeaders(connection.StreamReader, response.Headers); if (!args.IsTransparent) { await clientStreamWriter.WriteResponseAsync(response); } //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); } await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize, (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }, ExceptionFunc); break; } //construct the web request that we are going to issue on behalf of the client. await HandleHttpSessionRequestInternal(connection, args); //if connection is closing exit if (!response.KeepAlive) { break; } } catch (Exception e) when(!(e is ProxyHttpException)) { throw new ProxyHttpException("Error occured whilst handling session request", e, args); } finally { args.Dispose(); } } } finally { connection?.Dispose(); } }
public void OnHandleNextRequest() { TcpHelper helper = new TcpHelper(client); helper.ReadJsonData(OnReceivedNextRequest); }
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); } }