private Task ProcessResult(string text, CancellationToken cancelToken) { using StringReader reader = new(text); string line; List <IPAddressRange> ranges = new(); int lines = 0; while ((line = reader.ReadLine()) != null) { if (lines++ > 10000) { // prevent too many lines from crashing us break; } // trim line, ignore if necessary line = line.Trim(); if (line.Length == 0 || line.StartsWith("#") || line.StartsWith("'") || line.StartsWith("REM") || !IPAddressRange.TryParse(line, out IPAddressRange range)) { continue; } else if (whitelistChecker is null || !whitelistChecker.IsWhitelisted(range)) { // make sure to add only ranges that are not whitelisted ranges.Add(range); } } return(firewall.BlockIPAddresses(RulePrefix, ranges, null, cancelToken)); }
public override Task <bool> AllowIPAddresses(string ruleNamePrefix, IEnumerable <IPAddressRange> ipAddresses, IEnumerable <PortRange> allowedPorts = null, CancellationToken cancelToken = default) { IEnumerable <IPAddressRange> ipv4 = ipAddresses.Where(i => IPAddressRange.TryParse(i, out IPAddressRange obj) && obj.Begin.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork); IEnumerable <IPAddressRange> ipv6 = ipAddresses.Where(i => IPAddressRange.TryParse(i, out IPAddressRange obj) && obj.Begin.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6); bool result = base.AllowIPAddresses(ruleNamePrefix, ipv4, allowedPorts, cancelToken).Sync(); if (result) { result = firewall6.AllowIPAddresses(ruleNamePrefix, ipv6, allowedPorts, cancelToken).Sync(); } return(Task.FromResult(result)); }
public IEnumerable <IPAddressRange> EnumerateIPAddresses(string ruleNamePrefix = null) { string prefix = (string.IsNullOrWhiteSpace(ruleNamePrefix) ? RulePrefix : RulePrefix + ruleNamePrefix); foreach (INetFwRule rule in EnumerateRulesMatchingPrefix(prefix)) { string ipList = rule.RemoteAddresses; if (!string.IsNullOrWhiteSpace(ipList) && ipList != "*") { string[] ips = ipList.Split(','); foreach (string ip in ips) { if (IPAddressRange.TryParse(ip, out IPAddressRange range)) { yield return(range); } // else // should never happen } } } }
/// <summary> /// Get a firewall ip address, clean and normalize /// </summary> /// <param name="ipAddress">IP Address</param> /// <param name="normalizedIP">The normalized ip ready to go in the firewall or null if invalid ip address</param> /// <returns>True if ip address can go in the firewall, false otherwise</returns> public static bool TryNormalizeIPAddress(this string ipAddress, out string normalizedIP) { normalizedIP = (ipAddress ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(normalizedIP) || normalizedIP == "-" || normalizedIP == "0.0.0.0" || normalizedIP == "127.0.0.1" || normalizedIP == "::0" || normalizedIP == "::1" || !IPAddressRange.TryParse(normalizedIP, out IPAddressRange range)) { // try parsing assuming the ip is followed by a port int pos = normalizedIP.LastIndexOf(':'); if (pos >= 0) { normalizedIP = normalizedIP.Substring(0, pos); if (!IPAddressRange.TryParse(normalizedIP, out range)) { normalizedIP = null; return(false); } } else { normalizedIP = null; return(false); } } try { normalizedIP = (range.Begin.Equals(range.End) ? range.Begin.ToString() : range.ToCidrString()); } catch (Exception ex) { Logger.Debug("Failed to normalize ip {0}, it is not a single ip or cidr range: {1}", ipAddress, ex); return(false); } return(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())) { if (entry != "0.0.0.0" && entry != "::0" && entry != "127.0.0.1" && entry != "::1" && entry != "localhost") { try { if (IPAddressRange.TryParse(entry, 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(entry).Sync().AddressList; if (addresses != null) { foreach (IPAddress adr in addresses) { set.Add(adr); } } } catch { // eat exception, nothing we can do others.Add(entry); } } else { // add the entry itself others.Add(entry); } } } catch (System.Net.Sockets.SocketException) { // ignore, dns lookup fails } } } } if (!string.IsNullOrWhiteSpace(regexValue)) { regex = ParseRegex(regexValue); } }
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); } }
// 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 = 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); } }
// 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); } }
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)) { 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:"******"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); } } }
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); } } }
// 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.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 } }