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