Commit 37714ced authored by titanium007's avatar titanium007

Issue #36

Add endpoints
parent 75d6ba76
using System; using System;
using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Test namespace Titanium.Web.Proxy.Test
{ {
...@@ -15,20 +17,20 @@ namespace Titanium.Web.Proxy.Test ...@@ -15,20 +17,20 @@ namespace Titanium.Web.Proxy.Test
ProxyServer.BeforeRequest += OnRequest; ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse; ProxyServer.BeforeResponse += OnResponse;
ProxyServer.EnableSsl = EnableSsl;
ProxyServer.SetAsSystemProxy = SetAsSystemProxy;
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning //Usefull for clients that use certificate pinning
//for example dropbox.com //for example dropbox.com
ProxyServer.ExcludedHttpsHostNameRegex.Add(".dropbox.com"); // ProxyServer.ExcludedHttpsHostNameRegex.Add(".dropbox.com");
var explicitEndPoint = new ExplicitProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Any, Port = 8000 };
var transparentEndPoint = new TransparentProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Loopback, Port = 443 };
ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.AddEndPoint(transparentEndPoint);
ProxyServer.Start(); ProxyServer.Start();
ProxyServer.SetAsSystemProxy(explicitEndPoint);
ProxyServer.ListeningPort = ProxyServer.ListeningPort;
Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort); // Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort);
} }
public void Stop() public void Stop()
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Titanium.Web.Proxy.Models
{
public abstract class ProxyEndPoint
{
public IPAddress IpAddress { get; set; }
public int Port { get; set; }
public bool EnableSsl { get; set; }
internal TcpListener listener { get; set; }
}
public class ExplicitProxyEndPoint : ProxyEndPoint
{
internal bool IsSystemProxy { get; set; }
public List<string> ExcludedHostNameRegex { get; set; }
}
public class TransparentProxyEndPoint : ProxyEndPoint
{
}
}
...@@ -9,7 +9,9 @@ using System.Text.RegularExpressions; ...@@ -9,7 +9,9 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Linq;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -31,89 +33,115 @@ namespace Titanium.Web.Proxy ...@@ -31,89 +33,115 @@ namespace Titanium.Web.Proxy
private static readonly byte[] ChunkEnd = private static readonly byte[] ChunkEnd =
Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine); Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
private static TcpListener _listener; private static List<ProxyEndPoint> _proxyEndPoints { get; set; }
public static List<string> ExcludedHttpsHostNameRegex = new List<string>();
static ProxyServer() static ProxyServer()
{ {
CertManager = new CertificateManager("Titanium", CertManager = new CertificateManager("Titanium",
"Titanium Root Certificate Authority"); "Titanium Root Certificate Authority");
ListeningIpAddress = IPAddress.Any; _proxyEndPoints = new List<ProxyEndPoint>();
ListeningPort = 0;
Initialize(); Initialize();
} }
private static CertificateManager CertManager { get; set; } private static CertificateManager CertManager { get; set; }
private static bool EnableSsl { get; set; }
private static bool certTrusted { get; set; }
private static bool proxyStarted { get; set; }
public static string RootCertificateName { get; set; } public static string RootCertificateName { get; set; }
public static bool EnableSsl { get; set; }
public static bool SetAsSystemProxy { get; set; }
public static int ListeningPort { get; set; }
public static IPAddress ListeningIpAddress { get; set; }
public static event EventHandler<SessionEventArgs> BeforeRequest; public static event EventHandler<SessionEventArgs> BeforeRequest;
public static event EventHandler<SessionEventArgs> BeforeResponse; public static event EventHandler<SessionEventArgs> BeforeResponse;
public static void Initialize() public static void Initialize()
{ {
Task.Factory.StartNew(()=>TcpConnectionManager.ClearIdleConnections()); Task.Factory.StartNew(() => TcpConnectionManager.ClearIdleConnections());
} }
public static void AddEndPoint(ProxyEndPoint endPoint)
{
if (proxyStarted)
throw new Exception("Cannot add end points after proxy started.");
_proxyEndPoints.Add(endPoint);
}
public static bool Start() public static void SetAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
_listener = new TcpListener(ListeningIpAddress, ListeningPort); if (_proxyEndPoints.Contains(endPoint) == false)
_listener.Start(); throw new Exception("Cannot set endPoints not added to proxy as system proxy");
ListeningPort = ((IPEndPoint)_listener.LocalEndpoint).Port; if (!proxyStarted)
// accept clients asynchronously throw new Exception("Cannot set system proxy settings before proxy has been started.");
_listener.BeginAcceptTcpClient(OnAcceptConnection, _listener);
var certTrusted = false; //clear any settings previously added
_proxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemProxy = false);
if (EnableSsl) endPoint.IsSystemProxy = true;
certTrusted = CertManager.CreateTrustedRootCertificate();
if (SetAsSystemProxy) SystemProxyHelper.EnableProxyHttp(
{ Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port);
SystemProxyHelper.EnableProxyHttp(
Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort);
#if !DEBUG #if !DEBUG
FireFoxHelper.AddFirefox(); FireFoxHelper.AddFirefox();
#endif #endif
if (endPoint.EnableSsl)
{
RootCertificateName = RootCertificateName ?? "Titanium_Proxy_Test_Root";
if (EnableSsl) //If certificate was trusted by the machine
if (certTrusted)
{ {
RootCertificateName = RootCertificateName ?? "Titanium_Proxy_Test_Root"; SystemProxyHelper.EnableProxyHttps(
Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
//If certificate was trusted by the machine endPoint.Port);
if (certTrusted)
{
SystemProxyHelper.EnableProxyHttps(
Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(),
ListeningPort);
}
} }
} }
return true; }
public static void Start()
{
EnableSsl = _proxyEndPoints.Any(x => x.EnableSsl);
if (EnableSsl)
certTrusted = CertManager.CreateTrustedRootCertificate();
foreach (var endPoint in _proxyEndPoints)
{
endPoint.listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
endPoint.listener.Start();
endPoint.Port = ((IPEndPoint)endPoint.listener.LocalEndpoint).Port;
// accept clients asynchronously
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
}
proxyStarted = true;
} }
private static void OnAcceptConnection(IAsyncResult asyn) private static void OnAcceptConnection(IAsyncResult asyn)
{ {
var endPoint = (ProxyEndPoint)asyn.AsyncState;
// Get the listener that handles the client request.
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
var client = endPoint.listener.EndAcceptTcpClient(asyn);
try try
{ {
// Get the listener that handles the client request. if (endPoint.GetType() == typeof(TransparentProxyEndPoint))
_listener.BeginAcceptTcpClient(OnAcceptConnection, _listener); Task.Factory.StartNew(() => HandleClient(endPoint as TransparentProxyEndPoint, client));
else
var client = _listener.EndAcceptTcpClient(asyn); Task.Factory.StartNew(() => HandleClient(endPoint as ExplicitProxyEndPoint, client));
Task.Factory.StartNew(() => HandleClient(client));
} }
catch catch
{ {
...@@ -124,6 +152,8 @@ namespace Titanium.Web.Proxy ...@@ -124,6 +152,8 @@ namespace Titanium.Web.Proxy
public static void Stop() public static void Stop()
{ {
var SetAsSystemProxy = _proxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemProxy);
if (SetAsSystemProxy) if (SetAsSystemProxy)
{ {
SystemProxyHelper.DisableAllProxy(); SystemProxyHelper.DisableAllProxy();
...@@ -132,7 +162,11 @@ namespace Titanium.Web.Proxy ...@@ -132,7 +162,11 @@ namespace Titanium.Web.Proxy
#endif #endif
} }
_listener.Stop(); foreach (var endPoint in _proxyEndPoints)
{
endPoint.listener.Stop();
}
CertManager.Dispose(); CertManager.Dispose();
} }
} }
......
...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy ...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy
{ {
partial class ProxyServer partial class ProxyServer
{ {
private static void HandleClient(TcpClient client) private static void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client)
{ {
Stream clientStream = client.GetStream(); Stream clientStream = client.GetStream();
var clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII); var clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
...@@ -51,7 +51,8 @@ namespace Titanium.Web.Proxy ...@@ -51,7 +51,8 @@ namespace Titanium.Web.Proxy
var httpVersion = httpCmdSplit[2]; var httpVersion = httpCmdSplit[2];
var excluded = ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));
var excluded = endPoint.ExcludedHostNameRegex != null ? endPoint.ExcludedHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
//Client wants to create a secure tcp tunnel (its a HTTPS request) //Client wants to create a secure tcp tunnel (its a HTTPS request)
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443) if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443)
...@@ -103,7 +104,7 @@ namespace Titanium.Web.Proxy ...@@ -103,7 +104,7 @@ namespace Titanium.Web.Proxy
//Now create the request //Now create the request
HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter, HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.OriginalString : null); httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false);
} }
catch catch
{ {
...@@ -111,9 +112,43 @@ namespace Titanium.Web.Proxy ...@@ -111,9 +112,43 @@ namespace Titanium.Web.Proxy
} }
} }
private static void HandleClient(TransparentProxyEndPoint endPoint, TcpClient client)
{
var sslStream = new SslStream(client.GetStream(), true);
CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null;
var certificate = CertManager.CreateCertificate("127.0.0.1");
try
{
//Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServer(certificate, false,
SslProtocols.Tls, false);
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
clientStreamWriter = new StreamWriter(sslStream);
//HTTPS server created - we can now decrypt the client's traffic
}
catch (Exception e)
{
if (sslStream != null)
sslStream.Dispose();
Dispose(client, sslStream, clientStreamReader, clientStreamWriter, null);
return;
}
var httpCmd = clientStreamReader.ReadLine();
//Now create the request
HandleHttpSessionRequest(client, httpCmd, sslStream, clientStreamReader, clientStreamWriter,
true);
}
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool IsHttps)
{ {
TcpConnection connection = null; TcpConnection connection = null;
string lastRequestHostName = null; string lastRequestHostName = null;
...@@ -135,8 +170,8 @@ namespace Titanium.Web.Proxy ...@@ -135,8 +170,8 @@ namespace Titanium.Web.Proxy
var httpCmdSplit = httpCmd.Split(SpaceSplit, 3); var httpCmdSplit = httpCmd.Split(SpaceSplit, 3);
var httpMethod = httpCmdSplit[0]; var httpMethod = httpCmdSplit[0];
var httpRemoteUri =
new Uri(secureTunnelHostName == null ? httpCmdSplit[1] : (secureTunnelHostName + httpCmdSplit[1]));
var httpVersion = httpCmdSplit[2]; var httpVersion = httpCmdSplit[2];
Version version; Version version;
...@@ -149,11 +184,6 @@ namespace Titanium.Web.Proxy ...@@ -149,11 +184,6 @@ namespace Titanium.Web.Proxy
version = new Version(1, 0); version = new Version(1, 0);
} }
if (httpRemoteUri.Scheme == Uri.UriSchemeHttps)
{
args.IsHttps = true;
}
args.ProxySession.Request.RequestHeaders = new List<HttpHeader>(); args.ProxySession.Request.RequestHeaders = new List<HttpHeader>();
...@@ -166,6 +196,9 @@ namespace Titanium.Web.Proxy ...@@ -166,6 +196,9 @@ namespace Titanium.Web.Proxy
SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession); SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession);
var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.ProxySession.Request.Hostname, httpCmdSplit[1])));
args.IsHttps = IsHttps;
if (args.ProxySession.Request.UpgradeToWebSocket) if (args.ProxySession.Request.UpgradeToWebSocket)
{ {
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.ProxySession.Request.RequestHeaders, TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.ProxySession.Request.RequestHeaders,
...@@ -249,7 +282,7 @@ namespace Titanium.Web.Proxy ...@@ -249,7 +282,7 @@ namespace Titanium.Web.Proxy
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
break; break;
} }
} }
if (connection != null) if (connection != null)
......
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
<Compile Include="Helpers\CertificateManager.cs" /> <Compile Include="Helpers\CertificateManager.cs" />
<Compile Include="Helpers\Firefox.cs" /> <Compile Include="Helpers\Firefox.cs" />
<Compile Include="Helpers\SystemProxy.cs" /> <Compile Include="Helpers\SystemProxy.cs" />
<Compile Include="Models\EndPoint.cs" />
<Compile Include="Network\TcpExtensions.cs" /> <Compile Include="Network\TcpExtensions.cs" />
<Compile Include="Network\TcpConnectionManager.cs" /> <Compile Include="Network\TcpConnectionManager.cs" />
<Compile Include="Models\HttpHeader.cs" /> <Compile Include="Models\HttpHeader.cs" />
......
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