public CertificateValidationEventArgs(RequestStateBase state, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(state) { Certificate = certificate; Chain = chain; SslPolicyErrors = sslPolicyErrors; }
public CertificateSelectionEventArgs(RequestStateBase state, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(state) { TargetHost = targetHost; LocalCertificates = localCertificates; RemoteCertificate = remoteCertificate; AcceptableIssuers = acceptableIssuers; }
/// <summary> /// Create a server connection. /// </summary> /// <param name="proxyServer">The proxy server.</param> /// <param name="session">The session event arguments.</param> /// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="applicationProtocols"></param> /// <param name="noCache">if set to <c>true</c> [no cache].</param> /// <param name="cancellationToken">The cancellation token for this async task.</param> /// <returns></returns> internal async Task <TcpServerConnection> GetServerConnection(RequestStateBase state, bool isConnect, List <SslApplicationProtocol>?applicationProtocols, bool noCache, CancellationToken cancellationToken) { var proxyServer = state.Server; var session = state.session !; IExternalProxy?customUpStreamProxy = null; bool isHttps = session.IsHttps; if (proxyServer.GetCustomUpStreamProxyFunc != null) { customUpStreamProxy = await proxyServer.GetCustomUpStreamProxyFunc(session); } session.CustomUpStreamProxyUsed = customUpStreamProxy; var request = session.HttpClient.Request; string host; int port; if (request.Authority.Length > 0) { var authority = request.Authority; int idx = authority.IndexOf((byte)':'); if (idx == -1) { host = authority.GetString(); port = 80; } else { host = authority.Slice(0, idx).GetString(); port = int.Parse(authority.Slice(idx + 1).GetString()); } } else { var uri = request.RequestUri; host = uri.Host; port = uri.Port; } return(await GetServerConnection( state, host, port, session.HttpClient.Request.HttpVersion, isHttps, applicationProtocols, isConnect, session.HttpClient.UpStreamEndPoint ?? proxyServer.UpStreamEndPoint, customUpStreamProxy ?? (isHttps ? proxyServer.UpStreamHttpsProxy : proxyServer.UpStreamHttpProxy), noCache, cancellationToken)); }
/// <summary> /// Create a server connection. /// </summary> /// <param name="proxyServer">The proxy server.</param> /// <param name="session">The session event arguments.</param> /// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="applicationProtocol"></param> /// <param name="noCache">if set to <c>true</c> [no cache].</param> /// <param name="cancellationToken">The cancellation token for this async task.</param> /// <returns></returns> internal Task <TcpServerConnection> GetServerConnection(RequestStateBase state, bool isConnect, SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken) { List <SslApplicationProtocol>?applicationProtocols = null; if (applicationProtocol != default) { applicationProtocols = new List <SslApplicationProtocol> { applicationProtocol }; } return(GetServerConnection(state, isConnect, applicationProtocols, noCache, cancellationToken)); }
/// <summary> /// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class. /// </summary> private protected SessionEventArgsBase(RequestStateBase state, ProxyEndPoint endPoint, HttpClientStream clientStream, ConnectRequest?connectRequest, Request request, CancellationTokenSource cancellationTokenSource) : base(state) { var server = state.Server; BufferPool = server.BufferPool; ExceptionFunc = server.ExceptionFunc; TimeLine["Session Created"] = DateTime.Now; CancellationTokenSource = cancellationTokenSource; ClientStream = clientStream; HttpClient = new HttpWebClient(connectRequest, request, new Lazy <int>(() => clientStream.Connection.GetProcessId(endPoint))); LocalEndPoint = endPoint; EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported; }
/// <summary> /// Gets a TCP connection to server from connection pool. /// </summary> /// <param name="proxyServer">The current ProxyServer instance.</param> /// <param name="remoteHostName">The remote hostname.</param> /// <param name="remotePort">The remote port.</param> /// <param name="httpVersion">The http version to use.</param> /// <param name="isHttps">Is this a HTTPS request.</param> /// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param> /// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="sessionArgs">The session event arguments.</param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <param name="externalProxy">The external proxy to make request via.</param> /// <param name="noCache">Not from cache/create new connection.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param> /// <returns></returns> internal async Task <TcpServerConnection> GetServerConnection(RequestStateBase state, string remoteHostName, int remotePort, Version httpVersion, bool isHttps, List <SslApplicationProtocol>?applicationProtocols, bool isConnect, IPEndPoint?upStreamEndPoint, IExternalProxy?externalProxy, bool noCache, CancellationToken cancellationToken) { var session = state.session; var sslProtocol = session?.ClientConnection.SslProtocol ?? SslProtocols.None; var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort, isHttps, applicationProtocols, upStreamEndPoint, externalProxy); var proxyServer = state.Server; if (proxyServer.EnableConnectionPool && !noCache) { if (cache.TryGetValue(cacheKey, out var existingConnections)) { // +3 seconds for potential delay after getting connection var cutOff = DateTime.Now.AddSeconds(-proxyServer.ConnectionTimeOutSeconds + 3); while (existingConnections.Count > 0) { if (existingConnections.TryDequeue(out var recentConnection)) { if (recentConnection.LastAccess > cutOff && recentConnection.TcpClient.IsGoodConnection()) { return(recentConnection); } disposalBag.Add(recentConnection); } } } } var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, state, upStreamEndPoint, externalProxy, cacheKey, cancellationToken); return(connection); }
/// <summary> /// Constructor to initialize the proxy /// </summary> internal SessionEventArgs(RequestStateBase state, ProxyEndPoint endPoint, HttpClientStream clientStream, ConnectRequest?connectRequest, CancellationTokenSource cancellationTokenSource) : base(state, endPoint, clientStream, connectRequest, new Request(), cancellationTokenSource) { }
/// <summary> /// Creates a TCP connection to server /// </summary> /// <param name="remoteHostName">The remote hostname.</param> /// <param name="remotePort">The remote port.</param> /// <param name="httpVersion">The http version to use.</param> /// <param name="isHttps">Is this a HTTPS request.</param> /// <param name="sslProtocol">The SSL protocol.</param> /// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param> /// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="proxyServer">The current ProxyServer instance.</param> /// <param name="sessionArgs">The http session.</param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <param name="externalProxy">The external proxy to make request via.</param> /// <param name="cacheKey">The connection cache key</param> /// <param name="cancellationToken">The cancellation token for this async task.</param> /// <returns></returns> private async Task <TcpServerConnection> createServerConnection(string remoteHostName, int remotePort, Version httpVersion, bool isHttps, SslProtocols sslProtocol, List <SslApplicationProtocol>?applicationProtocols, bool isConnect, RequestStateBase state, IPEndPoint?upStreamEndPoint, IExternalProxy?externalProxy, string cacheKey, CancellationToken cancellationToken) { var proxyServer = state.Server; var sessionArgs = state.session; // deny connection to proxy end points to avoid infinite connection loop. if (Server.ProxyEndPoints.Any(x => x.Port == remotePort) && NetworkHelper.IsLocalIpAddress(remoteHostName)) { throw new Exception($"A client is making HTTP request to one of the listening ports of this proxy {remoteHostName}:{remotePort}"); } if (externalProxy != null) { if (Server.ProxyEndPoints.Any(x => x.Port == externalProxy.Port) && NetworkHelper.IsLocalIpAddress(externalProxy.HostName)) { throw new Exception($"A client is making HTTP request via external proxy to one of the listening ports of this proxy {remoteHostName}:{remotePort}"); } } bool useUpstreamProxy1 = false; // check if external proxy is set for HTTP/HTTPS if (externalProxy != null && !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort)) { useUpstreamProxy1 = true; // check if we need to ByPass if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName)) { useUpstreamProxy1 = false; } } if (!useUpstreamProxy1) { externalProxy = null; } TcpClient? tcpClient = null; HttpServerStream?stream = null; SslApplicationProtocol negotiatedApplicationProtocol = default; bool retry = true; var enabledSslProtocols = sslProtocol; retry: try { string hostname = externalProxy != null ? externalProxy.HostName : remoteHostName; int port = externalProxy?.Port ?? remotePort; var ipAddresses = await Dns.GetHostAddressesAsync(hostname); if (ipAddresses == null || ipAddresses.Length == 0) { throw new Exception($"Could not resolve the hostname {hostname}"); } if (sessionArgs != null) { sessionArgs.TimeLine["Dns Resolved"] = DateTime.Now; } Array.Sort(ipAddresses, (x, y) => x.AddressFamily.CompareTo(y.AddressFamily)); Exception?lastException = null; for (int i = 0; i < ipAddresses.Length; i++) { try { var ipAddress = ipAddresses[i]; if (upStreamEndPoint == null) { tcpClient = new TcpClient(ipAddress.AddressFamily); } else { tcpClient = new TcpClient(upStreamEndPoint); } tcpClient.NoDelay = proxyServer.NoDelay; tcpClient.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000; tcpClient.SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000; tcpClient.LingerState = new LingerOption(true, proxyServer.TcpTimeWaitSeconds); if (proxyServer.ReuseSocket && RunTime.IsSocketReuseAvailable) { tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } var connectTask = tcpClient.ConnectAsync(ipAddress, port); await Task.WhenAny(connectTask, Task.Delay(proxyServer.ConnectTimeOutSeconds * 1000)); if (!connectTask.IsCompleted || !tcpClient.Connected) { // here we can just do some cleanup and let the loop continue since // we will either get a connection or wind up with a null tcpClient // which will throw try { connectTask.Dispose(); } catch { // ignore } try { #if NET45 tcpClient?.Close(); #else tcpClient?.Dispose(); #endif tcpClient = null; } catch { // ignore } continue; } break; } catch (Exception e) { // dispose the current TcpClient and try the next address lastException = e; #if NET45 tcpClient?.Close(); #else tcpClient?.Dispose(); #endif tcpClient = null; } } if (tcpClient == null) { if (sessionArgs != null && proxyServer.CustomUpStreamProxyFailureFunc != null) { var newUpstreamProxy = await proxyServer.CustomUpStreamProxyFailureFunc(sessionArgs); if (newUpstreamProxy != null) { sessionArgs.CustomUpStreamProxyUsed = newUpstreamProxy; sessionArgs.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.Now; return(await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, state, upStreamEndPoint, externalProxy, cacheKey, cancellationToken)); } } throw new Exception($"Could not establish connection to {hostname}", lastException); } if (sessionArgs != null) { sessionArgs.TimeLine["Connection Established"] = DateTime.Now; } await proxyServer.InvokeServerConnectionCreateEvent(tcpClient); stream = new HttpServerStream(tcpClient.GetStream(), proxyServer.BufferPool); if (externalProxy != null && (isConnect || isHttps)) { var authority = $"{remoteHostName}:{remotePort}".GetByteString(); var connectRequest = new ConnectRequest(authority) { IsHttps = isHttps, RequestUriString8 = authority, HttpVersion = httpVersion }; connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive); if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null) { connectRequest.Headers.AddHeader(HttpHeader.ProxyConnectionKeepAlive); connectRequest.Headers.AddHeader(HttpHeader.GetProxyAuthorizationHeader(externalProxy.UserName, externalProxy.Password)); } await stream.WriteRequestAsync(connectRequest, cancellationToken); var httpStatus = await stream.ReadResponseStatus(cancellationToken); if (httpStatus.StatusCode != 200 && !httpStatus.Description.EqualsIgnoreCase("OK") && !httpStatus.Description.EqualsIgnoreCase("Connection Established")) { throw new Exception("Upstream proxy failed to create a secure tunnel"); } await stream.ReadAndIgnoreAllLinesAsync(cancellationToken); } if (isHttps) { var sslStream = new SslStream(stream, false, state.ValidateServerCertificate, state.SelectClientCertificate); stream = new HttpServerStream(sslStream, proxyServer.BufferPool); var options = new SslClientAuthenticationOptions { ApplicationProtocols = applicationProtocols, TargetHost = remoteHostName, ClientCertificates = null !, EnabledSslProtocols = enabledSslProtocols, CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation }; await sslStream.AuthenticateAsClientAsync(options, cancellationToken); #if NETSTANDARD2_1 negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol; #endif if (sessionArgs != null) { sessionArgs.TimeLine["HTTPS Established"] = DateTime.Now; } } } catch (IOException ex) when(ex.HResult == unchecked ((int)0x80131620) && retry && enabledSslProtocols >= SslProtocols.Tls11) { stream?.Dispose(); tcpClient?.Close(); enabledSslProtocols = SslProtocols.Tls; retry = false; goto retry; } catch (Exception) { stream?.Dispose(); tcpClient?.Close(); throw; } return(new TcpServerConnection(proxyServer, tcpClient, stream, remoteHostName, remotePort, isHttps, negotiatedApplicationProtocol, httpVersion, externalProxy, upStreamEndPoint, cacheKey)); }
public CertificateSelectionEventArgs(RequestStateBase state) : base(state) { }
internal TunnelConnectSessionEventArgs(RequestStateBase state, ProxyEndPoint endPoint, ConnectRequest connectRequest, TcpClientConnection clientConnection, HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource) : base(state, endPoint, clientStream, connectRequest, connectRequest, cancellationTokenSource) { }
internal BeforeSslAuthenticateEventArgs(RequestStateBase state, CancellationTokenSource taskCancellationSource, string sniHostName) : base(state) { TaskCancellationSource = taskCancellationSource; SniHostName = sniHostName; }
public ProxyEventArgsBase(RequestStateBase state) { this.State = state; }
internal static ExtendedProxyState Extended(this RequestStateBase state) { return((ExtendedProxyState)state); }