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

Stable #694

Merged
merged 9 commits into from
Dec 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Titanium.Web.Proxy.EventArguments;

namespace Titanium.Web.Proxy.Examples.Basic
{
public static class ProxyEventArgsBaseExtensions
{
public static SampleClientState GetState(this ProxyEventArgsBase args)
{
if (args.ClientUserData == null)
{
args.ClientUserData = new SampleClientState();
}

return (SampleClientState)args.ClientUserData;
}
}
}
39 changes: 34 additions & 5 deletions examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
Expand Down Expand Up @@ -64,6 +61,7 @@ public void StartProxy()
{
proxyServer.BeforeRequest += onRequest;
proxyServer.BeforeResponse += onResponse;
proxyServer.AfterResponse += onAfterResponse;

proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
Expand Down Expand Up @@ -128,19 +126,32 @@ public void Stop()

private async Task<IExternalProxy> onGetCustomUpStreamProxyFunc(SessionEventArgsBase arg)
{
arg.GetState().PipelineInfo.AppendLine(nameof(onGetCustomUpStreamProxyFunc));

// 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)
{
arg.GetState().PipelineInfo.AppendLine(nameof(onCustomUpStreamProxyFailureFunc));

// 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)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectRequest) + ":" + hostname);
await writeToConsole("Tunnel to: " + hostname);

if (hostname.Contains("dropbox.com"))
Expand Down Expand Up @@ -186,12 +197,16 @@ private void WebSocketDataSentReceived(SessionEventArgs args, DataEventArgs e, b

private Task onBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(onBeforeTunnelConnectResponse) + ":" + e.HttpClient.Request.RequestUri);

return Task.FromResult(false);
}

// intercept & cancel redirect or update requests
private async Task onRequest(object sender, SessionEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(onRequest) + ":" + e.HttpClient.Request.RequestUri);

await writeToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
await writeToConsole(e.HttpClient.Request.Url);

Expand Down Expand Up @@ -233,6 +248,8 @@ private async Task onRequest(object sender, SessionEventArgs e)
// Modify response
private async Task multipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(multipartRequestPartSent));

var session = (SessionEventArgs)sender;
await writeToConsole("Multipart form data headers:");
foreach (var header in e.Headers)
Expand All @@ -243,6 +260,8 @@ private async Task multipartRequestPartSent(object sender, MultipartRequestPartS

private async Task onResponse(object sender, SessionEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(onResponse));

if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket)
{
e.DataSent += WebSocket_DataSent;
Expand Down Expand Up @@ -293,13 +312,20 @@ private async Task onResponse(object sender, SessionEventArgs e)
//}
}

private async Task onAfterResponse(object sender, SessionEventArgs e)
{
await writeToConsole($"Pipelineinfo: {e.GetState().PipelineInfo}", ConsoleColor.Yellow);
}

/// <summary>
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateValidation));

// set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == SslPolicyErrors.None)
{
Expand All @@ -316,6 +342,8 @@ public Task OnCertificateValidation(object sender, CertificateValidationEventArg
/// <param name="e"></param>
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
{
e.GetState().PipelineInfo.AppendLine(nameof(OnCertificateSelection));

// set e.clientCertificate to override

return Task.FromResult(0);
Expand Down Expand Up @@ -352,3 +380,4 @@ private async Task writeToConsole(string message, ConsoleColor? consoleColor = n
//}
}
}

10 changes: 10 additions & 0 deletions examples/Titanium.Web.Proxy.Examples.Basic/SampleClientState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Text;

namespace Titanium.Web.Proxy.Examples.Basic
{
public class SampleClientState
{
public StringBuilder PipelineInfo { get; } = new StringBuilder();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
Expand Down
1 change: 1 addition & 0 deletions src/Titanium.Web.Proxy.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">120</s:Int64>
<s:String x:Key="/Default/CodeStyle/CSharpUsing/MandatoryImports/=System/@EntryIndexedValue">System</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVar</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String>
Expand Down
14 changes: 6 additions & 8 deletions src/Titanium.Web.Proxy/CertificateHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ public partial class ProxyServer
/// 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();
Expand All @@ -43,12 +44,13 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
/// 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)
{
Expand All @@ -75,12 +77,8 @@ internal bool ValidateServerCertificate(object sender, X509Certificate certifica
// 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
};

Expand Down
1 change: 0 additions & 1 deletion src/Titanium.Web.Proxy/Compression/CompressionFactory.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Http;

namespace Titanium.Web.Proxy.Compression
{
Expand Down
1 change: 0 additions & 1 deletion src/Titanium.Web.Proxy/Compression/DecompressionFactory.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Http;

namespace Titanium.Web.Proxy.Compression
{
Expand Down
2 changes: 1 addition & 1 deletion src/Titanium.Web.Proxy/Compression/HttpCompression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.IO.Compression;
using System;

namespace Titanium.Web.Proxy.Compression
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using System;
using System.Threading;
using Titanium.Web.Proxy.Network.Tcp;

namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// This is used in transparent endpoint before authenticating client.
/// </summary>
public class BeforeSslAuthenticateEventArgs : EventArgs
public class BeforeSslAuthenticateEventArgs : ProxyEventArgsBase
{
internal readonly CancellationTokenSource TaskCancellationSource;

internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName)
internal BeforeSslAuthenticateEventArgs(TcpClientConnection clientConnection, CancellationTokenSource taskCancellationSource, string sniHostName) : base(clientConnection)
{
TaskCancellationSource = taskCancellationSource;
SniHostName = sniHostName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,42 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// An argument passed on to user for client certificate selection during mutual SSL authentication.
/// </summary>
public class CertificateSelectionEventArgs : EventArgs
public class CertificateSelectionEventArgs : ProxyEventArgsBase
{
/// <summary>
/// The proxy server instance.
/// </summary>
public object? Sender { get; internal set; }
public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost,
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(session.ClientConnection)
{
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ namespace Titanium.Web.Proxy.EventArguments
/// An argument passed on to the user for validating the server certificate
/// during SSL authentication.
/// </summary>
public class CertificateValidationEventArgs : EventArgs
public class CertificateValidationEventArgs : ProxyEventArgsBase
{
public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(session.ClientConnection)
{
Session = session;
Certificate = certificate;
Chain = chain;
SslPolicyErrors = sslPolicyErrors;
}

/// <value>
/// The session.
/// </value>
public SessionEventArgsBase Session { get; }

/// <summary>
/// Server certificate.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
using System;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http;

namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Class that wraps the multipart sent request arguments.
/// </summary>
public class MultipartRequestPartSentEventArgs : EventArgs
public class MultipartRequestPartSentEventArgs : ProxyEventArgsBase
{
internal MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
internal MultipartRequestPartSentEventArgs(SessionEventArgs session, string boundary, HeaderCollection headers) : base(session.ClientConnection)
{
Session = session;
Boundary = boundary;
Headers = headers;
}

/// <value>
/// The session arguments.
/// </value>
public SessionEventArgs Session { get; }

/// <summary>
/// Boundary.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/Titanium.Web.Proxy/EventArguments/ProxyEventArgsBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Titanium.Web.Proxy.Network.Tcp;

namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// The base event arguments
/// </summary>
/// <seealso cref="System.EventArgs" />
public abstract class ProxyEventArgsBase : EventArgs
{
private readonly TcpClientConnection clientConnection;

public object ClientUserData
{
get => clientConnection.ClientUserData;
set => clientConnection.ClientUserData = value;
}

internal ProxyEventArgsBase(TcpClientConnection clientConnection)
{
this.clientConnection = clientConnection;
}
}
}
Loading