Commit d5f3c8b1 authored by justcoding121's avatar justcoding121 Committed by justcoding121

Cleanup; use existing code style; add flag to trust certificate

parent cad306d5
...@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public ProxyTestController() public ProxyTestController()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
proxyServer.TrustRootCertificate = true;
} }
public void StartProxy() public void StartProxy()
......
...@@ -20,7 +20,8 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -20,7 +20,8 @@ namespace Titanium.Web.Proxy.UnitTests
{ {
var tasks = new List<Task>(); var tasks = new List<Task>();
var mgr = new CertificateManager("Titanium", "Titanium Root Certificate Authority"); var mgr = new CertificateManager("Titanium", "Titanium Root Certificate Authority",
new Lazy<Action<Exception>>(() => (e => { })).Value);
mgr.ClearIdleCertificates(1); mgr.ClearIdleCertificates(1);
......
...@@ -64,8 +64,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -64,8 +64,6 @@ namespace Titanium.Web.Proxy.EventArguments
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; } public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; }
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
......
...@@ -38,7 +38,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -38,7 +38,8 @@ namespace Titanium.Web.Proxy.Http
{ {
if (processId == 0) if (processId == 0)
{ {
TcpRow tcpRow = TcpHelper.GetExtendedTcpTable().TcpRows.FirstOrDefault(row => row.LocalEndPoint.Port == ServerConnection.port); TcpRow tcpRow = TcpHelper.GetExtendedTcpTable().TcpRows
.FirstOrDefault(row => row.LocalEndPoint.Port == ServerConnection.port);
processId = tcpRow?.ProcessId ?? -1; processId = tcpRow?.ProcessId ?? -1;
} }
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
...@@ -10,7 +6,7 @@ using System.Threading; ...@@ -10,7 +6,7 @@ using System.Threading;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
public class CertEnrollEngine public class CertificateMaker
{ {
private Type typeX500DN; private Type typeX500DN;
...@@ -44,7 +40,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -44,7 +40,7 @@ namespace Titanium.Web.Proxy.Network
private object _SharedPrivateKey; private object _SharedPrivateKey;
public CertEnrollEngine() public CertificateMaker()
{ {
this.typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true); this.typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true);
this.typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true); this.typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true);
...@@ -62,12 +58,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -62,12 +58,12 @@ namespace Titanium.Web.Proxy.Network
this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames"); this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames");
} }
public X509Certificate2 CreateCert(string sSubjectCN, bool isRoot,X509Certificate2 signingCert=null) public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot,X509Certificate2 signingCert=null)
{ {
return this.InternalCreateCert(sSubjectCN, isRoot, true, signingCert); return this.MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
} }
private X509Certificate2 GenerateCertificate(bool IsRoot, string SubjectCN, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate) private X509Certificate2 MakeCertificate(bool IsRoot, string SubjectCN, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate)
{ {
X509Certificate2 cert; X509Certificate2 cert;
if (IsRoot != (null == SigningCertificate)) if (IsRoot != (null == SigningCertificate))
...@@ -193,7 +189,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -193,7 +189,7 @@ namespace Titanium.Web.Proxy.Network
return cert; return cert;
} }
private X509Certificate2 InternalCreateCert(string sSubjectCN, bool isRoot, bool switchToMTAIfNeeded,X509Certificate2 signingCert=null) private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, bool switchToMTAIfNeeded,X509Certificate2 signingCert=null)
{ {
X509Certificate2 rCert=null; X509Certificate2 rCert=null;
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
...@@ -201,7 +197,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -201,7 +197,7 @@ namespace Titanium.Web.Proxy.Network
ManualResetEvent manualResetEvent = new ManualResetEvent(false); ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((object o) => ThreadPool.QueueUserWorkItem((object o) =>
{ {
rCert = this.InternalCreateCert(sSubjectCN, isRoot, false,signingCert); rCert = this.MakeCertificateInternal(sSubjectCN, isRoot, false,signingCert);
manualResetEvent.Set(); manualResetEvent.Set();
}); });
manualResetEvent.WaitOne(); manualResetEvent.WaitOne();
...@@ -220,11 +216,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -220,11 +216,11 @@ namespace Titanium.Web.Proxy.Network
{ {
if (!isRoot) if (!isRoot)
{ {
rCert = this.GenerateCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), signingCert); rCert = this.MakeCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), signingCert);
} }
else else
{ {
rCert = this.GenerateCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), null); rCert = this.MakeCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), null);
} }
} }
catch (Exception e) catch (Exception e)
......
...@@ -13,31 +13,34 @@ namespace Titanium.Web.Proxy.Network ...@@ -13,31 +13,34 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class CertificateManager : IDisposable internal class CertificateManager : IDisposable
{ {
private CertEnrollEngine certEngine = null; private CertificateMaker certEngine = null;
private bool clearCertificates { get; set; }
/// <summary> /// <summary>
/// Cache dictionary /// Cache dictionary
/// </summary> /// </summary>
private readonly IDictionary<string, CachedCertificate> certificateCache; private readonly IDictionary<string, CachedCertificate> certificateCache;
private Action<Exception> exceptionFunc;
internal string Issuer { get; private set; } internal string Issuer { get; private set; }
internal string RootCertificateName { get; private set; } internal string RootCertificateName { get; private set; }
internal X509Certificate2 rootCertificate { get; set; } internal X509Certificate2 rootCertificate { get; set; }
internal CertificateManager(string issuer, string rootCertificateName) internal CertificateManager(string issuer, string rootCertificateName, Action<Exception> exceptionFunc)
{ {
certEngine = new CertEnrollEngine(); this.exceptionFunc = exceptionFunc;
certEngine = new CertificateMaker();
Issuer = issuer; Issuer = issuer;
RootCertificateName = rootCertificateName; RootCertificateName = rootCertificateName;
certificateCache = new ConcurrentDictionary<string, CachedCertificate>(); certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
} }
X509Certificate2 GetRootCertificate() internal X509Certificate2 GetRootCertificate()
{ {
var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "rootCert.pfx"); var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "rootCert.pfx");
...@@ -50,7 +53,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -50,7 +53,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
ProxyServer.ExceptionFunc(e); exceptionFunc(e);
return null; return null;
} }
} }
...@@ -74,7 +77,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -74,7 +77,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch(Exception e) catch(Exception e)
{ {
ProxyServer.ExceptionFunc(e); exceptionFunc(e);
} }
if (rootCertificate != null) if (rootCertificate != null)
{ {
...@@ -85,7 +88,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -85,7 +88,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch(Exception e) catch(Exception e)
{ {
ProxyServer.ExceptionFunc(e); exceptionFunc(e);
} }
} }
return rootCertificate != null; return rootCertificate != null;
...@@ -97,7 +100,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -97,7 +100,7 @@ namespace Titanium.Web.Proxy.Network
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param> /// <param name="isRootCertificate"></param>
/// <returns></returns> /// <returns></returns>
public virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
try try
{ {
...@@ -119,11 +122,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -119,11 +122,11 @@ namespace Titanium.Web.Proxy.Network
{ {
try try
{ {
certificate = certEngine.CreateCert(certificateName, isRootCertificate, rootCertificate); certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, rootCertificate);
} }
catch(Exception e) catch(Exception e)
{ {
ProxyServer.ExceptionFunc(e); exceptionFunc(e);
} }
if (certificate != null && !certificateCache.ContainsKey(certificateName)) if (certificate != null && !certificateCache.ContainsKey(certificateName))
{ {
...@@ -147,9 +150,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -147,9 +150,6 @@ namespace Titanium.Web.Proxy.Network
} }
private bool clearCertificates { get; set; }
/// <summary> /// <summary>
/// Stops the certificate cache clear process /// Stops the certificate cache clear process
/// </summary> /// </summary>
...@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Network
} }
} }
public bool TrustRootCertificate() internal bool TrustRootCertificate()
{ {
if (rootCertificate == null) if (rootCertificate == null)
{ {
...@@ -195,19 +195,25 @@ namespace Titanium.Web.Proxy.Network ...@@ -195,19 +195,25 @@ namespace Titanium.Web.Proxy.Network
} }
try try
{ {
X509Store x509Store = new X509Store(StoreName.Root, StoreLocation.CurrentUser); X509Store x509RootStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
x509Store.Open(OpenFlags.ReadWrite); var x509PersonalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
x509RootStore.Open(OpenFlags.ReadWrite);
x509PersonalStore.Open(OpenFlags.ReadWrite);
try try
{ {
x509Store.Add(rootCertificate); x509RootStore.Add(rootCertificate);
x509PersonalStore.Add(rootCertificate);
} }
finally finally
{ {
x509Store.Close(); x509RootStore.Close();
x509PersonalStore.Close();
} }
return true; return true;
} }
catch (Exception exception) catch
{ {
return false; return false;
} }
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy
{
public partial class ProxyServer
{
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, IEnumerable<HttpHeader> Headers)
{
if (AuthenticateUserFunc == null)
{
return true;
}
try
{
if (!Headers.Where(t => t.Name == "Proxy-Authorization").Any())
{
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Required", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
else
{
var headerValue = Headers.Where(t => t.Name == "Proxy-Authorization").FirstOrDefault().Value.Trim();
if (!headerValue.ToLower().StartsWith("basic"))
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
headerValue = headerValue.Substring(5).Trim();
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue));
if (decoded.Contains(":") == false)
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
var username = decoded.Substring(0, decoded.IndexOf(':'));
var password = decoded.Substring(decoded.IndexOf(':') + 1);
return await AuthenticateUserFunc(username, password).ConfigureAwait(false);
}
}
catch (Exception e)
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
}
}
}
...@@ -17,37 +17,16 @@ namespace Titanium.Web.Proxy ...@@ -17,37 +17,16 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public partial class ProxyServer : IDisposable public partial class ProxyServer : IDisposable
{ {
private static readonly Lazy<Action<Exception>> _defaultExceptionFunc = new Lazy<Action<Exception>>(() => (e => { }));
private static Action<Exception> _exceptionFunc; /// <summary>
public static Action<Exception> ExceptionFunc /// Is the root certificate used by this proxy is valid?
{ /// </summary>
get private bool certValidated { get; set; }
{
return _exceptionFunc ?? _defaultExceptionFunc.Value;
}
set
{
_exceptionFunc = value;
}
}
public Func<string, string, Task<bool>> AuthenticateUserFunc
{
get;
set;
}
//parameter is list of headers
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpProxyFunc
{
get;
set;
}
//parameter is list of headers
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{
get;
set;
}
/// <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
...@@ -55,26 +34,26 @@ namespace Titanium.Web.Proxy ...@@ -55,26 +34,26 @@ namespace Titanium.Web.Proxy
private CertificateManager certificateCacheManager { get; set; } private CertificateManager certificateCacheManager { get; set; }
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// An default exception log func
/// </summary> /// </summary>
private TcpConnectionFactory tcpConnectionFactory { get; set; } private readonly Lazy<Action<Exception>> defaultExceptionFunc = new Lazy<Action<Exception>>(() => (e => { }));
/// <summary> /// <summary>
/// Manage system proxy settings /// backing exception func for exposed public property
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; set; } private Action<Exception> exceptionFunc;
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
/// <summary> /// <summary>
/// Does the root certificate used by this proxy is trusted by the machine? /// A object that creates tcp connection to server
/// </summary> /// </summary>
private bool certTrusted { get; set; } private TcpConnectionFactory tcpConnectionFactory { get; set; }
/// <summary> /// <summary>
/// Is the proxy currently running /// Manage system proxy settings
/// </summary> /// </summary>
private bool proxyRunning { get; set; } private SystemProxyManager systemProxySettingsManager { get; set; }
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
/// <summary> /// <summary>
/// Buffer size used throughout this proxy /// Buffer size used throughout this proxy
...@@ -88,9 +67,19 @@ namespace Titanium.Web.Proxy ...@@ -88,9 +67,19 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Name of the root certificate /// Name of the root certificate
/// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate has to be in the proxy exe directory with the private key
/// The root certificate file should be named as "rootCert.pfx"
/// </summary> /// </summary>
public string RootCertificateName { get; set; } public string RootCertificateName { get; set; }
/// <summary>
/// Trust the RootCertificate used by this proxy server
/// Note that this do not make the client trust the certificate!
/// This would import the root certificate to the certificate store of machine that runs this proxy server
/// </summary>
public bool TrustRootCertificate { get; set; }
/// <summary> /// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly? /// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled /// Broken 100 contunue implementations on server/client may cause problems if enabled
...@@ -137,6 +126,51 @@ namespace Titanium.Web.Proxy ...@@ -137,6 +126,51 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event Func<object, CertificateSelectionEventArgs, Task> ClientCertificateSelectionCallback; public event Func<object, CertificateSelectionEventArgs, Task> ClientCertificateSelectionCallback;
/// <summary>
/// Callback for error events in proxy
/// </summary>
public Action<Exception> ExceptionFunc
{
get
{
return exceptionFunc ?? defaultExceptionFunc.Value;
}
set
{
exceptionFunc = value;
}
}
/// <summary>
/// A callback to authenticate clients
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public Func<string, string, Task<bool>> AuthenticateUserFunc
{
get;
set;
}
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpProxyFunc
{
get;
set;
}
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests
/// return the ExternalProxy object with valid credentials
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{
get;
set;
}
/// <summary> /// <summary>
/// A list of IpAddress & port this proxy is listening to /// A list of IpAddress & port this proxy is listening to
/// </summary> /// </summary>
...@@ -254,13 +288,14 @@ namespace Titanium.Web.Proxy ...@@ -254,13 +288,14 @@ namespace Titanium.Web.Proxy
//If certificate was trusted by the machine //If certificate was trusted by the machine
if (certTrusted) if (certValidated)
{ {
systemProxySettingsManager.SetHttpsProxy( systemProxySettingsManager.SetHttpsProxy(
Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(), Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
endPoint.Port); endPoint.Port);
} }
endPoint.IsSystemHttpsProxy = true; endPoint.IsSystemHttpsProxy = true;
#if !DEBUG #if !DEBUG
...@@ -304,9 +339,14 @@ namespace Titanium.Web.Proxy ...@@ -304,9 +339,14 @@ namespace Titanium.Web.Proxy
} }
certificateCacheManager = new CertificateManager(RootCertificateIssuerName, certificateCacheManager = new CertificateManager(RootCertificateIssuerName,
RootCertificateName); RootCertificateName, ExceptionFunc);
certValidated = certificateCacheManager.CreateTrustedRootCertificate();
certTrusted = certificateCacheManager.CreateTrustedRootCertificate(); if (TrustRootCertificate)
{
certificateCacheManager.TrustRootCertificate();
}
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
......
...@@ -25,83 +25,6 @@ namespace Titanium.Web.Proxy ...@@ -25,83 +25,6 @@ namespace Titanium.Web.Proxy
partial class ProxyServer partial class ProxyServer
{ {
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, IEnumerable<HttpHeader> Headers)
{
if (AuthenticateUserFunc == null)
{
return true;
}
try
{
if (!Headers.Where(t => t.Name == "Proxy-Authorization").Any())
{
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Required", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
else
{
var headerValue = Headers.Where(t => t.Name == "Proxy-Authorization").FirstOrDefault().Value.Trim();
if (!headerValue.ToLower().StartsWith("basic"))
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
headerValue = headerValue.Substring(5).Trim();
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue));
if (decoded.Contains(":") == false)
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
var username = decoded.Substring(0, decoded.IndexOf(':'));
var password = decoded.Substring(decoded.IndexOf(':') + 1);
return await AuthenticateUserFunc(username, password).ConfigureAwait(false);
}
}
catch (Exception e)
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
}
//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)
...@@ -172,6 +95,7 @@ namespace Titanium.Web.Proxy ...@@ -172,6 +95,7 @@ namespace Titanium.Web.Proxy
var newHeader = new HttpHeader(header[0], header[1]); var newHeader = new HttpHeader(header[0], header[1]);
connectRequestHeaders.Add(newHeader); connectRequestHeaders.Add(newHeader);
} }
if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false) if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false)
{ {
Dispose(clientStream, clientStreamReader, clientStreamWriter, null); Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
...@@ -287,6 +211,7 @@ namespace Titanium.Web.Proxy ...@@ -287,6 +211,7 @@ namespace Titanium.Web.Proxy
else else
{ {
clientStreamReader = new CustomBinaryReader(clientStream); clientStreamReader = new CustomBinaryReader(clientStream);
clientStreamWriter = new StreamWriter(clientStream);
} }
//now read the request line //now read the request line
...@@ -413,7 +338,7 @@ namespace Titanium.Web.Proxy ...@@ -413,7 +338,7 @@ namespace Titanium.Web.Proxy
} }
catch (Exception e) catch (Exception e)
{ {
ProxyServer.ExceptionFunc(e); ExceptionFunc(e);
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args); Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args);
return; return;
} }
...@@ -525,7 +450,6 @@ namespace Titanium.Web.Proxy ...@@ -525,7 +450,6 @@ namespace Titanium.Web.Proxy
} }
PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession); PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority;
...@@ -577,7 +501,7 @@ namespace Titanium.Web.Proxy ...@@ -577,7 +501,7 @@ namespace Titanium.Web.Proxy
} }
catch (Exception e) catch (Exception e)
{ {
ProxyServer.ExceptionFunc(e); ExceptionFunc(e);
Dispose(clientStream, clientStreamReader, clientStreamWriter, args); Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
break; break;
} }
......
...@@ -30,6 +30,7 @@ namespace Titanium.Web.Proxy ...@@ -30,6 +30,7 @@ namespace Titanium.Web.Proxy
} }
args.ReRequest = false; args.ReRequest = false;
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
{ {
...@@ -43,11 +44,13 @@ namespace Titanium.Web.Proxy ...@@ -43,11 +44,13 @@ namespace Titanium.Web.Proxy
await Task.WhenAll(handlerTasks); await Task.WhenAll(handlerTasks);
} }
if(args.ReRequest) if(args.ReRequest)
{ {
await HandleHttpSessionRequestInternal(null, args, null, null, true).ConfigureAwait(false); await HandleHttpSessionRequestInternal(null, args, null, null, true).ConfigureAwait(false);
return; return;
} }
args.WebSession.Response.ResponseLocked = true; args.WebSession.Response.ResponseLocked = true;
//Write back to client 100-conitinue response if that's what server returned //Write back to client 100-conitinue response if that's what server returned
...@@ -116,8 +119,9 @@ namespace Titanium.Web.Proxy ...@@ -116,8 +119,9 @@ namespace Titanium.Web.Proxy
await args.ProxyClient.ClientStream.FlushAsync(); await args.ProxyClient.ClientStream.FlushAsync();
} }
catch catch(Exception e)
{ {
ExceptionFunc(e);
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader,
args.ProxyClient.ClientStreamWriter, args); args.ProxyClient.ClientStreamWriter, args);
} }
......
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Network\CachedCertificate.cs" /> <Compile Include="Network\CachedCertificate.cs" />
<Compile Include="Network\CertEnrollEngine.cs" /> <Compile Include="Network\CertificateMaker.cs" />
<Compile Include="Network\ProxyClient.cs" /> <Compile Include="Network\ProxyClient.cs" />
<Compile Include="Exceptions\BodyNotFoundException.cs" /> <Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" /> <Compile Include="Extensions\HttpWebResponseExtensions.cs" />
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
<Compile Include="Models\HttpHeader.cs" /> <Compile Include="Models\HttpHeader.cs" />
<Compile Include="Http\HttpWebClient.cs" /> <Compile Include="Http\HttpWebClient.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProxyAuthorizationHandler.cs" />
<Compile Include="RequestHandler.cs" /> <Compile Include="RequestHandler.cs" />
<Compile Include="ResponseHandler.cs" /> <Compile Include="ResponseHandler.cs" />
<Compile Include="Helpers\CustomBinaryReader.cs" /> <Compile Include="Helpers\CustomBinaryReader.cs" />
......
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