public Task <bool> AllowIPAddresses(string ruleNamePrefix, IEnumerable <IPAddressRange> ipAddresses, IEnumerable <PortRange> allowedPorts = null, CancellationToken cancelToken = default) { lock (this) { allowRuleRanges[ruleNamePrefix] = new MemoryFirewallRuleRanges(ipAddresses.Select(i => IPAddressRange.Parse(i)).ToList(), allowedPorts?.ToList(), false); } return(Task.FromResult <bool>(true)); }
private void PopulateList(HashSet <System.Net.IPAddress> set, List <IPAddressRange> ranges, HashSet <string> others, ref Regex regex, string setValue, string regexValue) { setValue = (setValue ?? string.Empty).Trim(); regexValue = (regexValue ?? string.Empty).Replace("*", @"[0-9A-Fa-f:]+?").Trim(); set.Clear(); regex = null; if (!string.IsNullOrWhiteSpace(setValue)) { foreach (string entry in setValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim())) { string entryWithoutComment = entry; int pos = entryWithoutComment.IndexOf('?'); if (pos >= 0) { entryWithoutComment = entryWithoutComment.Substring(0, pos); } entryWithoutComment = entryWithoutComment.Trim(); if (entryWithoutComment != "0.0.0.0" && entryWithoutComment != "::0" && entryWithoutComment != "127.0.0.1" && entryWithoutComment != "::1" && entryWithoutComment != "localhost") { try { if (IPAddressRange.TryParse(entryWithoutComment, out IPAddressRange range)) { if (range.Begin.Equals(range.End)) { set.Add(range.Begin); } else if (!ranges.Contains(range)) { ranges.Add(range); } } else { if (dns != null) { try { // add entries for each ip address that matches the dns entry IPAddress[] addresses = dns.GetHostEntryAsync(entryWithoutComment).Sync().AddressList; if (addresses != null) { foreach (IPAddress adr in addresses) { set.Add(adr); } } } catch { // eat exception, nothing we can do others.Add(entryWithoutComment); } } else { // add the entry itself others.Add(entryWithoutComment); } } } catch (System.Net.Sockets.SocketException) { // ignore, dns lookup fails } } } } if (!string.IsNullOrWhiteSpace(regexValue)) { regex = ParseRegex(regexValue); } }
/// <summary> /// Check if an ip address range is whitelisted /// </summary> /// <param name="range">Range</param> /// <returns>True if whitelisted, false otherwise</returns> public bool IsWhitelisted(IPAddressRange range) { IPBanConfig config = Config; return(config != null && Config.IsWhitelisted(range)); }
public override bool IsIPAddressBlocked(string ipAddress, out string ruleName, int port = -1) { ruleName = null; try { lock (policy) { for (int i = 0; ; i += MaxIpAddressesPerRule) { string firewallRuleName = BlockRulePrefix + i.ToString(CultureInfo.InvariantCulture); try { INetFwRule rule = policy.Rules.Item(firewallRuleName); if (rule is null) { // no more rules to check break; } else { HashSet <string> set = new(rule.RemoteAddresses.Split(',').Select(i2 => IPAddressRange.Parse(i2).Begin.ToString())); if (set.Contains(ipAddress)) { ruleName = firewallRuleName; return(true); } } } catch { // no more rules to check break; } } } } catch (Exception ex) { if (!(ex is OperationCanceledException)) { Logger.Error(ex); } } return(false); }
// deleteRule will drop the rule and matching set before creating the rule and set, use this is you don't care to update the rule and set in place protected bool UpdateRuleDelta(string ruleName, string action, IEnumerable <IPBanFirewallIPAddressDelta> deltas, string hashType, int maxCount, bool deleteRule, IEnumerable <PortRange> allowPorts, CancellationToken cancelToken) { #if ENABLE_FIREWALL_PROFILING Stopwatch timer = Stopwatch.StartNew(); #endif string ipFileTemp = OSUtility.Instance.GetTempFileName(); try { // add and remove the appropriate ip addresses from the set using (StreamWriter writer = File.CreateText(ipFileTemp)) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } writer.WriteLine($"create {ruleName} hash:{hashType} family {INetFamily} hashsize {hashSize} maxelem {maxCount} -exist"); foreach (IPBanFirewallIPAddressDelta delta in deltas) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } if (IPAddressRange.TryParse(delta.IPAddress, out IPAddressRange range) && range.Begin.AddressFamily == addressFamily && range.End.AddressFamily == addressFamily) { try { if (delta.Added) { if (range.Begin.Equals(range.End)) { writer.WriteLine($"add {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"add {ruleName} {range.ToCidrString()} -exist"); } } else { if (range.Begin.Equals(range.End)) { writer.WriteLine($"del {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"del {ruleName} {range.ToCidrString()} -exist"); } } } catch { // ignore invalid cidr ranges } } } } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } else { // restore the deltas into the existing set bool result = (RunProcess("ipset", true, $"restore < \"{ipFileTemp}\"") == 0); CreateOrUpdateRule(ruleName, action, hashType, maxCount, allowPorts, cancelToken); return(result); } } finally { ExtensionMethods.FileDeleteWithRetry(ipFileTemp); #if ENABLE_FIREWALL_PROFILING timer.Stop(); Logger.Warn("BlockIPAddressesDelta rule '{0}' took {1:0.00}ms with {2} ips", ruleName, timer.Elapsed.TotalMilliseconds, deltas.Count()); #endif } }
private void PopulateList(HashSet <System.Net.IPAddress> set, HashSet <IPAddressRange> ranges, HashSet <string> others, ref Regex regex, string setValue, string regexValue) { setValue = (setValue ?? string.Empty).Trim(); regexValue = (regexValue ?? string.Empty).Replace("*", @"[0-9A-Fa-f]+?").Trim(); set.Clear(); regex = null; void AddIPAddressRange(IPAddressRange range) { if (range.Begin.Equals(range.End)) { lock (set) { set.Add(range.Begin); } } else { lock (ranges) { ranges.Add(range); } } } if (!string.IsNullOrWhiteSpace(setValue)) { List <string> entries = new List <string>(); foreach (string entry in setValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim())) { string entryWithoutComment = entry; int pos = entryWithoutComment.IndexOf('?'); if (pos >= 0) { entryWithoutComment = entryWithoutComment.Substring(0, pos); } entryWithoutComment = entryWithoutComment.Trim(); entries.Add(entryWithoutComment); } List <Task> entryTasks = new List <Task>(); // iterate in parallel for performance foreach (string entry in entries) { string entryWithoutComment = entry; entryTasks.Add(Task.Run(async() => { bool isUserName; if (entryWithoutComment.StartsWith("user:"******"user:"******"https://", StringComparison.OrdinalIgnoreCase) || entryWithoutComment.StartsWith("http://", StringComparison.OrdinalIgnoreCase))) { try { if (httpRequestMaker != null) { // assume url list of ips, newline delimited byte[] ipListBytes = null; Uri uri = new Uri(entryWithoutComment); await ExtensionMethods.RetryAsync(async() => ipListBytes = await httpRequestMaker.MakeRequestAsync(uri, null, ipListHeaders)); string ipList = Encoding.UTF8.GetString(ipListBytes); if (!string.IsNullOrWhiteSpace(ipList)) { foreach (string item in ipList.Split('\n')) { if (IPAddressRange.TryParse(item.Trim(), out IPAddressRange ipRangeFromUrl)) { AddIPAddressRange(ipRangeFromUrl); } } } } } catch (Exception ex) { Logger.Error(ex, "Failed to get ip list from url {0}", entryWithoutComment); } } else if (!isUserName && Uri.CheckHostName(entryWithoutComment) != UriHostNameType.Unknown) { try { // add entries for each ip address that matches the dns entry IPAddress[] addresses = null; await ExtensionMethods.RetryAsync(async() => addresses = await dns.GetHostAddressesAsync(entryWithoutComment), exceptionRetry: _ex => { // ignore host not found errors return(!(_ex is System.Net.Sockets.SocketException socketEx) || socketEx.SocketErrorCode != System.Net.Sockets.SocketError.HostNotFound); }); lock (set) { foreach (IPAddress adr in addresses) { set.Add(adr); } } } catch (Exception ex) { Logger.Debug("Unable to resolve dns for {0}: {1}", entryWithoutComment, ex.Message); lock (others) { // eat exception, nothing we can do others.Add(entryWithoutComment); } } } else { lock (others) { others.Add(entryWithoutComment); } } }
/// <summary> /// Filter ip address ranges from ranges using filter /// </summary> /// <param name="ranges">Ip address ranges to filter</param> /// <param name="filter">Ip address ranges to filter out of ranges, null for no filtering</param> /// <returns>Filtered ip address ranges in sorted order</returns> public static IEnumerable <IPAddressRange> FilterRanges(this IEnumerable <IPAddressRange> ranges, IEnumerable <IPAddressRange> filter) { // if null ranges we are done if (ranges is null) { yield break; } // if null filter, return ranges as is else if (filter is null) { foreach (IPAddressRange range in ranges.OrderBy(r => r)) { yield return(range); } yield break; } using (IEnumerator <IPAddressRange> rangeEnum = ranges.OrderBy(r => r).GetEnumerator()) using (IEnumerator <IPAddressRange> filterEnum = filter.OrderBy(r => r).GetEnumerator()) { // if no ranges left, we are done if (!rangeEnum.MoveNext()) { yield break; } IPAddressRange currentFilter = (filterEnum.MoveNext() ? filterEnum.Current : null); IPAddressRange currentRange = rangeEnum.Current; while (true) { // if no more filter, just continue returning ranges as is if (currentFilter is null) { yield return(currentRange); if (!rangeEnum.MoveNext()) { break; } continue; } int compare = currentFilter.Begin.CompareTo(currentRange.End); if (compare > 0) { // current filter begin is after the range end, just return the range as is yield return(currentRange); if (!rangeEnum.MoveNext()) { break; } currentRange = rangeEnum.Current; } else { compare = currentFilter.End.CompareTo(currentRange.Begin); // check if the current filter end is before the range begin if (compare < 0) { // current filter end is before the range begin, move to next filter currentFilter = (filterEnum.MoveNext() ? filterEnum.Current : null); } else { // the current filter is inside the current range, filter int compareBegin = currentFilter.Begin.CompareTo(currentRange.Begin); int compareEnd = currentFilter.End.CompareTo(currentRange.End); if (compareBegin <= 0) { // filter begin is less than or equal to the range begin if (compareEnd < 0 && currentFilter.End.TryIncrement(out IPAddress begin)) { // set the range to have the filtered portion removed currentRange = new IPAddressRange(begin, currentRange.End); // move to next filter currentFilter = (filterEnum.MoveNext() ? filterEnum.Current : currentFilter); } else { // else the filter has blocked out this entire range, ignore it if (!rangeEnum.MoveNext()) { break; } currentRange = rangeEnum.Current; } } else { // if compareBegin was >= the ip address range begin, we won't get here // this means the current filter begin must be greater than 0 if (!currentFilter.Begin.TryDecrement(out IPAddress end)) { throw new InvalidOperationException("Current filter should have been able to decrement the begin ip address"); } // filter begin is after the range begin, return the range begin and one before the filter begin yield return(new IPAddressRange(currentRange.Begin, end)); if (!currentFilter.End.TryIncrement(out IPAddress newBegin)) { newBegin = currentFilter.End; } if (newBegin.CompareTo(currentRange.End) > 0) { // end of range, get a new range if (!rangeEnum.MoveNext()) { break; } currentRange = rangeEnum.Current; } else { currentRange = new IPAddressRange(newBegin, currentRange.End); } } } } } } }
// deleteRule will drop the rule and matching set before creating the rule and set, use this is you don't care to update the rule and set in place protected bool UpdateRule(string ruleName, string action, IEnumerable <string> ipAddresses, string hashType, int maxCount, IEnumerable <PortRange> allowPorts, CancellationToken cancelToken) { string ipFileTemp = OSUtility.GetTempFileName(); try { // add and remove the appropriate ip addresses from the set using (StreamWriter writer = File.CreateText(ipFileTemp)) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } RunProcess("ipset", true, out IReadOnlyList <string> sets, "-L -n"); if (sets.Contains(ruleName)) { writer.WriteLine($"flush {ruleName}");// hash:{hashType} family {INetFamily} hashsize {hashSize} maxelem {maxCount} -exist"); } writer.WriteLine($"create {ruleName} hash:{hashType} family {INetFamily} hashsize {hashSize} maxelem {maxCount} -exist"); foreach (string ipAddress in ipAddresses) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } if (IPAddressRange.TryParse(ipAddress, out IPAddressRange range) && range.Begin.AddressFamily == addressFamily && range.End.AddressFamily == addressFamily) { try { if (hashType != hashTypeCidrMask || range.Begin.Equals(range.End)) { writer.WriteLine($"add {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"add {ruleName} {range.ToCidrString()} -exist"); } } catch { // ignore invalid cidr ranges } } } } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } else { // restore the set bool result = (RunProcess("ipset", true, $"restore < \"{ipFileTemp}\"") == 0); CreateOrUpdateRule(ruleName, action, hashType, maxCount, allowPorts, cancelToken); return(result); } } finally { ExtensionMethods.FileDeleteWithRetry(ipFileTemp); } }
// deleteRule will drop the rule and matching set before creating the rule and set, use this is you don't care to update the rule and set in place protected bool UpdateRuleDelta(string ruleName, string action, IEnumerable <IPBanFirewallIPAddressDelta> deltas, string hashType, int maxCount, bool deleteRule, IEnumerable <PortRange> allowPorts, CancellationToken cancelToken) { string ipFileTemp = IPBanOS.GetTempFileName(); try { // add and remove the appropriate ip addresses from the set using (StreamWriter writer = File.CreateText(ipFileTemp)) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } writer.WriteLine($"create {ruleName} hash:{hashType} family {INetFamily} hashsize {hashSize} maxelem {maxCount} -exist"); foreach (IPBanFirewallIPAddressDelta delta in deltas) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } if (IPAddressRange.TryParse(delta.IPAddress, out IPAddressRange range) && range.Begin.AddressFamily == addressFamily && range.End.AddressFamily == addressFamily) { try { if (delta.Added) { if (range.Begin.Equals(range.End)) { writer.WriteLine($"add {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"add {ruleName} {range.ToCidrString()} -exist"); } } else { if (range.Begin.Equals(range.End)) { writer.WriteLine($"del {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"del {ruleName} {range.ToCidrString()} -exist"); } } } catch { // ignore invalid cidr ranges } } } } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } else { // restore the deltas into the existing set bool result = (RunProcess("ipset", true, $"restore < \"{ipFileTemp}\"") == 0); CreateOrUpdateRule(ruleName, action, hashType, maxCount, allowPorts, cancelToken); return(result); } } finally { IPBanExtensionMethods.FileDeleteWithRetry(ipFileTemp); } }
private void PopulateList(HashSet <System.Net.IPAddress> set, HashSet <IPAddressRange> ranges, HashSet <string> others, ref Regex regex, string setValue, string regexValue) { setValue = (setValue ?? string.Empty).Trim(); regexValue = (regexValue ?? string.Empty).Replace("*", @"[0-9A-Fa-f:]+?").Trim(); set.Clear(); regex = null; if (!string.IsNullOrWhiteSpace(setValue)) { foreach (string entry in setValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim())) { string entryWithoutComment = entry; int pos = entryWithoutComment.IndexOf('?'); if (pos >= 0) { entryWithoutComment = entryWithoutComment.Substring(0, pos); } entryWithoutComment = entryWithoutComment.Trim(); if (!ignoreListEntries.Contains(entryWithoutComment)) { if (IPAddressRange.TryParse(entryWithoutComment, out IPAddressRange range)) { if (range.Begin.Equals(range.End)) { set.Add(range.Begin); } else { ranges.Add(range); } } else if (Uri.CheckHostName(entryWithoutComment) != UriHostNameType.Unknown) { try { // add entries for each ip address that matches the dns entry IPAddress[] addresses = null; ExtensionMethods.Retry(() => addresses = dns.GetHostAddressesAsync(entryWithoutComment).Sync()); foreach (IPAddress adr in addresses) { set.Add(adr); } } catch (Exception ex) { Logger.Error(ex, "Unable to resolve dns for {0}", entryWithoutComment); // eat exception, nothing we can do others.Add(entryWithoutComment); } } else { others.Add(entryWithoutComment); } } } } if (!string.IsNullOrWhiteSpace(regexValue)) { regex = ParseRegex(regexValue); } }