Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit 46c5910

Browse files
committed
allow to get local endpoint in certificate validation/selection events #460
1 parent 4febcb0 commit 46c5910

19 files changed

+140
-120
lines changed

examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,21 @@ public void Stop()
129129
private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
130130
{
131131
// this is just to show the functionality, provided values are junk
132-
return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.9", Port = 9090, Password = "fake", UserName = "fake", UseDefaultCredentials = false };
132+
return new ExternalProxy
133+
{
134+
BypassLocalhost = false, HostName = "127.0.0.9", Port = 9090, Password = "fake", UserName = "fake",
135+
UseDefaultCredentials = false
136+
};
133137
}
134138

135139
private async Task<IExternalProxy> onCustomUpStreamProxyFailureFunc(SessionEventArgsBase arg)
136140
{
137141
// this is just to show the functionality, provided values are junk
138-
return new ExternalProxy() { BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2", UseDefaultCredentials = false };
142+
return new ExternalProxy
143+
{
144+
BypassLocalhost = false, HostName = "127.0.0.10", Port = 9191, Password = "fake2", UserName = "fake2",
145+
UseDefaultCredentials = false
146+
};
139147
}
140148

141149
private async Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)

src/Titanium.Web.Proxy/CertificateHandler.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,18 @@ public partial class ProxyServer
1212
/// Call back to override server certificate validation
1313
/// </summary>
1414
/// <param name="sender">The sender object.</param>
15+
/// <param name="sessionArgs">The http session.</param>
1516
/// <param name="certificate">The remote certificate.</param>
1617
/// <param name="chain">The certificate chain.</param>
1718
/// <param name="sslPolicyErrors">Ssl policy errors</param>
1819
/// <returns>Return true if valid certificate.</returns>
19-
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
20+
internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sessionArgs, X509Certificate certificate, X509Chain chain,
2021
SslPolicyErrors sslPolicyErrors)
2122
{
2223
// if user callback is registered then do it
2324
if (ServerCertificateValidationCallback != null)
2425
{
25-
var args = new CertificateValidationEventArgs(certificate, chain, sslPolicyErrors);
26+
var args = new CertificateValidationEventArgs(sessionArgs, certificate, chain, sslPolicyErrors);
2627

2728
// why is the sender null?
2829
ServerCertificateValidationCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
@@ -43,12 +44,13 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
4344
/// Call back to select client certificate used for mutual authentication
4445
/// </summary>
4546
/// <param name="sender">The sender.</param>
47+
/// <param name="sessionArgs">The http session.</param>
4648
/// <param name="targetHost">The remote hostname.</param>
4749
/// <param name="localCertificates">Selected local certificates by SslStream.</param>
4850
/// <param name="remoteCertificate">The remote certificate of server.</param>
4951
/// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param>
5052
/// <returns></returns>
51-
internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
53+
internal X509Certificate? SelectClientCertificate(object sender, SessionEventArgsBase sessionArgs, string targetHost,
5254
X509CertificateCollection localCertificates,
5355
X509Certificate remoteCertificate, string[] acceptableIssuers)
5456
{
@@ -75,12 +77,8 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
7577
// If user call back is registered
7678
if (ClientCertificateSelectionCallback != null)
7779
{
78-
var args = new CertificateSelectionEventArgs
80+
var args = new CertificateSelectionEventArgs(sessionArgs, targetHost, localCertificates, remoteCertificate, acceptableIssuers)
7981
{
80-
TargetHost = targetHost,
81-
LocalCertificates = localCertificates,
82-
RemoteCertificate = remoteCertificate,
83-
AcceptableIssuers = acceptableIssuers,
8482
ClientCertificate = clientCertificate
8583
};
8684

src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,40 @@ namespace Titanium.Web.Proxy.EventArguments
88
/// </summary>
99
public class CertificateSelectionEventArgs : EventArgs
1010
{
11-
/// <summary>
12-
/// The proxy server instance.
13-
/// </summary>
14-
public object? Sender { get; internal set; }
11+
public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost,
12+
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
13+
{
14+
Session = session;
15+
TargetHost = targetHost;
16+
LocalCertificates = localCertificates;
17+
RemoteCertificate = remoteCertificate;
18+
AcceptableIssuers = acceptableIssuers;
19+
}
20+
21+
/// <value>
22+
/// The session.
23+
/// </value>
24+
public SessionEventArgsBase Session { get; }
1525

1626
/// <summary>
1727
/// The remote hostname to which we are authenticating against.
1828
/// </summary>
19-
public string? TargetHost { get; internal set; }
29+
public string TargetHost { get; }
2030

2131
/// <summary>
2232
/// Local certificates in store with matching issuers requested by TargetHost website.
2333
/// </summary>
24-
public X509CertificateCollection? LocalCertificates { get; internal set; }
34+
public X509CertificateCollection LocalCertificates { get; }
2535

2636
/// <summary>
2737
/// Certificate of the remote server.
2838
/// </summary>
29-
public X509Certificate? RemoteCertificate { get; internal set; }
39+
public X509Certificate RemoteCertificate { get; }
3040

3141
/// <summary>
3242
/// Acceptable issuers as listed by remote server.
3343
/// </summary>
34-
public string[]? AcceptableIssuers { get; internal set; }
44+
public string[] AcceptableIssuers { get; }
3545

3646
/// <summary>
3747
/// Client Certificate we selected. Set this value to override.

src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@ namespace Titanium.Web.Proxy.EventArguments
1010
/// </summary>
1111
public class CertificateValidationEventArgs : EventArgs
1212
{
13-
public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
13+
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
1414
{
15+
Session = session;
1516
Certificate = certificate;
1617
Chain = chain;
1718
SslPolicyErrors = sslPolicyErrors;
1819
}
1920

21+
/// <value>
22+
/// The session.
23+
/// </value>
24+
public SessionEventArgsBase Session { get; }
25+
2026
/// <summary>
2127
/// Server certificate.
2228
/// </summary>

src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class SessionEventArgs : SessionEventArgsBase
2626
/// </summary>
2727
private bool reRequest;
2828

29-
private WebSocketDecoder webSocketDecoder;
29+
private WebSocketDecoder? webSocketDecoder;
3030

3131
/// <summary>
3232
/// Is this session a HTTP/2 promise?
@@ -36,8 +36,8 @@ public class SessionEventArgs : SessionEventArgsBase
3636
/// <summary>
3737
/// Constructor to initialize the proxy
3838
/// </summary>
39-
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
40-
: base(server, endPoint, clientConnection, clientStream, connectRequest, new Request(), cancellationTokenSource)
39+
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, HttpClientStream clientStream, ConnectRequest? connectRequest, CancellationTokenSource cancellationTokenSource)
40+
: base(server, endPoint, clientStream, connectRequest, new Request(), cancellationTokenSource)
4141
{
4242
}
4343

src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public abstract class SessionEventArgsBase : EventArgs, IDisposable
2929
/// <summary>
3030
/// Holds a reference to client
3131
/// </summary>
32-
internal TcpClientConnection ClientConnection { get; }
32+
internal TcpClientConnection ClientConnection => ClientStream.Connection;
3333

3434
internal HttpClientStream ClientStream { get; }
3535

@@ -50,17 +50,16 @@ public abstract class SessionEventArgsBase : EventArgs, IDisposable
5050
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
5151
/// </summary>
5252
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
53-
TcpClientConnection clientConnection, HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
53+
HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
5454
{
5555
BufferPool = server.BufferPool;
5656
ExceptionFunc = server.ExceptionFunc;
5757
TimeLine["Session Created"] = DateTime.Now;
5858

5959
CancellationTokenSource = cancellationTokenSource;
6060

61-
ClientConnection = clientConnection;
6261
ClientStream = clientStream;
63-
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => clientConnection.GetProcessId(endPoint)));
62+
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => clientStream.Connection.GetProcessId(endPoint)));
6463
LocalEndPoint = endPoint;
6564
EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported;
6665
}

src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public class TunnelConnectSessionEventArgs : SessionEventArgsBase
1717
private bool? isHttpsConnect;
1818

1919
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
20-
TcpClientConnection clientConnection, HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
21-
: base(server, endPoint, clientConnection, clientStream, connectRequest, connectRequest, cancellationTokenSource)
20+
HttpClientStream clientStream, CancellationTokenSource cancellationTokenSource)
21+
: base(server, endPoint, clientStream, connectRequest, connectRequest, cancellationTokenSource)
2222
{
2323
}
2424

src/Titanium.Web.Proxy/ExplicitClientHandler.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
3636
var cancellationTokenSource = new CancellationTokenSource();
3737
var cancellationToken = cancellationTokenSource.Token;
3838

39-
var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);
39+
var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool);
4040

4141
Task<TcpServerConnection>? prefetchConnectionTask = null;
4242
bool closeServerConnection = false;
@@ -72,8 +72,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
7272

7373
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
7474

75-
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
76-
clientConnection, clientStream, cancellationTokenSource);
75+
connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, clientStream, cancellationTokenSource);
7776
clientStream.DataRead += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
7877
clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);
7978

@@ -137,7 +136,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
137136
if (decryptSsl && clientHelloInfo != null)
138137
{
139138
connectRequest.IsHttps = true; // todo: move this line to the previous "if"
140-
clientConnection.SslProtocol = clientHelloInfo.SslProtocol;
139+
clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;
141140

142141
bool http2Supported = false;
143142

@@ -221,11 +220,11 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
221220
await sslStream.AuthenticateAsServerAsync(options, cancellationToken);
222221

223222
#if NETSTANDARD2_1
224-
clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
223+
clientStream.Connection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
225224
#endif
226225

227226
// HTTPS server created - we can now decrypt the client's traffic
228-
clientStream = new HttpClientStream(sslStream, BufferPool);
227+
clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool);
229228
sslStream = null; // clientStream was created, no need to keep SSL stream reference
230229

231230
clientStream.DataRead += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
@@ -362,13 +361,13 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
362361
var connectionPreface = new ReadOnlyMemory<byte>(Http2Helper.ConnectionPreface);
363362
await connection.Stream.WriteAsync(connectionPreface, cancellationToken);
364363
await Http2Helper.SendHttp2(clientStream, connection.Stream,
365-
() => new SessionEventArgs(this, endPoint, clientConnection, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
364+
() => new SessionEventArgs(this, endPoint, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
366365
{
367366
UserData = connectArgs?.UserData
368367
},
369368
async args => { await onBeforeRequest(args); },
370369
async args => { await onBeforeResponse(args); },
371-
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
370+
connectArgs.CancellationTokenSource, clientStream.Connection.Id, ExceptionFunc);
372371
#endif
373372
}
374373
finally
@@ -381,7 +380,7 @@ await Http2Helper.SendHttp2(clientStream, connection.Stream,
381380
calledRequestHandler = true;
382381

383382
// Now create the request
384-
await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
383+
await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, connectArgs, prefetchConnectionTask);
385384
}
386385
catch (ProxyException e)
387386
{

src/Titanium.Web.Proxy/Extensions/StringExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Titanium.Web.Proxy.Extensions
66
{
77
internal static class StringExtensions
88
{
9-
internal static bool EqualsIgnoreCase(this string str, string value)
9+
internal static bool EqualsIgnoreCase(this string str, string? value)
1010
{
1111
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
1212
}
@@ -16,12 +16,12 @@ internal static bool EqualsIgnoreCase(this ReadOnlySpan<char> str, ReadOnlySpan<
1616
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
1717
}
1818

19-
internal static bool ContainsIgnoreCase(this string str, string value)
19+
internal static bool ContainsIgnoreCase(this string str, string? value)
2020
{
2121
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
2222
}
2323

24-
internal static int IndexOfIgnoreCase(this string str, string value)
24+
internal static int IndexOfIgnoreCase(this string str, string? value)
2525
{
2626
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase);
2727
}

src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
using System.Threading;
44
using System.Threading.Tasks;
55
using Titanium.Web.Proxy.Http;
6+
using Titanium.Web.Proxy.Network.Tcp;
67
using Titanium.Web.Proxy.StreamExtended.BufferPool;
78

89
namespace Titanium.Web.Proxy.Helpers
910
{
1011
internal sealed class HttpClientStream : HttpStream
1112
{
12-
internal HttpClientStream(Stream stream, IBufferPool bufferPool)
13+
public TcpClientConnection Connection { get; }
14+
15+
internal HttpClientStream(TcpClientConnection connection, Stream stream, IBufferPool bufferPool)
1316
: base(stream, bufferPool)
1417
{
18+
Connection = connection;
1519
}
1620

1721
/// <summary>

src/Titanium.Web.Proxy/Helpers/NativeMethods.SystemProxy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Titanium.Web.Proxy.Helpers
66
internal partial class NativeMethods
77
{
88
// Keeps it from getting garbage collected
9-
internal static ConsoleEventDelegate Handler;
9+
internal static ConsoleEventDelegate? Handler;
1010

1111
[DllImport("wininet.dll")]
1212
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,

src/Titanium.Web.Proxy/Helpers/ProxyInfo.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,7 @@ internal static List<HttpSystemProxyValue> GetSystemProxyValues(string? proxySer
211211
if (protocolType.HasValue)
212212
{
213213
var endPointParts = tmp.Substring(equalsIndex + 1).Split(':');
214-
return new HttpSystemProxyValue
215-
{
216-
HostName = endPointParts[0],
217-
Port = int.Parse(endPointParts[1]),
218-
ProtocolType = protocolType.Value
219-
};
214+
return new HttpSystemProxyValue(endPointParts[0], int.Parse(endPointParts[1]), protocolType.Value);
220215
}
221216
}
222217

0 commit comments

Comments
 (0)