diff --git a/IPTables.Net/Iptables/IpTablesRuleBuilder.cs b/IPTables.Net/Iptables/IpTablesRuleBuilder.cs new file mode 100644 index 0000000..ff5002a --- /dev/null +++ b/IPTables.Net/Iptables/IpTablesRuleBuilder.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace IPTables.Net.Iptables +{ + public class IpTablesRuleBuilder + { + public IpTablesRuleBuilder() : + this(compactMode: false, strictMode: false) + { + } + + /// + /// Construct rule builder with full control of behaviour + /// + /// + /// Determine type of name of parameter in rule result + /// + /// if compactMode set to true, result of AddJump is --jump value + /// but if compactMode set to false, result of AddJump is -j value + /// + /// + /// + /// Determine empty value cause to error or not, and in general determine strict input validation or not + /// + public IpTablesRuleBuilder(bool compactMode, bool strictMode) + { + stringBuilder = new StringBuilder(); + this.compactMode = compactMode; + this.strictMode = strictMode; + this.trasnportModuleUsed = false; + this.protocol = string.Empty; + } + + /// + /// This specifies the target of the rule; i.e., what to do if the packet matches it. + /// The target can be a user-defined chain (other than the one this rule is in), + /// one of the special builtin targets which decide the fate of the packet immediately, + /// or an extension (see EXTENSIONS below). If this option is omitted in a rule (and -g is not used), + /// then matching the rule will have no effect on the packet's fate, but the counters on the rule will be incremented. + /// + /// + /// + /// + /// + public IpTablesRuleBuilder AddJump(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "-j" : "--jump"; + stringBuilder.Append($" {parameter} {value}"); + + return this; + } + + /// + /// Name of an interface via which a packet was received (only for packets entering the INPUT, FORWARD and PREROUTING chains). + /// When the "!" argument is used before the interface name, the sense is inverted. + /// If the interface name ends in a "+", then any interface which begins with this name will match. + /// If this option is omitted, any interface name will match. + /// + /// + /// + /// + public IpTablesRuleBuilder AddInboundInterface(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "-i" : "--in-interface"; + stringBuilder.Append($" {parameter} {value}"); + + return this; + } + + /// + /// Name of an interface via which a packet is going to be sent (for packets entering the FORWARD, OUTPUT and POSTROUTING chains). + /// When the "!" argument is used before the interface name, the sense is inverted. + /// If the interface name ends in a "+", then any interface which begins with this name will match. + /// If this option is omitted, any interface name will match. + /// + /// + /// + /// + public IpTablesRuleBuilder AddOutboundInterface(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "-o" : "--out-interface"; + stringBuilder.Append($" {parameter} {value}"); + + return this; + } + + /// + /// The protocol of the rule or of the packet to check. + /// The specified protocol can be one of tcp, udp, icmp, or all, or it can be a numeric value, + /// representing one of these protocols or a different one. A protocol name from /etc/protocols is also allowed. + /// A "!" argument before the protocol inverts the test. The number zero is equivalent to all. + /// Protocol all will match with all protocols and is taken as default when this option is omitted. + /// + /// + /// + /// + /// + public IpTablesRuleBuilder AddProtocol(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + // TODO: in some case -p means port, so we have to investigate more + string parameter = compactMode ? "-p" : "--protocol"; + stringBuilder.Append($" {parameter} {value}"); + protocol = value; + + return this; + } + + /// + /// Source specification. Address can be either a network name, + /// a hostname (please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea), + /// a network IP address (with /mask), or a plain IP address. + /// The mask can be either a network mask or a plain number, specifying the number of 1's at the left side of the network mask. + /// Thus, a mask of 24 is equivalent to 255.255.255.0. A "!" argument before the address specification inverts the sense of the address. + /// + /// + /// + /// + public IpTablesRuleBuilder AddSourceIp(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "-s" : "--source"; + stringBuilder.Append($" {parameter} {value}"); + + return this; + } + + /// + /// Destination specification. + /// + /// + /// Internal usage + /// + public IpTablesRuleBuilder AddDestinationIp(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "-d" : "--destination"; + stringBuilder.Append($" {parameter} {value}"); + + return this; + } + + /// + /// Source port or port range specification. + /// This can either be a service name or a port number. + /// An inclusive range can also be specified, using the format port:port. + /// If the first port is omitted, "0" is assumed; if the last is omitted, "65535" is assumed. + /// If the second port greater then the first they will be swapped. + /// + /// It can only be used in conjunction with -p tcp or -p udp. + /// + /// + /// + /// + /// + public IpTablesRuleBuilder AddSourcePort(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "--sport" : "--source-port"; + stringBuilder.Append($" {parameter} {value}"); + trasnportModuleUsed = true; + + return this; + } + + /// + /// Destination port or port range specification. + /// + /// It can only be used in conjunction with -p tcp or -p udp. + /// + /// + /// + /// + /// + /// + public IpTablesRuleBuilder AddDestinationPort(string value, [CallerMemberName] string caller = null) + { + if (string.IsNullOrEmpty(value)) + { + if (strictMode) + throw new ArgumentNullException(caller); + return this; + } + + string parameter = compactMode ? "--dport" : "--destination-port"; + stringBuilder.Append($" {parameter} {value}"); + trasnportModuleUsed = true; + + + return this; + } + + /// + /// Serialize all parameter in form of iptables rule + /// + /// + public override string ToString() + { + if (trasnportModuleUsed) + { + stringBuilder.Insert(0, $"-m {protocol}"); + } + + return stringBuilder.ToString(); + } + + private readonly StringBuilder stringBuilder; + private readonly bool compactMode; + private readonly bool strictMode; + private bool trasnportModuleUsed; + private string protocol; + } +}