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