Commit 95fe4428 authored by ilushka85's avatar ilushka85

Create proper support for Upstream proxy supporting username, password, and ssl fixes

parent 3584b878
...@@ -69,8 +69,8 @@ Setup HTTP proxy: ...@@ -69,8 +69,8 @@ Setup HTTP proxy:
}; };
proxyServer.AddEndPoint(transparentEndPoint); proxyServer.AddEndPoint(transparentEndPoint);
//proxyServer.ExternalHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//proxyServer.ExternalHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
foreach (var endPoint in proxyServer.ProxyEndPoints) foreach (var endPoint in proxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
......
...@@ -39,6 +39,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -39,6 +39,7 @@ namespace Titanium.Web.Proxy.Http
/// <param name="Connection"></param> /// <param name="Connection"></param>
internal void SetConnection(TcpConnection Connection) internal void SetConnection(TcpConnection Connection)
{ {
Connection.LastAccess = DateTime.Now;
ServerConnection = Connection; ServerConnection = Connection;
} }
...@@ -59,13 +60,31 @@ namespace Titanium.Web.Proxy.Http ...@@ -59,13 +60,31 @@ namespace Titanium.Web.Proxy.Http
StringBuilder requestLines = new StringBuilder(); StringBuilder requestLines = new StringBuilder();
//prepare the request & headers //prepare the request & headers
requestLines.AppendLine(string.Join(" ", new string[3] if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps == true))
{ {
requestLines.AppendLine(string.Join(" ", new string[3]
{
this.Request.Method,
this.Request.RequestUri.AbsoluteUri,
string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
}));
}
else
{
requestLines.AppendLine(string.Join(" ", new string[3]
{
this.Request.Method, this.Request.Method,
this.Request.RequestUri.PathAndQuery, this.Request.RequestUri.PathAndQuery,
string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor) string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
})); }));
}
//Send Authentication to Upstream proxy if needed
if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false && ServerConnection.UpStreamHttpProxy.UserName != null && ServerConnection.UpStreamHttpProxy.UserName != "" && ServerConnection.UpStreamHttpProxy.Password != null)
{
requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password)));
}
//write request headers //write request headers
foreach (var headerItem in this.Request.RequestHeaders) foreach (var headerItem in this.Request.RequestHeaders)
{ {
...@@ -87,6 +106,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -87,6 +106,9 @@ namespace Titanium.Web.Proxy.Http
string request = requestLines.ToString(); string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request); byte[] requestBytes = Encoding.ASCII.GetBytes(request);
var test = Encoding.UTF8.GetString(requestBytes);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
await stream.FlushAsync(); await stream.FlushAsync();
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
/// </summary> /// </summary>
public class ExternalProxy public class ExternalProxy
{ {
public string UserName { get; set; }
public string Password { get; set; }
public string HostName { get; set; } public string HostName { get; set; }
public int Port { get; set; } public int Port { get; set; }
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -10,11 +11,20 @@ namespace Titanium.Web.Proxy.Network ...@@ -10,11 +11,20 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public class TcpConnection : IDisposable public class TcpConnection : IDisposable
{ {
internal ExternalProxy UpStreamHttpProxy { get; set; }
internal ExternalProxy UpStreamHttpsProxy { get; set; }
internal string HostName { get; set; } internal string HostName { get; set; }
internal int port { get; set; } internal int port { get; set; }
internal bool IsHttps { get; set; } internal bool IsHttps { get; set; }
/// <summary>
/// Http version
/// </summary>
internal Version Version { get; set; }
internal TcpClient TcpClient { get; set; } internal TcpClient TcpClient { get; set; }
/// <summary> /// <summary>
...@@ -27,6 +37,16 @@ namespace Titanium.Web.Proxy.Network ...@@ -27,6 +37,16 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal Stream Stream { get; set; } internal Stream Stream { get; set; }
/// <summary>
/// Last time this connection was used
/// </summary>
internal DateTime LastAccess { get; set; }
internal TcpConnection()
{
LastAccess = DateTime.Now;
}
public void Dispose() public void Dispose()
{ {
Stream.Close(); Stream.Close();
......
...@@ -7,6 +7,7 @@ using System.Net.Security; ...@@ -7,6 +7,7 @@ using System.Net.Security;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Security.Authentication; using System.Security.Authentication;
using System.Linq;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -34,7 +35,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -34,7 +35,7 @@ namespace Titanium.Web.Proxy.Network
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds, internal async Task<TcpConnection> CreateClient(int bufferSize, int connectionTimeOutSeconds,
string remoteHostName, int remotePort, Version httpVersion, string remoteHostName, int remotePort, Version httpVersion,
bool isHttps, SslProtocols supportedSslProtocols, bool isHttps, SslProtocols supportedSslProtocols,
RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback, RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback,
ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy,
...@@ -55,9 +56,15 @@ namespace Titanium.Web.Proxy.Network ...@@ -55,9 +56,15 @@ namespace Titanium.Web.Proxy.Network
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)) using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true))
{ {
await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", remoteHostName, remotePort, httpVersion)); await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} HTTP/{2}", remoteHostName, remotePort,httpVersion));
await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort));
await writer.WriteLineAsync("Connection: Keep-Alive"); await writer.WriteLineAsync("Connection: Keep-Alive");
if (externalHttpsProxy.UserName != null && externalHttpsProxy.UserName != "" && externalHttpsProxy.Password != null)
{
await writer.WriteLineAsync("Proxy-Connection: keep-alive");
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
}
await writer.WriteLineAsync(); await writer.WriteLineAsync();
await writer.FlushAsync(); await writer.FlushAsync();
writer.Close(); writer.Close();
...@@ -67,7 +74,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -67,7 +74,8 @@ namespace Titanium.Web.Proxy.Network
{ {
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!result.ToLower().Contains("200 connection established"))
if (!new string[] { "200 OK", "connection established" }.Any(s=> result.ToLower().Contains(s.ToLower())))
{ {
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
} }
...@@ -119,15 +127,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -119,15 +127,18 @@ namespace Titanium.Web.Proxy.Network
stream.ReadTimeout = connectionTimeOutSeconds * 1000; stream.ReadTimeout = connectionTimeOutSeconds * 1000;
stream.WriteTimeout = connectionTimeOutSeconds * 1000; stream.WriteTimeout = connectionTimeOutSeconds * 1000;
return new TcpConnection() return new TcpConnection()
{ {
UpStreamHttpProxy = externalHttpProxy,
UpStreamHttpsProxy = externalHttpsProxy,
HostName = remoteHostName, HostName = remoteHostName,
port = remotePort, port = remotePort,
IsHttps = isHttps, IsHttps = isHttps,
TcpClient = client, TcpClient = client,
StreamReader = new CustomBinaryReader(stream), StreamReader = new CustomBinaryReader(stream),
Stream = stream Stream = stream,
Version = httpVersion
}; };
} }
} }
......
...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Models; ...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Linq; using System.Linq;
using System.Security.Authentication; using System.Security.Authentication;
using System.Collections.Concurrent;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -17,15 +18,11 @@ namespace Titanium.Web.Proxy ...@@ -17,15 +18,11 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public partial class ProxyServer : IDisposable public partial class ProxyServer : IDisposable
{ {
/// <summary> public ConcurrentQueue<ExternalProxy> CustomUpStreamHttpProxies{ get; set; }
/// Does the root certificate used by this proxy is trusted by the machine? public Func<ConcurrentQueue<ExternalProxy>, ExternalProxy> GetCustomUpStreamHttpProxyFunc = null;
/// </summary> public ConcurrentQueue<ExternalProxy> CustomUpStreamHttpsProxies { get; set; }
private bool certTrusted { get; set; } public Func<ConcurrentQueue<ExternalProxy>, ExternalProxy> GetCustomUpStreamHttpsProxyFunc = null;
/// <summary>
/// Is the proxy currently running
/// </summary>
private bool proxyRunning { get; set; }
/// <summary> /// <summary>
/// Manages certificates used by this proxy /// Manages certificates used by this proxy
...@@ -44,6 +41,16 @@ namespace Titanium.Web.Proxy ...@@ -44,6 +41,16 @@ namespace Titanium.Web.Proxy
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; } private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
/// <summary>
/// Does the root certificate used by this proxy is trusted by the machine?
/// </summary>
private bool certTrusted { get; set; }
/// <summary>
/// Is the proxy currently running
/// </summary>
private bool proxyRunning { get; set; }
/// <summary> /// <summary>
/// Buffer size used throughout this proxy /// Buffer size used throughout this proxy
/// </summary> /// </summary>
...@@ -88,12 +95,12 @@ namespace Titanium.Web.Proxy ...@@ -88,12 +95,12 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// External proxy for Http /// External proxy for Http
/// </summary> /// </summary>
public ExternalProxy ExternalHttpProxy { get; set; } public ExternalProxy UpStreamHttpProxy { get; set; }
/// <summary> /// <summary>
/// External proxy for Https /// External proxy for Http
/// </summary> /// </summary>
public ExternalProxy ExternalHttpsProxy { get; set; } public ExternalProxy UpStreamHttpsProxy { get; set; }
/// <summary> /// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
...@@ -390,9 +397,16 @@ namespace Titanium.Web.Proxy ...@@ -390,9 +397,16 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient); ExternalProxy externalHttpProxy = null;
} if (GetCustomUpStreamHttpProxyFunc != null)
externalHttpProxy = GetCustomUpStreamHttpProxyFunc(CustomUpStreamHttpProxies);
ExternalProxy externalHttpsProxy = null;
if (GetCustomUpStreamHttpsProxyFunc != null)
externalHttpsProxy = GetCustomUpStreamHttpsProxyFunc(CustomUpStreamHttpsProxies);
await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient, externalHttpProxy, externalHttpsProxy);
}
} }
finally finally
{ {
......
...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy ...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy
{ {
//This is called when client is aware of proxy //This is called when client is aware of proxy
//So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy //So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client) private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
{ {
Stream clientStream = client.GetStream(); Stream clientStream = client.GetStream();
...@@ -137,11 +137,12 @@ namespace Titanium.Web.Proxy ...@@ -137,11 +137,12 @@ namespace Titanium.Web.Proxy
return; return;
} }
//Now create the request //Now create the request
await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter, await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null); httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, customUpStreamHttpProxy,customUpStreamHttpsProxy);
} }
catch catch(Exception ex)
{ {
Dispose(clientStream, clientStreamReader, clientStreamWriter, null); Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
} }
...@@ -213,7 +214,7 @@ namespace Titanium.Web.Proxy ...@@ -213,7 +214,7 @@ namespace Titanium.Web.Proxy
/// <param name="httpsHostName"></param> /// <param name="httpsHostName"></param>
/// <returns></returns> /// <returns></returns>
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
{ {
TcpConnection connection = null; TcpConnection connection = null;
...@@ -332,11 +333,12 @@ namespace Titanium.Web.Proxy ...@@ -332,11 +333,12 @@ namespace Titanium.Web.Proxy
args.IsHttps, SupportedSslProtocols, args.IsHttps, SupportedSslProtocols,
new RemoteCertificateValidationCallback(ValidateServerCertificate), new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate), new LocalCertificateSelectionCallback(SelectClientCertificate),
ExternalHttpProxy, ExternalHttpsProxy, clientStream); customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, clientStream);
} }
args.WebSession.Request.RequestLocked = true; args.WebSession.Request.RequestLocked = true;
//If request was cancelled by user then dispose the client //If request was cancelled by user then dispose the client
if (args.WebSession.Request.CancelRequest) if (args.WebSession.Request.CancelRequest)
{ {
...@@ -344,6 +346,7 @@ namespace Titanium.Web.Proxy ...@@ -344,6 +346,7 @@ namespace Titanium.Web.Proxy
break; break;
} }
//if expect continue is enabled then send the headers first //if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue //and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue) if (args.WebSession.Request.ExpectContinue)
...@@ -418,7 +421,7 @@ namespace Titanium.Web.Proxy ...@@ -418,7 +421,7 @@ namespace Titanium.Web.Proxy
httpCmd = await clientStreamReader.ReadLineAsync(); httpCmd = await clientStreamReader.ReadLineAsync();
} }
catch catch(Exception e)
{ {
Dispose(clientStream, clientStreamReader, clientStreamWriter, args); Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
break; break;
......
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