Commit 1651c926 authored by justcoding121's avatar justcoding121

Merge branch 'develop' into beta

parents 4477f6de f9c7ba4e
...@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public class ProxyTestController public class ProxyTestController
{ {
private readonly ProxyServer proxyServer; private readonly ProxyServer proxyServer;
private ExplicitProxyEndPoint explicitEndPoint;
//keep track of request headers //keep track of request headers
private readonly IDictionary<Guid, HeaderCollection> requestHeaderHistory = new ConcurrentDictionary<Guid, HeaderCollection>(); private readonly IDictionary<Guid, HeaderCollection> requestHeaderHistory = new ConcurrentDictionary<Guid, HeaderCollection>();
...@@ -52,38 +52,31 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -52,38 +52,31 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
proxyServer.BeforeRequest += OnRequest; proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse; proxyServer.BeforeResponse += OnResponse;
proxyServer.TunnelConnectRequest += OnTunnelConnectRequest;
proxyServer.TunnelConnectResponse += OnTunnelConnectResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
//proxyServer.EnableWinAuth = true; //proxyServer.EnableWinAuth = true;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{ {
//Exclude Https addresses you don't want to proxy //You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Useful for clients that use certificate pinning
//for example google.com and dropbox.com //Use self-issued generic certificate on all https requests
ExcludedHttpsHostNameRegex = new List<string> //Optimizes performance by not creating a certificate for each https-enabled domain
{ //Useful when certificate trust is not required by proxy clients
"dropbox.com" //GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
},
//Include Https addresses you want to proxy (others will be excluded)
//for example github.com
//IncludedHttpsHostNameRegex = new List<string>
//{
// "github.com"
//},
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain
//Useful when certificate trust is not required by proxy clients
//GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
}; };
//Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning
//for example google.com and dropbox.com
explicitEndPoint.BeforeTunnelConnect += OnBeforeTunnelConnect;
explicitEndPoint.TunnelConnectRequest += OnTunnelConnectRequest;
explicitEndPoint.TunnelConnectResponse += OnTunnelConnectResponse;
//An explicit endpoint is where the client knows about the existence of a proxy //An explicit endpoint is where the client knows about the existence of a proxy
//So client sends request in a proxy friendly manner //So client sends request in a proxy friendly manner
proxyServer.AddEndPoint(explicitEndPoint); proxyServer.AddEndPoint(explicitEndPoint);
...@@ -120,8 +113,10 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -120,8 +113,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
public void Stop() public void Stop()
{ {
proxyServer.TunnelConnectRequest -= OnTunnelConnectRequest; explicitEndPoint.BeforeTunnelConnect -= OnBeforeTunnelConnect;
proxyServer.TunnelConnectResponse -= OnTunnelConnectResponse; explicitEndPoint.TunnelConnectRequest -= OnTunnelConnectRequest;
explicitEndPoint.TunnelConnectResponse -= OnTunnelConnectResponse;
proxyServer.BeforeRequest -= OnRequest; proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse; proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation; proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
...@@ -133,6 +128,20 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -133,6 +128,20 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.CertificateManager.RemoveTrustedRootCertificates(); //proxyServer.CertificateManager.RemoveTrustedRootCertificates();
} }
private async Task<bool> OnBeforeTunnelConnect(string hostname)
{
if (hostname.Contains("amazon.com") || hostname.Contains("paypal.com"))
{
//exclude bing.com and google.com from being decrypted
//instead it will be relayed via a secure TCP tunnel
return await Task.FromResult(true);
}
else
{
return await Task.FromResult(false);
}
}
private async Task OnTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task OnTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
Console.WriteLine("Tunnel to: " + e.WebSession.Request.Host); Console.WriteLine("Tunnel to: " + e.WebSession.Request.Host);
...@@ -172,7 +181,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -172,7 +181,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//To cancel a request with a custom HTML content //To cancel a request with a custom HTML content
//Filter URL //Filter URL
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com")) if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("yahoo.com"))
{ {
await e.Ok("<!DOCTYPE html>" + await e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" + "<html><body><h1>" +
......
...@@ -109,8 +109,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -109,8 +109,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
proxyServer.BeforeRequest += ProxyServer_BeforeRequest; proxyServer.BeforeRequest += ProxyServer_BeforeRequest;
proxyServer.BeforeResponse += ProxyServer_BeforeResponse; proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.TunnelConnectRequest += ProxyServer_TunnelConnectRequest; explicitEndPoint.TunnelConnectRequest += ProxyServer_TunnelConnectRequest;
proxyServer.TunnelConnectResponse += ProxyServer_TunnelConnectResponse; explicitEndPoint.TunnelConnectResponse += ProxyServer_TunnelConnectResponse;
proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); }; proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); };
proxyServer.ServerConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ServerConnectionCount = proxyServer.ServerConnectionCount; }); }; proxyServer.ServerConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ServerConnectionCount = proxyServer.ServerConnectionCount; }); };
proxyServer.Start(); proxyServer.Start();
......
using System;
using System.IO;
namespace Titanium.Web.Proxy.Helpers
{
/// <summary>
/// A helper class to set proxy settings for firefox
/// </summary>
internal class FireFoxProxySettingsManager
{
/// <summary>
/// Add Firefox settings.
/// </summary>
internal void UseSystemProxy()
{
try
{
var myProfileDirectory =
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Mozilla\\Firefox\\Profiles\\")
.GetDirectories("*.default");
string myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (!File.Exists(myFfPrefFile))
{
return;
}
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
string myPrefContents = myReader.ReadToEnd();
myReader.Close();
for (int i = 0; i <= 4; i++)
{
string searchStr = $"user_pref(\"network.proxy.type\", {i});";
if (myPrefContents.Contains(searchStr))
{
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace(searchStr, "user_pref(\"network.proxy.type\", 5);");
}
}
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
catch (Exception)
{
// Only exception should be a read/write error because the user opened up FireFox so they can be ignored.
}
}
}
}
using System; using StreamExtended.Network;
using System;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -113,5 +115,45 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -113,5 +115,45 @@ namespace Titanium.Web.Proxy.Helpers
//return as it is //return as it is
return hostname; return hostname;
} }
/// <summary>
/// Determines whether is connect method.
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static async Task<int> IsConnectMethod(CustomBufferedStream clientStream)
{
bool isConnect = true;
int legthToCheck = 10;
for (int i = 0; i < legthToCheck; i++)
{
int b = await clientStream.PeekByteAsync(i);
if (b == -1)
{
return -1;
}
if (b == ' ' && i > 2)
{
// at least 3 letters and a space
return isConnect ? 1 : 0;
}
char ch = (char)b;
if (!char.IsLetter(ch))
{
// non letter or too short
return -1;
}
if (i > 6 || ch != "CONNECT"[i])
{
isConnect = false;
}
}
// only letters
return isConnect ? 1 : 0;
}
} }
} }
...@@ -5,6 +5,9 @@ using System.Net; ...@@ -5,6 +5,9 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
...@@ -67,9 +70,15 @@ namespace Titanium.Web.Proxy.Models ...@@ -67,9 +70,15 @@ namespace Titanium.Web.Proxy.Models
internal bool IsSystemHttpsProxy { get; set; } internal bool IsSystemHttpsProxy { get; set; }
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public X509Certificate2 GenericCertificate { get; set; }
/// <summary> /// <summary>
/// List of host names to exclude using Regular Expressions. /// List of host names to exclude using Regular Expressions.
/// </summary> /// </summary>
[Obsolete("ExcludedHttpsHostNameRegex is deprecated, please use BeforeTunnelConnect event instead.")]
public IEnumerable<string> ExcludedHttpsHostNameRegex public IEnumerable<string> ExcludedHttpsHostNameRegex
{ {
get { return ExcludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); } get { return ExcludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
...@@ -87,6 +96,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -87,6 +96,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// List of host names to exclude using Regular Expressions. /// List of host names to exclude using Regular Expressions.
/// </summary> /// </summary>
[Obsolete("IncludedHttpsHostNameRegex is deprecated, please use BeforeTunnelConnect event instead.")]
public IEnumerable<string> IncludedHttpsHostNameRegex public IEnumerable<string> IncludedHttpsHostNameRegex
{ {
get { return IncludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); } get { return IncludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
...@@ -102,10 +112,22 @@ namespace Titanium.Web.Proxy.Models ...@@ -102,10 +112,22 @@ namespace Titanium.Web.Proxy.Models
} }
/// <summary> /// <summary>
/// Generic certificate to use for SSL decryption. /// Return true if this HTTP connect request should'nt be decrypted and instead be relayed
/// Valid only for explicit endpoints
/// </summary> /// </summary>
public X509Certificate2 GenericCertificate { get; set; } public Func<string, Task<bool>> BeforeTunnelConnect;
/// <summary>
/// Intercept tunnel connect request
/// Valid only for explicit endpoints
/// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> TunnelConnectRequest;
/// <summary>
/// Intercept tunnel connect response
/// Valid only for explicit endpoints
/// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> TunnelConnectResponse;
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
...@@ -115,6 +137,31 @@ namespace Titanium.Web.Proxy.Models ...@@ -115,6 +137,31 @@ namespace Titanium.Web.Proxy.Models
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl) public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl)
{ {
} }
internal async Task InvokeTunnectConnectRequest(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, Action<Exception> exceptionFunc)
{
if (TunnelConnectRequest != null)
{
await TunnelConnectRequest.InvokeAsync(proxyServer, connectArgs, exceptionFunc);
}
}
internal async Task InvokeTunnectConnectResponse(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, Action<Exception> exceptionFunc)
{
if (TunnelConnectResponse != null)
{
await TunnelConnectResponse.InvokeAsync(proxyServer, connectArgs, exceptionFunc);
}
}
internal async Task InvokeTunnectConnectResponse(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, Action<Exception> exceptionFunc, bool isClientHello)
{
if (TunnelConnectResponse != null)
{
connectArgs.IsHttpsConnect = isClientHello;
await TunnelConnectResponse.InvokeAsync(proxyServer, connectArgs, exceptionFunc);
}
}
} }
/// <summary> /// <summary>
......
...@@ -27,16 +27,6 @@ namespace Titanium.Web.Proxy ...@@ -27,16 +27,6 @@ namespace Titanium.Web.Proxy
internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp; internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps; internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
/// <summary>
/// An default exception log func
/// </summary>
private readonly Lazy<Action<Exception>> defaultExceptionFunc = new Lazy<Action<Exception>>(() => (e => { }));
/// <summary>
/// backing exception func for exposed public property
/// </summary>
private Action<Exception> exceptionFunc;
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
...@@ -62,51 +52,33 @@ namespace Titanium.Web.Proxy ...@@ -62,51 +52,33 @@ namespace Titanium.Web.Proxy
private SystemProxyManager systemProxySettingsManager { get; } private SystemProxyManager systemProxySettingsManager { get; }
/// <summary> /// <summary>
/// Set firefox to use default system proxy /// An default exception log func
/// </summary>
private readonly FireFoxProxySettingsManager firefoxProxySettingsManager = new FireFoxProxySettingsManager();
/// <summary>
/// Buffer size used throughout this proxy
/// </summary> /// </summary>
public int BufferSize { get; set; } = 8192; private readonly Lazy<Action<Exception>> defaultExceptionFunc = new Lazy<Action<Exception>>(() => (e => { }));
/// <summary> /// <summary>
/// Manages certificates used by this proxy /// backing exception func for exposed public property
/// </summary> /// </summary>
public CertificateManager CertificateManager { get; } private Action<Exception> exceptionFunc;
/// <summary> /// <summary>
/// The root certificate /// Is the proxy currently running
/// </summary> /// </summary>
public X509Certificate2 RootCertificate public bool ProxyRunning { get; private set; }
{
get => CertificateManager.RootCertificate;
set => CertificateManager.RootCertificate = value;
}
/// <summary> /// <summary>
/// Name of the root certificate issuer /// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// (This is valid only when RootCertificate property is not set)
/// </summary> /// </summary>
public string RootCertificateIssuerName public bool ForwardToUpstreamGateway { get; set; }
{
get => CertificateManager.Issuer;
set => CertificateManager.Issuer = value;
}
/// <summary> /// <summary>
/// Name of the root certificate /// Enable disable Windows Authentication (NTLM/Kerberos)
/// (This is valid only when RootCertificate property is not set) /// Note: NTLM/Kerberos will always send local credentials of current user
/// If no certificate is provided then a default Root Certificate will be created and used /// who is running the proxy process. This is because a man
/// The provided root certificate will be stored in proxy exe directory with the private key /// in middle attack is not currently supported
/// Root certificate file will be named as "rootCert.pfx" /// (which would require windows delegation enabled for this server process)
/// </summary> /// </summary>
public string RootCertificateName public bool EnableWinAuth { get; set; }
{
get => CertificateManager.RootCertificateName;
set => CertificateManager.RootCertificateName = value;
}
/// <summary> /// <summary>
/// Trust the RootCertificate used by this proxy server /// Trust the RootCertificate used by this proxy server
...@@ -146,6 +118,73 @@ namespace Titanium.Web.Proxy ...@@ -146,6 +118,73 @@ namespace Titanium.Web.Proxy
set => CertificateManager.OverwritePfXFile = value; set => CertificateManager.OverwritePfXFile = value;
} }
/// <summary>
/// Should we check for certificare revocation during SSL authentication to servers
/// Note: If enabled can reduce performance (Default disabled)
/// </summary>
public bool CheckCertificateRevocation { get; set; }
/// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled
/// </summary>
public bool Enable100ContinueBehaviour { get; set; }
/// <summary>
/// Buffer size used throughout this proxy
/// </summary>
public int BufferSize { get; set; } = 8192;
/// <summary>
/// Minutes certificates should be kept in cache when not used
/// </summary>
public int CertificateCacheTimeOutMinutes { get; set; }
/// <summary>
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete
/// </summary>
public int ConnectionTimeOutSeconds { get; set; }
/// <summary>
/// Total number of active client connections
/// </summary>
public int ClientConnectionCount => clientConnectionCount;
/// <summary>
/// Total number of active server connections
/// </summary>
public int ServerConnectionCount => serverConnectionCount;
/// <summary>
/// Name of the root certificate issuer
/// (This is valid only when RootCertificate property is not set)
/// </summary>
public string RootCertificateIssuerName
{
get => CertificateManager.Issuer;
set => CertificateManager.Issuer = value;
}
/// <summary>
/// Name of the root certificate
/// (This is valid only when RootCertificate property is not set)
/// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate will be stored in proxy exe directory with the private key
/// Root certificate file will be named as "rootCert.pfx"
/// </summary>
public string RootCertificateName
{
get => CertificateManager.RootCertificateName;
set => CertificateManager.RootCertificateName = value;
}
/// <summary>
/// Realm used during Proxy Basic Authentication
/// </summary>
public string ProxyRealm { get; set; } = "TitaniumProxy";
/// <summary> /// <summary>
/// Password of the Root certificate file /// Password of the Root certificate file
/// <para>Set a password for the .pfx file</para> /// <para>Set a password for the .pfx file</para>
...@@ -166,6 +205,24 @@ namespace Titanium.Web.Proxy ...@@ -166,6 +205,24 @@ namespace Titanium.Web.Proxy
set => CertificateManager.PfxFilePath = value; set => CertificateManager.PfxFilePath = value;
} }
/// <summary>
/// List of supported Ssl versions
/// </summary>
public SslProtocols SupportedSslProtocols { get; set; } =
#if NET45
SslProtocols.Ssl3 |
#endif
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
/// <summary>
/// The root certificate
/// </summary>
public X509Certificate2 RootCertificate
{
get => CertificateManager.RootCertificate;
set => CertificateManager.RootCertificate = value;
}
public X509KeyStorageFlags StorageFlag public X509KeyStorageFlags StorageFlag
{ {
get => storageFlag; get => storageFlag;
...@@ -188,46 +245,30 @@ namespace Titanium.Web.Proxy ...@@ -188,46 +245,30 @@ namespace Titanium.Web.Proxy
} }
/// <summary> /// <summary>
/// Should we check for certificare revocation during SSL authentication to servers /// Manages certificates used by this proxy
/// Note: If enabled can reduce performance (Default disabled)
/// </summary>
public bool CheckCertificateRevocation { get; set; }
/// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled
/// </summary>
public bool Enable100ContinueBehaviour { get; set; }
/// <summary>
/// Minutes certificates should be kept in cache when not used
/// </summary>
public int CertificateCacheTimeOutMinutes { get; set; }
/// <summary>
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete
/// </summary> /// </summary>
public int ConnectionTimeOutSeconds { get; set; } public CertificateManager CertificateManager { get; }
/// <summary> /// <summary>
/// Intercept request to server /// External proxy for Http
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeRequest; public ExternalProxy UpStreamHttpProxy { get; set; }
/// <summary> /// <summary>
/// Intercept response from server /// External proxy for Http
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeResponse; public ExternalProxy UpStreamHttpsProxy { get; set; }
/// <summary> /// <summary>
/// Intercept tunnel connect reques /// Local adapter/NIC endpoint (where proxy makes request via)
/// default via any IP addresses of this machine
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> TunnelConnectRequest; public IPEndPoint UpStreamEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
/// <summary> /// <summary>
/// Intercept tunnel connect response /// A list of IpAddress and port this proxy is listening to
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> TunnelConnectResponse; public List<ProxyEndPoint> ProxyEndPoints { get; set; }
/// <summary> /// <summary>
/// Occurs when client connection count changed. /// Occurs when client connection count changed.
...@@ -240,49 +281,30 @@ namespace Titanium.Web.Proxy ...@@ -240,49 +281,30 @@ namespace Titanium.Web.Proxy
public event EventHandler ServerConnectionCountChanged; public event EventHandler ServerConnectionCountChanged;
/// <summary> /// <summary>
/// External proxy for Http /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
public ExternalProxy UpStreamHttpProxy { get; set; }
/// <summary>
/// External proxy for Http
/// </summary>
public ExternalProxy UpStreamHttpsProxy { get; set; }
/// <summary>
/// Local adapter/NIC endpoint (where proxy makes request via)
/// default via any IP addresses of this machine
/// </summary>
public IPEndPoint UpStreamEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
/// <summary>
/// Is the proxy currently running
/// </summary> /// </summary>
public bool ProxyRunning { get; private set; } public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway. /// Callback tooverride client certificate during SSL mutual authentication
/// </summary> /// </summary>
public bool ForwardToUpstreamGateway { get; set; } public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback;
/// <summary> /// <summary>
/// Enable disable Windows Authentication (NTLM/Kerberos) /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests
/// Note: NTLM/Kerberos will always send local credentials of current user /// return the ExternalProxy object with valid credentials
/// who is running the proxy process. This is because a man
/// in middle attack is not currently supported
/// (which would require windows delegation enabled for this server process)
/// </summary> /// </summary>
public bool EnableWinAuth { get; set; } public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
/// <summary> /// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication /// Intercept request to server
/// </summary> /// </summary>
public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback; public event AsyncEventHandler<SessionEventArgs> BeforeRequest;
/// <summary> /// <summary>
/// Callback tooverride client certificate during SSL mutual authentication /// Intercept response from server
/// </summary> /// </summary>
public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback; public event AsyncEventHandler<SessionEventArgs> BeforeResponse;
/// <summary> /// <summary>
/// Callback for error events in proxy /// Callback for error events in proxy
...@@ -300,40 +322,6 @@ namespace Titanium.Web.Proxy ...@@ -300,40 +322,6 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; } public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; }
/// <summary>
/// Realm used during Proxy Basic Authentication
/// </summary>
public string ProxyRealm { get; set; } = "TitaniumProxy";
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
/// <summary>
/// A list of IpAddress and port this proxy is listening to
/// </summary>
public List<ProxyEndPoint> ProxyEndPoints { get; set; }
/// <summary>
/// List of supported Ssl versions
/// </summary>
public SslProtocols SupportedSslProtocols { get; set; } =
#if NET45
SslProtocols.Ssl3 |
#endif
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
/// <summary>
/// Total number of active client connections
/// </summary>
public int ClientConnectionCount => clientConnectionCount;
/// <summary>
/// Total number of active server connections
/// </summary>
public int ServerConnectionCount => serverConnectionCount;
/// <summary> /// <summary>
/// Constructor /// Constructor
...@@ -492,8 +480,6 @@ namespace Titanium.Web.Proxy ...@@ -492,8 +480,6 @@ namespace Titanium.Web.Proxy
endPoint.IsSystemHttpsProxy = true; endPoint.IsSystemHttpsProxy = true;
} }
firefoxProxySettingsManager.UseSystemProxy();
string proxyType = null; string proxyType = null;
switch (protocolType) switch (protocolType)
{ {
......
...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy ...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy
ConnectRequest connectRequest = null; ConnectRequest connectRequest = null;
//Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (await IsConnectMethod(clientStream) == 1) if (await HttpHelper.IsConnectMethod(clientStream) == 1)
{ {
//read the first line HTTP command //read the first line HTTP command
string httpCmd = await clientStreamReader.ReadLineAsync(); string httpCmd = await clientStreamReader.ReadLineAsync();
...@@ -77,6 +77,11 @@ namespace Titanium.Web.Proxy ...@@ -77,6 +77,11 @@ namespace Titanium.Web.Proxy
excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname)); excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
} }
if(endPoint.BeforeTunnelConnect!=null)
{
excluded = await endPoint.BeforeTunnelConnect(connectHostname);
}
connectRequest = new ConnectRequest connectRequest = new ConnectRequest
{ {
RequestUri = httpRemoteUri, RequestUri = httpRemoteUri,
...@@ -90,18 +95,12 @@ namespace Titanium.Web.Proxy ...@@ -90,18 +95,12 @@ namespace Titanium.Web.Proxy
connectArgs.ProxyClient.TcpClient = tcpClient; connectArgs.ProxyClient.TcpClient = tcpClient;
connectArgs.ProxyClient.ClientStream = clientStream; connectArgs.ProxyClient.ClientStream = clientStream;
if (TunnelConnectRequest != null) await endPoint.InvokeTunnectConnectRequest(this, connectArgs, ExceptionFunc);
{
await TunnelConnectRequest.InvokeAsync(this, connectArgs, ExceptionFunc);
}
if (await CheckAuthorization(clientStreamWriter, connectArgs) == false) if (await CheckAuthorization(clientStreamWriter, connectArgs) == false)
{ {
if (TunnelConnectResponse != null) await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc);
{
await TunnelConnectResponse.InvokeAsync(this, connectArgs, ExceptionFunc);
}
return; return;
} }
...@@ -119,11 +118,7 @@ namespace Titanium.Web.Proxy ...@@ -119,11 +118,7 @@ namespace Titanium.Web.Proxy
connectRequest.ClientHelloInfo = clientHelloInfo; connectRequest.ClientHelloInfo = clientHelloInfo;
} }
if (TunnelConnectResponse != null) await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);
{
connectArgs.IsHttpsConnect = isClientHello;
await TunnelConnectResponse.InvokeAsync(this, connectArgs, ExceptionFunc);
}
if (!excluded && isClientHello) if (!excluded && isClientHello)
{ {
...@@ -155,7 +150,7 @@ namespace Titanium.Web.Proxy ...@@ -155,7 +150,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
if (await IsConnectMethod(clientStream) == -1) if (await HttpHelper.IsConnectMethod(clientStream) == -1)
{ {
// It can be for example some Google (Cloude Messaging for Chrome) magic // It can be for example some Google (Cloude Messaging for Chrome) magic
excluded = true; excluded = true;
...@@ -228,45 +223,6 @@ namespace Titanium.Web.Proxy ...@@ -228,45 +223,6 @@ namespace Titanium.Web.Proxy
} }
} }
/// <summary>
/// Determines whether is connect method.
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
private async Task<int> IsConnectMethod(CustomBufferedStream clientStream)
{
bool isConnect = true;
int legthToCheck = 10;
for (int i = 0; i < legthToCheck; i++)
{
int b = await clientStream.PeekByteAsync(i);
if (b == -1)
{
return -1;
}
if (b == ' ' && i > 2)
{
// at least 3 letters and a space
return isConnect ? 1 : 0;
}
char ch = (char)b;
if (!char.IsLetter(ch))
{
// non letter or too short
return -1;
}
if (i > 6 || ch != "CONNECT"[i])
{
isConnect = false;
}
}
// only letters
return isConnect ? 1 : 0;
}
/// <summary> /// <summary>
/// This is called when this proxy acts as a reverse proxy (like a real http server) /// This is called when this proxy acts as a reverse proxy (like a real http server)
...@@ -640,15 +596,9 @@ namespace Titanium.Web.Proxy ...@@ -640,15 +596,9 @@ namespace Titanium.Web.Proxy
/// <param name="requestHeaders"></param> /// <param name="requestHeaders"></param>
private void PrepareRequestHeaders(HeaderCollection requestHeaders) private void PrepareRequestHeaders(HeaderCollection requestHeaders)
{ {
foreach (var header in requestHeaders) if(requestHeaders.HeaderExists(KnownHeaders.AcceptEncoding))
{ {
switch (header.Name.ToLower()) requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding, "gzip,deflate");
{
//these are the only encoding this proxy can read
case KnownHeaders.AcceptEncoding:
header.Value = "gzip,deflate";
break;
}
} }
requestHeaders.FixProxyHeaders(); requestHeaders.FixProxyHeaders();
......
...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy ...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy
/// User to server to authenticate requests. /// User to server to authenticate requests.
/// To disable this set ProxyServer.EnableWinAuth to false /// To disable this set ProxyServer.EnableWinAuth to false
/// </summary> /// </summary>
internal async Task<bool> Handle401UnAuthorized(SessionEventArgs args) internal async Task Handle401UnAuthorized(SessionEventArgs args)
{ {
string headerName = null; string headerName = null;
HttpHeader authHeader = null; HttpHeader authHeader = null;
...@@ -137,8 +137,6 @@ namespace Titanium.Web.Proxy ...@@ -137,8 +137,6 @@ namespace Titanium.Web.Proxy
//and server cookies //and server cookies
await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args); await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);
} }
return false;
} }
} }
} }
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