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