Commit 02be898e authored by justcoding121's avatar justcoding121 Committed by justcoding121

Fix memory leaks

parent f6dc0470
......@@ -6,7 +6,7 @@ using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy.Helpers
{
public class CertificateManager
public class CertificateManager : IDisposable
{
private const string CERT_CREATE_FORMAT =
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 -b {5:MM/dd/yyyy} {6}";
......@@ -172,5 +172,14 @@ namespace Titanium.Web.Proxy.Helpers
return certCreatArgs;
}
public void Dispose()
{
if (MyStore != null)
MyStore.Close();
if (RootStore != null)
RootStore.Close();
}
}
}
\ No newline at end of file
......@@ -44,9 +44,13 @@ namespace Titanium.Web.Proxy.Helpers
return readBuffer.ToString();
}
catch (IOException)
{ return readBuffer.ToString(); }
{
return readBuffer.ToString();
}
catch (Exception)
{ throw; }
{
throw;
}
}
......
......@@ -9,20 +9,14 @@ namespace Titanium.Web.Proxy.Helpers
public static class StreamHelper
{
private const int DEFAULT_BUFFER_SIZE = 8192; // +32767
public static void CopyTo(this Stream input, Stream output)
{
input.CopyTo(output, DEFAULT_BUFFER_SIZE);
return;
}
public static void CopyTo(string initialData, Stream input, Stream output, int bufferSize)
{
var bytes = Encoding.ASCII.GetBytes(initialData);
output.Write(bytes,0, bytes.Length);
CopyTo(input, output, bufferSize);
}
public static void CopyTo(this Stream input, Stream output, int bufferSize)
public static void CopyTo(Stream input, Stream output, int bufferSize)
{
try
{
......
......@@ -6,6 +6,7 @@ using System.Net.Security;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers
{
......@@ -16,24 +17,38 @@ namespace Titanium.Web.Proxy.Helpers
public static void SendRaw(string hostname, int tunnelPort, System.IO.Stream clientStream)
{
System.Net.Sockets.TcpClient tunnelClient = null;
NetworkStream tunnelStream = null;
System.Net.Sockets.TcpClient tunnelClient = new System.Net.Sockets.TcpClient(hostname, tunnelPort);
var tunnelStream = tunnelClient.GetStream();
var tunnelReadBuffer = new byte[BUFFER_SIZE];
try
{
tunnelClient = new System.Net.Sockets.TcpClient(hostname, tunnelPort);
tunnelStream = tunnelClient.GetStream();
var tunnelReadBuffer = new byte[BUFFER_SIZE];
Task sendRelay = new Task(() => StreamHelper.CopyTo(clientStream, tunnelStream, BUFFER_SIZE));
Task receiveRelay = new Task(() => StreamHelper.CopyTo(tunnelStream, clientStream, BUFFER_SIZE));
Task sendRelay = Task.Factory.StartNew(() => StreamHelper.CopyTo(clientStream, tunnelStream, BUFFER_SIZE));
Task receiveRelay = Task.Factory.StartNew(() => StreamHelper.CopyTo(tunnelStream, clientStream, BUFFER_SIZE));
sendRelay.Start();
receiveRelay.Start();
sendRelay.Start();
receiveRelay.Start();
Task.WaitAll(sendRelay, receiveRelay);
Task.WaitAll(sendRelay, receiveRelay);
}
catch
{
if (tunnelStream != null)
{
tunnelStream.Close();
tunnelStream.Dispose();
}
if (tunnelStream != null)
tunnelStream.Close();
if (tunnelClient != null)
tunnelClient.Close();
if (tunnelClient != null)
tunnelClient.Close();
throw;
}
}
public static void SendRaw(string httpCmd, string secureHostName, List<string> requestLines, bool isHttps, Stream clientStream)
{
......@@ -76,30 +91,51 @@ namespace Titanium.Web.Proxy.Helpers
}
System.Net.Sockets.TcpClient tunnelClient = new System.Net.Sockets.TcpClient(hostname, tunnelPort);
var tunnelStream = tunnelClient.GetStream() as System.IO.Stream;
if (isHttps)
System.Net.Sockets.TcpClient tunnelClient = null;
Stream tunnelStream = null;
try
{
var sslStream = new SslStream(tunnelStream);
sslStream.AuthenticateAsClient(hostname);
tunnelStream = sslStream;
}
tunnelClient = new System.Net.Sockets.TcpClient(hostname, tunnelPort);
tunnelStream = tunnelClient.GetStream() as Stream;
if (isHttps)
{
SslStream sslStream = null;
try
{
sslStream = new SslStream(tunnelStream);
sslStream.AuthenticateAsClient(hostname);
tunnelStream = sslStream;
}
catch
{
if (sslStream != null)
sslStream.Dispose();
}
}
var sendRelay = new Task(() => StreamHelper.CopyTo(sb.ToString(), clientStream, tunnelStream, BUFFER_SIZE));
var receiveRelay = new Task(() => StreamHelper.CopyTo(tunnelStream, clientStream, BUFFER_SIZE));
sendRelay.Start();
receiveRelay.Start();
var sendRelay = new Task(() => StreamHelper.CopyTo(sb.ToString(), clientStream, tunnelStream, BUFFER_SIZE));
var receiveRelay = new Task(() => StreamHelper.CopyTo(tunnelStream, clientStream, BUFFER_SIZE));
Task.WaitAll(sendRelay, receiveRelay);
sendRelay.Start();
receiveRelay.Start();
if (tunnelStream != null)
tunnelStream.Close();
Task.WaitAll(sendRelay, receiveRelay);
}
catch
{
if (tunnelStream != null)
{
tunnelStream.Close();
tunnelStream.Dispose();
}
if (tunnelClient != null)
tunnelClient.Close();
if (tunnelClient != null)
tunnelClient.Close();
throw;
}
}
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Models
internal int BUFFER_SIZE;
internal TcpClient Client { get; set; }
internal Stream ClientStream { get; set; }
internal CustomBinaryReader ClientStreamReader { get; set; }
internal StreamWriter ClientStreamWriter { get; set; }
......@@ -30,8 +31,7 @@ namespace Titanium.Web.Proxy.Models
internal string ResponseHtmlBody { get; set; }
internal bool ResponseWasModified { get; set; }
public TcpClient Client { get; set; }
public int ClientPort { get; set; }
public IPAddress ClientIpAddress { get; set; }
public string tunnelHostName { get; set; }
......@@ -54,17 +54,6 @@ namespace Titanium.Web.Proxy.Models
if (this.ServerResponse != null)
this.ServerResponse.Close();
if (this.ClientStreamReader != null)
this.ClientStreamReader.Dispose();
if (this.ClientStreamWriter != null)
this.ClientStreamWriter.Dispose();
if (this.ClientStream != null)
this.ClientStream.Dispose();
if (this.Client != null)
this.Client.Close();
}
public SessionEventArgs(int bufferSize)
......
......@@ -33,8 +33,6 @@ namespace Titanium.Web.Proxy
private static object certificateAccessLock = new object();
private static List<string> pinnedCertificateClients = new List<string>();
private static TcpListener listener;
private static Thread listenerThread;
......@@ -43,8 +41,6 @@ namespace Titanium.Web.Proxy
public static event EventHandler<SessionEventArgs> BeforeRequest;
public static event EventHandler<SessionEventArgs> BeforeResponse;
public static IPAddress ListeningIPInterface { get; set; }
public static string RootCertificateName { get; set; }
......@@ -94,25 +90,27 @@ namespace Titanium.Web.Proxy
{
TcpListener listener = (TcpListener)obj;
try
while (ShouldListen)
{
while (ShouldListen)
TcpClient client = null;
try
{
var client = listener.AcceptTcpClient();
client = listener.AcceptTcpClient();
Task.Factory.StartNew(() => HandleClient(client));
}
catch
{
if (client != null)
client.Close();
}
}
catch (ThreadInterruptedException) { }
catch (SocketException) { }
}
public static bool Start()
{
listener = new TcpListener(IPAddress.Any, 0);
listener.Start();
listenerThread = new Thread(new ParameterizedThreadStart(Listen));
......@@ -125,7 +123,7 @@ namespace Titanium.Web.Proxy
SystemProxyHelper.EnableProxyHTTP("localhost", ListeningPort);
FireFoxHelper.AddFirefox();
if (EnableSSL)
{
RootCertificateName = RootCertificateName == null ? "Titanium_Proxy_Test_Root" : RootCertificateName;
......@@ -137,7 +135,7 @@ namespace Titanium.Web.Proxy
SystemProxyHelper.EnableProxyHTTPS("localhost", ListeningPort);
}
}
}
......@@ -156,12 +154,13 @@ namespace Titanium.Web.Proxy
ShouldListen = false;
listener.Stop();
listenerThread.Interrupt();
CertManager.Dispose();
}
......
......@@ -25,20 +25,18 @@ namespace Titanium.Web.Proxy
private static void HandleClient(TcpClient client)
{
Stream clientStream = null;
CustomBinaryReader clientStreamReader = null;
StreamWriter connectStreamWriter = null;
Stream clientStream = client.GetStream();
CustomBinaryReader clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
StreamWriter clientStreamWriter = new StreamWriter(clientStream);
string tunnelHostName = null;
int tunnelPort = 0;
try
{
clientStream = client.GetStream();
clientStreamReader = new CustomBinaryReader(clientStream, Encoding.ASCII);
string securehost = null;
List<string> requestLines = new List<string>();
......@@ -101,12 +99,11 @@ namespace Titanium.Web.Proxy
}
requestLines.Clear();
connectStreamWriter = new StreamWriter(clientStream);
connectStreamWriter.WriteLine(RequestVersion + " 200 Connection established");
connectStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
connectStreamWriter.WriteLine(String.Format("connection:close"));
connectStreamWriter.WriteLine();
connectStreamWriter.Flush();
clientStreamWriter.WriteLine(RequestVersion + " 200 Connection established");
clientStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString()));
clientStreamWriter.WriteLine(String.Format("connection:close"));
clientStreamWriter.WriteLine();
clientStreamWriter.Flush();
//If port is not 443 its not a HTTP request, so just relay
......@@ -114,15 +111,24 @@ namespace Titanium.Web.Proxy
{
TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Close();
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
//Create the fake certificate signed using our fake certificate authority
Monitor.Enter(certificateAccessLock);
var _certificate = ProxyServer.CertManager.CreateCertificate(tunnelHostName);
var certificate = ProxyServer.CertManager.CreateCertificate(tunnelHostName);
Monitor.Exit(certificateAccessLock);
SslStream sslStream = null;
......@@ -131,14 +137,20 @@ namespace Titanium.Web.Proxy
//So just relay the request after identifying it by first failure
if (!pinnedCertificateClients.Contains(tunnelHostName) && isSecure)
{
sslStream = new SslStream(clientStream, true);
try
{
sslStream = new SslStream(clientStream, true);
//Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
clientStreamWriter = new StreamWriter(sslStream);
//HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream;
}
catch (AuthenticationException)
catch
{
//if authentication failed it could be because client uses pinned certificates
//So add the hostname to this list so that next time we can relay without touching it (tunnel the request)
......@@ -146,6 +158,10 @@ namespace Titanium.Web.Proxy
{
pinnedCertificateClients.Add(tunnelHostName);
}
if (sslStream != null)
sslStream.Dispose();
throw;
}
......@@ -155,14 +171,21 @@ namespace Titanium.Web.Proxy
//Hostname was a previously failed request due to certificate pinning, just relay (tunnel the request)
TcpHelper.SendRaw(tunnelHostName, tunnelPort, clientStreamReader.BaseStream);
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Close();
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
clientStreamReader = new CustomBinaryReader(sslStream, Encoding.ASCII);
//HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream;
while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{
......@@ -180,48 +203,55 @@ namespace Titanium.Web.Proxy
}
//Now create the request
Task.Factory.StartNew(()=>HandleHttpSessionRequest(client, httpCmd, clientStream, tunnelHostName, requestLines, clientStreamReader, securehost));
Task.Factory.StartNew(() => HandleHttpSessionRequest(client, httpCmd, clientStream, tunnelHostName, requestLines, clientStreamReader, clientStreamWriter, securehost));
}
catch (AuthenticationException)
{
return;
}
catch (EndOfStreamException)
{
return;
}
catch (IOException)
{
return;
}
catch (UriFormatException)
{
return;
}
catch (WebException)
{
return;
}
finally
catch
{
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
}
}
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, string tunnelHostName, List<string> requestLines, CustomBinaryReader clientStreamReader, string securehost)
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, string tunnelHostName, List<string> requestLines, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string securehost)
{
if (httpCmd == null) return;
if (httpCmd == null)
{
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
var args = new SessionEventArgs(BUFFER_SIZE);
args.Client = client;
args.tunnelHostName = tunnelHostName;
args.securehost = securehost;
try
{
//break up the line into three components (method, remote URL & Http Version)
......@@ -231,8 +261,20 @@ namespace Titanium.Web.Proxy
{
TcpHelper.SendRaw(httpCmd, tunnelHostName, requestLines, args.IsSSLRequest, clientStreamReader.BaseStream);
if (args != null)
args.Dispose();
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Close();
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
......@@ -262,6 +304,7 @@ namespace Titanium.Web.Proxy
args.ProxyRequest.ProtocolVersion = version;
args.ClientStream = clientStream;
args.ClientStreamReader = clientStreamReader;
args.ClientStreamWriter = clientStreamWriter;
for (int i = 1; i < requestLines.Count; i++)
{
......@@ -274,8 +317,20 @@ namespace Titanium.Web.Proxy
TcpHelper.SendRaw(httpCmd, tunnelHostName, requestLines, args.IsSSLRequest, clientStreamReader.BaseStream);
if (args != null)
args.Dispose();
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Close();
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
......@@ -308,19 +363,22 @@ namespace Titanium.Web.Proxy
string tmpLine;
if (args.CancelRequest)
{
if (args.RequestIsAlive)
{
requestLines.Clear();
while (!String.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{
requestLines.Add(tmpLine);
}
if (args != null)
args.Dispose();
httpCmd = requestLines.Count > 0 ? requestLines[0] : null;
return;
}
else
return;
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
return;
}
args.ProxyRequest.ConnectionGroupName = args.RequestHostname;
......@@ -347,7 +405,7 @@ namespace Titanium.Web.Proxy
//Http request body sent, now wait asynchronously for response
args.ProxyRequest.BeginGetResponse(new AsyncCallback(HandleHttpSessionResponse), args);
}
else
{
......@@ -359,37 +417,39 @@ namespace Titanium.Web.Proxy
//Now read the next request (if keep-Alive is enabled, otherwise exit this thread)
//If client is pipeling the request, this will be immediately hit before response for previous request was made
if (args.ProxyRequest.KeepAlive)
{
tmpLine = null;
requestLines.Clear();
while (!String.IsNullOrEmpty(tmpLine = args.ClientStreamReader.ReadLine()))
{
requestLines.Add(tmpLine);
}
httpCmd = requestLines.Count() > 0 ? requestLines[0] : null;
TcpClient Client = args.Client;
//Http request body sent, now wait for next request
Task.Factory.StartNew(() => HandleHttpSessionRequest(Client, httpCmd, args.ClientStream, args.tunnelHostName, requestLines, args.ClientStreamReader, args.securehost));
tmpLine = null;
requestLines.Clear();
while (!String.IsNullOrEmpty(tmpLine = args.ClientStreamReader.ReadLine()))
{
requestLines.Add(tmpLine);
}
httpCmd = requestLines.Count() > 0 ? requestLines[0] : null;
TcpClient Client = args.Client;
//Http request body sent, now wait for next request
Task.Factory.StartNew(() => HandleHttpSessionRequest(Client, httpCmd, args.ClientStream, args.tunnelHostName, requestLines, args.ClientStreamReader, args.ClientStreamWriter, args.securehost));
}
catch (IOException)
{
return;
}
catch (UriFormatException)
{
return;
}
catch (WebException)
{
return;
}
finally
catch
{
if (args != null)
args.Dispose();
if (clientStreamReader != null)
clientStreamReader.Dispose();
if (clientStreamWriter != null)
clientStreamWriter.Dispose();
if (clientStream != null)
clientStream.Dispose();
if (client != null)
client.Close();
}
}
......@@ -488,7 +548,7 @@ namespace Titanium.Web.Proxy
//This is called when the request is PUT/POST to read the body
private static void SendClientRequestBody(SessionEventArgs args)
{
// End the operation
Stream postStream = args.ProxyRequest.GetRequestStream();
......@@ -527,22 +587,16 @@ namespace Titanium.Web.Proxy
postStream.Close();
}
catch (IOException)
{
args.ProxyRequest.KeepAlive = false;
throw;
}
catch (WebException)
catch
{
if (postStream != null)
postStream.Close();
args.ProxyRequest.KeepAlive = false;
throw;
}
}
//Need to revist, find any potential bugs
//Need to revist, find any potential bugs
else if (args.ProxyRequest.SendChunked)
{
args.ProxyRequest.AllowWriteStreamBuffering = true;
......@@ -598,32 +652,24 @@ namespace Titanium.Web.Proxy
args.ClientStream.ReadByte();
}
sb.Clear();
}
}
postStream.Close();
}
catch (IOException)
catch
{
if (postStream != null)
postStream.Close();
args.ProxyRequest.KeepAlive = false;
throw;
}
catch (WebException)
{
if (postStream != null)
postStream.Close();
}
args.ProxyRequest.KeepAlive = false;
throw;
}
}
}
......
......@@ -22,6 +22,7 @@ namespace Titanium.Web.Proxy
{
SessionEventArgs args = (SessionEventArgs)asynchronousResult.AsyncState;
try
{
args.ServerResponse = (HttpWebResponse)args.ProxyRequest.EndGetResponse(asynchronousResult);
......@@ -29,27 +30,18 @@ namespace Titanium.Web.Proxy
catch (WebException webEx)
{
//Things line 404, 500 etc
//args.ProxyRequest.KeepAlive = false;
args.ServerResponse = webEx.Response as HttpWebResponse;
}
try
{
if (args.ClientStreamWriter == null)
{
args.ClientStreamWriter = new StreamWriter(args.ClientStream);
}
if (args.ServerResponse != null)
{
List<Tuple<String, String>> responseHeaders = ProcessResponse(args.ServerResponse);
args.ServerResponseStream = args.ServerResponse.GetResponseStream();
if (args.ServerResponse.Headers.Count == 0 && args.ServerResponse.ContentLength == -1)
args.ProxyRequest.KeepAlive = false;
bool isChunked = args.ServerResponse.GetResponseHeader("transfer-encoding") == null ? false : args.ServerResponse.GetResponseHeader("transfer-encoding").ToLower() == "chunked" ? true : false;
args.ProxyRequest.KeepAlive = args.ServerResponse.GetResponseHeader("connection") == null ? args.ProxyRequest.KeepAlive : (args.ServerResponse.GetResponseHeader("connection") == "close" ? false : args.ProxyRequest.KeepAlive);
args.UpgradeProtocol = args.ServerResponse.GetResponseHeader("upgrade") == null ? null : args.ServerResponse.GetResponseHeader("upgrade");
if (BeforeResponse != null)
......@@ -103,49 +95,30 @@ namespace Titanium.Web.Proxy
args.ClientStream.Flush();
}
else
args.ProxyRequest.KeepAlive = false;
}
catch (IOException)
{
args.ProxyRequest.KeepAlive = false;
}
catch (SocketException)
{
args.ProxyRequest.KeepAlive = false;
}
catch (ArgumentException)
{
args.ProxyRequest.KeepAlive = false;
}
catch (WebException)
catch
{
args.ProxyRequest.KeepAlive = false;
}
finally
{
//If this is false then terminate the tcp client
if (args.ProxyRequest.KeepAlive == false)
{
args.Dispose();
}
else
{
if (args.ClientStreamReader != null)
args.ClientStreamReader.Dispose();
//Close this HttpWebRequest session
if (args.ProxyRequest != null)
args.ProxyRequest.Abort();
if (args.ClientStreamWriter != null)
args.ClientStreamWriter.Dispose();
if (args.ServerResponse != null)
args.ServerResponse.Close();
if (args.ClientStream != null)
args.ClientStream.Dispose();
if (args.ServerResponseStream != null)
args.ServerResponseStream.Close();
if (args.Client != null)
args.Client.Close();
}
}
finally
{
if (args != null)
args.Dispose();
}
}
......
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