Unverified Commit d3829eab authored by honfika's avatar honfika Committed by GitHub

Merge pull request #409 from justcoding121/develop

Merge to beta
parents abe87ce7 26d815c3
...@@ -80,8 +80,6 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -80,8 +80,6 @@ namespace Titanium.Web.Proxy.Examples.Basic
explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000) explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000)
{ {
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Use self-issued generic certificate on all https requests //Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain //Optimizes performance by not creating a certificate for each https-enabled domain
//Useful when certificate trust is not required by proxy clients //Useful when certificate trust is not required by proxy clients
...@@ -152,7 +150,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -152,7 +150,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning //Useful for clients that use certificate pinning
//for example dropbox.com //for example dropbox.com
e.Excluded = true; e.DecryptSsl = false;
} }
} }
......
...@@ -119,7 +119,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -119,7 +119,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
string hostname = e.WebSession.Request.RequestUri.Host; string hostname = e.WebSession.Request.RequestUri.Host;
if (hostname.EndsWith("webex.com")) if (hostname.EndsWith("webex.com"))
{ {
e.Excluded = true; e.DecryptSsl = false;
} }
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
...@@ -128,7 +128,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -128,7 +128,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
}); });
} }
private async Task ProxyServer_BeforeTunnelConnectResponse(object sender, SessionEventArgs e) private async Task ProxyServer_BeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{ {
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
{ {
...@@ -191,7 +191,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -191,7 +191,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
}); });
} }
private SessionListItem AddSession(SessionEventArgs e) private SessionListItem AddSession(SessionEventArgsBase e)
{ {
var item = CreateSessionListItem(e); var item = CreateSessionListItem(e);
Sessions.Add(item); Sessions.Add(item);
...@@ -199,7 +199,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -199,7 +199,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
return item; return item;
} }
private SessionListItem CreateSessionListItem(SessionEventArgs e) private SessionListItem CreateSessionListItem(SessionEventArgsBase e)
{ {
lastSessionNumber++; lastSessionNumber++;
bool isTunnelConnect = e is TunnelConnectSessionEventArgs; bool isTunnelConnect = e is TunnelConnectSessionEventArgs;
......
...@@ -66,8 +66,6 @@ proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; ...@@ -66,8 +66,6 @@ proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{ {
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Use self-issued generic certificate on all https requests //Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain //Optimizes performance by not creating a certificate for each https-enabled domain
//Useful when certificate trust is not required by proxy clients //Useful when certificate trust is not required by proxy clients
...@@ -135,7 +133,7 @@ private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSess ...@@ -135,7 +133,7 @@ private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSess
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning //Useful for clients that use certificate pinning
//for example dropbox.com //for example dropbox.com
e.Excluded = true; e.DecryptSsl = false;
} }
} }
...@@ -232,7 +230,6 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs ...@@ -232,7 +230,6 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs
#### Roadmap #### Roadmap
* Support HTTP 2.0 * Support HTTP 2.0
* Support SOCKS protocol
#### Collaborators #### Collaborators
......
...@@ -22,35 +22,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -22,35 +22,17 @@ namespace Titanium.Web.Proxy.EventArguments
/// A proxy session ends when client terminates connection to proxy /// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy /// or when server terminates connection from proxy
/// </summary> /// </summary>
public class SessionEventArgs : EventArgs, IDisposable public class SessionEventArgs : SessionEventArgsBase
{ {
private static readonly byte[] emptyData = new byte[0]; private static readonly byte[] emptyData = new byte[0];
/// <summary>
/// Size of Buffers used by this object
/// </summary>
private readonly int bufferSize;
private readonly ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
private bool reRequest; private bool reRequest;
/// <summary>
/// Holds a reference to client
/// </summary>
internal ProxyClient ProxyClient { get; }
private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null; private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null;
/// <summary>
/// Returns a unique Id for this request/response session
/// same as RequestId of WebSession
/// </summary>
public Guid Id => WebSession.RequestId;
/// <summary> /// <summary>
/// Should we send the request again /// Should we send the request again
/// </summary> /// </summary>
...@@ -68,42 +50,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -68,42 +50,11 @@ namespace Titanium.Web.Proxy.EventArguments
} }
} }
/// <summary>
/// Does this session uses SSL
/// </summary>
public bool IsHttps => WebSession.Request.IsHttps;
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary>
/// A web session corresponding to a single request/response sequence
/// within a proxy connection
/// </summary>
public HttpWebClient WebSession { get; }
/// <summary>
/// Are we using a custom upstream HTTP(S) proxy?
/// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
public event EventHandler<DataEventArgs> DataSent;
public event EventHandler<DataEventArgs> DataReceived;
/// <summary> /// <summary>
/// Occurs when multipart request part sent. /// Occurs when multipart request part sent.
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
public ProxyEndPoint LocalEndPoint { get; }
public bool IsTransparent => LocalEndPoint is TransparentProxyEndPoint;
public Exception Exception { get; internal set; }
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
...@@ -113,32 +64,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -113,32 +64,8 @@ namespace Titanium.Web.Proxy.EventArguments
} }
protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc, Request request) protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc, Request request)
: base(bufferSize, endPoint, exceptionFunc, request)
{ {
this.bufferSize = bufferSize;
this.exceptionFunc = exceptionFunc;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() =>
{
if (RunTime.IsWindows)
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
}
//can't access process Id of remote request from remote machine
return -1;
}
throw new PlatformNotSupportedException();
});
} }
private CustomBufferedStream GetStream(bool isRequest) private CustomBufferedStream GetStream(bool isRequest)
...@@ -188,30 +115,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -188,30 +115,6 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response = new Response(); WebSession.Response = new Response();
} }
internal void OnDataSent(byte[] buffer, int offset, int count)
{
try
{
DataSent?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
exceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
internal void OnDataReceived(byte[] buffer, int offset, int count)
{
try
{
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
exceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers) internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers)
{ {
try try
...@@ -220,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -220,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
exceptionFunc(new Exception("Exception thrown in user event", ex)); ExceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
...@@ -257,7 +160,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -257,7 +160,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, bufferSize); var writer = new HttpWriter(bodyStream, BufferSize);
if (isRequest) if (isRequest)
{ {
...@@ -282,7 +185,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -282,7 +185,7 @@ namespace Titanium.Web.Proxy.EventArguments
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, bufferSize); var writer = new HttpWriter(bodyStream, BufferSize);
await CopyBodyAsync(isRequest, writer, TransformationMode.None, null); await CopyBodyAsync(isRequest, writer, TransformationMode.None, null);
} }
} }
...@@ -303,8 +206,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -303,8 +206,8 @@ namespace Titanium.Web.Proxy.EventArguments
var reader = GetStreamReader(true); var reader = GetStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, bufferSize)) using (var copyStream = new CopyStream(reader, writer, BufferSize))
using (var copyStreamReader = new CustomBinaryReader(copyStream, bufferSize)) using (var copyStreamReader = new CustomBinaryReader(copyStream, BufferSize))
{ {
while (contentLength > copyStream.ReadBytes) while (contentLength > copyStream.ReadBytes)
{ {
...@@ -365,8 +268,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -365,8 +268,8 @@ namespace Titanium.Web.Proxy.EventArguments
try try
{ {
var bufStream = new CustomBufferedStream(s, bufferSize, true); var bufStream = new CustomBufferedStream(s, BufferSize, true);
reader = new CustomBinaryReader(bufStream, bufferSize); reader = new CustomBinaryReader(bufStream, BufferSize);
await writer.CopyBodyAsync(reader, false, -1, onCopy); await writer.CopyBodyAsync(reader, false, -1, onCopy);
} }
...@@ -388,7 +291,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -388,7 +291,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
var buffer = BufferPool.GetBuffer(bufferSize); var buffer = BufferPool.GetBuffer(BufferSize);
try try
{ {
int boundaryLength = boundary.Length + 4; int boundaryLength = boundary.Length + 4;
...@@ -684,16 +587,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -684,16 +587,10 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// implement any cleanup here /// implement any cleanup here
/// </summary> /// </summary>
public void Dispose() public override void Dispose()
{ {
CustomUpStreamProxyUsed = null;
DataSent = null;
DataReceived = null;
MultipartRequestPartSent = null; MultipartRequestPartSent = null;
Exception = null; base.Dispose();
WebSession.FinishSession();
} }
} }
} }
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended.Network;
using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Holds info related to a single proxy session (single request/response sequence)
/// A proxy session is bounded to a single connection from client
/// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy
/// </summary>
public class SessionEventArgsBase : EventArgs, IDisposable
{
/// <summary>
/// Size of Buffers used by this object
/// </summary>
protected readonly int BufferSize;
protected readonly ExceptionHandler ExceptionFunc;
/// <summary>
/// Holds a reference to client
/// </summary>
internal ProxyClient ProxyClient { get; }
/// <summary>
/// Returns a unique Id for this request/response session
/// same as RequestId of WebSession
/// </summary>
public Guid Id => WebSession.RequestId;
/// <summary>
/// Does this session uses SSL
/// </summary>
public bool IsHttps => WebSession.Request.IsHttps;
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary>
/// A web session corresponding to a single request/response sequence
/// within a proxy connection
/// </summary>
public HttpWebClient WebSession { get; }
/// <summary>
/// Are we using a custom upstream HTTP(S) proxy?
/// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
public event EventHandler<DataEventArgs> DataSent;
public event EventHandler<DataEventArgs> DataReceived;
public ProxyEndPoint LocalEndPoint { get; }
public bool IsTransparent => LocalEndPoint is TransparentProxyEndPoint;
public Exception Exception { get; internal set; }
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, exceptionFunc, null)
{
}
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc, Request request)
{
this.BufferSize = bufferSize;
this.ExceptionFunc = exceptionFunc;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() =>
{
if (RunTime.IsWindows)
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
}
//can't access process Id of remote request from remote machine
return -1;
}
throw new PlatformNotSupportedException();
});
}
internal void OnDataSent(byte[] buffer, int offset, int count)
{
try
{
DataSent?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
internal void OnDataReceived(byte[] buffer, int offset, int count)
{
try
{
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
/// <summary>
/// implement any cleanup here
/// </summary>
public virtual void Dispose()
{
CustomUpStreamProxyUsed = null;
DataSent = null;
DataReceived = null;
Exception = null;
WebSession.FinishSession();
}
}
}
...@@ -4,9 +4,11 @@ using Titanium.Web.Proxy.Models; ...@@ -4,9 +4,11 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
public class TunnelConnectSessionEventArgs : SessionEventArgs public class TunnelConnectSessionEventArgs : SessionEventArgsBase
{ {
public bool Excluded { get; set; } public bool DecryptSsl { get; set; } = true;
public bool BlockConnect { get; set; }
public bool IsHttpsConnect { get; internal set; } public bool IsHttpsConnect { get; internal set; }
......
...@@ -17,13 +17,13 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -17,13 +17,13 @@ namespace Titanium.Web.Proxy.Exceptions
/// <param name="session">The <see cref="SessionEventArgs"/> instance containing the event data.</param> /// <param name="session">The <see cref="SessionEventArgs"/> instance containing the event data.</param>
/// <param name="innerException">Inner exception associated to upstream proxy authorization</param> /// <param name="innerException">Inner exception associated to upstream proxy authorization</param>
/// <param name="headers">Http's headers associated</param> /// <param name="headers">Http's headers associated</param>
internal ProxyAuthorizationException(string message, SessionEventArgs session, Exception innerException, IEnumerable<HttpHeader> headers) : base(message, innerException) internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException, IEnumerable<HttpHeader> headers) : base(message, innerException)
{ {
Session = session; Session = session;
Headers = headers; Headers = headers;
} }
public SessionEventArgs Session { get; } public SessionEventArgsBase Session { get; }
/// <summary> /// <summary>
/// Headers associated with the authorization exception /// Headers associated with the authorization exception
......
...@@ -17,11 +17,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -17,11 +17,6 @@ namespace Titanium.Web.Proxy.Models
internal bool IsSystemHttpsProxy { get; set; } internal bool IsSystemHttpsProxy { get; set; }
/// <summary>
/// Enable SSL?
/// </summary>
public bool DecryptSsl { get; }
/// <summary> /// <summary>
/// Generic certificate to use for SSL decryption. /// Generic certificate to use for SSL decryption.
/// </summary> /// </summary>
...@@ -30,7 +25,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -30,7 +25,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Intercept tunnel connect request /// Intercept tunnel connect request
/// Valid only for explicit endpoints /// Valid only for explicit endpoints
/// Set the <see cref="TunnelConnectSessionEventArgs.Excluded"/> property to true if this HTTP connect request should'nt be decrypted and instead be relayed /// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl"/> property to false if this HTTP connect request should'nt be decrypted and instead be relayed
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest; public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest;
...@@ -46,9 +41,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -46,9 +41,8 @@ namespace Titanium.Web.Proxy.Models
/// <param name="ipAddress"></param> /// <param name="ipAddress"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="decryptSsl"></param> /// <param name="decryptSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port) public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port, decryptSsl)
{ {
this.DecryptSsl = decryptSsl;
} }
internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc) internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
......
...@@ -16,13 +16,14 @@ namespace Titanium.Web.Proxy.Models ...@@ -16,13 +16,14 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
/// <param name="ipAddress"></param> /// <param name="ipAddress"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="enableSsl"></param> /// <param name="decryptSsl"></param>
protected ProxyEndPoint(IPAddress ipAddress, int port) protected ProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl)
{ {
IpAddress = ipAddress; IpAddress = ipAddress;
Port = port; Port = port;
DecryptSsl = decryptSsl;
} }
/// <summary> /// <summary>
/// underlying TCP Listener object /// underlying TCP Listener object
/// </summary> /// </summary>
...@@ -38,6 +39,10 @@ namespace Titanium.Web.Proxy.Models ...@@ -38,6 +39,10 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public int Port { get; internal set; } public int Port { get; internal set; }
/// <summary>
/// Enable SSL?
/// </summary>
public bool DecryptSsl { get; }
/// <summary> /// <summary>
/// Is IPv6 enabled? /// Is IPv6 enabled?
......
...@@ -19,8 +19,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -19,8 +19,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
/// <param name="ipAddress"></param> /// <param name="ipAddress"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="enableSsl"></param> /// <param name="decryptSsl"></param>
public TransparentProxyEndPoint(IPAddress ipAddress, int port) : base(ipAddress, port) public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port, decryptSsl)
{ {
GenericCertificateName = "localhost"; GenericCertificateName = "localhost";
} }
......
...@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy ...@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy
{ {
public partial class ProxyServer public partial class ProxyServer
{ {
private async Task<bool> CheckAuthorization(SessionEventArgs session) private async Task<bool> CheckAuthorization(SessionEventArgsBase session)
{ {
if (AuthenticateUserFunc == null) if (AuthenticateUserFunc == null)
{ {
......
...@@ -177,7 +177,7 @@ namespace Titanium.Web.Proxy ...@@ -177,7 +177,7 @@ namespace Titanium.Web.Proxy
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests
/// return the ExternalProxy object with valid credentials /// return the ExternalProxy object with valid credentials
/// </summary> /// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
/// <summary> /// <summary>
/// Intercept request to server /// Intercept request to server
...@@ -581,7 +581,7 @@ namespace Titanium.Web.Proxy ...@@ -581,7 +581,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="sessionEventArgs">The <see cref="SessionEventArgs"/> instance containing the event data.</param> /// <param name="sessionEventArgs">The <see cref="SessionEventArgs"/> instance containing the event data.</param>
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns> /// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns>
private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgs sessionEventArgs) private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
{ {
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri); var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
return Task.FromResult(proxy); return Task.FromResult(proxy);
......
...@@ -79,7 +79,24 @@ namespace Titanium.Web.Proxy ...@@ -79,7 +79,24 @@ namespace Titanium.Web.Proxy
await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc); await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);
//filter out excluded host names //filter out excluded host names
bool excluded = !endPoint.DecryptSsl || connectArgs.Excluded; bool decryptSsl = endPoint.DecryptSsl && connectArgs.DecryptSsl;
if (connectArgs.BlockConnect)
{
if (connectArgs.WebSession.Response.StatusCode == 0)
{
connectArgs.WebSession.Response = new Response
{
HttpVersion = HttpHeader.Version11,
StatusCode = (int)HttpStatusCode.Forbidden,
StatusDescription = "Forbidden",
};
}
//send the response
await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response);
return;
}
if (await CheckAuthorization(connectArgs) == false) if (await CheckAuthorization(connectArgs) == false)
{ {
...@@ -109,7 +126,7 @@ namespace Titanium.Web.Proxy ...@@ -109,7 +126,7 @@ namespace Titanium.Web.Proxy
await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello); await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);
if (!excluded && isClientHello) if (decryptSsl && isClientHello)
{ {
connectRequest.RequestUri = new Uri("https://" + httpUrl); connectRequest.RequestUri = new Uri("https://" + httpUrl);
...@@ -143,12 +160,12 @@ namespace Titanium.Web.Proxy ...@@ -143,12 +160,12 @@ namespace Titanium.Web.Proxy
if (await HttpHelper.IsConnectMethod(clientStream) == -1) if (await HttpHelper.IsConnectMethod(clientStream) == -1)
{ {
// It can be for example some Google (Cloude Messaging for Chrome) magic // It can be for example some Google (Cloude Messaging for Chrome) magic
excluded = true; decryptSsl = false;
} }
} }
//Hostname is excluded or it is not an HTTPS connect //Hostname is excluded or it is not an HTTPS connect
if (excluded || !isClientHello) if (!decryptSsl || !isClientHello)
{ {
//create new connection //create new connection
using (var connection = await GetServerConnection(connectArgs, true)) using (var connection = await GetServerConnection(connectArgs, true))
...@@ -237,32 +254,72 @@ namespace Titanium.Web.Proxy ...@@ -237,32 +254,72 @@ namespace Titanium.Web.Proxy
if (isHttps) if (isHttps)
{ {
SslStream sslStream = null; httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;
try if (endPoint.DecryptSsl)
{ {
sslStream = new SslStream(clientStream); SslStream sslStream = null;
httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName; try
{
sslStream = new SslStream(clientStream);
string certName = HttpHelper.GetWildCardDomainName(httpsHostName); string certName = HttpHelper.GetWildCardDomainName(httpsHostName);
var certificate = await CertificateManager.CreateCertificateAsync(certName); var certificate = await CertificateManager.CreateCertificateAsync(certName);
//Successfully managed to authenticate the client using the fake certificate //Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamReader.Dispose(); clientStreamReader.Dispose();
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
}
catch (Exception e)
{
ExceptionFunc(new Exception($"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e));
sslStream?.Dispose();
return;
}
} }
catch (Exception e) else
{ {
ExceptionFunc(new Exception($"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e)); //create new connection
sslStream?.Dispose(); var connection = new TcpClient(UpStreamEndPoint);
return; await connection.ConnectAsync(httpsHostName, endPoint.Port);
connection.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
connection.SendTimeout = ConnectionTimeOutSeconds * 1000;
using (connection)
{
var serverStream = connection.GetStream();
int available = clientStream.Available;
if (available > 0)
{
//send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available);
await serverStream.WriteAsync(data, 0, available);
await serverStream.FlushAsync();
}
finally
{
BufferPool.ReturnBuffer(data);
}
}
//var serverHelloInfo = await SslTools.PeekServerHello(serverStream);
await TcpHelper.SendRaw(clientStream, serverStream, BufferSize,
null, null, ExceptionFunc);
}
} }
} }
...@@ -590,7 +647,7 @@ namespace Titanium.Web.Proxy ...@@ -590,7 +647,7 @@ namespace Titanium.Web.Proxy
/// <param name="args"></param> /// <param name="args"></param>
/// <param name="isConnect"></param> /// <param name="isConnect"></param>
/// <returns></returns> /// <returns></returns>
private async Task<TcpConnection> GetServerConnection(SessionEventArgs args, bool isConnect) private async Task<TcpConnection> GetServerConnection(SessionEventArgsBase args, bool isConnect)
{ {
ExternalProxy customUpStreamProxy = null; ExternalProxy customUpStreamProxy = null;
......
...@@ -14,14 +14,14 @@ namespace Titanium.Web.Proxy ...@@ -14,14 +14,14 @@ namespace Titanium.Web.Proxy
public partial class ProxyServer public partial class ProxyServer
{ {
//possible header names //possible header names
private static readonly HashSet<string> authHeaderNames = new HashSet<string> private static readonly HashSet<string> authHeaderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
"WWW-Authenticate".ToLower(), "WWW-Authenticate",
//IIS 6.0 messed up names below //IIS 6.0 messed up names below
"WWWAuthenticate".ToLower(), "WWWAuthenticate",
"NTLMAuthorization".ToLower(), "NTLMAuthorization",
"NegotiateAuthorization".ToLower(), "NegotiateAuthorization",
"KerberosAuthorization".ToLower() "KerberosAuthorization"
}; };
private static readonly HashSet<string> authSchemes = new HashSet<string> private static readonly HashSet<string> authSchemes = new HashSet<string>
...@@ -48,8 +48,7 @@ namespace Titanium.Web.Proxy ...@@ -48,8 +48,7 @@ namespace Titanium.Web.Proxy
var response = args.WebSession.Response; var response = args.WebSession.Response;
//check in non-unique headers first //check in non-unique headers first
var header = response.Headers.NonUniqueHeaders.FirstOrDefault( var header = response.Headers.NonUniqueHeaders.FirstOrDefault(x => authHeaderNames.Contains(x.Key));
x => authHeaderNames.Contains(x.Key.ToLower()));
if (!header.Equals(new KeyValuePair<string, List<HttpHeader>>())) if (!header.Equals(new KeyValuePair<string, List<HttpHeader>>()))
{ {
...@@ -68,7 +67,7 @@ namespace Titanium.Web.Proxy ...@@ -68,7 +67,7 @@ namespace Titanium.Web.Proxy
headerName = null; headerName = null;
//check in non-unique headers first //check in non-unique headers first
var uHeader = response.Headers.Headers.FirstOrDefault(x => authHeaderNames.Contains(x.Key.ToLower())); var uHeader = response.Headers.Headers.FirstOrDefault(x => authHeaderNames.Contains(x.Key));
if (!uHeader.Equals(new KeyValuePair<string, HttpHeader>())) if (!uHeader.Equals(new KeyValuePair<string, HttpHeader>()))
{ {
......
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