iptables-xml.c revision f643eb37e49a212d40eb060bcdfafbc366c0d616
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
26struct 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
34static void print_usage(const char *name, const char *version)
35	    __attribute__ ((noreturn));
36
37static int verbose = 0;
38/* Whether to combine actions of sequential rules with identical conditions */
39static int combine = 0;
40/* Keeping track of external matches and targets.  */
41static struct option options[] = {
42	{"verbose", 0, NULL, 'v'},
43	{"combine", 0, NULL, 'c'},
44	{"help", 0, NULL, 'h'},
45	{ .name = NULL }
46};
47
48static void
49print_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
58static int
59parse_counters(char *string, struct ipt_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 */
75static char *newargv[255];
76static unsigned int newargc = 0;
77
78static char *oldargv[255];
79static unsigned int oldargc = 0;
80
81/* arg meta data, were they quoted, frinstance */
82static int newargvattr[255];
83
84#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
85static char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
86static char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
87static char curTable[IPT_TABLE_MAXNAMELEN + 1];
88static char curChain[IPT_CHAIN_MAXNAMELEN + 1];
89
90struct chain {
91	char *chain;
92	char *policy;
93	struct ipt_counters count;
94	int created;
95};
96
97#define maxChains 10240		/* max chains per table */
98static struct chain chains[maxChains];
99static int nextChain = 0;
100
101/* funCtion adding one argument to newargv, updating newargc
102 * returns true if argument added, false otherwise */
103static int
104add_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
116static void
117free_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 */
136static void
137save_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 */
152static void
153xmlEncode(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 */
173static void
174xmlCommentEscape(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
195static void
196xmlComment(char *comment)
197{
198	printf("<!-- ");
199	xmlCommentEscape(comment);
200	printf(" -->\n");
201}
202
203static void
204xmlAttrS(char *name, char *value)
205{
206	printf("%s=\"", name);
207	xmlEncode(value);
208	printf("\" ");
209}
210
211static void
212xmlAttrI(char *name, long long int num)
213{
214	printf("%s=\"%lld\" ", name, num);
215}
216
217static void
218closeChain(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
235static void
236openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
237{
238	closeChain();
239
240	strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
241	curChain[IPT_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
256static int
257existsChain(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
270static void
271needChain(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
289static void
290saveChain(char *chain, char *policy, struct ipt_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
305static void
306finishChains(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
320static void
321closeTable(void)
322{
323	closeChain();
324	finishChains();
325	if (curTable[0])
326		printf("  </table>\n");
327	curTable[0] = 0;
328}
329
330static void
331openTable(char *table)
332{
333	closeTable();
334
335	strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
336	curTable[IPT_TABLE_MAXNAMELEN] = '\0';
337
338	printf("  <table ");
339	xmlAttrS("name", curTable);
340	printf(">\n");
341}
342
343// is char* -j --jump -g or --goto
344static int
345isTarget(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
355static int
356isTerminatingTarget(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
366static void
367do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
368	     char *argv[], int argvattr[])
369{
370	int arg = 1;		// ignore leading -A
371	char invert_next = 0;
372	char *spacer = "";	// space when needed to assemble arguments
373	char *level1 = NULL;
374	char *level2 = NULL;
375	char *leveli1 = "        ";
376	char *leveli2 = "          ";
377
378#define CLOSE_LEVEL(LEVEL) \
379	do { \
380		if (level ## LEVEL) printf("</%s>\n", \
381		(leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
382		level ## LEVEL=NULL;\
383	} while(0)
384
385#define OPEN_LEVEL(LEVEL,TAG) \
386	do {\
387		level ## LEVEL=TAG;\
388		if (leveltag ## LEVEL) {\
389			printf("%s<%s ", (leveli ## LEVEL), \
390				(leveltag ## LEVEL));\
391			xmlAttrS("type", (TAG)); \
392		} else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
393	} while(0)
394
395	if (part == 1) {	/* skip */
396		/* use argvattr to tell which arguments were quoted
397		   to avoid comparing quoted arguments, like comments, to -j, */
398		while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
399			arg++;
400	}
401
402	/* Before we start, if the first arg is -[^-] and not -m or -j or -g
403	   then start a dummy <match> tag for old style built-in matches.
404	   We would do this in any case, but no need if it would be empty */
405	if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
406	    && strcmp(argv[arg], "-m") != 0) {
407		OPEN_LEVEL(1, "match");
408		printf(">\n");
409	}
410	while (arg < argc) {
411		// If ! is followed by -* then apply to that else output as data
412		// Stop, if we need to
413		if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
414			break;
415		} else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
416			if ((arg + 1) < argc && argv[arg + 1][0] == '-')
417				invert_next = '!';
418			else
419				printf("%s%s", spacer, argv[arg]);
420			spacer = " ";
421		} else if (!argvattr[arg] && isTarget(argv[arg])
422			   && existsChain(argv[arg + 1])
423			   && (2 + arg >= argc)) {
424			if (!((1 + arg) < argc))
425				// no args to -j, -m or -g, ignore & finish loop
426				break;
427			CLOSE_LEVEL(2);
428			if (level1)
429				printf("%s", leveli1);
430			CLOSE_LEVEL(1);
431			spacer = "";
432			invert_next = 0;
433			if (strcmp(argv[arg], "-g") == 0
434			    || strcmp(argv[arg], "--goto") == 0) {
435				/* goto user chain */
436				OPEN_LEVEL(1, "goto");
437				printf(">\n");
438				arg++;
439				OPEN_LEVEL(2, argv[arg]);
440				printf("/>\n");
441				level2 = NULL;
442			} else {
443				/* call user chain */
444				OPEN_LEVEL(1, "call");
445				printf(">\n");
446				arg++;
447				OPEN_LEVEL(2, argv[arg]);
448				printf("/>\n");
449				level2 = NULL;
450			}
451		} else if (!argvattr[arg]
452			   && (isTarget(argv[arg])
453			       || strcmp(argv[arg], "-m") == 0
454			       || strcmp(argv[arg], "--module") == 0)) {
455			if (!((1 + arg) < argc))
456				// no args to -j, -m or -g, ignore & finish loop
457				break;
458			CLOSE_LEVEL(2);
459			if (level1)
460				printf("%s", leveli1);
461			CLOSE_LEVEL(1);
462			spacer = "";
463			invert_next = 0;
464			arg++;
465			OPEN_LEVEL(1, (argv[arg]));
466			// Optimize case, can we close this tag already?
467			if ((arg + 1) >= argc || (!argvattr[arg + 1]
468						  && (isTarget(argv[arg + 1])
469						      || strcmp(argv[arg + 1],
470								"-m") == 0
471						      || strcmp(argv[arg + 1],
472								"--module") ==
473						      0))) {
474				printf(" />\n");
475				level1 = NULL;
476			} else {
477				printf(">\n");
478			}
479		} else if (!argvattr[arg] && argv[arg][0] == '-') {
480			char *tag;
481			CLOSE_LEVEL(2);
482			// Skip past any -
483			tag = argv[arg];
484			while (*tag == '-' && *tag)
485				tag++;
486
487			spacer = "";
488			OPEN_LEVEL(2, tag);
489			if (invert_next)
490				printf(" invert=\"1\"");
491			invert_next = 0;
492
493			// Optimize case, can we close this tag already?
494			if (!((arg + 1) < argc)
495			    || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
496				printf(" />\n");
497				level2 = NULL;
498			} else {
499				printf(">");
500			}
501		} else {	// regular data
502			char *spaces = strchr(argv[arg], ' ');
503			printf("%s", spacer);
504			if (spaces || argvattr[arg])
505				printf("&quot;");
506			// if argv[arg] contains a space, enclose in quotes
507			xmlEncode(argv[arg]);
508			if (spaces || argvattr[arg])
509				printf("&quot;");
510			spacer = " ";
511		}
512		arg++;
513	}
514	CLOSE_LEVEL(2);
515	if (level1)
516		printf("%s", leveli1);
517	CLOSE_LEVEL(1);
518}
519
520static int
521compareRules(void)
522{
523	/* compare arguments up to -j or -g for match.
524	   NOTE: We don't want to combine actions if there were no criteria
525	   in each rule, or rules didn't have an action
526	   NOTE: Depends on arguments being in some kind of "normal" order which
527	   is the case when processing the ACTUAL output of actual iptables-save
528	   rather than a file merely in a compatable format */
529
530	unsigned int old = 0;
531	unsigned int new = 0;
532
533	int compare = 0;
534
535	while (new < newargc && old < oldargc) {
536		if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
537			/* if oldarg was a terminating action then it makes no sense
538			 * to combine further actions into the same xml */
539			if (((strcmp((oldargv[old]), "-j") == 0
540					|| strcmp((oldargv[old]), "--jump") == 0)
541				&& old+1 < oldargc
542				&& isTerminatingTarget(oldargv[old+1]) )
543			    || strcmp((oldargv[old]), "-g") == 0
544			    || strcmp((oldargv[old]), "--goto") == 0 ) {
545				/* Previous rule had terminating action */
546				compare = 0;
547			} else {
548				compare = 1;
549			}
550			break;
551		}
552		// break when old!=new
553		if (strcmp(oldargv[old], newargv[new]) != 0) {
554			compare = 0;
555			break;
556		}
557
558		old++;
559		new++;
560	}
561	// We won't match unless both rules had a target.
562	// This means we don't combine target-less rules, which is good
563
564	return compare == 1;
565}
566
567/* has a nice parsed rule starting with -A */
568static void
569do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
570{
571	/* are these conditions the same as the previous rule?
572	 * If so, skip arg straight to -j or -g */
573	if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
574		xmlComment("Combine action from next rule");
575	} else {
576
577		if (closeActionTag[0]) {
578			printf("%s\n", closeActionTag);
579			closeActionTag[0] = 0;
580		}
581		if (closeRuleTag[0]) {
582			printf("%s\n", closeRuleTag);
583			closeRuleTag[0] = 0;
584		}
585
586		printf("      <rule ");
587		//xmlAttrS("table",curTable); // not needed in full mode
588		//xmlAttrS("chain",argv[1]); // not needed in full mode
589		if (pcnt)
590			xmlAttrS("packet-count", pcnt);
591		if (bcnt)
592			xmlAttrS("byte-count", bcnt);
593		printf(">\n");
594
595		strncpy(closeRuleTag, "      </rule>\n", IPT_TABLE_MAXNAMELEN);
596		closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
597
598		/* no point in writing out condition if there isn't one */
599		if (argc >= 3 && !isTarget(argv[2])) {
600			printf("       <conditions>\n");
601			do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
602			printf("       </conditions>\n");
603		}
604	}
605	/* Write out the action */
606	//do_rule_part("action","arg",1,argc,argv,argvattr);
607	if (!closeActionTag[0]) {
608		printf("       <actions>\n");
609		strncpy(closeActionTag, "       </actions>\n",
610			IPT_TABLE_MAXNAMELEN);
611		closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
612	}
613	do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
614}
615
616int
617iptables_xml_main(int argc, char *argv[])
618{
619	char buffer[10240];
620	int c;
621	FILE *in;
622
623	line = 0;
624
625	xtables_set_params(&iptables_xml_globals);
626	while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
627		switch (c) {
628		case 'c':
629			combine = 1;
630			break;
631		case 'v':
632			printf("xptables-xml\n");
633			verbose = 1;
634			break;
635		case 'h':
636			print_usage("iptables-xml", IPTABLES_VERSION);
637			break;
638		}
639	}
640
641	if (optind == argc - 1) {
642		in = fopen(argv[optind], "re");
643		if (!in) {
644			fprintf(stderr, "Can't open %s: %s", argv[optind],
645				strerror(errno));
646			exit(1);
647		}
648	} else if (optind < argc) {
649		fprintf(stderr, "Unknown arguments found on commandline");
650		exit(1);
651	} else
652		in = stdin;
653
654	printf("<iptables-rules version=\"1.0\">\n");
655
656	/* Grab standard input. */
657	while (fgets(buffer, sizeof(buffer), in)) {
658		int ret = 0;
659
660		line++;
661
662		if (buffer[0] == '\n')
663			continue;
664		else if (buffer[0] == '#') {
665			xmlComment(buffer);
666			continue;
667		}
668
669		if (verbose) {
670			printf("<!-- line %d ", line);
671			xmlCommentEscape(buffer);
672			printf(" -->\n");
673		}
674
675		if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
676			DEBUGP("Calling commit\n");
677			closeTable();
678			ret = 1;
679		} else if ((buffer[0] == '*')) {
680			/* New table */
681			char *table;
682
683			table = strtok(buffer + 1, " \t\n");
684			DEBUGP("line %u, table '%s'\n", line, table);
685			if (!table) {
686				xtables_error(PARAMETER_PROBLEM,
687					   "%s: line %u table name invalid\n",
688					   prog_name, line);
689				exit(1);
690			}
691			openTable(table);
692
693			ret = 1;
694		} else if ((buffer[0] == ':') && (curTable[0])) {
695			/* New chain. */
696			char *policy, *chain;
697			struct ipt_counters count;
698			char *ctrs;
699
700			chain = strtok(buffer + 1, " \t\n");
701			DEBUGP("line %u, chain '%s'\n", line, chain);
702			if (!chain) {
703				xtables_error(PARAMETER_PROBLEM,
704					   "%s: line %u chain name invalid\n",
705					   prog_name, line);
706				exit(1);
707			}
708
709			DEBUGP("Creating new chain '%s'\n", chain);
710
711			policy = strtok(NULL, " \t\n");
712			DEBUGP("line %u, policy '%s'\n", line, policy);
713			if (!policy) {
714				xtables_error(PARAMETER_PROBLEM,
715					   "%s: line %u policy invalid\n",
716					   prog_name, line);
717				exit(1);
718			}
719
720			ctrs = strtok(NULL, " \t\n");
721			parse_counters(ctrs, &count);
722			saveChain(chain, policy, &count);
723
724			ret = 1;
725		} else if (curTable[0]) {
726			unsigned int a;
727			char *ptr = buffer;
728			char *pcnt = NULL;
729			char *bcnt = NULL;
730			char *parsestart;
731			char *chain = NULL;
732
733			/* the parser */
734			char *param_start, *curchar;
735			int quote_open, quoted;
736
737			/* reset the newargv */
738			newargc = 0;
739
740			if (buffer[0] == '[') {
741				/* we have counters in our input */
742				ptr = strchr(buffer, ']');
743				if (!ptr)
744					xtables_error(PARAMETER_PROBLEM,
745						   "Bad line %u: need ]\n",
746						   line);
747
748				pcnt = strtok(buffer + 1, ":");
749				if (!pcnt)
750					xtables_error(PARAMETER_PROBLEM,
751						   "Bad line %u: need :\n",
752						   line);
753
754				bcnt = strtok(NULL, "]");
755				if (!bcnt)
756					xtables_error(PARAMETER_PROBLEM,
757						   "Bad line %u: need ]\n",
758						   line);
759
760				/* start command parsing after counter */
761				parsestart = ptr + 1;
762			} else {
763				/* start command parsing at start of line */
764				parsestart = buffer;
765			}
766
767
768			/* This is a 'real' parser crafted in artist mode
769			 * not hacker mode. If the author can live with that
770			 * then so can everyone else */
771
772			quote_open = 0;
773			/* We need to know which args were quoted so we
774			   can preserve quote */
775			quoted = 0;
776			param_start = parsestart;
777
778			for (curchar = parsestart; *curchar; curchar++) {
779				if (*curchar == '"') {
780					/* quote_open cannot be true if there
781					 * was no previous character.  Thus,
782					 * curchar-1 has to be within bounds */
783					if (quote_open &&
784					    *(curchar - 1) != '\\') {
785						quote_open = 0;
786						*curchar = ' ';
787					} else {
788						quote_open = 1;
789						quoted = 1;
790						param_start++;
791					}
792				}
793				if (*curchar == ' '
794				    || *curchar == '\t' || *curchar == '\n') {
795					char param_buffer[1024];
796					int param_len = curchar - param_start;
797
798					if (quote_open)
799						continue;
800
801					if (!param_len) {
802						/* two spaces? */
803						param_start++;
804						continue;
805					}
806
807					/* end of one parameter */
808					strncpy(param_buffer, param_start,
809						param_len);
810					*(param_buffer + param_len) = '\0';
811
812					/* check if table name specified */
813					if (!strncmp(param_buffer, "-t", 3)
814					    || !strncmp(param_buffer,
815							"--table", 8)) {
816						xtables_error(PARAMETER_PROBLEM,
817							   "Line %u seems to have a "
818							   "-t table option.\n",
819							   line);
820						exit(1);
821					}
822
823					add_argv(param_buffer, quoted);
824					if (newargc >= 2
825					    && 0 ==
826					    strcmp(newargv[newargc - 2], "-A"))
827						chain = newargv[newargc - 1];
828					quoted = 0;
829					param_start += param_len + 1;
830				} else {
831					/* regular character, skip */
832				}
833			}
834
835			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
836			       newargc, curTable);
837
838			for (a = 0; a < newargc; a++)
839				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
840
841			needChain(chain);// Should we explicitly look for -A
842			do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
843
844			save_argv();
845			ret = 1;
846		}
847		if (!ret) {
848			fprintf(stderr, "%s: line %u failed\n",
849				prog_name, line);
850			exit(1);
851		}
852	}
853	if (curTable[0]) {
854		fprintf(stderr, "%s: COMMIT expected at line %u\n",
855			prog_name, line + 1);
856		exit(1);
857	}
858
859	fclose(in);
860	printf("</iptables-rules>\n");
861	free_argv();
862
863	return 0;
864}
865