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 #ifndef IPTABLES_MULTI
     27 int line = 0;
     28 #endif
     29 
     30 struct xtables_globals iptables_xml_globals = {
     31 	.option_offset = 0,
     32 	.program_version = IPTABLES_VERSION,
     33 	.program_name = "iptables-xml",
     34 };
     35 #define prog_name iptables_xml_globals.program_name
     36 #define prog_vers iptables_xml_globals.program_version
     37 
     38 static void print_usage(const char *name, const char *version)
     39 	    __attribute__ ((noreturn));
     40 
     41 static int verbose = 0;
     42 /* Whether to combine actions of sequential rules with identical conditions */
     43 static int combine = 0;
     44 /* Keeping track of external matches and targets.  */
     45 static struct option options[] = {
     46 	{"verbose", 0, NULL, 'v'},
     47 	{"combine", 0, NULL, 'c'},
     48 	{"help", 0, NULL, 'h'},
     49 	{ .name = NULL }
     50 };
     51 
     52 static void
     53 print_usage(const char *name, const char *version)
     54 {
     55 	fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
     56 		"          [--combine ]\n"
     57 		"	   [ --verbose ]\n" "	   [ --help ]\n", name);
     58 
     59 	exit(1);
     60 }
     61 
     62 static int
     63 parse_counters(char *string, struct ipt_counters *ctr)
     64 {
     65 	__u64 *pcnt, *bcnt;
     66 
     67 	if (string != NULL) {
     68 		pcnt = &ctr->pcnt;
     69 		bcnt = &ctr->bcnt;
     70 		return (sscanf
     71 			(string, "[%llu:%llu]",
     72 			 (unsigned long long *)pcnt,
     73 			 (unsigned long long *)bcnt) == 2);
     74 	} else
     75 		return (0 == 2);
     76 }
     77 
     78 /* global new argv and argc */
     79 static char *newargv[255];
     80 static unsigned int newargc = 0;
     81 
     82 static char *oldargv[255];
     83 static unsigned int oldargc = 0;
     84 
     85 /* arg meta data, were they quoted, frinstance */
     86 static int newargvattr[255];
     87 
     88 #define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
     89 static char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
     90 static char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
     91 static char curTable[IPT_TABLE_MAXNAMELEN + 1];
     92 static char curChain[IPT_CHAIN_MAXNAMELEN + 1];
     93 
     94 struct chain {
     95 	char *chain;
     96 	char *policy;
     97 	struct ipt_counters count;
     98 	int created;
     99 };
    100 
    101 #define maxChains 10240		/* max chains per table */
    102 static struct chain chains[maxChains];
    103 static int nextChain = 0;
    104 
    105 /* funCtion adding one argument to newargv, updating newargc
    106  * returns true if argument added, false otherwise */
    107 static int
    108 add_argv(char *what, int quoted)
    109 {
    110 	DEBUGP("add_argv: %d %s\n", newargc, what);
    111 	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
    112 		newargv[newargc] = strdup(what);
    113 		newargvattr[newargc] = quoted;
    114 		newargc++;
    115 		return 1;
    116 	} else
    117 		return 0;
    118 }
    119 
    120 static void
    121 free_argv(void)
    122 {
    123 	unsigned int i;
    124 
    125 	for (i = 0; i < newargc; i++) {
    126 		free(newargv[i]);
    127 		newargv[i] = NULL;
    128 	}
    129 	newargc = 0;
    130 
    131 	for (i = 0; i < oldargc; i++) {
    132 		free(oldargv[i]);
    133 		oldargv[i] = NULL;
    134 	}
    135 	oldargc = 0;
    136 }
    137 
    138 /* save parsed rule for comparison with next rule
    139    to perform action agregation on duplicate conditions */
    140 static void
    141 save_argv(void)
    142 {
    143 	unsigned int i;
    144 
    145 	for (i = 0; i < oldargc; i++)
    146 		free(oldargv[i]);
    147 	oldargc = newargc;
    148 	newargc = 0;
    149 	for (i = 0; i < oldargc; i++) {
    150 		oldargv[i] = newargv[i];
    151 		newargv[i] = NULL;
    152 	}
    153 }
    154 
    155 /* like puts but with xml encoding */
    156 static void
    157 xmlEncode(char *text)
    158 {
    159 	while (text && *text) {
    160 		if ((unsigned char) (*text) >= 127)
    161 			printf("&#%d;", (unsigned char) (*text));
    162 		else if (*text == '&')
    163 			printf("&amp;");
    164 		else if (*text == '<')
    165 			printf("&lt;");
    166 		else if (*text == '>')
    167 			printf("&gt;");
    168 		else if (*text == '"')
    169 			printf("&quot;");
    170 		else
    171 			putchar(*text);
    172 		text++;
    173 	}
    174 }
    175 
    176 /* Output text as a comment, avoiding a double hyphen */
    177 static void
    178 xmlCommentEscape(char *comment)
    179 {
    180 	int h_count = 0;
    181 
    182 	while (comment && *comment) {
    183 		if (*comment == '-') {
    184 			h_count++;
    185 			if (h_count >= 2) {
    186 				h_count = 0;
    187 				putchar(' ');
    188 			}
    189 			putchar('*');
    190 		}
    191 		/* strip trailing newline */
    192 		if (*comment == '\n' && *(comment + 1) == 0);
    193 		else
    194 			putchar(*comment);
    195 		comment++;
    196 	}
    197 }
    198 
    199 static void
    200 xmlComment(char *comment)
    201 {
    202 	printf("<!-- ");
    203 	xmlCommentEscape(comment);
    204 	printf(" -->\n");
    205 }
    206 
    207 static void
    208 xmlAttrS(char *name, char *value)
    209 {
    210 	printf("%s=\"", name);
    211 	xmlEncode(value);
    212 	printf("\" ");
    213 }
    214 
    215 static void
    216 xmlAttrI(char *name, long long int num)
    217 {
    218 	printf("%s=\"%lld\" ", name, num);
    219 }
    220 
    221 static void
    222 closeChain(void)
    223 {
    224 	if (curChain[0] == 0)
    225 		return;
    226 
    227 	if (closeActionTag[0])
    228 		printf("%s\n", closeActionTag);
    229 	closeActionTag[0] = 0;
    230 	if (closeRuleTag[0])
    231 		printf("%s\n", closeRuleTag);
    232 	closeRuleTag[0] = 0;
    233 	if (curChain[0])
    234 		printf("    </chain>\n");
    235 	curChain[0] = 0;
    236 	//lastRule[0]=0;
    237 }
    238 
    239 static void
    240 openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
    241 {
    242 	closeChain();
    243 
    244 	strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
    245 	curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
    246 
    247 	printf("    <chain ");
    248 	xmlAttrS("name", curChain);
    249 	if (strcmp(policy, "-") != 0)
    250 		xmlAttrS("policy", policy);
    251 	xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
    252 	xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
    253 	if (close) {
    254 		printf("%c", close);
    255 		curChain[0] = 0;
    256 	}
    257 	printf(">\n");
    258 }
    259 
    260 static int
    261 existsChain(char *chain)
    262 {
    263 	/* open a saved chain */
    264 	int c = 0;
    265 
    266 	if (0 == strcmp(curChain, chain))
    267 		return 1;
    268 	for (c = 0; c < nextChain; c++)
    269 		if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
    270 			return 1;
    271 	return 0;
    272 }
    273 
    274 static void
    275 needChain(char *chain)
    276 {
    277 	/* open a saved chain */
    278 	int c = 0;
    279 
    280 	if (0 == strcmp(curChain, chain))
    281 		return;
    282 
    283 	for (c = 0; c < nextChain; c++)
    284 		if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
    285 			openChain(chains[c].chain, chains[c].policy,
    286 				  &(chains[c].count), '\0');
    287 			/* And, mark it as done so we don't create
    288 			   an empty chain at table-end time */
    289 			chains[c].created = 1;
    290 		}
    291 }
    292 
    293 static void
    294 saveChain(char *chain, char *policy, struct ipt_counters *ctr)
    295 {
    296 	if (nextChain >= maxChains) {
    297 		xtables_error(PARAMETER_PROBLEM,
    298 			   "%s: line %u chain name invalid\n",
    299 			   prog_name, line);
    300 		exit(1);
    301 	};
    302 	chains[nextChain].chain = strdup(chain);
    303 	chains[nextChain].policy = strdup(policy);
    304 	chains[nextChain].count = *ctr;
    305 	chains[nextChain].created = 0;
    306 	nextChain++;
    307 }
    308 
    309 static void
    310 finishChains(void)
    311 {
    312 	int c;
    313 
    314 	for (c = 0; c < nextChain; c++)
    315 		if (!chains[c].created) {
    316 			openChain(chains[c].chain, chains[c].policy,
    317 				  &(chains[c].count), '/');
    318 			free(chains[c].chain);
    319 			free(chains[c].policy);
    320 		}
    321 	nextChain = 0;
    322 }
    323 
    324 static void
    325 closeTable(void)
    326 {
    327 	closeChain();
    328 	finishChains();
    329 	if (curTable[0])
    330 		printf("  </table>\n");
    331 	curTable[0] = 0;
    332 }
    333 
    334 static void
    335 openTable(char *table)
    336 {
    337 	closeTable();
    338 
    339 	strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
    340 	curTable[IPT_TABLE_MAXNAMELEN] = '\0';
    341 
    342 	printf("  <table ");
    343 	xmlAttrS("name", curTable);
    344 	printf(">\n");
    345 }
    346 
    347 // is char* -j --jump -g or --goto
    348 static int
    349 isTarget(char *arg)
    350 {
    351 	return ((arg)
    352 		&& (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
    353 		    || strcmp((arg), "-g") == 0
    354 		    || strcmp((arg), "--goto") == 0));
    355 }
    356 
    357 // is it a terminating target like -j ACCEPT, etc
    358 // (or I guess -j SNAT in nat table, but we don't check for that yet
    359 static int
    360 isTerminatingTarget(char *arg)
    361 {
    362 	return ((arg)
    363 		&& (strcmp((arg), "ACCEPT") == 0
    364 		    || strcmp((arg), "DROP") == 0
    365 		    || strcmp((arg), "QUEUE") == 0
    366 		    || strcmp((arg), "RETURN") == 0));
    367 }
    368 
    369 // part=-1 means do conditions, part=1 means do rules, part=0 means do both
    370 static void
    371 do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
    372 	     char *argv[], int argvattr[])
    373 {
    374 	int arg = 1;		// ignore leading -A
    375 	char invert_next = 0;
    376 	char *spacer = "";	// space when needed to assemble arguments
    377 	char *level1 = NULL;
    378 	char *level2 = NULL;
    379 	char *leveli1 = "        ";
    380 	char *leveli2 = "          ";
    381 
    382 #define CLOSE_LEVEL(LEVEL) \
    383 	do { \
    384 		if (level ## LEVEL) printf("</%s>\n", \
    385 		(leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
    386 		level ## LEVEL=NULL;\
    387 	} while(0)
    388 
    389 #define OPEN_LEVEL(LEVEL,TAG) \
    390 	do {\
    391 		level ## LEVEL=TAG;\
    392 		if (leveltag ## LEVEL) {\
    393 			printf("%s<%s ", (leveli ## LEVEL), \
    394 				(leveltag ## LEVEL));\
    395 			xmlAttrS("type", (TAG)); \
    396 		} else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
    397 	} while(0)
    398 
    399 	if (part == 1) {	/* skip */
    400 		/* use argvattr to tell which arguments were quoted
    401 		   to avoid comparing quoted arguments, like comments, to -j, */
    402 		while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
    403 			arg++;
    404 	}
    405 
    406 	/* Before we start, if the first arg is -[^-] and not -m or -j or -g
    407 	   then start a dummy <match> tag for old style built-in matches.
    408 	   We would do this in any case, but no need if it would be empty */
    409 	if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
    410 	    && strcmp(argv[arg], "-m") != 0) {
    411 		OPEN_LEVEL(1, "match");
    412 		printf(">\n");
    413 	}
    414 	while (arg < argc) {
    415 		// If ! is followed by -* then apply to that else output as data
    416 		// Stop, if we need to
    417 		if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
    418 			break;
    419 		} else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
    420 			if ((arg + 1) < argc && argv[arg + 1][0] == '-')
    421 				invert_next = '!';
    422 			else
    423 				printf("%s%s", spacer, argv[arg]);
    424 			spacer = " ";
    425 		} else if (!argvattr[arg] && isTarget(argv[arg])
    426 			   && existsChain(argv[arg + 1])
    427 			   && (2 + arg >= argc)) {
    428 			if (!((1 + arg) < argc))
    429 				// no args to -j, -m or -g, ignore & finish loop
    430 				break;
    431 			CLOSE_LEVEL(2);
    432 			if (level1)
    433 				printf("%s", leveli1);
    434 			CLOSE_LEVEL(1);
    435 			spacer = "";
    436 			invert_next = 0;
    437 			if (strcmp(argv[arg], "-g") == 0
    438 			    || strcmp(argv[arg], "--goto") == 0) {
    439 				/* goto user chain */
    440 				OPEN_LEVEL(1, "goto");
    441 				printf(">\n");
    442 				arg++;
    443 				OPEN_LEVEL(2, argv[arg]);
    444 				printf("/>\n");
    445 				level2 = NULL;
    446 			} else {
    447 				/* call user chain */
    448 				OPEN_LEVEL(1, "call");
    449 				printf(">\n");
    450 				arg++;
    451 				OPEN_LEVEL(2, argv[arg]);
    452 				printf("/>\n");
    453 				level2 = NULL;
    454 			}
    455 		} else if (!argvattr[arg]
    456 			   && (isTarget(argv[arg])
    457 			       || strcmp(argv[arg], "-m") == 0
    458 			       || strcmp(argv[arg], "--module") == 0)) {
    459 			if (!((1 + arg) < argc))
    460 				// no args to -j, -m or -g, ignore & finish loop
    461 				break;
    462 			CLOSE_LEVEL(2);
    463 			if (level1)
    464 				printf("%s", leveli1);
    465 			CLOSE_LEVEL(1);
    466 			spacer = "";
    467 			invert_next = 0;
    468 			arg++;
    469 			OPEN_LEVEL(1, (argv[arg]));
    470 			// Optimize case, can we close this tag already?
    471 			if ((arg + 1) >= argc || (!argvattr[arg + 1]
    472 						  && (isTarget(argv[arg + 1])
    473 						      || strcmp(argv[arg + 1],
    474 								"-m") == 0
    475 						      || strcmp(argv[arg + 1],
    476 								"--module") ==
    477 						      0))) {
    478 				printf(" />\n");
    479 				level1 = NULL;
    480 			} else {
    481 				printf(">\n");
    482 			}
    483 		} else if (!argvattr[arg] && argv[arg][0] == '-') {
    484 			char *tag;
    485 			CLOSE_LEVEL(2);
    486 			// Skip past any -
    487 			tag = argv[arg];
    488 			while (*tag == '-' && *tag)
    489 				tag++;
    490 
    491 			spacer = "";
    492 			OPEN_LEVEL(2, tag);
    493 			if (invert_next)
    494 				printf(" invert=\"1\"");
    495 			invert_next = 0;
    496 
    497 			// Optimize case, can we close this tag already?
    498 			if (!((arg + 1) < argc)
    499 			    || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
    500 				printf(" />\n");
    501 				level2 = NULL;
    502 			} else {
    503 				printf(">");
    504 			}
    505 		} else {	// regular data
    506 			char *spaces = strchr(argv[arg], ' ');
    507 			printf("%s", spacer);
    508 			if (spaces || argvattr[arg])
    509 				printf("&quot;");
    510 			// if argv[arg] contains a space, enclose in quotes
    511 			xmlEncode(argv[arg]);
    512 			if (spaces || argvattr[arg])
    513 				printf("&quot;");
    514 			spacer = " ";
    515 		}
    516 		arg++;
    517 	}
    518 	CLOSE_LEVEL(2);
    519 	if (level1)
    520 		printf("%s", leveli1);
    521 	CLOSE_LEVEL(1);
    522 }
    523 
    524 static int
    525 compareRules(void)
    526 {
    527 	/* compare arguments up to -j or -g for match.
    528 	   NOTE: We don't want to combine actions if there were no criteria
    529 	   in each rule, or rules didn't have an action
    530 	   NOTE: Depends on arguments being in some kind of "normal" order which
    531 	   is the case when processing the ACTUAL output of actual iptables-save
    532 	   rather than a file merely in a compatable format */
    533 
    534 	unsigned int old = 0;
    535 	unsigned int new = 0;
    536 
    537 	int compare = 0;
    538 
    539 	while (new < newargc && old < oldargc) {
    540 		if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
    541 			/* if oldarg was a terminating action then it makes no sense
    542 			 * to combine further actions into the same xml */
    543 			if (((strcmp((oldargv[old]), "-j") == 0
    544 					|| strcmp((oldargv[old]), "--jump") == 0)
    545 				&& old+1 < oldargc
    546 				&& isTerminatingTarget(oldargv[old+1]) )
    547 			    || strcmp((oldargv[old]), "-g") == 0
    548 			    || strcmp((oldargv[old]), "--goto") == 0 ) {
    549 				/* Previous rule had terminating action */
    550 				compare = 0;
    551 			} else {
    552 				compare = 1;
    553 			}
    554 			break;
    555 		}
    556 		// break when old!=new
    557 		if (strcmp(oldargv[old], newargv[new]) != 0) {
    558 			compare = 0;
    559 			break;
    560 		}
    561 
    562 		old++;
    563 		new++;
    564 	}
    565 	// We won't match unless both rules had a target.
    566 	// This means we don't combine target-less rules, which is good
    567 
    568 	return compare == 1;
    569 }
    570 
    571 /* has a nice parsed rule starting with -A */
    572 static void
    573 do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
    574 {
    575 	/* are these conditions the same as the previous rule?
    576 	 * If so, skip arg straight to -j or -g */
    577 	if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
    578 		xmlComment("Combine action from next rule");
    579 	} else {
    580 
    581 		if (closeActionTag[0]) {
    582 			printf("%s\n", closeActionTag);
    583 			closeActionTag[0] = 0;
    584 		}
    585 		if (closeRuleTag[0]) {
    586 			printf("%s\n", closeRuleTag);
    587 			closeRuleTag[0] = 0;
    588 		}
    589 
    590 		printf("      <rule ");
    591 		//xmlAttrS("table",curTable); // not needed in full mode
    592 		//xmlAttrS("chain",argv[1]); // not needed in full mode
    593 		if (pcnt)
    594 			xmlAttrS("packet-count", pcnt);
    595 		if (bcnt)
    596 			xmlAttrS("byte-count", bcnt);
    597 		printf(">\n");
    598 
    599 		strncpy(closeRuleTag, "      </rule>\n", IPT_TABLE_MAXNAMELEN);
    600 		closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
    601 
    602 		/* no point in writing out condition if there isn't one */
    603 		if (argc >= 3 && !isTarget(argv[2])) {
    604 			printf("       <conditions>\n");
    605 			do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
    606 			printf("       </conditions>\n");
    607 		}
    608 	}
    609 	/* Write out the action */
    610 	//do_rule_part("action","arg",1,argc,argv,argvattr);
    611 	if (!closeActionTag[0]) {
    612 		printf("       <actions>\n");
    613 		strncpy(closeActionTag, "       </actions>\n",
    614 			IPT_TABLE_MAXNAMELEN);
    615 		closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
    616 	}
    617 	do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
    618 }
    619 
    620 #ifdef IPTABLES_MULTI
    621 int
    622 iptables_xml_main(int argc, char *argv[])
    623 #else
    624 int
    625 main(int argc, char *argv[])
    626 #endif
    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 ipt_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 
    746 			/* reset the newargv */
    747 			newargc = 0;
    748 
    749 			if (buffer[0] == '[') {
    750 				/* we have counters in our input */
    751 				ptr = strchr(buffer, ']');
    752 				if (!ptr)
    753 					xtables_error(PARAMETER_PROBLEM,
    754 						   "Bad line %u: need ]\n",
    755 						   line);
    756 
    757 				pcnt = strtok(buffer + 1, ":");
    758 				if (!pcnt)
    759 					xtables_error(PARAMETER_PROBLEM,
    760 						   "Bad line %u: need :\n",
    761 						   line);
    762 
    763 				bcnt = strtok(NULL, "]");
    764 				if (!bcnt)
    765 					xtables_error(PARAMETER_PROBLEM,
    766 						   "Bad line %u: need ]\n",
    767 						   line);
    768 
    769 				/* start command parsing after counter */
    770 				parsestart = ptr + 1;
    771 			} else {
    772 				/* start command parsing at start of line */
    773 				parsestart = buffer;
    774 			}
    775 
    776 
    777 			/* This is a 'real' parser crafted in artist mode
    778 			 * not hacker mode. If the author can live with that
    779 			 * then so can everyone else */
    780 
    781 			quote_open = 0;
    782 			/* We need to know which args were quoted so we
    783 			   can preserve quote */
    784 			quoted = 0;
    785 			param_start = parsestart;
    786 
    787 			for (curchar = parsestart; *curchar; curchar++) {
    788 				if (*curchar == '"') {
    789 					/* quote_open cannot be true if there
    790 					 * was no previous character.  Thus,
    791 					 * curchar-1 has to be within bounds */
    792 					if (quote_open &&
    793 					    *(curchar - 1) != '\\') {
    794 						quote_open = 0;
    795 						*curchar = ' ';
    796 					} else {
    797 						quote_open = 1;
    798 						quoted = 1;
    799 						param_start++;
    800 					}
    801 				}
    802 				if (*curchar == ' '
    803 				    || *curchar == '\t' || *curchar == '\n') {
    804 					char param_buffer[1024];
    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 			needChain(chain);// Should we explicitly look for -A
    851 			do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
    852 
    853 			save_argv();
    854 			ret = 1;
    855 		}
    856 		if (!ret) {
    857 			fprintf(stderr, "%s: line %u failed\n",
    858 				prog_name, line);
    859 			exit(1);
    860 		}
    861 	}
    862 	if (curTable[0]) {
    863 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
    864 			prog_name, line + 1);
    865 		exit(1);
    866 	}
    867 
    868 	if (in != NULL)
    869 		fclose(in);
    870 	printf("</iptables-rules>\n");
    871 	free_argv();
    872 
    873 	return 0;
    874 }
    875