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