Commit e1543300 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #275 from justcoding121/beta

Stable
parents acad670d ac2877be
......@@ -202,4 +202,4 @@ FakesAssemblies/
*.plg
# Visual Studio 6 workspace options file
*.opt
\ No newline at end of file
*.opt
......@@ -14,10 +14,6 @@ namespace Titanium.Web.Proxy.Examples.Basic
//fix console hang due to QuickEdit mode
ConsoleHelper.DisableQuickEditMode();
//On Console exit make sure we also exit the proxy
NativeMethods.Handler = ConsoleEventCallback;
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
//Start proxy controller
controller.StartProxy();
......@@ -27,34 +23,5 @@ namespace Titanium.Web.Proxy.Examples.Basic
controller.Stop();
}
private static bool ConsoleEventCallback(int eventType)
{
if (eventType != 2) return false;
try
{
controller.Stop();
}
catch
{
// ignored
}
return false;
}
}
internal static class NativeMethods
{
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
// Pinvoke
internal delegate bool ConsoleEventDelegate(int eventType);
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Examples.Basic
......@@ -15,8 +17,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
//share requestBody outside handlers
//Using a dictionary is not a good idea since it can cause memory overflow
//ideally the data should be moved out of memory
//private readonly Dictionary<Guid, string> requestBodyHistory
// = new Dictionary<Guid, string>();
//private readonly IDictionary<Guid, string> requestBodyHistory
// = new ConcurrentDictionary<Guid, string>();
public ProxyTestController()
{
......@@ -29,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
......@@ -92,8 +95,9 @@ namespace Titanium.Web.Proxy.Examples.Basic
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
//Only explicit proxies can be set as system proxy!
proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
//proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
//proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
}
public void Stop()
......
......@@ -112,7 +112,8 @@ Sample request and response event handlers
```csharp
//To access requestBody from OnResponse handler
private Dictionary<Guid, string> requestBodyHistory = new Dictionary<Guid, string>();
private IDictionary<Guid, string> requestBodyHistory
= new ConcurrentDictionary<Guid, string>();
public async Task OnRequest(object sender, SessionEventArgs e)
{
......
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();
proxyManager.SetProxyOverride("<-loopback>");
CompareUrls();
proxyManager.SetProxyOverride("<local>");
CompareUrls();
proxyManager.SetProxyOverride("yahoo.com");
CompareUrls();
proxyManager.SetProxyOverride("*.local");
CompareUrls();
proxyManager.SetProxyOverride("http://*.local");
CompareUrls();
proxyManager.SetProxyOverride("<-loopback>;*.local");
CompareUrls();
proxyManager.SetProxyOverride("<-loopback>;*.local;<local>");
CompareUrls();
}
finally
{
proxyManager.RestoreOriginalSettings();
}
}
private void CompareUrls()
{
var webProxy = WebRequest.GetSystemWebProxy();
var resolver = new WinHttpWebProxyFinder();
resolver.LoadFromIE();
CompareProxy(webProxy, resolver, "http://127.0.0.1");
CompareProxy(webProxy, resolver, "https://127.0.0.1");
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");
CompareProxy(webProxy, resolver, "http://yahoo.com");
CompareProxy(webProxy, resolver, "https://yahoo.com");
CompareProxy(webProxy, resolver, "http://test.local");
CompareProxy(webProxy, resolver, "https://test.local");
}
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 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;
AutoConfigUrl = autoConfigUrl;
ProxyEnable = proxyEnable;
ProxyServer = proxyServer;
ProxyOverride = proxyOverride;
if (proxyServer != null)
{
Proxies = GetSystemProxyValues(proxyServer).ToDictionary(x=>x.ProtocolType);
}
if (proxyOverride != null)
{
var overrides = proxyOverride.Split(';');
var overrides2 = new List<string>();
foreach (var overrideHost in overrides)
{
if (overrideHost == "<-loopback>")
{
BypassLoopback = true;
}
else if (overrideHost == "<local>")
{
BypassOnLocal = true;
}
else
{
overrides2.Add(BypassStringEscape(overrideHost));
}
}
if (overrides2.Count > 0)
{
BypassList = overrides2.ToArray();
}
Proxies = GetSystemProxyValues(proxyServer).ToDictionary(x => x.ProtocolType);
}
}
private static string BypassStringEscape(string rawString)
{
Match match = new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Match(rawString);
string empty1;
string rawString1;
string empty2;
if (match.Success)
{
empty1 = match.Groups["scheme"].Value;
rawString1 = match.Groups["host"].Value;
empty2 = match.Groups["port"].Value;
}
else
{
empty1 = string.Empty;
rawString1 = rawString;
empty2 = string.Empty;
}
string str1 = ConvertRegexReservedChars(empty1);
string str2 = ConvertRegexReservedChars(rawString1);
string str3 = ConvertRegexReservedChars(empty2);
if (str1 == string.Empty)
str1 = "(?:.*://)?";
if (str3 == string.Empty)
str3 = "(?::[0-9]{1,5})?";
return "^" + str1 + str2 + str3 + "$";
}
private static string ConvertRegexReservedChars(string rawString)
{
if (rawString.Length == 0)
return rawString;
var stringBuilder = new StringBuilder();
foreach (char ch in rawString)
{
if ("#$()+.?[\\^{|".IndexOf(ch) != -1)
stringBuilder.Append('\\');
else if (ch == 42)
stringBuilder.Append('.');
stringBuilder.Append(ch);
}
return stringBuilder.ToString();
}
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,16 +2,33 @@
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
namespace Titanium.Web.Proxy.Helpers
{
internal enum ProxyProtocolType
[Flags]
public enum ProxyProtocolType
{
Http,
Https,
/// <summary>
/// The none
/// </summary>
None = 0,
/// <summary>
/// HTTP
/// </summary>
Http = 1,
/// <summary>
/// HTTPS
/// </summary>
Https = 2,
/// <summary>
/// Both HTTP and HTTPS
/// </summary>
AllHttp = Http | Https,
}
internal partial class NativeMethods
......@@ -19,17 +36,44 @@ namespace Titanium.Web.Proxy.Helpers
[DllImport("wininet.dll")]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
int dwBufferLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow();
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
// Pinvoke
internal delegate bool ConsoleEventDelegate(int eventType);
}
internal class HttpSystemProxyValue
{
internal string HostName { get; set; }
internal int Port { get; set; }
internal bool IsHttps { get; set; }
internal ProxyProtocolType ProtocolType { get; set; }
public override string ToString()
{
return $"{(IsHttps ? "https" : "http")}={HostName}:{Port}";
string protocol;
switch (ProtocolType)
{
case ProxyProtocolType.Http:
protocol = "http";
break;
case ProxyProtocolType.Https:
protocol = "https";
break;
default:
throw new Exception("Unsupported protocol type");
}
return $"{protocol}={HostName}:{Port}";
}
}
......@@ -38,90 +82,118 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
internal class SystemProxyManager
{
private const string regKeyInternetSettings = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
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;
internal void SetHttpProxy(string hostname, int port)
{
SetProxy(hostname, port, ProxyProtocolType.Http);
}
private ProxyInfo originalValues;
/// <summary>
/// Remove the http proxy setting from current machine
/// </summary>
internal void RemoveHttpProxy()
public SystemProxyManager()
{
RemoveProxy(ProxyProtocolType.Http);
AppDomain.CurrentDomain.ProcessExit += (o, args) => RestoreOriginalSettings();
if (Environment.UserInteractive && NativeMethods.GetConsoleWindow() != IntPtr.Zero)
{
NativeMethods.Handler = eventType =>
{
if (eventType != 2)
{
return false;
}
RestoreOriginalSettings();
return false;
};
//On Console exit make sure we also exit the proxy
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
}
}
/// <summary>
/// Set the HTTPS proxy server for current machine
/// Set the HTTP and/or HTTPS proxy server for current machine
/// </summary>
/// <param name="hostname"></param>
/// <param name="port"></param>
internal void SetHttpsProxy(string hostname, int port)
{
SetProxy(hostname, port, ProxyProtocolType.Https);
}
/// <summary>
/// Removes the https proxy setting to nothing
/// </summary>
internal void RemoveHttpsProxy()
{
RemoveProxy(ProxyProtocolType.Https);
}
private void SetProxy(string hostname, int port, ProxyProtocolType protocolType)
/// <param name="protocolType"></param>
internal void SetProxy(string hostname, int port, ProxyProtocolType protocolType)
{
var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
PrepareRegistry(reg);
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => protocolType == ProxyProtocolType.Https ? x.IsHttps : !x.IsHttps);
existingSystemProxyValues.Add(new HttpSystemProxyValue
var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0)
{
HostName = hostname,
IsHttps = protocolType == ProxyProtocolType.Https,
Port = port
});
existingSystemProxyValues.Add(new HttpSystemProxyValue
{
HostName = hostname,
ProtocolType = ProxyProtocolType.Http,
Port = port
});
}
reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
if ((protocolType & ProxyProtocolType.Https) != 0)
{
existingSystemProxyValues.Add(new HttpSystemProxyValue
{
HostName = hostname,
ProtocolType = ProxyProtocolType.Https,
Port = port
});
}
reg.DeleteValue(regAutoConfigUrl, false);
reg.SetValue(regProxyEnable, 1);
reg.SetValue(regProxyServer, string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
Refresh();
Refresh();
}
}
private void RemoveProxy(ProxyProtocolType protocolType)
/// <summary>
/// Remove the HTTP and/or HTTPS proxy setting from current machine
/// </summary>
internal void RemoveProxy(ProxyProtocolType protocolType, bool saveOriginalConfig = true)
{
var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg?.GetValue("ProxyServer") != null)
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (reg != null)
{
var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => protocolType == ProxyProtocolType.Https ? x.IsHttps : !x.IsHttps);
if (existingSystemProxyValues.Count != 0)
if (saveOriginalConfig)
{
reg.SetValue("ProxyEnable", 1);
reg.SetValue("ProxyServer", string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
SaveOriginalProxyConfiguration(reg);
}
else
if (reg.GetValue(regProxyServer) != null)
{
reg.SetValue("ProxyEnable", 0);
reg.SetValue("ProxyServer", string.Empty);
var exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if (existingSystemProxyValues.Count != 0)
{
reg.SetValue(regProxyEnable, 1);
reg.SetValue(regProxyServer, string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
}
else
{
reg.SetValue(regProxyEnable, 0);
reg.SetValue(regProxyServer, string.Empty);
}
}
}
Refresh();
Refresh();
}
}
/// <summary>
......@@ -129,83 +201,144 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
internal void DisableAllProxy()
{
var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (reg != null)
{
reg.SetValue("ProxyEnable", 0);
reg.SetValue("ProxyServer", string.Empty);
}
SaveOriginalProxyConfiguration(reg);
Refresh();
reg.SetValue(regProxyEnable, 0);
reg.SetValue(regProxyServer, string.Empty);
Refresh();
}
}
/// <summary>
/// Get the current system proxy setting values
/// </summary>
/// <param name="prevServerValue"></param>
/// <returns></returns>
private List<HttpSystemProxyValue> GetSystemProxyValues(string prevServerValue)
internal void SetAutoProxyUrl(string url)
{
var result = new List<HttpSystemProxyValue>();
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (string.IsNullOrWhiteSpace(prevServerValue))
return result;
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
reg.SetValue(regAutoConfigUrl, url);
Refresh();
}
}
var proxyValues = prevServerValue.Split(';');
internal void SetProxyOverride(string proxyOverride)
{
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (proxyValues.Length > 0)
if (reg != null)
{
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null));
SaveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyOverride, proxyOverride);
Refresh();
}
else
}
internal void RestoreOriginalSettings()
{
if (originalValues == null)
{
var parsedValue = ParseProxyValue(prevServerValue);
if (parsedValue != null)
result.Add(parsedValue);
return;
}
return result;
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (reg != null)
{
var ov = originalValues;
if (ov.AutoConfigUrl != null)
{
reg.SetValue(regAutoConfigUrl, ov.AutoConfigUrl);
}
else
{
reg.DeleteValue(regAutoConfigUrl, false);
}
if (ov.ProxyEnable.HasValue)
{
reg.SetValue(regProxyEnable, ov.ProxyEnable.Value);
}
else
{
reg.DeleteValue(regProxyEnable, false);
}
if (ov.ProxyServer != null)
{
reg.SetValue(regProxyServer, ov.ProxyServer);
}
else
{
reg.DeleteValue(regProxyServer, false);
}
if (ov.ProxyOverride != null)
{
reg.SetValue(regProxyOverride, ov.ProxyOverride);
}
else
{
reg.DeleteValue(regProxyOverride, false);
}
originalValues = null;
Refresh();
}
}
/// <summary>
/// Parses the system proxy setting string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private HttpSystemProxyValue ParseProxyValue(string value)
internal ProxyInfo GetProxyInfoFromRegistry()
{
var tmp = Regex.Replace(value, @"\s+", " ").Trim().ToLower();
var reg = Registry.CurrentUser.OpenSubKey(regKeyInternetSettings, true);
if (tmp.StartsWith("http=") || tmp.StartsWith("https="))
if (reg != null)
{
var endPoint = tmp.Substring(5);
return new HttpSystemProxyValue
{
HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]),
IsHttps = tmp.StartsWith("https=")
};
return GetProxyInfoFromRegistry(reg);
}
return null;
}
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,
reg.GetValue(regProxyOverride) as string);
return pi;
}
private void SaveOriginalProxyConfiguration(RegistryKey reg)
{
if (originalValues != null)
{
return;
}
originalValues = GetProxyInfoFromRegistry(reg);
}
/// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary>
/// <param name="reg"></param>
private static void PrepareRegistry(RegistryKey reg)
{
if (reg.GetValue("ProxyEnable") == null)
if (reg.GetValue(regProxyEnable) == null)
{
reg.SetValue("ProxyEnable", 0);
reg.SetValue(regProxyEnable, 0);
}
if (reg.GetValue("ProxyServer") == null || reg.GetValue("ProxyEnable") as string == "0")
if (reg.GetValue(regProxyServer) == null || reg.GetValue(regProxyEnable) as int? == 0)
{
reg.SetValue("ProxyServer", string.Empty);
reg.SetValue(regProxyServer, string.Empty);
}
}
......
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
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Helpers.WinHttp
{
internal sealed class WinHttpWebProxyFinder : IDisposable
{
private readonly WinHttpHandle session;
private bool autoDetectFailed;
private AutoWebProxyState state;
public ICredentials Credentials { get; set; }
public ProxyInfo ProxyInfo { get; internal set; }
public bool BypassLoopback { get; internal set; }
public bool BypassOnLocal { get; internal set; }
public Uri AutomaticConfigurationScript { get; internal set; }
public bool AutomaticallyDetectSettings { get; internal set; }
private WebProxy Proxy { get; set; }
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 bool GetAutoProxies(Uri destination, out IList<string> proxyList)
{
proxyList = null;
if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme)
return false;
string proxyListString = null;
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed)
{
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, null, out proxyListString);
autoDetectFailed = IsErrorFatalForAutoDetect(errorCode);
if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme)
{
state = AutoWebProxyState.UnrecognizedScheme;
return false;
}
}
if (AutomaticConfigurationScript != null && IsRecoverableAutoProxyError(errorCode))
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, AutomaticConfigurationScript, out proxyListString);
state = GetStateFromErrorCode(errorCode);
if (state != AutoWebProxyState.Completed)
return false;
if (!string.IsNullOrEmpty(proxyListString))
{
proxyListString = RemoveWhitespaces(proxyListString);
proxyList = proxyListString.Split(';');
}
return true;
}
public ExternalProxy GetProxy(Uri destination)
{
IList<string> proxies;
if (GetAutoProxies(destination, out proxies))
{
if (proxies == null)
{
return 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,
};
return systemProxy;
}
if (Proxy?.IsBypassed(destination) == true)
return null;
var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme);
if (protocolType.HasValue)
{
HttpSystemProxyValue value = null;
if (ProxyInfo?.Proxies?.TryGetValue(protocolType.Value, out value) == true)
{
var systemProxy = new ExternalProxy
{
HostName = value.HostName,
Port = value.Port,
};
return systemProxy;
}
}
return null;
}
public void LoadFromIE()
{
var pi = GetProxyInfo();
ProxyInfo = pi;
AutomaticallyDetectSettings = pi.AutoDetect == true;
AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl);
BypassLoopback = pi.BypassLoopback;
BypassOnLocal = pi.BypassOnLocal;
Proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList);
}
private ProxyInfo GetProxyInfo()
{
var proxyConfig = new NativeMethods.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG();
RuntimeHelpers.PrepareConstrainedRegions();
try
{
ProxyInfo result;
if (NativeMethods.WinHttp.WinHttpGetIEProxyConfigForCurrentUser(ref proxyConfig))
{
result = new ProxyInfo(
proxyConfig.AutoDetect,
Marshal.PtrToStringUni(proxyConfig.AutoConfigUrl),
null,
Marshal.PtrToStringUni(proxyConfig.Proxy),
Marshal.PtrToStringUni(proxyConfig.ProxyBypass));
}
else
{
if (Marshal.GetLastWin32Error() == 8)
throw new OutOfMemoryException();
result = new ProxyInfo(true, null, null, null, null);
}
return result;
}
finally
{
Marshal.FreeHGlobal(proxyConfig.Proxy);
Marshal.FreeHGlobal(proxyConfig.ProxyBypass);
Marshal.FreeHGlobal(proxyConfig.AutoConfigUrl);
}
}
public void Reset()
{
state = AutoWebProxyState.Uninitialized;
autoDetectFailed = false;
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing || session == null || session.IsInvalid)
return;
session.Close();
}
private int GetAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString)
{
int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
autoProxyOptions.AutoLogonIfChallenged = false;
if (scriptLocation == null)
{
autoProxyOptions.Flags = NativeMethods.WinHttp.AutoProxyFlags.AutoDetect;
autoProxyOptions.AutoConfigUrl = null;
autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.Dhcp | NativeMethods.WinHttp.AutoDetectType.DnsA;
}
else
{
autoProxyOptions.Flags = NativeMethods.WinHttp.AutoProxyFlags.AutoProxyConfigUrl;
autoProxyOptions.AutoConfigUrl = scriptLocation.ToString();
autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.None;
}
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{
num = GetLastWin32Error();
if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null)
{
autoProxyOptions.AutoLogonIfChallenged = true;
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
num = GetLastWin32Error();
}
}
return num;
}
private bool WinHttpGetProxyForUrl(string destination, ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString)
{
proxyListString = null;
bool flag;
var proxyInfo = new NativeMethods.WinHttp.WINHTTP_PROXY_INFO();
RuntimeHelpers.PrepareConstrainedRegions();
try
{
flag = NativeMethods.WinHttp.WinHttpGetProxyForUrl(session, destination, ref autoProxyOptions, out proxyInfo);
if (flag)
proxyListString = Marshal.PtrToStringUni(proxyInfo.Proxy);
}
finally
{
Marshal.FreeHGlobal(proxyInfo.Proxy);
Marshal.FreeHGlobal(proxyInfo.ProxyBypass);
}
return flag;
}
private static int GetLastWin32Error()
{
int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error == 8)
throw new OutOfMemoryException();
return lastWin32Error;
}
private static bool IsRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode)
{
switch (errorCode)
{
case NativeMethods.WinHttp.ErrorCodes.AutoProxyServiceError:
case NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed:
case NativeMethods.WinHttp.ErrorCodes.BadAutoProxyScript:
case NativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript:
case NativeMethods.WinHttp.ErrorCodes.LoginFailure:
case NativeMethods.WinHttp.ErrorCodes.OperationCancelled:
case NativeMethods.WinHttp.ErrorCodes.Timeout:
case NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme:
return true;
default:
return false;
}
}
private static AutoWebProxyState GetStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode)
{
if (errorCode == 0L)
return AutoWebProxyState.Completed;
switch (errorCode)
{
case NativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript:
return AutoWebProxyState.DownloadFailure;
case NativeMethods.WinHttp.ErrorCodes.AutoProxyServiceError:
case NativeMethods.WinHttp.ErrorCodes.InvalidUrl:
case NativeMethods.WinHttp.ErrorCodes.BadAutoProxyScript:
return AutoWebProxyState.Completed;
case NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed:
return AutoWebProxyState.DiscoveryFailure;
case NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme:
return AutoWebProxyState.UnrecognizedScheme;
default:
return AutoWebProxyState.CompilationFailure;
}
}
private static string RemoveWhitespaces(string value)
{
var stringBuilder = new StringBuilder();
foreach (char c in value)
{
if (!char.IsWhiteSpace(c))
stringBuilder.Append(c);
}
return stringBuilder.ToString();
}
private static bool IsErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode)
{
switch (errorCode)
{
case NativeMethods.WinHttp.ErrorCodes.BadAutoProxyScript:
case NativeMethods.WinHttp.ErrorCodes.AutoProxyServiceError:
case NativeMethods.WinHttp.ErrorCodes.Success:
case NativeMethods.WinHttp.ErrorCodes.InvalidUrl:
return false;
default:
return true;
}
}
private enum AutoWebProxyState
{
Uninitialized,
DiscoveryFailure,
DownloadFailure,
CompilationFailure,
UnrecognizedScheme,
Completed,
}
}
}
\ No newline at end of file
......@@ -80,17 +80,14 @@ namespace Titanium.Web.Proxy.Http
var requestLines = new StringBuilder();
//prepare the request & headers
if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false || ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps)
{
requestLines.AppendLine($"{Request.Method} {Request.RequestUri.AbsoluteUri} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
}
else
{
requestLines.AppendLine($"{Request.Method} {Request.RequestUri.PathAndQuery} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
}
requestLines.AppendLine($"{Request.Method} {Request.RequestUri.PathAndQuery} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
//Send Authentication to Upstream proxy if needed
if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false && !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName) && ServerConnection.UpStreamHttpProxy.Password != null)
if (ServerConnection.UpStreamHttpProxy != null
&& ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName)
&& ServerConnection.UpStreamHttpProxy.Password != null)
{
requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(
......
......@@ -33,9 +33,11 @@ namespace Titanium.Web.Proxy.Http
public bool HasBody => Method == "POST" || Method == "PUT" || Method == "PATCH";
/// <summary>
/// Request Http hostanem
/// Http hostname header value if exists
/// Note: Changing this does NOT change host in RequestUri
/// Users can set new RequestUri separately
/// </summary>
internal string Host
public string Host
{
get
{
......@@ -57,9 +59,9 @@ namespace Titanium.Web.Proxy.Http
}
/// <summary>
/// Request content encoding
/// Content encoding header value
/// </summary>
internal string ContentEncoding
public string ContentEncoding
{
get
{
......@@ -231,7 +233,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Encoding for this request
/// </summary>
internal Encoding Encoding => this.GetEncoding();
public Encoding Encoding => this.GetEncoding();
/// <summary>
/// Terminates the underlying Tcp Connection to client after current request
......@@ -261,7 +263,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Does this request has an upgrade to websocket header?
/// </summary>
internal bool UpgradeToWebSocket
public bool UpgradeToWebSocket
{
get
{
......
......@@ -23,12 +23,15 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public string ResponseStatusDescription { get; set; }
internal Encoding Encoding => this.GetResponseCharacterEncoding();
/// <summary>
/// Encoding used in response
/// </summary>
public Encoding Encoding => this.GetResponseCharacterEncoding();
/// <summary>
/// Content encoding for this response
/// </summary>
internal string ContentEncoding
public string ContentEncoding
{
get
{
......@@ -41,12 +44,15 @@ namespace Titanium.Web.Proxy.Http
}
}
internal Version HttpVersion { get; set; }
/// <summary>
/// Http version
/// </summary>
public Version HttpVersion { get; set; }
/// <summary>
/// Keep the connection alive?
/// </summary>
internal bool ResponseKeepAlive
public bool ResponseKeepAlive
{
get
{
......@@ -89,7 +95,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Length of response body
/// </summary>
internal long ContentLength
public long ContentLength
{
get
{
......@@ -142,7 +148,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Response transfer-encoding is chunked?
/// </summary>
internal bool IsChunked
public bool IsChunked
{
get
{
......@@ -198,14 +204,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public Dictionary<string, List<HttpHeader>> NonUniqueResponseHeaders { get; set; }
/// <summary>
/// Response network stream
/// </summary>
public Stream ResponseStream { get; set; }
/// <summary>
/// response body contenst as byte array
/// response body content as byte array
/// </summary>
internal byte[] ResponseBody { get; set; }
......
......@@ -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>
......@@ -287,9 +289,11 @@ namespace Titanium.Web.Proxy
ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory();
systemProxySettingsManager = new SystemProxyManager();
if (!RunTime.IsRunningOnMono)
{
systemProxySettingsManager = new SystemProxyManager();
}
CertificateManager = new CertificateManager(ExceptionFunc);
if (rootCertificateName != null)
{
......@@ -348,24 +352,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint)
{
if (RunTime.IsRunningOnMono)
{
throw new Exception("Mono Runtime do not support system proxy settings.");
}
ValidateEndPointAsSystemProxy(endPoint);
//clear any settings previously added
ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpProxy = false);
systemProxySettingsManager.SetHttpProxy(
Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(), endPoint.Port);
endPoint.IsSystemHttpProxy = true;
firefoxProxySettingsManager.UseSystemProxy();
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTP Proxy", endPoint.IpAddress, endPoint.Port);
SetAsSystemProxy(endPoint, ProxyProtocolType.Http);
}
......@@ -374,6 +361,16 @@ namespace Titanium.Web.Proxy
/// </summary>
/// <param name="endPoint"></param>
public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint)
{
SetAsSystemProxy(endPoint, ProxyProtocolType.Https);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
/// <param name="protocolType"></param>
public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType)
{
if (RunTime.IsRunningOnMono)
{
......@@ -382,33 +379,73 @@ namespace Titanium.Web.Proxy
ValidateEndPointAsSystemProxy(endPoint);
if (!endPoint.EnableSsl)
bool isHttp = (protocolType & ProxyProtocolType.Http) > 0;
bool isHttps = (protocolType & ProxyProtocolType.Https) > 0;
if (isHttps)
{
throw new Exception("Endpoint do not support Https connections");
if (!endPoint.EnableSsl)
{
throw new Exception("Endpoint do not support Https connections");
}
EnsureRootCertificate();
//If certificate was trusted by the machine
if (!CertificateManager.CertValidated)
{
protocolType = protocolType & ~ProxyProtocolType.Https;
isHttps = false;
}
}
//clear any settings previously added
ProxyEndPoints.OfType<ExplicitProxyEndPoint>()
.ToList()
.ForEach(x => x.IsSystemHttpsProxy = false);
EnsureRootCertificate();
if (isHttp)
{
ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpProxy = false);
}
//If certificate was trusted by the machine
if (CertificateManager.CertValidated)
if (isHttps)
{
systemProxySettingsManager.SetHttpsProxy(
Equals(endPoint.IpAddress, IPAddress.Any) |
Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
endPoint.Port);
ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpsProxy = false);
}
systemProxySettingsManager.SetProxy(
Equals(endPoint.IpAddress, IPAddress.Any) |
Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
endPoint.Port,
protocolType);
endPoint.IsSystemHttpsProxy = true;
if (isHttp)
{
endPoint.IsSystemHttpsProxy = true;
}
if (isHttps)
{
endPoint.IsSystemHttpsProxy = true;
}
firefoxProxySettingsManager.UseSystemProxy();
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", endPoint.IpAddress, endPoint.Port);
string proxyType = null;
switch (protocolType)
{
case ProxyProtocolType.Http:
proxyType = "HTTP";
break;
case ProxyProtocolType.Https:
proxyType = "HTTPS";
break;
case ProxyProtocolType.AllHttp:
proxyType = "HTTP and HTTPS";
break;
}
if (protocolType != ProxyProtocolType.None)
{
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress, endPoint.Port, proxyType);
}
}
/// <summary>
......@@ -416,25 +453,28 @@ namespace Titanium.Web.Proxy
/// </summary>
public void DisableSystemHttpProxy()
{
if (RunTime.IsRunningOnMono)
{
throw new Exception("Mono Runtime do not support system proxy settings.");
}
systemProxySettingsManager.RemoveHttpProxy();
DisableSystemProxy(ProxyProtocolType.Http);
}
/// <summary>
/// Remove any HTTPS proxy setting for current machine
/// </summary>
public void DisableSystemHttpsProxy()
{
DisableSystemProxy(ProxyProtocolType.Https);
}
/// <summary>
/// Remove the specified proxy settings for current machine
/// </summary>
public void DisableSystemProxy(ProxyProtocolType protocolType)
{
if (RunTime.IsRunningOnMono)
{
throw new Exception("Mono Runtime do not support system proxy settings.");
}
systemProxySettingsManager.RemoveHttpsProxy();
systemProxySettingsManager.RemoveProxy(protocolType);
}
/// <summary>
......@@ -460,9 +500,38 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is already running.");
}
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null
&& GetCustomUpStreamHttpsProxyFunc == null)
//clear any system proxy settings which is pointing to our own endpoint
//due to non gracious proxy shutdown before
if (systemProxySettingsManager != null)
{
var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry();
if (proxyInfo.Proxies != null)
{
var protocolToRemove = ProxyProtocolType.None;
foreach (var proxy in proxyInfo.Proxies.Values)
{
if (proxy.HostName == "127.0.0.1" && ProxyEndPoints.Any(x => x.Port == proxy.Port))
{
protocolToRemove |= proxy.ProtocolType;
}
}
if (protocolToRemove != ProxyProtocolType.None)
{
//do not restore to any of listening address when we quit
systemProxySettingsManager.RemoveProxy(protocolToRemove, false);
}
}
}
if (ForwardToUpstreamGateway
&& GetCustomUpStreamHttpProxyFunc == null && GetCustomUpStreamHttpsProxyFunc == null
&& systemProxySettingsManager != null)
{
// Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE();
GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy;
GetCustomUpStreamHttpsProxyFunc = GetSystemUpStreamProxy;
}
......@@ -494,11 +563,14 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is not running.");
}
var setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
if (setAsSystemProxy)
if (!RunTime.IsRunningOnMono)
{
systemProxySettingsManager.DisableAllProxy();
var setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
if (setAsSystemProxy)
{
systemProxySettingsManager.RestoreOriginalSettings();
}
}
foreach (var endPoint in ProxyEndPoints)
......@@ -565,19 +637,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 = new WebProxy();
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);
}
......
......@@ -6,6 +6,7 @@ using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
......@@ -236,12 +237,12 @@ namespace Titanium.Web.Proxy
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="httpsHostName"></param>
/// <param name="httpsConnectHostname"></param>
/// <param name="endPoint"></param>
/// <param name="connectHeaders"></param>
/// <returns></returns>
private async Task<bool> HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsConnectHostname,
ProxyEndPoint endPoint, List<HttpHeader> connectHeaders)
{
bool disposed = false;
......@@ -299,9 +300,9 @@ namespace Titanium.Web.Proxy
//Read the request headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.NonUniqueRequestHeaders, args.WebSession.Request.RequestHeaders);
var httpRemoteUri = new Uri(httpsHostName == null
var httpRemoteUri = new Uri(httpsConnectHostname == null
? httpCmdSplit[1]
: string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1]));
: string.Concat("https://", args.WebSession.Request.Host ?? httpsConnectHostname, httpCmdSplit[1]));
args.WebSession.Request.RequestUri = httpRemoteUri;
......@@ -311,7 +312,8 @@ namespace Titanium.Web.Proxy
args.ProxyClient.ClientStreamReader = clientStreamReader;
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
if (httpsHostName == null &&
//proxy authorization check
if (httpsConnectHostname == null &&
await CheckAuthorization(clientStreamWriter,
args.WebSession.Request.RequestHeaders.Values) == false)
{
......@@ -338,6 +340,12 @@ namespace Titanium.Web.Proxy
await BeforeRequest.InvokeParallelAsync(this, args);
}
if (args.WebSession.Request.CancelRequest)
{
args.Dispose();
break;
}
//if upgrading to websocket then relay the requet without reading the contents
if (args.WebSession.Request.UpgradeToWebSocket)
{
......@@ -354,6 +362,14 @@ namespace Titanium.Web.Proxy
{
connection = await GetServerConnection(args);
}
//create a new connection if hostname changes
else if (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host,
StringComparison.OrdinalIgnoreCase))
{
connection.Dispose();
Interlocked.Decrement(ref serverConnectionCount);
connection = await GetServerConnection(args);
}
//construct the web request that we are going to issue on behalf of the client.
disposed = await HandleHttpSessionRequestInternal(connection, args, false);
......@@ -364,13 +380,7 @@ namespace Titanium.Web.Proxy
args.Dispose();
break;
}
if (args.WebSession.Request.CancelRequest)
{
args.Dispose();
break;
}
//if connection is closing exit
if (args.WebSession.Response.ResponseKeepAlive == false)
{
......@@ -415,12 +425,6 @@ namespace Titanium.Web.Proxy
{
args.WebSession.Request.RequestLocked = true;
//If request was cancelled by user then dispose the client
if (args.WebSession.Request.CancelRequest)
{
return true;
}
//if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue)
......
......@@ -31,11 +31,6 @@ namespace Titanium.Web.Proxy
//read response & headers from server
await args.WebSession.ReceiveResponse();
if (!args.WebSession.Response.ResponseBodyRead)
{
args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream;
}
//check for windows authentication
if(EnableWinAuth
&& !RunTime.IsRunningOnMono
......
......@@ -52,7 +52,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
<Reference Include="System.configuration" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
......@@ -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" />
......@@ -129,7 +133,7 @@
<Compile Include="WinAuthHandler.cs" />
</ItemGroup>
<ItemGroup>
<COMReference Include="CERTENROLLLib">
<COMReference Include="CERTENROLLLib" Condition="'$(OS)' != 'Unix'">
<Guid>{728AB348-217D-11DA-B2A4-000E7BBB2B09}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
......@@ -159,4 +163,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
</Project>
......@@ -6,8 +6,8 @@
<title>Titanium web proxy</title>
<authors>Titanium</authors>
<owners>Titanium</owners>
<licenseUrl>https://github.com/titanium007/Titanium/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/titanium007/Titanium/</projectUrl>
<licenseUrl>https://github.com/justcoding121/Titanium-Web-Proxy/blob/develop/LICENSE</licenseUrl>
<projectUrl>https://github.com/justcoding121/Titanium-Web-Proxy</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Titanium is a light weight web proxy.</description>
<releaseNotes></releaseNotes>
......@@ -20,4 +20,4 @@
<files>
<file src="bin\$configuration$\Titanium.Web.Proxy.dll" target="lib\net45" />
</files>
</package>
\ No newline at end of file
</package>
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