Commit 7d115949 authored by titanium007's avatar titanium007

Merge pull request #41 from titanium007/release

Release 2.1
parents 60c23d7e 1b650f08
...@@ -21,7 +21,7 @@ if(!$Configuration) { $Configuration = $env:Configuration } ...@@ -21,7 +21,7 @@ if(!$Configuration) { $Configuration = $env:Configuration }
if(!$Configuration) { $Configuration = "Release" } if(!$Configuration) { $Configuration = "Release" }
if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION } if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION }
if(!$Version) { $Version = "1.0.$BuildNumber" } if(!$Version) { $Version = "2.0.$BuildNumber" }
if(!$Branch) { $Branch = $env:APPVEYOR_REPO_BRANCH } if(!$Branch) { $Branch = $env:APPVEYOR_REPO_BRANCH }
if(!$Branch) { $Branch = "local" } if(!$Branch) { $Branch = "local" }
......
...@@ -22,7 +22,7 @@ Refer the HTTP Proxy Server library in your project, look up Test project to lea ...@@ -22,7 +22,7 @@ Refer the HTTP Proxy Server library in your project, look up Test project to lea
Install by nuget: Install by nuget:
Install-Package Titanium.Web.Proxy Install-Package Titanium.Web.Proxy -Pre
After installing nuget package mark following files to be copied to app directory After installing nuget package mark following files to be copied to app directory
...@@ -34,13 +34,46 @@ Setup HTTP proxy: ...@@ -34,13 +34,46 @@ Setup HTTP proxy:
```csharp ```csharp
// listen to client request & server response events // listen to client request & server response events
ProxyServer.BeforeRequest += OnRequest; ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse; ProxyServer.BeforeResponse += OnResponse;
ProxyServer.EnableSSL = true; //Exclude Https addresses you don't want to proxy
ProxyServer.SetAsSystemProxy = true; //Usefull for clients that use certificate pinning
//for example dropbox.com
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true){
ExcludedHttpsHostNameRegex = new List<string>() { "dropbox.com" }
};
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.Start(); ProxyServer.Start();
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
//Please read about it before asking questions!
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) {
GenericCertificateName = "google.com"
};
ProxyServer.AddEndPoint(transparentEndPoint);
foreach (var endPoint in ProxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
//You can also add/remove end points after proxy has been started
ProxyServer.RemoveEndPoint(transparentEndPoint);
//Only explicit proxies can be set as system proxy!
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
//wait here (You can use something else as a wait function, I am using this as a demo) //wait here (You can use something else as a wait function, I am using this as a demo)
Console.Read(); Console.Read();
...@@ -113,6 +146,8 @@ Sample request and response event handlers ...@@ -113,6 +146,8 @@ Sample request and response event handlers
``` ```
Future updates Future updates
============ ============
* Add callbacks for client/server certificate validation/selection
* Support mutual authentication * Support mutual authentication
* Add Server Name Indication (SNI) for transparent endpoints
* Support HTTP 2.0 * Support HTTP 2.0
* Support modification of web socket requests
...@@ -14,22 +14,6 @@ namespace Titanium.Web.Proxy.Test ...@@ -14,22 +14,6 @@ namespace Titanium.Web.Proxy.Test
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true); NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
Console.Write("Do you want to monitor HTTPS? (Y/N):");
var readLine = Console.ReadLine();
if (readLine != null && readLine.Trim().ToLower() == "y")
{
Controller.EnableSsl = true;
}
Console.Write("Do you want to set this as a System Proxy? (Y/N):");
var line = Console.ReadLine();
if (line != null && line.Trim().ToLower() == "y")
{
Controller.SetAsSystemProxy = true;
}
//Start proxy controller //Start proxy controller
Controller.StartProxy(); Controller.StartProxy();
......
using System; using System;
using System.Collections.Generic;
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
{ {
public class ProxyTestController public class ProxyTestController
{ {
public int ListeningPort { get; set; }
public bool EnableSsl { get; set; }
public bool SetAsSystemProxy { get; set; }
public void StartProxy() public void StartProxy()
{ {
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"); var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true){
ExcludedHttpsHostNameRegex = new List<string>() { "dropbox.com" }
};
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.Start(); ProxyServer.Start();
ProxyServer.ListeningPort = ProxyServer.ListeningPort;
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
//Please read about it before asking questions!
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) {
GenericCertificateName = "google.com"
};
ProxyServer.AddEndPoint(transparentEndPoint);
foreach (var endPoint in ProxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
Console.WriteLine("Proxy listening on local machine port: {0} ", ProxyServer.ListeningPort); //You can also add/remove end points after proxy has been started
ProxyServer.RemoveEndPoint(transparentEndPoint);
//Only explicit proxies can be set as system proxy!
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
} }
public void Stop() public void Stop()
...@@ -39,7 +62,6 @@ namespace Titanium.Web.Proxy.Test ...@@ -39,7 +62,6 @@ namespace Titanium.Web.Proxy.Test
ProxyServer.Stop(); ProxyServer.Stop();
} }
//Test On Request, intecept requests //Test On Request, intecept requests
//Read browser URL send back to proxy by the injection script in OnResponse event //Read browser URL send back to proxy by the injection script in OnResponse event
public void OnRequest(object sender, SessionEventArgs e) public void OnRequest(object sender, SessionEventArgs e)
......
...@@ -8,8 +8,14 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -8,8 +8,14 @@ namespace Titanium.Web.Proxy.Extensions
{ {
public static Encoding GetResponseEncoding(this HttpWebSession response) public static Encoding GetResponseEncoding(this HttpWebSession response)
{ {
if (string.IsNullOrEmpty(response.Response.CharacterSet)) return Encoding.GetEncoding("ISO-8859-1"); if (string.IsNullOrEmpty(response.Response.CharacterSet))
return Encoding.GetEncoding(response.Response.CharacterSet.Replace(@"""", string.Empty)); return Encoding.GetEncoding("ISO-8859-1");
try
{
return Encoding.GetEncoding(response.Response.CharacterSet.Replace(@"""", string.Empty));
}
catch { return Encoding.GetEncoding("ISO-8859-1"); }
} }
} }
} }
\ No newline at end of file
...@@ -8,13 +8,17 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -8,13 +8,17 @@ namespace Titanium.Web.Proxy.Extensions
{ {
public static void CopyToAsync(this Stream input, string initialData, Stream output, int bufferSize) public static void CopyToAsync(this Stream input, string initialData, Stream output, int bufferSize)
{ {
var bytes = Encoding.ASCII.GetBytes(initialData); if(!string.IsNullOrEmpty(initialData))
output.Write(bytes, 0, bytes.Length); {
var bytes = Encoding.ASCII.GetBytes(initialData);
output.Write(bytes, 0, bytes.Length);
}
CopyToAsync(input, output, bufferSize); CopyToAsync(input, output, bufferSize);
} }
//http://stackoverflow.com/questions/1540658/net-asynchronous-stream-read-write //http://stackoverflow.com/questions/1540658/net-asynchronous-stream-read-write
public static void CopyToAsync(this Stream input, Stream output, int bufferSize) private static void CopyToAsync(this Stream input, Stream output, int bufferSize)
{ {
try try
{ {
......
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Win32; using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -11,36 +14,135 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -11,36 +14,135 @@ namespace Titanium.Web.Proxy.Helpers
int dwBufferLength); int dwBufferLength);
} }
internal class HttpSystemProxyValue
{
public string HostName { get; set; }
public int Port { get; set; }
public bool IsSecure { get; set; }
public override string ToString()
{
if (!IsSecure)
return "http=" + HostName + ":" + Port;
else
return "https=" + HostName + ":" + Port;
}
}
public static class SystemProxyHelper public static class SystemProxyHelper
{ {
public const int InternetOptionSettingsChanged = 39; public const int InternetOptionSettingsChanged = 39;
public const int InternetOptionRefresh = 37; public const int InternetOptionRefresh = 37;
private static object _prevProxyServer;
private static object _prevProxyEnable;
public static void EnableProxyHttp(string hostname, int port) public static void SetHttpProxy(string hostname, int port)
{ {
var reg = Registry.CurrentUser.OpenSubKey( var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null) if (reg != null)
{ {
_prevProxyEnable = reg.GetValue("ProxyEnable"); prepareRegistry(reg);
_prevProxyServer = reg.GetValue("ProxyServer");
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => !x.IsSecure);
existingSystemProxyValues.Add(new HttpSystemProxyValue()
{
HostName = hostname,
IsSecure = false,
Port = port
});
reg.SetValue("ProxyEnable", 1); reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", "http=" + hostname + ":" + port + ";"); reg.SetValue("ProxyServer", String.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
Refresh();
}
public static void RemoveHttpProxy()
{
var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null)
{
if (reg.GetValue("ProxyServer")!=null)
{
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => !x.IsSecure);
if (!(existingSystemProxyValues.Count() == 0))
{
reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", String.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
else
{
reg.SetValue("ProxyEnable", 0);
reg.SetValue("ProxyServer", string.Empty);
}
}
} }
Refresh(); Refresh();
} }
public static void EnableProxyHttps(string hostname, int port) public static void SetHttpsProxy(string hostname, int port)
{ {
var reg = Registry.CurrentUser.OpenSubKey( var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null) if (reg != null)
{ {
prepareRegistry(reg);
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => x.IsSecure);
existingSystemProxyValues.Add(new HttpSystemProxyValue()
{
HostName = hostname,
IsSecure = true,
Port = port
});
reg.SetValue("ProxyEnable", 1); reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", "http=" + hostname + ":" + port + ";https=" + hostname + ":" + port); reg.SetValue("ProxyServer", String.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
Refresh();
}
public static void RemoveHttpsProxy()
{
var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null)
{
if (reg.GetValue("ProxyServer") != null)
{
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => x.IsSecure);
if (!(existingSystemProxyValues.Count() == 0))
{
reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", String.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
else
{
reg.SetValue("ProxyEnable", 0);
reg.SetValue("ProxyServer", string.Empty);
}
}
} }
Refresh(); Refresh();
} }
...@@ -48,18 +150,88 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -48,18 +150,88 @@ namespace Titanium.Web.Proxy.Helpers
{ {
var reg = Registry.CurrentUser.OpenSubKey( var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null) if (reg != null)
{ {
reg.SetValue("ProxyEnable", _prevProxyEnable); reg.SetValue("ProxyEnable", 0);
if (_prevProxyServer != null) reg.SetValue("ProxyServer", string.Empty);
reg.SetValue("ProxyServer", _prevProxyServer);
} }
Refresh(); Refresh();
} }
private static List<HttpSystemProxyValue> GetSystemProxyValues(string prevServerValue)
{
var result = new List<HttpSystemProxyValue>();
if (string.IsNullOrWhiteSpace(prevServerValue))
return result;
var proxyValues = prevServerValue.Split(';');
if (proxyValues.Length > 0)
{
foreach (var value in proxyValues)
{
var parsedValue = parseProxyValue(value);
if (parsedValue != null)
result.Add(parsedValue);
}
}
else
{
var parsedValue = parseProxyValue(prevServerValue);
if (parsedValue != null)
result.Add(parsedValue);
}
return result;
}
private static HttpSystemProxyValue parseProxyValue(string value)
{
var tmp = Regex.Replace(value, @"\s+", " ").Trim().ToLower();
if (tmp.StartsWith("http="))
{
var endPoint = tmp.Substring(5);
return new HttpSystemProxyValue()
{
HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]),
IsSecure = false
};
}
else if (tmp.StartsWith("https="))
{
var endPoint = tmp.Substring(5);
return new HttpSystemProxyValue()
{
HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]),
IsSecure = true
};
}
return null;
}
private static void prepareRegistry(RegistryKey reg)
{
if (reg.GetValue("ProxyEnable") == null)
{
reg.SetValue("ProxyEnable", 0);
}
if (reg.GetValue("ProxyServer") == null || reg.GetValue("ProxyEnable") as string == "0")
{
reg.SetValue("ProxyServer", string.Empty);
}
}
private static void Refresh() private static void Refresh()
{ {
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero,0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0);
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
} }
} }
......
...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
sslStream = new SslStream(tunnelStream); sslStream = new SslStream(tunnelStream);
sslStream.AuthenticateAsClient(hostName); sslStream.AuthenticateAsClient(hostName, null, ProxyServer.SupportedProtocols, false);
tunnelStream = sslStream; tunnelStream = sslStream;
} }
catch catch
...@@ -69,10 +69,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -69,10 +69,10 @@ namespace Titanium.Web.Proxy.Helpers
if (sb != null) if (sb != null)
clientStream.CopyToAsync(sb.ToString(), tunnelStream, BUFFER_SIZE); clientStream.CopyToAsync(sb.ToString(), tunnelStream, BUFFER_SIZE);
else else
clientStream.CopyToAsync(tunnelStream, BUFFER_SIZE); clientStream.CopyToAsync(string.Empty, tunnelStream, BUFFER_SIZE);
}); });
var receiveRelay = Task.Factory.StartNew(() => tunnelStream.CopyToAsync(clientStream, BUFFER_SIZE)); var receiveRelay = Task.Factory.StartNew(() => tunnelStream.CopyToAsync(string.Empty, clientStream, BUFFER_SIZE));
Task.WaitAll(sendRelay, receiveRelay); Task.WaitAll(sendRelay, receiveRelay);
} }
......
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 ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
{
this.IpAddress = IpAddress;
this.Port = Port;
this.EnableSsl = EnableSsl;
}
public IPAddress IpAddress { get; internal set; }
public int Port { get; internal set; }
public bool EnableSsl { get; internal set; }
internal TcpListener listener { get; set; }
}
public class ExplicitProxyEndPoint : ProxyEndPoint
{
internal bool IsSystemHttpProxy { get; set; }
internal bool IsSystemHttpsProxy { get; set; }
public List<string> ExcludedHttpsHostNameRegex { get; set; }
public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
}
}
public class TransparentProxyEndPoint : ProxyEndPoint
{
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
//This is valid only when UseServerNameIndication is set to false
public string GenericCertificateName { get; set; }
// public bool UseServerNameIndication { get; set; }
public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
this.GenericCertificateName = "localhost";
}
}
}
...@@ -9,6 +9,7 @@ using System.IO; ...@@ -9,6 +9,7 @@ using System.IO;
using System.Net.Security; using System.Net.Security;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using System.Threading; using System.Threading;
using System.Security.Authentication;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -76,7 +77,7 @@ namespace Titanium.Web.Proxy.Network
try try
{ {
sslStream = new SslStream(stream); sslStream = new SslStream(stream);
sslStream.AuthenticateAsClient(Hostname); sslStream.AuthenticateAsClient(Hostname, null, ProxyServer.SupportedProtocols , false);
stream = (Stream)sslStream; stream = (Stream)sslStream;
} }
catch catch
......
This diff is collapsed.
...@@ -15,12 +15,14 @@ using Titanium.Web.Proxy.Extensions; ...@@ -15,12 +15,14 @@ using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
partial class ProxyServer partial class ProxyServer
{ {
private static void HandleClient(TcpClient client) //This is called when client is aware of proxy
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,10 +53,10 @@ namespace Titanium.Web.Proxy ...@@ -51,10 +53,10 @@ 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.ExcludedHttpsHostNameRegex != null ? endPoint.ExcludedHttpsHostNameRegex.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 != 80)
{ {
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
clientStreamReader.ReadAllLines(); clientStreamReader.ReadAllLines();
...@@ -68,9 +70,10 @@ namespace Titanium.Web.Proxy ...@@ -68,9 +70,10 @@ namespace Titanium.Web.Proxy
try try
{ {
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
//Successfully managed to authenticate the client using the fake certificate //Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServer(certificate, false, sslStream.AuthenticateAsServer(certificate, false,
SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false); SupportedProtocols, false);
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII); clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
clientStreamWriter = new StreamWriter(sslStream); clientStreamWriter = new StreamWriter(sslStream);
...@@ -95,15 +98,18 @@ namespace Titanium.Web.Proxy ...@@ -95,15 +98,18 @@ namespace Titanium.Web.Proxy
{ {
clientStreamReader.ReadAllLines(); clientStreamReader.ReadAllLines();
WriteConnectResponse(clientStreamWriter, httpVersion); WriteConnectResponse(clientStreamWriter, httpVersion);
TcpHelper.SendRaw(clientStreamReader.BaseStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false); false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return; return;
} }
//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 +117,61 @@ namespace Titanium.Web.Proxy ...@@ -111,9 +117,61 @@ namespace Titanium.Web.Proxy
} }
} }
//This is called when requests are routed through router to this endpoint
//For ssl requests
private static void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{
Stream clientStream = tcpClient.GetStream();
CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null;
X509Certificate2 certificate = null;
if (endPoint.EnableSsl)
{
var sslStream = new SslStream(clientStream, true);
//if(endPoint.UseServerNameIndication)
//{
// //implement in future once SNI supported by SSL stream
// certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);
//}
//else
certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName);
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)
{
if (sslStream != null)
sslStream.Dispose();
Dispose(tcpClient, sslStream, clientStreamReader, clientStreamWriter, null);
return;
}
clientStream = sslStream;
}
else
{
clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
}
var httpCmd = clientStreamReader.ReadLine();
//Now create the request
HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, 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 +193,8 @@ namespace Titanium.Web.Proxy ...@@ -135,8 +193,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 +207,6 @@ namespace Titanium.Web.Proxy ...@@ -149,11 +207,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,10 +219,13 @@ namespace Titanium.Web.Proxy ...@@ -166,10 +219,13 @@ 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(clientStream, httpCmd, args.ProxySession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps); httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; return;
} }
...@@ -249,7 +305,7 @@ namespace Titanium.Web.Proxy ...@@ -249,7 +305,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)
......
...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy ...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy
if (response.Response.ResponseHeaders[i].Value.Contains(";")) if (response.Response.ResponseHeaders[i].Value.Contains(";"))
{ {
response.Response.ContentType = response.Response.ResponseHeaders[i].Value.Split(';')[0].Trim(); response.Response.ContentType = response.Response.ResponseHeaders[i].Value.Split(';')[0].Trim();
response.Response.CharacterSet = response.Response.ResponseHeaders[i].Value.Split(';')[1].ToLower().Replace("charset=", string.Empty).Trim(); response.Response.CharacterSet = response.Response.ResponseHeaders[i].Value.Split(';')[1].Substring(9).Trim();
} }
else else
response.Response.ContentType = response.Response.ResponseHeaders[i].Value.ToLower().Trim(); response.Response.ContentType = response.Response.ResponseHeaders[i].Value.ToLower().Trim();
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug-Net45\</OutputPath> <OutputPath>bin\Debug-Net45\</OutputPath>
<DefineConstants>DEBUG</DefineConstants> <DefineConstants>DEBUG;NET45</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DefineConstants>NET45</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Ionic.Zip, Version=1.9.7.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL"> <Reference Include="Ionic.Zip, Version=1.9.7.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
...@@ -85,6 +86,7 @@ ...@@ -85,6 +86,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" />
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
# - Section names should be unique on each level. # - Section names should be unique on each level.
# version format # version format
version: 1.1.{build} version: 2.1.{build}
shallow_clone: true shallow_clone: true
......
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