OSDN Git Service

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