Commit 000f3715 authored by justcoding121's avatar justcoding121

enforce max line length; enforce one line braces

parent f870fad5
...@@ -16,7 +16,8 @@ namespace Titanium.Web.Proxy ...@@ -16,7 +16,8 @@ namespace Titanium.Web.Proxy
/// <param name="chain"></param> /// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param> /// <param name="sslPolicyErrors"></param>
/// <returns></returns> /// <returns></returns>
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{ {
//if user callback is registered then do it //if user callback is registered then do it
if (ServerCertificateValidationCallback != null) if (ServerCertificateValidationCallback != null)
...@@ -52,14 +53,15 @@ namespace Titanium.Web.Proxy ...@@ -52,14 +53,15 @@ namespace Titanium.Web.Proxy
/// <param name="remoteCertificate"></param> /// <param name="remoteCertificate"></param>
/// <param name="acceptableIssuers"></param> /// <param name="acceptableIssuers"></param>
/// <returns></returns> /// <returns></returns>
internal X509Certificate SelectClientCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, internal X509Certificate SelectClientCertificate(object sender, string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers) X509Certificate remoteCertificate, string[] acceptableIssuers)
{ {
X509Certificate clientCertificate = null; X509Certificate clientCertificate = null;
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && localCertificates.Count > 0) if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null &&
localCertificates.Count > 0)
{ {
// Use the first certificate that is from an acceptable issuer.
foreach (var certificate in localCertificates) foreach (var certificate in localCertificates)
{ {
string issuer = certificate.Issuer; string issuer = certificate.Issuer;
......
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
{ {
......
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
{ {
......
using System.IO; using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
{ {
/// <summary> /// <summary>
/// An inteface for http compression /// An inteface for http compression
/// </summary> /// </summary>
interface ICompression internal interface ICompression
{ {
Stream GetStream(Stream stream); Stream GetStream(Stream stream);
} }
......
...@@ -10,7 +10,9 @@ namespace Titanium.Web.Proxy.Decompression ...@@ -10,7 +10,9 @@ namespace Titanium.Web.Proxy.Decompression
{ {
//cache //cache
private static readonly Lazy<IDecompression> gzip = new Lazy<IDecompression>(() => new GZipDecompression()); private static readonly Lazy<IDecompression> gzip = new Lazy<IDecompression>(() => new GZipDecompression());
private static readonly Lazy<IDecompression> deflate = new Lazy<IDecompression>(() => new DeflateDecompression());
private static readonly Lazy<IDecompression> deflate =
new Lazy<IDecompression>(() => new DeflateDecompression());
public static IDecompression Create(string type) public static IDecompression Create(string type)
{ {
......
using StreamExtended.Helpers; using System.IO;
using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression namespace Titanium.Web.Proxy.Decompression
{ {
......
using StreamExtended.Helpers; using System.IO;
using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression namespace Titanium.Web.Proxy.Decompression
{ {
......
using System.IO; using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression namespace Titanium.Web.Proxy.Decompression
{ {
......
...@@ -4,17 +4,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -4,17 +4,17 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
public class DataEventArgs : EventArgs public class DataEventArgs : EventArgs
{ {
public byte[] Buffer { get; }
public int Offset { get; }
public int Count { get; }
internal DataEventArgs(byte[] buffer, int offset, int count) internal DataEventArgs(byte[] buffer, int offset, int count)
{ {
Buffer = buffer; Buffer = buffer;
Offset = offset; Offset = offset;
Count = count; Count = count;
} }
public byte[] Buffer { get; }
public int Offset { get; }
public int Count { get; }
} }
} }
...@@ -9,14 +9,15 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -9,14 +9,15 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
internal class LimitedStream : Stream internal class LimitedStream : Stream
{ {
private readonly CustomBufferedStream baseStream;
private readonly CustomBinaryReader baseReader; private readonly CustomBinaryReader baseReader;
private readonly CustomBufferedStream baseStream;
private readonly bool isChunked; private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail; private bool readChunkTrail;
private long bytesRemaining;
internal LimitedStream(CustomBufferedStream baseStream, CustomBinaryReader baseReader, bool isChunked, long contentLength) internal LimitedStream(CustomBufferedStream baseStream, CustomBinaryReader baseReader, bool isChunked,
long contentLength)
{ {
this.baseStream = baseStream; this.baseStream = baseStream;
this.baseReader = baseReader; this.baseReader = baseReader;
...@@ -28,6 +29,20 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -28,6 +29,20 @@ namespace Titanium.Web.Proxy.EventArguments
: contentLength; : contentLength;
} }
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
private void GetNextChunk() private void GetNextChunk()
{ {
if (readChunkTrail) if (readChunkTrail)
...@@ -42,7 +57,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -42,7 +57,6 @@ namespace Titanium.Web.Proxy.EventArguments
int idx = chunkHead.IndexOf(";"); int idx = chunkHead.IndexOf(";");
if (idx >= 0) if (idx >= 0)
{ {
// remove chunk extension
chunkHead = chunkHead.Substring(0, idx); chunkHead = chunkHead.Substring(0, idx);
} }
...@@ -133,19 +147,5 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -133,19 +147,5 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
} }
} }
...@@ -5,14 +5,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -5,14 +5,14 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
public class MultipartRequestPartSentEventArgs : EventArgs public class MultipartRequestPartSentEventArgs : EventArgs
{ {
public string Boundary { get; }
public HeaderCollection Headers { get; }
public MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers) public MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
{ {
Boundary = boundary; Boundary = boundary;
Headers = headers; Headers = headers;
} }
public string Boundary { get; }
public HeaderCollection Headers { get; }
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
...@@ -10,8 +11,6 @@ using Titanium.Web.Proxy.Helpers; ...@@ -10,8 +11,6 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using System.Threading;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
...@@ -30,6 +29,21 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -30,6 +29,21 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
private bool reRequest; private bool reRequest;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{
}
protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, Request request, ExceptionHandler exceptionFunc)
: base(bufferSize, endPoint, cancellationTokenSource, request, exceptionFunc)
{
}
private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null; private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null;
/// <summary> /// <summary>
...@@ -54,21 +68,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -54,21 +68,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{
}
protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, Request request, ExceptionHandler exceptionFunc)
: base(bufferSize, endPoint, cancellationTokenSource, request, exceptionFunc)
{
}
private CustomBufferedStream GetStream(bool isRequest) private CustomBufferedStream GetStream(bool isRequest)
{ {
return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream; return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream;
......
...@@ -25,6 +25,47 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -25,6 +25,47 @@ namespace Titanium.Web.Proxy.EventArguments
internal CancellationTokenSource cancellationTokenSource; internal CancellationTokenSource cancellationTokenSource;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{
}
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc)
{
BufferSize = bufferSize;
ExceptionFunc = exceptionFunc;
this.cancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() =>
{
if (RunTime.IsWindows)
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
}
//can't access process Id of remote request from remote machine
return -1;
}
throw new PlatformNotSupportedException();
});
}
/// <summary> /// <summary>
/// Holds a reference to client /// Holds a reference to client
/// </summary> /// </summary>
...@@ -57,10 +98,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -57,10 +98,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; } public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
public event EventHandler<DataEventArgs> DataSent;
public event EventHandler<DataEventArgs> DataReceived;
public ProxyEndPoint LocalEndPoint { get; } public ProxyEndPoint LocalEndPoint { get; }
public bool IsTransparent => LocalEndPoint is TransparentProxyEndPoint; public bool IsTransparent => LocalEndPoint is TransparentProxyEndPoint;
...@@ -68,45 +105,22 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -68,45 +105,22 @@ namespace Titanium.Web.Proxy.EventArguments
public Exception Exception { get; internal set; } public Exception Exception { get; internal set; }
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// implement any cleanup here
/// </summary> /// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, public virtual void Dispose()
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{
}
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc)
{ {
this.BufferSize = bufferSize; CustomUpStreamProxyUsed = null;
this.ExceptionFunc = exceptionFunc;
this.cancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() => DataSent = null;
{ DataReceived = null;
if (RunTime.IsWindows) Exception = null;
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id WebSession.FinishSession();
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
} }
//can't access process Id of remote request from remote machine public event EventHandler<DataEventArgs> DataSent;
return -1;
}
throw new PlatformNotSupportedException(); public event EventHandler<DataEventArgs> DataReceived;
});
}
internal void OnDataSent(byte[] buffer, int offset, int count) internal void OnDataSent(byte[] buffer, int offset, int count)
{ {
...@@ -139,18 +153,5 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -139,18 +153,5 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
} }
/// <summary>
/// implement any cleanup here
/// </summary>
public virtual void Dispose()
{
CustomUpStreamProxyUsed = null;
DataSent = null;
DataReceived = null;
Exception = null;
WebSession.FinishSession();
}
} }
} }
using System; namespace Titanium.Web.Proxy.EventArguments
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.EventArguments
{ {
internal enum TransformationMode internal enum TransformationMode
{ {
...@@ -18,6 +12,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -18,6 +12,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Uncompress the body (this also removes the chunked encoding if exists) /// Uncompress the body (this also removes the chunked encoding if exists)
/// </summary> /// </summary>
Uncompress, Uncompress
} }
} }
using System; using System.Threading;
using System.Threading;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -7,6 +6,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -7,6 +6,12 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
public class TunnelConnectSessionEventArgs : SessionEventArgsBase public class TunnelConnectSessionEventArgs : SessionEventArgsBase
{ {
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest,
ExceptionHandler exceptionFunc, CancellationTokenSource cancellationTokenSource)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc)
{
}
public bool DecryptSsl { get; set; } = true; public bool DecryptSsl { get; set; } = true;
/// <summary> /// <summary>
...@@ -15,10 +20,5 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -15,10 +20,5 @@ namespace Titanium.Web.Proxy.EventArguments
public bool DenyConnect { get; set; } public bool DenyConnect { get; set; }
public bool IsHttpsConnect { get; internal set; } public bool IsHttpsConnect { get; internal set; }
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest, ExceptionHandler exceptionFunc, CancellationTokenSource cancellationTokenSource)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc)
{
}
} }
} }
...@@ -14,10 +14,11 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -14,10 +14,11 @@ namespace Titanium.Web.Proxy.Exceptions
/// Instantiate new instance /// Instantiate new instance
/// </summary> /// </summary>
/// <param name="message">Exception message</param> /// <param name="message">Exception message</param>
/// <param name="session">The <see cref="SessionEventArgs"/> instance containing the event data.</param> /// <param name="session">The <see cref="SessionEventArgs" /> instance containing the event data.</param>
/// <param name="innerException">Inner exception associated to upstream proxy authorization</param> /// <param name="innerException">Inner exception associated to upstream proxy authorization</param>
/// <param name="headers">Http's headers associated</param> /// <param name="headers">Http's headers associated</param>
internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException, IEnumerable<HttpHeader> headers) : base(message, innerException) internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException,
IEnumerable<HttpHeader> headers) : base(message, innerException)
{ {
Session = session; Session = session;
Headers = headers; Headers = headers;
......
...@@ -13,8 +13,9 @@ namespace Titanium.Web.Proxy.Exceptions ...@@ -13,8 +13,9 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary> /// </summary>
/// <param name="message">Message for this exception</param> /// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param> /// <param name="innerException">Associated inner exception</param>
/// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs"/> associated to the exception</param> /// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param>
internal ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base(message, innerException) internal ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base(
message, innerException)
{ {
SessionEventArgs = sessionEventArgs; SessionEventArgs = sessionEventArgs;
} }
......
...@@ -3,6 +3,7 @@ using System.IO; ...@@ -3,6 +3,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
using StreamExtended.Helpers; using StreamExtended.Helpers;
...@@ -12,7 +13,6 @@ using Titanium.Web.Proxy.Exceptions; ...@@ -12,7 +13,6 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Threading;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -57,12 +57,13 @@ namespace Titanium.Web.Proxy ...@@ -57,12 +57,13 @@ namespace Titanium.Web.Proxy
{ {
RequestUri = httpRemoteUri, RequestUri = httpRemoteUri,
OriginalUrl = httpUrl, OriginalUrl = httpUrl,
HttpVersion = version, HttpVersion = version
}; };
await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers); await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers);
var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, ExceptionFunc, cancellationTokenSource); var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest,
ExceptionFunc, cancellationTokenSource);
connectArgs.ProxyClient.TcpClient = tcpClient; connectArgs.ProxyClient.TcpClient = tcpClient;
connectArgs.ProxyClient.ClientStream = clientStream; connectArgs.ProxyClient.ClientStream = clientStream;
...@@ -79,7 +80,7 @@ namespace Titanium.Web.Proxy ...@@ -79,7 +80,7 @@ namespace Titanium.Web.Proxy
{ {
HttpVersion = HttpHeader.Version11, HttpVersion = HttpHeader.Version11,
StatusCode = (int)HttpStatusCode.Forbidden, StatusCode = (int)HttpStatusCode.Forbidden,
StatusDescription = "Forbidden", StatusDescription = "Forbidden"
}; };
} }
...@@ -128,7 +129,8 @@ namespace Titanium.Web.Proxy ...@@ -128,7 +129,8 @@ namespace Titanium.Web.Proxy
string certName = HttpHelper.GetWildCardDomainName(connectHostname); string certName = HttpHelper.GetWildCardDomainName(connectHostname);
var certificate = endPoint.GenericCertificate ?? await CertificateManager.CreateCertificateAsync(certName); var certificate = endPoint.GenericCertificate ??
await CertificateManager.CreateCertificateAsync(certName);
//Successfully managed to authenticate the client using the fake certificate //Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false);
...@@ -142,14 +144,14 @@ namespace Titanium.Web.Proxy ...@@ -142,14 +144,14 @@ namespace Titanium.Web.Proxy
} }
catch (Exception e) catch (Exception e)
{ {
ExceptionFunc(new Exception($"Could'nt authenticate client '{connectHostname}' with fake certificate.", e)); ExceptionFunc(new Exception(
$"Could'nt authenticate client '{connectHostname}' with fake certificate.", e));
sslStream?.Dispose(); sslStream?.Dispose();
return; return;
} }
if (await HttpHelper.IsConnectMethod(clientStream) == -1) if (await HttpHelper.IsConnectMethod(clientStream) == -1)
{ {
// It can be for example some Google (Cloude Messaging for Chrome) magic
decryptSsl = false; decryptSsl = false;
} }
} }
...@@ -200,7 +202,8 @@ namespace Titanium.Web.Proxy ...@@ -200,7 +202,8 @@ namespace Titanium.Web.Proxy
} }
//Now create the request //Now create the request
await HandleHttpSessionRequest(endPoint, tcpClient, clientStream, clientStreamReader, clientStreamWriter, cancellationTokenSource, connectHostname, connectRequest); await HandleHttpSessionRequest(endPoint, tcpClient, clientStream, clientStreamReader,
clientStreamWriter, cancellationTokenSource, connectHostname, connectRequest);
} }
catch (ProxyHttpException e) catch (ProxyHttpException e)
{ {
...@@ -228,7 +231,5 @@ namespace Titanium.Web.Proxy ...@@ -228,7 +231,5 @@ namespace Titanium.Web.Proxy
} }
} }
} }
} }
} }
...@@ -6,8 +6,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -6,8 +6,8 @@ namespace Titanium.Web.Proxy.Extensions
{ {
internal static class FuncExtensions internal static class FuncExtensions
{ {
public static async Task InvokeAsync<T>(this AsyncEventHandler<T> callback, object sender, T args,
public static async Task InvokeAsync<T>(this AsyncEventHandler<T> callback, object sender, T args, ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
var invocationList = callback.GetInvocationList(); var invocationList = callback.GetInvocationList();
...@@ -17,7 +17,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -17,7 +17,8 @@ namespace Titanium.Web.Proxy.Extensions
} }
} }
private static async Task InternalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args, ExceptionHandler exceptionFunc) private static async Task InternalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args,
ExceptionHandler exceptionFunc)
{ {
try try
{ {
......
using System; using StreamExtended;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StreamExtended;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
static class SslExtensions internal static class SslExtensions
{ {
public static string GetServerName(this ClientHelloInfo clientHelloInfo) public static string GetServerName(this ClientHelloInfo clientHelloInfo)
{ {
if (clientHelloInfo.Extensions != null && clientHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension)) if (clientHelloInfo.Extensions != null &&
clientHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension))
{ {
return serverNameExtension.Data; return serverNameExtension.Data;
} }
......
...@@ -18,7 +18,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -18,7 +18,8 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="output"></param> /// <param name="output"></param>
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, int bufferSize) internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize)
{ {
return CopyToAsync(input, output, onCopy, bufferSize, CancellationToken.None); return CopyToAsync(input, output, onCopy, bufferSize, CancellationToken.None);
} }
...@@ -31,16 +32,18 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -31,16 +32,18 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, int bufferSize, CancellationToken cancellationToken) internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize, CancellationToken cancellationToken)
{ {
byte[] buffer = BufferPool.GetBuffer(bufferSize); var buffer = BufferPool.GetBuffer(bufferSize);
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
// cancellation is not working on Socket ReadAsync // cancellation is not working on Socket ReadAsync
// https://github.com/dotnet/corefx/issues/15033 // https://github.com/dotnet/corefx/issues/15033
int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).WithCancellation(cancellationToken); int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
.WithCancellation(cancellationToken);
int bytesRead; int bytesRead;
if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested) if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested)
{ {
...@@ -66,7 +69,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -66,7 +69,7 @@ namespace Titanium.Web.Proxy.Extensions
{ {
if (task != await Task.WhenAny(task, tcs.Task)) if (task != await Task.WhenAny(task, tcs.Task))
{ {
return default(T); return default;
} }
} }
......
...@@ -17,7 +17,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -17,7 +17,8 @@ namespace Titanium.Web.Proxy.Extensions
var method = property.GetMethod; var method = property.GetMethod;
if (method != null && method.ReturnType == typeof(bool)) if (method != null && method.ReturnType == typeof(bool))
{ {
socketCleanedUpGetter = (Func<Socket, bool>)Delegate.CreateDelegate(typeof(Func<Socket, bool>), method); socketCleanedUpGetter =
(Func<Socket, bool>)Delegate.CreateDelegate(typeof(Func<Socket, bool>), method);
} }
} }
} }
...@@ -39,7 +40,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -39,7 +40,8 @@ namespace Titanium.Web.Proxy.Extensions
/// <returns>The remote port</returns> /// <returns>The remote port</returns>
internal static int GetRemotePort(this NativeMethods.TcpRow tcpRow) internal static int GetRemotePort(this NativeMethods.TcpRow tcpRow)
{ {
return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16); return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) +
(tcpRow.remotePort4 << 16);
} }
internal static void CloseSocket(this TcpClient tcpClient) internal static void CloseSocket(this TcpClient tcpClient)
......
using StreamExtended.Network; using System;
using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -37,7 +37,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -37,7 +37,6 @@ namespace Titanium.Web.Proxy.Helpers
string value = split[1]; string value = split[1];
if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase)) if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase))
{ {
//todo: what is this?
continue; continue;
} }
...@@ -103,7 +102,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -103,7 +102,7 @@ namespace Titanium.Web.Proxy.Helpers
int idx = hostname.IndexOf(ProxyConstants.DotSplit); int idx = hostname.IndexOf(ProxyConstants.DotSplit);
//issue #352 //issue #352
if(hostname.Substring(0, idx).Contains("-")) if (hostname.Substring(0, idx).Contains("-"))
{ {
return hostname; return hostname;
} }
...@@ -135,14 +134,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -135,14 +134,12 @@ namespace Titanium.Web.Proxy.Helpers
if (b == ' ' && i > 2) if (b == ' ' && i > 2)
{ {
// at least 3 letters and a space
return isConnect ? 1 : 0; return isConnect ? 1 : 0;
} }
char ch = (char)b; char ch = (char)b;
if (!char.IsLetter(ch)) if (!char.IsLetter(ch))
{ {
// non letter or too short
return -1; return -1;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
sealed class HttpRequestWriter : HttpWriter internal sealed class HttpRequestWriter : HttpWriter
{ {
public HttpRequestWriter(Stream stream, int bufferSize) : base(stream, bufferSize) public HttpRequestWriter(Stream stream, int bufferSize) : base(stream, bufferSize)
{ {
......
...@@ -5,7 +5,7 @@ using Titanium.Web.Proxy.Http; ...@@ -5,7 +5,7 @@ using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
sealed class HttpResponseWriter : HttpWriter internal sealed class HttpResponseWriter : HttpWriter
{ {
public HttpResponseWriter(Stream stream, int bufferSize) : base(stream, bufferSize) public HttpResponseWriter(Stream stream, int bufferSize) : base(stream, bufferSize)
{ {
......
...@@ -5,7 +5,6 @@ using System.Text; ...@@ -5,7 +5,6 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -13,14 +12,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -13,14 +12,12 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal class HttpWriter : CustomBinaryWriter internal class HttpWriter : CustomBinaryWriter
{ {
public int BufferSize { get; }
private readonly char[] charBuffer;
private static readonly byte[] newLine = ProxyConstants.NewLine; private static readonly byte[] newLine = ProxyConstants.NewLine;
private static readonly Encoder encoder = Encoding.ASCII.GetEncoder(); private static readonly Encoder encoder = Encoding.ASCII.GetEncoder();
private readonly char[] charBuffer;
internal HttpWriter(Stream stream, int bufferSize) : base(stream) internal HttpWriter(Stream stream, int bufferSize) : base(stream)
{ {
BufferSize = bufferSize; BufferSize = bufferSize;
...@@ -29,6 +26,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -29,6 +26,8 @@ namespace Titanium.Web.Proxy.Helpers
charBuffer = new char[BufferSize - 1]; charBuffer = new char[BufferSize - 1];
} }
public int BufferSize { get; }
public Task WriteLineAsync() public Task WriteLineAsync()
{ {
return WriteAsync(newLine); return WriteAsync(newLine);
...@@ -102,7 +101,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -102,7 +101,6 @@ namespace Titanium.Web.Proxy.Helpers
await WriteLineAsync(); await WriteLineAsync();
if (flush) if (flush)
{ {
// flush the stream
await FlushAsync(); await FlushAsync();
} }
} }
...@@ -112,7 +110,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -112,7 +110,6 @@ namespace Titanium.Web.Proxy.Helpers
await WriteAsync(data, 0, data.Length); await WriteAsync(data, 0, data.Length);
if (flush) if (flush)
{ {
// flush the stream
await FlushAsync(); await FlushAsync();
} }
} }
...@@ -122,7 +119,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -122,7 +119,6 @@ namespace Titanium.Web.Proxy.Helpers
await WriteAsync(data, offset, count); await WriteAsync(data, offset, count);
if (flush) if (flush)
{ {
// flush the stream
await FlushAsync(); await FlushAsync();
} }
} }
...@@ -152,13 +148,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -152,13 +148,12 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="contentLength"></param> /// <param name="contentLength"></param>
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <returns></returns> /// <returns></returns>
internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength, Action<byte[], int, int> onCopy) internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength,
Action<byte[], int, int> onCopy)
{ {
//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 (isChunked) if (isChunked)
{ {
//Need to revist, find any potential bugs
//send the body bytes to server in chunks
return CopyBodyChunkedAsync(streamReader, onCopy); return CopyBodyChunkedAsync(streamReader, onCopy);
} }
...@@ -204,7 +199,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -204,7 +199,6 @@ namespace Titanium.Web.Proxy.Helpers
int idx = chunkHead.IndexOf(";"); int idx = chunkHead.IndexOf(";");
if (idx >= 0) if (idx >= 0)
{ {
// remove chunk extension
chunkHead = chunkHead.Substring(0, idx); chunkHead = chunkHead.Substring(0, idx);
} }
......
...@@ -5,15 +5,16 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -5,15 +5,16 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal partial class NativeMethods internal partial class NativeMethods
{ {
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("wininet.dll")] [DllImport("wininet.dll")]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
int dwBufferLength);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow(); internal static extern IntPtr GetConsoleWindow();
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add); internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
......
...@@ -9,6 +9,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -9,6 +9,13 @@ namespace Titanium.Web.Proxy.Helpers
internal const int AfInet = 2; internal const int AfInet = 2;
internal const int AfInet6 = 23; internal const int AfInet6 = 23;
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa365928.aspx" />
/// </summary>
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static extern uint GetExtendedTcpTable(IntPtr tcpTable, ref int size, bool sort, int ipVersion,
int tableClass, int reserved);
internal enum TcpTableType internal enum TcpTableType
{ {
BasicListener, BasicListener,
...@@ -19,11 +26,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -19,11 +26,11 @@ namespace Titanium.Web.Proxy.Helpers
OwnerPidAll, OwnerPidAll,
OwnerModuleListener, OwnerModuleListener,
OwnerModuleConnections, OwnerModuleConnections,
OwnerModuleAll, OwnerModuleAll
} }
/// <summary> /// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366921.aspx"/> /// <see href="http://msdn2.microsoft.com/en-us/library/aa366921.aspx" />
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct TcpTable internal struct TcpTable
...@@ -33,7 +40,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -33,7 +40,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
/// <summary> /// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/> /// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx" />
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct TcpRow internal struct TcpRow
...@@ -51,11 +58,5 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -51,11 +58,5 @@ namespace Titanium.Web.Proxy.Helpers
public byte remotePort4; public byte remotePort4;
public int owningPid; public int owningPid;
} }
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa365928.aspx"/>
/// </summary>
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static extern uint GetExtendedTcpTable(IntPtr tcpTable, ref int size, bool sort, int ipVersion, int tableClass, int reserved);
} }
} }
...@@ -55,7 +55,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -55,7 +55,9 @@ namespace Titanium.Web.Proxy.Helpers
localhost = Dns.GetHostEntry(Dns.GetHostName()); localhost = Dns.GetHostEntry(Dns.GetHostName());
if (IPAddress.TryParse(hostName, out var ipAddress)) if (IPAddress.TryParse(hostName, out var ipAddress))
{
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress)); isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
}
if (!isLocalhost) if (!isLocalhost)
{ {
......
...@@ -8,25 +8,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -8,25 +8,8 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal class ProxyInfo internal class ProxyInfo
{ {
public bool? AutoDetect { get; } public ProxyInfo(bool? autoDetect, string autoConfigUrl, int? proxyEnable, string proxyServer,
string proxyOverride)
public string AutoConfigUrl { get; }
public int? ProxyEnable { get; }
public string ProxyServer { get; }
public string ProxyOverride { get; }
public bool BypassLoopback { get; }
public bool BypassOnLocal { get; }
public Dictionary<ProxyProtocolType, HttpSystemProxyValue> Proxies { get; }
public string[] BypassList { get; }
public ProxyInfo(bool? autoDetect, string autoConfigUrl, int? proxyEnable, string proxyServer, string proxyOverride)
{ {
AutoDetect = autoDetect; AutoDetect = autoDetect;
AutoConfigUrl = autoConfigUrl; AutoConfigUrl = autoConfigUrl;
...@@ -68,10 +51,29 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -68,10 +51,29 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
public bool? AutoDetect { get; }
public string AutoConfigUrl { get; }
public int? ProxyEnable { get; }
public string ProxyServer { get; }
public string ProxyOverride { get; }
public bool BypassLoopback { get; }
public bool BypassOnLocal { get; }
public Dictionary<ProxyProtocolType, HttpSystemProxyValue> Proxies { get; }
public string[] BypassList { get; }
private static string BypassStringEscape(string rawString) private static string BypassStringEscape(string rawString)
{ {
var match = var match =
new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Match(rawString); new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Match(rawString);
string empty1; string empty1;
string rawString1; string rawString1;
string empty2; string empty2;
...@@ -92,10 +94,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -92,10 +94,14 @@ namespace Titanium.Web.Proxy.Helpers
string str2 = ConvertRegexReservedChars(rawString1); string str2 = ConvertRegexReservedChars(rawString1);
string str3 = ConvertRegexReservedChars(empty2); string str3 = ConvertRegexReservedChars(empty2);
if (str1 == string.Empty) if (str1 == string.Empty)
{
str1 = "(?:.*://)?"; str1 = "(?:.*://)?";
}
if (str3 == string.Empty) if (str3 == string.Empty)
{
str3 = "(?::[0-9]{1,5})?"; str3 = "(?::[0-9]{1,5})?";
}
return "^" + str1 + str2 + str3 + "$"; return "^" + str1 + str2 + str3 + "$";
} }
...@@ -103,15 +109,21 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -103,15 +109,21 @@ namespace Titanium.Web.Proxy.Helpers
private static string ConvertRegexReservedChars(string rawString) private static string ConvertRegexReservedChars(string rawString)
{ {
if (rawString.Length == 0) if (rawString.Length == 0)
{
return rawString; return rawString;
}
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
foreach (char ch in rawString) foreach (char ch in rawString)
{ {
if ("#$()+.?[\\^{|".IndexOf(ch) != -1) if ("#$()+.?[\\^{|".IndexOf(ch) != -1)
{
stringBuilder.Append('\\'); stringBuilder.Append('\\');
}
else if (ch == 42) else if (ch == 42)
{
stringBuilder.Append('.'); stringBuilder.Append('.');
}
stringBuilder.Append(ch); stringBuilder.Append(ch);
} }
...@@ -131,7 +143,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -131,7 +143,8 @@ namespace Titanium.Web.Proxy.Helpers
{ {
protocolType = ProxyProtocolType.Http; protocolType = ProxyProtocolType.Http;
} }
else if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttps, StringComparison.InvariantCultureIgnoreCase)) else if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttps,
StringComparison.InvariantCultureIgnoreCase))
{ {
protocolType = ProxyProtocolType.Https; protocolType = ProxyProtocolType.Https;
} }
...@@ -149,7 +162,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -149,7 +162,9 @@ namespace Titanium.Web.Proxy.Helpers
var result = new List<HttpSystemProxyValue>(); var result = new List<HttpSystemProxyValue>();
if (string.IsNullOrWhiteSpace(proxyServerValues)) if (string.IsNullOrWhiteSpace(proxyServerValues))
{
return result; return result;
}
var proxyValues = proxyServerValues.Split(';'); var proxyValues = proxyServerValues.Split(';');
...@@ -161,8 +176,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -161,8 +176,10 @@ namespace Titanium.Web.Proxy.Helpers
{ {
var parsedValue = ParseProxyValue(proxyServerValues); var parsedValue = ParseProxyValue(proxyServerValues);
if (parsedValue != null) if (parsedValue != null)
{
result.Add(parsedValue); result.Add(parsedValue);
} }
}
return result; return result;
} }
...@@ -189,7 +206,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -189,7 +206,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
HostName = endPointParts[0], HostName = endPointParts[0],
Port = int.Parse(endPointParts[1]), Port = int.Parse(endPointParts[1]),
ProtocolType = protocolType.Value, ProtocolType = protocolType.Value
}; };
} }
} }
......
...@@ -15,11 +15,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -15,11 +15,12 @@ namespace Titanium.Web.Proxy.Helpers
private static readonly Lazy<bool> isRunningOnMono = new Lazy<bool>(() => Type.GetType("Mono.Runtime") != null); private static readonly Lazy<bool> isRunningOnMono = new Lazy<bool>(() => Type.GetType("Mono.Runtime") != null);
#if NETSTANDARD2_0 #if NETSTANDARD2_0
/// <summary> /// <summary>
/// cache for Windows platform check /// cache for Windows platform check
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static readonly Lazy<bool> isRunningOnWindows = new Lazy<bool>(() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); private static readonly Lazy<bool> isRunningOnWindows = new Lazy<bool>(() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
#endif #endif
/// <summary> /// <summary>
......
...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Both HTTP and HTTPS /// Both HTTP and HTTPS
/// </summary> /// </summary>
AllHttp = Http | Https, AllHttp = Http | Https
} }
internal class HttpSystemProxyValue internal class HttpSystemProxyValue
...@@ -133,7 +133,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -133,7 +133,8 @@ namespace Titanium.Web.Proxy.Helpers
reg.DeleteValue(regAutoConfigUrl, false); reg.DeleteValue(regAutoConfigUrl, false);
reg.SetValue(regProxyEnable, 1); reg.SetValue(regProxyEnable, 1);
reg.SetValue(regProxyServer, string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray())); reg.SetValue(regProxyServer,
string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
Refresh(); Refresh();
} }
...@@ -162,7 +163,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -162,7 +163,8 @@ namespace Titanium.Web.Proxy.Helpers
if (existingSystemProxyValues.Count != 0) if (existingSystemProxyValues.Count != 0)
{ {
reg.SetValue(regProxyEnable, 1); reg.SetValue(regProxyEnable, 1);
reg.SetValue(regProxyServer, string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray())); reg.SetValue(regProxyServer,
string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
} }
else else
{ {
...@@ -284,7 +286,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -284,7 +286,8 @@ namespace Titanium.Web.Proxy.Helpers
private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg) private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg)
{ {
var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?, reg.GetValue(regProxyServer) as string, var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?,
reg.GetValue(regProxyServer) as string,
reg.GetValue(regProxyOverride) as string); reg.GetValue(regProxyOverride) as string);
return pi; return pi;
......
...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Helpers
internal enum IpVersion internal enum IpVersion
{ {
Ipv4 = 1, Ipv4 = 1,
Ipv6 = 2, Ipv6 = 2
} }
internal class TcpHelper internal class TcpHelper
...@@ -21,7 +21,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -21,7 +21,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Gets the extended TCP table. /// Gets the extended TCP table.
/// </summary> /// </summary>
/// <returns>Collection of <see cref="TcpRow"/>.</returns> /// <returns>Collection of <see cref="TcpRow" />.</returns>
internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion) internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion)
{ {
var tcpRows = new List<TcpRow>(); var tcpRows = new List<TcpRow>();
...@@ -37,15 +37,18 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -37,15 +37,18 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
tcpTable = Marshal.AllocHGlobal(tcpTableLength); tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, allPid, 0) == 0) if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, allPid,
0) == 0)
{ {
var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable,
typeof(NativeMethods.TcpTable));
var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i) for (int i = 0; i < table.length; ++i)
{ {
tcpRows.Add(new TcpRow((NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)))); tcpRows.Add(new TcpRow(
(NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow))));
rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow))); rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow)));
} }
} }
...@@ -65,7 +68,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -65,7 +68,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Gets the TCP row by local port number. /// Gets the TCP row by local port number.
/// </summary> /// </summary>
/// <returns><see cref="TcpRow"/>.</returns> /// <returns><see cref="TcpRow" />.</returns>
internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort) internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort)
{ {
var tcpTable = IntPtr.Zero; var tcpTable = IntPtr.Zero;
...@@ -79,15 +82,18 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -79,15 +82,18 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
tcpTable = Marshal.AllocHGlobal(tcpTableLength); tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, allPid, 0) == 0) if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, allPid,
0) == 0)
{ {
var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable,
typeof(NativeMethods.TcpTable));
var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i) for (int i = 0; i < table.length; ++i)
{ {
var tcpRow = (NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)); var tcpRow =
(NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow));
if (tcpRow.GetLocalPort() == localPort) if (tcpRow.GetLocalPort() == localPort)
{ {
return new TcpRow(tcpRow); return new TcpRow(tcpRow);
...@@ -110,7 +116,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -110,7 +116,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
/// <summary> /// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix
/// Usefull for websocket requests /// Usefull for websocket requests
/// Asynchronous Programming Model, which does not throw exceptions when the socket is closed /// Asynchronous Programming Model, which does not throw exceptions when the socket is closed
/// </summary> /// </summary>
...@@ -122,18 +129,20 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -122,18 +129,20 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRawApm(Stream clientStream, Stream serverStream, int bufferSize, internal static async Task SendRawApm(Stream clientStream, Stream serverStream, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc, CancellationTokenSource cancellationTokenSource) Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc,
CancellationTokenSource cancellationTokenSource)
{ {
var taskCompletionSource = new TaskCompletionSource<bool>(); var taskCompletionSource = new TaskCompletionSource<bool>();
cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true)); cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));
//Now async relay all server=>client & client=>server data //Now async relay all server=>client & client=>server data
byte[] clientBuffer = BufferPool.GetBuffer(bufferSize); var clientBuffer = BufferPool.GetBuffer(bufferSize);
byte[] serverBuffer = BufferPool.GetBuffer(bufferSize); var serverBuffer = BufferPool.GetBuffer(bufferSize);
try try
{ {
BeginRead(clientStream, serverStream, clientBuffer, cancellationTokenSource, onDataSend, exceptionFunc); BeginRead(clientStream, serverStream, clientBuffer, cancellationTokenSource, onDataSend, exceptionFunc);
BeginRead(serverStream, clientStream, serverBuffer, cancellationTokenSource, onDataReceive, exceptionFunc); BeginRead(serverStream, clientStream, serverBuffer, cancellationTokenSource, onDataReceive,
exceptionFunc);
await taskCompletionSource.Task; await taskCompletionSource.Task;
} }
finally finally
...@@ -143,7 +152,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -143,7 +152,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
private static void BeginRead(Stream inputStream, Stream outputStream, byte[] buffer, CancellationTokenSource cancellationTokenSource, Action<byte[], int, int> onCopy, private static void BeginRead(Stream inputStream, Stream outputStream, byte[] buffer,
CancellationTokenSource cancellationTokenSource, Action<byte[], int, int> onCopy,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
if (cancellationTokenSource.IsCancellationRequested) if (cancellationTokenSource.IsCancellationRequested)
...@@ -182,7 +192,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -182,7 +192,8 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
outputStream.EndWrite(ar2); outputStream.EndWrite(ar2);
BeginRead(inputStream, outputStream, buffer, cancellationTokenSource, onCopy, exceptionFunc); BeginRead(inputStream, outputStream, buffer, cancellationTokenSource, onCopy,
exceptionFunc);
} }
catch (IOException ex) catch (IOException ex)
{ {
...@@ -208,7 +219,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -208,7 +219,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
/// <summary> /// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix
/// Usefull for websocket requests /// Usefull for websocket requests
/// Task-based Asynchronous Pattern /// Task-based Asynchronous Pattern
/// </summary> /// </summary>
...@@ -220,11 +232,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -220,11 +232,14 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
private static async Task SendRawTap(Stream clientStream, Stream serverStream, int bufferSize, private static async Task SendRawTap(Stream clientStream, Stream serverStream, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc, CancellationTokenSource cancellationTokenSource) Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc,
CancellationTokenSource cancellationTokenSource)
{ {
//Now async relay all server=>client & client=>server data //Now async relay all server=>client & client=>server data
var sendRelay = clientStream.CopyToAsync(serverStream, onDataSend, bufferSize, cancellationTokenSource.Token); var sendRelay =
var receiveRelay = serverStream.CopyToAsync(clientStream, onDataReceive, bufferSize, cancellationTokenSource.Token); clientStream.CopyToAsync(serverStream, onDataSend, bufferSize, cancellationTokenSource.Token);
var receiveRelay =
serverStream.CopyToAsync(clientStream, onDataReceive, bufferSize, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay); await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
...@@ -233,7 +248,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -233,7 +248,8 @@ namespace Titanium.Web.Proxy.Helpers
} }
/// <summary> /// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix
/// Usefull for websocket requests /// Usefull for websocket requests
/// </summary> /// </summary>
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
...@@ -244,10 +260,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -244,10 +260,12 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static Task SendRaw(Stream clientStream, Stream serverStream, int bufferSize, internal static Task SendRaw(Stream clientStream, Stream serverStream, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc, CancellationTokenSource cancellationTokenSource) Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, ExceptionHandler exceptionFunc,
CancellationTokenSource cancellationTokenSource)
{ {
// todo: fix APM mode // todo: fix APM mode
return SendRawTap(clientStream, serverStream, bufferSize, onDataSend, onDataReceive, exceptionFunc, cancellationTokenSource); return SendRawTap(clientStream, serverStream, bufferSize, onDataSend, onDataReceive, exceptionFunc,
cancellationTokenSource);
} }
} }
} }
...@@ -4,21 +4,25 @@ using System.Runtime.InteropServices; ...@@ -4,21 +4,25 @@ using System.Runtime.InteropServices;
// Helper classes for setting system proxy settings // Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers.WinHttp namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
internal partial class NativeMethods internal class NativeMethods
{ {
internal static class WinHttp internal static class WinHttp
{ {
[DllImport("winhttp.dll", SetLastError = true)] [DllImport("winhttp.dll", SetLastError = true)]
internal static extern bool WinHttpGetIEProxyConfigForCurrentUser(ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig); internal static extern bool WinHttpGetIEProxyConfigForCurrentUser(
ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern WinHttpHandle WinHttpOpen(string userAgent, AccessType accessType, string proxyName, string proxyBypass, int dwFlags); internal static extern WinHttpHandle WinHttpOpen(string userAgent, AccessType accessType, string proxyName,
string proxyBypass, int dwFlags);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout, int connectTimeout, int sendTimeout, int receiveTimeout); internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout,
int connectTimeout, int sendTimeout, int receiveTimeout);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpGetProxyForUrl(WinHttpHandle session, string url, [In] ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, internal static extern bool WinHttpGetProxyForUrl(WinHttpHandle session, string url,
[In] ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions,
out WINHTTP_PROXY_INFO proxyInfo); out WINHTTP_PROXY_INFO proxyInfo);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
......
...@@ -9,11 +9,11 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -9,11 +9,11 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
} }
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
return NativeMethods.WinHttp.WinHttpCloseHandle(handle); return NativeMethods.WinHttp.WinHttpCloseHandle(handle);
} }
public override bool IsInvalid => handle == IntPtr.Zero;
} }
} }
...@@ -14,6 +14,26 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -14,6 +14,26 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
private bool autoDetectFailed; private bool autoDetectFailed;
private AutoWebProxyState state; private AutoWebProxyState state;
public WinHttpWebProxyFinder()
{
session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);
if (session == null || session.IsInvalid)
{
int lastWin32Error = GetLastWin32Error();
}
else
{
int downloadTimeout = 60 * 1000;
if (NativeMethods.WinHttp.WinHttpSetTimeouts(session, downloadTimeout, downloadTimeout, downloadTimeout,
downloadTimeout))
{
return;
}
int lastWin32Error = GetLastWin32Error();
}
}
public ICredentials Credentials { get; set; } public ICredentials Credentials { get; set; }
public ProxyInfo ProxyInfo { get; internal set; } public ProxyInfo ProxyInfo { get; internal set; }
...@@ -28,27 +48,18 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -28,27 +48,18 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
private WebProxy proxy { get; set; } private WebProxy proxy { get; set; }
public WinHttpWebProxyFinder() public void Dispose()
{
session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);
if (session == null || session.IsInvalid)
{
int lastWin32Error = GetLastWin32Error();
}
else
{ {
int downloadTimeout = 60 * 1000; Dispose(true);
if (NativeMethods.WinHttp.WinHttpSetTimeouts(session, downloadTimeout, downloadTimeout, downloadTimeout, downloadTimeout))
return;
int lastWin32Error = GetLastWin32Error();
}
} }
public bool GetAutoProxies(Uri destination, out IList<string> proxyList) public bool GetAutoProxies(Uri destination, out IList<string> proxyList)
{ {
proxyList = null; proxyList = null;
if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme) if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme)
{
return false; return false;
}
string proxyListString = null; string proxyListString = null;
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed; var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
...@@ -64,11 +75,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -64,11 +75,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
if (AutomaticConfigurationScript != null && IsRecoverableAutoProxyError(errorCode)) if (AutomaticConfigurationScript != null && IsRecoverableAutoProxyError(errorCode))
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, AutomaticConfigurationScript, out proxyListString); {
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, AutomaticConfigurationScript,
out proxyListString);
}
state = GetStateFromErrorCode(errorCode); state = GetStateFromErrorCode(errorCode);
if (state != AutoWebProxyState.Completed) if (state != AutoWebProxyState.Completed)
{
return false; return false;
}
if (!string.IsNullOrEmpty(proxyListString)) if (!string.IsNullOrEmpty(proxyListString))
{ {
...@@ -101,14 +117,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -101,14 +117,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var systemProxy = new ExternalProxy var systemProxy = new ExternalProxy
{ {
HostName = proxyStr, HostName = proxyStr,
Port = port, Port = port
}; };
return systemProxy; return systemProxy;
} }
if (proxy?.IsBypassed(destination) == true) if (proxy?.IsBypassed(destination) == true)
{
return null; return null;
}
var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme); var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme);
if (protocolType.HasValue) if (protocolType.HasValue)
...@@ -119,7 +137,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -119,7 +137,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var systemProxy = new ExternalProxy var systemProxy = new ExternalProxy
{ {
HostName = value.HostName, HostName = value.HostName,
Port = value.Port, Port = value.Port
}; };
return systemProxy; return systemProxy;
...@@ -159,7 +177,9 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -159,7 +177,9 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
else else
{ {
if (Marshal.GetLastWin32Error() == 8) if (Marshal.GetLastWin32Error() == 8)
{
throw new OutOfMemoryException(); throw new OutOfMemoryException();
}
result = new ProxyInfo(true, null, null, null, null); result = new ProxyInfo(true, null, null, null, null);
} }
...@@ -180,15 +200,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -180,15 +200,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoDetectFailed = false; autoDetectFailed = false;
} }
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing) private void Dispose(bool disposing)
{ {
if (!disposing || session == null || session.IsInvalid) if (!disposing || session == null || session.IsInvalid)
{
return; return;
}
session.Close(); session.Close();
} }
...@@ -201,7 +219,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -201,7 +219,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
autoProxyOptions.Flags = NativeMethods.WinHttp.AutoProxyFlags.AutoDetect; autoProxyOptions.Flags = NativeMethods.WinHttp.AutoProxyFlags.AutoDetect;
autoProxyOptions.AutoConfigUrl = null; autoProxyOptions.AutoConfigUrl = null;
autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.Dhcp | NativeMethods.WinHttp.AutoDetectType.DnsA; autoProxyOptions.AutoDetectFlags =
NativeMethods.WinHttp.AutoDetectType.Dhcp | NativeMethods.WinHttp.AutoDetectType.DnsA;
} }
else else
{ {
...@@ -218,14 +237,17 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -218,14 +237,17 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
autoProxyOptions.AutoLogonIfChallenged = true; autoProxyOptions.AutoLogonIfChallenged = true;
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{
num = GetLastWin32Error(); num = GetLastWin32Error();
} }
} }
}
return num; return num;
} }
private bool WinHttpGetProxyForUrl(string destination, ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString) private bool WinHttpGetProxyForUrl(string destination,
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString)
{ {
proxyListString = null; proxyListString = null;
bool flag; bool flag;
...@@ -233,15 +255,19 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -233,15 +255,19 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
RuntimeHelpers.PrepareConstrainedRegions(); RuntimeHelpers.PrepareConstrainedRegions();
try try
{ {
flag = NativeMethods.WinHttp.WinHttpGetProxyForUrl(session, destination, ref autoProxyOptions, out proxyInfo); flag = NativeMethods.WinHttp.WinHttpGetProxyForUrl(session, destination, ref autoProxyOptions,
out proxyInfo);
if (flag) if (flag)
{
proxyListString = Marshal.PtrToStringUni(proxyInfo.Proxy); proxyListString = Marshal.PtrToStringUni(proxyInfo.Proxy);
} }
}
finally finally
{ {
Marshal.FreeHGlobal(proxyInfo.Proxy); Marshal.FreeHGlobal(proxyInfo.Proxy);
Marshal.FreeHGlobal(proxyInfo.ProxyBypass); Marshal.FreeHGlobal(proxyInfo.ProxyBypass);
} }
return flag; return flag;
} }
...@@ -249,7 +275,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -249,7 +275,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{ {
int lastWin32Error = Marshal.GetLastWin32Error(); int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error == 8) if (lastWin32Error == 8)
{
throw new OutOfMemoryException(); throw new OutOfMemoryException();
}
return lastWin32Error; return lastWin32Error;
} }
...@@ -274,7 +303,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -274,7 +303,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
private static AutoWebProxyState GetStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode) private static AutoWebProxyState GetStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
if (errorCode == 0L) if (errorCode == 0L)
{
return AutoWebProxyState.Completed; return AutoWebProxyState.Completed;
}
switch (errorCode) switch (errorCode)
{ {
case NativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript: case NativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript:
...@@ -298,8 +330,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -298,8 +330,10 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
foreach (char c in value) foreach (char c in value)
{ {
if (!char.IsWhiteSpace(c)) if (!char.IsWhiteSpace(c))
{
stringBuilder.Append(c); stringBuilder.Append(c);
} }
}
return stringBuilder.ToString(); return stringBuilder.ToString();
} }
...@@ -325,7 +359,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -325,7 +359,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
DownloadFailure, DownloadFailure,
CompilationFailure, CompilationFailure,
UnrecognizedScheme, UnrecognizedScheme,
Completed, Completed
} }
} }
} }
...@@ -4,11 +4,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -4,11 +4,11 @@ namespace Titanium.Web.Proxy.Http
{ {
public class ConnectRequest : Request public class ConnectRequest : Request
{ {
public ClientHelloInfo ClientHelloInfo { get; set; }
public ConnectRequest() public ConnectRequest()
{ {
Method = "CONNECT"; Method = "CONNECT";
} }
public ClientHelloInfo ClientHelloInfo { get; set; }
} }
} }
using StreamExtended; using System;
using System;
using System.Net; using System.Net;
using StreamExtended;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
......
...@@ -15,6 +15,17 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,6 +15,17 @@ namespace Titanium.Web.Proxy.Http
private readonly Dictionary<string, List<HttpHeader>> nonUniqueHeaders; private readonly Dictionary<string, List<HttpHeader>> nonUniqueHeaders;
/// <summary>
/// Initializes a new instance of the <see cref="HeaderCollection" /> class.
/// </summary>
public HeaderCollection()
{
headers = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
nonUniqueHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
Headers = new ReadOnlyDictionary<string, HttpHeader>(headers);
NonUniqueHeaders = new ReadOnlyDictionary<string, List<HttpHeader>>(nonUniqueHeaders);
}
/// <summary> /// <summary>
/// Unique Request header collection /// Unique Request header collection
/// </summary> /// </summary>
...@@ -26,14 +37,19 @@ namespace Titanium.Web.Proxy.Http ...@@ -26,14 +37,19 @@ namespace Titanium.Web.Proxy.Http
public ReadOnlyDictionary<string, List<HttpHeader>> NonUniqueHeaders { get; } public ReadOnlyDictionary<string, List<HttpHeader>> NonUniqueHeaders { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HeaderCollection"/> class. /// Returns an enumerator that iterates through the collection.
/// </summary> /// </summary>
public HeaderCollection() /// <returns>
/// An enumerator that can be used to iterate through the collection.
/// </returns>
public IEnumerator<HttpHeader> GetEnumerator()
{ {
headers = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); return headers.Values.Concat(nonUniqueHeaders.Values.SelectMany(x => x)).GetEnumerator();
nonUniqueHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); }
Headers = new ReadOnlyDictionary<string, HttpHeader>(headers);
NonUniqueHeaders = new ReadOnlyDictionary<string, List<HttpHeader>>(nonUniqueHeaders); IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
} }
/// <summary> /// <summary>
...@@ -190,7 +206,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -190,7 +206,8 @@ namespace Titanium.Web.Proxy.Http
{ {
if (header.Key != header.Value.Name) if (header.Key != header.Value.Name)
{ {
throw new Exception("Header name mismatch. Key and the name of the HttpHeader object should be the same."); throw new Exception(
"Header name mismatch. Key and the name of the HttpHeader object should be the same.");
} }
AddHeader(header.Value); AddHeader(header.Value);
...@@ -201,8 +218,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -201,8 +218,10 @@ namespace Titanium.Web.Proxy.Http
/// removes all headers with given name /// removes all headers with given name
/// </summary> /// </summary>
/// <param name="headerName"></param> /// <param name="headerName"></param>
/// <returns>True if header was removed /// <returns>
/// False if no header exists with given name</returns> /// True if header was removed
/// False if no header exists with given name
/// </returns>
public bool RemoveHeader(string headerName) public bool RemoveHeader(string headerName)
{ {
bool result = headers.Remove(headerName); bool result = headers.Remove(headerName);
...@@ -286,21 +305,5 @@ namespace Titanium.Web.Proxy.Http ...@@ -286,21 +305,5 @@ namespace Titanium.Web.Proxy.Http
SetOrAddHeaderValue(KnownHeaders.Connection, proxyHeader); SetOrAddHeaderValue(KnownHeaders.Connection, proxyHeader);
} }
} }
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// An enumerator that can be used to iterate through the collection.
/// </returns>
public IEnumerator<HttpHeader> GetEnumerator()
{
return headers.Values.Concat(nonUniqueHeaders.Values.SelectMany(x => x)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
} }
} }
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
......
...@@ -15,6 +15,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,6 +15,15 @@ namespace Titanium.Web.Proxy.Http
{ {
private readonly int bufferSize; private readonly int bufferSize;
internal HttpWebClient(int bufferSize, Request request = null, Response response = null)
{
this.bufferSize = bufferSize;
RequestId = Guid.NewGuid();
Request = request ?? new Request();
Response = response ?? new Response();
}
/// <summary> /// <summary>
/// Connection to server /// Connection to server
/// </summary> /// </summary>
...@@ -56,19 +65,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -56,19 +65,10 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public bool IsHttps => Request.IsHttps; public bool IsHttps => Request.IsHttps;
internal HttpWebClient(int bufferSize, Request request = null, Response response = null)
{
this.bufferSize = bufferSize;
RequestId = Guid.NewGuid();
Request = request ?? new Request();
Response = response ?? new Response();
}
/// <summary> /// <summary>
/// Set the tcp connection to server used by this webclient /// Set the tcp connection to server used by this webclient
/// </summary> /// </summary>
/// <param name="connection">Instance of <see cref="TcpConnection"/></param> /// <param name="connection">Instance of <see cref="TcpConnection" /></param>
internal void SetConnection(TcpConnection connection) internal void SetConnection(TcpConnection connection)
{ {
connection.LastAccess = DateTime.Now; connection.LastAccess = DateTime.Now;
...@@ -100,7 +100,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -100,7 +100,8 @@ namespace Titanium.Web.Proxy.Http
&& upstreamProxy.Password != null) && upstreamProxy.Password != null)
{ {
await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer); await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer);
await HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).WriteToStreamAsync(writer); await HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)
.WriteToStreamAsync(writer);
} }
//write request headers //write request headers
...@@ -120,7 +121,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -120,7 +121,8 @@ namespace Titanium.Web.Proxy.Http
{ {
string httpStatus = await ServerConnection.StreamReader.ReadLineAsync(); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode, out string responseStatusDescription); Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode,
out string responseStatusDescription);
//find if server is willing for expect continue //find if server is willing for expect continue
if (responseStatusCode == (int)HttpStatusCode.Continue if (responseStatusCode == (int)HttpStatusCode.Continue
...@@ -147,7 +149,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -147,7 +149,9 @@ namespace Titanium.Web.Proxy.Http
{ {
//return if this is already read //return if this is already read
if (Response.StatusCode != 0) if (Response.StatusCode != 0)
{
return; return;
}
string httpStatus = await ServerConnection.StreamReader.ReadLineAsync(); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
if (httpStatus == null) if (httpStatus == null)
...@@ -157,7 +161,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -157,7 +161,6 @@ namespace Titanium.Web.Proxy.Http
if (httpStatus == string.Empty) if (httpStatus == string.Empty)
{ {
//Empty content in first-line, try again
httpStatus = await ServerConnection.StreamReader.ReadLineAsync(); httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
} }
......
using System; namespace Titanium.Web.Proxy.Http
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Http
{ {
public static class KnownHeaders public static class KnownHeaders
{ {
......
...@@ -100,36 +100,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -100,36 +100,6 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
internal bool CancelRequest { get; set; } internal bool CancelRequest { get; set; }
internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{
if (BodyInternal != null)
{
return;
}
//GET request don't have a request body to read
if (!HasBody)
{
throw new BodyNotFoundException("Request don't have a body. " + "Please verify that this request is a Http POST/PUT/PATCH and request " +
"content length is greater than zero before accessing the body.");
}
if (!IsBodyRead)
{
if (Locked)
{
throw new Exception("You cannot get the request body after request is made to server.");
}
if (throwWhenNotReadYet)
{
throw new Exception("Request body is not read yet. " +
"Use SessionEventArgs.GetRequestBody() or SessionEventArgs.GetRequestBodyAsString() " +
"method to read the request body.");
}
}
}
/// <summary> /// <summary>
/// Does this request has an upgrade to websocket header? /// Does this request has an upgrade to websocket header?
/// </summary> /// </summary>
...@@ -177,12 +147,44 @@ namespace Titanium.Web.Proxy.Http ...@@ -177,12 +147,44 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{
if (BodyInternal != null)
{
return;
}
//GET request don't have a request body to read
if (!HasBody)
{
throw new BodyNotFoundException("Request don't have a body. " +
"Please verify that this request is a Http POST/PUT/PATCH and request " +
"content length is greater than zero before accessing the body.");
}
if (!IsBodyRead)
{
if (Locked)
{
throw new Exception("You cannot get the request body after request is made to server.");
}
if (throwWhenNotReadYet)
{
throw new Exception("Request body is not read yet. " +
"Use SessionEventArgs.GetRequestBody() or SessionEventArgs.GetRequestBodyAsString() " +
"method to read the request body.");
}
}
}
internal static string CreateRequestLine(string httpMethod, string httpUrl, Version version) internal static string CreateRequestLine(string httpMethod, string httpUrl, Version version)
{ {
return $"{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}"; return $"{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
} }
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl, out Version version) internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl,
out Version version)
{ {
//break up the line into three components (method, remote URL & Http Version) //break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);
...@@ -196,11 +198,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -196,11 +198,6 @@ namespace Titanium.Web.Proxy.Http
httpMethod = httpCmdSplit[0]; httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod)) if (!IsAllUpper(httpMethod))
{ {
//method should be upper cased: https://tools.ietf.org/html/rfc7231#section-4
//todo: create protocol violation message
//fix it
httpMethod = httpMethod.ToUpper(); httpMethod = httpMethod.ToUpper();
} }
......
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -23,6 +21,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -23,6 +21,11 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
private string bodyString; private string bodyString;
/// <summary>
/// Store weather the original request/response has body or not, since the user may change the parameters
/// </summary>
internal bool OriginalHasBody;
/// <summary> /// <summary>
/// Keeps the body data after the session is finished /// Keeps the body data after the session is finished
/// </summary> /// </summary>
...@@ -145,9 +148,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -145,9 +148,21 @@ namespace Titanium.Web.Proxy.Http
public abstract bool HasBody { get; } public abstract bool HasBody { get; }
/// <summary> /// <summary>
/// Store weather the original request/response has body or not, since the user may change the parameters /// Body as string
/// Use the encoding specified to decode the byte[] data to string
/// </summary> /// </summary>
internal bool OriginalHasBody; [Browsable(false)]
public string BodyString => bodyString ?? (bodyString = Encoding.GetString(Body));
/// <summary>
/// Was the body read by user?
/// </summary>
public bool IsBodyRead { get; internal set; }
/// <summary>
/// Is the request/response no more modifyable by user (user callbacks complete?)
/// </summary>
internal bool Locked { get; set; }
internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true); internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true);
...@@ -205,23 +220,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -205,23 +220,6 @@ namespace Titanium.Web.Proxy.Http
return null; return null;
} }
/// <summary>
/// Body as string
/// Use the encoding specified to decode the byte[] data to string
/// </summary>
[Browsable(false)]
public string BodyString => bodyString ?? (bodyString = Encoding.GetString(Body));
/// <summary>
/// Was the body read by user?
/// </summary>
public bool IsBodyRead { get; internal set; }
/// <summary>
/// Is the request/response no more modifyable by user (user callbacks complete?)
/// </summary>
internal bool Locked { get; set; }
internal void UpdateContentLength() internal void UpdateContentLength()
{ {
ContentLength = IsChunked ? -1 : BodyInternal?.Length ?? 0; ContentLength = IsChunked ? -1 : BodyInternal?.Length ?? 0;
......
...@@ -13,6 +13,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -13,6 +13,21 @@ namespace Titanium.Web.Proxy.Http
[TypeConverter(typeof(ExpandableObjectConverter))] [TypeConverter(typeof(ExpandableObjectConverter))]
public class Response : RequestResponseBase public class Response : RequestResponseBase
{ {
/// <summary>
/// Constructor.
/// </summary>
public Response()
{
}
/// <summary>
/// Constructor.
/// </summary>
public Response(byte[] body)
{
Body = body;
}
/// <summary> /// <summary>
/// Response Status Code. /// Response Status Code.
/// </summary> /// </summary>
...@@ -79,21 +94,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -79,21 +94,6 @@ namespace Titanium.Web.Proxy.Http
internal bool TerminateResponse { get; set; } internal bool TerminateResponse { get; set; }
internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{
if (BodyInternal != null)
{
return;
}
if (!IsBodyRead && throwWhenNotReadYet)
{
throw new Exception("Response body is not read yet. " +
"Use SessionEventArgs.GetResponseBody() or SessionEventArgs.GetResponseBodyAsString() " +
"method to read the response body.");
}
}
/// <summary> /// <summary>
/// Is response 100-continue /// Is response 100-continue
/// </summary> /// </summary>
...@@ -128,19 +128,19 @@ namespace Titanium.Web.Proxy.Http ...@@ -128,19 +128,19 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary> internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
/// Constructor. {
/// </summary> if (BodyInternal != null)
public Response()
{ {
return;
} }
/// <summary> if (!IsBodyRead && throwWhenNotReadYet)
/// Constructor.
/// </summary>
public Response(byte[] body)
{ {
Body = body; throw new Exception("Response body is not read yet. " +
"Use SessionEventArgs.GetResponseBody() or SessionEventArgs.GetResponseBodyAsString() " +
"method to read the response body.");
}
} }
internal static string CreateResponseLine(Version version, int statusCode, string statusDescription) internal static string CreateResponseLine(Version version, int statusCode, string statusDescription)
...@@ -148,7 +148,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -148,7 +148,8 @@ namespace Titanium.Web.Proxy.Http
return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}"; return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
} }
internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode, out string statusDescription) internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode,
out string statusDescription)
{ {
var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3); var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3);
if (httpResult.Length != 3) if (httpResult.Length != 3)
......
...@@ -12,6 +12,17 @@ namespace Titanium.Web.Proxy.Models ...@@ -12,6 +12,17 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class ExplicitProxyEndPoint : ProxyEndPoint public class ExplicitProxyEndPoint : ProxyEndPoint
{ {
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="decryptSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port,
decryptSsl)
{
}
internal bool IsSystemHttpProxy { get; set; } internal bool IsSystemHttpProxy { get; set; }
internal bool IsSystemHttpsProxy { get; set; } internal bool IsSystemHttpsProxy { get; set; }
...@@ -24,7 +35,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -24,7 +35,8 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Intercept tunnel connect request /// Intercept tunnel connect request
/// Valid only for explicit endpoints /// Valid only for explicit endpoints
/// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl"/> property to false if this HTTP connect request should'nt be decrypted and instead be relayed /// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl" /> property to false if this HTTP connect request
/// should'nt be decrypted and instead be relayed
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest; public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest;
...@@ -34,17 +46,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -34,17 +46,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectResponse; public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectResponse;
/// <summary> internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer,
/// Constructor. TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="decryptSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port, decryptSsl)
{
}
internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
{ {
if (BeforeTunnelConnectRequest != null) if (BeforeTunnelConnectRequest != null)
{ {
...@@ -52,7 +55,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -52,7 +55,8 @@ namespace Titanium.Web.Proxy.Models
} }
} }
internal async Task InvokeBeforeTunnectConnectResponse(ProxyServer proxyServer, TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc, bool isClientHello = false) internal async Task InvokeBeforeTunnectConnectResponse(ProxyServer proxyServer,
TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc, bool isClientHello = false)
{ {
if (BeforeTunnelConnectResponse != null) if (BeforeTunnelConnectResponse != null)
{ {
......
...@@ -8,11 +8,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -8,11 +8,13 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class ExternalProxy public class ExternalProxy
{ {
private static readonly Lazy<NetworkCredential> defaultCredentials = new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials); private static readonly Lazy<NetworkCredential> defaultCredentials =
new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string userName;
private string password; private string password;
private string userName;
/// <summary> /// <summary>
/// Use default windows credentials? /// Use default windows credentials?
/// </summary> /// </summary>
......
using System.Collections.Generic; using System.Net;
using System.Linq;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text.RegularExpressions;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
......
...@@ -11,6 +11,18 @@ namespace Titanium.Web.Proxy.Models ...@@ -11,6 +11,18 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class TransparentProxyEndPoint : ProxyEndPoint public class TransparentProxyEndPoint : ProxyEndPoint
{ {
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="decryptSsl"></param>
public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port,
decryptSsl)
{
GenericCertificateName = "localhost";
}
/// <summary> /// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy) /// 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 /// This is valid only when UseServerNameIndication is set to false
...@@ -22,18 +34,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -22,18 +34,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public event AsyncEventHandler<BeforeSslAuthenticateEventArgs> BeforeSslAuthenticate; public event AsyncEventHandler<BeforeSslAuthenticateEventArgs> BeforeSslAuthenticate;
/// <summary> internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer,
/// Constructor. BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="decryptSsl"></param>
public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true) : base(ipAddress, port, decryptSsl)
{
GenericCertificateName = "localhost";
}
internal async Task InvokeBeforeSslAuthenticate(ProxyServer proxyServer, BeforeSslAuthenticateEventArgs connectArgs, ExceptionHandler exceptionFunc)
{ {
if (BeforeSslAuthenticate != null) if (BeforeSslAuthenticate != null)
{ {
......
...@@ -8,6 +8,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -8,6 +8,11 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class CachedCertificate internal class CachedCertificate
{ {
internal CachedCertificate()
{
LastAccess = DateTime.Now;
}
internal X509Certificate2 Certificate { get; set; } internal X509Certificate2 Certificate { get; set; }
/// <summary> /// <summary>
...@@ -15,10 +20,5 @@ namespace Titanium.Web.Proxy.Network ...@@ -15,10 +20,5 @@ namespace Titanium.Web.Proxy.Network
/// Usefull in determining its cache lifetime /// Usefull in determining its cache lifetime
/// </summary> /// </summary>
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
internal CachedCertificate()
{
LastAccess = DateTime.Now;
}
} }
} }
...@@ -17,6 +17,7 @@ using Org.BouncyCastle.Security; ...@@ -17,6 +17,7 @@ using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509; using Org.BouncyCastle.X509;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
namespace Titanium.Web.Proxy.Network.Certificate namespace Titanium.Web.Proxy.Network.Certificate
{ {
...@@ -79,7 +80,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -79,7 +80,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
var certificateGenerator = new X509V3CertificateGenerator(); var certificateGenerator = new X509V3CertificateGenerator();
// Serial Number // Serial Number
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom); var serialNumber =
BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom);
certificateGenerator.SetSerialNumber(serialNumber); certificateGenerator.SetSerialNumber(serialNumber);
// Issuer and Subject Name // Issuer and Subject Name
...@@ -94,10 +96,11 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -94,10 +96,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (hostName != null) if (hostName != null)
{ {
//add subject alternative names //add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(GeneralName.DnsName, hostName), }; var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(GeneralName.DnsName, hostName) };
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension); certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false,
subjectAlternativeNamesExtension);
} }
// Subject Public Key // Subject Public Key
...@@ -109,13 +112,15 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -109,13 +112,15 @@ namespace Titanium.Web.Proxy.Network.Certificate
certificateGenerator.SetPublicKey(subjectKeyPair.Public); certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Set certificate intended purposes to only Server Authentication // Set certificate intended purposes to only Server Authentication
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth)); certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false,
new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
if (issuerPrivateKey == null) if (issuerPrivateKey == null)
{ {
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true)); certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true));
} }
var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, secureRandom); var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm,
issuerPrivateKey ?? subjectKeyPair.Private, secureRandom);
// Self-sign the certificate // Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory); var certificate = certificateGenerator.Generate(signatureFactory);
...@@ -131,7 +136,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -131,7 +136,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
var rsa = RsaPrivateKeyStructure.GetInstance(seq); var rsa = RsaPrivateKeyStructure.GetInstance(seq);
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent,
rsa.Prime1, rsa.Prime2, rsa.Exponent1,
rsa.Exponent2, rsa.Coefficient); rsa.Exponent2, rsa.Coefficient);
#if NET45 #if NET45
...@@ -158,7 +164,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -158,7 +164,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
return x509Certificate; return x509Certificate;
} }
private static X509Certificate2 WithPrivateKey(Org.BouncyCastle.X509.X509Certificate certificate, AsymmetricKeyParameter privateKey) private static X509Certificate2 WithPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{ {
const string password = "password"; const string password = "password";
var store = new Pkcs12Store(); var store = new Pkcs12Store();
...@@ -184,25 +190,29 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -184,25 +190,29 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="validTo">The valid to.</param> /// <param name="validTo">The valid to.</param>
/// <param name="signingCertificate">The signing certificate.</param> /// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception> /// <exception cref="System.ArgumentException">
/// You must specify a Signing Certificate if and only if you are not creating a
/// root.
/// </exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot, private X509Certificate2 MakeCertificateInternal(bool isRoot,
string hostName, string subjectName, string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate) DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
if (isRoot != (null == signingCertificate)) if (isRoot != (null == signingCertificate))
{ {
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate)); throw new ArgumentException(
"You must specify a Signing Certificate if and only if you are not creating a root.",
nameof(signingCertificate));
} }
if (isRoot) if (isRoot)
{ {
return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo); return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo);
} }
else
{
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey); var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, issuerPrivateKey: kp.Private); return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo,
} issuerPrivateKey: kp.Private);
} }
/// <summary> /// <summary>
...@@ -216,7 +226,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -216,7 +226,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns>X509Certificate2.</returns> /// <returns>X509Certificate2.</returns>
private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot, private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default)
{ {
#if NET45 #if NET45
if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
...@@ -248,7 +258,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -248,7 +258,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
#endif #endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert); return MakeCertificateInternal(isRoot, subject, $"CN={subject}",
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
isRoot ? null : signingCert);
} }
} }
} }
...@@ -11,40 +11,39 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -11,40 +11,39 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// </summary> /// </summary>
internal class WinCertificateMaker : ICertificateMaker internal class WinCertificateMaker : ICertificateMaker
{ {
private readonly Type typeX500DN; private readonly ExceptionHandler exceptionFunc;
private readonly Type typeX509PrivateKey; private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
private readonly Type typeOID; private readonly Type typeAltNamesCollection;
private readonly Type typeOIDS; private readonly Type typeBasicConstraints;
private readonly Type typeKUExt; private readonly Type typeCAlternativeName;
private readonly Type typeEKUExt; private readonly Type typeEKUExt;
private readonly Type typeRequestCert; private readonly Type typeExtNames;
private readonly Type typeX509Extensions; private readonly Type typeKUExt;
private readonly Type typeBasicConstraints; private readonly Type typeOID;
private readonly Type typeSignerCertificate; private readonly Type typeOIDS;
private readonly Type typeX509Enrollment; private readonly Type typeRequestCert;
private readonly Type typeAltNamesCollection; private readonly Type typeSignerCertificate;
private readonly Type typeX500DN;
private readonly Type typeExtNames; private readonly Type typeX509Enrollment;
private readonly Type typeCAlternativeName; private readonly Type typeX509Extensions;
private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0"; private readonly Type typeX509PrivateKey;
private object sharedPrivateKey; private object sharedPrivateKey;
private readonly ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
...@@ -88,7 +87,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -88,7 +87,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
if (isRoot != (null == signingCertificate)) if (isRoot != (null == signingCertificate))
{ {
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(isRoot)); throw new ArgumentException(
"You must specify a Signing Certificate if and only if you are not creating a root.",
nameof(isRoot));
} }
var x500CertDN = Activator.CreateInstance(typeX500DN); var x500CertDN = Activator.CreateInstance(typeX500DN);
...@@ -114,20 +115,25 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -114,20 +115,25 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey); sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey);
typeValue = new object[] { sProviderName }; typeValue = new object[] { sProviderName };
typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue);
typeValue[0] = 2; typeValue[0] = 2;
typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue);
typeValue = new object[] { isRoot ? 2 : 1 }; typeValue = new object[] { isRoot ? 2 : 1 };
typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue);
if (!isRoot) if (!isRoot)
{ {
typeValue = new object[] { 176 }; typeValue = new object[] { 176 };
typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue);
} }
typeValue[0] = privateKeyLength; typeValue[0] = privateKeyLength;
typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey,
typeValue);
typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null); typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null);
if (!isRoot) if (!isRoot)
...@@ -153,7 +159,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -153,7 +159,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
var requestCert = Activator.CreateInstance(typeRequestCert); var requestCert = Activator.CreateInstance(typeRequestCert);
typeValue = new[] { 1, sharedPrivateKey, string.Empty }; typeValue = new[] { 1, sharedPrivateKey, string.Empty };
typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue); typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert,
typeValue);
typeValue = new[] { x500CertDN }; typeValue = new[] { x500CertDN };
typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = x500RootCertDN; typeValue[0] = x500RootCertDN;
...@@ -168,7 +175,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -168,7 +175,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue[0] = 176; typeValue[0] = 176;
typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, kuExt, typeValue); typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, kuExt, typeValue);
var certificate = typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, requestCert, null); var certificate =
typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, requestCert, null);
typeValue = new object[1]; typeValue = new object[1];
if (!isRoot) if (!isRoot)
...@@ -190,10 +198,12 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -190,10 +198,12 @@ namespace Titanium.Web.Proxy.Network.Certificate
var altDnsNames = Activator.CreateInstance(typeCAlternativeName); var altDnsNames = Activator.CreateInstance(typeCAlternativeName);
typeValue = new object[] { 3, subject }; typeValue = new object[] { 3, subject };
typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames, typeValue); typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames,
typeValue);
typeValue = new[] { altDnsNames }; typeValue = new[] { altDnsNames };
typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection, typeValue); typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection,
typeValue);
typeValue = new[] { altNameCollection }; typeValue = new[] { altNameCollection };
...@@ -208,16 +218,19 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -208,16 +218,19 @@ namespace Titanium.Web.Proxy.Network.Certificate
var signerCertificate = Activator.CreateInstance(typeSignerCertificate); var signerCertificate = Activator.CreateInstance(typeSignerCertificate);
typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint }; typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint };
typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue); typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate,
typeValue);
typeValue = new[] { signerCertificate }; typeValue = new[] { signerCertificate };
typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert,
typeValue);
} }
else else
{ {
var basicConstraints = Activator.CreateInstance(typeBasicConstraints); var basicConstraints = Activator.CreateInstance(typeBasicConstraints);
typeValue = new object[] { "true", "0" }; typeValue = new object[] { "true", "0" };
typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints, typeValue); typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints,
typeValue);
typeValue = new[] { basicConstraints }; typeValue = new[] { basicConstraints };
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
} }
...@@ -234,29 +247,34 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -234,29 +247,34 @@ namespace Titanium.Web.Proxy.Network.Certificate
var x509Enrollment = Activator.CreateInstance(typeX509Enrollment); var x509Enrollment = Activator.CreateInstance(typeX509Enrollment);
typeValue[0] = requestCert; typeValue[0] = requestCert;
typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, x509Enrollment,
typeValue);
if (isRoot) if (isRoot)
{ {
typeValue[0] = fullSubject; typeValue[0] = fullSubject;
typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, x509Enrollment, typeValue); typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null,
x509Enrollment, typeValue);
} }
typeValue[0] = 0; typeValue[0] = 0;
var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null,
x509Enrollment, typeValue);
typeValue = new[] { 2, createCertRequest, 0, string.Empty }; typeValue = new[] { 2, createCertRequest, 0, string.Empty };
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment,
typeValue);
typeValue = new object[] { null, 0, 1 }; typeValue = new object[] { null, 0, 1 };
string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null,
x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null, bool switchToMTAIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default)
{ {
X509Certificate2 certificate = null; X509Certificate2 certificate = null;
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
...@@ -299,7 +317,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -299,7 +317,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
var graceTime = DateTime.Now.AddDays(GraceDays); var graceTime = DateTime.Now.AddDays(GraceDays);
var now = DateTime.Now; var now = DateTime.Now;
certificate = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), isRoot ? null : signingCert); certificate = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime,
now.AddDays(ValidDays), isRoot ? null : signingCert);
return certificate; return certificate;
} }
} }
......
...@@ -34,32 +34,82 @@ namespace Titanium.Web.Proxy.Network ...@@ -34,32 +34,82 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public sealed class CertificateManager : IDisposable public sealed class CertificateManager : IDisposable
{ {
private const string defaultRootCertificateIssuer = "Titanium"; private const string defaultRootCertificateIssuer = "Titanium";
private const string defaultRootRootCertificateName = "Titanium Root Certificate Authority"; private const string defaultRootRootCertificateName = "Titanium Root Certificate Authority";
private CertificateEngine engine; /// <summary>
/// Cache dictionary
/// </summary>
private readonly ConcurrentDictionary<string, CachedCertificate> certificateCache;
private readonly ExceptionHandler exceptionFunc;
private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks;
private ICertificateMaker certEngine; private ICertificateMaker certEngine;
private CertificateEngine engine;
private string issuer; private string issuer;
private string rootCertificateName; private bool pfxFileExists;
private bool pfxFileExists = false; private X509Certificate2 rootCertificate;
private bool clearCertificates { get; set; } private string rootCertificateName;
private X509Certificate2 rootCertificate;
/// <summary> /// <summary>
/// Cache dictionary /// Constructor.
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, CachedCertificate> certificateCache; /// <param name="rootCertificateName">Name of root certificate.</param>
private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks; /// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
/// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">
/// Note:setting machineTrustRootCertificate to true will force
/// userTrustRootCertificate to true
/// </param>
/// <param name="trustRootCertificateAsAdmin"></param>
/// <param name="exceptionFunc"></param>
internal CertificateManager(string rootCertificateName, string rootCertificateIssuerName,
bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin,
ExceptionHandler exceptionFunc)
{
this.exceptionFunc = exceptionFunc;
private readonly ExceptionHandler exceptionFunc; UserTrustRoot = userTrustRootCertificate;
if (machineTrustRootCertificate)
{
userTrustRootCertificate = true;
}
MachineTrustRoot = machineTrustRootCertificate;
TrustRootAsAdministrator = trustRootCertificateAsAdmin;
if (rootCertificateName != null)
{
RootCertificateName = rootCertificateName;
}
if (rootCertificateIssuerName != null)
{
RootCertificateIssuerName = rootCertificateIssuerName;
}
if (RunTime.IsWindows)
{
CertificateEngine = CertificateEngine.DefaultWindows;
}
else
{
CertificateEngine = CertificateEngine.BouncyCastle;
}
certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
pendingCertificateCreationTasks = new ConcurrentDictionary<string, Task<X509Certificate2>>();
}
private bool clearCertificates { get; set; }
/// <summary> /// <summary>
/// Is the root certificate used by this proxy is valid? /// Is the root certificate used by this proxy is valid?
...@@ -69,19 +119,19 @@ namespace Titanium.Web.Proxy.Network ...@@ -69,19 +119,19 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Trust the RootCertificate used by this proxy server for current user /// Trust the RootCertificate used by this proxy server for current user
/// </summary> /// </summary>
internal bool UserTrustRoot { get; set; } = false; internal bool UserTrustRoot { get; set; }
/// <summary> /// <summary>
/// Trust the RootCertificate used by this proxy server for current machine /// Trust the RootCertificate used by this proxy server for current machine
/// Needs elevated permission, otherwise will fail silently. /// Needs elevated permission, otherwise will fail silently.
/// </summary> /// </summary>
internal bool MachineTrustRoot { get; set; } = false; internal bool MachineTrustRoot { get; set; }
/// <summary> /// <summary>
/// Whether trust operations should be done with elevated privillages /// Whether trust operations should be done with elevated privillages
/// Will prompt with UAC if required. Works only on Windows. /// Will prompt with UAC if required. Works only on Windows.
/// </summary> /// </summary>
internal bool TrustRootAsAdministrator { get; set; } = false; internal bool TrustRootAsAdministrator { get; set; }
/// <summary> /// <summary>
/// Select Certificate Engine /// Select Certificate Engine
...@@ -122,7 +172,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -122,7 +172,10 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Name(path) of the Root certificate file /// Name(path) of the Root certificate file
/// <para>Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as "rootCert.pfx" (and will be saved in proxy dll directory)</para> /// <para>
/// Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as
/// "rootCert.pfx" (and will be saved in proxy dll directory)
/// </para>
/// </summary> /// </summary>
public string PfxFilePath { get; set; } = string.Empty; public string PfxFilePath { get; set; } = string.Empty;
...@@ -133,10 +186,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -133,10 +186,7 @@ namespace Titanium.Web.Proxy.Network
public string RootCertificateIssuerName public string RootCertificateIssuerName
{ {
get => issuer ?? defaultRootCertificateIssuer; get => issuer ?? defaultRootCertificateIssuer;
set set => issuer = value;
{
issuer = value;
}
} }
/// <summary> /// <summary>
...@@ -149,10 +199,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -149,10 +199,7 @@ namespace Titanium.Web.Proxy.Network
public string RootCertificateName public string RootCertificateName
{ {
get => rootCertificateName ?? defaultRootRootCertificateName; get => rootCertificateName ?? defaultRootRootCertificateName;
set set => rootCertificateName = value;
{
rootCertificateName = value;
}
} }
/// <summary> /// <summary>
...@@ -190,51 +237,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -190,51 +237,11 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public X509KeyStorageFlags StorageFlag { get; set; } = X509KeyStorageFlags.Exportable; public X509KeyStorageFlags StorageFlag { get; set; } = X509KeyStorageFlags.Exportable;
/// <summary> /// <summary>
/// Constructor. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// </summary>
/// <param name="rootCertificateName">Name of root certificate.</param> public void Dispose()
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
/// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true</param>
/// <param name="trustRootCertificateAsAdmin"></param>
/// <param name="exceptionFunc"></param>
internal CertificateManager(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin, ExceptionHandler exceptionFunc)
{
this.exceptionFunc = exceptionFunc;
UserTrustRoot = userTrustRootCertificate;
if (machineTrustRootCertificate)
{
userTrustRootCertificate = true;
}
MachineTrustRoot = machineTrustRootCertificate;
TrustRootAsAdministrator = trustRootCertificateAsAdmin;
if (rootCertificateName != null)
{ {
RootCertificateName = rootCertificateName;
}
if (rootCertificateIssuerName != null)
{
RootCertificateIssuerName = rootCertificateIssuerName;
}
if (RunTime.IsWindows)
{
//this is faster in Windows based on tests (see unit test project CertificateManagerTests.cs)
CertificateEngine = CertificateEngine.DefaultWindows;
}
else
{
CertificateEngine = CertificateEngine.BouncyCastle;
}
certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
pendingCertificateCreationTasks = new ConcurrentDictionary<string, Task<X509Certificate2>>();
} }
...@@ -250,7 +257,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -250,7 +257,9 @@ namespace Titanium.Web.Proxy.Network
string path = Path.GetDirectoryName(assemblyLocation); string path = Path.GetDirectoryName(assemblyLocation);
if (null == path) if (null == path)
{
throw new NullReferenceException(); throw new NullReferenceException();
}
return path; return path;
} }
...@@ -295,7 +304,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -295,7 +304,8 @@ namespace Titanium.Web.Proxy.Network
|| FindCertificates(StoreName.My, storeLocation, value).Count > 0); || FindCertificates(StoreName.My, storeLocation, value).Count > 0);
} }
private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue) private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation,
string findValue)
{ {
var x509Store = new X509Store(storeName, storeLocation); var x509Store = new X509Store(storeName, storeLocation);
try try
...@@ -334,13 +344,13 @@ namespace Titanium.Web.Proxy.Network ...@@ -334,13 +344,13 @@ namespace Titanium.Web.Proxy.Network
{ {
x509store.Open(OpenFlags.ReadWrite); x509store.Open(OpenFlags.ReadWrite);
x509store.Add(RootCertificate); x509store.Add(RootCertificate);
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( exceptionFunc(
new Exception("Failed to make system trust root certificate " new Exception("Failed to make system trust root certificate "
+ $" for {storeName}\\{storeLocation} store location. You may need admin rights.", e)); + $" for {storeName}\\{storeLocation} store location. You may need admin rights.",
e));
} }
finally finally
{ {
...@@ -355,7 +365,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -355,7 +365,8 @@ namespace Titanium.Web.Proxy.Network
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <param name="certificate"></param> /// <param name="certificate"></param>
/// <returns></returns> /// <returns></returns>
private void UninstallCertificate(StoreName storeName, StoreLocation storeLocation, X509Certificate2 certificate) private void UninstallCertificate(StoreName storeName, StoreLocation storeLocation,
X509Certificate2 certificate)
{ {
if (certificate == null) if (certificate == null)
{ {
...@@ -397,7 +408,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -397,7 +408,6 @@ namespace Titanium.Web.Proxy.Network
if (CertificateEngine == CertificateEngine.DefaultWindows) if (CertificateEngine == CertificateEngine.DefaultWindows)
{ {
//prevent certificates getting piled up in User\Personal store
Task.Run(() => UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, certificate)); Task.Run(() => UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, certificate));
} }
...@@ -412,7 +422,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -412,7 +422,6 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns> /// <returns></returns>
internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
X509Certificate2 certificate = null; X509Certificate2 certificate = null;
try try
{ {
...@@ -421,7 +430,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -421,7 +430,7 @@ namespace Titanium.Web.Proxy.Network
string path = GetCertificatePath(); string path = GetCertificatePath();
string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty); string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty);
subjectName = subjectName.Replace("*", "$x$"); subjectName = subjectName.Replace("*", "$x$");
var certificatePath = Path.Combine(path, subjectName + ".pfx"); string certificatePath = Path.Combine(path, subjectName + ".pfx");
if (!File.Exists(certificatePath)) if (!File.Exists(certificatePath))
{ {
...@@ -434,7 +443,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -434,7 +443,10 @@ namespace Titanium.Web.Proxy.Network
{ {
File.WriteAllBytes(certificatePath, certificate.Export(X509ContentType.Pkcs12)); File.WriteAllBytes(certificatePath, certificate.Export(X509ContentType.Pkcs12));
} }
catch (Exception e) { exceptionFunc(new Exception("Failed to save fake certificate.", e)); } catch (Exception e)
{
exceptionFunc(new Exception("Failed to save fake certificate.", e));
}
}); });
} }
else else
...@@ -483,8 +495,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -483,8 +495,6 @@ namespace Titanium.Web.Proxy.Network
Task<X509Certificate2> task; Task<X509Certificate2> task;
if (pendingCertificateCreationTasks.TryGetValue(certificateName, out task)) if (pendingCertificateCreationTasks.TryGetValue(certificateName, out task))
{ {
//certificate already added to cache
//just return the result here
return await task; return await task;
} }
...@@ -494,14 +504,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -494,14 +504,12 @@ namespace Titanium.Web.Proxy.Network
var result = CreateCertificate(certificateName, false); var result = CreateCertificate(certificateName, false);
if (result != null) if (result != null)
{ {
//this is ConcurrentDictionary
//if key exists it will silently handle; no need for locking
certificateCache.TryAdd(certificateName, new CachedCertificate certificateCache.TryAdd(certificateName, new CachedCertificate
{ {
Certificate = result Certificate = result
}); });
} }
return result; return result;
}); });
pendingCertificateCreationTasks.TryAdd(certificateName, task); pendingCertificateCreationTasks.TryAdd(certificateName, task);
...@@ -511,7 +519,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -511,7 +519,6 @@ namespace Titanium.Web.Proxy.Network
pendingCertificateCreationTasks.TryRemove(certificateName, out task); pendingCertificateCreationTasks.TryRemove(certificateName, out task);
return certificate; return certificate;
} }
/// <summary> /// <summary>
...@@ -528,7 +535,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -528,7 +535,9 @@ namespace Titanium.Web.Proxy.Network
CachedCertificate removed; CachedCertificate removed;
foreach (var cache in outdated) foreach (var cache in outdated)
{
certificateCache.TryRemove(cache.Key, out removed); certificateCache.TryRemove(cache.Key, out removed);
}
//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);
...@@ -629,14 +638,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -629,14 +638,18 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// Manually load a Root certificate file from give path (.pfx file) /// Manually load a Root certificate file from give path (.pfx file)
/// </summary> /// </summary>
/// <param name="pfxFilePath">Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as "rootCert.pfx" (and will be saved in proxy dll directory)</param> /// <param name="pfxFilePath">
/// Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be
/// named as "rootCert.pfx" (and will be saved in proxy dll directory)
/// </param>
/// <param name="password">Set a password for the .pfx file</param> /// <param name="password">Set a password for the .pfx file</param>
/// <param name="overwritePfXFile">true : replace an existing .pfx file if password is incorect or if RootCertificate==null</param> /// <param name="overwritePfXFile">true : replace an existing .pfx file if password is incorect or if RootCertificate==null</param>
/// <param name="storageFlag"></param> /// <param name="storageFlag"></param>
/// <returns> /// <returns>
/// true if succeeded, else false /// true if succeeded, else false
/// </returns> /// </returns>
public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true, X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable) public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true,
X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable)
{ {
PfxFilePath = pfxFilePath; PfxFilePath = pfxFilePath;
PfxPassword = password; PfxPassword = password;
...@@ -645,7 +658,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -645,7 +658,7 @@ namespace Titanium.Web.Proxy.Network
RootCertificate = LoadRootCertificate(); RootCertificate = LoadRootCertificate();
return (RootCertificate != null); return RootCertificate != null;
} }
/// <summary> /// <summary>
...@@ -654,7 +667,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -654,7 +667,6 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public void TrustRootCertificate(bool machineTrusted = false) public void TrustRootCertificate(bool machineTrusted = false)
{ {
//currentUser\personal //currentUser\personal
InstallCertificate(StoreName.My, StoreLocation.CurrentUser); InstallCertificate(StoreName.My, StoreLocation.CurrentUser);
...@@ -692,7 +704,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -692,7 +704,7 @@ namespace Titanium.Web.Proxy.Network
File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword)); File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword));
//currentUser\Root, currentMachine\Personal & currentMachine\Root //currentUser\Root, currentMachine\Personal & currentMachine\Root
var info = new ProcessStartInfo() var info = new ProcessStartInfo
{ {
FileName = "certutil.exe", FileName = "certutil.exe",
CreateNoWindow = true, CreateNoWindow = true,
...@@ -721,7 +733,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -721,7 +733,6 @@ namespace Titanium.Web.Proxy.Network
process.WaitForExit(); process.WaitForExit();
File.Delete(pfxFileName); File.Delete(pfxFileName);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -751,7 +762,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -751,7 +762,6 @@ namespace Titanium.Web.Proxy.Network
{ {
TrustRootCertificate(MachineTrustRoot); TrustRootCertificate(MachineTrustRoot);
} }
} }
/// <summary> /// <summary>
...@@ -759,7 +769,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -759,7 +769,8 @@ namespace Titanium.Web.Proxy.Network
/// Also makes root certificate trusted based on provided parameters /// Also makes root certificate trusted based on provided parameters
/// Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true /// Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true
/// </summary> /// </summary>
public void EnsureRootCertificate(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) public void EnsureRootCertificate(bool userTrustRootCertificate = true,
bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false)
{ {
UserTrustRoot = userTrustRootCertificate; UserTrustRoot = userTrustRootCertificate;
if (machineTrustRootCertificate) if (machineTrustRootCertificate)
...@@ -812,12 +823,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -812,12 +823,10 @@ namespace Titanium.Web.Proxy.Network
//this adds to both currentUser\Root & currentMachine\Root //this adds to both currentUser\Root & currentMachine\Root
UninstallCertificate(StoreName.Root, StoreLocation.LocalMachine, RootCertificate); UninstallCertificate(StoreName.Root, StoreLocation.LocalMachine, RootCertificate);
} }
} }
/// <summary> /// <summary>
/// Removes the trusted certificates from user store, optionally also from machine store /// Removes the trusted certificates from user store, optionally also from machine store
// Prompts with UAC if elevated permissions are required. Works only on Windows.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false) public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false)
...@@ -826,14 +835,14 @@ namespace Titanium.Web.Proxy.Network ...@@ -826,14 +835,14 @@ namespace Titanium.Web.Proxy.Network
{ {
return false; return false;
} }
//currentUser\Personal //currentUser\Personal
UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate); UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate);
var infos = new List<ProcessStartInfo>(); var infos = new List<ProcessStartInfo>();
if (!machineTrusted) if (!machineTrusted)
{ {
//currentUser\Root infos.Add(new ProcessStartInfo
infos.Add(new ProcessStartInfo()
{ {
FileName = "certutil.exe", FileName = "certutil.exe",
Arguments = "-delstore -user Root \"" + RootCertificateName + "\"", Arguments = "-delstore -user Root \"" + RootCertificateName + "\"",
...@@ -843,14 +852,15 @@ namespace Titanium.Web.Proxy.Network ...@@ -843,14 +852,15 @@ namespace Titanium.Web.Proxy.Network
ErrorDialog = false, ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden WindowStyle = ProcessWindowStyle.Hidden
}); });
} }
else else
{ {
infos.AddRange( infos.AddRange(
new List<ProcessStartInfo>() { new List<ProcessStartInfo>
{
//currentMachine\Personal //currentMachine\Personal
new ProcessStartInfo(){ new ProcessStartInfo
{
FileName = "certutil.exe", FileName = "certutil.exe",
Arguments = "-delstore My \"" + RootCertificateName + "\"", Arguments = "-delstore My \"" + RootCertificateName + "\"",
CreateNoWindow = true, CreateNoWindow = true,
...@@ -860,7 +870,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -860,7 +870,8 @@ namespace Titanium.Web.Proxy.Network
WindowStyle = ProcessWindowStyle.Hidden WindowStyle = ProcessWindowStyle.Hidden
}, },
//currentUser\Personal & currentMachine\Personal //currentUser\Personal & currentMachine\Personal
new ProcessStartInfo(){ new ProcessStartInfo
{
FileName = "certutil.exe", FileName = "certutil.exe",
Arguments = "-delstore Root \"" + RootCertificateName + "\"", Arguments = "-delstore Root \"" + RootCertificateName + "\"",
CreateNoWindow = true, CreateNoWindow = true,
...@@ -872,7 +883,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -872,7 +883,7 @@ namespace Titanium.Web.Proxy.Network
}); });
} }
var success = true; bool success = true;
try try
{ {
foreach (var info in infos) foreach (var info in infos)
...@@ -900,12 +911,5 @@ namespace Titanium.Web.Proxy.Network ...@@ -900,12 +911,5 @@ namespace Titanium.Web.Proxy.Network
certificateCache.Clear(); certificateCache.Clear();
rootCertificate = null; rootCertificate = null;
} }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
}
} }
} }
#if DEBUG #if DEBUG
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
class DebugCustomBufferedStream : CustomBufferedStream internal class DebugCustomBufferedStream : CustomBufferedStream
{ {
private const string basePath = @"."; private const string basePath = @".";
private static int counter; private static int counter;
public int Counter { get; } private readonly FileStream fileStreamReceived;
private readonly FileStream fileStreamSent; private readonly FileStream fileStreamSent;
private readonly FileStream fileStreamReceived;
public DebugCustomBufferedStream(Stream baseStream, int bufferSize) : base(baseStream, bufferSize) public DebugCustomBufferedStream(Stream baseStream, int bufferSize) : base(baseStream, bufferSize)
{ {
Counter = Interlocked.Increment(ref counter); Counter = Interlocked.Increment(ref counter);
...@@ -29,6 +22,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -29,6 +22,8 @@ namespace Titanium.Web.Proxy.Network
fileStreamReceived = new FileStream(Path.Combine(basePath, $"{Counter}_received.dat"), FileMode.Create); fileStreamReceived = new FileStream(Path.Combine(basePath, $"{Counter}_received.dat"), FileMode.Create);
} }
public int Counter { get; }
protected override void OnDataSent(byte[] buffer, int offset, int count) protected override void OnDataSent(byte[] buffer, int offset, int count)
{ {
fileStreamSent.Write(buffer, offset, count); fileStreamSent.Write(buffer, offset, count);
......
using StreamExtended.Network; using System.Net.Sockets;
using System.Net.Sockets; using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
......
using StreamExtended.Network; using System;
using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -13,6 +13,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -13,6 +13,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal class TcpConnection : IDisposable internal class TcpConnection : IDisposable
{ {
internal TcpConnection(ProxyServer proxyServer)
{
LastAccess = DateTime.Now;
this.proxyServer = proxyServer;
this.proxyServer.UpdateServerConnectionCount(true);
}
private ProxyServer proxyServer { get; } private ProxyServer proxyServer { get; }
internal ExternalProxy UpStreamProxy { get; set; } internal ExternalProxy UpStreamProxy { get; set; }
...@@ -57,13 +64,6 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -57,13 +64,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
internal TcpConnection(ProxyServer proxyServer)
{
LastAccess = DateTime.Now;
this.proxyServer = proxyServer;
this.proxyServer.UpdateServerConnectionCount(true);
}
/// <summary> /// <summary>
/// Dispose. /// Dispose.
/// </summary> /// </summary>
......
using StreamExtended.Network; using System;
using System;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
...@@ -37,7 +36,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -37,7 +36,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
bool useUpstreamProxy = false; bool useUpstreamProxy = false;
//check if external proxy is set for HTTP/HTTPS //check if external proxy is set for HTTP/HTTPS
if (externalProxy != null && !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort)) if (externalProxy != null &&
!(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort))
{ {
useUpstreamProxy = true; useUpstreamProxy = true;
...@@ -102,10 +102,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -102,10 +102,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (isHttps) if (isHttps)
{ {
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate, proxyServer.SelectClientCertificate); var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize); stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize);
await sslStream.AuthenticateAsClientAsync(remoteHostName, null, proxyServer.SupportedSslProtocols, proxyServer.CheckCertificateRevocation); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, proxyServer.SupportedSslProtocols,
proxyServer.CheckCertificateRevocation);
} }
client.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000; client.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
......
...@@ -6,12 +6,12 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -6,12 +6,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
/// <summary> /// <summary>
/// Represents a managed interface of IP Helper API TcpRow struct /// Represents a managed interface of IP Helper API TcpRow struct
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/> /// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx" />
/// </summary> /// </summary>
internal class TcpRow internal class TcpRow
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TcpRow"/> class. /// Initializes a new instance of the <see cref="TcpRow" /> class.
/// </summary> /// </summary>
/// <param name="tcpRow">TcpRow struct.</param> /// <param name="tcpRow">TcpRow struct.</param>
internal TcpRow(NativeMethods.TcpRow tcpRow) internal TcpRow(NativeMethods.TcpRow tcpRow)
......
...@@ -12,7 +12,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -12,7 +12,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal class TcpTable : IEnumerable<TcpRow> internal class TcpTable : IEnumerable<TcpRow>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TcpTable"/> class. /// Initializes a new instance of the <see cref="TcpTable" /> class.
/// </summary> /// </summary>
/// <param name="tcpRows">TcpRow collection to initialize with.</param> /// <param name="tcpRows">TcpRow collection to initialize with.</param>
internal TcpTable(IEnumerable<TcpRow> tcpRows) internal TcpTable(IEnumerable<TcpRow> tcpRows)
......
...@@ -5,6 +5,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -5,6 +5,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{ {
internal class Common internal class Common
{ {
internal static uint NewContextAttributes = 0;
internal static SecurityInteger NewLifeTime = new SecurityInteger(0);
#region Private constants #region Private constants
private const int ISC_REQ_REPLAY_DETECT = 0x00000004; private const int ISC_REQ_REPLAY_DETECT = 0x00000004;
...@@ -14,12 +17,11 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -14,12 +17,11 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
#endregion #endregion
internal static uint NewContextAttributes = 0;
internal static SecurityInteger NewLifeTime = new SecurityInteger(0);
#region internal constants #region internal constants
internal const int StandardContextAttributes = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONNECTION; internal const int StandardContextAttributes =
ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONNECTION;
internal const int SecurityNativeDataRepresentation = 0x10; internal const int SecurityNativeDataRepresentation = 0x10;
internal const int MaximumTokenSize = 12288; internal const int MaximumTokenSize = 12288;
internal const int SecurityCredentialsOutbound = 2; internal const int SecurityCredentialsOutbound = 2;
...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
Negotiate128 = 0x20000000, Negotiate128 = 0x20000000,
// Indicates that this client supports medium (56-bit) encryption. // Indicates that this client supports medium (56-bit) encryption.
Negotiate56 = (unchecked((int)0x80000000)) Negotiate56 = unchecked((int)0x80000000)
} }
internal enum NtlmAuthLevel internal enum NtlmAuthLevel
...@@ -86,7 +88,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -86,7 +88,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
NTLM_only, NTLM_only,
/* Use NTLMv2 only. */ /* Use NTLMv2 only. */
NTLMv2_only, NTLMv2_only
} }
#endregion #endregion
...@@ -211,7 +213,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -211,7 +213,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
//What we need to do here is to grab a hold of the pvBuffer allocate by the individual //What we need to do here is to grab a hold of the pvBuffer allocate by the individual
//SecBuffer and release it... //SecBuffer and release it...
int currentOffset = index * Marshal.SizeOf(typeof(Buffer)); int currentOffset = index * Marshal.SizeOf(typeof(Buffer));
var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers,
currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)));
Marshal.FreeHGlobal(secBufferpvBuffer); Marshal.FreeHGlobal(secBufferpvBuffer);
} }
} }
...@@ -267,13 +270,14 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -267,13 +270,14 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
//byte array... //byte array...
int currentOffset = index * Marshal.SizeOf(typeof(Buffer)); int currentOffset = index * Marshal.SizeOf(typeof(Buffer));
int bytesToCopy = Marshal.ReadInt32(pBuffers, currentOffset); int bytesToCopy = Marshal.ReadInt32(pBuffers, currentOffset);
var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers,
currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)));
Marshal.Copy(secBufferpvBuffer, buffer, bufferIndex, bytesToCopy); Marshal.Copy(secBufferpvBuffer, buffer, bufferIndex, bytesToCopy);
bufferIndex += bytesToCopy; bufferIndex += bytesToCopy;
} }
} }
return (buffer); return buffer;
} }
} }
......
...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{ {
} }
private static unsafe byte[] GetUShortBytes(byte*bytes) private static unsafe byte[] GetUShortBytes(byte* bytes)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
...@@ -47,7 +47,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -47,7 +47,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return new[] { bytes[1], bytes[0] }; return new[] { bytes[1], bytes[0] };
} }
private static unsafe byte[] GetUIntBytes(byte*bytes) private static unsafe byte[] GetUIntBytes(byte* bytes)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
...@@ -57,7 +57,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -57,7 +57,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return new[] { bytes[3], bytes[2], bytes[1], bytes[0] }; return new[] { bytes[3], bytes[2], bytes[1], bytes[0] };
} }
private static unsafe byte[] GetULongBytes(byte*bytes) private static unsafe byte[] GetULongBytes(byte* bytes)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
...@@ -117,7 +117,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -117,7 +117,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return GetULongBytes((byte*)&value); return GetULongBytes((byte*)&value);
} }
private static unsafe void UShortFromBytes(byte*dst, byte[] src, int startIndex) private static unsafe void UShortFromBytes(byte* dst, byte[] src, int startIndex)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
...@@ -131,7 +131,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -131,7 +131,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
} }
} }
private static unsafe void UIntFromBytes(byte*dst, byte[] src, int startIndex) private static unsafe void UIntFromBytes(byte* dst, byte[] src, int startIndex)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
...@@ -149,19 +149,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -149,19 +149,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
} }
} }
private static unsafe void ULongFromBytes(byte*dst, byte[] src, int startIndex) private static unsafe void ULongFromBytes(byte* dst, byte[] src, int startIndex)
{ {
if (BitConverter.IsLittleEndian) if (BitConverter.IsLittleEndian)
{ {
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{
dst[i] = src[startIndex + i]; dst[i] = src[startIndex + i];
} }
}
else else
{ {
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{
dst[i] = src[startIndex + (7 - i)]; dst[i] = src[startIndex + (7 - i)];
} }
} }
}
internal static bool ToBoolean(byte[] value, int startIndex) internal static bool ToBoolean(byte[] value, int startIndex)
{ {
......
...@@ -42,6 +42,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -42,6 +42,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{ {
private static readonly byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00 }; private static readonly byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00 };
private readonly int type;
internal Message(byte[] message) internal Message(byte[] message)
{ {
type = 3; type = 3;
...@@ -58,8 +60,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -58,8 +60,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// </summary> /// </summary>
internal string Username { get; private set; } internal string Username { get; private set; }
private readonly int type;
internal Common.NtlmFlags Flags { get; set; } internal Common.NtlmFlags Flags { get; set; }
// methods // methods
...@@ -68,7 +68,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -68,7 +68,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
//base.Decode (message); //base.Decode (message);
if (message == null) if (message == null)
{
throw new ArgumentNullException("message"); throw new ArgumentNullException("message");
}
if (message.Length < 12) if (message.Length < 12)
{ {
...@@ -108,12 +110,13 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -108,12 +110,13 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
Username = DecodeString(message, userOff, userLen); Username = DecodeString(message, userOff, userLen);
} }
string DecodeString(byte[] buffer, int offset, int len) private string DecodeString(byte[] buffer, int offset, int len)
{ {
if ((Flags & Common.NtlmFlags.NegotiateUnicode) != 0) if ((Flags & Common.NtlmFlags.NegotiateUnicode) != 0)
{ {
return Encoding.Unicode.GetString(buffer, offset, len); return Encoding.Unicode.GetString(buffer, offset, len);
} }
return Encoding.ASCII.GetString(buffer, offset, len); return Encoding.ASCII.GetString(buffer, offset, len);
} }
...@@ -122,9 +125,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -122,9 +125,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
for (int i = 0; i < header.Length; i++) for (int i = 0; i < header.Length; i++)
{ {
if (message[i] != header[i]) if (message[i] != header[i])
{
return false; return false;
} }
return (LittleEndian.ToUInt32(message, 8) == type); }
return LittleEndian.ToUInt32(message, 8) == type;
} }
} }
} }
...@@ -16,21 +16,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -16,21 +16,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
INITIAL_TOKEN, INITIAL_TOKEN,
FINAL_TOKEN, FINAL_TOKEN,
AUTHORIZED AUTHORIZED
};
internal State()
{
Credentials = new Common.SecurityHandle(0);
Context = new Common.SecurityHandle(0);
LastSeen = DateTime.Now;
AuthState = WinAuthState.UNAUTHORIZED;
} }
/// <summary> /// <summary>
/// Credentials used to validate NTLM hashes /// Current state of the authentication process
/// </summary> /// </summary>
internal Common.SecurityHandle Credentials; internal WinAuthState AuthState;
/// <summary> /// <summary>
/// Context will be used to validate HTLM hashes /// Context will be used to validate HTLM hashes
...@@ -38,14 +29,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -38,14 +29,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
internal Common.SecurityHandle Context; internal Common.SecurityHandle Context;
/// <summary> /// <summary>
/// Timestamp needed to calculate validity of the authenticated session /// Credentials used to validate NTLM hashes
/// </summary> /// </summary>
internal DateTime LastSeen; internal Common.SecurityHandle Credentials;
/// <summary> /// <summary>
/// Current state of the authentication process /// Timestamp needed to calculate validity of the authenticated session
/// </summary> /// </summary>
internal WinAuthState AuthState; internal DateTime LastSeen;
internal State()
{
Credentials = new Common.SecurityHandle(0);
Context = new Common.SecurityHandle(0);
LastSeen = DateTime.Now;
AuthState = WinAuthState.UNAUTHORIZED;
}
internal void ResetHandles() internal void ResetHandles()
{ {
......
...@@ -53,7 +53,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -53,7 +53,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
if (result != SuccessfulResult) if (result != SuccessfulResult)
{ {
// Credentials acquire operation failed.
return null; return null;
} }
...@@ -73,7 +72,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -73,7 +72,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
if (result != IntermediateResult) if (result != IntermediateResult)
{ {
// Client challenge issue operation failed.
return null; return null;
} }
...@@ -128,7 +126,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -128,7 +126,6 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
if (result != SuccessfulResult) if (result != SuccessfulResult)
{ {
// Client challenge issue operation failed.
return null; return null;
} }
...@@ -172,22 +169,22 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -172,22 +169,22 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <returns></returns> /// <returns></returns>
internal static bool ValidateWinAuthState(Guid requestId, State.WinAuthState expectedAuthState) internal static bool ValidateWinAuthState(Guid requestId, State.WinAuthState expectedAuthState)
{ {
var stateExists = authStates.TryGetValue(requestId, out var state); bool stateExists = authStates.TryGetValue(requestId, out var state);
if (expectedAuthState == State.WinAuthState.UNAUTHORIZED) if (expectedAuthState == State.WinAuthState.UNAUTHORIZED)
{ {
// Validation before initial token
return stateExists == false || return stateExists == false ||
state.AuthState == State.WinAuthState.UNAUTHORIZED || state.AuthState == State.WinAuthState.UNAUTHORIZED ||
state.AuthState == State.WinAuthState.AUTHORIZED; // Server may require re-authentication on an open connection state.AuthState ==
State.WinAuthState.AUTHORIZED; // Server may require re-authentication on an open connection
} }
if (expectedAuthState == State.WinAuthState.INITIAL_TOKEN) if (expectedAuthState == State.WinAuthState.INITIAL_TOKEN)
{ {
// Validation before final token
return stateExists && return stateExists &&
(state.AuthState == State.WinAuthState.INITIAL_TOKEN || (state.AuthState == State.WinAuthState.INITIAL_TOKEN ||
state.AuthState == State.WinAuthState.AUTHORIZED); // Server may require re-authentication on an open connection state.AuthState == State.WinAuthState.AUTHORIZED
); // Server may require re-authentication on an open connection
} }
throw new Exception("Unsupported validation of WinAuthState"); throw new Exception("Unsupported validation of WinAuthState");
...@@ -209,7 +206,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -209,7 +206,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
#region Native calls to secur32.dll #region Native calls to secur32.dll
[DllImport("secur32.dll", SetLastError = true)] [DllImport("secur32.dll", SetLastError = true)]
static extern int InitializeSecurityContext(ref SecurityHandle phCredential, //PCredHandle private static extern int InitializeSecurityContext(ref SecurityHandle phCredential, //PCredHandle
IntPtr phContext, //PCtxtHandle IntPtr phContext, //PCtxtHandle
string pszTargetName, string pszTargetName,
int fContextReq, int fContextReq,
...@@ -223,7 +220,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -223,7 +220,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
out SecurityInteger ptsExpiry); //PTimeStamp out SecurityInteger ptsExpiry); //PTimeStamp
[DllImport("secur32", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("secur32", CharSet = CharSet.Auto, SetLastError = true)]
static extern int InitializeSecurityContext(ref SecurityHandle phCredential, //PCredHandle private static extern int InitializeSecurityContext(ref SecurityHandle phCredential, //PCredHandle
ref SecurityHandle phContext, //PCtxtHandle ref SecurityHandle phContext, //PCtxtHandle
string pszTargetName, string pszTargetName,
int fContextReq, int fContextReq,
......
...@@ -34,7 +34,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth ...@@ -34,7 +34,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth
/// <returns></returns> /// <returns></returns>
public static string GetFinalAuthToken(string serverHostname, string serverToken, Guid requestId) public static string GetFinalAuthToken(string serverHostname, string serverToken, Guid requestId)
{ {
var tokenBytes = WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken), requestId); var tokenBytes =
WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken),
requestId);
return string.Concat(" ", Convert.ToBase64String(tokenBytes)); return string.Concat(" ", Convert.ToBase64String(tokenBytes));
} }
......
...@@ -32,7 +32,8 @@ namespace Titanium.Web.Proxy ...@@ -32,7 +32,8 @@ namespace Titanium.Web.Proxy
} }
var headerValueParts = header.Value.Split(ProxyConstants.SpaceSplit); var headerValueParts = header.Value.Split(ProxyConstants.SpaceSplit);
if (headerValueParts.Length != 2 || !headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic)) if (headerValueParts.Length != 2 ||
!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic))
{ {
//Return not authorized //Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
...@@ -53,7 +54,6 @@ namespace Titanium.Web.Proxy ...@@ -53,7 +54,6 @@ namespace Titanium.Web.Proxy
bool authenticated = await AuthenticateUserFunc(username, password); bool authenticated = await AuthenticateUserFunc(username, password);
if (!authenticated) if (!authenticated)
{ {
//Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
} }
...@@ -61,7 +61,8 @@ namespace Titanium.Web.Proxy ...@@ -61,7 +61,8 @@ namespace Titanium.Web.Proxy
} }
catch (Exception e) catch (Exception e)
{ {
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", session, e, httpHeaders)); ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", session, e,
httpHeaders));
//Return not authorized //Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
......
...@@ -25,20 +25,25 @@ namespace Titanium.Web.Proxy ...@@ -25,20 +25,25 @@ namespace Titanium.Web.Proxy
internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp; internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps; internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
/// <summary>
/// An default exception log func
/// </summary>
private readonly Lazy<ExceptionHandler> defaultExceptionFunc = new Lazy<ExceptionHandler>(() => (e => { }));
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
private int clientConnectionCount; private int clientConnectionCount;
/// <summary> /// <summary>
/// Backing field for corresponding public property /// backing exception func for exposed public property
/// </summary> /// </summary>
private int serverConnectionCount; private ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// Backing field for corresponding public property
/// </summary> /// </summary>
private TcpConnectionFactory tcpConnectionFactory { get; } private int serverConnectionCount;
/// <summary> /// <summary>
/// Manaage upstream proxy detection /// Manaage upstream proxy detection
...@@ -46,19 +51,58 @@ namespace Titanium.Web.Proxy ...@@ -46,19 +51,58 @@ namespace Titanium.Web.Proxy
private WinHttpWebProxyFinder systemProxyResolver; private WinHttpWebProxyFinder systemProxyResolver;
/// <summary> /// <summary>
/// Manage system proxy settings /// Constructor
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; } /// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">
/// Note:setting machineTrustRootCertificate to true will force
/// userTrustRootCertificate to true
/// </param>
/// <param name="trustRootCertificateAsAdmin"></param>
public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate,
machineTrustRootCertificate, trustRootCertificateAsAdmin)
{
}
/// <summary> /// <summary>
/// An default exception log func /// Constructor.
/// </summary> /// </summary>
private readonly Lazy<ExceptionHandler> defaultExceptionFunc = new Lazy<ExceptionHandler>(() => (e => { })); /// <param name="rootCertificateName">Name of root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
/// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">
/// Note:setting machineTrustRootCertificate to true will force
/// userTrustRootCertificate to true
/// </param>
/// <param name="trustRootCertificateAsAdmin"></param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName,
bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false)
{
//default values
ConnectionTimeOutSeconds = 30;
ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory();
if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
{
systemProxySettingsManager = new SystemProxyManager();
}
CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName,
userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
}
/// <summary> /// <summary>
/// backing exception func for exposed public property /// A object that creates tcp connection to server
/// </summary> /// </summary>
private ExceptionHandler exceptionFunc; private TcpConnectionFactory tcpConnectionFactory { get; }
/// <summary>
/// Manage system proxy settings
/// </summary>
private SystemProxyManager systemProxySettingsManager { get; }
/// <summary> /// <summary>
/// Is the proxy currently running /// Is the proxy currently running
...@@ -153,6 +197,41 @@ namespace Titanium.Web.Proxy ...@@ -153,6 +197,41 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public List<ProxyEndPoint> ProxyEndPoints { get; set; } public List<ProxyEndPoint> ProxyEndPoints { get; set; }
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
/// <summary>
/// Callback for error events in proxy
/// </summary>
public ExceptionHandler ExceptionFunc
{
get => exceptionFunc ?? defaultExceptionFunc.Value;
set => exceptionFunc = value;
}
/// <summary>
/// A callback to authenticate clients
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; }
/// <summary>
/// Dispose Proxy.
/// </summary>
public void Dispose()
{
if (ProxyRunning)
{
Stop();
}
CertificateManager?.Dispose();
}
/// <summary> /// <summary>
/// Occurs when client connection count changed. /// Occurs when client connection count changed.
/// </summary> /// </summary>
...@@ -173,12 +252,6 @@ namespace Titanium.Web.Proxy ...@@ -173,12 +252,6 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback; public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback;
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
/// <summary> /// <summary>
/// Intercept request to server /// Intercept request to server
/// </summary> /// </summary>
...@@ -194,62 +267,14 @@ namespace Titanium.Web.Proxy ...@@ -194,62 +267,14 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> AfterResponse; public event AsyncEventHandler<SessionEventArgs> AfterResponse;
/// <summary>
/// Callback for error events in proxy
/// </summary>
public ExceptionHandler ExceptionFunc
{
get => exceptionFunc ?? defaultExceptionFunc.Value;
set => exceptionFunc = value;
}
/// <summary>
/// A callback to authenticate clients
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true</param>
/// <param name="trustRootCertificateAsAdmin"></param>
public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="rootCertificateName">Name of root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
/// <param name="userTrustRootCertificate"></param>
/// <param name="machineTrustRootCertificate">Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true</param>
/// <param name="trustRootCertificateAsAdmin"></param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false)
{
//default values
ConnectionTimeOutSeconds = 30;
ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory();
if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
{
systemProxySettingsManager = new SystemProxyManager();
}
CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName, userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
}
/// <summary> /// <summary>
/// Add a proxy end point /// Add a proxy end point
/// </summary> /// </summary>
/// <param name="endPoint"></param> /// <param name="endPoint"></param>
public void AddEndPoint(ProxyEndPoint endPoint) public void AddEndPoint(ProxyEndPoint endPoint)
{ {
if (ProxyEndPoints.Any(x => x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port)) if (ProxyEndPoints.Any(x =>
x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port))
{ {
throw new Exception("Cannot add another endpoint to same port & ip address"); throw new Exception("Cannot add another endpoint to same port & ip address");
} }
...@@ -319,7 +344,6 @@ namespace Titanium.Web.Proxy ...@@ -319,7 +344,6 @@ namespace Titanium.Web.Proxy
if (isHttps) if (isHttps)
{ {
CertificateManager.EnsureRootCertificate(); CertificateManager.EnsureRootCertificate();
//If certificate was trusted by the machine //If certificate was trusted by the machine
...@@ -375,7 +399,8 @@ namespace Titanium.Web.Proxy ...@@ -375,7 +399,8 @@ namespace Titanium.Web.Proxy
if (protocolType != ProxyProtocolType.None) if (protocolType != ProxyProtocolType.None)
{ {
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress, endPoint.Port, proxyType); Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress,
endPoint.Port, proxyType);
} }
} }
...@@ -456,7 +481,6 @@ namespace Titanium.Web.Proxy ...@@ -456,7 +481,6 @@ namespace Titanium.Web.Proxy
if (protocolToRemove != ProxyProtocolType.None) if (protocolToRemove != ProxyProtocolType.None)
{ {
//do not restore to any of listening address when we quit
systemProxySettingsManager.RemoveProxy(protocolToRemove, false); systemProxySettingsManager.RemoveProxy(protocolToRemove, false);
} }
} }
...@@ -482,7 +506,6 @@ namespace Titanium.Web.Proxy ...@@ -482,7 +506,6 @@ namespace Titanium.Web.Proxy
if (RunTime.IsWindows && !RunTime.IsRunningOnMono) if (RunTime.IsWindows && !RunTime.IsRunningOnMono)
{ {
//clear orphaned windows auth states every 2 minutes
WinAuthEndPoint.ClearIdleStates(2); WinAuthEndPoint.ClearIdleStates(2);
} }
} }
...@@ -499,7 +522,8 @@ namespace Titanium.Web.Proxy ...@@ -499,7 +522,8 @@ namespace Titanium.Web.Proxy
if (!RunTime.IsRunningOnMono && RunTime.IsWindows) if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
{ {
bool setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy); bool setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>()
.Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
if (setAsSystemProxy) if (setAsSystemProxy)
{ {
...@@ -519,19 +543,6 @@ namespace Titanium.Web.Proxy ...@@ -519,19 +543,6 @@ namespace Titanium.Web.Proxy
ProxyRunning = false; ProxyRunning = false;
} }
/// <summary>
/// Dispose Proxy.
/// </summary>
public void Dispose()
{
if (ProxyRunning)
{
Stop();
}
CertificateManager?.Dispose();
}
/// <summary> /// <summary>
/// Listen on the given end point on local machine /// Listen on the given end point on local machine
/// </summary> /// </summary>
...@@ -550,7 +561,8 @@ namespace Titanium.Web.Proxy ...@@ -550,7 +561,8 @@ namespace Titanium.Web.Proxy
} }
catch (SocketException ex) catch (SocketException ex)
{ {
var pex = new Exception($"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex); var pex = new Exception(
$"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex);
pex.Data.Add("ipAddress", endPoint.IpAddress); pex.Data.Add("ipAddress", endPoint.IpAddress);
pex.Data.Add("port", endPoint.Port); pex.Data.Add("port", endPoint.Port);
throw pex; throw pex;
...@@ -564,7 +576,10 @@ namespace Titanium.Web.Proxy ...@@ -564,7 +576,10 @@ namespace Titanium.Web.Proxy
private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
if (endPoint == null) if (endPoint == null)
{
throw new ArgumentNullException(nameof(endPoint)); 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");
...@@ -579,8 +594,11 @@ namespace Titanium.Web.Proxy ...@@ -579,8 +594,11 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Gets the system up stream proxy. /// Gets the system up stream proxy.
/// </summary> /// </summary>
/// <param name="sessionEventArgs">The <see cref="SessionEventArgs"/> instance containing the event data.</param> /// <param name="sessionEventArgs">The <see cref="SessionEventArgs" /> instance containing the event data.</param>
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns> /// <returns>
/// <see cref="ExternalProxy" /> instance containing valid proxy configuration from PAC/WAPD scripts if any
/// exists.
/// </returns>
private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs) private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
{ {
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri); var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
...@@ -616,10 +634,7 @@ namespace Titanium.Web.Proxy ...@@ -616,10 +634,7 @@ namespace Titanium.Web.Proxy
if (tcpClient != null) if (tcpClient != null)
{ {
Task.Run(async () => Task.Run(async () => { await HandleClient(tcpClient, endPoint); });
{
await HandleClient(tcpClient, endPoint);
});
} }
// Get the listener that handles the client request. // Get the listener that handles the client request.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -11,7 +12,6 @@ using Titanium.Web.Proxy.Helpers; ...@@ -11,7 +12,6 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using System.Threading;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -20,9 +20,11 @@ namespace Titanium.Web.Proxy ...@@ -20,9 +20,11 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
partial class ProxyServer partial class ProxyServer
{ {
private static readonly Regex uriSchemeRegex = new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex uriSchemeRegex =
new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private bool isWindowsAuthenticationEnabledAndSupported => EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono; private bool isWindowsAuthenticationEnabledAndSupported =>
EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono;
/// <summary> /// <summary>
/// This is the core request handler method for a particular connection from client /// This is the core request handler method for a particular connection from client
...@@ -40,8 +42,10 @@ namespace Titanium.Web.Proxy ...@@ -40,8 +42,10 @@ namespace Titanium.Web.Proxy
/// <param name="isTransparentEndPoint"></param> /// <param name="isTransparentEndPoint"></param>
/// <returns></returns> /// <returns></returns>
private async Task HandleHttpSessionRequest(ProxyEndPoint endPoint, TcpClient client, private async Task HandleHttpSessionRequest(ProxyEndPoint endPoint, TcpClient client,
CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader,
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest, bool isTransparentEndPoint = false) HttpResponseWriter clientStreamWriter,
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest,
bool isTransparentEndPoint = false)
{ {
TcpConnection connection = null; TcpConnection connection = null;
...@@ -68,7 +72,8 @@ namespace Titanium.Web.Proxy ...@@ -68,7 +72,8 @@ namespace Titanium.Web.Proxy
{ {
try try
{ {
Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version); Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl,
out var version);
//Read the request headers in to unique and non-unique header collections //Read the request headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers); await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers);
...@@ -94,7 +99,8 @@ namespace Titanium.Web.Proxy ...@@ -94,7 +99,8 @@ namespace Titanium.Web.Proxy
hostAndPath += httpUrl; hostAndPath += httpUrl;
} }
string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://", hostAndPath); string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://",
hostAndPath);
try try
{ {
httpRemoteUri = new Uri(url); httpRemoteUri = new Uri(url);
...@@ -116,7 +122,8 @@ namespace Titanium.Web.Proxy ...@@ -116,7 +122,8 @@ namespace Titanium.Web.Proxy
args.ProxyClient.ClientStreamWriter = clientStreamWriter; args.ProxyClient.ClientStreamWriter = clientStreamWriter;
//proxy authorization check //proxy authorization check
if (!args.IsTransparent && httpsConnectHostname == null && await CheckAuthorization(args) == false) if (!args.IsTransparent && httpsConnectHostname == null &&
await CheckAuthorization(args) == false)
{ {
await InvokeBeforeResponse(args); await InvokeBeforeResponse(args);
...@@ -163,9 +170,10 @@ namespace Titanium.Web.Proxy ...@@ -163,9 +170,10 @@ namespace Titanium.Web.Proxy
//create a new connection if hostname/upstream end point changes //create a new connection if hostname/upstream end point changes
if (connection != null if (connection != null
&& (!connection.HostName.Equals(request.RequestUri.Host, StringComparison.OrdinalIgnoreCase) && (!connection.HostName.Equals(request.RequestUri.Host,
|| (args.WebSession.UpStreamEndPoint != null StringComparison.OrdinalIgnoreCase)
&& !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint)))) || args.WebSession.UpStreamEndPoint != null
&& !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint)))
{ {
connection.Dispose(); connection.Dispose();
connection = null; connection = null;
...@@ -184,7 +192,8 @@ namespace Titanium.Web.Proxy ...@@ -184,7 +192,8 @@ namespace Titanium.Web.Proxy
await connection.StreamWriter.WriteHeadersAsync(request.Headers); await connection.StreamWriter.WriteHeadersAsync(request.Headers);
string httpStatus = await connection.StreamReader.ReadLineAsync(); string httpStatus = await connection.StreamReader.ReadLineAsync();
Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, Response.ParseResponseLine(httpStatus, out var responseVersion,
out int responseStatusCode,
out string responseStatusDescription); out string responseStatusDescription);
response.HttpVersion = responseVersion; response.HttpVersion = responseVersion;
response.StatusCode = responseStatusCode; response.StatusCode = responseStatusCode;
...@@ -216,7 +225,6 @@ namespace Titanium.Web.Proxy ...@@ -216,7 +225,6 @@ namespace Titanium.Web.Proxy
if (args.WebSession.ServerConnection == null) if (args.WebSession.ServerConnection == null)
{ {
//server connection was closed
return; return;
} }
...@@ -246,7 +254,6 @@ namespace Titanium.Web.Proxy ...@@ -246,7 +254,6 @@ namespace Titanium.Web.Proxy
await InvokeAfterResponse(args); await InvokeAfterResponse(args);
args.Dispose(); args.Dispose();
} }
} }
} }
finally finally
...@@ -285,12 +292,14 @@ namespace Titanium.Web.Proxy ...@@ -285,12 +292,14 @@ namespace Titanium.Web.Proxy
if (request.Is100Continue) if (request.Is100Continue)
{ {
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue"); await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue");
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
} }
else if (request.ExpectationFailed) else if (request.ExpectationFailed)
{ {
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed"); await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
} }
} }
...@@ -305,7 +314,6 @@ namespace Titanium.Web.Proxy ...@@ -305,7 +314,6 @@ namespace Titanium.Web.Proxy
//check if content-length is > 0 //check if content-length is > 0
if (request.ContentLength > 0) if (request.ContentLength > 0)
{ {
//If request was modified by user
if (request.IsBodyRead) if (request.IsBodyRead)
{ {
var writer = args.WebSession.ServerConnection.StreamWriter; var writer = args.WebSession.ServerConnection.StreamWriter;
...@@ -315,7 +323,6 @@ namespace Titanium.Web.Proxy ...@@ -315,7 +323,6 @@ namespace Titanium.Web.Proxy
{ {
if (!request.ExpectationFailed) if (!request.ExpectationFailed)
{ {
//If its a post/put/patch request, then read the client html body and send it to server
if (request.HasBody) if (request.HasBody)
{ {
HttpWriter writer = args.WebSession.ServerConnection.StreamWriter; HttpWriter writer = args.WebSession.ServerConnection.StreamWriter;
......
...@@ -87,12 +87,14 @@ namespace Titanium.Web.Proxy ...@@ -87,12 +87,14 @@ namespace Titanium.Web.Proxy
//Write back to client 100-conitinue response if that's what server returned //Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue) if (response.Is100Continue)
{ {
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.Continue, "Continue"); await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue");
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
} }
else if (response.ExpectationFailed) else if (response.ExpectationFailed)
{ {
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed"); await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
} }
...@@ -108,7 +110,8 @@ namespace Titanium.Web.Proxy ...@@ -108,7 +110,8 @@ namespace Titanium.Web.Proxy
else else
{ {
//Write back response status to client //Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription); await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
response.StatusDescription);
await clientStreamWriter.WriteHeadersAsync(response.Headers); await clientStreamWriter.WriteHeadersAsync(response.Headers);
//Write body if exists //Write body if exists
......
...@@ -14,8 +14,9 @@ namespace Titanium.Web.Proxy.Shared ...@@ -14,8 +14,9 @@ namespace Titanium.Web.Proxy.Shared
internal static readonly char[] SemiColonSplit = { ';' }; internal static readonly char[] SemiColonSplit = { ';' };
internal static readonly char[] EqualSplit = { '=' }; internal static readonly char[] EqualSplit = { '=' };
internal static readonly byte[] NewLine = {(byte)'\r', (byte)'\n' }; internal static readonly byte[] NewLine = { (byte)'\r', (byte)'\n' };
public static readonly Regex CNRemoverRegex = new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled); public static readonly Regex CNRemoverRegex =
new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled);
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
using StreamExtended.Helpers; using StreamExtended.Helpers;
...@@ -10,13 +11,11 @@ using Titanium.Web.Proxy.EventArguments; ...@@ -10,13 +11,11 @@ using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Threading;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
partial class ProxyServer partial class ProxyServer
{ {
/// <summary> /// <summary>
/// This is called when this proxy acts as a reverse proxy (like a real http server) /// This is called when this proxy acts as a reverse proxy (like a real http server)
/// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client /// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
...@@ -37,7 +36,7 @@ namespace Titanium.Web.Proxy ...@@ -37,7 +36,7 @@ namespace Titanium.Web.Proxy
{ {
var clientHelloInfo = await SslTools.PeekClientHello(clientStream); var clientHelloInfo = await SslTools.PeekClientHello(clientStream);
var isHttps = clientHelloInfo != null; bool isHttps = clientHelloInfo != null;
string httpsHostName = null; string httpsHostName = null;
if (isHttps) if (isHttps)
...@@ -76,7 +75,8 @@ namespace Titanium.Web.Proxy ...@@ -76,7 +75,8 @@ namespace Titanium.Web.Proxy
} }
catch (Exception e) catch (Exception e)
{ {
ExceptionFunc(new Exception($"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e)); ExceptionFunc(new Exception(
$"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e));
sslStream?.Dispose(); sslStream?.Dispose();
return; return;
} }
......
...@@ -58,7 +58,8 @@ namespace Titanium.Web.Proxy ...@@ -58,7 +58,8 @@ namespace Titanium.Web.Proxy
if (headerName != null) if (headerName != null)
{ {
authHeader = response.Headers.NonUniqueHeaders[headerName] authHeader = response.Headers.NonUniqueHeaders[headerName]
.FirstOrDefault(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase))); .FirstOrDefault(
x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase)));
} }
//check in unique headers //check in unique headers
...@@ -87,7 +88,8 @@ namespace Titanium.Web.Proxy ...@@ -87,7 +88,8 @@ namespace Titanium.Web.Proxy
{ {
string scheme = authSchemes.Contains(authHeader.Value.ToLower()) ? authHeader.Value.ToLower() : null; string scheme = authSchemes.Contains(authHeader.Value.ToLower()) ? authHeader.Value.ToLower() : null;
var expectedAuthState = scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED; var expectedAuthState =
scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED;
if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.RequestId, expectedAuthState)) if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.RequestId, expectedAuthState))
{ {
...@@ -120,7 +122,8 @@ namespace Titanium.Web.Proxy ...@@ -120,7 +122,8 @@ namespace Titanium.Web.Proxy
//challenge value will start with any of the scheme selected //challenge value will start with any of the scheme selected
else else
{ {
scheme = authSchemes.First(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) && scheme = authSchemes.First(x =>
authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) &&
authHeader.Value.Length > x.Length + 1); authHeader.Value.Length > x.Length + 1);
string serverToken = authHeader.Value.Substring(scheme.Length + 1); string serverToken = authHeader.Value.Substring(scheme.Length + 1);
...@@ -157,27 +160,30 @@ namespace Titanium.Web.Proxy ...@@ -157,27 +160,30 @@ namespace Titanium.Web.Proxy
var response = args.WebSession.Response; var response = args.WebSession.Response;
// Strip authentication headers to avoid credentials prompt in client web browser // Strip authentication headers to avoid credentials prompt in client web browser
foreach (var authHeaderName in authHeaderNames) foreach (string authHeaderName in authHeaderNames)
{ {
response.Headers.RemoveHeader(authHeaderName); response.Headers.RemoveHeader(authHeaderName);
} }
// Add custom div to body to clarify that the proxy (not the client browser) failed authentication // Add custom div to body to clarify that the proxy (not the client browser) failed authentication
string authErrorMessage = "<div class=\"inserted-by-proxy\"><h2>NTLM authentication through Titanium.Web.Proxy (" + string authErrorMessage =
"<div class=\"inserted-by-proxy\"><h2>NTLM authentication through Titanium.Web.Proxy (" +
args.ProxyClient.TcpClient.Client.LocalEndPoint + args.ProxyClient.TcpClient.Client.LocalEndPoint +
") failed. Please check credentials.</h2></div>"; ") failed. Please check credentials.</h2></div>";
string originalErrorMessage = "<div class=\"inserted-by-proxy\"><h3>Response from remote web server below.</h3></div><br/>"; string originalErrorMessage =
"<div class=\"inserted-by-proxy\"><h3>Response from remote web server below.</h3></div><br/>";
string body = await args.GetResponseBodyAsString(); string body = await args.GetResponseBodyAsString();
int idx = body.IndexOfIgnoreCase("<body>"); int idx = body.IndexOfIgnoreCase("<body>");
if (idx >= 0) if (idx >= 0)
{ {
var bodyPos = idx + "<body>".Length; int bodyPos = idx + "<body>".Length;
body = body.Insert(bodyPos, authErrorMessage + originalErrorMessage); body = body.Insert(bodyPos, authErrorMessage + originalErrorMessage);
} }
else else
{ {
// Cannot parse response body, replace it // Cannot parse response body, replace it
body = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" + body =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">" + "<html xmlns=\"http://www.w3.org/1999/xhtml\">" +
"<body>" + "<body>" +
authErrorMessage + authErrorMessage +
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
</configuration> </configuration>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.1.4" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.1.4" targetFramework="net45" />
<package id="StreamExtended" version="1.0.141-beta" targetFramework="net45" /> <package id="StreamExtended" version="1.0.141-beta" targetFramework="net45" />
......
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