Setting tag iptables-1.3.8-12
[iptables.git] / iptables-xml.c
1 /* Code to convert iptables-save format to xml format,
2  * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
3  * based on iptables-restor (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
4  * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
5  *
6  * This code is distributed under the terms of GNU GPL v2
7  *
8  * $Id: iptables-xml.c,v 1.4 2006/11/09 12:02:17 azez Exp $
9  */
10
11 #include <getopt.h>
12 #include <sys/errno.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 #include "iptables.h"
18 #include "libiptc/libiptc.h"
19
20 #ifdef DEBUG
21 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
22 #else
23 #define DEBUGP(x, args...)
24 #endif
25
26 /* no need to link with iptables.o */
27 const char *program_name;
28 const char *program_version;
29
30 #ifndef IPTABLES_MULTI
31 int line = 0;
32 void exit_error(enum exittype status, char *msg, ...)
33 {
34         va_list args;
35
36         va_start(args, msg);
37         fprintf(stderr, "%s v%s: ", program_name, program_version);
38         vfprintf(stderr, msg, args);
39         va_end(args);
40         fprintf(stderr, "\n");
41         /* On error paths, make sure that we don't leak memory */
42         exit(status);
43 }
44 #endif
45
46 static void print_usage(const char *name, const char *version)
47             __attribute__ ((noreturn));
48
49 static int verbose = 0;
50 /* Whether to combine actions of sequential rules with identical conditions */
51 static int combine = 0;
52 /* Keeping track of external matches and targets.  */
53 static struct option options[] = {
54         {"verbose", 0, 0, 'v'},
55         {"combine", 0, 0, 'c'},
56         {"help", 0, 0, 'h'},
57         {0}
58 };
59
60 static void
61 print_usage(const char *name, const char *version)
62 {
63         fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
64                 "          [--combine ]\n"
65                 "          [ --verbose ]\n" "      [ --help ]\n", name);
66
67         exit(1);
68 }
69
70 static int
71 parse_counters(char *string, struct ipt_counters *ctr)
72 {
73         if (string != NULL)
74                 return (sscanf
75                         (string, "[%llu:%llu]",
76                          (unsigned long long *) &ctr->pcnt,
77                          (unsigned long long *) &ctr->bcnt) == 2);
78         else
79                 return (0 == 2);
80 }
81
82 /* global new argv and argc */
83 static char *newargv[255];
84 static int newargc = 0;
85
86 static char *oldargv[255];
87 static int oldargc = 0;
88
89 /* arg meta data, were they quoted, frinstance */
90 static int newargvattr[255];
91
92 #define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
93 char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
94 char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
95 char curTable[IPT_TABLE_MAXNAMELEN + 1];
96 char curChain[IPT_CHAIN_MAXNAMELEN + 1];
97
98 typedef struct chain
99 {
100         char *chain;
101         char *policy;
102         struct ipt_counters count;
103         int created;
104 } chain;
105
106 #define maxChains 10240         /* max chains per table */
107 static chain chains[maxChains];
108 static int nextChain = 0;
109
110 /* funCtion adding one argument to newargv, updating newargc 
111  * returns true if argument added, false otherwise */
112 static int
113 add_argv(char *what, int quoted)
114 {
115         DEBUGP("add_argv: %d %s\n", newargc, what);
116         if (what && ((newargc + 1) < sizeof(newargv) / sizeof(char *))) {
117                 newargv[newargc] = strdup(what);
118                 newargvattr[newargc] = quoted;
119                 newargc++;
120                 return 1;
121         } else
122                 return 0;
123 }
124
125 static void
126 free_argv(void)
127 {
128         int i;
129
130         for (i = 0; i < newargc; i++) {
131                 free(newargv[i]);
132                 newargv[i] = NULL;
133         }
134         newargc = 0;
135
136         for (i = 0; i < oldargc; i++) {
137                 free(oldargv[i]);
138                 oldargv[i] = NULL;
139         }
140         oldargc = 0;
141 }
142
143 /* save parsed rule for comparison with next rule 
144    to perform action agregation on duplicate conditions */
145 static void
146 save_argv(void)
147 {
148         int i;
149
150         for (i = 0; i < oldargc; i++)
151                 free(oldargv[i]);
152         oldargc = newargc;
153         newargc = 0;
154         for (i = 0; i < oldargc; i++) {
155                 oldargv[i] = newargv[i];
156                 newargv[i] = NULL;
157         }
158 }
159
160 /* like puts but with xml encoding */
161 static void
162 xmlEncode(char *text)
163 {
164         while (text && *text) {
165                 if ((unsigned char) (*text) >= 127)
166                         printf("&#%d;", (unsigned char) (*text));
167                 else if (*text == '&')
168                         printf("&amp;");
169                 else if (*text == '<')
170                         printf("&lt;");
171                 else if (*text == '>')
172                         printf("&gt;");
173                 else if (*text == '"')
174                         printf("&quot;");
175                 else
176                         putchar(*text);
177                 text++;
178         }
179 }
180
181 /* Output text as a comment, avoiding a double hyphen */
182 static void
183 xmlCommentEscape(char *comment)
184 {
185         int h_count = 0;
186
187         while (comment && *comment) {
188                 if (*comment == '-') {
189                         h_count++;
190                         if (h_count >= 2) {
191                                 h_count = 0;
192                                 putchar(' ');
193                         }
194                         putchar('*');
195                 }
196                 /* strip trailing newline */
197                 if (*comment == '\n' && *(comment + 1) == 0);
198                 else
199                         putchar(*comment);
200                 comment++;
201         }
202 }
203
204 static void
205 xmlComment(char *comment)
206 {
207         printf("<!-- ");
208         xmlCommentEscape(comment);
209         printf(" -->\n");
210 }
211
212 static void
213 xmlAttrS(char *name, char *value)
214 {
215         printf("%s=\"", name);
216         xmlEncode(value);
217         printf("\" ");
218 }
219
220 static void
221 xmlAttrI(char *name, long long int num)
222 {
223         printf("%s=\"%lld\" ", name, num);
224 }
225
226 static void
227 closeChain()
228 {
229         if (curChain[0] == 0)
230                 return;
231
232         if (closeActionTag[0])
233                 printf("%s\n", closeActionTag);
234         closeActionTag[0] = 0;
235         if (closeRuleTag[0])
236                 printf("%s\n", closeRuleTag);
237         closeRuleTag[0] = 0;
238         if (curChain[0])
239                 printf("    </chain>\n");
240         curChain[0] = 0;
241         //lastRule[0]=0;
242 }
243
244 static void
245 openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
246 {
247         closeChain();
248
249         strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
250         curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
251
252         printf("    <chain ");
253         xmlAttrS("name", curChain);
254         if (strcmp(policy, "-") != 0)
255                 xmlAttrS("policy", policy);
256         xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
257         xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
258         if (close) {
259                 printf("%c", close);
260                 curChain[0] = 0;
261         }
262         printf(">\n");
263 }
264
265 static int
266 existsChain(char *chain)
267 {
268         /* open a saved chain */
269         int c = 0;
270
271         if (0 == strcmp(curChain, chain))
272                 return 1;
273         for (c = 0; c < nextChain; c++)
274                 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
275                         return 1;
276         return 0;
277 }
278
279 static void
280 needChain(char *chain)
281 {
282         /* open a saved chain */
283         int c = 0;
284
285         if (0 == strcmp(curChain, chain))
286                 return;
287
288         for (c = 0; c < nextChain; c++)
289                 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
290                         openChain(chains[c].chain, chains[c].policy,
291                                   &(chains[c].count), '\0');
292                         /* And, mark it as done so we don't create 
293                            an empty chain at table-end time */
294                         chains[c].created = 1;
295                 }
296 }
297
298 static void
299 saveChain(char *chain, char *policy, struct ipt_counters *ctr)
300 {
301         if (nextChain >= maxChains) {
302                 exit_error(PARAMETER_PROBLEM,
303                            "%s: line %u chain name invalid\n",
304                            program_name, line);
305                 exit(1);
306         };
307         chains[nextChain].chain = strdup(chain);
308         chains[nextChain].policy = strdup(policy);
309         chains[nextChain].count = *ctr;
310         chains[nextChain].created = 0;
311         nextChain++;
312 }
313
314 static void
315 finishChains()
316 {
317         int c;
318
319         for (c = 0; c < nextChain; c++)
320                 if (!chains[c].created) {
321                         openChain(chains[c].chain, chains[c].policy,
322                                   &(chains[c].count), '/');
323                         free(chains[c].chain);
324                         free(chains[c].policy);
325                 }
326         nextChain = 0;
327 }
328
329 static void
330 closeTable()
331 {
332         closeChain();
333         finishChains();
334         if (curTable[0])
335                 printf("  </table>\n");
336         curTable[0] = 0;
337 }
338
339 static void
340 openTable(char *table)
341 {
342         closeTable();
343
344         strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
345         curTable[IPT_TABLE_MAXNAMELEN] = '\0';
346
347         printf("  <table ");
348         xmlAttrS("name", curTable);
349         printf(">\n");
350 }
351
352 // is char* -j --jump -g or --goto
353 static int
354 isTarget(char *arg)
355 {
356         return ((arg)
357                 && (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
358                     || strcmp((arg), "-g") == 0
359                     || strcmp((arg), "--goto") == 0));
360 }
361
362 // part=-1 means do conditions, part=1 means do rules, part=0 means do both
363 static void
364 do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
365              char *argv[], int argvattr[])
366 {
367         int arg = 1;            // ignore leading -A
368         char invert_next = 0;
369         char *thisChain = NULL;
370         char *spacer = "";      // space when needed to assemble arguments
371         char *level1 = NULL;
372         char *level2 = NULL;
373         char *leveli1 = "        ";
374         char *leveli2 = "          ";
375
376 #define CLOSE_LEVEL(LEVEL) \
377         do { \
378                 if (level ## LEVEL) printf("</%s>\n", \
379                 (leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
380                 level ## LEVEL=NULL;\
381         } while(0)
382
383 #define OPEN_LEVEL(LEVEL,TAG) \
384         do {\
385                 level ## LEVEL=TAG;\
386                 if (leveltag ## LEVEL) {\
387                         printf("%s<%s ", (leveli ## LEVEL), \
388                                 (leveltag ## LEVEL));\
389                         xmlAttrS("type", (TAG)); \
390                 } else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
391         } while(0)
392
393         thisChain = argv[arg++];
394
395         if (part == 1) {        /* skip */
396                 /* use argvattr to tell which arguments were quoted 
397                    to avoid comparing quoted arguments, like comments, to -j, */
398                 while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
399                         arg++;
400         }
401
402         /* Before we start, if the first arg is -[^-] and not -m or -j or -g 
403            then start a dummy <match> tag for old style built-in matches.  
404            We would do this in any case, but no need if it would be empty */
405         if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
406             && strcmp(argv[arg], "-m") != 0) {
407                 OPEN_LEVEL(1, "match");
408                 printf(">\n");
409         }
410         while (arg < argc) {
411                 // If ! is followed by -* then apply to that else output as data
412                 // Stop, if we need to
413                 if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
414                         break;
415                 } else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
416                         if ((arg + 1) < argc && argv[arg + 1][0] == '-')
417                                 invert_next = '!';
418                         else
419                                 printf("%s%s", spacer, argv[arg]);
420                         spacer = " ";
421                 } else if (!argvattr[arg] && isTarget(argv[arg])
422                            && existsChain(argv[arg + 1])
423                            && (2 + arg >= argc)) {
424                         if (!((1 + arg) < argc))
425                                 // no args to -j, -m or -g, ignore & finish loop
426                                 break;
427                         CLOSE_LEVEL(2);
428                         if (level1)
429                                 printf("%s", leveli1);
430                         CLOSE_LEVEL(1);
431                         spacer = "";
432                         invert_next = 0;
433                         if (strcmp(argv[arg], "-g") == 0
434                             || strcmp(argv[arg], "--goto") == 0) {
435                                 /* goto user chain */
436                                 OPEN_LEVEL(1, "goto");
437                                 printf(">\n");
438                                 arg++;
439                                 OPEN_LEVEL(2, argv[arg]);
440                                 printf("/>\n");
441                                 level2 = NULL;
442                         } else {
443                                 /* call user chain */
444                                 OPEN_LEVEL(1, "call");
445                                 printf(">\n");
446                                 arg++;
447                                 OPEN_LEVEL(2, argv[arg]);
448                                 printf("/>\n");
449                                 level2 = NULL;
450                         }
451                 } else if (!argvattr[arg]
452                            && (isTarget(argv[arg])
453                                || strcmp(argv[arg], "-m") == 0
454                                || strcmp(argv[arg], "--module") == 0)) {
455                         if (!((1 + arg) < argc))
456                                 // no args to -j, -m or -g, ignore & finish loop
457                                 break;
458                         CLOSE_LEVEL(2);
459                         if (level1)
460                                 printf("%s", leveli1);
461                         CLOSE_LEVEL(1);
462                         spacer = "";
463                         invert_next = 0;
464                         arg++;
465                         OPEN_LEVEL(1, (argv[arg]));
466                         // Optimize case, can we close this tag already?
467                         if ((arg + 1) >= argc || (!argvattr[arg + 1]
468                                                   && (isTarget(argv[arg + 1])
469                                                       || strcmp(argv[arg + 1],
470                                                                 "-m") == 0
471                                                       || strcmp(argv[arg + 1],
472                                                                 "--module") ==
473                                                       0))) {
474                                 printf(" />\n");
475                                 level1 = NULL;
476                         } else {
477                                 printf(">\n");
478                         }
479                 } else if (!argvattr[arg] && argv[arg][0] == '-') {
480                         char *tag;
481                         CLOSE_LEVEL(2);
482                         // Skip past any -
483                         tag = argv[arg];
484                         while (*tag == '-' && *tag)
485                                 tag++;
486
487                         spacer = "";
488                         OPEN_LEVEL(2, tag);
489                         if (invert_next)
490                                 printf(" invert=\"1\"");
491                         invert_next = 0;
492
493                         // Optimize case, can we close this tag already?
494                         if (!((arg + 1) < argc)
495                             || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
496                                 printf(" />\n");
497                                 level2 = NULL;
498                         } else {
499                                 printf(">");
500                         }
501                 } else {        // regular data
502                         char *spaces = strchr(argv[arg], ' ');
503                         printf("%s", spacer);
504                         if (spaces || argvattr[arg])
505                                 printf("&quot;");
506                         // if argv[arg] contains a space, enclose in quotes
507                         xmlEncode(argv[arg]);
508                         if (spaces || argvattr[arg])
509                                 printf("&quot;");
510                         spacer = " ";
511                 }
512                 arg++;
513         }
514         CLOSE_LEVEL(2);
515         if (level1)
516                 printf("%s", leveli1);
517         CLOSE_LEVEL(1);
518
519         return;
520 }
521
522 static int
523 compareRules()
524 {
525         /* compare arguments up to -j or -g for match.
526            NOTE: We don't want to combine actions if there were no criteria 
527            in each rule, or rules didn't have an action 
528            NOTE: Depends on arguments being in some kind of "normal" order which 
529            is the case when processing the ACTUAL output of actual iptables-save 
530            rather than a file merely in a compatable format */
531
532         int old = 0;
533         int new = 0;
534
535         int compare = 0;
536
537         while (new < newargc && old < oldargc) {
538                 if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
539                         compare = 1;
540                         break;
541                 }
542                 // break when old!=new
543                 if (strcmp(oldargv[old], newargv[new]) != 0) {
544                         compare = 0;
545                         break;
546                 }
547
548                 old++;
549                 new++;
550         }
551         // We won't match unless both rules had a target. 
552         // This means we don't combine target-less rules, which is good
553
554         return compare == 1;
555 }
556
557 /* has a nice parsed rule starting with -A */
558 static void
559 do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
560 {
561         /* are these conditions the same as the previous rule?
562          * If so, skip arg straight to -j or -g */
563         if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
564                 xmlComment("Combine action from next rule");
565         } else {
566
567                 if (closeActionTag[0]) {
568                         printf("%s\n", closeActionTag);
569                         closeActionTag[0] = 0;
570                 }
571                 if (closeRuleTag[0]) {
572                         printf("%s\n", closeRuleTag);
573                         closeRuleTag[0] = 0;
574                 }
575
576                 printf("      <rule ");
577                 //xmlAttrS("table",curTable); // not needed in full mode 
578                 //xmlAttrS("chain",argv[1]); // not needed in full mode 
579                 if (pcnt)
580                         xmlAttrS("packet-count", pcnt);
581                 if (bcnt)
582                         xmlAttrS("byte-count", bcnt);
583                 printf(">\n");
584
585                 strncpy(closeRuleTag, "      </rule>\n", IPT_TABLE_MAXNAMELEN);
586                 closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
587
588                 /* no point in writing out condition if there isn't one */
589                 if (argc >= 3 && !isTarget(argv[2])) {
590                         printf("       <conditions>\n");
591                         do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
592                         printf("       </conditions>\n");
593                 }
594         }
595         /* Write out the action */
596         //do_rule_part("action","arg",1,argc,argv,argvattr);
597         if (!closeActionTag[0]) {
598                 printf("       <actions>\n");
599                 strncpy(closeActionTag, "       </actions>\n",
600                         IPT_TABLE_MAXNAMELEN);
601                 closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
602         }
603         do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
604 }
605
606
607 #ifdef IPTABLES_MULTI
608 int
609 iptables_xml_main(int argc, char *argv[])
610 #else
611 int
612 main(int argc, char *argv[])
613 #endif
614 {
615         char buffer[10240];
616         int c;
617         FILE *in;
618
619         program_name = "iptables-xml";
620         program_version = IPTABLES_VERSION;
621         line = 0;
622
623         while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
624                 switch (c) {
625                 case 'c':
626                         combine = 1;
627                         break;
628                 case 'v':
629                         printf("xptables-xml\n");
630                         verbose = 1;
631                         break;
632                 case 'h':
633                         print_usage("iptables-xml", IPTABLES_VERSION);
634                         break;
635                 }
636         }
637
638         if (optind == argc - 1) {
639                 in = fopen(argv[optind], "r");
640                 if (!in) {
641                         fprintf(stderr, "Can't open %s: %s", argv[optind],
642                                 strerror(errno));
643                         exit(1);
644                 }
645         } else if (optind < argc) {
646                 fprintf(stderr, "Unknown arguments found on commandline");
647                 exit(1);
648         } else
649                 in = stdin;
650
651         printf("<iptables-rules version=\"1.0\">\n");
652
653         /* Grab standard input. */
654         while (fgets(buffer, sizeof(buffer), in)) {
655                 int ret = 0;
656
657                 line++;
658
659                 if (buffer[0] == '\n')
660                         continue;
661                 else if (buffer[0] == '#') {
662                         xmlComment(buffer);
663                         continue;
664                 }
665
666                 if (verbose) {
667                         printf("<!-- line %d ", line);
668                         xmlCommentEscape(buffer);
669                         printf(" -->\n");
670                 }
671
672                 if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
673                         DEBUGP("Calling commit\n");
674                         closeTable();
675                         ret = 1;
676                 } else if ((buffer[0] == '*')) {
677                         /* New table */
678                         char *table;
679
680                         table = strtok(buffer + 1, " \t\n");
681                         DEBUGP("line %u, table '%s'\n", line, table);
682                         if (!table) {
683                                 exit_error(PARAMETER_PROBLEM,
684                                            "%s: line %u table name invalid\n",
685                                            program_name, line);
686                                 exit(1);
687                         }
688                         openTable(table);
689
690                         ret = 1;
691                 } else if ((buffer[0] == ':') && (curTable[0])) {
692                         /* New chain. */
693                         char *policy, *chain;
694                         struct ipt_counters count;
695                         char *ctrs;
696
697                         chain = strtok(buffer + 1, " \t\n");
698                         DEBUGP("line %u, chain '%s'\n", line, chain);
699                         if (!chain) {
700                                 exit_error(PARAMETER_PROBLEM,
701                                            "%s: line %u chain name invalid\n",
702                                            program_name, line);
703                                 exit(1);
704                         }
705
706                         DEBUGP("Creating new chain '%s'\n", chain);
707
708                         policy = strtok(NULL, " \t\n");
709                         DEBUGP("line %u, policy '%s'\n", line, policy);
710                         if (!policy) {
711                                 exit_error(PARAMETER_PROBLEM,
712                                            "%s: line %u policy invalid\n",
713                                            program_name, line);
714                                 exit(1);
715                         }
716
717                         ctrs = strtok(NULL, " \t\n");
718                         parse_counters(ctrs, &count);
719                         saveChain(chain, policy, &count);
720
721                         ret = 1;
722                 } else if (curTable[0]) {
723                         int a;
724                         char *ptr = buffer;
725                         char *pcnt = NULL;
726                         char *bcnt = NULL;
727                         char *parsestart;
728                         char *chain = NULL;
729
730                         /* the parser */
731                         char *param_start, *curchar;
732                         int quote_open, quoted;
733
734                         /* reset the newargv */
735                         newargc = 0;
736
737                         if (buffer[0] == '[') {
738                                 /* we have counters in our input */
739                                 ptr = strchr(buffer, ']');
740                                 if (!ptr)
741                                         exit_error(PARAMETER_PROBLEM,
742                                                    "Bad line %u: need ]\n",
743                                                    line);
744
745                                 pcnt = strtok(buffer + 1, ":");
746                                 if (!pcnt)
747                                         exit_error(PARAMETER_PROBLEM,
748                                                    "Bad line %u: need :\n",
749                                                    line);
750
751                                 bcnt = strtok(NULL, "]");
752                                 if (!bcnt)
753                                         exit_error(PARAMETER_PROBLEM,
754                                                    "Bad line %u: need ]\n",
755                                                    line);
756
757                                 /* start command parsing after counter */
758                                 parsestart = ptr + 1;
759                         } else {
760                                 /* start command parsing at start of line */
761                                 parsestart = buffer;
762                         }
763
764
765                         /* This is a 'real' parser crafted in artist mode
766                          * not hacker mode. If the author can live with that
767                          * then so can everyone else */
768
769                         quote_open = 0;
770                         /* We need to know which args were quoted so we 
771                            can preserve quote */
772                         quoted = 0;
773                         param_start = parsestart;
774
775                         for (curchar = parsestart; *curchar; curchar++) {
776                                 if (*curchar == '"') {
777                                         /* quote_open cannot be true if there
778                                          * was no previous character.  Thus, 
779                                          * curchar-1 has to be within bounds */
780                                         if (quote_open &&
781                                             *(curchar - 1) != '\\') {
782                                                 quote_open = 0;
783                                                 *curchar = ' ';
784                                         } else {
785                                                 quote_open = 1;
786                                                 quoted = 1;
787                                                 param_start++;
788                                         }
789                                 }
790                                 if (*curchar == ' '
791                                     || *curchar == '\t' || *curchar == '\n') {
792                                         char param_buffer[1024];
793                                         int param_len = curchar - param_start;
794
795                                         if (quote_open)
796                                                 continue;
797
798                                         if (!param_len) {
799                                                 /* two spaces? */
800                                                 param_start++;
801                                                 continue;
802                                         }
803
804                                         /* end of one parameter */
805                                         strncpy(param_buffer, param_start,
806                                                 param_len);
807                                         *(param_buffer + param_len) = '\0';
808
809                                         /* check if table name specified */
810                                         if (!strncmp(param_buffer, "-t", 3)
811                                             || !strncmp(param_buffer,
812                                                         "--table", 8)) {
813                                                 exit_error(PARAMETER_PROBLEM,
814                                                            "Line %u seems to have a "
815                                                            "-t table option.\n",
816                                                            line);
817                                                 exit(1);
818                                         }
819
820                                         add_argv(param_buffer, quoted);
821                                         if (newargc >= 2
822                                             && 0 ==
823                                             strcmp(newargv[newargc - 2], "-A"))
824                                                 chain = newargv[newargc - 1];
825                                         quoted = 0;
826                                         param_start += param_len + 1;
827                                 } else {
828                                         /* regular character, skip */
829                                 }
830                         }
831
832                         DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
833                                newargc, curTable);
834
835                         for (a = 0; a < newargc; a++)
836                                 DEBUGP("argv[%u]: %s\n", a, newargv[a]);
837
838                         needChain(chain);// Should we explicitly look for -A
839                         do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
840
841                         save_argv();
842                         ret = 1;
843                 }
844                 if (!ret) {
845                         fprintf(stderr, "%s: line %u failed\n",
846                                 program_name, line);
847                         exit(1);
848                 }
849         }
850         if (curTable[0]) {
851                 fprintf(stderr, "%s: COMMIT expected at line %u\n",
852                         program_name, line + 1);
853                 exit(1);
854         }
855
856         printf("</iptables-rules>\n");
857         free_argv();
858
859         return 0;
860 }