Skip to content

Commit

Permalink
Add the SSL listener
Browse files Browse the repository at this point in the history
  • Loading branch information
petermreid committed Jun 15, 2014
1 parent ae8e5a0 commit dbe9dd9
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 10 deletions.
10 changes: 5 additions & 5 deletions BuskerProxy.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extensions", "Extensions\Ex
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Image", "Image\Image.csproj", "{CB2D46D5-8D2C-46B6-9D07-0CF7D2CC19D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Secure", "Secure\Secure.csproj", "{B7640C4A-53A3-4084-B9EA-8B2FC8525060}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Secure", "Secure\Secure.csproj", "{97944F33-FC7F-4486-A530-8E474ACD5BFA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -83,10 +83,10 @@ Global
{CB2D46D5-8D2C-46B6-9D07-0CF7D2CC19D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB2D46D5-8D2C-46B6-9D07-0CF7D2CC19D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB2D46D5-8D2C-46B6-9D07-0CF7D2CC19D9}.Release|Any CPU.Build.0 = Release|Any CPU
{B7640C4A-53A3-4084-B9EA-8B2FC8525060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7640C4A-53A3-4084-B9EA-8B2FC8525060}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7640C4A-53A3-4084-B9EA-8B2FC8525060}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7640C4A-53A3-4084-B9EA-8B2FC8525060}.Release|Any CPU.Build.0 = Release|Any CPU
{97944F33-FC7F-4486-A530-8E474ACD5BFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97944F33-FC7F-4486-A530-8E474ACD5BFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97944F33-FC7F-4486-A530-8E474ACD5BFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97944F33-FC7F-4486-A530-8E474ACD5BFA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
6 changes: 6 additions & 0 deletions Secure/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
53 changes: 53 additions & 0 deletions Secure/Extension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace BuskerProxy.Secure
{
public static class Extension
{
const char cr = '\r';
const char lf = '\n';

public static string ReadLine(this BinaryReader br)
{
char ch='\0';
string line="";
while (br.BaseStream.CanRead)
{
ch = br.ReadChar();
if (ch == cr)
{
br.ReadChar();
break;
}
line += ch;
}
return line;
}

public static void WriteLine(this BinaryWriter bw, string line="")
{
foreach(char ch in line.AsEnumerable())
bw.Write(ch);
bw.Write(cr);
bw.Write(lf);
}


public static string ReadLine(this Stream stream)
{
var reader = new BinaryReader(stream, Encoding.UTF8, true);
return reader.ReadLine();
}

public static void WriteLine(this Stream stream, string line)
{
var writer = new BinaryWriter(stream, Encoding.UTF8, true);
writer.WriteLine(line);
}
}
}
205 changes: 205 additions & 0 deletions Secure/Listener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;

namespace BuskerProxy.Secure
{

public class SecureProxyListener
{
private static IPAddress _secureProxyAddress = IPAddress.Loopback;
private int _secureProxyPort;
private static IPAddress _insecureProxyAddress = IPAddress.Loopback;
private static int _insecureProxyPort;
private static X509Certificate2 _certificate;

public SecureProxyListener(IPAddress secureProxyAddress, int secureProxyPort, IPAddress insecureProxyAddress, int insecureProxyPort)
{
_secureProxyAddress = secureProxyAddress;
_secureProxyPort = secureProxyPort;
_insecureProxyAddress = insecureProxyAddress;
_insecureProxyPort = insecureProxyPort;
_certificate = new X509Certificate2("buskerproxy.pfx", "");
}

///<summary>
/// Start listening for connection
/// </summary>
public async void Start()
{
TcpListener listener = new TcpListener(_secureProxyAddress, _secureProxyPort);

listener.Start();
LogMessage("Server is running");
LogMessage("Listening on port " + _secureProxyPort);

try
{
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
while (!ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), client)) ;
}
}
catch (ThreadAbortException) { }
catch (SocketException) { }
}

private static void ProcessClient(Object obj)
{
TcpClient tcpClient = (TcpClient)obj;
TcpClient tcpProxy = null;
try
{
string clientInfo = tcpClient.Client.RemoteEndPoint.ToString();
LogMessage(string.Format("Got connection request from {0}", clientInfo));
//make a forward connection to proxy
tcpProxy = new TcpClient();
tcpProxy.Connect(_insecureProxyAddress, _insecureProxyPort);
ForwardClientToProxy(tcpClient, tcpProxy);
}
catch (SocketException exp)
{
LogMessage("Unable to forward requests to proxy, Check proxy is running");
}
catch (Exception exp)
{
LogMessage(exp.ToString());
}
finally
{
if (tcpClient != null)
tcpClient.Close();
if (tcpProxy != null)
tcpProxy.Close();
}
}

private static void ForwardClientToProxy(TcpClient tcpClient, TcpClient tcpProxy)
{
string httpCmd;
string method;
string version;
string url;
string sslTunnelDomain = null;

Stream clientStream = tcpClient.GetStream();
Stream proxyStream = tcpProxy.GetStream();

httpCmd = clientStream.ReadLine();
ParseHttpCommand(httpCmd, out method, out url, out version);
if (method == "CONNECT")
{
//client wants to ssl tunnel through the proxy
clientStream = GetSslTunnelStream(clientStream, version);
if (clientStream == null)
return;
httpCmd = clientStream.ReadLine();
sslTunnelDomain = url;
}
if (!String.IsNullOrEmpty(sslTunnelDomain))
{
//modify the path in the http request to include the domain
ParseHttpCommand(httpCmd, out method, out url, out version);
//modify the forward address so it has complete URL
httpCmd = method + ' ' + "https://" + sslTunnelDomain + url + ' ' + version;
}

LogMessage(string.Format(httpCmd));
proxyStream.WriteLine(httpCmd);
proxyStream.Flush();
CopyHttpStream(clientStream, proxyStream);
CopyHttpStream(proxyStream, clientStream);

clientStream.Close();
proxyStream.Close();
}

private static Stream GetSslTunnelStream(Stream stream, string version = "HTTP/1.1")
{
SslStream sslStream = null;
//Browser wants to create a secure tunnel
//read and ignore headers
while (!String.IsNullOrEmpty(stream.ReadLine())) ;
//tell the client that a tunnel has been established
LogMessage(string.Format("Doing CONNECT"));
var connectStreamWriter = new BinaryWriter(stream);
connectStreamWriter.WriteLine(version + " 200 Connection established");
connectStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
connectStreamWriter.WriteLine("Proxy-agent: buskerproxy");
connectStreamWriter.WriteLine();
connectStreamWriter.Flush();

//open a decrypting stream
sslStream = new SslStream(stream, false);
try
{
sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true);
}
catch (Exception ex)
{
stream.Close();
sslStream.Close();
return null;
}
return sslStream;
}

private static void CopyHttpStream(Stream fromStream, Stream toStream, string httpCmd = "", Encoding encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
using (var fromStreamReader = new BinaryReader(fromStream, encoding, true))
using (var toStreamWriter = new BinaryWriter(toStream, encoding, true))
{

string line;
int contentLength = 0;

//copy the headers
while (!String.IsNullOrEmpty(line = fromStreamReader.ReadLine()))
{
if (line.StartsWith("Content-Length:", true, CultureInfo.CurrentCulture))
contentLength = int.Parse(line.Replace("Content-Length:", ""));
toStreamWriter.WriteLine(line);
}
toStreamWriter.WriteLine();
if (contentLength > 0)
toStreamWriter.Write(fromStreamReader.ReadBytes(contentLength));
toStreamWriter.Flush();
}
}

private static void ParseHttpCommand(string httpCmd, out string method, out string requestPath, out string version)
{
char[] splitChar = { ' ' };
method = "";
requestPath = null;
version = "";
//read the first line
//break up the line into three components
String[] splitBuffer = httpCmd.Split(splitChar, 3);
if (splitBuffer.Length == 3)
{
method = splitBuffer[0];
requestPath = splitBuffer[1];
version = splitBuffer[2];
}
}

private static void LogMessage(string message, [CallerMemberName]string callername = "")
{
System.Console.WriteLine("[{0}] - Thread-{1}- {2}",
callername, Thread.CurrentThread.ManagedThreadId, message);
}
}
}

17 changes: 17 additions & 0 deletions Secure/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Net;

namespace BuskerProxy.Secure
{
class Program
{
static void Main(string[] args)
{
SecureProxyListener secureProxyListener = new SecureProxyListener(IPAddress.Loopback,8081,IPAddress.Loopback,8080);
secureProxyListener.Start();
Console.ReadLine();
}
}
}


2 changes: 1 addition & 1 deletion Secure/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("78ffa33b-7683-4224-b60a-9da28bab097d")]
[assembly: Guid("600bab30-fd21-442b-99de-13b1fa5604fd")]

// Version information for an assembly consists of the following four values:
//
Expand Down
12 changes: 8 additions & 4 deletions Secure/Secure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B7640C4A-53A3-4084-B9EA-8B2FC8525060}</ProjectGuid>
<OutputType>Library</OutputType>
<ProjectGuid>{97944F33-FC7F-4486-A530-8E474ACD5BFA}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BuskerProxy.Secure</RootNamespace>
<AssemblyName>Secure</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
Expand All @@ -22,6 +23,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
Expand All @@ -31,7 +33,6 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand All @@ -40,10 +41,13 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extension.cs" />
<Compile Include="Listener.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SslListener.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="buskerproxy.pfx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down

0 comments on commit dbe9dd9

Please sign in to comment.