iptables-xml.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
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
27int line = 0;
28#endif
29
30struct 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
38static void print_usage(const char *name, const char *version)
39	    __attribute__ ((noreturn));
40
41static int verbose = 0;
42/* Whether to combine actions of sequential rules with identical conditions */
43static int combine = 0;
44/* Keeping track of external matches and targets.  */
45static struct option options[] = {
46	{"verbose", 0, NULL, 'v'},
47	{"combine", 0, NULL, 'c'},
48	{"help", 0, NULL, 'h'},
49	{ .name = NULL }
50};
51
52static void
53print_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
62static int
63parse_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 */
79static char *newargv[255];
80static unsigned int newargc = 0;
81
82static char *oldargv[255];
83static unsigned int oldargc = 0;
84
85/* arg meta data, were they quoted, frinstance */
86static int newargvattr[255];
87
88#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
89static char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
90static char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
91static char curTable[IPT_TABLE_MAXNAMELEN + 1];
92static char curChain[IPT_CHAIN_MAXNAMELEN + 1];
93
94struct 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 */
102static struct chain chains[maxChains];
103static int nextChain = 0;
104
105/* funCtion adding one argument to newargv, updating newargc
106 * returns true if argument added, false otherwise */
107static int
108add_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
120static void
121free_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 */
140static void
141save_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 */
156static void
157xmlEncode(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 */
177static void
178xmlCommentEscape(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
199static void
200xmlComment(char *comment)
201{
202	printf("<!-- ");
203	xmlCommentEscape(comment);
204	printf(" -->\n");
205}
206
207static void
208xmlAttrS(char *name, char *value)
209{
210	printf("%s=\"", name);
211	xmlEncode(value);
212	printf("\" ");
213}
214
215static void
216xmlAttrI(char *name, long long int num)
217{
218	printf("%s=\"%lld\" ", name, num);
219}
220
221static void
222closeChain(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
239static void
240openChain(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
260static int
261existsChain(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
274static void
275needChain(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
293static void
294saveChain(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
309static void
310finishChains(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
324static void
325closeTable(void)
326{
327	closeChain();
328	finishChains();
329	if (curTable[0])
330		printf("  </table>\n");
331	curTable[0] = 0;
332}
333
334static void
335openTable(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
348static int
349isTarget(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
359static int
360isTerminatingTarget(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
370static void
371do_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
524static int
525compareRules(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 */
572static void
573do_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
621int
622iptables_xml_main(int argc, char *argv[])
623#else
624int
625main(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