Commit 8022c434 authored by Rob Garrett's avatar Rob Garrett

Resharper cleanup

parent 4ef9a02d
using System; using System;
using System.Linq;
using System.Net.Security; using System.Net.Security;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -26,11 +25,13 @@ namespace Titanium.Web.Proxy ...@@ -26,11 +25,13 @@ namespace Titanium.Web.Proxy
//if user callback is registered then do it //if user callback is registered then do it
if (ServerCertificateValidationCallback != null) if (ServerCertificateValidationCallback != null)
{ {
var args = new CertificateValidationEventArgs(); var args = new CertificateValidationEventArgs
{
Certificate = certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors
};
args.Certificate = certificate;
args.Chain = chain;
args.SslPolicyErrors = sslPolicyErrors;
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList(); Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList();
...@@ -73,7 +74,6 @@ namespace Titanium.Web.Proxy ...@@ -73,7 +74,6 @@ namespace Titanium.Web.Proxy
string[] acceptableIssuers) string[] acceptableIssuers)
{ {
X509Certificate clientCertificate = null; X509Certificate clientCertificate = null;
var customSslStream = sender as SslStream;
if (acceptableIssuers != null && if (acceptableIssuers != null &&
acceptableIssuers.Length > 0 && acceptableIssuers.Length > 0 &&
...@@ -100,13 +100,15 @@ namespace Titanium.Web.Proxy ...@@ -100,13 +100,15 @@ namespace Titanium.Web.Proxy
//If user call back is registered //If user call back is registered
if (ClientCertificateSelectionCallback != null) if (ClientCertificateSelectionCallback != null)
{ {
var args = new CertificateSelectionEventArgs(); var args = new CertificateSelectionEventArgs
{
TargetHost = targetHost,
LocalCertificates = localCertificates,
RemoteCertificate = remoteCertificate,
AcceptableIssuers = acceptableIssuers,
ClientCertificate = clientCertificate
};
args.TargetHost = targetHost;
args.LocalCertificates = localCertificates;
args.RemoteCertificate = remoteCertificate;
args.AcceptableIssuers = acceptableIssuers;
args.ClientCertificate = clientCertificate;
Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList(); Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length]; Task[] handlerTasks = new Task[invocationList.Length];
......
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression namespace Titanium.Web.Proxy.Decompression
{ {
......
...@@ -8,12 +8,29 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -8,12 +8,29 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public class CertificateSelectionEventArgs : EventArgs public class CertificateSelectionEventArgs : EventArgs
{ {
/// <summary>
/// Sender object.
/// </summary>
public object Sender { get; internal set; } public object Sender { get; internal set; }
/// <summary>
/// Target host.
/// </summary>
public string TargetHost { get; internal set; } public string TargetHost { get; internal set; }
/// <summary>
/// Local certificates.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; } public X509CertificateCollection LocalCertificates { get; internal set; }
/// <summary>
/// Remote certificate.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; } public X509Certificate RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers.
/// </summary>
public string[] AcceptableIssuers { get; internal set; } public string[] AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate.
/// </summary>
public X509Certificate ClientCertificate { get; set; } public X509Certificate ClientCertificate { get; set; }
} }
......
...@@ -7,17 +7,24 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -7,17 +7,24 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// An argument passed on to the user for validating the server certificate during SSL authentication /// An argument passed on to the user for validating the server certificate during SSL authentication
/// </summary> /// </summary>
public class CertificateValidationEventArgs : EventArgs, IDisposable public class CertificateValidationEventArgs : EventArgs
{ {
/// <summary>
/// Certificate
/// </summary>
public X509Certificate Certificate { get; internal set; } public X509Certificate Certificate { get; internal set; }
/// <summary>
/// Certificate chain
/// </summary>
public X509Chain Chain { get; internal set; } public X509Chain Chain { get; internal set; }
/// <summary>
/// SSL policy errors.
/// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; } public SslPolicyErrors SslPolicyErrors { get; internal set; }
/// <summary>
/// is a valid certificate?
/// </summary>
public bool IsValid { get; set; } public bool IsValid { get; set; }
public void Dispose()
{
}
} }
} }
...@@ -44,7 +44,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -44,7 +44,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public Guid Id => WebSession.RequestId; public Guid Id => WebSession.RequestId;
//Should we send a rerequest /// <summary>
/// Should we send a rerequest
/// </summary>
public bool ReRequest public bool ReRequest
{ {
get; get;
...@@ -56,7 +58,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -56,7 +58,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public bool IsHttps => WebSession.Request.RequestUri.Scheme == Uri.UriSchemeHttps; public bool IsHttps => WebSession.Request.RequestUri.Scheme == Uri.UriSchemeHttps;
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint; public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary> /// <summary>
...@@ -65,8 +69,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -65,8 +69,14 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public HttpWebClient WebSession { get; set; } public HttpWebClient WebSession { get; set; }
/// <summary>
/// Are we using a custom upstream HTTP proxy?
/// </summary>
public ExternalProxy CustomUpStreamHttpProxyUsed { get; set; } public ExternalProxy CustomUpStreamHttpProxyUsed { get; set; }
/// <summary>
/// Are we using a custom upstream HTTPS proxy?
/// </summary>
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; } public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; }
/// <summary> /// <summary>
...@@ -105,7 +115,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -105,7 +115,7 @@ namespace Titanium.Web.Proxy.EventArguments
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol //For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (WebSession.Request.IsChunked) if (WebSession.Request.IsChunked)
{ {
await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream); await ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream);
} }
else else
{ {
...@@ -113,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -113,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (WebSession.Request.ContentLength > 0) if (WebSession.Request.ContentLength > 0)
{ {
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response //If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream, await ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream,
WebSession.Request.ContentLength); WebSession.Request.ContentLength);
} }
...@@ -431,7 +441,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -431,7 +441,7 @@ namespace Titanium.Web.Proxy.EventArguments
            await GenericResponse(html, null, status);             await GenericResponse(html, null, status);
} }
/// <summary> /// <summary>
/// Before request is made to server  /// Before request is made to server 
/// Respond with the specified HTML string to client /// Respond with the specified HTML string to client
/// and the specified status /// and the specified status
...@@ -485,12 +495,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -485,12 +495,17 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Request.CancelRequest = true; WebSession.Request.CancelRequest = true;
} }
/// <summary>
/// Redirect to URL.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public async Task Redirect(string url) public async Task Redirect(string url)
{ {
var response = new RedirectResponse(); var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.Add("Location", new Models.HttpHeader("Location", url)); response.ResponseHeaders.Add("Location", new HttpHeader("Location", url));
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty); response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
await Respond(response); await Respond(response);
......
using System; 
namespace Titanium.Web.Proxy.Exceptions namespace Titanium.Web.Proxy.Exceptions
{ {
/// <summary> /// <summary>
...@@ -7,6 +6,10 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -7,6 +6,10 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary> /// </summary>
public class BodyNotFoundException : ProxyException public class BodyNotFoundException : ProxyException
{ {
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
public BodyNotFoundException(string message) public BodyNotFoundException(string message)
: base(message) : base(message)
{ {
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
/// <summary>
/// Extension methods for Byte Arrays.
/// </summary>
public static class ByteArrayExtensions public static class ByteArrayExtensions
{ {
/// <summary> /// <summary>
...@@ -14,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -14,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <returns></returns> /// <returns></returns>
public static T[] SubArray<T>(this T[] data, int index, int length) public static T[] SubArray<T>(this T[] data, int index, int length)
{ {
T[] result = new T[length]; var result = new T[length];
Array.Copy(data, index, result, 0, length); Array.Copy(data, index, result, 0, length);
return result; return result;
} }
......
...@@ -12,11 +12,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -12,11 +12,11 @@ namespace Titanium.Web.Proxy.Extensions
internal static bool IsConnected(this Socket client) internal static bool IsConnected(this Socket client)
{ {
// This is how you can determine whether a socket is still connected. // This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking; var blockingState = client.Blocking;
try try
{ {
byte[] tmp = new byte[1]; var tmp = new byte[1];
client.Blocking = false; client.Blocking = false;
client.Send(tmp, 0, 0); client.Send(tmp, 0, 0);
...@@ -25,14 +25,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -25,14 +25,7 @@ namespace Titanium.Web.Proxy.Extensions
catch (SocketException e) catch (SocketException e)
{ {
// 10035 == WSAEWOULDBLOCK // 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035)) return e.NativeErrorCode.Equals(10035);
{
return true;
}
else
{
return false;
}
} }
finally finally
{ {
......
...@@ -4,7 +4,6 @@ using System.IO; ...@@ -4,7 +4,6 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -16,15 +15,15 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,15 +15,15 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
internal class CustomBinaryReader : IDisposable internal class CustomBinaryReader : IDisposable
{ {
private Stream stream; private readonly Stream stream;
private Encoding encoding; private readonly Encoding encoding;
internal CustomBinaryReader(Stream stream) internal CustomBinaryReader(Stream stream)
{ {
this.stream = stream; this.stream = stream;
//default to UTF-8 //default to UTF-8
this.encoding = Encoding.UTF8; encoding = Encoding.UTF8;
} }
internal Stream BaseStream => stream; internal Stream BaseStream => stream;
...@@ -40,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -40,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
var lastChar = default(char); var lastChar = default(char);
var buffer = new byte[1]; var buffer = new byte[1];
while ((await this.stream.ReadAsync(buffer, 0, 1)) > 0) while ((await stream.ReadAsync(buffer, 0, 1)) > 0)
{ {
//if new line //if new line
if (lastChar == '\r' && buffer[0] == '\n') if (lastChar == '\r' && buffer[0] == '\n')
...@@ -82,6 +81,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -82,6 +81,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Read the specified number of raw bytes from the base stream /// Read the specified number of raw bytes from the base stream
/// </summary> /// </summary>
/// <param name="bufferSize"></param>
/// <param name="totalBytesToRead"></param> /// <param name="totalBytesToRead"></param>
/// <returns></returns> /// <returns></returns>
internal async Task<byte[]> ReadBytesAsync(int bufferSize, long totalBytesToRead) internal async Task<byte[]> ReadBytesAsync(int bufferSize, long totalBytesToRead)
...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Helpers
using (var outStream = new MemoryStream()) using (var outStream = new MemoryStream())
{ {
while ((bytesRead += await this.stream.ReadAsync(buffer, 0, bytesToRead)) > 0) while ((bytesRead += await stream.ReadAsync(buffer, 0, bytesToRead)) > 0)
{ {
await outStream.WriteAsync(buffer, 0, bytesRead); await outStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
......
...@@ -8,7 +8,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -8,7 +8,10 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
public class FireFoxProxySettingsManager public class FireFoxProxySettingsManager
{ {
public void AddFirefox() /// <summary>
/// Add Firefox settings.
/// </summary>
public void AddFirefox()
{ {
try try
{ {
...@@ -16,21 +19,17 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,21 +19,17 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default"); "\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js"; var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile)) if (!File.Exists(myFfPrefFile)) return;
{ // We have a pref file so let''s make sure it has the proxy setting
// We have a pref file so let''s make sure it has the proxy setting var myReader = new StreamReader(myFfPrefFile);
var myReader = new StreamReader(myFfPrefFile); var myPrefContents = myReader.ReadToEnd();
var myPrefContents = myReader.ReadToEnd(); myReader.Close();
myReader.Close(); if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);")) return;
if (myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);")) // Add the proxy enable line and write it back to the file
{ myPrefContents = myPrefContents.Replace("user_pref(\"network.proxy.type\", 0);", "");
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace("user_pref(\"network.proxy.type\", 0);", "");
File.Delete(myFfPrefFile); File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents); File.WriteAllText(myFfPrefFile, myPrefContents);
}
}
} }
catch (Exception) catch (Exception)
{ {
...@@ -38,7 +37,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -38,7 +37,10 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
public void RemoveFirefox() /// <summary>
/// Remove firefox settings.
/// </summary>
public void RemoveFirefox()
{ {
try try
{ {
...@@ -46,20 +48,18 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -46,20 +48,18 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default"); "\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js"; var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile)) if (!File.Exists(myFfPrefFile)) return;
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{ {
// We have a pref file so let''s make sure it has the proxy setting // Add the proxy enable line and write it back to the file
var myReader = new StreamReader(myFfPrefFile); myPrefContents = myPrefContents + "\n\r" + "user_pref(\"network.proxy.type\", 0);";
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents + "\n\r" + "user_pref(\"network.proxy.type\", 0);";
File.Delete(myFfPrefFile); File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents); File.WriteAllText(myFfPrefFile, myPrefContents);
}
} }
} }
catch (Exception) catch (Exception)
......
using System; using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -33,28 +29,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -33,28 +29,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Adapated from below link /// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost /// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary> /// </summary>
/// <param name="address></param> /// <param name="address"></param>
/// <returns></returns> /// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address) internal static bool IsLocalIpAddress(IPAddress address)
{ {
try // get local IP addresses
{ var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// get local IP addresses // test if any host IP equals to any local IP or to localhost
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName()); return IPAddress.IsLoopback(address) || localIPs.Contains(address);
// test if any host IP equals to any local IP or to localhost
// is localhost
if (IPAddress.IsLoopback(address)) return true;
// is local address
foreach (IPAddress localIP in localIPs)
{
if (address.Equals(localIP)) return true;
}
}
catch { }
return false;
} }
} }
} }
...@@ -4,11 +4,8 @@ using Microsoft.Win32; ...@@ -4,11 +4,8 @@ using Microsoft.Win32;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Sockets;
/// <summary> // Helper classes for setting system proxy settings
/// Helper classes for setting system proxy settings
/// </summary>
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal enum ProxyProtocolType internal enum ProxyProtocolType
......
...@@ -140,6 +140,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -140,6 +140,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="localCertificateSelectionCallback"></param> /// <param name="localCertificateSelectionCallback"></param>
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <param name="tcpConnectionFactory"></param> /// <param name="tcpConnectionFactory"></param>
/// <param name="upStreamEndPoint"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds, internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds,
string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders, string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -15,17 +13,26 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,17 +13,26 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class HttpWebClient public class HttpWebClient
{ {
/// <summary> /// <summary>
/// Connection to server /// Connection to server
/// </summary> /// </summary>
internal TcpConnection ServerConnection { get; set; } internal TcpConnection ServerConnection { get; set; }
public Guid RequestId { get; private set; } /// <summary>
/// Request ID.
/// </summary>
public Guid RequestId { get; }
/// <summary>
/// Headers passed with Connect.
/// </summary>
public List<HttpHeader> ConnectHeaders { get; set; } public List<HttpHeader> ConnectHeaders { get; set; }
/// <summary>
/// Web Request.
/// </summary>
public Request Request { get; set; } public Request Request { get; set; }
/// <summary>
/// Web Response.
/// </summary>
public Response Response { get; set; } public Response Response { get; set; }
/// <summary> /// <summary>
...@@ -37,15 +44,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -37,15 +44,14 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Is Https? /// Is Https?
/// </summary> /// </summary>
public bool IsHttps => this.Request.RequestUri.Scheme == Uri.UriSchemeHttps; public bool IsHttps => Request.RequestUri.Scheme == Uri.UriSchemeHttps;
internal HttpWebClient() internal HttpWebClient()
{ {
this.RequestId = Guid.NewGuid(); RequestId = Guid.NewGuid();
Request = new Request();
this.Request = new Request(); Response = new Response();
this.Response = new Response();
} }
/// <summary> /// <summary>
...@@ -65,18 +71,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -65,18 +71,18 @@ namespace Titanium.Web.Proxy.Http
/// <returns></returns> /// <returns></returns>
internal async Task SendRequest(bool enable100ContinueBehaviour) internal async Task SendRequest(bool enable100ContinueBehaviour)
{ {
Stream stream = ServerConnection.Stream; var stream = ServerConnection.Stream;
StringBuilder requestLines = new StringBuilder(); var requestLines = new StringBuilder();
//prepare the request & headers //prepare the request & headers
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps == true)) if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps))
{ {
requestLines.AppendLine(string.Join(" ", this.Request.Method, this.Request.RequestUri.AbsoluteUri, $"HTTP/{this.Request.HttpVersion.Major}.{this.Request.HttpVersion.Minor}")); requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.AbsoluteUri, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"));
} }
else else
{ {
requestLines.AppendLine(string.Join(" ", this.Request.Method, this.Request.RequestUri.PathAndQuery, $"HTTP/{this.Request.HttpVersion.Major}.{this.Request.HttpVersion.Minor}")); requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.PathAndQuery, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"));
} }
//Send Authentication to Upstream proxy if needed //Send Authentication to Upstream proxy if needed
...@@ -86,7 +92,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -86,7 +92,7 @@ namespace Titanium.Web.Proxy.Http
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password))); requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password)));
} }
//write request headers //write request headers
foreach (var headerItem in this.Request.RequestHeaders) foreach (var headerItem in Request.RequestHeaders)
{ {
var header = headerItem.Value; var header = headerItem.Value;
if (headerItem.Key != "Proxy-Authorization") if (headerItem.Key != "Proxy-Authorization")
...@@ -96,7 +102,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -96,7 +102,7 @@ namespace Titanium.Web.Proxy.Http
} }
//write non unique request headers //write non unique request headers
foreach (var headerItem in this.Request.NonUniqueRequestHeaders) foreach (var headerItem in Request.NonUniqueRequestHeaders)
{ {
var headers = headerItem.Value; var headers = headerItem.Value;
foreach (var header in headers) foreach (var header in headers)
...@@ -110,15 +116,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -110,15 +116,15 @@ namespace Titanium.Web.Proxy.Http
requestLines.AppendLine(); requestLines.AppendLine();
string request = requestLines.ToString(); var request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request); var requestBytes = Encoding.ASCII.GetBytes(request);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
await stream.FlushAsync(); await stream.FlushAsync();
if (enable100ContinueBehaviour) if (enable100ContinueBehaviour)
{ {
if (this.Request.ExpectContinue) if (Request.ExpectContinue)
{ {
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3);
var responseStatusCode = httpResult[1].Trim(); var responseStatusCode = httpResult[1].Trim();
...@@ -128,13 +134,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -128,13 +134,13 @@ namespace Titanium.Web.Proxy.Http
if (responseStatusCode.Equals("100") if (responseStatusCode.Equals("100")
&& responseStatusDescription.ToLower().Equals("continue")) && responseStatusDescription.ToLower().Equals("continue"))
{ {
this.Request.Is100Continue = true; Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
else if (responseStatusCode.Equals("417") else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed")) && responseStatusDescription.ToLower().Equals("expectation failed"))
{ {
this.Request.ExpectationFailed = true; Request.ExpectationFailed = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
} }
...@@ -148,7 +154,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -148,7 +154,7 @@ namespace Titanium.Web.Proxy.Http
internal async Task ReceiveResponse() internal async Task ReceiveResponse()
{ {
//return if this is already read //return if this is already read
if (this.Response.ResponseStatusCode != null) return; if (Response.ResponseStatusCode != null) return;
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3);
...@@ -161,33 +167,33 @@ namespace Titanium.Web.Proxy.Http ...@@ -161,33 +167,33 @@ namespace Titanium.Web.Proxy.Http
var httpVersion = httpResult[0].Trim().ToLower(); var httpVersion = httpResult[0].Trim().ToLower();
var version = new Version(1, 1); var version = new Version(1, 1);
if (httpVersion == "http/1.0") if (0 == string.CompareOrdinal(httpVersion, "http/1.0"))
{ {
version = new Version(1, 0); version = new Version(1, 0);
} }
this.Response.HttpVersion = version; Response.HttpVersion = version;
this.Response.ResponseStatusCode = httpResult[1].Trim(); Response.ResponseStatusCode = httpResult[1].Trim();
this.Response.ResponseStatusDescription = httpResult[2].Trim(); Response.ResponseStatusDescription = httpResult[2].Trim();
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request //For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (this.Response.ResponseStatusCode.Equals("100") if (Response.ResponseStatusCode.Equals("100")
&& this.Response.ResponseStatusDescription.ToLower().Equals("continue")) && Response.ResponseStatusDescription.ToLower().Equals("continue"))
{ {
//Read the next line after 100-continue //Read the next line after 100-continue
this.Response.Is100Continue = true; Response.Is100Continue = true;
this.Response.ResponseStatusCode = null; Response.ResponseStatusCode = null;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
return; return;
} }
else if (this.Response.ResponseStatusCode.Equals("417") else if (Response.ResponseStatusCode.Equals("417")
&& this.Response.ResponseStatusDescription.ToLower().Equals("expectation failed")) && Response.ResponseStatusDescription.ToLower().Equals("expectation failed"))
{ {
//read next line after expectation failed response //read next line after expectation failed response
this.Response.ExpectationFailed = true; Response.ExpectationFailed = true;
this.Response.ResponseStatusCode = null; Response.ResponseStatusCode = null;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
......
...@@ -34,12 +34,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -34,12 +34,7 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
var hasHeader = RequestHeaders.ContainsKey("host"); var hasHeader = RequestHeaders.ContainsKey("host");
if (hasHeader) return hasHeader ? RequestHeaders["host"].Value : null;
{
return RequestHeaders["host"].Value;
}
return null;
} }
set set
{ {
...@@ -154,11 +149,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -154,11 +149,11 @@ namespace Titanium.Web.Proxy.Http
if (hasHeader) if (hasHeader)
{ {
var header = RequestHeaders["content-type"]; var header = RequestHeaders["content-type"];
header.Value = value.ToString(); header.Value = value;
} }
else else
{ {
RequestHeaders.Add("content-type", new HttpHeader("content-type", value.ToString())); RequestHeaders.Add("content-type", new HttpHeader("content-type", value));
} }
} }
...@@ -219,14 +214,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -219,14 +214,10 @@ namespace Titanium.Web.Proxy.Http
{ {
var hasHeader = RequestHeaders.ContainsKey("expect"); var hasHeader = RequestHeaders.ContainsKey("expect");
if (hasHeader) if (!hasHeader) return false;
{ var header = RequestHeaders["expect"];
var header = RequestHeaders["expect"];
return header.Value.Equals("100-continue");
}
return false; return header.Value.Equals("100-continue");
} }
} }
...@@ -275,13 +266,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -275,13 +266,7 @@ namespace Titanium.Web.Proxy.Http
var header = RequestHeaders["upgrade"]; var header = RequestHeaders["upgrade"];
if (header.Value.ToLower() == "websocket") return header.Value.ToLower() == "websocket";
{
return true;
}
return false;
} }
} }
...@@ -305,6 +290,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -305,6 +290,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public bool ExpectationFailed { get; internal set; } public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Request() public Request()
{ {
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
......
...@@ -12,7 +12,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -12,7 +12,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class Response public class Response
{ {
/// <summary>
/// Response Status Code.
/// </summary>
public string ResponseStatusCode { get; set; } public string ResponseStatusCode { get; set; }
/// <summary>
/// Response Status description.
/// </summary>
public string ResponseStatusDescription { get; set; } public string ResponseStatusDescription { get; set; }
internal Encoding Encoding => this.GetResponseCharacterEncoding(); internal Encoding Encoding => this.GetResponseCharacterEncoding();
...@@ -26,14 +32,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -26,14 +32,10 @@ namespace Titanium.Web.Proxy.Http
{ {
var hasHeader = ResponseHeaders.ContainsKey("content-encoding"); var hasHeader = ResponseHeaders.ContainsKey("content-encoding");
if (hasHeader) if (!hasHeader) return null;
{ var header = ResponseHeaders["content-encoding"];
var header = ResponseHeaders["content-encoding"];
return header.Value.Trim(); return header.Value.Trim();
}
return null;
} }
} }
...@@ -229,10 +231,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -229,10 +231,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public bool ExpectationFailed { get; internal set; } public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Response() public Response()
{ {
this.ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
this.NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
} }
......
...@@ -7,16 +7,25 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -7,16 +7,25 @@ namespace Titanium.Web.Proxy.Http.Responses
    /// </summary>     /// </summary>
    public class GenericResponse : Response     public class GenericResponse : Response
    {     {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="status"></param>
        public GenericResponse(HttpStatusCode status)         public GenericResponse(HttpStatusCode status)
        {         {
            ResponseStatusCode = ((int)status).ToString();             ResponseStatusCode = ((int)status).ToString();
            ResponseStatusDescription = status.ToString();              ResponseStatusDescription = status.ToString(); 
        }         }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="statusDescription"></param>
        public GenericResponse(string statusCode, string statusDescription)         public GenericResponse(string statusCode, string statusDescription)
        {         {
            ResponseStatusCode = statusCode;             ResponseStatusCode = statusCode;
            ResponseStatusDescription = statusCode;             ResponseStatusDescription = statusDescription;
        }         }
    }     }
} }
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
/// </summary> /// </summary>
public sealed class OkResponse : Response public sealed class OkResponse : Response
{ {
/// <summary>
/// Constructor.
/// </summary>
public OkResponse() public OkResponse()
{ {
ResponseStatusCode = "200"; ResponseStatusCode = "200";
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
/// </summary> /// </summary>
public sealed class RedirectResponse : Response public sealed class RedirectResponse : Response
{ {
/// <summary>
/// Constructor.
/// </summary>
public RedirectResponse() public RedirectResponse()
{ {
ResponseStatusCode = "302"; ResponseStatusCode = "302";
......
...@@ -10,20 +10,38 @@ namespace Titanium.Web.Proxy.Models ...@@ -10,20 +10,38 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public abstract class ProxyEndPoint public abstract class ProxyEndPoint
{ {
public ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl) /// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
protected ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
{ {
this.IpAddress = IpAddress; this.IpAddress = IpAddress;
this.Port = Port; this.Port = Port;
this.EnableSsl = EnableSsl; this.EnableSsl = EnableSsl;
} }
/// <summary>
/// Ip Address.
/// </summary>
public IPAddress IpAddress { get; internal set; } public IPAddress IpAddress { get; internal set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; internal set; } public int Port { get; internal set; }
/// <summary>
/// Enable SSL?
/// </summary>
public bool EnableSsl { get; internal set; } public bool EnableSsl { get; internal set; }
public bool IpV6Enabled => IpAddress == IPAddress.IPv6Any /// <summary>
|| IpAddress == IPAddress.IPv6Loopback /// Is IPv6 enabled?
|| IpAddress == IPAddress.IPv6None; /// </summary>
public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any)
|| Equals(IpAddress, IPAddress.IPv6Loopback)
|| Equals(IpAddress, IPAddress.IPv6None);
internal TcpListener listener { get; set; } internal TcpListener listener { get; set; }
} }
...@@ -37,14 +55,26 @@ namespace Titanium.Web.Proxy.Models ...@@ -37,14 +55,26 @@ namespace Titanium.Web.Proxy.Models
internal bool IsSystemHttpProxy { get; set; } internal bool IsSystemHttpProxy { get; set; }
internal bool IsSystemHttpsProxy { get; set; } internal bool IsSystemHttpsProxy { get; set; }
public List<string> ExcludedHttpsHostNameRegex { get; set; } /// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
public List<string> ExcludedHttpsHostNameRegex { get; set; }
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public X509Certificate2 GenericCertificate { get; set; } public X509Certificate2 GenericCertificate { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl) public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl) : base(IpAddress, Port, EnableSsl)
{ {
} }
} }
...@@ -54,13 +84,19 @@ namespace Titanium.Web.Proxy.Models ...@@ -54,13 +84,19 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class TransparentProxyEndPoint : ProxyEndPoint public class TransparentProxyEndPoint : ProxyEndPoint
{ {
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
//This is valid only when UseServerNameIndication is set to false
public string GenericCertificateName { get; set; }
/// <summary>
// public bool UseServerNameIndication { get; set; } /// Name of the Certificate need to be sent (same as the hostname we want to proxy)
/// This is valid only when UseServerNameIndication is set to false
/// </summary>
public string GenericCertificateName { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl) public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl) : base(IpAddress, Port, EnableSsl)
{ {
......
...@@ -13,10 +13,16 @@ namespace Titanium.Web.Proxy.Models ...@@ -13,10 +13,16 @@ namespace Titanium.Web.Proxy.Models
private string userName; private string userName;
private string password; private string password;
/// <summary>
/// Use default windows credentials?
/// </summary>
public bool UseDefaultCredentials { get; set; } public bool UseDefaultCredentials { get; set; }
/// <summary>
/// Username.
/// </summary>
public string UserName { public string UserName {
get { return UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName; } get => UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName;
set set
{ {
userName = value; userName = value;
...@@ -28,9 +34,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -28,9 +34,12 @@ namespace Titanium.Web.Proxy.Models
} }
} }
/// <summary>
/// Password.
/// </summary>
public string Password public string Password
{ {
get { return UseDefaultCredentials ? DefaultCredentials.Value.Password : password; } get => UseDefaultCredentials ? DefaultCredentials.Value.Password : password;
set set
{ {
password = value; password = value;
...@@ -42,7 +51,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -42,7 +51,13 @@ namespace Titanium.Web.Proxy.Models
} }
} }
/// <summary>
/// Host name.
/// </summary>
public string HostName { get; set; } public string HostName { get; set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; set; } public int Port { get; set; }
} }
} }
...@@ -7,6 +7,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -7,6 +7,12 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class HttpHeader public class HttpHeader
{ {
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <exception cref="Exception"></exception>
public HttpHeader(string name, string value) public HttpHeader(string name, string value)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
...@@ -18,7 +24,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -18,7 +24,13 @@ namespace Titanium.Web.Proxy.Models
Value = value.Trim(); Value = value.Trim();
} }
/// <summary>
/// Header Name.
/// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// Header Value.
/// </summary>
public string Value { get; set; } public string Value { get; set; }
/// <summary> /// <summary>
...@@ -27,7 +39,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -27,7 +39,7 @@ namespace Titanium.Web.Proxy.Models
/// <returns></returns> /// <returns></returns>
public override string ToString() public override string ToString()
{ {
return string.Format("{0}: {1}", Name, Value); return $"{Name}: {Value}";
} }
} }
} }
\ No newline at end of file
...@@ -6,187 +6,197 @@ using System.Threading; ...@@ -6,187 +6,197 @@ using System.Threading;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
/// <summary>
/// Certificate Maker - uses MakeCert
/// </summary>
public class CertificateMaker public class CertificateMaker
{ {
private Type typeX500DN; private readonly Type typeX500DN;
private Type typeX509PrivateKey; private readonly Type typeX509PrivateKey;
private Type typeOID; private readonly Type typeOID;
private Type typeOIDS; private readonly Type typeOIDS;
private Type typeKUExt; private readonly Type typeKUExt;
private Type typeEKUExt; private readonly Type typeEKUExt;
private Type typeRequestCert; private readonly Type typeRequestCert;
private Type typeX509Extensions; private readonly Type typeX509Extensions;
private Type typeBasicConstraints; private readonly Type typeBasicConstraints;
private Type typeSignerCertificate; private readonly Type typeSignerCertificate;
private Type typeX509Enrollment; private readonly Type typeX509Enrollment;
private Type typeAlternativeName; //private Type typeAlternativeName;
private Type typeAlternativeNames; //private Type typeAlternativeNames;
private Type typeAlternativeNamesExt; //private Type typeAlternativeNamesExt;
private string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0"; private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
private object _SharedPrivateKey; private object _SharedPrivateKey;
/// <summary>
/// Constructor.
/// </summary>
public CertificateMaker() public CertificateMaker()
{ {
this.typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true); typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true);
this.typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true); typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true);
this.typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true); typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true);
this.typeOIDS = Type.GetTypeFromProgID("X509Enrollment.CObjectIds.1", true); typeOIDS = Type.GetTypeFromProgID("X509Enrollment.CObjectIds.1", true);
this.typeEKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage"); typeEKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage");
this.typeKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage"); typeKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage");
this.typeRequestCert = Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestCertificate"); typeRequestCert = Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestCertificate");
this.typeX509Extensions = Type.GetTypeFromProgID("X509Enrollment.CX509Extensions"); typeX509Extensions = Type.GetTypeFromProgID("X509Enrollment.CX509Extensions");
this.typeBasicConstraints = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionBasicConstraints"); typeBasicConstraints = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionBasicConstraints");
this.typeSignerCertificate = Type.GetTypeFromProgID("X509Enrollment.CSignerCertificate"); typeSignerCertificate = Type.GetTypeFromProgID("X509Enrollment.CSignerCertificate");
this.typeX509Enrollment = Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment"); typeX509Enrollment = Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment");
this.typeAlternativeName = Type.GetTypeFromProgID("X509Enrollment.CAlternativeName"); //this.typeAlternativeName = Type.GetTypeFromProgID("X509Enrollment.CAlternativeName");
this.typeAlternativeNames = Type.GetTypeFromProgID("X509Enrollment.CAlternativeNames"); //this.typeAlternativeNames = Type.GetTypeFromProgID("X509Enrollment.CAlternativeNames");
this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames"); //this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames");
} }
/// <summary>
/// Make certificate.
/// </summary>
/// <param name="sSubjectCN"></param>
/// <param name="isRoot"></param>
/// <param name="signingCert"></param>
/// <returns></returns>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot,X509Certificate2 signingCert=null) public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot,X509Certificate2 signingCert=null)
{ {
return this.MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert); return MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
} }
private X509Certificate2 MakeCertificate(bool IsRoot, string SubjectCN, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate) private X509Certificate2 MakeCertificate(bool IsRoot, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate)
{ {
X509Certificate2 cert;
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.", "oSigningCertificate"); throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(IsRoot));
} }
object x500DN = Activator.CreateInstance(this.typeX500DN); var x500DN = Activator.CreateInstance(typeX500DN);
object[] subject = new object[] { FullSubject, 0 }; var subject = new object[] { FullSubject, 0 };
this.typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN, subject); typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN, subject);
object x500DN2 = Activator.CreateInstance(this.typeX500DN); var x500DN2 = Activator.CreateInstance(typeX500DN);
if (!IsRoot) if (!IsRoot)
{ {
subject[0] = SigningCertificate.Subject; subject[0] = SigningCertificate.Subject;
} }
this.typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN2, subject); typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN2, subject);
object sharedPrivateKey = null; object sharedPrivateKey = null;
if (!IsRoot) if (!IsRoot)
{ {
sharedPrivateKey = this._SharedPrivateKey; sharedPrivateKey = _SharedPrivateKey;
} }
if (sharedPrivateKey == null) if (sharedPrivateKey == null)
{ {
sharedPrivateKey = Activator.CreateInstance(this.typeX509PrivateKey); sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey);
subject = new object[] { this.sProviderName }; subject = new object[] { sProviderName };
this.typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject); typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
subject[0] = 2; subject[0] = 2;
this.typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject); typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
subject = new object[] { (IsRoot ? 2 : 1) }; subject = new object[] { (IsRoot ? 2 : 1) };
this.typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject); typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
if (!IsRoot) if (!IsRoot)
{ {
subject = new object[] { 176 }; subject = new object[] { 176 };
this.typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject); typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
} }
subject[0] = PrivateKeyLength; subject[0] = PrivateKeyLength;
this.typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject); typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
this.typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null); typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null);
if (!IsRoot) if (!IsRoot)
{ {
this._SharedPrivateKey = sharedPrivateKey; _SharedPrivateKey = sharedPrivateKey;
} }
} }
subject = new object[1]; subject = new object[1];
object obj3 = Activator.CreateInstance(this.typeOID); var obj3 = Activator.CreateInstance(typeOID);
subject[0] = "1.3.6.1.5.5.7.3.1"; subject[0] = "1.3.6.1.5.5.7.3.1";
this.typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, obj3, subject); typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, obj3, subject);
object obj4 = Activator.CreateInstance(this.typeOIDS); var obj4 = Activator.CreateInstance(typeOIDS);
subject[0] = obj3; subject[0] = obj3;
this.typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj4, subject); typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj4, subject);
object obj5 = Activator.CreateInstance(this.typeEKUExt); var obj5 = Activator.CreateInstance(typeEKUExt);
subject[0] = obj4; subject[0] = obj4;
this.typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj5, subject); typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj5, subject);
object obj6 = Activator.CreateInstance(this.typeRequestCert); var obj6 = Activator.CreateInstance(typeRequestCert);
subject = new object[] { 1, sharedPrivateKey, string.Empty }; subject = new[] { 1, sharedPrivateKey, string.Empty };
this.typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, obj6, subject); typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, obj6, subject);
subject = new object[] { x500DN }; subject = new[] { x500DN };
this.typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = x500DN; subject[0] = x500DN;
this.typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = ValidFrom; subject[0] = ValidFrom;
this.typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = ValidTo; subject[0] = ValidTo;
this.typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, obj6, subject);
object obj7 = Activator.CreateInstance(this.typeKUExt); var obj7 = Activator.CreateInstance(typeKUExt);
subject[0] = 176; subject[0] = 176;
this.typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj7, subject); typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj7, subject);
object obj8 = this.typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, obj6, null); var obj8 = typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, obj6, null);
subject = new object[1]; subject = new object[1];
if (!IsRoot) if (!IsRoot)
{ {
subject[0] = obj7; subject[0] = obj7;
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
} }
subject[0] = obj5; subject[0] = obj5;
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
if (!IsRoot) if (!IsRoot)
{ {
object obj12 = Activator.CreateInstance(this.typeSignerCertificate); var obj12 = Activator.CreateInstance(typeSignerCertificate);
subject = new object[] { 0, 0, 12, SigningCertificate.Thumbprint }; subject = new object[] { 0, 0, 12, SigningCertificate.Thumbprint };
this.typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, obj12, subject); typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, obj12, subject);
subject = new object[] { obj12 }; subject = new[] { obj12 };
this.typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, obj6, subject);
} }
else else
{ {
object obj13 = Activator.CreateInstance(this.typeBasicConstraints); var obj13 = Activator.CreateInstance(typeBasicConstraints);
subject = new object[] { "true", "0" }; subject = new object[] { "true", "0" };
this.typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj13, subject); typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj13, subject);
subject = new object[] { obj13 }; subject = new[] { obj13 };
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
} }
object obj14 = Activator.CreateInstance(this.typeOID); var obj14 = Activator.CreateInstance(typeOID);
subject = new object[] { 1, 0, 0, HashAlg }; subject = new object[] { 1, 0, 0, HashAlg };
this.typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, obj14, subject); typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, obj14, subject);
subject = new object[] { obj14 }; subject = new[] { obj14 };
this.typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, obj6, subject); typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, obj6, subject);
this.typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, obj6, null); typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, obj6, null);
object obj15 = Activator.CreateInstance(this.typeX509Enrollment); var obj15 = Activator.CreateInstance(typeX509Enrollment);
subject[0] = obj6; subject[0] = obj6;
this.typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, obj15, subject); typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, obj15, subject);
if (IsRoot) if (IsRoot)
{ {
subject[0] = "DO_NOT_TRUST_TitaniumProxy-CE"; subject[0] = "DO_NOT_TRUST_TitaniumProxy-CE";
this.typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, obj15, subject); typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, obj15, subject);
} }
subject[0] = 0; subject[0] = 0;
object obj16 = this.typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, obj15, subject); var obj16 = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, obj15, subject);
subject = new object[] { 2, obj16, 0, string.Empty }; subject = new[] { 2, obj16, 0, string.Empty };
this.typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, obj15, subject); typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, obj15, subject);
subject = new object[] { null, 0, 1 }; subject = new object[] { null, 0, 1 };
string empty = string.Empty;
try try
{ {
empty = (string)this.typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, obj15, subject); var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, obj15, subject);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
catch (Exception exception1) catch (Exception)
{ {
Exception exception = exception1; // ignored
cert = null;
} }
return cert; return null;
} }
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, bool switchToMTAIfNeeded,X509Certificate2 signingCert=null) private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, bool switchToMTAIfNeeded,X509Certificate2 signingCert=null)
...@@ -194,39 +204,26 @@ namespace Titanium.Web.Proxy.Network ...@@ -194,39 +204,26 @@ namespace Titanium.Web.Proxy.Network
X509Certificate2 rCert=null; X509Certificate2 rCert=null;
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
ManualResetEvent manualResetEvent = new ManualResetEvent(false); var manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((object o) => ThreadPool.QueueUserWorkItem(o =>
{ {
rCert = this.MakeCertificateInternal(sSubjectCN, isRoot, false,signingCert); rCert = MakeCertificateInternal(sSubjectCN, isRoot, false,signingCert);
manualResetEvent.Set(); manualResetEvent.Set();
}); });
manualResetEvent.WaitOne(); manualResetEvent.WaitOne();
manualResetEvent.Close(); manualResetEvent.Close();
return rCert; return rCert;
} }
string fullSubject = string.Format("CN={0}{1}", sSubjectCN, "");//Subject var fullSubject = $"CN={sSubjectCN}";//Subject
string HashAlgo = "SHA256"; //Sig Algo var HashAlgo = "SHA256"; //Sig Algo
int GraceDays = -366; //Grace Days var GraceDays = -366; //Grace Days
int ValidDays = 1825; //ValiDays var ValidDays = 1825; //ValiDays
int keyLength = 2048; //KeyLength var keyLength = 2048; //KeyLength
DateTime graceTime = DateTime.Now.AddDays((double)GraceDays); var graceTime = DateTime.Now.AddDays(GraceDays);
DateTime now = DateTime.Now; var now = DateTime.Now;
try rCert = !isRoot ? MakeCertificate(false, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), signingCert) :
{ MakeCertificate(true, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), null);
if (!isRoot)
{
rCert = this.MakeCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), signingCert);
}
else
{
rCert = this.MakeCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), null);
}
}
catch (Exception e)
{
throw e;
}
return rCert; return rCert;
} }
} }
......
...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class CertificateManager : IDisposable internal class CertificateManager : IDisposable
{ {
private CertificateMaker certEngine = null; private readonly CertificateMaker certEngine;
private bool clearCertificates { get; set; } private bool clearCertificates { get; set; }
/// <summary> /// <summary>
...@@ -21,10 +21,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -21,10 +21,10 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
private readonly IDictionary<string, CachedCertificate> certificateCache; private readonly IDictionary<string, CachedCertificate> certificateCache;
private Action<Exception> exceptionFunc; private readonly Action<Exception> exceptionFunc;
internal string Issuer { get; private set; } internal string Issuer { get; }
internal string RootCertificateName { get; private set; } internal string RootCertificateName { get; }
internal X509Certificate2 rootCertificate { get; set; } internal X509Certificate2 rootCertificate { get; set; }
...@@ -35,29 +35,28 @@ namespace Titanium.Web.Proxy.Network ...@@ -35,29 +35,28 @@ namespace Titanium.Web.Proxy.Network
certEngine = new CertificateMaker(); certEngine = new CertificateMaker();
Issuer = issuer; Issuer = issuer;
RootCertificateName = rootCertificateName; RootCertificateName = rootCertificateName;
certificateCache = new ConcurrentDictionary<string, CachedCertificate>(); certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
} }
internal X509Certificate2 GetRootCertificate() internal X509Certificate2 GetRootCertificate()
{ {
var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "rootCert.pfx"); var path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
if (null == path) throw new NullReferenceException();
var fileName = Path.Combine(path, "rootCert.pfx");
if (File.Exists(fileName)) if (!File.Exists(fileName)) return null;
try
{ {
try return new X509Certificate2(fileName, string.Empty, X509KeyStorageFlags.Exportable);
{
return new X509Certificate2(fileName, string.Empty, X509KeyStorageFlags.Exportable); }
catch (Exception e)
} {
catch (Exception e) exceptionFunc(e);
{ return null;
exceptionFunc(e);
return null;
}
} }
return null;
} }
/// <summary> /// <summary>
/// Attempts to create a RootCertificate /// Attempts to create a RootCertificate
...@@ -75,7 +74,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -75,7 +74,7 @@ namespace Titanium.Web.Proxy.Network
{ {
rootCertificate = CreateCertificate(RootCertificateName, true); rootCertificate = CreateCertificate(RootCertificateName, true);
} }
catch(Exception e) catch (Exception e)
{ {
exceptionFunc(e); exceptionFunc(e);
} }
...@@ -83,38 +82,36 @@ namespace Titanium.Web.Proxy.Network ...@@ -83,38 +82,36 @@ namespace Titanium.Web.Proxy.Network
{ {
try try
{ {
var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "rootCert.pfx"); var path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
if (null == path) throw new NullReferenceException();
var fileName = Path.Combine(path, "rootCert.pfx");
File.WriteAllBytes(fileName, rootCertificate.Export(X509ContentType.Pkcs12)); File.WriteAllBytes(fileName, rootCertificate.Export(X509ContentType.Pkcs12));
} }
catch(Exception e) catch (Exception e)
{ {
exceptionFunc(e); exceptionFunc(e);
} }
} }
return rootCertificate != null; return rootCertificate != null;
} }
/// <summary> /// <summary>
/// Create an SSL certificate /// Create an SSL certificate
/// </summary> /// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param> /// <param name="isRootCertificate"></param>
/// <returns></returns> /// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
try
if (certificateCache.ContainsKey(certificateName))
{ {
if (certificateCache.ContainsKey(certificateName)) var cached = certificateCache[certificateName];
{ cached.LastAccess = DateTime.Now;
var cached = certificateCache[certificateName]; return cached.Certificate;
cached.LastAccess = DateTime.Now;
return cached.Certificate;
}
} }
catch
{
}
X509Certificate2 certificate = null; X509Certificate2 certificate = null;
lock (string.Intern(certificateName)) lock (string.Intern(certificateName))
{ {
...@@ -124,7 +121,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -124,7 +121,7 @@ namespace Titanium.Web.Proxy.Network
{ {
certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, rootCertificate); certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, rootCertificate);
} }
catch(Exception e) catch (Exception e)
{ {
exceptionFunc(e); exceptionFunc(e);
} }
...@@ -166,27 +163,20 @@ namespace Titanium.Web.Proxy.Network ...@@ -166,27 +163,20 @@ namespace Titanium.Web.Proxy.Network
clearCertificates = true; clearCertificates = true;
while (clearCertificates) while (clearCertificates)
{ {
var cutOff = DateTime.Now.AddMinutes(-1 * certificateCacheTimeOutMinutes);
try var outdated = certificateCache
{ .Where(x => x.Value.LastAccess < cutOff)
var cutOff = DateTime.Now.AddMinutes(-1 * certificateCacheTimeOutMinutes); .ToList();
var outdated = certificateCache foreach (var cache in outdated)
.Where(x => x.Value.LastAccess < cutOff) certificateCache.Remove(cache.Key);
.ToList();
foreach (var cache in outdated)
certificateCache.Remove(cache.Key);
}
finally
{
}
//after a minute come back to check for outdated certificates in cache //after a minute come back to check for outdated certificates in cache
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
} }
} }
internal bool TrustRootCertificate() internal bool TrustRootCertificate()
{ {
if (rootCertificate == null) if (rootCertificate == null)
...@@ -213,7 +203,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -213,7 +203,7 @@ namespace Titanium.Web.Proxy.Network
} }
return true; return true;
} }
catch catch
{ {
return false; return false;
} }
......
...@@ -47,6 +47,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -47,6 +47,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
LastAccess = DateTime.Now; LastAccess = DateTime.Now;
} }
/// <summary>
/// Dispose.
/// </summary>
public void Dispose() public void Dispose()
{ {
Stream.Close(); Stream.Close();
......
...@@ -19,14 +19,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -19,14 +19,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal class TcpConnectionFactory internal class TcpConnectionFactory
{ {
/// <summary> /// <summary>
/// Creates a TCP connection to server /// Creates a TCP connection to server
/// </summary> /// </summary>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
/// <param name="connectionTimeOutSeconds"></param> /// <param name="connectionTimeOutSeconds"></param>
/// <param name="remoteHostName"></param> /// <param name="remoteHostName"></param>
/// <param name="httpCmd"></param>
/// <param name="httpVersion"></param> /// <param name="httpVersion"></param>
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <param name="remotePort"></param> /// <param name="remotePort"></param>
...@@ -69,7 +67,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -69,7 +67,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null) if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null)
{ {
await writer.WriteLineAsync("Proxy-Connection: keep-alive"); await writer.WriteLineAsync("Proxy-Connection: keep-alive");
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password))); await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
} }
await writer.WriteLineAsync(); await writer.WriteLineAsync();
await writer.FlushAsync(); await writer.FlushAsync();
...@@ -81,7 +79,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -81,7 +79,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!new string[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower()))) if (!new[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower())))
{ {
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
} }
......
...@@ -29,16 +29,16 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -29,16 +29,16 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary> /// <summary>
/// Gets the local end point. /// Gets the local end point.
/// </summary> /// </summary>
public IPEndPoint LocalEndPoint { get; private set; } public IPEndPoint LocalEndPoint { get; }
/// <summary> /// <summary>
/// Gets the remote end point. /// Gets the remote end point.
/// </summary> /// </summary>
public IPEndPoint RemoteEndPoint { get; private set; } public IPEndPoint RemoteEndPoint { get; }
/// <summary> /// <summary>
/// Gets the process identifier. /// Gets the process identifier.
/// </summary> /// </summary>
public int ProcessId { get; private set; } public int ProcessId { get; }
} }
} }
\ No newline at end of file
...@@ -6,7 +6,9 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -6,7 +6,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary> /// <summary>
/// Represents collection of TcpRows /// Represents collection of TcpRows
/// </summary> /// </summary>
/// <seealso cref="System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}" /> /// <seealso>
/// <cref>System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}</cref>
/// </seealso>
internal class TcpTable : IEnumerable<TcpRow> internal class TcpTable : IEnumerable<TcpRow>
{ {
private readonly IEnumerable<TcpRow> tcpRows; private readonly IEnumerable<TcpRow> tcpRows;
......
...@@ -24,58 +24,78 @@ namespace Titanium.Web.Proxy ...@@ -24,58 +24,78 @@ namespace Titanium.Web.Proxy
try try
{ {
if (!httpHeaders.Where(t => t.Name == "Proxy-Authorization").Any()) if (httpHeaders.All(t => t.Name != "Proxy-Authorization"))
{ {
await WriteResponseStatus(new Version(1, 1), "407", await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Required", clientStreamWriter); "Proxy Authentication Required", clientStreamWriter);
var response = new Response(); var response = new Response
response.ResponseHeaders = new Dictionary<string, HttpHeader>(); {
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")); ResponseHeaders = new Dictionary<string, HttpHeader>
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close")); {
{
"Proxy-Authenticate",
new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response); await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
return false; return false;
} }
else var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (null == header) throw new NullReferenceException();
var headerValue = header.Value.Trim();
if (!headerValue.ToLower().StartsWith("basic"))
{ {
var headerValue = httpHeaders.Where(t => t.Name == "Proxy-Authorization").FirstOrDefault().Value.Trim(); //Return not authorized
if (!headerValue.ToLower().StartsWith("basic")) await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response
{ {
//Return not authorized ResponseHeaders = new Dictionary<string, HttpHeader>
await WriteResponseStatus(new Version(1, 1), "407", {
"Proxy Authentication Invalid", clientStreamWriter); {
var response = new Response(); "Proxy-Authenticate",
response.ResponseHeaders = new Dictionary<string, HttpHeader>(); new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")); },
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close")); {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
await WriteResponseHeaders(clientStreamWriter, response); }
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
return false; return false;
} }
headerValue = headerValue.Substring(5).Trim(); headerValue = headerValue.Substring(5).Trim();
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue)); var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue));
if (decoded.Contains(":") == false) if (decoded.Contains(":") == false)
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response
{ {
//Return not authorized ResponseHeaders = new Dictionary<string, HttpHeader>
await WriteResponseStatus(new Version(1, 1), "407", {
"Proxy Authentication Invalid", clientStreamWriter); {
var response = new Response(); "Proxy-Authenticate",
response.ResponseHeaders = new Dictionary<string, HttpHeader>(); new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")); },
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close")); {"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
await WriteResponseHeaders(clientStreamWriter, response); }
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
return false; return false;
}
var username = decoded.Substring(0, decoded.IndexOf(':'));
var password = decoded.Substring(decoded.IndexOf(':') + 1);
return await AuthenticateUserFunc(username, password).ConfigureAwait(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) catch (Exception e)
{ {
...@@ -83,10 +103,14 @@ namespace Titanium.Web.Proxy ...@@ -83,10 +103,14 @@ namespace Titanium.Web.Proxy
//Return not authorized //Return not authorized
await WriteResponseStatus(new Version(1, 1), "407", await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter); "Proxy Authentication Invalid", clientStreamWriter);
var response = new Response(); var response = new Response
response.ResponseHeaders = new Dictionary<string, HttpHeader>(); {
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")); ResponseHeaders = new Dictionary<string, HttpHeader>
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close")); {
{"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response); await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
......
...@@ -47,14 +47,16 @@ namespace Titanium.Web.Proxy ...@@ -47,14 +47,16 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// A object that creates tcp connection to server
/// </summary> /// </summary>
private TcpConnectionFactory tcpConnectionFactory { get; set; } private TcpConnectionFactory tcpConnectionFactory { get; }
/// <summary> /// <summary>
/// Manage system proxy settings /// Manage system proxy settings
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; set; } private SystemProxyManager systemProxySettingsManager { get; }
#if !DEBUG
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; } private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
#endif
/// <summary> /// <summary>
/// Buffer size used throughout this proxy /// Buffer size used throughout this proxy
...@@ -138,14 +140,8 @@ namespace Titanium.Web.Proxy ...@@ -138,14 +140,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public Action<Exception> ExceptionFunc public Action<Exception> ExceptionFunc
{ {
get get => exceptionFunc ?? defaultExceptionFunc.Value;
{ set => exceptionFunc = value;
return exceptionFunc ?? defaultExceptionFunc.Value;
}
set
{
exceptionFunc = value;
}
} }
/// <summary> /// <summary>
...@@ -172,6 +168,7 @@ namespace Titanium.Web.Proxy ...@@ -172,6 +168,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests
/// return the ExternalProxy object with valid credentials /// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{ {
get; get;
...@@ -203,6 +200,11 @@ namespace Titanium.Web.Proxy ...@@ -203,6 +200,11 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public ProxyServer() : this(null, null) { } public ProxyServer() : this(null, null) { }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="rootCertificateName">Name of root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName) public ProxyServer(string rootCertificateName, string rootCertificateIssuerName)
{ {
RootCertificateName = rootCertificateName; RootCertificateName = rootCertificateName;
...@@ -214,8 +216,9 @@ namespace Titanium.Web.Proxy ...@@ -214,8 +216,9 @@ namespace Titanium.Web.Proxy
ProxyEndPoints = new List<ProxyEndPoint>(); ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory(); tcpConnectionFactory = new TcpConnectionFactory();
systemProxySettingsManager = new SystemProxyManager(); systemProxySettingsManager = new SystemProxyManager();
firefoxProxySettingsManager = new FireFoxProxySettingsManager(); #if !DEBUG
new FireFoxProxySettingsManager();
#endif
RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority"; RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority";
RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium"; RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium";
} }
...@@ -277,7 +280,7 @@ namespace Titanium.Web.Proxy ...@@ -277,7 +280,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG #if !DEBUG
firefoxProxySettingsManager.AddFirefox(); firefoxProxySettingsManager.AddFirefox();
#endif #endif
Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTP Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTP Proxy", endPoint.IpAddress, endPoint.Port);
} }
...@@ -313,7 +316,7 @@ namespace Titanium.Web.Proxy ...@@ -313,7 +316,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG #if !DEBUG
firefoxProxySettingsManager.AddFirefox(); firefoxProxySettingsManager.AddFirefox();
#endif #endif
Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", endPoint.IpAddress, endPoint.Port);
} }
/// <summary> /// <summary>
...@@ -461,6 +464,7 @@ namespace Titanium.Web.Proxy ...@@ -461,6 +464,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param> /// <param name="endPoint"></param>
private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
if (endPoint == null) throw new ArgumentNullException(nameof(endPoint));
if (ProxyEndPoints.Contains(endPoint) == false) if (ProxyEndPoints.Contains(endPoint) == false)
{ {
throw new Exception("Cannot set endPoints not added to proxy as system proxy"); throw new Exception("Cannot set endPoints not added to proxy as system proxy");
...@@ -542,6 +546,9 @@ namespace Titanium.Web.Proxy ...@@ -542,6 +546,9 @@ namespace Titanium.Web.Proxy
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint); endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
} }
/// <summary>
/// Dispose Proxy.
/// </summary>
public void Dispose() public void Dispose()
{ {
if (proxyRunning) if (proxyRunning)
...@@ -551,5 +558,45 @@ namespace Titanium.Web.Proxy ...@@ -551,5 +558,45 @@ namespace Titanium.Web.Proxy
certificateCacheManager?.Dispose(); certificateCacheManager?.Dispose();
} }
/// <summary>
/// Invocator for BeforeRequest event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnBeforeRequest(object sender, SessionEventArgs e)
{
BeforeRequest?.Invoke(sender, e);
}
/// <summary>
/// Invocator for BeforeResponse event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
protected virtual void OnBeforeResponse(object sender, SessionEventArgs e)
{
BeforeResponse?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ServerCertificateValidationCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnServerCertificateValidationCallback(object sender, CertificateValidationEventArgs e)
{
ServerCertificateValidationCallback?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ClientCertifcateSelectionCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnClientCertificateSelectionCallback(object sender, CertificateSelectionEventArgs e)
{
ClientCertificateSelectionCallback?.Invoke(sender, e);
}
} }
} }
\ No newline at end of file
...@@ -11,7 +11,6 @@ using Titanium.Web.Proxy.Exceptions; ...@@ -11,7 +11,6 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Security.Cryptography.X509Certificates;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -58,29 +57,21 @@ namespace Titanium.Web.Proxy ...@@ -58,29 +57,21 @@ namespace Titanium.Web.Proxy
//Find the request Verb //Find the request Verb
var httpVerb = httpCmdSplit[0]; var httpVerb = httpCmdSplit[0];
if (httpVerb.ToUpper() == "CONNECT") httpRemoteUri = httpVerb.ToUpper() == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);
{
httpRemoteUri = new Uri("http://" + httpCmdSplit[1]);
}
else
{
httpRemoteUri = new Uri(httpCmdSplit[1]);
}
//parse the HTTP version //parse the HTTP version
Version version = new Version(1, 1); var version = new Version(1, 1);
if (httpCmdSplit.Length == 3) if (httpCmdSplit.Length == 3)
{ {
string httpVersion = httpCmdSplit[2].Trim(); var httpVersion = httpCmdSplit[2].Trim();
if (httpVersion == "http/1.0") if (0 == string.CompareOrdinal(httpVersion, "http/1.0"))
{ {
version = new Version(1, 0); version = new Version(1, 0);
} }
} }
//filter out excluded host names //filter out excluded host names
var excluded = endPoint.ExcludedHttpsHostNameRegex != null ? var excluded = endPoint.ExcludedHttpsHostNameRegex != null && endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));
endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
List<HttpHeader> connectRequestHeaders = null; List<HttpHeader> connectRequestHeaders = null;
...@@ -89,7 +80,7 @@ namespace Titanium.Web.Proxy ...@@ -89,7 +80,7 @@ namespace Titanium.Web.Proxy
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80) if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80)
{ {
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
string tmpLine = null; string tmpLine;
connectRequestHeaders = new List<HttpHeader>(); connectRequestHeaders = new List<HttpHeader>();
while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
{ {
...@@ -114,15 +105,7 @@ namespace Titanium.Web.Proxy ...@@ -114,15 +105,7 @@ namespace Titanium.Web.Proxy
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
X509Certificate2 certificate; var certificate = endPoint.GenericCertificate ?? certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
if (endPoint.GenericCertificate != null)
{
certificate = endPoint.GenericCertificate;
}
else
{
certificate = certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
}
//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, await sslStream.AuthenticateAsServerAsync(certificate, false,
...@@ -136,10 +119,7 @@ namespace Titanium.Web.Proxy ...@@ -136,10 +119,7 @@ namespace Titanium.Web.Proxy
} }
catch catch
{ {
if (sslStream != null) sslStream?.Dispose();
{
sslStream.Dispose();
}
Dispose(clientStream, clientStreamReader, clientStreamWriter, null); Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
return; return;
...@@ -160,8 +140,8 @@ namespace Titanium.Web.Proxy ...@@ -160,8 +140,8 @@ namespace Titanium.Web.Proxy
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, version, null, httpCmd, version, null,
false, SupportedSslProtocols, false, SupportedSslProtocols,
new RemoteCertificateValidationCallback(ValidateServerCertificate), ValidateServerCertificate,
new LocalCertificateSelectionCallback(SelectClientCertificate), SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint); clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, null); Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
...@@ -169,7 +149,7 @@ namespace Titanium.Web.Proxy ...@@ -169,7 +149,7 @@ namespace Titanium.Web.Proxy
} }
//Now create the request //Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders, null, null); httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders);
} }
catch (Exception) catch (Exception)
{ {
...@@ -189,14 +169,13 @@ namespace Titanium.Web.Proxy ...@@ -189,14 +169,13 @@ namespace Titanium.Web.Proxy
CustomBinaryReader clientStreamReader = null; CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null; StreamWriter clientStreamWriter = null;
X509Certificate2 certificate = null;
if (endPoint.EnableSsl) if (endPoint.EnableSsl)
{ {
var sslStream = new SslStream(clientStream, true); var sslStream = new SslStream(clientStream, true);
//implement in future once SNI supported by SSL stream, for now use the same certificate //implement in future once SNI supported by SSL stream, for now use the same certificate
certificate = certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false); var certificate = certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false);
try try
{ {
...@@ -259,8 +238,8 @@ namespace Titanium.Web.Proxy ...@@ -259,8 +238,8 @@ namespace Titanium.Web.Proxy
connection = await tcpConnectionFactory.CreateClient(BUFFER_SIZE, ConnectionTimeOutSeconds, connection = await tcpConnectionFactory.CreateClient(BUFFER_SIZE, ConnectionTimeOutSeconds,
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion,
args.IsHttps, SupportedSslProtocols, args.IsHttps, SupportedSslProtocols,
new RemoteCertificateValidationCallback(ValidateServerCertificate), ValidateServerCertificate,
new LocalCertificateSelectionCallback(SelectClientCertificate), SelectClientCertificate,
customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, args.ProxyClient.ClientStream, UpStreamEndPoint); customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, args.ProxyClient.ClientStream, UpStreamEndPoint);
} }
...@@ -367,9 +346,14 @@ namespace Titanium.Web.Proxy ...@@ -367,9 +346,14 @@ namespace Titanium.Web.Proxy
/// <param name="clientStreamReader"></param> /// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param> /// <param name="clientStreamWriter"></param>
/// <param name="httpsHostName"></param> /// <param name="httpsHostName"></param>
/// <param name="endPoint"></param>
/// <param name="connectHeaders"></param>
/// <param name="customUpStreamHttpProxy"></param>
/// <param name="customUpStreamHttpsProxy"></param>
/// <returns></returns> /// <returns></returns>
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List<HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
ProxyEndPoint endPoint, List<HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
{ {
TcpConnection connection = null; TcpConnection connection = null;
...@@ -383,9 +367,12 @@ namespace Titanium.Web.Proxy ...@@ -383,9 +367,12 @@ namespace Titanium.Web.Proxy
break; break;
} }
var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse); var args =
args.ProxyClient.TcpClient = client; new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse)
args.WebSession.ConnectHeaders = connectHeaders; {
ProxyClient = {TcpClient = client},
WebSession = {ConnectHeaders = connectHeaders}
};
args.WebSession.ProcessId = new Lazy<int>(() => args.WebSession.ProcessId = new Lazy<int>(() =>
{ {
...@@ -409,12 +396,12 @@ namespace Titanium.Web.Proxy ...@@ -409,12 +396,12 @@ namespace Titanium.Web.Proxy
var httpMethod = httpCmdSplit[0]; var httpMethod = httpCmdSplit[0];
//find the request HTTP version //find the request HTTP version
Version httpVersion = new Version(1, 1); var httpVersion = new Version(1, 1);
if (httpCmdSplit.Length == 3) if (httpCmdSplit.Length == 3)
{ {
var httpVersionString = httpCmdSplit[2].ToLower().Trim(); var httpVersionString = httpCmdSplit[2].ToLower().Trim();
if (httpVersionString == "http/1.0") if (0 == string.CompareOrdinal(httpVersionString, "http/1.0"))
{ {
httpVersion = new Version(1, 0); httpVersion = new Version(1, 0);
} }
...@@ -439,10 +426,8 @@ namespace Titanium.Web.Proxy ...@@ -439,10 +426,8 @@ namespace Titanium.Web.Proxy
{ {
var existing = args.WebSession.Request.RequestHeaders[newHeader.Name]; var existing = args.WebSession.Request.RequestHeaders[newHeader.Name];
var nonUniqueHeaders = new List<HttpHeader>(); var nonUniqueHeaders = new List<HttpHeader> {existing, newHeader};
nonUniqueHeaders.Add(existing);
nonUniqueHeaders.Add(newHeader);
args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders); args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders);
args.WebSession.Request.RequestHeaders.Remove(newHeader.Name); args.WebSession.Request.RequestHeaders.Remove(newHeader.Name);
...@@ -455,8 +440,7 @@ namespace Titanium.Web.Proxy ...@@ -455,8 +440,7 @@ namespace Titanium.Web.Proxy
} }
var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1] var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1]
: (string.Concat("https://", args.WebSession.Request.Host == null ? : (string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1])));
httpsHostName : args.WebSession.Request.Host, httpCmdSplit[1])));
args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.RequestUri = httpRemoteUri;
...@@ -479,10 +463,10 @@ namespace Titanium.Web.Proxy ...@@ -479,10 +463,10 @@ namespace Titanium.Web.Proxy
//If user requested interception do it //If user requested interception do it
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
Delegate[] invocationList = BeforeRequest.GetInvocationList(); var invocationList = BeforeRequest.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length]; var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++) for (var i = 0; i < invocationList.Length; i++)
{ {
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args); handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args);
} }
...@@ -495,8 +479,8 @@ namespace Titanium.Web.Proxy ...@@ -495,8 +479,8 @@ namespace Titanium.Web.Proxy
{ {
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), SupportedSslProtocols, ValidateServerCertificate,
new LocalCertificateSelectionCallback(SelectClientCertificate), SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint); clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, args); Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
...@@ -504,7 +488,7 @@ namespace Titanium.Web.Proxy ...@@ -504,7 +488,7 @@ namespace Titanium.Web.Proxy
} }
//construct the web request that we are going to issue on behalf of the client. //construct the web request that we are going to issue on behalf of the client.
await HandleHttpSessionRequestInternal(connection, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false); await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false);
if (args.WebSession.Request.CancelRequest) if (args.WebSession.Request.CancelRequest)
...@@ -531,12 +515,8 @@ namespace Titanium.Web.Proxy ...@@ -531,12 +515,8 @@ namespace Titanium.Web.Proxy
} }
if (connection != null) //dispose
{ connection?.Dispose();
//dispose
connection.Dispose();
}
} }
/// <summary> /// <summary>
...@@ -547,8 +527,9 @@ namespace Titanium.Web.Proxy ...@@ -547,8 +527,9 @@ namespace Titanium.Web.Proxy
/// <returns></returns> /// <returns></returns>
private async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion) private async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion)
{ {
await clientStreamWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2}", httpVersion.Major, httpVersion.Minor, "200 Connection established")); await clientStreamWriter.WriteLineAsync(
await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now)); $"HTTP/{httpVersion.Major}.{httpVersion.Minor} 200 Connection established");
await clientStreamWriter.WriteLineAsync($"Timestamp: {DateTime.Now}");
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
await clientStreamWriter.FlushAsync(); await clientStreamWriter.FlushAsync();
} }
...@@ -570,9 +551,6 @@ namespace Titanium.Web.Proxy ...@@ -570,9 +551,6 @@ namespace Titanium.Web.Proxy
case "accept-encoding": case "accept-encoding":
header.Value = "gzip,deflate"; header.Value = "gzip,deflate";
break; break;
default:
break;
} }
} }
......
...@@ -17,7 +17,11 @@ namespace Titanium.Web.Proxy ...@@ -17,7 +17,11 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
partial class ProxyServer partial class ProxyServer
{ {
//Called asynchronously when a request was successfully and we received the response /// <summary>
/// Called asynchronously when a request was successfully and we received the response
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public async Task HandleHttpSessionResponse(SessionEventArgs args) public async Task HandleHttpSessionResponse(SessionEventArgs args)
{ {
//read response & headers from server //read response & headers from server
...@@ -156,14 +160,14 @@ namespace Titanium.Web.Proxy ...@@ -156,14 +160,14 @@ namespace Titanium.Web.Proxy
private async Task WriteResponseStatus(Version version, string code, string description, private async Task WriteResponseStatus(Version version, string code, string description,
StreamWriter responseWriter) StreamWriter responseWriter)
{ {
await responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description)); await responseWriter.WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}");
} }
/// <summary> /// <summary>
/// Write response headers to client /// Write response headers to client
/// </summary> /// </summary>
/// <param name="responseWriter"></param> /// <param name="responseWriter"></param>
/// <param name="headers"></param> /// <param name="response"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response) private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response)
{ {
...@@ -220,7 +224,6 @@ namespace Titanium.Web.Proxy ...@@ -220,7 +224,6 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Handle dispose of a client/server session /// Handle dispose of a client/server session
/// </summary> /// </summary>
/// <param name="tcpClient"></param>
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param> /// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param> /// <param name="clientStreamWriter"></param>
...@@ -235,21 +238,13 @@ namespace Titanium.Web.Proxy ...@@ -235,21 +238,13 @@ namespace Titanium.Web.Proxy
clientStream.Dispose(); clientStream.Dispose();
} }
if (args != null) args?.Dispose();
{
args.Dispose();
}
if (clientStreamReader != null) clientStreamReader?.Dispose();
{
clientStreamReader.Dispose();
}
if (clientStreamWriter != null) if (clientStreamWriter == null) return;
{ clientStreamWriter.Close();
clientStreamWriter.Close(); clientStreamWriter.Dispose();
clientStreamWriter.Dispose();
}
} }
} }
} }
\ No newline at end of file
using System; using System.Text;
using System.Text;
namespace Titanium.Web.Proxy.Shared namespace Titanium.Web.Proxy.Shared
{ {
......
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