private void LoadOSInfo() { tempFolder = Path.GetTempPath(); if (string.IsNullOrWhiteSpace(tempFolder)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { tempFolder = "c:\\temp"; } else { tempFolder = "/tmp"; } } Directory.CreateDirectory(tempFolder); // start off with built in version info, this is not as detailed or nice as we like, // so we try some other ways to get more detailed information CpuArchitecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); Version = Environment.OSVersion.Version.ToString(); Description = RuntimeInformation.OSDescription; // attempt to get detailed version info if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Name = FriendlyName = OSUtility.Linux; isLinux = true; string tempFile = GetTempFileName(); Process.Start("/bin/bash", "-c \"cat /etc/*release* > " + tempFile + "\"").WaitForExit(); System.Threading.Tasks.Task.Delay(100); // wait a small bit for file to really be closed string versionText = File.ReadAllText(tempFile).Trim(); ExtensionMethods.FileDeleteWithRetry(tempFile); if (string.IsNullOrWhiteSpace(versionText)) { Logger.Error(new IOException("Unable to load os version from /etc/*release* ...")); } else { FriendlyName = ExtractRegex(versionText, "^(Id|Distrib_Id)=(?<value>.*?)$", string.Empty); if (FriendlyName.Length != 0) { string codeName = ExtractRegex(versionText, "^(Name|Distrib_CodeName)=(?<value>.+)$", string.Empty); if (codeName.Length != 0) { FriendlyName += " - " + codeName; } Version = ExtractRegex(versionText, "^Version_Id=(?<value>.+)$", Version); } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Name = FriendlyName = OSUtility.Windows; isWindows = true; processVerb = "runas"; try { LoadVersionFromWmic(); } catch (Exception ex) { Logger.Error(ex, "Failed to load os info from wmic"); // last resort, try wmi api LoadVersionFromWmiApi(); } // Windows loves to add a trailing .0 for some reason Version = Regex.Replace(Version, "\\.0$", string.Empty); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // TODO: Implement better for MAC Name = FriendlyName = OSUtility.Mac; FriendlyName = "OSX"; isMac = true; } else { Name = OSUtility.Unknown; FriendlyName = "Unknown"; } }
/// <summary> /// Create a test IPBanService /// </summary> /// <param name="directory">Root directory</param> /// <param name="configFileName">Config file name</param> /// <param name="defaultBannedIPAddressHandlerUrl">Url for banned ip handling or null to not handle banned ip</param> /// <param name="configFileModifier">Change config file (param are file text, returns new file text)</param> /// <returns>Service</returns> public static T CreateAndStartIPBanTestService <T>(string directory = null, string configFileName = null, string defaultBannedIPAddressHandlerUrl = null, Func <string, string> configFileModifier = null) where T : IPBanService { NLog.Time.TimeSource.Current = new TestTimeSource(); string defaultNLogConfig = $@"<?xml version=""1.0""?> <nlog xmlns=""http://www.nlog-project.org/schemas/NLog.xsd"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" throwExceptions=""false"" internalLogToConsole=""false"" internalLogToConsoleError=""false"" internalLogLevel=""Trace""> <targets> <target name=""logfile"" xsi:type=""File"" fileName=""${{basedir}}/logfile.txt"" encoding=""UTF-8""/> <target name=""console"" xsi:type=""Console""/> </targets> <rules> <logger name=""*"" minlevel=""Debug"" writeTo=""logfile""/> <logger name=""*"" minlevel=""Debug"" writeTo=""console""/> </rules> </nlog>"; File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "nlog.config"), defaultNLogConfig); string logFilePath = Path.Combine(AppContext.BaseDirectory, "logfile.txt"); if (File.Exists(logFilePath)) { File.Delete(logFilePath); } ExtensionMethods.RemoveDatabaseFiles(); DefaultHttpRequestMaker.DisableLiveRequests = true; if (string.IsNullOrWhiteSpace(directory)) { directory = AppContext.BaseDirectory; } if (string.IsNullOrWhiteSpace(configFileName)) { configFileName = IPBanConfig.DefaultFileName; } string configFilePath = Path.Combine(directory, configFileName); string configFileText = File.ReadAllText(configFilePath); configFilePath += ".tmp"; if (configFileModifier != null) { configFileText = configFileModifier(configFileText); } ExtensionMethods.FileWriteAllTextWithRetry(configFilePath, configFileText); T service = IPBanService.CreateService <T>() as T; service.ExternalIPAddressLookup = LocalMachineExternalIPAddressLookupTest.Instance; service.ConfigFilePath = configFilePath; service.MultiThreaded = false; service.ManualCycle = true; service.DnsList = null; // too slow for tests, turn off if (defaultBannedIPAddressHandlerUrl is null) { service.BannedIPAddressHandler = NullBannedIPAddressHandler.Instance; } else { service.BannedIPAddressHandler = new DefaultBannedIPAddressHandler { BaseUrl = defaultBannedIPAddressHandlerUrl }; } service.Version = "1.1.1.1"; service.RunAsync(CancellationToken.None).Sync(); service.RunCycleAsync().Sync(); service.DB.Truncate(true); service.Firewall.Truncate(); return(service); }
/// <summary> /// Static constructor /// </summary> static OSUtility() { try { tempFolder = Path.GetTempPath(); if (string.IsNullOrWhiteSpace(tempFolder)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { tempFolder = "c:\\temp"; } else { tempFolder = "/tmp"; } } Directory.CreateDirectory(tempFolder); // start off with built in version info, this is not as detailed or nice as we like, // so we try some other ways to get more detailed information Version = Environment.OSVersion.VersionString; Description = RuntimeInformation.OSDescription; // attempt to get detailed version info if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { isLinux = true; string tempFile = OSUtility.GetTempFileName(); Process.Start("/bin/bash", "-c \"cat /etc/*release* > " + tempFile + "\"").WaitForExit(); System.Threading.Tasks.Task.Delay(100); // wait a small bit for file to really be closed string versionText = File.ReadAllText(tempFile).Trim(); ExtensionMethods.FileDeleteWithRetry(tempFile); if (string.IsNullOrWhiteSpace(versionText)) { Logger.Error(new IOException("Unable to load os version from /etc/*release* ...")); } else { Name = OSUtility.Linux; FriendlyName = ExtractRegex(versionText, "^(Id|Distrib_Id)=(?<value>.*?)$", string.Empty); if (FriendlyName.Length != 0) { string codeName = ExtractRegex(versionText, "^(Name|Distrib_CodeName)=(?<value>.+)$", string.Empty); if (codeName.Length != 0) { FriendlyName += " - " + codeName; } Version = ExtractRegex(versionText, "^Version_Id=(?<value>.+)$", Version); } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { isWindows = true; processVerb = "runas"; Name = OSUtility.Windows; string tempFile = OSUtility.GetTempFileName(); // .net core WMI has a strange bug where WMI will not initialize on some systems // since this is the only place where WMI is used, we can just work-around it // with the wmic executable, which exists (as of 2018) on all supported Windows. StartProcessAndWait("cmd", "/C wmic path Win32_OperatingSystem get Caption,Version /format:table > \"" + tempFile + "\""); if (File.Exists(tempFile)) { // try up to 10 times to read the file for (int i = 0; i < 10; i++) { try { string[] lines = File.ReadAllLines(tempFile); ExtensionMethods.FileDeleteWithRetry(tempFile); if (lines.Length > 1) { int versionIndex = lines[0].IndexOf("Version"); if (versionIndex >= 0) { FriendlyName = lines[1].Substring(0, versionIndex - 1).Trim(); Version = lines[1].Substring(versionIndex).Trim(); break; } } throw new IOException("Invalid file generated from wmic"); } catch (Exception ex) { if (i < 9) { System.Threading.Tasks.Task.Delay(200).Wait(); } else { Logger.Error(ex, "Unable to load os version using wmic, trying wmi api..."); // last resort, try wmi api LoadVersionFromWmiApi(); } } } } else { // last resort, try wmi api LoadVersionFromWmiApi(); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // TODO: Implement better for MAC isMac = true; Name = OSUtility.Mac; FriendlyName = "OSX"; } else { Name = OSUtility.Unknown; FriendlyName = "Unknown"; } } catch (Exception ex) { Logger.Error("Error determining platform info", ex); } }
// 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.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); } 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) { #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 } }
/* // makes nlog go haywire, revisit later * private static readonly CustomTimeSource timeSource = new CustomTimeSource(); * * private class CustomTimeSource : NLog.Time.TimeSource * { * private TimeZoneInfo zoneInfo = TimeZoneInfo.Utc; * * [Required] * public string Zone * { * get { return zoneInfo.DisplayName; } * set { zoneInfo = TimeZoneInfo.FindSystemTimeZoneById(value); } * } * * public override DateTime Time => IPBanService.UtcNow; * * public override DateTime FromSystemTime(DateTime systemTime) * { * return systemTime.ToUniversalTime(); * } * * public DateTime CurrentTime { get; set; } = IPBanService.UtcNow; * } */ static Logger() { try { LogFactory factory = null; try { factory = LogManager.LoadConfiguration(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath); } catch { // if no config, exception is thrown that is OK } if (factory is null || factory.Configuration.AllTargets.Count == 0) { string nlogConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "nlog.config"); if (!File.Exists(nlogConfigPath)) { string logLevel = "Warn"; Console.WriteLine("Creating default nlog.config file"); // storing this as a resource fails to use correct string in precompiled .exe with .net core, bug with Microsoft I think string defaultNLogConfig = $@"<?xml version=""1.0""?> <nlog xmlns=""http://www.nlog-project.org/schemas/NLog.xsd"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" throwExceptions=""false"" internalLogToConsole=""false"" internalLogToConsoleError=""false"" internalLogLevel=""Trace""> <targets> <target name=""logfile"" xsi:type=""File"" fileName=""${{basedir}}/logfile.txt"" archiveNumbering=""Sequence"" archiveEvery=""Day"" maxArchiveFiles=""28"" encoding=""UTF-8""/> <target name=""console"" xsi:type=""Console""/> </targets> <rules> <logger name=""*"" minlevel=""{logLevel}"" writeTo=""logfile""/> <logger name=""*"" minlevel=""{logLevel}"" writeTo=""console""/> </rules> </nlog>"; ExtensionMethods.FileWriteAllTextWithRetry(nlogConfigPath, defaultNLogConfig); } if (File.Exists(nlogConfigPath)) { factory = LogManager.LoadConfiguration(nlogConfigPath); } else { throw new IOException("Unable to create nlog configuration file, nlog.config file failed to write default config."); } } nlogInstance = factory.GetCurrentClassLogger(); instance = new NLogWrapper(nlogInstance); if (UnitTestDetector.Running) { foreach (LoggingRule rule in LogManager.Configuration.LoggingRules) { rule.EnableLoggingForLevels(NLog.LogLevel.Trace, NLog.LogLevel.Fatal); } LogManager.ReconfigExistingLoggers(); } //NLog.Time.TimeSource.Current = timeSource; } catch (Exception ex) { // log to console as no other logger is available Console.WriteLine("Failed to initialize logger: {0}", ex); } }
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); } }
/// <summary> /// Create a firewall /// </summary> /// <param name="osAndFirewall">Dictionary of string operating system name (Windows, Linux, OSX) and firewall class</param> /// <param name="rulePrefix">Rule prefix or null for default</param> /// <returns>Firewall</returns> public static IIPBanFirewall CreateFirewall(IReadOnlyDictionary <string, string> osAndFirewall, string rulePrefix = null, IIPBanFirewall existing = null) { try { bool foundFirewallType = false; int priority = int.MinValue; Type firewallType = typeof(IIPBanFirewall); List <Type> allTypes = ExtensionMethods.GetAllTypes(); var q = from fwType in allTypes where fwType.IsPublic && fwType != firewallType && firewallType.IsAssignableFrom(fwType) && fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>() != null && fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>().IsValid select new { FirewallType = fwType, OS = fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>(), Name = fwType.GetCustomAttribute <CustomNameAttribute>() }; var array = q.ToArray(); foreach (var result in array) { // look up the requested firewall by os name bool matchPriority = priority < result.OS.Priority; if (matchPriority) { bool matchName = true; if (osAndFirewall != null && osAndFirewall.Count != 0 && (osAndFirewall.TryGetValue(OSUtility.Instance.Name, out string firewallToUse) || osAndFirewall.TryGetValue("*", out firewallToUse))) { matchName = result.Name.Name.Equals(firewallToUse, StringComparison.OrdinalIgnoreCase); } if (matchName) { // if IsAvailable method is provided, attempt to call MethodInfo available = result.FirewallType.GetMethod("IsAvailable", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (available != null) { try { if (!Convert.ToBoolean(available.Invoke(null, null))) { continue; } } catch { continue; } } firewallType = result.FirewallType; priority = result.OS.Priority; foundFirewallType = true; } } } if (firewallType is null) { throw new ArgumentException("Firewall is null, at least one type should implement IIPBanFirewall"); } else if (osAndFirewall.Count != 0 && !foundFirewallType) { string typeString = string.Join(',', osAndFirewall.Select(kv => kv.Key + ":" + kv.Value)); throw new ArgumentException("Unable to find firewalls of types: " + typeString + ", osname: " + OSUtility.Instance.Name); } if (existing != null && existing.GetType().Equals(firewallType)) { return(existing); } return(Activator.CreateInstance(firewallType, new object[] { rulePrefix }) as IIPBanFirewall); } catch (Exception ex) { throw new ArgumentException("Unable to create firewall, please double check your Firewall configuration property", ex); } }
/// <summary> /// Create a firewall /// </summary> /// <param name="rulePrefix">Rule prefix or null for default</param> /// <returns>Firewall</returns> public static IIPBanFirewall CreateFirewall(string rulePrefix = null, IIPBanFirewall existing = null) { try { int priority = int.MinValue; Type firewallType = typeof(IIPBanFirewall); Type fallbackType = null; IReadOnlyCollection <Type> allTypes = ExtensionMethods.GetAllTypes(); var q = from fwType in allTypes where fwType.IsPublic && fwType != firewallType && firewallType.IsAssignableFrom(fwType) && fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>() != null && fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>().IsMatch select new { FirewallType = fwType, OS = fwType.GetCustomAttribute <RequiredOperatingSystemAttribute>(), Name = fwType.GetCustomAttribute <CustomNameAttribute>() }; var array = q.OrderBy(f => f.OS.Priority).ToArray(); foreach (var result in array) { bool matchPriority = priority < result.OS.Priority; if (matchPriority) { // if IsAvailable method is provided, attempt to call MethodInfo available = result.FirewallType.GetMethod("IsAvailable", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (available != null) { try { if (!Convert.ToBoolean(available.Invoke(null, null))) { continue; } } catch { continue; } } firewallType = result.FirewallType; priority = result.OS.Priority; fallbackType = result.OS.FallbackFirewallType; } } if (firewallType is null || firewallType == typeof(IIPBanFirewall)) { throw new ArgumentException("Firewall is null, at least one type should implement IIPBanFirewall"); } RequiredOperatingSystemAttribute fallbackAttr = fallbackType?.GetCustomAttribute <RequiredOperatingSystemAttribute>(); Type existingType = existing?.GetType(); if (existingType != null && // if we have an existing firewall and ( firewallType.Equals(existingType)) || // if the existing firewall is the desired type or ( fallbackType != null && // we have a fallback type and ( fallbackType.Equals(existingType) || // the existing firewall is the fallback type or ( // the fallback firewall has another fallback firewall and it matches the existing type fallbackAttr?.FallbackFirewallType != null && fallbackAttr.FallbackFirewallType.Equals(existingType) ) ) ) ) { return(existing); } try { return(Activator.CreateInstance(firewallType, new object[] { rulePrefix }) as IIPBanFirewall); } catch (Exception ex) { // see if there's a fallback if (fallbackType is null) { throw; } Logger.Error(ex, "Failed to create firewall of type {0}, falling back to firewall type {1}", firewallType, fallbackType); try { return(Activator.CreateInstance(fallbackType, new object[] { rulePrefix }) as IIPBanFirewall); } catch (Exception ex2) { // last fallback attempt if (fallbackAttr?.FallbackFirewallType is null) { throw; } Logger.Error(ex2, "Failed to create firewall of type {0}, falling back to final attempt with firewall type {1}", fallbackType, fallbackAttr.FallbackFirewallType); return(Activator.CreateInstance(fallbackAttr.FallbackFirewallType, new object[] { rulePrefix }) as IIPBanFirewall); } } } catch (Exception ex) { throw new ArgumentException("Unable to create firewall, please double check your Firewall configuration property", ex); } }
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); } } }