summaryrefslogtreecommitdiffstats
path: root/iptables-xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables-xml.c')
-rw-r--r--iptables-xml.c859
1 files changed, 859 insertions, 0 deletions
diff --git a/iptables-xml.c b/iptables-xml.c
new file mode 100644
index 00000000..34437e07
--- /dev/null
+++ b/iptables-xml.c
@@ -0,0 +1,859 @@
+/* Code to convert iptables-save format to xml format,
+ * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
+ * based on iptables-restor (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-xml.c,v 1.4 2006/11/09 12:02:17 azez Exp $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+/* no need to link with iptables.o */
+const char *program_name;
+const char *program_version;
+int line = 0;
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", program_name, program_version);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ /* On error paths, make sure that we don't leak memory */
+ exit(status);
+}
+
+static void print_usage(const char *name, const char *version)
+ __attribute__ ((noreturn));
+
+static int verbose = 0;
+/* Whether to combine actions of sequential rules with identical conditions */
+static int combine = 0;
+/* Keeping track of external matches and targets. */
+static struct option options[] = {
+ {"verbose", 0, 0, 'v'},
+ {"combine", 0, 0, 'c'},
+ {"help", 0, 0, 'h'},
+ {0}
+};
+
+static void
+print_usage(const char *name, const char *version)
+{
+ fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
+ " [--combine ]\n"
+ " [ --verbose ]\n" " [ --help ]\n", name);
+
+ exit(1);
+}
+
+int
+parse_counters(char *string, struct ipt_counters *ctr)
+{
+ if (string != NULL)
+ return (sscanf
+ (string, "[%llu:%llu]",
+ (unsigned long long *) &ctr->pcnt,
+ (unsigned long long *) &ctr->bcnt) == 2);
+ else
+ return (0 == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc = 0;
+
+static char *oldargv[255];
+static int oldargc = 0;
+
+/* arg meta data, were they quoted, frinstance */
+static int newargvattr[255];
+
+#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
+char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
+char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
+char curTable[IPT_TABLE_MAXNAMELEN + 1];
+char curChain[IPT_CHAIN_MAXNAMELEN + 1];
+
+typedef struct chain
+{
+ char *chain;
+ char *policy;
+ struct ipt_counters count;
+ int created;
+} chain;
+
+#define maxChains 10240 /* max chains per table */
+static chain chains[maxChains];
+static int nextChain = 0;
+
+/* funCtion adding one argument to newargv, updating newargc
+ * returns true if argument added, false otherwise */
+static int
+add_argv(char *what, int quoted)
+{
+ DEBUGP("add_argv: %d %s\n", newargc, what);
+ if (what && ((newargc + 1) < sizeof(newargv) / sizeof(char *))) {
+ newargv[newargc] = strdup(what);
+ newargvattr[newargc] = quoted;
+ newargc++;
+ return 1;
+ } else
+ return 0;
+}
+
+static void
+free_argv(void)
+{
+ int i;
+
+ for (i = 0; i < newargc; i++) {
+ free(newargv[i]);
+ newargv[i] = NULL;
+ }
+ newargc = 0;
+
+ for (i = 0; i < oldargc; i++) {
+ free(oldargv[i]);
+ oldargv[i] = NULL;
+ }
+ oldargc = 0;
+}
+
+/* save parsed rule for comparison with next rule
+ to perform action agregation on duplicate conditions */
+static void
+save_argv(void)
+{
+ int i;
+
+ for (i = 0; i < oldargc; i++)
+ free(oldargv[i]);
+ oldargc = newargc;
+ newargc = 0;
+ for (i = 0; i < oldargc; i++) {
+ oldargv[i] = newargv[i];
+ newargv[i] = NULL;
+ }
+}
+
+/* like puts but with xml encoding */
+static void
+xmlEncode(char *text)
+{
+ while (text && *text) {
+ if ((unsigned char) (*text) >= 127)
+ printf("&#%d;", (unsigned char) (*text));
+ else if (*text == '&')
+ printf("&amp;");
+ else if (*text == '<')
+ printf("&lt;");
+ else if (*text == '>')
+ printf("&gt;");
+ else if (*text == '"')
+ printf("&quot;");
+ else
+ putchar(*text);
+ text++;
+ }
+}
+
+/* Output text as a comment, avoiding a double hyphen */
+static void
+xmlCommentEscape(char *comment)
+{
+ int h_count = 0;
+
+ while (comment && *comment) {
+ if (*comment == '-') {
+ h_count++;
+ if (h_count >= 2) {
+ h_count = 0;
+ putchar(' ');
+ }
+ putchar('*');
+ }
+ /* strip trailing newline */
+ if (*comment == '\n' && *(comment + 1) == 0);
+ else
+ putchar(*comment);
+ comment++;
+ }
+}
+
+static void
+xmlComment(char *comment)
+{
+ printf("<!-- ");
+ xmlCommentEscape(comment);
+ printf(" -->\n");
+}
+
+static void
+xmlAttrS(char *name, char *value)
+{
+ printf("%s=\"", name);
+ xmlEncode(value);
+ printf("\" ");
+}
+
+static void
+xmlAttrI(char *name, long long int num)
+{
+ printf("%s=\"%lld\" ", name, num);
+}
+
+static void
+closeChain()
+{
+ if (curChain[0] == 0)
+ return;
+
+ if (closeActionTag[0])
+ printf("%s\n", closeActionTag);
+ closeActionTag[0] = 0;
+ if (closeRuleTag[0])
+ printf("%s\n", closeRuleTag);
+ closeRuleTag[0] = 0;
+ if (curChain[0])
+ printf(" </chain>\n");
+ curChain[0] = 0;
+ //lastRule[0]=0;
+}
+
+static void
+openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
+{
+ closeChain();
+
+ strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
+ curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
+
+ printf(" <chain ");
+ xmlAttrS("name", curChain);
+ if (strcmp(policy, "-") != 0)
+ xmlAttrS("policy", policy);
+ xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
+ xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
+ if (close) {
+ printf("%c", close);
+ curChain[0] = 0;
+ }
+ printf(">\n");
+}
+
+static int
+existsChain(char *chain)
+{
+ /* open a saved chain */
+ int c = 0;
+
+ if (0 == strcmp(curChain, chain))
+ return 1;
+ for (c = 0; c < nextChain; c++)
+ if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
+ return 1;
+ return 0;
+}
+
+static void
+needChain(char *chain)
+{
+ /* open a saved chain */
+ int c = 0;
+
+ if (0 == strcmp(curChain, chain))
+ return;
+
+ for (c = 0; c < nextChain; c++)
+ if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
+ openChain(chains[c].chain, chains[c].policy,
+ &(chains[c].count), '\0');
+ /* And, mark it as done so we don't create
+ an empty chain at table-end time */
+ chains[c].created = 1;
+ }
+}
+
+static void
+saveChain(char *chain, char *policy, struct ipt_counters *ctr)
+{
+ if (nextChain >= maxChains) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u chain name invalid\n",
+ program_name, line);
+ exit(1);
+ };
+ chains[nextChain].chain = strdup(chain);
+ chains[nextChain].policy = strdup(policy);
+ chains[nextChain].count = *ctr;
+ chains[nextChain].created = 0;
+ nextChain++;
+}
+
+static void
+finishChains()
+{
+ int c;
+
+ for (c = 0; c < nextChain; c++)
+ if (!chains[c].created) {
+ openChain(chains[c].chain, chains[c].policy,
+ &(chains[c].count), '/');
+ free(chains[c].chain);
+ free(chains[c].policy);
+ }
+ nextChain = 0;
+}
+
+static void
+closeTable()
+{
+ closeChain();
+ finishChains();
+ if (curTable[0])
+ printf(" </table>\n");
+ curTable[0] = 0;
+}
+
+static void
+openTable(char *table)
+{
+ closeTable();
+
+ strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
+ curTable[IPT_TABLE_MAXNAMELEN] = '\0';
+
+ printf(" <table ");
+ xmlAttrS("name", curTable);
+ printf(">\n");
+}
+
+// is char* -j --jump -g or --goto
+static int
+isTarget(char *arg)
+{
+ return ((arg)
+ && (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
+ || strcmp((arg), "-g") == 0
+ || strcmp((arg), "--goto") == 0));
+}
+
+// part=-1 means do conditions, part=1 means do rules, part=0 means do both
+static void
+do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
+ char *argv[], int argvattr[])
+{
+ int arg = 1; // ignore leading -A
+ char invert_next = 0;
+ char *thisChain = NULL;
+ char *spacer = ""; // space when needed to assemble arguments
+ char *level1 = NULL;
+ char *level2 = NULL;
+ char *leveli1 = " ";
+ char *leveli2 = " ";
+
+#define CLOSE_LEVEL(LEVEL) \
+ do { \
+ if (level ## LEVEL) printf("</%s>\n", \
+ (leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
+ level ## LEVEL=NULL;\
+ } while(0)
+
+#define OPEN_LEVEL(LEVEL,TAG) \
+ do {\
+ level ## LEVEL=TAG;\
+ if (leveltag ## LEVEL) {\
+ printf("%s<%s ", (leveli ## LEVEL), \
+ (leveltag ## LEVEL));\
+ xmlAttrS("type", (TAG)); \
+ } else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
+ } while(0)
+
+ thisChain = argv[arg++];
+
+ if (part == 1) { /* skip */
+ /* use argvattr to tell which arguments were quoted
+ to avoid comparing quoted arguments, like comments, to -j, */
+ while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
+ arg++;
+ }
+
+ /* Before we start, if the first arg is -[^-] and not -m or -j or -g
+ then start a dummy <match> tag for old style built-in matches.
+ We would do this in any case, but no need if it would be empty */
+ if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
+ && strcmp(argv[arg], "-m") != 0) {
+ OPEN_LEVEL(1, "match");
+ printf(">\n");
+ }
+ while (arg < argc) {
+ // If ! is followed by -* then apply to that else output as data
+ // Stop, if we need to
+ if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
+ break;
+ } else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
+ if ((arg + 1) < argc && argv[arg + 1][0] == '-')
+ invert_next = '!';
+ else
+ printf("%s%s", spacer, argv[arg]);
+ spacer = " ";
+ } else if (!argvattr[arg] && isTarget(argv[arg])
+ && existsChain(argv[arg + 1])
+ && (2 + arg >= argc)) {
+ if (!((1 + arg) < argc))
+ // no args to -j, -m or -g, ignore & finish loop
+ break;
+ CLOSE_LEVEL(2);
+ if (level1)
+ printf("%s", leveli1);
+ CLOSE_LEVEL(1);
+ spacer = "";
+ invert_next = 0;
+ if (strcmp(argv[arg], "-g") == 0
+ || strcmp(argv[arg], "--goto") == 0) {
+ /* goto user chain */
+ OPEN_LEVEL(1, "goto");
+ printf(">\n");
+ arg++;
+ OPEN_LEVEL(2, argv[arg]);
+ printf("/>\n");
+ level2 = NULL;
+ } else {
+ /* call user chain */
+ OPEN_LEVEL(1, "call");
+ printf(">\n");
+ arg++;
+ OPEN_LEVEL(2, argv[arg]);
+ printf("/>\n");
+ level2 = NULL;
+ }
+ } else if (!argvattr[arg]
+ && (isTarget(argv[arg])
+ || strcmp(argv[arg], "-m") == 0
+ || strcmp(argv[arg], "--module") == 0)) {
+ if (!((1 + arg) < argc))
+ // no args to -j, -m or -g, ignore & finish loop
+ break;
+ CLOSE_LEVEL(2);
+ if (level1)
+ printf("%s", leveli1);
+ CLOSE_LEVEL(1);
+ spacer = "";
+ invert_next = 0;
+ arg++;
+ OPEN_LEVEL(1, (argv[arg]));
+ // Optimize case, can we close this tag already?
+ if ((arg + 1) >= argc || (!argvattr[arg + 1]
+ && (isTarget(argv[arg + 1])
+ || strcmp(argv[arg + 1],
+ "-m") == 0
+ || strcmp(argv[arg + 1],
+ "--module") ==
+ 0))) {
+ printf(" />\n");
+ level1 = NULL;
+ } else {
+ printf(">\n");
+ }
+ } else if (!argvattr[arg] && argv[arg][0] == '-') {
+ char *tag;
+ CLOSE_LEVEL(2);
+ // Skip past any -
+ tag = argv[arg];
+ while (*tag == '-' && *tag)
+ tag++;
+
+ spacer = "";
+ OPEN_LEVEL(2, tag);
+ if (invert_next)
+ printf(" invert=\"1\"");
+ invert_next = 0;
+
+ // Optimize case, can we close this tag already?
+ if (!((arg + 1) < argc)
+ || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
+ printf(" />\n");
+ level2 = NULL;
+ } else {
+ printf(">");
+ }
+ } else { // regular data
+ char *spaces = strchr(argv[arg], ' ');
+ printf("%s", spacer);
+ if (spaces || argvattr[arg])
+ printf("&quot;");
+ // if argv[arg] contains a space, enclose in quotes
+ xmlEncode(argv[arg]);
+ if (spaces || argvattr[arg])
+ printf("&quot;");
+ spacer = " ";
+ }
+ arg++;
+ }
+ CLOSE_LEVEL(2);
+ if (level1)
+ printf("%s", leveli1);
+ CLOSE_LEVEL(1);
+
+ return;
+}
+
+static int
+compareRules()
+{
+ /* compare arguments up to -j or -g for match.
+ NOTE: We don't want to combine actions if there were no criteria
+ in each rule, or rules didn't have an action
+ NOTE: Depends on arguments being in some kind of "normal" order which
+ is the case when processing the ACTUAL output of actual iptables-save
+ rather than a file merely in a compatable format */
+
+ int old = 0;
+ int new = 0;
+
+ int compare = 0;
+
+ while (new < newargc && old < oldargc) {
+ if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
+ compare = 1;
+ break;
+ }
+ // break when old!=new
+ if (strcmp(oldargv[old], newargv[new]) != 0) {
+ compare = 0;
+ break;
+ }
+
+ old++;
+ new++;
+ }
+ // We won't match unless both rules had a target.
+ // This means we don't combine target-less rules, which is good
+
+ return compare == 1;
+}
+
+/* has a nice parsed rule starting with -A */
+static void
+do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
+{
+ /* are these conditions the same as the previous rule?
+ * If so, skip arg straight to -j or -g */
+ if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
+ xmlComment("Combine action from next rule");
+ } else {
+
+ if (closeActionTag[0]) {
+ printf("%s\n", closeActionTag);
+ closeActionTag[0] = 0;
+ }
+ if (closeRuleTag[0]) {
+ printf("%s\n", closeRuleTag);
+ closeRuleTag[0] = 0;
+ }
+
+ printf(" <rule ");
+ //xmlAttrS("table",curTable); // not needed in full mode
+ //xmlAttrS("chain",argv[1]); // not needed in full mode
+ if (pcnt)
+ xmlAttrS("packet-count", pcnt);
+ if (bcnt)
+ xmlAttrS("byte-count", bcnt);
+ printf(">\n");
+
+ strncpy(closeRuleTag, " </rule>\n", IPT_TABLE_MAXNAMELEN);
+ closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
+
+ /* no point in writing out condition if there isn't one */
+ if (argc >= 3 && !isTarget(argv[2])) {
+ printf(" <conditions>\n");
+ do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
+ printf(" </conditions>\n");
+ }
+ }
+ /* Write out the action */
+ //do_rule_part("action","arg",1,argc,argv,argvattr);
+ if (!closeActionTag[0]) {
+ printf(" <actions>\n");
+ strncpy(closeActionTag, " </actions>\n",
+ IPT_TABLE_MAXNAMELEN);
+ closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
+ }
+ do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
+}
+
+
+#ifdef IPTABLES_MULTI
+int
+iptables_restore_main(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+ char buffer[10240];
+ int c;
+ FILE *in;
+
+ program_name = "iptables-xml";
+ program_version = IPTABLES_VERSION;
+ line = 0;
+
+ while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ combine = 1;
+ break;
+ case 'v':
+ printf("xptables-xml\n");
+ verbose = 1;
+ break;
+ case 'h':
+ print_usage("iptables-xml", IPTABLES_VERSION);
+ break;
+ }
+ }
+
+ if (optind == argc - 1) {
+ in = fopen(argv[optind], "r");
+ if (!in) {
+ fprintf(stderr, "Can't open %s: %s", argv[optind],
+ strerror(errno));
+ exit(1);
+ }
+ } else if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline");
+ exit(1);
+ } else
+ in = stdin;
+
+ printf("<iptables-rules version=\"1.0\">\n");
+
+ /* Grab standard input. */
+ while (fgets(buffer, sizeof(buffer), in)) {
+ int ret = 0;
+
+ line++;
+
+ if (buffer[0] == '\n')
+ continue;
+ else if (buffer[0] == '#') {
+ xmlComment(buffer);
+ continue;
+ }
+
+ if (verbose) {
+ printf("<!-- line %d ", line);
+ xmlCommentEscape(buffer);
+ printf(" -->\n");
+ }
+
+ if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
+ DEBUGP("Calling commit\n");
+ closeTable();
+ ret = 1;
+ } else if ((buffer[0] == '*')) {
+ /* New table */
+ char *table;
+
+ table = strtok(buffer + 1, " \t\n");
+ DEBUGP("line %u, table '%s'\n", line, table);
+ if (!table) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u table name invalid\n",
+ program_name, line);
+ exit(1);
+ }
+ openTable(table);
+
+ ret = 1;
+ } else if ((buffer[0] == ':') && (curTable[0])) {
+ /* New chain. */
+ char *policy, *chain;
+ struct ipt_counters count;
+ char *ctrs;
+
+ chain = strtok(buffer + 1, " \t\n");
+ DEBUGP("line %u, chain '%s'\n", line, chain);
+ if (!chain) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u chain name invalid\n",
+ program_name, line);
+ exit(1);
+ }
+
+ DEBUGP("Creating new chain '%s'\n", chain);
+
+ policy = strtok(NULL, " \t\n");
+ DEBUGP("line %u, policy '%s'\n", line, policy);
+ if (!policy) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u policy invalid\n",
+ program_name, line);
+ exit(1);
+ }
+
+ ctrs = strtok(NULL, " \t\n");
+ parse_counters(ctrs, &count);
+ saveChain(chain, policy, &count);
+
+ ret = 1;
+ } else if (curTable[0]) {
+ int a;
+ char *ptr = buffer;
+ char *pcnt = NULL;
+ char *bcnt = NULL;
+ char *parsestart;
+ char *chain = NULL;
+
+ /* the parser */
+ char *param_start, *curchar;
+ int quote_open, quoted;
+
+ /* reset the newargv */
+ newargc = 0;
+
+ if (buffer[0] == '[') {
+ /* we have counters in our input */
+ ptr = strchr(buffer, ']');
+ if (!ptr)
+ exit_error(PARAMETER_PROBLEM,
+ "Bad line %u: need ]\n",
+ line);
+
+ pcnt = strtok(buffer + 1, ":");
+ if (!pcnt)
+ exit_error(PARAMETER_PROBLEM,
+ "Bad line %u: need :\n",
+ line);
+
+ bcnt = strtok(NULL, "]");
+ if (!bcnt)
+ exit_error(PARAMETER_PROBLEM,
+ "Bad line %u: need ]\n",
+ line);
+
+ /* start command parsing after counter */
+ parsestart = ptr + 1;
+ } else {
+ /* start command parsing at start of line */
+ parsestart = buffer;
+ }
+
+
+ /* This is a 'real' parser crafted in artist mode
+ * not hacker mode. If the author can live with that
+ * then so can everyone else */
+
+ quote_open = 0;
+ /* We need to know which args were quoted so we
+ can preserve quote */
+ quoted = 0;
+ param_start = parsestart;
+
+ for (curchar = parsestart; *curchar; curchar++) {
+ if (*curchar == '"') {
+ /* quote_open cannot be true if there
+ * was no previous character. Thus,
+ * curchar-1 has to be within bounds */
+ if (quote_open &&
+ *(curchar - 1) != '\\') {
+ quote_open = 0;
+ *curchar = ' ';
+ } else {
+ quote_open = 1;
+ quoted = 1;
+ param_start++;
+ }
+ }
+ if (*curchar == ' '
+ || *curchar == '\t' || *curchar == '\n') {
+ char param_buffer[1024];
+ int param_len = curchar - param_start;
+
+ if (quote_open)
+ continue;
+
+ if (!param_len) {
+ /* two spaces? */
+ param_start++;
+ continue;
+ }
+
+ /* end of one parameter */
+ strncpy(param_buffer, param_start,
+ param_len);
+ *(param_buffer + param_len) = '\0';
+
+ /* check if table name specified */
+ if (!strncmp(param_buffer, "-t", 3)
+ || !strncmp(param_buffer,
+ "--table", 8)) {
+ exit_error(PARAMETER_PROBLEM,
+ "Line %u seems to have a "
+ "-t table option.\n",
+ line);
+ exit(1);
+ }
+
+ add_argv(param_buffer, quoted);
+ if (newargc >= 2
+ && 0 ==
+ strcmp(newargv[newargc - 2], "-A"))
+ chain = newargv[newargc - 1];
+ quoted = 0;
+ param_start += param_len + 1;
+ } else {
+ /* regular character, skip */
+ }
+ }
+
+ DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+ newargc, curTable);
+
+ for (a = 0; a < newargc; a++)
+ DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+ needChain(chain);// Should we explicitly look for -A
+ do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
+
+ save_argv();
+ ret = 1;
+ }
+ if (!ret) {
+ fprintf(stderr, "%s: line %u failed\n",
+ program_name, line);
+ exit(1);
+ }
+ }
+ if (curTable[0]) {
+ fprintf(stderr, "%s: COMMIT expected at line %u\n",
+ program_name, line + 1);
+ exit(1);
+ }
+
+ printf("</iptables-rules>\n");
+ free_argv();
+
+ return 0;
+}