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