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