1 /* Code to convert iptables-save format to xml format,
2  * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
3  * based on iptables-restore (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
4  * based on previous code from Rusty Russell <rusty@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
print_usage(const char * name,const char * version)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
parse_counters(char * string,struct xt_counters * ctr)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
add_argv(char * what,int quoted)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
free_argv(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
save_argv(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
xmlEncode(char * text)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
xmlCommentEscape(char * comment)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
xmlComment(char * comment)196 xmlComment(char *comment)
197 {
198 	printf("<!-- ");
199 	xmlCommentEscape(comment);
200 	printf(" -->\n");
201 }
202 
203 static void
xmlAttrS(char * name,char * value)204 xmlAttrS(char *name, char *value)
205 {
206 	printf("%s=\"", name);
207 	xmlEncode(value);
208 	printf("\" ");
209 }
210 
211 static void
xmlAttrI(char * name,long long int num)212 xmlAttrI(char *name, long long int num)
213 {
214 	printf("%s=\"%lld\" ", name, num);
215 }
216 
217 static void
closeChain(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
openChain(char * chain,char * policy,struct xt_counters * ctr,char close)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
existsChain(char * chain)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
needChain(char * chain)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
saveChain(char * chain,char * policy,struct xt_counters * ctr)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
finishChains(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
closeTable(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
openTable(char * table)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
isTarget(char * arg)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
isTerminatingTarget(char * arg)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
do_rule_part(char * leveltag1,char * leveltag2,int part,int argc,char * argv[],int argvattr[])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
compareRules(void)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
do_rule(char * pcnt,char * bcnt,int argc,char * argv[],int argvattr[])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
iptables_xml_main(int argc,char * argv[])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