Commit 46c59104 authored by Honfika's avatar Honfika

allow to get local endpoint in certificate validation/selection events #460

parent 4febcb05
......@@ -129,13 +129,21 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
{
// this is just to show the functionality, provided values are junk
return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.9", Port = 9090, Password = "fake", UserName = "fake", UseDefaultCredentials = false };
return new ExternalProxy
{
BypassLocalhost = false, HostName = "127.0.0.9", Port = 9090, Password = "fake", UserName = "fake",
UseDefaultCredentials = false
};
}
private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
{
// this is just to show the functionality, provided values are junk
return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2", UseDefaultCredentials = false };
return new ExternalProxy
{
BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2",
UseDefaultCredentials = false
};
}
private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
......
......@@ -12,17 +12,18 @@ namespace Titanium.Web.Proxy
/// Call back to override server certificate validation
/// </summary>
/// <param name="sender">The sender object.</param>
/// <param name="sessionArgs">The http session.</param>
/// <param name="certificate">The remote certificate.</param>
/// <param name="chain">The certificate chain.</param>
/// <param name="sslPolicyErrors">Ssl policy errors</param>
/// <returns>Return true if valid certificate.</returns>
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sessionArgs, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs(certificate, chain, sslPolicyErrors);
var args = new CertificateValidationEventArgs(sessionArgs, certificate, chain, sslPolicyErrors);
// why is the sender null?
ServerCertificateValidationCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
......@@ -43,12 +44,13 @@ namespace Titanium.Web.Proxy
/// Call back to select client certificate used for mutual authentication
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="sessionArgs">The http session.</param>
/// <param name="targetHost">The remote hostname.</param>
/// <param name="localCertificates">Selected local certificates by SslStream.</param>
/// <param name="remoteCertificate">The remote certificate of server.</param>
/// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param>
/// <returns></returns>
internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
internal X509Certificate? SelectClientCertificate(object sender, SessionEventArgsBase sessionArgs, string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers)
{
......@@ -75,12 +77,8 @@ namespace Titanium.Web.Proxy
// If user call back is registered
if (ClientCertificateSelectionCallback != null)
{
var args = new CertificateSelectionEventArgs
var args = new CertificateSelectionEventArgs(sessionArgs, targetHost, localCertificates, remoteCertificate, acceptableIssuers)
{
TargetHost = targetHost,
LocalCertificates = localCertificates,
RemoteCertificate = remoteCertificate,
AcceptableIssuers = acceptableIssuers,
ClientCertificate = clientCertificate
};
......
......@@ -8,30 +8,40 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class CertificateSelectionEventArgs : EventArgs
{
/// <summary>
/// The proxy server instance.
/// </summary>
public object? Sender { get; internal set; }
public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost,
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
Session = session;
TargetHost = targetHost;
LocalCertificates = localCertificates;
RemoteCertificate = remoteCertificate;
AcceptableIssuers = acceptableIssuers;
}
/// <value>
/// The session.
/// </value>
public SessionEventArgsBase Session { get; }
/// <summary>
/// The remote hostname to which we are authenticating against.
/// </summary>
public string? TargetHost { get; internal set; }
public string TargetHost { get; }
/// <summary>
/// Local certificates in store with matching issuers requested by TargetHost website.
/// </summary>
public X509CertificateCollection? LocalCertificates { get; internal set; }
public X509CertificateCollection LocalCertificates { get; }
/// <summary>
/// Certificate of the remote server.
/// </summary>
public X509Certificate? RemoteCertificate { get; internal set; }
public X509Certificate RemoteCertificate { get; }
/// <summary>
/// Acceptable issuers as listed by remote server.
/// </summary>
public string[]? AcceptableIssuers { get; internal set; }
public string[] AcceptableIssuers { get; }
/// <summary>
/// Client Certificate we selected. Set this value to override.
......
......@@ -10,13 +10,19 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class CertificateValidationEventArgs : EventArgs
{
public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Session = session;
Certificate = certificate;
Chain = chain;
SslPolicyErrors = sslPolicyErrors;
}
/// <value>
/// The session.
/// </value>
public SessionEventArgsBase Session { get; }
/// <summary>
/// Server certificate.
/// </summary>
......
......@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
private bool reRequest;
private WebSocketDecoder webSocketDecoder;
private WebSocketDecoder? webSocketDecoder;
/// <summary>
/// Is this session a HTTP/2 promise?
......@@ -36,8 +36,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientConnection, clientStream, connectRequest, new Request(), cancellationTokenSource)
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientStream, connectRequest, new Request(), cancellationTokenSource)
{
}
......
......@@ -29,7 +29,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Holds a reference to client
/// </summary>
internal TcpClientConnection ClientConnection { get; }
internal TcpClientConnection ClientConnection => ClientStream.Connection;
internal HttpClientStream ClientStream { get; }
......@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary>
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
{
BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc;
......@@ -58,9 +58,8 @@ namespace Titanium.Web.Proxy.EventArguments
CancellationTokenSource = cancellationTokenSource;
ClientConnection = clientConnection;
ClientStream = clientStream;
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => clientConnection.GetProcessId(endPoint)));
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => clientStream.Connection.GetProcessId(endPoint)));
LocalEndPoint = endPoint;
EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported;
}
......
......@@ -17,8 +17,8 @@ namespace Titanium.Web.Proxy.EventArguments
private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
TcpClientConnection clientConnection, HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientConnection, clientStream, connectRequest, connectRequest, cancellationTokenSource)
HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, clientStream, connectRequest, connectRequest, cancellationTokenSource)
{
}
......
......@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool);
Task<TcpServerConnection>? prefetchConnectionTask = null;
bool closeServerConnection = false;
......@@ -72,8 +72,7 @@ namespace Titanium.Web.Proxy
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
clientConnection, clientStream, cancellationTokenSource);
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, clientStream, cancellationTokenSource);
clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);
......@@ -137,7 +136,7 @@ namespace Titanium.Web.Proxy
if (decryptSsl && clientHelloInfo != null)
{
connectRequest.IsHttps = true; // todo: move this line to the previous "if"
clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;
bool http2Supported = false;
......@@ -221,11 +220,11 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(options, cancellationToken);
#if NETSTANDARD2_1
clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
clientStream.Connection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new HttpClientStream(sslStream, BufferPool);
clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool);
sslStream = null; // clientStream was created, no need to keep SSL stream reference
clientStream.DataRead += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
......@@ -362,13 +361,13 @@ namespace Titanium.Web.Proxy
var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface);
await connection.Stream.WriteAsync(connectionPreface, cancellationToken);
await Http2Helper.SendHttp2(clientStream, connection.Stream,
() => new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
() => new SessionEventArgs(this, endPoint, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
},
async args => { await onBeforeRequest(args); },
async args => { await onBeforeResponse(args); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
connectArgs.CancellationTokenSource, clientStream.Connection.Id, ExceptionFunc);
#endif
}
finally
......@@ -381,7 +380,7 @@ namespace Titanium.Web.Proxy
calledRequestHandler = true;
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
}
catch (ProxyException e)
{
......
......@@ -6,7 +6,7 @@ namespace Titanium.Web.Proxy.Extensions
{
internal static class StringExtensions
{
internal static bool EqualsIgnoreCase(this string str, string value)
internal static bool EqualsIgnoreCase(this string str, string? value)
{
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
......@@ -16,12 +16,12 @@ namespace Titanium.Web.Proxy.Extensions
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
internal static bool ContainsIgnoreCase(this string str, string value)
internal static bool ContainsIgnoreCase(this string str, string? value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
}
internal static int IndexOfIgnoreCase(this string str, string value)
internal static int IndexOfIgnoreCase(this string str, string? value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase);
}
......
......@@ -3,15 +3,19 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.Helpers
{
internal sealed class HttpClientStream : HttpStream
{
internal HttpClientStream(Stream stream, IBufferPool bufferPool)
public TcpClientConnection Connection { get; }
internal HttpClientStream(TcpClientConnection connection, Stream stream, IBufferPool bufferPool)
: base(stream, bufferPool)
{
Connection = connection;
}
/// <summary>
......
......@@ -6,7 +6,7 @@ namespace Titanium.Web.Proxy.Helpers
internal partial class NativeMethods
{
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
internal static ConsoleEventDelegate? Handler;
[DllImport("wininet.dll")]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
......
......@@ -211,12 +211,7 @@ namespace Titanium.Web.Proxy.Helpers
if (protocolType.HasValue)
{
var endPointParts = tmp.Substring(equalsIndex + 1).Split(':');
return new HttpSystemProxyValue
{
HostName = endPointParts[0],
Port = int.Parse(endPointParts[1]),
ProtocolType = protocolType.Value
};
return new HttpSystemProxyValue(endPointParts[0], int.Parse(endPointParts[1]), protocolType.Value);
}
}
......
......@@ -9,11 +9,18 @@ namespace Titanium.Web.Proxy.Helpers
{
internal class HttpSystemProxyValue
{
internal string HostName { get; set; }
internal string HostName { get; }
internal int Port { get; set; }
internal int Port { get; }
internal ProxyProtocolType ProtocolType { get; set; }
internal ProxyProtocolType ProtocolType { get; }
public HttpSystemProxyValue(string hostName, int port, ProxyProtocolType protocolType)
{
HostName = hostName;
Port = port;
ProtocolType = protocolType;
}
public override string ToString()
{
......@@ -56,7 +63,7 @@ namespace Titanium.Web.Proxy.Helpers
AppDomain.CurrentDomain.ProcessExit += (o, args) => RestoreOriginalSettings();
if (Environment.UserInteractive && NativeMethods.GetConsoleWindow() != IntPtr.Zero)
{
NativeMethods.Handler = eventType =>
var handler = new NativeMethods.ConsoleEventDelegate(eventType =>
{
if (eventType != 2)
{
......@@ -65,10 +72,11 @@ namespace Titanium.Web.Proxy.Helpers
RestoreOriginalSettings();
return false;
};
});
NativeMethods.Handler = handler;
// On Console exit make sure we also exit the proxy
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
NativeMethods.SetConsoleCtrlHandler(handler, true);
}
}
......@@ -95,22 +103,12 @@ namespace Titanium.Web.Proxy.Helpers
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0)
{
existingSystemProxyValues.Add(new HttpSystemProxyValue
{
HostName = hostname,
ProtocolType = ProxyProtocolType.Http,
Port = port
});
existingSystemProxyValues.Add(new HttpSystemProxyValue(hostname, port, ProxyProtocolType.Http));
}
if ((protocolType & ProxyProtocolType.Https) != 0)
{
existingSystemProxyValues.Add(new HttpSystemProxyValue
{
HostName = hostname,
ProtocolType = ProxyProtocolType.Https,
Port = port
});
existingSystemProxyValues.Add(new HttpSystemProxyValue(hostname, port, ProxyProtocolType.Https));
}
reg.DeleteValue(regAutoConfigUrl, false);
......
......@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Http
return String.AsSpan().EqualsIgnoreCase(value);
}
internal bool Equals(string value)
internal bool Equals(string? value)
{
return String.EqualsIgnoreCase(value);
}
......
......@@ -141,7 +141,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
}
Size -= removed.Size;
headerFields[tail++] = null;
headerFields[tail++] = null!;
if (tail == headerFields.Length)
{
tail = 0;
......@@ -157,7 +157,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
{
while (tail != head)
{
headerFields[tail++] = null;
headerFields[tail++] = null!;
if (tail == headerFields.Length)
{
tail = 0;
......
......@@ -139,14 +139,14 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="server">The proxy server.</param>
/// <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(ProxyServer server, SessionEventArgsBase session, bool isConnect,
internal Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
{
List<SslApplicationProtocol>? applicationProtocols = null;
......@@ -155,28 +155,28 @@ namespace Titanium.Web.Proxy.Network.Tcp
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
return GetServerConnection(server, session, isConnect, applicationProtocols, noCache, cancellationToken);
return GetServerConnection(proxyServer, session, isConnect, applicationProtocols, noCache, cancellationToken);
}
/// <summary>
/// Create a server connection.
/// </summary>
/// <param name="server">The proxy server.</param>
/// <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(ProxyServer server, SessionEventArgsBase session, bool isConnect,
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, SessionEventArgsBase session, bool isConnect,
List<SslApplicationProtocol>? applicationProtocols, bool noCache, CancellationToken cancellationToken)
{
IExternalProxy? customUpStreamProxy = null;
bool isHttps = session.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null)
if (proxyServer.GetCustomUpStreamProxyFunc != null)
{
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(session);
customUpStreamProxy = await proxyServer.GetCustomUpStreamProxyFunc(session);
}
session.CustomUpStreamProxyUsed = customUpStreamProxy;
......@@ -207,37 +207,36 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
return await GetServerConnection(
host,
port,
proxyServer, host, port,
session.HttpClient.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
server, session, session.HttpClient.UpStreamEndPoint ?? server.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy),
session, session.HttpClient.UpStreamEndPoint ?? proxyServer.UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? proxyServer.UpStreamHttpsProxy : proxyServer.UpStreamHttpProxy),
noCache, cancellationToken);
}
/// <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="proxyServer">The current ProxyServer instance.</param>
/// <param name="session">The session.</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(string remoteHostName, int remotePort,
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer proxyServer, string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy,
bool noCache, CancellationToken cancellationToken)
{
var sslProtocol = session?.ClientConnection.SslProtocol ?? SslProtocols.None;
var sslProtocol = sessionArgs.ClientConnection.SslProtocol;
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
isHttps, applicationProtocols, upStreamEndPoint, externalProxy);
......@@ -264,7 +263,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol,
applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
return connection;
}
......@@ -280,7 +279,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <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="session">The http session.</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>
......@@ -288,7 +287,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <returns></returns>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, SslProtocols sslProtocol, List<SslApplicationProtocol>? applicationProtocols, bool isConnect,
ProxyServer proxyServer, SessionEventArgsBase? session, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
ProxyServer proxyServer, SessionEventArgsBase sessionArgs, IPEndPoint? upStreamEndPoint, IExternalProxy? externalProxy, string cacheKey,
CancellationToken cancellationToken)
{
// deny connection to proxy end points to avoid infinite connection loop.
......@@ -346,14 +345,14 @@ retry:
throw new Exception($"Could not resolve the hostname {hostname}");
}
if (session != null)
if (sessionArgs != null)
{
session.TimeLine["Dns Resolved"] = DateTime.Now;
sessionArgs.TimeLine["Dns Resolved"] = DateTime.Now;
}
Array.Sort(ipAddresses, (x, y) => x.AddressFamily.CompareTo(y.AddressFamily));
Exception lastException = null;
Exception? lastException = null;
for (int i = 0; i < ipAddresses.Length; i++)
{
try
......@@ -427,23 +426,23 @@ retry:
if (tcpClient == null)
{
if (session != null && proxyServer.CustomUpStreamProxyFailureFunc != null)
if (sessionArgs != null && proxyServer.CustomUpStreamProxyFailureFunc != null)
{
var newUpstreamProxy = await proxyServer.CustomUpStreamProxyFailureFunc(session);
var newUpstreamProxy = await proxyServer.CustomUpStreamProxyFailureFunc(sessionArgs);
if (newUpstreamProxy != null)
{
session.CustomUpStreamProxyUsed = newUpstreamProxy;
session.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.Now;
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
sessionArgs.CustomUpStreamProxyUsed = newUpstreamProxy;
sessionArgs.TimeLine["Retrying Upstream Proxy Connection"] = DateTime.Now;
return await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps, sslProtocol, applicationProtocols, isConnect, proxyServer, sessionArgs, upStreamEndPoint, externalProxy, cacheKey, cancellationToken);
}
}
throw new Exception($"Could not establish connection to {hostname}", lastException);
}
if (session != null)
if (sessionArgs != null)
{
session.TimeLine["Connection Established"] = DateTime.Now;
sessionArgs.TimeLine["Connection Established"] = DateTime.Now;
}
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
......@@ -483,8 +482,13 @@ retry:
if (isHttps)
{
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate);
var sslStream = new SslStream(stream, false,
(sender, certificate, chain, sslPolicyErrors) =>
proxyServer.ValidateServerCertificate(sender, sessionArgs, certificate, chain,
sslPolicyErrors),
(sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
proxyServer.SelectClientCertificate(sender, sessionArgs, targetHost, localCertificates,
remoteCertificate, acceptableIssuers));
stream = new HttpServerStream(sslStream, proxyServer.BufferPool);
var options = new SslClientAuthenticationOptions
......@@ -500,9 +504,9 @@ retry:
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif
if (session != null)
if (sessionArgs != null)
{
session.TimeLine["HTTPS Established"] = DateTime.Now;
sessionArgs.TimeLine["HTTPS Established"] = DateTime.Now;
}
}
}
......
......@@ -705,7 +705,7 @@ namespace Titanium.Web.Proxy
throw new ArgumentNullException(nameof(endPoint));
}
if (ProxyEndPoints.Contains(endPoint) == false)
if (!ProxyEndPoints.Contains(endPoint))
{
throw new Exception("Cannot set endPoints not added to proxy as system proxy");
}
......@@ -739,7 +739,7 @@ namespace Titanium.Web.Proxy
try
{
// based on end point type call appropriate request handlers
tcpClient = endPoint.Listener.EndAcceptTcpClient(asyn);
tcpClient = endPoint.Listener!.EndAcceptTcpClient(asyn);
tcpClient.NoDelay = NoDelay;
}
catch (ObjectDisposedException)
......@@ -763,7 +763,7 @@ namespace Titanium.Web.Proxy
}
// Get the listener that handles the client request.
endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
endPoint.Listener!.BeginAcceptTcpClient(onAcceptConnection, endPoint);
}
......@@ -825,7 +825,7 @@ namespace Titanium.Web.Proxy
/// </summary>
private void quitListen(ProxyEndPoint endPoint)
{
endPoint.Listener.Stop();
endPoint.Listener!.Stop();
endPoint.Listener.Server.Dispose();
}
......
......@@ -29,13 +29,12 @@ namespace Titanium.Web.Proxy
/// 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="cancellationTokenSource">The cancellation token source for this async task.</param>
/// <param name="connectArgs">The Connect request if this is a HTTPS request from explicit endpoint.</param>
/// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, HttpClientStream clientStream,
CancellationTokenSource cancellationTokenSource, TunnelConnectSessionEventArgs? connectArgs = null,
Task<TcpServerConnection>? prefetchConnectionTask = null)
{
var connectRequest = connectArgs?.HttpClient.ConnectRequest;
......@@ -64,7 +63,7 @@ namespace Titanium.Web.Proxy
return;
}
var args = new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectRequest, cancellationTokenSource)
var args = new SessionEventArgs(this, endPoint, clientStream, connectRequest, cancellationTokenSource)
{
UserData = connectArgs?.UserData
};
......@@ -163,7 +162,7 @@ namespace Titanium.Web.Proxy
// or when prefetch task has a unexpectedly different connection.
if (connection != null
&& (await tcpConnectionFactory.GetConnectionCacheKey(this, args,
clientConnection.NegotiatedApplicationProtocol)
clientStream.Connection.NegotiatedApplicationProtocol)
!= connection.CacheKey))
{
await tcpConnectionFactory.Release(connection);
......@@ -171,7 +170,7 @@ namespace Titanium.Web.Proxy
}
var result = await handleHttpSessionRequest(args, connection,
clientConnection.NegotiatedApplicationProtocol,
clientStream.Connection.NegotiatedApplicationProtocol,
cancellationToken, cancellationTokenSource);
// update connection to latest used
......
......@@ -33,7 +33,7 @@ namespace Titanium.Web.Proxy
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool);
SslStream? sslStream = null;
......@@ -58,7 +58,7 @@ namespace Titanium.Web.Proxy
if (endPoint.DecryptSsl && args.DecryptSsl)
{
clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;
// do client authentication using certificate
X509Certificate2? certificate = null;
......@@ -74,14 +74,13 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new HttpClientStream(sslStream, BufferPool);
clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool);
sslStream = null; // clientStream was created, no need to keep SSL stream reference
}
catch (Exception e)
{
var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
var session = new SessionEventArgs(this, endPoint, clientConnection, clientStream, null,
cancellationTokenSource);
var session = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
throw new ProxyConnectException(
$"Couldn't authenticate host '{httpsHostName}' with certificate '{certName}'.", e, session);
}
......@@ -89,9 +88,10 @@ namespace Titanium.Web.Proxy
}
else
{
var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
var connection = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, endPoint.Port,
HttpHeader.VersionUnknown, false, null,
true, this, null, UpStreamEndPoint,
true, sessionArgs, UpStreamEndPoint,
UpStreamHttpsProxy, true, cancellationToken);
try
......@@ -131,7 +131,7 @@ namespace Titanium.Web.Proxy
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource);
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource);
}
catch (ProxyException e)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment