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