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
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 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 */
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 XT_CHAIN_MAXNAMELEN XT_TABLE_MAXNAMELEN
85static char closeActionTag[XT_TABLE_MAXNAMELEN + 1];
86static char closeRuleTag[XT_TABLE_MAXNAMELEN + 1];
87static char curTable[XT_TABLE_MAXNAMELEN + 1];
88static char curChain[XT_CHAIN_MAXNAMELEN + 1];
89
90struct 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 */
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 to perform action aggregation
135 * on duplicate conditions.
136 */
137static void
138save_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 */
153static void
154xmlEncode(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 */
174static void
175xmlCommentEscape(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
196static void
197xmlComment(char *comment)
198{
199	printf("<!-- ");
200	xmlCommentEscape(comment);
201	printf(" -->\n");
202}
203
204static void
205xmlAttrS(char *name, char *value)
206{
207	printf("%s=\"", name);
208	xmlEncode(value);
209	printf("\" ");
210}
211
212static void
213xmlAttrI(char *name, long long int num)
214{
215	printf("%s=\"%lld\" ", name, num);
216}
217
218static void
219closeChain(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
236static void
237openChain(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
257static int
258existsChain(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
271static void
272needChain(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
290static void
291saveChain(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
306static void
307finishChains(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
321static void
322closeTable(void)
323{
324	closeChain();
325	finishChains();
326	if (curTable[0])
327		printf("  </table>\n");
328	curTable[0] = 0;
329}
330
331static void
332openTable(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
345static int
346isTarget(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
356static int
357isTerminatingTarget(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
367static void
368do_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
528static int
529compareRules(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 */
577static void
578do_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
625int
626iptables_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