private List <uint> LoadIPAddresses(string ruleName, string action, string hashType, int maxCount) { List <uint> ipAddresses = new List <uint>(); try { if (hashType != "ip") { throw new ArgumentException("Can only load hash of type 'ip'"); } CreateOrUpdateRule(ruleName, action, hashType, maxCount, null, default); // copy ip addresses from the rule to the set string fileName = GetSetFileName(ruleName); if (File.Exists(fileName)) { uint value; foreach (string line in File.ReadLines(fileName).Skip(1)) { string[] pieces = line.Split(' '); if (pieces.Length > 2 && pieces[0] == "add" && (value = IPBanFirewallUtility.ParseIPV4(pieces[2])) != 0) { ipAddresses.Add(value); } } } } catch (Exception ex) { IPBanLog.Error(ex); } return(ipAddresses); }
private bool GetOrCreateRule(string ruleName, string remoteIPAddresses, NET_FW_ACTION_ action, IEnumerable <PortRange> allowedPorts = null) { remoteIPAddresses = (remoteIPAddresses ?? string.Empty).Trim(); bool emptyIPAddressString = string.IsNullOrWhiteSpace(remoteIPAddresses) || remoteIPAddresses == "*"; bool ruleNeedsToBeAdded = false; lock (policy) { recreateRule: INetFwRule rule = null; try { rule = policy.Rules.Item(ruleName); } catch { // ignore exception, assume does not exist } if (rule == null) { rule = Activator.CreateInstance(ruleType) as INetFwRule; rule.Name = ruleName; rule.Enabled = true; rule.Action = action; rule.Description = "Automatically created by IPBan"; rule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN; rule.EdgeTraversal = false; rule.Grouping = "IPBan"; rule.LocalAddresses = "*"; rule.Profiles = int.MaxValue; // all ruleNeedsToBeAdded = true; } // do not ever set an empty string, Windows treats this as * which means everything if (!emptyIPAddressString) { try { PortRange[] allowedPortsArray = (allowedPorts?.ToArray()); if (allowedPortsArray != null && allowedPortsArray.Length != 0) { rule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; string localPorts; if (action == NET_FW_ACTION_.NET_FW_ACTION_BLOCK) { localPorts = IPBanFirewallUtility.GetPortRangeStringBlockExcept(allowedPortsArray); } else { localPorts = IPBanFirewallUtility.GetPortRangeStringAllow(allowedPortsArray); } rule.LocalPorts = localPorts; } else { try { rule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY; } catch { // failed to set protocol to any, we are switching from tcp back to any without ports, the only option is to // recreate the rule if (!ruleNeedsToBeAdded) { policy.Rules.Remove(ruleName); goto recreateRule; } } } rule.RemoteAddresses = remoteIPAddresses; } catch (Exception ex) { // if something failed, do not create the rule emptyIPAddressString = true; IPBanLog.Error(ex); } } if (emptyIPAddressString || string.IsNullOrWhiteSpace(rule.RemoteAddresses) || rule.RemoteAddresses == "*") { // if no ip addresses, remove the rule as it will allow or block everything with an empty RemoteAddresses string try { rule = null; policy.Rules.Remove(ruleName); } catch { } } else if (ruleNeedsToBeAdded) { policy.Rules.Add(rule); } return(rule != null); } }
public bool IsIPAddressAllowed(string ipAddress) { return(allowedIPAddresses.Contains(IPBanFirewallUtility.ParseIPV4(ipAddress))); }
public IEnumerable <string> EnumerateAllowedIPAddresses() { return(allowedIPAddresses.Select(b => IPBanFirewallUtility.IPV4ToString(b))); }
// 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 private List <uint> UpdateRule(string ruleName, string action, IEnumerable <string> ipAddresses, List <uint> existingIPAddresses, string hashType, int maxCount, bool deleteRule, IEnumerable <PortRange> allowPorts, CancellationToken cancelToken, out bool result) { string ipFile = GetSetFileName(ruleName); string ipFileTemp = ipFile + ".tmp"; List <uint> newIPAddressesUint = new List <uint>(); uint value = 0; // 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 (string ipAddress in ipAddresses) { if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } // only allow ipv4 for now if (IPAddressRange.TryParse(ipAddress, out IPAddressRange range) && range.Begin.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && range.End.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && // if deleting the rule, don't track the uint value (!deleteRule || (value = IPBanFirewallUtility.ParseIPV4(ipAddress)) != 0)) { try { if (range.Begin.Equals(range.End)) { writer.WriteLine($"add {ruleName} {range.Begin} -exist"); } else { writer.WriteLine($"add {ruleName} {range.ToCidrString()} -exist"); } if (!deleteRule) { newIPAddressesUint.Add(value); } } catch { // ignore invalid cidr ranges } } } newIPAddressesUint.Sort(); // if the rule was deleted, no need to add del entries if (!deleteRule) { // for ip that dropped out, remove from firewall foreach (uint droppedIP in existingIPAddresses.Where(e => newIPAddressesUint.BinarySearch(e) < 0)) { writer.WriteLine($"del {ruleName} {IPBanFirewallUtility.IPV4ToString(droppedIP)} -exist"); } } } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } else { // TODO: Is there an easier way to move to a file that exists? if (File.Exists(ipFile)) { DeleteFile(ipFile); } File.Move(ipFileTemp, ipFile); if (deleteRule) { DeleteRule(ruleName); } // restore the file to get the set updated result = (RunProcess("ipset", true, $"restore < \"{ipFile}\"") == 0); // ensure rule exists for the set CreateOrUpdateRule(ruleName, action, hashType, maxCount, allowPorts, cancelToken); } return(newIPAddressesUint); }
private bool CreateOrUpdateRule(string ruleName, string action, string hashType, int maxCount, IEnumerable <PortRange> allowedPorts, CancellationToken cancelToken) { // ensure that a set exists for the iptables rule in the event that this is the first run RunProcess("ipset", false, $"create {ruleName} hash:{hashType} family {inetFamily} hashsize {hashSize} maxelem {maxCount} -exist"); if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } string setFileName = GetSetFileName(ruleName); if (!File.Exists(setFileName)) { RunProcess("ipset", true, $"save {ruleName} > \"{setFileName}\""); } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } PortRange[] allowedPortsArray = allowedPorts?.ToArray(); // create or update the rule in iptables RunProcess("iptables", true, out IReadOnlyList <string> lines, "-L --line-numbers"); string portString = " "; bool replaced = false; if (allowedPortsArray != null && allowedPortsArray.Length != 0) { string portList = (action == "DROP" ? IPBanFirewallUtility.GetPortRangeStringBlockExcept(allowedPorts) : IPBanFirewallUtility.GetPortRangeStringAllow(allowedPorts)); portString = " -m multiport --dports " + portList.Replace('-', ':') + " "; // iptables uses ':' instead of '-' for range } string ruleNameWithSpaces = " " + ruleName + " "; foreach (string line in lines) { if (line.Contains(ruleNameWithSpaces, StringComparison.OrdinalIgnoreCase)) { // rule number is first piece of the line int index = line.IndexOf(' '); int ruleNum = int.Parse(line.Substring(0, index)); // replace the rule with the new info RunProcess("iptables", true, $"-R INPUT {ruleNum} -m set{portString}--match-set \"{ruleName}\" src -j {action}"); replaced = true; break; } } if (!replaced) { // add a new rule RunProcess("iptables", true, $"-A INPUT -m set{portString}--match-set \"{ruleName}\" src -j {action}"); } if (cancelToken.IsCancellationRequested) { throw new OperationCanceledException(cancelToken); } SaveTableToDisk(); return(true); }