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

Commit 37714ce

Browse files
committed
Issue #36
Add endpoints
1 parent 75d6ba7 commit 37714ce

File tree

5 files changed

+161
-61
lines changed

5 files changed

+161
-61
lines changed

Titanium.Web.Proxy.Test/ProxyTestController.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Net;
23
using System.Text.RegularExpressions;
34
using Titanium.Web.Proxy.EventArguments;
5+
using Titanium.Web.Proxy.Models;
46

57
namespace Titanium.Web.Proxy.Test
68
{
@@ -15,20 +17,20 @@ public void StartProxy()
1517
ProxyServer.BeforeRequest += OnRequest;
1618
ProxyServer.BeforeResponse += OnResponse;
1719

18-
ProxyServer.EnableSsl = EnableSsl;
19-
20-
ProxyServer.SetAsSystemProxy = SetAsSystemProxy;
2120

2221
//Exclude Https addresses you don't want to proxy
2322
//Usefull for clients that use certificate pinning
2423
//for example dropbox.com
25-
ProxyServer.ExcludedHttpsHostNameRegex.Add(".dropbox.com");
26-
24+
// ProxyServer.ExcludedHttpsHostNameRegex.Add(".dropbox.com");
25+
var explicitEndPoint = new ExplicitProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Any, Port = 8000 };
26+
var transparentEndPoint = new TransparentProxyEndPoint { EnableSsl = true, IpAddress = IPAddress.Loopback, Port = 443 };
27+
ProxyServer.AddEndPoint(explicitEndPoint);
28+
ProxyServer.AddEndPoint(transparentEndPoint);
2729
ProxyServer.Start();
30+
ProxyServer.SetAsSystemProxy(explicitEndPoint);
2831

29-
ProxyServer.ListeningPort = ProxyServer.ListeningPort;
3032

31-
Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort);
33+
// Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort);
3234
}
3335

3436
public void Stop()

Titanium.Web.Proxy/Models/EndPoint.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Net.Sockets;
6+
using System.Text;
7+
8+
namespace Titanium.Web.Proxy.Models
9+
{
10+
public abstract class ProxyEndPoint
11+
{
12+
public IPAddress IpAddress { get; set; }
13+
public int Port { get; set; }
14+
public bool EnableSsl { get; set; }
15+
16+
internal TcpListener listener { get; set; }
17+
}
18+
19+
public class ExplicitProxyEndPoint : ProxyEndPoint
20+
{
21+
internal bool IsSystemProxy { get; set; }
22+
public List<string> ExcludedHostNameRegex { get; set; }
23+
24+
}
25+
26+
public class TransparentProxyEndPoint : ProxyEndPoint
27+
{
28+
29+
}
30+
}

Titanium.Web.Proxy/ProxyServer.cs

Lines changed: 76 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
using System.Threading.Tasks;
1010
using Titanium.Web.Proxy.EventArguments;
1111
using Titanium.Web.Proxy.Helpers;
12+
using Titanium.Web.Proxy.Models;
1213
using Titanium.Web.Proxy.Network;
14+
using System.Linq;
1315

1416
namespace Titanium.Web.Proxy
1517
{
@@ -31,89 +33,115 @@ public partial class ProxyServer
3133
private static readonly byte[] ChunkEnd =
3234
Encoding.ASCII.GetBytes(0.ToString("x2") + Environment.NewLine + Environment.NewLine);
3335

34-
private static TcpListener _listener;
36+
private static List<ProxyEndPoint> _proxyEndPoints { get; set; }
3537

36-
public static List<string> ExcludedHttpsHostNameRegex = new List<string>();
3738

3839
static ProxyServer()
3940
{
4041
CertManager = new CertificateManager("Titanium",
4142
"Titanium Root Certificate Authority");
4243

43-
ListeningIpAddress = IPAddress.Any;
44-
ListeningPort = 0;
44+
_proxyEndPoints = new List<ProxyEndPoint>();
4545

4646
Initialize();
4747
}
4848

4949
private static CertificateManager CertManager { get; set; }
50+
private static bool EnableSsl { get; set; }
51+
private static bool certTrusted { get; set; }
52+
private static bool proxyStarted { get; set; }
5053

5154
public static string RootCertificateName { get; set; }
52-
public static bool EnableSsl { get; set; }
53-
public static bool SetAsSystemProxy { get; set; }
54-
55-
public static int ListeningPort { get; set; }
56-
public static IPAddress ListeningIpAddress { get; set; }
5755

5856
public static event EventHandler<SessionEventArgs> BeforeRequest;
5957
public static event EventHandler<SessionEventArgs> BeforeResponse;
6058

59+
6160
public static void Initialize()
62-
{
63-
Task.Factory.StartNew(()=>TcpConnectionManager.ClearIdleConnections());
61+
{
62+
Task.Factory.StartNew(() => TcpConnectionManager.ClearIdleConnections());
6463
}
6564

65+
public static void AddEndPoint(ProxyEndPoint endPoint)
66+
{
67+
if (proxyStarted)
68+
throw new Exception("Cannot add end points after proxy started.");
69+
70+
_proxyEndPoints.Add(endPoint);
71+
}
72+
6673

67-
public static bool Start()
74+
public static void SetAsSystemProxy(ExplicitProxyEndPoint endPoint)
6875
{
69-
_listener = new TcpListener(ListeningIpAddress, ListeningPort);
70-
_listener.Start();
76+
if (_proxyEndPoints.Contains(endPoint) == false)
77+
throw new Exception("Cannot set endPoints not added to proxy as system proxy");
7178

72-
ListeningPort = ((IPEndPoint)_listener.LocalEndpoint).Port;
73-
// accept clients asynchronously
74-
_listener.BeginAcceptTcpClient(OnAcceptConnection, _listener);
79+
if (!proxyStarted)
80+
throw new Exception("Cannot set system proxy settings before proxy has been started.");
7581

76-
var certTrusted = false;
82+
//clear any settings previously added
83+
_proxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemProxy = false);
7784

78-
if (EnableSsl)
79-
certTrusted = CertManager.CreateTrustedRootCertificate();
85+
endPoint.IsSystemProxy = true;
8086

81-
if (SetAsSystemProxy)
82-
{
83-
SystemProxyHelper.EnableProxyHttp(
84-
Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(), ListeningPort);
87+
SystemProxyHelper.EnableProxyHttp(
88+
Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port);
8589

8690
#if !DEBUG
87-
FireFoxHelper.AddFirefox();
91+
FireFoxHelper.AddFirefox();
8892
#endif
8993

94+
if (endPoint.EnableSsl)
95+
{
96+
RootCertificateName = RootCertificateName ?? "Titanium_Proxy_Test_Root";
9097

91-
if (EnableSsl)
98+
//If certificate was trusted by the machine
99+
if (certTrusted)
92100
{
93-
RootCertificateName = RootCertificateName ?? "Titanium_Proxy_Test_Root";
94-
95-
//If certificate was trusted by the machine
96-
if (certTrusted)
97-
{
98-
SystemProxyHelper.EnableProxyHttps(
99-
Equals(ListeningIpAddress, IPAddress.Any) ? "127.0.0.1" : ListeningIpAddress.ToString(),
100-
ListeningPort);
101-
}
101+
SystemProxyHelper.EnableProxyHttps(
102+
Equals(endPoint.IpAddress, IPAddress.Any) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
103+
endPoint.Port);
102104
}
103105
}
104106

105-
return true;
107+
}
108+
109+
public static void Start()
110+
{
111+
EnableSsl = _proxyEndPoints.Any(x => x.EnableSsl);
112+
113+
if (EnableSsl)
114+
certTrusted = CertManager.CreateTrustedRootCertificate();
115+
116+
foreach (var endPoint in _proxyEndPoints)
117+
{
118+
119+
endPoint.listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
120+
endPoint.listener.Start();
121+
122+
endPoint.Port = ((IPEndPoint)endPoint.listener.LocalEndpoint).Port;
123+
// accept clients asynchronously
124+
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
125+
}
126+
127+
proxyStarted = true;
106128
}
107129

108130
private static void OnAcceptConnection(IAsyncResult asyn)
109131
{
132+
var endPoint = (ProxyEndPoint)asyn.AsyncState;
133+
134+
// Get the listener that handles the client request.
135+
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
136+
137+
var client = endPoint.listener.EndAcceptTcpClient(asyn);
138+
110139
try
111140
{
112-
// Get the listener that handles the client request.
113-
_listener.BeginAcceptTcpClient(OnAcceptConnection, _listener);
114-
115-
var client = _listener.EndAcceptTcpClient(asyn);
116-
Task.Factory.StartNew(() => HandleClient(client));
141+
if (endPoint.GetType() == typeof(TransparentProxyEndPoint))
142+
Task.Factory.StartNew(() => HandleClient(endPoint as TransparentProxyEndPoint, client));
143+
else
144+
Task.Factory.StartNew(() => HandleClient(endPoint as ExplicitProxyEndPoint, client));
117145
}
118146
catch
119147
{
@@ -124,6 +152,8 @@ private static void OnAcceptConnection(IAsyncResult asyn)
124152

125153
public static void Stop()
126154
{
155+
var SetAsSystemProxy = _proxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemProxy);
156+
127157
if (SetAsSystemProxy)
128158
{
129159
SystemProxyHelper.DisableAllProxy();
@@ -132,7 +162,11 @@ public static void Stop()
132162
#endif
133163
}
134164

135-
_listener.Stop();
165+
foreach (var endPoint in _proxyEndPoints)
166+
{
167+
endPoint.listener.Stop();
168+
}
169+
136170
CertManager.Dispose();
137171
}
138172
}

Titanium.Web.Proxy/RequestHandler.cs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy
2020
{
2121
partial class ProxyServer
2222
{
23-
private static void HandleClient(TcpClient client)
23+
private static void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client)
2424
{
2525
Stream clientStream = client.GetStream();
2626
var clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
@@ -51,7 +51,8 @@ private static void HandleClient(TcpClient client)
5151

5252
var httpVersion = httpCmdSplit[2];
5353

54-
var excluded = ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));
54+
55+
var excluded = endPoint.ExcludedHostNameRegex != null ? endPoint.ExcludedHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
5556

5657
//Client wants to create a secure tcp tunnel (its a HTTPS request)
5758
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port == 443)
@@ -103,17 +104,51 @@ private static void HandleClient(TcpClient client)
103104

104105
//Now create the request
105106
HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
106-
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.OriginalString : null);
107+
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false);
107108
}
108109
catch
109110
{
110111
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
111112
}
112113
}
113114

115+
private static void HandleClient(TransparentProxyEndPoint endPoint, TcpClient client)
116+
{
117+
var sslStream = new SslStream(client.GetStream(), true);
118+
CustomBinaryReader clientStreamReader = null;
119+
StreamWriter clientStreamWriter = null;
120+
var certificate = CertManager.CreateCertificate("127.0.0.1");
121+
122+
try
123+
{
124+
//Successfully managed to authenticate the client using the fake certificate
125+
sslStream.AuthenticateAsServer(certificate, false,
126+
SslProtocols.Tls, false);
127+
128+
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
129+
clientStreamWriter = new StreamWriter(sslStream);
130+
//HTTPS server created - we can now decrypt the client's traffic
131+
132+
}
133+
134+
catch (Exception e)
135+
{
136+
if (sslStream != null)
137+
sslStream.Dispose();
138+
139+
Dispose(client, sslStream, clientStreamReader, clientStreamWriter, null);
140+
return;
141+
}
142+
143+
var httpCmd = clientStreamReader.ReadLine();
144+
145+
//Now create the request
146+
HandleHttpSessionRequest(client, httpCmd, sslStream, clientStreamReader, clientStreamWriter,
147+
true);
148+
}
114149

115150
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
116-
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName)
151+
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool IsHttps)
117152
{
118153
TcpConnection connection = null;
119154
string lastRequestHostName = null;
@@ -135,8 +170,8 @@ private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, S
135170
var httpCmdSplit = httpCmd.Split(SpaceSplit, 3);
136171

137172
var httpMethod = httpCmdSplit[0];
138-
var httpRemoteUri =
139-
new Uri(secureTunnelHostName == null ? httpCmdSplit[1] : (secureTunnelHostName + httpCmdSplit[1]));
173+
174+
140175
var httpVersion = httpCmdSplit[2];
141176

142177
Version version;
@@ -149,11 +184,6 @@ private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, S
149184
version = new Version(1, 0);
150185
}
151186

152-
if (httpRemoteUri.Scheme == Uri.UriSchemeHttps)
153-
{
154-
args.IsHttps = true;
155-
}
156-
157187

158188
args.ProxySession.Request.RequestHeaders = new List<HttpHeader>();
159189

@@ -166,6 +196,9 @@ private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, S
166196

167197
SetRequestHeaders(args.ProxySession.Request.RequestHeaders, args.ProxySession);
168198

199+
var httpRemoteUri = new Uri(!IsHttps ? httpCmdSplit[1] : (string.Concat("https://", args.ProxySession.Request.Hostname, httpCmdSplit[1])));
200+
args.IsHttps = IsHttps;
201+
169202
if (args.ProxySession.Request.UpgradeToWebSocket)
170203
{
171204
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.ProxySession.Request.RequestHeaders,
@@ -249,7 +282,7 @@ private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, S
249282
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
250283
break;
251284
}
252-
285+
253286
}
254287

255288
if (connection != null)

Titanium.Web.Proxy/Titanium.Web.Proxy.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<Compile Include="Helpers\CertificateManager.cs" />
8686
<Compile Include="Helpers\Firefox.cs" />
8787
<Compile Include="Helpers\SystemProxy.cs" />
88+
<Compile Include="Models\EndPoint.cs" />
8889
<Compile Include="Network\TcpExtensions.cs" />
8990
<Compile Include="Network\TcpConnectionManager.cs" />
9091
<Compile Include="Models\HttpHeader.cs" />

0 commit comments

Comments
 (0)