diff --git a/IPBanCore/Core/IPBan/IPBanConfig.cs b/IPBanCore/Core/IPBan/IPBanConfig.cs index 5cd733bf..86fac997 100644 --- a/IPBanCore/Core/IPBan/IPBanConfig.cs +++ b/IPBanCore/Core/IPBan/IPBanConfig.cs @@ -115,6 +115,10 @@ public void Dispose() private readonly TimeSpan minimumTimeBetweenSuccessfulLoginAttempts = TimeSpan.FromSeconds(5.0); private readonly string ipThreatApiKey = string.Empty; + private readonly string proxyAddress = string.Empty; + private readonly string proxyUserName = string.Empty; + private readonly string proxyPassword = string.Empty; + private readonly bool proxyServiceUrls; private readonly int failedLoginAttemptsBeforeBan = 5; private readonly bool resetFailedLoginCountForUnbannedIPAddresses; private readonly string firewallRulePrefix = "IPBan_"; @@ -185,6 +189,10 @@ private IPBanConfig(XmlDocument doc, IDnsLookup dns = null, IDnsServerList dnsLi } TryGetConfig("IPThreatApiKey", ref ipThreatApiKey, false); + TryGetConfig("ProxyAddress", ref proxyAddress, false); + TryGetConfig("ProxyUserName", ref proxyUserName, false); + TryGetConfig("ProxyPassword", ref proxyPassword, false); + TryGetConfig("ProxyServiceUrls", ref proxyServiceUrls); GetConfig("FailedLoginAttemptsBeforeBan", ref failedLoginAttemptsBeforeBan, 1, 50); TryGetConfig("ResetFailedLoginCountForUnbannedIPAddresses", ref resetFailedLoginCountForUnbannedIPAddresses); GetConfigArray("BanTime", ref banTimes, emptyTimeSpanArray); @@ -1014,6 +1022,27 @@ public static string ValidateFirewallUriRules(string firewallUriRules) /// public string IPThreatApiKey { get { return ipThreatApiKey; } } + /// + /// Proxy address for http requests (e.g. http://proxy:8080) + /// + public string ProxyAddress { get { return proxyAddress; } } + + /// + /// Proxy user name, if required + /// + public string ProxyUserName { get { return proxyUserName; } } + + /// + /// Proxy password, if required + /// + public string ProxyPassword { get { return proxyPassword; } } + + /// + /// Whether to use proxy for service URLs (GetUrlUpdate, GetUrlStart, GetUrlStop, GetUrlConfig). + /// Default is false - service URLs will not use proxy. + /// + public bool ProxyServiceUrls { get { return proxyServiceUrls; } } + /// /// Number of failed login attempts before a ban is initiated /// diff --git a/IPBanCore/Core/IPBan/IPBanService_Private.cs b/IPBanCore/Core/IPBan/IPBanService_Private.cs index ecfb7a4d..d61552b0 100644 --- a/IPBanCore/Core/IPBan/IPBanService_Private.cs +++ b/IPBanCore/Core/IPBan/IPBanService_Private.cs @@ -101,6 +101,11 @@ internal async Task UpdateConfiguration(CancellationToken cancelToken) // set new config and re-load everything Config = newConfig; + // update proxy settings for http requests + DefaultHttpRequestMaker.ProxyAddress = newConfig.ProxyAddress; + DefaultHttpRequestMaker.ProxyUserName = newConfig.ProxyUserName; + DefaultHttpRequestMaker.ProxyPassword = newConfig.ProxyPassword; + // load the firewall, detecting a change by referencing the old config await LoadFirewall(oldConfig); @@ -847,7 +852,8 @@ protected virtual async Task GetUrl(UrlType urlType, CancellationToken can try { KeyValuePair[] headers = (Authorization is null ? null : new KeyValuePair[] { new("Authorization", Authorization) }); - byte[] bytes = await RequestMaker.MakeRequestAsync(new Uri(url), headers: headers, cancelToken: cancelToken); + IHttpRequestMaker requestMaker = (Config.ProxyServiceUrls ? RequestMaker : DirectHttpRequestMaker.Instance); + byte[] bytes = await requestMaker.MakeRequestAsync(new Uri(url), headers: headers, cancelToken: cancelToken); if (urlType == UrlType.Start) { GotStartUrl = true; diff --git a/IPBanCore/Core/Interfaces/IHttpRequestMaker.cs b/IPBanCore/Core/Interfaces/IHttpRequestMaker.cs index b342d16f..c608dd25 100644 --- a/IPBanCore/Core/Interfaces/IHttpRequestMaker.cs +++ b/IPBanCore/Core/Interfaces/IHttpRequestMaker.cs @@ -55,6 +55,65 @@ Task MakeRequestAsync(Uri uri, CancellationToken cancelToken = default); } + /// + /// Direct http request maker that does not use proxy + /// + public sealed class DirectHttpRequestMaker : IHttpRequestMaker + { + /// + /// Singleton of DirectHttpRequestMaker + /// + public static DirectHttpRequestMaker Instance { get; } = new DirectHttpRequestMaker(); + + /// + public async Task MakeRequestAsync(Uri uri, + byte[] postJson = null, + IEnumerable> headers = null, + string method = null, + CancellationToken cancelToken = default) + { + Assembly versionAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetAssembly(typeof(IPBanService)) ?? GetType().Assembly; + HttpRequestMessage msg = new() + { + RequestUri = uri + }; + msg.Headers.Add("User-Agent", versionAssembly.GetName().Name); + if (headers != null) + { + foreach (KeyValuePair header in headers) + { + msg.Headers.Add(header.Key, header.Value.ToHttpHeaderString()); + } + } + byte[] response; + if (postJson is null || postJson.Length == 0) + { + msg.Method = HttpMethod.Get; + } + else + { + msg.Method = HttpMethod.Post; + msg.Headers.Add("Cache-Control", "no-cache"); + msg.Content = new ByteArrayContent(postJson); + msg.Content.Headers.Add("Content-Type", "application/json; charset=utf-8"); + } + + if (!string.IsNullOrWhiteSpace(method)) + { + msg.Method = new HttpMethod(method); + } + + var client = new HttpClient(); + var responseMsg = await client.SendAsync(msg, cancelToken); + response = await responseMsg.Content.ReadAsByteArrayAsync(cancelToken); + if (!responseMsg.IsSuccessStatusCode) + { + throw new HttpRequestException("Request to url " + uri + " failed, status: " + responseMsg.StatusCode); + } + return response; + } + } + /// /// Default implementation of IHttpRequestMaker /// @@ -70,6 +129,21 @@ public sealed class DefaultHttpRequestMaker : IHttpRequestMaker /// public static bool DisableLiveRequests { get; set; } + /// + /// Proxy address for http requests (e.g. http://proxy:8080) + /// + public static string ProxyAddress { get; set; } + + /// + /// Proxy user name, if required + /// + public static string ProxyUserName { get; set; } + + /// + /// Proxy password, if required + /// + public static string ProxyPassword { get; set; } + private static long liveRequestCount; /// /// Global counter of live requests made @@ -135,7 +209,21 @@ public async Task MakeRequestAsync(Uri uri, msg.Method = new HttpMethod(method); } - var client = new HttpClient(); + HttpClient client; + if (!string.IsNullOrWhiteSpace(ProxyAddress)) + { + var handler = new HttpClientHandler(); + handler.Proxy = new System.Net.WebProxy(ProxyAddress); + if (!string.IsNullOrWhiteSpace(ProxyUserName) && !string.IsNullOrWhiteSpace(ProxyPassword)) + { + handler.Proxy.Credentials = new System.Net.NetworkCredential(ProxyUserName, ProxyPassword); + } + client = new HttpClient(handler); + } + else + { + client = new HttpClient(); + } var responseMsg = await client.SendAsync(msg, cancelToken); response = await responseMsg.Content.ReadAsByteArrayAsync(cancelToken); if (!responseMsg.IsSuccessStatusCode) diff --git a/IPBanCore/ipban.config b/IPBanCore/ipban.config index 913fe341..b88fc708 100644 --- a/IPBanCore/ipban.config +++ b/IPBanCore/ipban.config @@ -975,6 +975,20 @@ + + + + + + + + + +