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-restore (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 
      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
     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
     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
    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
    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
    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
    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("&amp;");
    161 		else if (*text == '<')
    162 			printf("&lt;");
    163 		else if (*text == '>')
    164 			printf("&gt;");
    165 		else if (*text == '"')
    166 			printf("&quot;");
    167 		else
    168 			putchar(*text);
    169 		text++;
    170 	}
    171 }
    172 
    173 /* Output text as a comment, avoiding a double hyphen */
    174 static void
    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
    197 xmlComment(char *comment)
    198 {
    199 	printf("<!-- ");
    200 	xmlCommentEscape(comment);
    201 	printf(" -->\n");
    202 }
    203 
    204 static void
    205 xmlAttrS(char *name, char *value)
    206 {
    207 	printf("%s=\"", name);
    208 	xmlEncode(value);
    209 	printf("\" ");
    210 }
    211 
    212 static void
    213 xmlAttrI(char *name, long long int num)
    214 {
    215 	printf("%s=\"%lld\" ", name, num);
    216 }
    217 
    218 static 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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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("&quot;");
    514 			// if argv[arg] contains a space, enclose in quotes
    515 			xmlEncode(argv[arg]);
    516 			if (spaces || argvattr[arg])
    517 				printf("&quot;");
    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
    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
    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
    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