Commit 06e9dc0a authored by justcoding121's avatar justcoding121 Committed by justcoding121

Merge pull request #271 from honfika/develop

#264 GetSystemUpStreamProxy fixed
parents 0a2199b9 71b961b9
......@@ -31,6 +31,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message);
proxyServer.TrustRootCertificate = true;
proxyServer.ForwardToUpstreamGateway = true;
//optionally set the Certificate Engine
//Under Mono only BouncyCastle will be supported
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp;
namespace Titanium.Web.Proxy.UnitTests
{
[TestClass]
public class SystemProxyTest
{
[TestMethod]
public void CompareProxyAdddressReturendByWebProxyAndWinHttpProxyResolver()
{
var proxyManager = new SystemProxyManager();
try
{
CompareUrls();
proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Http);
CompareUrls();
proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Https);
CompareUrls();
proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.AllHttp);
CompareUrls();
// for this test you need to add a proxy.pac file to a local webserver
//function FindProxyForURL(url, host)
//{
// if (shExpMatch(host, "google.com"))
// {
// return "PROXY 127.0.0.1:8888";
// }
// return "DIRECT";
//}
//proxyManager.SetAutoProxyUrl("http://localhost/proxy.pac");
//CompareUrls();
}
finally
{
proxyManager.RestoreOriginalSettings();
}
}
private void CompareUrls()
{
var webProxy = WebRequest.GetSystemWebProxy();
var resolver = new WinHttpWebProxyFinder();
resolver.LoadFromIE();
resolver.BypassOnLocal = WebProxy.GetDefaultProxy().BypassProxyOnLocal;
CompareProxy(webProxy, resolver, "http://localhost");
CompareProxy(webProxy, resolver, "https://localhost");
string hostName = null;
try
{
hostName = Dns.GetHostName();
}
catch{}
if (hostName != null)
{
CompareProxy(webProxy, resolver, "http://" + hostName);
CompareProxy(webProxy, resolver, "https://" + hostName);
}
CompareProxy(webProxy, resolver, "http://google.com");
CompareProxy(webProxy, resolver, "https://google.com");
CompareProxy(webProxy, resolver, "http://bing.com");
CompareProxy(webProxy, resolver, "https://bing.com");
}
private void CompareProxy(IWebProxy webProxy, WinHttpWebProxyFinder resolver, string url)
{
var uri = new Uri(url);
var expectedProxyUri = webProxy.GetProxy(uri);
var proxy = resolver.GetProxy(uri);
if (expectedProxyUri == uri)
{
// no proxy
Assert.AreEqual(proxy, null);
return;
}
Assert.AreEqual(expectedProxyUri.ToString(), $"http://{proxy.HostName}:{proxy.Port}/");
}
}
}
......@@ -60,6 +60,7 @@
<Compile Include="CertificateManagerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProxyServerTests.cs" />
<Compile Include="SystemProxyTest.cs" />
<Compile Include="WinAuthTests.cs" />
</ItemGroup>
<ItemGroup>
......
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;
}
}
}
......@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.Win32;
// Helper classes for setting system proxy settings
......@@ -54,7 +53,9 @@ namespace Titanium.Web.Proxy.Helpers
internal class HttpSystemProxyValue
{
internal string HostName { get; set; }
internal int Port { get; set; }
internal ProxyProtocolType ProtocolType { get; set; }
public override string ToString()
......@@ -85,14 +86,12 @@ namespace Titanium.Web.Proxy.Helpers
private const string regAutoConfigUrl = "AutoConfigURL";
private const string regProxyEnable = "ProxyEnable";
private const string regProxyServer = "ProxyServer";
private const string regProxyOverride = "ProxyOverride";
internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37;
private bool originalValuesLoaded;
private string originalAutoConfigUrl;
private int? originalProxyEnable;
private string originalProxyServer;
private ProxyInfo originalValues;
public SystemProxyManager()
{
......@@ -131,7 +130,7 @@ namespace Titanium.Web.Proxy.Helpers
PrepareRegistry(reg);
var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0)
{
......@@ -175,7 +174,7 @@ namespace Titanium.Web.Proxy.Helpers
{
var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if (existingSystemProxyValues.Count != 0)
......@@ -212,9 +211,21 @@ namespace Titanium.Web.Proxy.Helpers
}
}
internal void SetAutoProxyUrl(string url)
{
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
reg.SetValue(regAutoConfigUrl, url);
Refresh();
}
}
internal void RestoreOriginalSettings()
{
if (!originalValuesLoaded)
if (originalValues == null)
{
return;
}
......@@ -223,110 +234,80 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
if (originalAutoConfigUrl != null)
var ov = originalValues;
if (ov.AutoConfigUrl != null)
{
reg.SetValue(regAutoConfigUrl, originalAutoConfigUrl);
reg.SetValue(regAutoConfigUrl, ov.AutoConfigUrl);
}
else
{
reg.DeleteValue(regAutoConfigUrl, false);
}
if (originalProxyEnable.HasValue)
if (ov.ProxyEnable.HasValue)
{
reg.SetValue(regProxyEnable, originalProxyEnable.Value);
reg.SetValue(regProxyEnable, ov.ProxyEnable.Value);
}
else
{
reg.DeleteValue(regProxyEnable, false);
}
if (originalProxyServer != null)
if (ov.ProxyServer != null)
{
reg.SetValue(regProxyServer, originalProxyServer);
reg.SetValue(regProxyServer, ov.ProxyServer);
}
else
{
reg.DeleteValue(regProxyServer, false);
}
originalValuesLoaded = false;
if (ov.ProxyOverride != null)
{
reg.SetValue(regProxyOverride, ov.ProxyOverride);
}
else
{
reg.DeleteValue(regProxyOverride, false);
}
originalValues = null;
Refresh();
}
}
private void SaveOriginalProxyConfiguration(RegistryKey reg)
{
originalAutoConfigUrl = reg.GetValue(regAutoConfigUrl) as string;
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)
internal ProxyInfo GetProxyInfoFromRegistry()
{
var result = new List<HttpSystemProxyValue>();
if (string.IsNullOrWhiteSpace(prevServerValue))
return result;
var proxyValues = prevServerValue.Split(';');
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (proxyValues.Length > 0)
{
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null));
}
else
if (reg != null)
{
var parsedValue = ParseProxyValue(prevServerValue);
if (parsedValue != null)
result.Add(parsedValue);
return GetProxyInfoFromRegistry(reg);
}
return result;
return null;
}
/// <summary>
/// Parses the system proxy setting string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private HttpSystemProxyValue ParseProxyValue(string value)
private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg)
{
var tmp = Regex.Replace(value, @"\s+", " ").Trim();
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);
return pi;
}
int equalsIndex = tmp.IndexOf("=", StringComparison.InvariantCulture);
if (equalsIndex >= 0)
private void SaveOriginalProxyConfiguration(RegistryKey reg)
{
if (originalValues != null)
{
string protocolTypeStr = tmp.Substring(0, equalsIndex);
ProxyProtocolType? protocolType = null;
if (protocolTypeStr.Equals("http", StringComparison.InvariantCultureIgnoreCase))
{
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;
}
return null;
originalValues = GetProxyInfoFromRegistry(reg);
}
/// <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;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
......@@ -36,6 +37,8 @@ namespace Titanium.Web.Proxy
/// </summary>
private Action<Exception> exceptionFunc;
private WinHttpWebProxyFinder systemProxyResolver;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
......@@ -61,7 +64,6 @@ namespace Titanium.Web.Proxy
/// </summary>
private SystemProxyManager systemProxySettingsManager { get; }
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
......@@ -498,9 +500,14 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is already running.");
}
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null
&& GetCustomUpStreamHttpsProxyFunc == null)
if (ForwardToUpstreamGateway
&& GetCustomUpStreamHttpProxyFunc == null && GetCustomUpStreamHttpsProxyFunc == null
&& systemProxySettingsManager != null)
{
// Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE();
GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy;
GetCustomUpStreamHttpsProxyFunc = GetSystemUpStreamProxy;
}
......@@ -606,19 +613,8 @@ namespace Titanium.Web.Proxy
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns>
private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgs sessionEventArgs)
{
// Use built-in WebProxy class to handle PAC/WAPD scripts.
var systemProxyResolver = WebRequest.GetSystemWebProxy();
var systemProxyUri = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
// TODO: Apply authorization
var systemProxy = new ExternalProxy
{
HostName = systemProxyUri.Host,
Port = systemProxyUri.Port
};
return Task.FromResult(systemProxy);
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
return Task.FromResult(proxy);
}
......
......@@ -79,8 +79,12 @@
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\BufferPool.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\RunTime.cs" />
<Compile Include="Helpers\WinHttp\WinHttpHandle.cs" />
<Compile Include="Helpers\WinHttp\WinHttpWebProxyFinder.cs" />
<Compile Include="Http\HeaderParser.cs" />
<Compile Include="Http\Responses\GenericResponse.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