• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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
print_usage(const char * name,const char * version)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
parse_counters(char * string,struct ipt_counters * ctr)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
add_argv(char * what,int quoted)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
free_argv(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
save_argv(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
xmlEncode(char * text)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
xmlCommentEscape(char * comment)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
xmlComment(char * comment)200 xmlComment(char *comment)
201 {
202 	printf("<!-- ");
203 	xmlCommentEscape(comment);
204 	printf(" -->\n");
205 }
206 
207 static void
xmlAttrS(char * name,char * value)208 xmlAttrS(char *name, char *value)
209 {
210 	printf("%s=\"", name);
211 	xmlEncode(value);
212 	printf("\" ");
213 }
214 
215 static void
xmlAttrI(char * name,long long int num)216 xmlAttrI(char *name, long long int num)
217 {
218 	printf("%s=\"%lld\" ", name, num);
219 }
220 
221 static void
closeChain(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
openChain(char * chain,char * policy,struct ipt_counters * ctr,char close)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
existsChain(char * chain)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
needChain(char * chain)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
saveChain(char * chain,char * policy,struct ipt_counters * ctr)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
finishChains(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
closeTable(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
openTable(char * table)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
isTarget(char * arg)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
isTerminatingTarget(char * arg)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
do_rule_part(char * leveltag1,char * leveltag2,int part,int argc,char * argv[],int argvattr[])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
compareRules(void)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
do_rule(char * pcnt,char * bcnt,int argc,char * argv[],int argvattr[])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
iptables_xml_main(int argc,char * argv[])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