Unverified Commit 4640ea36 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #331 from justcoding121/develop

Beta 3.0.171+ review
parents 250a081e bfb66f70
...@@ -11,6 +11,7 @@ using Titanium.Web.Proxy.EventArguments; ...@@ -11,6 +11,7 @@ using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Examples.Wpf namespace Titanium.Web.Proxy.Examples.Wpf
{ {
...@@ -62,7 +63,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -62,7 +63,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public MainWindow() public MainWindow()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
//proxyServer.CertificateEngine = CertificateEngine.BouncyCastle;
proxyServer.TrustRootCertificate = true; proxyServer.TrustRootCertificate = true;
proxyServer.CertificateManager.TrustRootCertificateAsAdministrator();
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
......
...@@ -82,14 +82,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -82,14 +82,9 @@ namespace Titanium.Web.Proxy.EventArguments
public HttpWebClient WebSession { get; } public HttpWebClient WebSession { get; }
/// <summary> /// <summary>
/// Are we using a custom upstream HTTP proxy? /// Are we using a custom upstream HTTP(S) proxy?
/// </summary> /// </summary>
public ExternalProxy CustomUpStreamHttpProxyUsed { get; internal set; } public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
/// <summary>
/// Are we using a custom upstream HTTPS proxy?
/// </summary>
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; internal set; }
public event EventHandler<DataEventArgs> DataSent; public event EventHandler<DataEventArgs> DataSent;
...@@ -519,8 +514,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -519,8 +514,7 @@ namespace Titanium.Web.Proxy.EventArguments
public void Dispose() public void Dispose()
{ {
httpResponseHandler = null; httpResponseHandler = null;
CustomUpStreamHttpProxyUsed = null; CustomUpStreamProxyUsed = null;
CustomUpStreamHttpsProxyUsed = null;
WebSession.FinishSession(); WebSession.FinishSession();
} }
......
...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Http
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var writer = new HttpRequestWriter(ms, bufferSize)) using (var writer = new HttpRequestWriter(ms, bufferSize))
{ {
var upstreamProxy = ServerConnection.UpStreamHttpProxy; var upstreamProxy = ServerConnection.UpStreamProxy;
bool useUpstreamProxy = upstreamProxy != null && ServerConnection.IsHttps == false; bool useUpstreamProxy = upstreamProxy != null && ServerConnection.IsHttps == false;
......
#if NET45 using System;
using System; using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
...@@ -27,6 +28,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -27,6 +28,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
private const int certificateValidDays = 1825; private const int certificateValidDays = 1825;
private const int certificateGraceDays = 366; private const int certificateGraceDays = 366;
// The FriendlyName value cannot be set on Unix.
// Set this flag to true when exception detected to avoid further exceptions
private static bool doNotSetFriendlyName;
/// <summary> /// <summary>
/// Makes the certificate. /// Makes the certificate.
/// </summary> /// </summary>
...@@ -87,6 +92,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -87,6 +92,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension); certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
} }
// Subject Public Key // Subject Public Key
var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength); var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator(); var keyPairGenerator = new RsaKeyPairGenerator();
...@@ -97,12 +103,15 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -97,12 +103,15 @@ namespace Titanium.Web.Proxy.Network.Certificate
// Set certificate intended purposes to only Server Authentication // Set certificate intended purposes to only Server Authentication
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth)); certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
if (issuerPrivateKey == null)
{
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true));
}
var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, secureRandom); var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, secureRandom);
// Self-sign the certificate // Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory); var certificate = certificateGenerator.Generate(signatureFactory);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
// Corresponding private key // Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
...@@ -119,8 +128,20 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -119,8 +128,20 @@ namespace Titanium.Web.Proxy.Network.Certificate
rsa.Exponent2, rsa.Coefficient); rsa.Exponent2, rsa.Coefficient);
// Set private key onto certificate instance // Set private key onto certificate instance
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams); x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
if (!doNotSetFriendlyName)
{
try
{
x509Certificate.FriendlyName = subjectName; x509Certificate.FriendlyName = subjectName;
}
catch (PlatformNotSupportedException)
{
doNotSetFriendlyName = true;
}
}
return x509Certificate; return x509Certificate;
} }
...@@ -136,18 +157,24 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -136,18 +157,24 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCertificate">The signing certificate.</param> /// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception> /// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot, string hostName, string subjectName, DateTime validFrom, DateTime validTo, private X509Certificate2 MakeCertificateInternal(bool isRoot,
X509Certificate2 signingCertificate) string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
if (isRoot != (null == signingCertificate)) if (isRoot != (null == signingCertificate))
{ {
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate)); throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate));
} }
return isRoot if (isRoot)
? GenerateCertificate(null, subjectName, subjectName, validFrom, validTo) {
: GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo);
issuerPrivateKey: DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey).Private); }
else
{
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, issuerPrivateKey: kp.Private);
}
} }
/// <summary> /// <summary>
...@@ -163,10 +190,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -163,10 +190,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
X509Certificate2 certificate = null; #if NET45
if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
X509Certificate2 certificate = null;
using (var manualResetEvent = new ManualResetEventSlim(false)) using (var manualResetEvent = new ManualResetEventSlim(false))
{ {
ThreadPool.QueueUserWorkItem(o => ThreadPool.QueueUserWorkItem(o =>
...@@ -184,10 +211,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -184,10 +211,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
return certificate; return certificate;
} }
#endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
} }
} }
} }
#endif
\ No newline at end of file
...@@ -12,11 +12,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -12,11 +12,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal class TcpConnection : IDisposable internal class TcpConnection : IDisposable
{ {
internal ExternalProxy UpStreamHttpProxy { get; set; } internal ExternalProxy UpStreamProxy { get; set; }
internal ExternalProxy UpStreamHttpsProxy { get; set; }
internal ExternalProxy UpStreamProxy => UseUpstreamProxy ? IsHttps ? UpStreamHttpsProxy : UpStreamHttpProxy : null;
internal string HostName { get; set; } internal string HostName { get; set; }
......
...@@ -27,15 +27,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -27,15 +27,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <param name="isConnect"></param> /// <param name="isConnect"></param>
/// <param name="upStreamEndPoint"></param> /// <param name="upStreamEndPoint"></param>
/// <param name="externalHttpProxy"></param> /// <param name="externalProxy"></param>
/// <param name="externalHttpsProxy"></param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpConnection> CreateClient(ProxyServer server, internal async Task<TcpConnection> CreateClient(ProxyServer server,
string remoteHostName, int remotePort, Version httpVersion, bool isHttps, string remoteHostName, int remotePort, Version httpVersion, bool isHttps,
bool isConnect, IPEndPoint upStreamEndPoint, ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy) bool isConnect, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy)
{ {
bool useUpstreamProxy = false; bool useUpstreamProxy = false;
var externalProxy = isHttps ? externalHttpsProxy : externalHttpProxy;
//check if external proxy is set for HTTP/HTTPS //check if external proxy is set for HTTP/HTTPS
if (externalProxy != null && !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort)) if (externalProxy != null && !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort))
...@@ -129,8 +127,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -129,8 +127,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
return new TcpConnection return new TcpConnection
{ {
UpStreamHttpProxy = externalHttpProxy, UpStreamProxy = externalProxy,
UpStreamHttpsProxy = externalHttpsProxy,
UpStreamEndPoint = upStreamEndPoint, UpStreamEndPoint = upStreamEndPoint,
HostName = remoteHostName, HostName = remoteHostName,
Port = remotePort, Port = remotePort,
......
...@@ -273,16 +273,10 @@ namespace Titanium.Web.Proxy ...@@ -273,16 +273,10 @@ namespace Titanium.Web.Proxy
public string ProxyRealm { get; set; } = "TitaniumProxy"; public string ProxyRealm { get; set; } = "TitaniumProxy";
/// <summary> /// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP 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>> GetCustomUpStreamHttpProxyFunc { get; set; } public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { 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
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc { get; set; }
/// <summary> /// <summary>
/// A list of IpAddress and port this proxy is listening to /// A list of IpAddress and port this proxy is listening to
...@@ -571,16 +565,13 @@ namespace Titanium.Web.Proxy ...@@ -571,16 +565,13 @@ namespace Titanium.Web.Proxy
} }
} }
if (ForwardToUpstreamGateway if (ForwardToUpstreamGateway && GetCustomUpStreamProxyFunc == null && systemProxySettingsManager != null)
&& GetCustomUpStreamHttpProxyFunc == null && GetCustomUpStreamHttpsProxyFunc == null
&& systemProxySettingsManager != null)
{ {
// Use WinHttp to handle PAC/WAPD scripts. // Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver = new WinHttpWebProxyFinder(); systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE(); systemProxyResolver.LoadFromIE();
GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy; GetCustomUpStreamProxyFunc = GetSystemUpStreamProxy;
GetCustomUpStreamHttpsProxyFunc = GetSystemUpStreamProxy;
} }
#endif #endif
...@@ -692,13 +683,19 @@ namespace Titanium.Web.Proxy ...@@ -692,13 +683,19 @@ namespace Titanium.Web.Proxy
endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port; endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
while (true) while (proxyRunning)
{
try
{ {
TcpClient tcpClient = await endPoint.Listener.AcceptTcpClientAsync(); TcpClient tcpClient = await endPoint.Listener.AcceptTcpClientAsync();
if (tcpClient != null) if (tcpClient != null)
Task.Run(async () => HandleClient(tcpClient, endPoint)); Task.Run(async () => HandleClient(tcpClient, endPoint));
} }
catch (ObjectDisposedException)
{
// proxy was stopped
}
}
} }
#endif #endif
......
...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy ...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
partial class ProxyServer partial class ProxyServer
{ {
private bool IsWindowsAuthenticationEnabledAndSupported => EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono; private bool isWindowsAuthenticationEnabledAndSupported => EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono;
/// <summary> /// <summary>
/// This is called when client is aware of proxy /// This is called when client is aware of proxy
...@@ -340,7 +340,7 @@ namespace Titanium.Web.Proxy ...@@ -340,7 +340,7 @@ namespace Titanium.Web.Proxy
//if win auth is enabled //if win auth is enabled
//we need a cache of request body //we need a cache of request body
//so that we can send it after authentication in WinAuthHandler.cs //so that we can send it after authentication in WinAuthHandler.cs
if (IsWindowsAuthenticationEnabledAndSupported && args.WebSession.Request.HasBody) if (isWindowsAuthenticationEnabledAndSupported && args.WebSession.Request.HasBody)
{ {
await args.GetRequestBody(); await args.GetRequestBody();
} }
...@@ -581,35 +581,23 @@ namespace Titanium.Web.Proxy ...@@ -581,35 +581,23 @@ namespace Titanium.Web.Proxy
/// <returns></returns> /// <returns></returns>
private async Task<TcpConnection> GetServerConnection(SessionEventArgs args, bool isConnect) private async Task<TcpConnection> GetServerConnection(SessionEventArgs args, bool isConnect)
{ {
ExternalProxy customUpStreamHttpProxy = null; ExternalProxy customUpStreamProxy = null;
ExternalProxy customUpStreamHttpsProxy = null;
if (args.WebSession.Request.IsHttps) bool isHttps = args.IsHttps;
if (GetCustomUpStreamProxyFunc != null)
{ {
if (GetCustomUpStreamHttpsProxyFunc != null) customUpStreamProxy = await GetCustomUpStreamProxyFunc(args);
{
customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args);
}
}
else
{
if (GetCustomUpStreamHttpProxyFunc != null)
{
customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args);
}
} }
args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; args.CustomUpStreamProxyUsed = customUpStreamProxy;
args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy;
return await tcpConnectionFactory.CreateClient(this, return await tcpConnectionFactory.CreateClient(this,
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port, args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion, args.WebSession.Request.HttpVersion,
args.IsHttps, isConnect, isHttps, isConnect,
args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy));
customUpStreamHttpsProxy ?? UpStreamHttpsProxy);
} }
/// <summary> /// <summary>
......
...@@ -5,7 +5,6 @@ using Titanium.Web.Proxy.Compression; ...@@ -5,7 +5,6 @@ using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -29,7 +28,7 @@ namespace Titanium.Web.Proxy ...@@ -29,7 +28,7 @@ namespace Titanium.Web.Proxy
var response = args.WebSession.Response; var response = args.WebSession.Response;
//check for windows authentication //check for windows authentication
if (IsWindowsAuthenticationEnabledAndSupported && response.StatusCode == (int)HttpStatusCode.Unauthorized) if (isWindowsAuthenticationEnabledAndSupported && response.StatusCode == (int)HttpStatusCode.Unauthorized)
{ {
bool disposed = await Handle401UnAuthorized(args); bool disposed = await Handle401UnAuthorized(args);
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.2" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
<PackageReference Include="StreamExtended" Version="1.0.81" /> <PackageReference Include="StreamExtended" Version="1.0.81" />
</ItemGroup> </ItemGroup>
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<tags></tags> <tags></tags>
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.81" /> <dependency id="StreamExtended" version="1.0.81" />
<dependency id="Portable.BouncyCastle" version="1.8.1.2" /> <dependency id="Portable.BouncyCastle" version="1.8.1.3" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.1.2" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="net45" />
<package id="StreamExtended" version="1.0.81" targetFramework="net45" /> <package id="StreamExtended" version="1.0.81" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
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