Commit 18ff30f2 authored by Honfika's avatar Honfika

#264 GetSystemUpStreamProxy

parent 0a2199b9
...@@ -31,6 +31,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -31,6 +31,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message); proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message);
proxyServer.TrustRootCertificate = true; proxyServer.TrustRootCertificate = true;
proxyServer.ForwardToUpstreamGateway = true;
//optionally set the Certificate Engine //optionally set the Certificate Engine
//Under Mono only BouncyCastle will be supported //Under Mono only BouncyCastle will be supported
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers
{
internal class ProxyInfo
{
public bool? AutoDetect { get; }
public string AutoConfigUrl { get; }
public int? ProxyEnable { get; }
public string ProxyServer { get; }
public string ProxyOverride { get; }
public Dictionary<ProxyProtocolType, HttpSystemProxyValue> Proxies { get; }
public ProxyInfo(bool? autoDetect, string autoConfigUrl, int? proxyEnable, string proxyServer, string proxyOverride)
{
AutoDetect = autoDetect;
AutoConfigUrl = autoConfigUrl;
ProxyEnable = proxyEnable;
ProxyServer = proxyServer;
ProxyOverride = proxyOverride;
if (proxyServer != null)
{
Proxies = GetSystemProxyValues(proxyServer).ToDictionary(x=>x.ProtocolType);
}
}
public static ProxyProtocolType? ParseProtocolType(string protocolTypeStr)
{
if (protocolTypeStr == null)
{
return null;
}
ProxyProtocolType? protocolType = null;
if (protocolTypeStr.Equals("http", StringComparison.InvariantCultureIgnoreCase))
{
protocolType = ProxyProtocolType.Http;
}
else if (protocolTypeStr.Equals("https", StringComparison.InvariantCultureIgnoreCase))
{
protocolType = ProxyProtocolType.Https;
}
return protocolType;
}
/// <summary>
/// Parse the system proxy setting values
/// </summary>
/// <param name="proxyServerValues"></param>
/// <returns></returns>
public static List<HttpSystemProxyValue> GetSystemProxyValues(string proxyServerValues)
{
var result = new List<HttpSystemProxyValue>();
if (string.IsNullOrWhiteSpace(proxyServerValues))
return result;
var proxyValues = proxyServerValues.Split(';');
if (proxyValues.Length > 0)
{
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null));
}
else
{
var parsedValue = ParseProxyValue(proxyServerValues);
if (parsedValue != null)
result.Add(parsedValue);
}
return result;
}
/// <summary>
/// Parses the system proxy setting string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static HttpSystemProxyValue ParseProxyValue(string value)
{
var tmp = Regex.Replace(value, @"\s+", " ").Trim();
int equalsIndex = tmp.IndexOf("=", StringComparison.InvariantCulture);
if (equalsIndex >= 0)
{
string protocolTypeStr = tmp.Substring(0, equalsIndex);
var protocolType = ParseProtocolType(protocolTypeStr);
if (protocolType.HasValue)
{
var endPointParts = tmp.Substring(equalsIndex + 1).Split(':');
return new HttpSystemProxyValue
{
HostName = endPointParts[0],
Port = int.Parse(endPointParts[1]),
ProtocolType = protocolType.Value,
};
}
}
return null;
}
}
}
...@@ -54,7 +54,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -54,7 +54,9 @@ namespace Titanium.Web.Proxy.Helpers
internal class HttpSystemProxyValue internal class HttpSystemProxyValue
{ {
internal string HostName { get; set; } internal string HostName { get; set; }
internal int Port { get; set; } internal int Port { get; set; }
internal ProxyProtocolType ProtocolType { get; set; } internal ProxyProtocolType ProtocolType { get; set; }
public override string ToString() public override string ToString()
...@@ -85,14 +87,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -85,14 +87,12 @@ namespace Titanium.Web.Proxy.Helpers
private const string regAutoConfigUrl = "AutoConfigURL"; private const string regAutoConfigUrl = "AutoConfigURL";
private const string regProxyEnable = "ProxyEnable"; private const string regProxyEnable = "ProxyEnable";
private const string regProxyServer = "ProxyServer"; private const string regProxyServer = "ProxyServer";
private const string regProxyOverride = "ProxyOverride";
internal const int InternetOptionSettingsChanged = 39; internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37; internal const int InternetOptionRefresh = 37;
private bool originalValuesLoaded; private ProxyInfo originalValues;
private string originalAutoConfigUrl;
private int? originalProxyEnable;
private string originalProxyServer;
public SystemProxyManager() public SystemProxyManager()
{ {
...@@ -131,7 +131,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -131,7 +131,7 @@ namespace Titanium.Web.Proxy.Helpers
PrepareRegistry(reg); PrepareRegistry(reg);
var exisitingContent = reg.GetValue(regProxyServer) as string; var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0) if ((protocolType & ProxyProtocolType.Http) != 0)
{ {
...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
var exisitingContent = reg.GetValue(regProxyServer) as string; var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if (existingSystemProxyValues.Count != 0) if (existingSystemProxyValues.Count != 0)
...@@ -214,7 +214,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -214,7 +214,7 @@ namespace Titanium.Web.Proxy.Helpers
internal void RestoreOriginalSettings() internal void RestoreOriginalSettings()
{ {
if (!originalValuesLoaded) if (originalValues == null)
{ {
return; return;
} }
...@@ -223,110 +223,75 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -223,110 +223,75 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
if (originalAutoConfigUrl != null) var ov = originalValues;
if (ov.AutoConfigUrl != null)
{ {
reg.SetValue(regAutoConfigUrl, originalAutoConfigUrl); reg.SetValue(regAutoConfigUrl, ov.AutoConfigUrl);
} }
else else
{ {
reg.DeleteValue(regAutoConfigUrl, false); reg.DeleteValue(regAutoConfigUrl, false);
} }
if (originalProxyEnable.HasValue) if (ov.ProxyEnable.HasValue)
{ {
reg.SetValue(regProxyEnable, originalProxyEnable.Value); reg.SetValue(regProxyEnable, ov.ProxyEnable.Value);
} }
else else
{ {
reg.DeleteValue(regProxyEnable, false); reg.DeleteValue(regProxyEnable, false);
} }
if (originalProxyServer != null) if (ov.ProxyServer != null)
{ {
reg.SetValue(regProxyServer, originalProxyServer); reg.SetValue(regProxyServer, ov.ProxyServer);
} }
else else
{ {
reg.DeleteValue(regProxyServer, false); reg.DeleteValue(regProxyServer, false);
} }
originalValuesLoaded = false; if (ov.ProxyOverride != null)
{
reg.SetValue(regProxyOverride, ov.ProxyOverride);
}
else
{
reg.DeleteValue(regProxyOverride, false);
}
originalValues = null;
Refresh(); Refresh();
} }
} }
private void SaveOriginalProxyConfiguration(RegistryKey reg) internal ProxyInfo GetProxyInfoFromRegistry()
{ {
originalAutoConfigUrl = reg.GetValue(regAutoConfigUrl) as string; var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
originalProxyServer = reg.GetValue(regProxyServer) as string;
originalProxyEnable = reg.GetValue(regProxyEnable) as int?;
originalValuesLoaded = true;
}
/// <summary>
/// Get the current system proxy setting values
/// </summary>
/// <param name="prevServerValue"></param>
/// <returns></returns>
private List<HttpSystemProxyValue> GetSystemProxyValues(string prevServerValue)
{
var result = new List<HttpSystemProxyValue>();
if (string.IsNullOrWhiteSpace(prevServerValue))
return result;
var proxyValues = prevServerValue.Split(';');
if (proxyValues.Length > 0) if (reg != null)
{
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null));
}
else
{ {
var parsedValue = ParseProxyValue(prevServerValue); return GetProxyInfoFromRegistry(reg);
if (parsedValue != null)
result.Add(parsedValue);
} }
return result; return null;
} }
/// <summary> private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg)
/// Parses the system proxy setting string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private HttpSystemProxyValue ParseProxyValue(string value)
{ {
var tmp = Regex.Replace(value, @"\s+", " ").Trim(); var pi = new ProxyInfo(
null,
int equalsIndex = tmp.IndexOf("=", StringComparison.InvariantCulture); reg.GetValue(regAutoConfigUrl) as string,
if (equalsIndex >= 0) reg.GetValue(regProxyEnable) as int?,
{ reg.GetValue(regProxyServer) as string,
string protocolTypeStr = tmp.Substring(0, equalsIndex); reg.GetValue(regProxyOverride) as string);
ProxyProtocolType? protocolType = null;
if (protocolTypeStr.Equals("http", StringComparison.InvariantCultureIgnoreCase)) return pi;
{ }
protocolType = ProxyProtocolType.Http;
}
else if (protocolTypeStr.Equals("https", StringComparison.InvariantCultureIgnoreCase))
{
protocolType = ProxyProtocolType.Https;
}
if (protocolType.HasValue)
{
var endPointParts = tmp.Substring(equalsIndex + 1).Split(':');
return new HttpSystemProxyValue
{
HostName = endPointParts[0],
Port = int.Parse(endPointParts[1]),
ProtocolType = protocolType.Value,
};
}
}
return null; private void SaveOriginalProxyConfiguration(RegistryKey reg)
{
originalValues = GetProxyInfoFromRegistry(reg);
} }
/// <summary> /// <summary>
......
using System;
using System.Runtime.InteropServices;
// Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers.WinHttp
{
internal partial class NativeMethods
{
internal static class WinHttp
{
[DllImport("winhttp.dll", SetLastError = true)]
internal static extern bool WinHttpGetIEProxyConfigForCurrentUser(ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern WinHttpHandle WinHttpOpen(string userAgent, AccessType accessType, string proxyName, string proxyBypass, int dwFlags);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout, int connectTimeout, int sendTimeout, int receiveTimeout);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpGetProxyForUrl(WinHttpHandle session, string url, [In] ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out WINHTTP_PROXY_INFO proxyInfo);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpCloseHandle(IntPtr httpSession);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
{
public bool AutoDetect;
public IntPtr AutoConfigUrl;
public IntPtr Proxy;
public IntPtr ProxyBypass;
}
[Flags]
internal enum AutoProxyFlags
{
AutoDetect = 1,
AutoProxyConfigUrl = 2,
RunInProcess = 65536,
RunOutProcessOnly = 131072
}
internal enum AccessType
{
DefaultProxy = 0,
NoProxy = 1,
NamedProxy = 3
}
[Flags]
internal enum AutoDetectType
{
None = 0,
Dhcp = 1,
DnsA = 2
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct WINHTTP_AUTOPROXY_OPTIONS
{
public AutoProxyFlags Flags;
public AutoDetectType AutoDetectFlags;
[MarshalAs(UnmanagedType.LPWStr)] public string AutoConfigUrl;
private IntPtr lpvReserved;
private int dwReserved;
public bool AutoLogonIfChallenged;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct WINHTTP_PROXY_INFO
{
public AccessType AccessType;
public IntPtr Proxy;
public IntPtr ProxyBypass;
}
internal enum ErrorCodes
{
Success = 0,
OutOfHandles = 12001,
Timeout = 12002,
InternalError = 12004,
InvalidUrl = 12005,
UnrecognizedScheme = 12006,
NameNotResolved = 12007,
InvalidOption = 12009,
OptionNotSettable = 12011,
Shutdown = 12012,
LoginFailure = 12015,
OperationCancelled = 12017,
IncorrectHandleType = 12018,
IncorrectHandleState = 12019,
CannotConnect = 12029,
ConnectionError = 12030,
ResendRequest = 12032,
SecureCertDateInvalid = 12037,
SecureCertCNInvalid = 12038,
AuthCertNeeded = 12044,
SecureInvalidCA = 12045,
SecureCertRevFailed = 12057,
CannotCallBeforeOpen = 12100,
CannotCallBeforeSend = 12101,
CannotCallAfterSend = 12102,
CannotCallAfterOpen = 12103,
HeaderNotFound = 12150,
InvalidServerResponse = 12152,
InvalidHeader = 12153,
InvalidQueryRequest = 12154,
HeaderAlreadyExists = 12155,
RedirectFailed = 12156,
SecureChannelError = 12157,
BadAutoProxyScript = 12166,
UnableToDownloadScript = 12167,
SecureInvalidCert = 12169,
SecureCertRevoked = 12170,
NotInitialized = 12172,
SecureFailure = 12175,
AutoProxyServiceError = 12178,
SecureCertWrongUsage = 12179,
AudodetectionFailed = 12180,
HeaderCountExceeded = 12181,
HeaderSizeOverflow = 12182,
ChunkedEncodingHeaderSizeOverflow = 12183,
ResponseDrainOverflow = 12184,
ClientCertNoPrivateKey = 12185,
ClientCertNoAccessPrivateKey = 12186
}
}
}
}
using System;
using System.Runtime.InteropServices;
namespace Titanium.Web.Proxy.Helpers.WinHttp
{
internal class WinHttpHandle : SafeHandle
{
public WinHttpHandle() : base(IntPtr.Zero, true)
{
}
protected override bool ReleaseHandle()
{
return NativeMethods.WinHttp.WinHttpCloseHandle(handle);
}
public override bool IsInvalid => handle == IntPtr.Zero;
}
}
\ No newline at end of file
This diff is collapsed.
...@@ -9,6 +9,7 @@ using System.Threading; ...@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
...@@ -36,6 +37,8 @@ namespace Titanium.Web.Proxy ...@@ -36,6 +37,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private Action<Exception> exceptionFunc; private Action<Exception> exceptionFunc;
private WinHttpWebProxyFinder systemProxyResolver;
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
...@@ -61,7 +64,6 @@ namespace Titanium.Web.Proxy ...@@ -61,7 +64,6 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private SystemProxyManager systemProxySettingsManager { get; } private SystemProxyManager systemProxySettingsManager { get; }
/// <summary> /// <summary>
/// Set firefox to use default system proxy /// Set firefox to use default system proxy
/// </summary> /// </summary>
...@@ -498,9 +500,14 @@ namespace Titanium.Web.Proxy ...@@ -498,9 +500,14 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is already running."); throw new Exception("Proxy is already running.");
} }
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null if (ForwardToUpstreamGateway
&& GetCustomUpStreamHttpsProxyFunc == null) && GetCustomUpStreamHttpProxyFunc == null && GetCustomUpStreamHttpsProxyFunc == null
&& systemProxySettingsManager != null)
{ {
// Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE();
GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy; GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy;
GetCustomUpStreamHttpsProxyFunc = GetSystemUpStreamProxy; GetCustomUpStreamHttpsProxyFunc = GetSystemUpStreamProxy;
} }
...@@ -606,19 +613,46 @@ namespace Titanium.Web.Proxy ...@@ -606,19 +613,46 @@ namespace Titanium.Web.Proxy
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns> /// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns>
private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgs sessionEventArgs) private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgs sessionEventArgs)
{ {
// Use built-in WebProxy class to handle PAC/WAPD scripts. var uri = sessionEventArgs.WebSession.Request.RequestUri;
var systemProxyResolver = WebRequest.GetSystemWebProxy(); IList<string> proxies;
if (systemProxyResolver.GetProxies(uri, out proxies) && proxies != null)
{
string proxy = proxies[0];
int port = 80;
if (proxy.Contains(":"))
{
var parts = proxy.Split(new[] { ':' }, 2);
proxy = parts[0];
port = int.Parse(parts[1]);
}
// TODO: Apply authorization
var systemProxy = new ExternalProxy
{
HostName = proxy,
Port = port,
};
var systemProxyUri = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri); return Task.FromResult(systemProxy);
}
// TODO: Apply authorization var protocolType = ProxyInfo.ParseProtocolType(uri.Scheme);
var systemProxy = new ExternalProxy if (protocolType.HasValue)
{ {
HostName = systemProxyUri.Host, HttpSystemProxyValue value = null;
Port = systemProxyUri.Port if (systemProxyResolver.ProxyInfo?.Proxies?.TryGetValue(protocolType.Value, out value) == true)
}; {
var systemProxy = new ExternalProxy
{
HostName = value.HostName,
Port = value.Port,
};
return Task.FromResult(systemProxy);
}
}
return Task.FromResult(systemProxy); return Task.FromResult((ExternalProxy)null);
} }
......
...@@ -79,8 +79,12 @@ ...@@ -79,8 +79,12 @@
<Compile Include="Extensions\StringExtensions.cs" /> <Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\BufferPool.cs" /> <Compile Include="Helpers\BufferPool.cs" />
<Compile Include="Helpers\CustomBufferedStream.cs" /> <Compile Include="Helpers\CustomBufferedStream.cs" />
<Compile Include="Helpers\ProxyInfo.cs" />
<Compile Include="Helpers\WinHttp\NativeMethods.WinHttp.cs" />
<Compile Include="Helpers\Network.cs" /> <Compile Include="Helpers\Network.cs" />
<Compile Include="Helpers\RunTime.cs" /> <Compile Include="Helpers\RunTime.cs" />
<Compile Include="Helpers\WinHttp\WinHttpHandle.cs" />
<Compile Include="Helpers\WinHttp\WinHttpWebProxyFinder.cs" />
<Compile Include="Http\HeaderParser.cs" /> <Compile Include="Http\HeaderParser.cs" />
<Compile Include="Http\Responses\GenericResponse.cs" /> <Compile Include="Http\Responses\GenericResponse.cs" />
<Compile Include="Network\CachedCertificate.cs" /> <Compile Include="Network\CachedCertificate.cs" />
......
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