diff --git a/docs/README-iptables.md b/docs/README-iptables.md index a9a641b..104d426 100644 --- a/docs/README-iptables.md +++ b/docs/README-iptables.md @@ -222,4 +222,18 @@ The wrapper error codes matches (unsurprisingly) the same as iptables. They are: if (error) { console.log(error); } - }); \ No newline at end of file + }); + +#### Dump + + iptables.dump({ + table: 'nat', // default: null == all + }, function(err, dump) { + for (var table_name in dump) { + var table_dump = dump[table]; + for (var chain_name in table_dump.chains) { + var chain_dump = table_dump.chains[chain_name]; + console.log(table_name, chain_name, chain_dump); + } + } + }); diff --git a/lib/child_utils.js b/lib/child_utils.js new file mode 100644 index 0000000..1237c4e --- /dev/null +++ b/lib/child_utils.js @@ -0,0 +1,61 @@ +var processQueue = require('process-queue'); + +var default_queue = processQueue.createQueue({concurrency:1}); + +exports.exec = function (line, opts, cb) { + var stdout, stdout_length, stderr, stderr_length; + + // avoid allocation + if (cb) { + stdout = []; + stdout_length = 0; + stderr = []; + stderr_length = 0; + } + + var child; + var queue = opts.queue || default_queue; + if (cb) { + queue = queue.wrap({ + child: function (spawned_child, next) { + child = spawned_child; + if (cb) { + child.stdout.on('data', function (data) { + if (!Buffer.isBuffer(data)) data = new Buffer(data); + stdout_length += data.length; + stdout.push(data); + }); + child.stderr.on('data', function (data) { + if (!Buffer.isBuffer(data)) data = new Buffer(data); + stderr_length += data.length; + stderr.push(data); + }); + } + next(null, child); + } + }); + } + var cmd = { + spawnOptions: ['sh', ['-c', 'exec ' + line]] + }; + function onFinish(err) { + if (!cb) return; + if (!child) { + cb(new Error('there was a problem spawning the child process')); + return; + } + if (err) { + err.killed = child.killed; + err.code = child.exitCode; + err.signal = child.signalCode; + } + cb(err, + stdout_length ? String(Buffer.concat(stdout, stdout_length)) : '', + stderr_length ? String(Buffer.concat(stderr, stderr_length)) : '' + ); + } + queue.push(cmd, onFinish); + + // cleanup for gc + cmd = null; +} diff --git a/lib/ipset/add.js b/lib/ipset/add.js index 2c53f52..fd36502 100644 --- a/lib/ipset/add.js +++ b/lib/ipset/add.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Add a given entry to the set. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/create.js b/lib/ipset/create.js index ca9fc7b..a6bed4c 100644 --- a/lib/ipset/create.js +++ b/lib/ipset/create.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Create a set identified with setsetname and specified type. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/del.js b/lib/ipset/del.js index f1bfa39..3188fba 100644 --- a/lib/ipset/del.js +++ b/lib/ipset/del.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Delete an entry from a set. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/destroy.js b/lib/ipset/destroy.js index 6e34d29..683a342 100644 --- a/lib/ipset/destroy.js +++ b/lib/ipset/destroy.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Destroy the specified set or all the sets if none is given. @@ -44,7 +44,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -56,4 +56,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/flush.js b/lib/ipset/flush.js index b9121a3..b5f1d00 100644 --- a/lib/ipset/flush.js +++ b/lib/ipset/flush.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Flush all entries from the specified set or flush all sets if none is given. @@ -44,7 +44,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -56,4 +56,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/rename.js b/lib/ipset/rename.js index 8baaab7..974b011 100644 --- a/lib/ipset/rename.js +++ b/lib/ipset/rename.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Rename a set. @@ -35,7 +35,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -47,4 +47,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/swap.js b/lib/ipset/swap.js index 28b6db5..9897164 100644 --- a/lib/ipset/swap.js +++ b/lib/ipset/swap.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Swap the content of two sets. @@ -35,7 +35,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -47,4 +47,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/test.js b/lib/ipset/test.js index f31f9ba..3f4f289 100644 --- a/lib/ipset/test.js +++ b/lib/ipset/test.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Test wether an entry is in a set or not. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null, 0); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/version.js b/lib/ipset/version.js index 3627916..50fc45c 100644 --- a/lib/ipset/version.js +++ b/lib/ipset/version.js @@ -1,12 +1,16 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Print program version. * * @param cb */ -module.exports = function (cb) { - if (typeof arguments[0] != 'function') { +module.exports = function (options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + if (typeof cb != 'function') { throw new Error('Invalid arguments. Signature: (callback)'); } @@ -22,7 +26,7 @@ module.exports = function (cb) { /* * Execute command. */ - exec(cmd.join(' '), function (error, stdout, stderror) { + exec(cmd.join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -34,4 +38,4 @@ module.exports = function (cb) { cb(null, stdout.split('\n')[0]); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/append.js b/lib/iptables/append.js index 9f0204a..c445bdc 100644 --- a/lib/iptables/append.js +++ b/lib/iptables/append.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/check.js b/lib/iptables/check.js new file mode 100644 index 0000000..4bcbf99 --- /dev/null +++ b/lib/iptables/check.js @@ -0,0 +1,66 @@ +var exec = require('../child_utils').exec; + +var tables = require('./utils').tables; +var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; + + +/** + * Checks the existence one or more rules from the selected chain. + * + * @param options + * @param cb + */ +module.exports = function (options, cb) { + if (typeof arguments[0] != 'object') { + throw new Error('Invalid arguments. Signature: (options, callback?)'); + } + + var table = (typeof options.table != 'undefined') + ? options.table + : tables.filter; + + var ipt_cmd = (options.sudo) + ? 'sudo ' + : ''; + + ipt_cmd += (options.ipv6) + ? 'ip6tables' + : 'iptables'; + + /* + * Build cmd to execute. + */ + var cmd = [ipt_cmd, '--table', table, '--check']; + var args = []; + + /* + * Process options. + */ + if (typeof options.chain != 'undefined') { + args = args.concat(options.chain); + } + + if (typeof options.rulenum != 'undefined') { + args = args.concat(options.rulenum); + } + else { + var common_rule_specs = processCommonRuleSpecs(options); + args = args.concat(common_rule_specs); + } + + /* + * Execute command. + */ + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { + if (error && cb) { + var err = new Error(stderror.split('\n')[0]); + err.cmd = cmd.concat(args).join(' '); + err.code = error.code; + + cb(err); + } + else if (cb) { + cb(null); + } + }); +}; diff --git a/lib/iptables/delete.js b/lib/iptables/delete.js index 1dfb4c2..cfa3b19 100644 --- a/lib/iptables/delete.js +++ b/lib/iptables/delete.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/delete_chain.js b/lib/iptables/delete_chain.js index f187da3..049f051 100644 --- a/lib/iptables/delete_chain.js +++ b/lib/iptables/delete_chain.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -54,7 +54,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -66,4 +66,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/dump.js b/lib/iptables/dump.js new file mode 100644 index 0000000..e81a976 --- /dev/null +++ b/lib/iptables/dump.js @@ -0,0 +1,267 @@ +var exec = require('../child_utils').exec; +var shell_quote = require('shell-quote'); + +var tables = require('./utils').tables; + + +/** + * Print out all the rules in iptables-save + * + * @param options + * @param cb(err, {table:[rule,...]}) + */ +var MAPPED_ARG = { + '-A': '--append', + '-D': '--delete', + '-I': '--insert', + '-R': '--replace', + '-L': '--list', + '-S': '--list-rules', + '-F': '--flush', + '-Z': '--zero', + '-N': '--new-chain', + '-X': '--delete-chain', + '-P': '--policy', + '-E': '--rename-chain', + '-d': '--destination', + '--dport': '--destination-port', + '--dports': '--destination-ports', + '-i': '--in-interface', + '-j': '--jump', + '-f': '--fragment', + '-g': '--goto', + '-m': '--match', + '-o': '--out-interface', + '-p': '--protocol', + '-s': '--source', + '-t': '--table', + '--sport': '--source-port', + '--sports': '--source-ports' +}; +var MATCH_EXTENSIONS = [ + 'addrtype', + 'ah', + 'cluster', + 'comment', + 'connbytes', + 'connlimit', + 'connmark', + 'conntrack', + 'dccp', + 'dscp', + 'ecn', + 'esp', + 'hashlimit', + 'helper', + 'iprange', + 'length', + 'limit', + 'mac', + 'mark', + 'multiport', + 'owner', + 'physdev', + 'pkttype', + 'policy', + 'quota', + 'rateest', + 'realm', + 'recent', + 'sctp', + 'set', + 'socket', + 'state', + 'statistic', + 'string', + 'tcpmss', + 'time', + 'tos', + 'ttl', + 'u32', + 'unclean' +]; +var COMMON_ARGS = [ + 'protocol', + 'jump', + 'goto', + 'source', + 'destination', + 'in-interface', + 'out-interface', + 'fragment', +] +module.exports = function (options, cb) { + if (typeof arguments[0] != 'object') { + throw new Error('Invalid arguments. Signature: (options, callback?)'); + } + + var table = options.table; + + var ipt_cmd = (options.sudo) + ? 'sudo ' + : ''; + + ipt_cmd += (options.ipv6) + ? 'ip6tables-save' + : 'iptables-save'; + + /* + * Build cmd to execute. + */ + var cmd = [ipt_cmd]; + if (table) { + cmd.push('--table', table); + } + var args = []; + + /* + * Execute command. + */ + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { + if (error && cb) { + var err = new Error(stderror.split('\n')[0]); + err.cmd = cmd.concat(args).join(' '); + err.code = error.code; + + cb(err); + } + else if (cb) { + var lines = String(stdout).split(/\n/g); + var tables = Object.create(null); + var chains = Object.create(null); + var table = ''; + var chain = ''; + var policy = ''; + var rules = []; + function commit_chain() { + if (chain === '') return; + if (chains[chain]) { + chains[chain].rules = chains[chain].rules.concat(rules); + } + else { + chains[chain] = { + policy: policy, + rules: rules + } + } + chain = ''; + policy = ''; + rules = []; + } + lines.forEach(function (line) { + line = line.trim(); + if (line === 'COMMIT') { + commit_chain(); + tables[table] = { + chains: chains + }; + table = ''; + chains = Object.create(null); + return; + } + var c = line.charAt(0); + if (c === '#') return; + else if (c === '*') { + table = line.slice(1).trim(); + return; + } + else if (c === ':') { + commit_chain(); + var parts = /^:(\S+)\s+(\S+)/.exec(line); + chain = parts[1]; + policy = parts[2]; + return; + } + else { + var args = shell_quote.parse(line); + var rule = Object.create(null); + var negated = false; + var option = ''; + var in_target = false; + var match = null; + var matches = Object.create(null); + var target_options = null; + var had_match = false; + args.forEach(function (arg) { + if (option === '') { + arg = Object.prototype.hasOwnProperty.call(MAPPED_ARG, arg) ? MAPPED_ARG[arg] : arg; + if (arg.charAt(0) === '-') { + option = arg.replace(/^[\-]+/,''); + if ('random' === option) { + rule.random = true; + option = ''; + } + else if ('ports' === option) { + rule['source-ports'] = rule['source-ports'] || []; + rule['destination-ports'] = rule['destination-ports'] || []; + } + else if ('--source-ports' === option) { + rule['source-ports'] = rule['source-ports'] || []; + } + else if ('--destination-ports' === option) { + rule['destination-ports'] = rule['destination-ports'] || []; + } + } + else if (arg === '!') { + negated = true; + } + } + else { + if (option === 'jump') { + match = null; + in_target = true; + } + var configure_target = rule; + if (MATCH_EXTENSIONS.indexOf(option) !== -1) { + had_match = true; + matches[option] = match = Object.create(null); + option = ''; + negated = false; + return; + } + else if (COMMON_ARGS.indexOf(option) === -1) { + // little bit wonky to detect if target_options was used + if (in_target) configure_target = target_options || (target_options = Object.create(null)); + else configure_target = match; + } + if (option === 'match' || option === 'protocol') { + had_match = true; + matches[arg] = match = matches[arg] || Object.create(null); + option = ''; + negated = false; + } + else if (option === 'append') { + if (arg != chain) commit_chain(); + chain = arg; + option = ''; + negated = false; + } + else if (option === 'ports') { + configure_target['source-ports'].push(arg); + configure_target['destination-ports'].push(arg); + option = ''; + negated = false; + } + else { + var val = negated ? '!' + arg : arg; + if (Array.isArray(rule[option])) { + configure_target[option].push(val); + } + else { + configure_target[option] = val; + } + option = ''; + negated = false; + } + } + }); + if (had_match) rule.matches = matches; + if (target_options != null) rule.target_options = target_options; + rules.push(rule); + } + }); + cb(null, tables); + } + }); +}; + diff --git a/lib/iptables/flush.js b/lib/iptables/flush.js index 6147cfc..0af81a9 100644 --- a/lib/iptables/flush.js +++ b/lib/iptables/flush.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -54,7 +54,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -66,4 +66,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/index.js b/lib/iptables/index.js index a03b69b..fd1259d 100644 --- a/lib/iptables/index.js +++ b/lib/iptables/index.js @@ -28,5 +28,11 @@ exports.insert = require('./insert'); // Replace a rule in the selected chain. exports.replace = require('./replace'); +// Get the current chains and rules. +exports.dump = require('./dump'); + +// Check for existence of a rule in a chain. +exports.check = require('./check'); + // General utility functions and constants. -exports.utils = require('./utils'); \ No newline at end of file +exports.utils = require('./utils'); diff --git a/lib/iptables/insert.js b/lib/iptables/insert.js index e95fabd..608547c 100644 --- a/lib/iptables/insert.js +++ b/lib/iptables/insert.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/new.js b/lib/iptables/new.js index f598f4c..8e6a32d 100644 --- a/lib/iptables/new.js +++ b/lib/iptables/new.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -41,7 +41,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -53,4 +53,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/policy.js b/lib/iptables/policy.js index 558fbcd..ff9a659 100644 --- a/lib/iptables/policy.js +++ b/lib/iptables/policy.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -45,7 +45,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -57,4 +57,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/rename.js b/lib/iptables/rename.js index fdcb5ff..e0f1025 100644 --- a/lib/iptables/rename.js +++ b/lib/iptables/rename.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -45,7 +45,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue:options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -57,4 +57,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/replace.js b/lib/iptables/replace.js index 7bac661..7092bf6 100644 --- a/lib/iptables/replace.js +++ b/lib/iptables/replace.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/utils.js b/lib/iptables/utils.js index c125d4b..e79944c 100644 --- a/lib/iptables/utils.js +++ b/lib/iptables/utils.js @@ -13,84 +13,60 @@ exports.tables = { * @param options * @returns {Array} */ -exports.processCommonRuleSpecs = function (options) { - var args = []; - - if (typeof options.protocol != 'undefined') { - if (options.protocol.split('!').length > 1) { - args = args.concat('! --protocol', options.protocol.split('!')[1]); - } - else { - args = args.concat('--protocol', options.protocol); - } - } - - if (typeof options.source != 'undefined') { - if (options.source.split('!').length > 1) { - args = args.concat('! --source', options.source.split('!')[1]); - } - else { - args = args.concat('--source', options.source); - } - } - - if (typeof options.destination != 'undefined') { - if (options.destination.split('!').length > 1) { - args = args.concat('! --destination', options.destination.split('!')[1]); +function manage_arg(property, flag, args) { + if (property != null) { + var val = String(property); + var index = val.indexOf('!'); + if (index !== -1) { + args.push('!', flag, val.slice(index)); } else { - args = args.concat('--destination', options.destination); + args.push(flag, val); } } - - if (typeof options['in-interface'] != 'undefined') { - if (options['in-interface'].split('!').length > 1) { - args = args.concat('! --in-interface', options['in-interface'].split('!')[1]); - } - else { - args = args.concat('--in-interface', options['in-interface']); - } +} +function hasOwnProperty(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} +exports.processCommonRuleSpecs = function (options) { + var args = []; + + if (options.comment != null) { + args.push('-m', 'comment', '--comment', String(options.comment).slice(0,255).replace(/[^A-Za-z_]/g,'\\$&')); } - if (typeof options['out-interface'] != 'undefined') { - if (options['out-interface'].split('!').length > 1) { - args = args.concat('! --out-interface', options['out-interface'].split('!')[1]); - } - else { - args = args.concat('--out-interface', options['out-interface']); - } - } + manage_arg(options.protocol, '--protocol', args); + manage_arg(options.source, '--source', args); + manage_arg(options['in-interface'], '--in-interface', args); + manage_arg(options['out-interface'], '--out-interface', args); - if (typeof options.fragment != 'undefined') { - if (options.fragment.split('!').length > 1) { - args = args.concat('! --fragment'); + if (options.fragment != null) { + var val = String(options.fragment); + var index = val.indexOf('!'); + if (index !== -1) { + args.push('!', '--fragment'); } else { - args = args.concat('--fragment'); + args.push('--fragment'); } } /* * Matches processing. */ - if (typeof options.matches != 'undefined') { + if (options.matches != null) { for (var match in options.matches) { - if (options.matches.hasOwnProperty(match)) { + if (hasOwnProperty(options.matches, match)) { // Initial match load. - args = args.concat('--match', match); + args.push('--match', match); // Specific match options. var this_match = options.matches[match]; - for (var match_option in this_match) { - if (this_match.hasOwnProperty(match_option)) { - if (typeof this_match[match_option] == 'string' - && this_match[match_option].split('!').length > 1) { - - args = args.concat('! --' + match_option, this_match[match_option].split('!')[1]); - } - else { - args = args.concat('--' + match_option, this_match[match_option]); + if (this_match != null && typeof this_match === 'object') { + for (var match_option in this_match) { + if (hasOwnProperty(this_match, match_option)) { + manage_arg(this_match[match_option], '--' + match_option, args); } } } @@ -98,22 +74,22 @@ exports.processCommonRuleSpecs = function (options) { } } - if (typeof options.goto != 'undefined') { - args = args.concat('--goto', options.goto); + if (options.goto != null) { + args.push('--goto', String(options.goto)); } - if (typeof options.jump != 'undefined') { - args = args.concat('--jump', options.jump); + if (options.jump != null) { + args.push('--jump', String(options.jump)); // Target extension options. - if (typeof options.target_options != 'undefined') { + if (options.target_options != null && typeof options.target_options === 'object') { for (var target_option in options.target_options) { - if (options.target_options.hasOwnProperty(target_option)) { - args = args.concat('--' + target_option, options.target_options[target_option]); + if (hasOwnProperty(options.target_options, target_option)) { + manage_arg(options.target_options[target_option], '--' + target_option, args); } } } } return args; -}; \ No newline at end of file +}; diff --git a/lib/iptables/zero.js b/lib/iptables/zero.js index 8d49d3f..64d8d23 100644 --- a/lib/iptables/zero.js +++ b/lib/iptables/zero.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -58,7 +58,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -70,4 +70,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index b16fc29..a49cf78 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name" : "netfilter", - "version" : "0.2.3", - "description" : "Packet filtering framework. Wrapper to provide netfilter capabilities from Node.js", - "main" : "index.js", - "keywords" : [ + "name": "netfilter", + "version": "0.2.3", + "description": "Packet filtering framework. Wrapper to provide netfilter capabilities from Node.js", + "main": "index.js", + "keywords": [ "stateless", "stateful", "packet", @@ -28,22 +28,28 @@ "set" ], "dependencies": { - "async": "*" + "async": "~0.9.0", + "offspring": "~1.0.0", + "process-queue": "^1.0.1", + "shell-quote": "^1.4.2" }, - "engines" : { + "engines": { "node": ">=0.8.0" }, - "homepage" : "https://github.com/diosney/node-netfilter", - "bugs" : { + "homepage": "https://github.com/diosney/node-netfilter", + "bugs": { "url": "https://github.com/diosney/node-netfilter/issues" }, - "author" : { - "name" : "Diosney Sarmiento", + "author": { + "name": "Diosney Sarmiento", "email": "diosney.s@gmail.com" }, - "license" : "MIT", - "repository" : { + "license": "MIT", + "repository": { "type": "git", - "url" : "https://github.com/diosney/node-netfilter.git" + "url": "https://github.com/diosney/node-netfilter.git" + }, + "scripts": { + "test": "node test/*.js" } }