From d5961840b47b5bd15d33545ea4fdabd656f4f915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20G=C3=B3rka?= Date: Sat, 15 Apr 2023 14:17:41 +0200 Subject: [PATCH] Add support for setting up bypass rules and proxy localhost traffic --- .../MainWindow.xaml.cs | 5 +- src/Titanium.Web.Proxy/Helpers/SystemProxy.cs | 4 +- src/Titanium.Web.Proxy/ProxyServer.cs | 34 ++++++++- .../SystemProxyBypassRuleSet.cs | 72 +++++++++++++++++++ .../Titanium.Web.Proxy.csproj | 2 +- .../SystemProxyTest.cs | 6 +- 6 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 src/Titanium.Web.Proxy/SystemProxyBypassRuleSet.cs diff --git a/examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs b/examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs index 0342ef62f..2b933fdf0 100644 --- a/examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs +++ b/examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs @@ -105,7 +105,10 @@ public MainWindow() }; proxyServer.Start(); - proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp); + var proxyLocalhostTraffic = new SystemProxyBypassRuleSet() + .SubtractImplicitBypassRules(); + + proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp, proxyLocalhostTraffic); InitializeComponent(); } diff --git a/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs b/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs index 38a237c5a..60aa03820 100644 --- a/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs +++ b/src/Titanium.Web.Proxy/Helpers/SystemProxy.cs @@ -84,7 +84,8 @@ public SystemProxyManager() /// /// /// - internal void SetProxy(string hostname, int port, ProxyProtocolType protocolType) + /// + internal void SetProxy(string hostname, int port, ProxyProtocolType protocolType, string bypassRules) { using (var reg = OpenInternetSettingsKey()) { @@ -106,6 +107,7 @@ internal void SetProxy(string hostname, int port, ProxyProtocolType protocolType reg.SetValue(RegProxyEnable, 1); reg.SetValue(RegProxyServer, string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray())); + reg.SetValue(RegProxyOverride, bypassRules); Refresh(); } diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs index 4fa65f853..3aa30a4e8 100644 --- a/src/Titanium.Web.Proxy/ProxyServer.cs +++ b/src/Titanium.Web.Proxy/ProxyServer.cs @@ -452,6 +452,16 @@ public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) SetAsSystemProxy(endPoint, ProxyProtocolType.Http); } + /// + /// Set the given explicit end point as the default proxy server for current machine. + /// + /// The explicit endpoint. + /// The system proxy bypass rules. + public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint, SystemProxyBypassRuleSet bypassRules) + { + SetAsSystemProxy(endPoint, ProxyProtocolType.Http, bypassRules); + } + /// /// Set the given explicit end point as the default proxy server for current machine. /// @@ -461,12 +471,33 @@ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) SetAsSystemProxy(endPoint, ProxyProtocolType.Https); } + /// + /// Set the given explicit end point as the default proxy server for current machine. + /// + /// The explicit endpoint. + /// The system proxy bypass rules. + public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint, SystemProxyBypassRuleSet bypassRules) + { + SetAsSystemProxy(endPoint, ProxyProtocolType.Https, bypassRules); + } + /// /// Set the given explicit end point as the default proxy server for current machine. /// /// The explicit endpoint. /// The proxy protocol type. public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) + { + SetAsSystemProxy(endPoint, protocolType, new SystemProxyBypassRuleSet()); + } + + /// + /// Set the given explicit end point as the default proxy server for current machine. + /// + /// The explicit endpoint. + /// The proxy protocol type. + /// The system proxy bypass rules. + public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType, SystemProxyBypassRuleSet bypassRules) { if (SystemProxySettingsManager == null) throw new NotSupportedException(@"Setting system proxy settings are only supported in Windows. @@ -500,7 +531,8 @@ public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType p ? "localhost" : endPoint.IpAddress.ToString(), endPoint.Port, - protocolType); + protocolType, + bypassRules?.ToString() ?? string.Empty); if (isHttp) endPoint.IsSystemHttpProxy = true; diff --git a/src/Titanium.Web.Proxy/SystemProxyBypassRuleSet.cs b/src/Titanium.Web.Proxy/SystemProxyBypassRuleSet.cs new file mode 100644 index 000000000..6805d2079 --- /dev/null +++ b/src/Titanium.Web.Proxy/SystemProxyBypassRuleSet.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +namespace Titanium.Web.Proxy; + +/// +/// +/// This class is used to control when system should skip or use (in case of implicitly ignored addresses) system proxy. +/// By default for secutiry reasons proxy is ignored on local networks considered as trusted, you can disable it by using . +/// Be aware that rules are check in order they are added, it's important when they are contradicting. +/// +public class SystemProxyBypassRuleSet +{ + private const string IgnoreImplicitBypassRules = "<-loopback>"; + private const string BypassSimpleHostnamesRule = ""; + + private readonly List _bypassRules; + + /// + /// Initialize new system proxy rule set. + /// + public SystemProxyBypassRuleSet() + { + _bypassRules = new List(); + } + + /// + /// Adds user defined rule to bypass system proxy. + /// + /// Rule specifying which urls should not go though system proxy. + /// Itself. + /// + /// + /// var ruleSet = + /// new SystemProxyBypassRuleSet() + /// .AddRule("https://x.*.y.com:99") + /// .AddRule("*foobar.com") + /// .AddRule("*.org:443") + /// .AddRule("192.168.1.1/16"); + /// + /// + public SystemProxyBypassRuleSet AddRule(string rule) + { + _bypassRules.Add(rule); + return this; + } + + /// + /// Adds rule for bypassing hostnames without a period in them, and that are not IP literals. + /// + /// Itself. + public SystemProxyBypassRuleSet AddSimpleHostnames() + { + _bypassRules.Add(BypassSimpleHostnamesRule); + return this; + } + + /// + /// Subtracts the implicit proxy bypass rules (localhost and link local addresses). + /// This is generally only needed for test setups. Beware of the security implications to proxying localhost. + /// Ordering may matter when using a subtractive rule, as rules will be evaluated in a left-to-right order. + /// + /// Itself. + public SystemProxyBypassRuleSet SubtractImplicitBypassRules() + { + _bypassRules.Add(IgnoreImplicitBypassRules); + return this; + } + + public override string ToString() + { + return string.Join(";", _bypassRules); + } +} diff --git a/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj b/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj index ccd580dc5..bce3d9679 100644 --- a/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj +++ b/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj @@ -1,7 +1,7 @@  - net461;net60 + net48;net60 Titanium.Web.Proxy false True diff --git a/tests/Titanium.Web.Proxy.UnitTests/SystemProxyTest.cs b/tests/Titanium.Web.Proxy.UnitTests/SystemProxyTest.cs index 5cbb62d8b..12c37446c 100644 --- a/tests/Titanium.Web.Proxy.UnitTests/SystemProxyTest.cs +++ b/tests/Titanium.Web.Proxy.UnitTests/SystemProxyTest.cs @@ -19,13 +19,13 @@ public void CompareProxyAddressReturnedByWebProxyAndWinHttpProxyResolver() { CompareUrls(); - proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Http); + proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Http, string.Empty); CompareUrls(); - proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Https); + proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.Https, string.Empty); CompareUrls(); - proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.AllHttp); + proxyManager.SetProxy("127.0.0.1", 8000, ProxyProtocolType.AllHttp, string.Empty); CompareUrls(); // for this test you need to add a proxy.pac file to a local webserver