• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <config.h>
2 #include <ctype.h>
3 #include <getopt.h>
4 #include <errno.h>
5 #include <libgen.h>
6 #include <netdb.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/file.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <sys/time.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <xtables.h>
19 #include <math.h>
20 #include "xshared.h"
21 
22 /*
23  * Print out any special helps. A user might like to be able to add a --help
24  * to the commandline, and see expected results. So we call help for all
25  * specified matches and targets.
26  */
print_extension_helps(const struct xtables_target * t,const struct xtables_rule_match * m)27 void print_extension_helps(const struct xtables_target *t,
28     const struct xtables_rule_match *m)
29 {
30 	for (; t != NULL; t = t->next) {
31 		if (t->used) {
32 			printf("\n");
33 			if (t->help == NULL)
34 				printf("%s does not take any options\n",
35 				       t->name);
36 			else
37 				t->help();
38 		}
39 	}
40 	for (; m != NULL; m = m->next) {
41 		printf("\n");
42 		if (m->match->help == NULL)
43 			printf("%s does not take any options\n",
44 			       m->match->name);
45 		else
46 			m->match->help();
47 	}
48 }
49 
50 const char *
proto_to_name(uint8_t proto,int nolookup)51 proto_to_name(uint8_t proto, int nolookup)
52 {
53 	unsigned int i;
54 
55 	if (proto && !nolookup) {
56 		struct protoent *pent = getprotobynumber(proto);
57 		if (pent)
58 			return pent->p_name;
59 	}
60 
61 	for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
62 		if (xtables_chain_protos[i].num == proto)
63 			return xtables_chain_protos[i].name;
64 
65 	return NULL;
66 }
67 
68 static struct xtables_match *
find_proto(const char * pname,enum xtables_tryload tryload,int nolookup,struct xtables_rule_match ** matches)69 find_proto(const char *pname, enum xtables_tryload tryload,
70 	   int nolookup, struct xtables_rule_match **matches)
71 {
72 	unsigned int proto;
73 
74 	if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) {
75 		const char *protoname = proto_to_name(proto, nolookup);
76 
77 		if (protoname)
78 			return xtables_find_match(protoname, tryload, matches);
79 	} else
80 		return xtables_find_match(pname, tryload, matches);
81 
82 	return NULL;
83 }
84 
85 /*
86  * Some explanations (after four different bugs in 3 different releases): If
87  * we encounter a parameter, that has not been parsed yet, it's not an option
88  * of an explicitly loaded match or a target. However, we support implicit
89  * loading of the protocol match extension. '-p tcp' means 'l4 proto 6' and at
90  * the same time 'load tcp protocol match on demand if we specify --dport'.
91  *
92  * To make this work, we need to make sure:
93  * - the parameter has not been parsed by a match (m above)
94  * - a protocol has been specified
95  * - the protocol extension has not been loaded yet, or is loaded and unused
96  *   [think of ip6tables-restore!]
97  * - the protocol extension can be successively loaded
98  */
should_load_proto(struct iptables_command_state * cs)99 static bool should_load_proto(struct iptables_command_state *cs)
100 {
101 	if (cs->protocol == NULL)
102 		return false;
103 	if (find_proto(cs->protocol, XTF_DONT_LOAD,
104 	    cs->options & OPT_NUMERIC, NULL) == NULL)
105 		return true;
106 	return !cs->proto_used;
107 }
108 
load_proto(struct iptables_command_state * cs)109 struct xtables_match *load_proto(struct iptables_command_state *cs)
110 {
111 	if (!should_load_proto(cs))
112 		return NULL;
113 	return find_proto(cs->protocol, XTF_TRY_LOAD,
114 			  cs->options & OPT_NUMERIC, &cs->matches);
115 }
116 
command_default(struct iptables_command_state * cs,struct xtables_globals * gl)117 int command_default(struct iptables_command_state *cs,
118 		    struct xtables_globals *gl)
119 {
120 	struct xtables_rule_match *matchp;
121 	struct xtables_match *m;
122 
123 	if (cs->target != NULL &&
124 	    (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
125 	    cs->c >= cs->target->option_offset &&
126 	    cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
127 		xtables_option_tpcall(cs->c, cs->argv, cs->invert,
128 				      cs->target, &cs->fw);
129 		return 0;
130 	}
131 
132 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
133 		m = matchp->match;
134 
135 		if (matchp->completed ||
136 		    (m->x6_parse == NULL && m->parse == NULL))
137 			continue;
138 		if (cs->c < matchp->match->option_offset ||
139 		    cs->c >= matchp->match->option_offset + XT_OPTION_OFFSET_SCALE)
140 			continue;
141 		xtables_option_mpcall(cs->c, cs->argv, cs->invert, m, &cs->fw);
142 		return 0;
143 	}
144 
145 	/* Try loading protocol */
146 	m = load_proto(cs);
147 	if (m != NULL) {
148 		size_t size;
149 
150 		cs->proto_used = 1;
151 
152 		size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
153 
154 		m->m = xtables_calloc(1, size);
155 		m->m->u.match_size = size;
156 		strcpy(m->m->u.user.name, m->name);
157 		m->m->u.user.revision = m->revision;
158 		xs_init_match(m);
159 
160 		if (m->x6_options != NULL)
161 			gl->opts = xtables_options_xfrm(gl->orig_opts,
162 							gl->opts,
163 							m->x6_options,
164 							&m->option_offset);
165 		else
166 			gl->opts = xtables_merge_options(gl->orig_opts,
167 							 gl->opts,
168 							 m->extra_opts,
169 							 &m->option_offset);
170 		if (gl->opts == NULL)
171 			xtables_error(OTHER_PROBLEM, "can't alloc memory!");
172 		optind--;
173 		/* Indicate to rerun getopt *immediately* */
174  		return 1;
175 	}
176 
177 	if (cs->c == ':')
178 		xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
179 		              "requires an argument", cs->argv[optind-1]);
180 	if (cs->c == '?')
181 		xtables_error(PARAMETER_PROBLEM, "unknown option "
182 			      "\"%s\"", cs->argv[optind-1]);
183 	xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
184 }
185 
subcmd_get(const char * cmd,const struct subcommand * cb)186 static mainfunc_t subcmd_get(const char *cmd, const struct subcommand *cb)
187 {
188 	for (; cb->name != NULL; ++cb)
189 		if (strcmp(cb->name, cmd) == 0)
190 			return cb->main;
191 	return NULL;
192 }
193 
subcmd_main(int argc,char ** argv,const struct subcommand * cb)194 int subcmd_main(int argc, char **argv, const struct subcommand *cb)
195 {
196 	const char *cmd = basename(*argv);
197 	mainfunc_t f = subcmd_get(cmd, cb);
198 
199 	if (f == NULL && argc > 1) {
200 		/*
201 		 * Unable to find a main method for our command name?
202 		 * Let's try again with the first argument!
203 		 */
204 		++argv;
205 		--argc;
206 		f = subcmd_get(*argv, cb);
207 	}
208 
209 	/* now we should have a valid function pointer */
210 	if (f != NULL)
211 		return f(argc, argv);
212 
213 	fprintf(stderr, "ERROR: No valid subcommand given.\nValid subcommands:\n");
214 	for (; cb->name != NULL; ++cb)
215 		fprintf(stderr, " * %s\n", cb->name);
216 	exit(EXIT_FAILURE);
217 }
218 
xs_init_target(struct xtables_target * target)219 void xs_init_target(struct xtables_target *target)
220 {
221 	if (target->udata_size != 0) {
222 		free(target->udata);
223 		target->udata = calloc(1, target->udata_size);
224 		if (target->udata == NULL)
225 			xtables_error(RESOURCE_PROBLEM, "malloc");
226 	}
227 	if (target->init != NULL)
228 		target->init(target->t);
229 }
230 
xs_init_match(struct xtables_match * match)231 void xs_init_match(struct xtables_match *match)
232 {
233 	if (match->udata_size != 0) {
234 		/*
235 		 * As soon as a subsequent instance of the same match
236 		 * is used, e.g. "-m time -m time", the first instance
237 		 * is no longer reachable anyway, so we can free udata.
238 		 * Same goes for target.
239 		 */
240 		free(match->udata);
241 		match->udata = calloc(1, match->udata_size);
242 		if (match->udata == NULL)
243 			xtables_error(RESOURCE_PROBLEM, "malloc");
244 	}
245 	if (match->init != NULL)
246 		match->init(match->m);
247 }
248 
xtables_lock(int wait,struct timeval * wait_interval)249 static int xtables_lock(int wait, struct timeval *wait_interval)
250 {
251 	struct timeval time_left, wait_time;
252 	const char *lock_file;
253 	int fd, i = 0;
254 
255 	time_left.tv_sec = wait;
256 	time_left.tv_usec = 0;
257 
258 	lock_file = getenv("XTABLES_LOCKFILE");
259 	if (lock_file == NULL || lock_file[0] == '\0')
260 		lock_file = XT_LOCK_NAME;
261 
262 	fd = open(lock_file, O_CREAT, 0600);
263 	if (fd < 0) {
264 		fprintf(stderr, "Fatal: can't open lock file %s: %s\n",
265 			lock_file, strerror(errno));
266 		return XT_LOCK_FAILED;
267 	}
268 
269 	if (wait == -1) {
270 		if (flock(fd, LOCK_EX) == 0)
271 			return fd;
272 
273 		fprintf(stderr, "Can't lock %s: %s\n", lock_file,
274 			strerror(errno));
275 		return XT_LOCK_BUSY;
276 	}
277 
278 	while (1) {
279 		if (flock(fd, LOCK_EX | LOCK_NB) == 0)
280 			return fd;
281 		else if (timercmp(&time_left, wait_interval, <))
282 			return XT_LOCK_BUSY;
283 
284 		if (++i % 10 == 0) {
285 			fprintf(stderr, "Another app is currently holding the xtables lock; "
286 				"still %lds %ldus time ahead to have a chance to grab the lock...\n",
287 				time_left.tv_sec, time_left.tv_usec);
288 		}
289 
290 		wait_time = *wait_interval;
291 		select(0, NULL, NULL, NULL, &wait_time);
292 		timersub(&time_left, wait_interval, &time_left);
293 	}
294 }
295 
xtables_unlock(int lock)296 void xtables_unlock(int lock)
297 {
298 	if (lock >= 0)
299 		close(lock);
300 }
301 
xtables_lock_or_exit(int wait,struct timeval * wait_interval)302 int xtables_lock_or_exit(int wait, struct timeval *wait_interval)
303 {
304 	int lock = xtables_lock(wait, wait_interval);
305 
306 	if (lock == XT_LOCK_FAILED) {
307 		xtables_free_opts(1);
308 		exit(RESOURCE_PROBLEM);
309 	}
310 
311 	if (lock == XT_LOCK_BUSY) {
312 		fprintf(stderr, "Another app is currently holding the xtables lock. ");
313 		if (wait == 0)
314 			fprintf(stderr, "Perhaps you want to use the -w option?\n");
315 		else
316 			fprintf(stderr, "Stopped waiting after %ds.\n", wait);
317 		xtables_free_opts(1);
318 		exit(RESOURCE_PROBLEM);
319 	}
320 
321 	return lock;
322 }
323 
parse_wait_time(int argc,char * argv[])324 int parse_wait_time(int argc, char *argv[])
325 {
326 	int wait = -1;
327 
328 	if (optarg) {
329 		if (sscanf(optarg, "%i", &wait) != 1)
330 			xtables_error(PARAMETER_PROBLEM,
331 				"wait seconds not numeric");
332 	} else if (xs_has_arg(argc, argv))
333 		if (sscanf(argv[optind++], "%i", &wait) != 1)
334 			xtables_error(PARAMETER_PROBLEM,
335 				"wait seconds not numeric");
336 
337 	return wait;
338 }
339 
parse_wait_interval(int argc,char * argv[],struct timeval * wait_interval)340 void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval)
341 {
342 	const char *arg;
343 	unsigned int usec;
344 	int ret;
345 
346 	if (optarg)
347 		arg = optarg;
348 	else if (xs_has_arg(argc, argv))
349 		arg = argv[optind++];
350 	else
351 		xtables_error(PARAMETER_PROBLEM, "wait interval value required");
352 
353 	ret = sscanf(arg, "%u", &usec);
354 	if (ret == 1) {
355 		if (usec > 999999)
356 			xtables_error(PARAMETER_PROBLEM,
357 				      "too long usec wait %u > 999999 usec",
358 				      usec);
359 
360 		wait_interval->tv_sec = 0;
361 		wait_interval->tv_usec = usec;
362 		return;
363 	}
364 	xtables_error(PARAMETER_PROBLEM, "wait interval not numeric");
365 }
366 
parse_counters(const char * string,struct xt_counters * ctr)367 int parse_counters(const char *string, struct xt_counters *ctr)
368 {
369 	int ret;
370 
371 	if (!string)
372 		return 0;
373 
374 	ret = sscanf(string, "[%llu:%llu]",
375 		     (unsigned long long *)&ctr->pcnt,
376 		     (unsigned long long *)&ctr->bcnt);
377 
378 	return ret == 2;
379 }
380 
381 /* Tokenize counters argument of typical iptables-restore format rule.
382  *
383  * If *bufferp contains counters, update *pcntp and *bcntp to point at them,
384  * change bytes after counters in *bufferp to nul-bytes, update *bufferp to
385  * point to after the counters and return true.
386  * If *bufferp does not contain counters, return false.
387  * If syntax is wrong in *bufferp, call xtables_error() and hence exit().
388  * */
tokenize_rule_counters(char ** bufferp,char ** pcntp,char ** bcntp,int line)389 bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line)
390 {
391 	char *ptr, *buffer = *bufferp, *pcnt, *bcnt;
392 
393 	if (buffer[0] != '[')
394 		return false;
395 
396 	/* we have counters in our input */
397 
398 	ptr = strchr(buffer, ']');
399 	if (!ptr)
400 		xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
401 
402 	pcnt = strtok(buffer+1, ":");
403 	if (!pcnt)
404 		xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :\n", line);
405 
406 	bcnt = strtok(NULL, "]");
407 	if (!bcnt)
408 		xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
409 
410 	*pcntp = pcnt;
411 	*bcntp = bcnt;
412 	/* start command parsing after counter */
413 	*bufferp = ptr + 1;
414 
415 	return true;
416 }
417 
xs_has_arg(int argc,char * argv[])418 inline bool xs_has_arg(int argc, char *argv[])
419 {
420 	return optind < argc &&
421 	       argv[optind][0] != '-' &&
422 	       argv[optind][0] != '!';
423 }
424 
425 /* function adding one argument to store, updating argc
426  * returns if argument added, does not return otherwise */
add_argv(struct argv_store * store,const char * what,int quoted)427 void add_argv(struct argv_store *store, const char *what, int quoted)
428 {
429 	DEBUGP("add_argv: %s\n", what);
430 
431 	if (store->argc + 1 >= MAX_ARGC)
432 		xtables_error(PARAMETER_PROBLEM,
433 			      "Parser cannot handle more arguments\n");
434 	if (!what)
435 		xtables_error(PARAMETER_PROBLEM,
436 			      "Trying to store NULL argument\n");
437 
438 	store->argv[store->argc] = strdup(what);
439 	store->argvattr[store->argc] = quoted;
440 	store->argv[++store->argc] = NULL;
441 }
442 
free_argv(struct argv_store * store)443 void free_argv(struct argv_store *store)
444 {
445 	while (store->argc) {
446 		store->argc--;
447 		free(store->argv[store->argc]);
448 		store->argvattr[store->argc] = 0;
449 	}
450 }
451 
452 /* Save parsed rule for comparison with next rule to perform action aggregation
453  * on duplicate conditions.
454  */
save_argv(struct argv_store * dst,struct argv_store * src)455 void save_argv(struct argv_store *dst, struct argv_store *src)
456 {
457 	int i;
458 
459 	free_argv(dst);
460 	for (i = 0; i < src->argc; i++) {
461 		dst->argvattr[i] = src->argvattr[i];
462 		dst->argv[i] = src->argv[i];
463 		src->argv[i] = NULL;
464 	}
465 	dst->argc = src->argc;
466 	src->argc = 0;
467 }
468 
469 struct xt_param_buf {
470 	char	buffer[1024];
471 	int 	len;
472 };
473 
add_param(struct xt_param_buf * param,const char * curchar)474 static void add_param(struct xt_param_buf *param, const char *curchar)
475 {
476 	param->buffer[param->len++] = *curchar;
477 	if (param->len >= sizeof(param->buffer))
478 		xtables_error(PARAMETER_PROBLEM,
479 			      "Parameter too long!");
480 }
481 
add_param_to_argv(struct argv_store * store,char * parsestart,int line)482 void add_param_to_argv(struct argv_store *store, char *parsestart, int line)
483 {
484 	int quote_open = 0, escaped = 0, quoted = 0;
485 	struct xt_param_buf param = {};
486 	char *curchar;
487 
488 	/* After fighting with strtok enough, here's now
489 	 * a 'real' parser. According to Rusty I'm now no
490 	 * longer a real hacker, but I can live with that */
491 
492 	for (curchar = parsestart; *curchar; curchar++) {
493 		if (quote_open) {
494 			if (escaped) {
495 				add_param(&param, curchar);
496 				escaped = 0;
497 				continue;
498 			} else if (*curchar == '\\') {
499 				escaped = 1;
500 				continue;
501 			} else if (*curchar == '"') {
502 				quote_open = 0;
503 			} else {
504 				add_param(&param, curchar);
505 				continue;
506 			}
507 		} else {
508 			if (*curchar == '"') {
509 				quote_open = 1;
510 				quoted = 1;
511 				continue;
512 			}
513 		}
514 
515 		switch (*curchar) {
516 		case '"':
517 			break;
518 		case ' ':
519 		case '\t':
520 		case '\n':
521 			if (!param.len) {
522 				/* two spaces? */
523 				continue;
524 			}
525 			break;
526 		default:
527 			/* regular character, copy to buffer */
528 			add_param(&param, curchar);
529 			continue;
530 		}
531 
532 		param.buffer[param.len] = '\0';
533 		add_argv(store, param.buffer, quoted);
534 		param.len = 0;
535 		quoted = 0;
536 	}
537 	if (param.len) {
538 		param.buffer[param.len] = '\0';
539 		add_argv(store, param.buffer, 0);
540 	}
541 }
542 
543 #ifdef DEBUG
debug_print_argv(struct argv_store * store)544 void debug_print_argv(struct argv_store *store)
545 {
546 	int i;
547 
548 	for (i = 0; i < store->argc; i++)
549 		fprintf(stderr, "argv[%d]: %s\n", i, store->argv[i]);
550 }
551 #endif
552 
ipv4_addr_to_string(const struct in_addr * addr,const struct in_addr * mask,unsigned int format)553 static const char *ipv4_addr_to_string(const struct in_addr *addr,
554 				       const struct in_addr *mask,
555 				       unsigned int format)
556 {
557 	static char buf[BUFSIZ];
558 
559 	if (!mask->s_addr && !(format & FMT_NUMERIC))
560 		return "anywhere";
561 
562 	if (format & FMT_NUMERIC)
563 		strncpy(buf, xtables_ipaddr_to_numeric(addr), BUFSIZ - 1);
564 	else
565 		strncpy(buf, xtables_ipaddr_to_anyname(addr), BUFSIZ - 1);
566 	buf[BUFSIZ - 1] = '\0';
567 
568 	strncat(buf, xtables_ipmask_to_numeric(mask),
569 		BUFSIZ - strlen(buf) - 1);
570 
571 	return buf;
572 }
573 
print_ipv4_addresses(const struct ipt_entry * fw,unsigned int format)574 void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format)
575 {
576 	fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
577 	printf(FMT("%-19s ", "%s "),
578 	       ipv4_addr_to_string(&fw->ip.src, &fw->ip.smsk, format));
579 
580 	fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
581 	printf(FMT("%-19s ", "-> %s"),
582 	       ipv4_addr_to_string(&fw->ip.dst, &fw->ip.dmsk, format));
583 }
584 
ipv6_addr_to_string(const struct in6_addr * addr,const struct in6_addr * mask,unsigned int format)585 static const char *ipv6_addr_to_string(const struct in6_addr *addr,
586 				       const struct in6_addr *mask,
587 				       unsigned int format)
588 {
589 	static char buf[BUFSIZ];
590 
591 	if (IN6_IS_ADDR_UNSPECIFIED(addr) && !(format & FMT_NUMERIC))
592 		return "anywhere";
593 
594 	if (format & FMT_NUMERIC)
595 		strncpy(buf, xtables_ip6addr_to_numeric(addr), BUFSIZ - 1);
596 	else
597 		strncpy(buf, xtables_ip6addr_to_anyname(addr), BUFSIZ - 1);
598 	buf[BUFSIZ - 1] = '\0';
599 
600 	strncat(buf, xtables_ip6mask_to_numeric(mask),
601 		BUFSIZ - strlen(buf) - 1);
602 
603 	return buf;
604 }
605 
print_ipv6_addresses(const struct ip6t_entry * fw6,unsigned int format)606 void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format)
607 {
608 	fputc(fw6->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
609 	printf(FMT("%-19s ", "%s "),
610 	       ipv6_addr_to_string(&fw6->ipv6.src,
611 				   &fw6->ipv6.smsk, format));
612 
613 	fputc(fw6->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
614 	printf(FMT("%-19s ", "-> %s"),
615 	       ipv6_addr_to_string(&fw6->ipv6.dst,
616 				   &fw6->ipv6.dmsk, format));
617 }
618 
619 /* Luckily, IPT_INV_VIA_IN and IPT_INV_VIA_OUT
620  * have the same values as IP6T_INV_VIA_IN and IP6T_INV_VIA_OUT
621  * so this function serves for both iptables and ip6tables */
print_ifaces(const char * iniface,const char * outiface,uint8_t invflags,unsigned int format)622 void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
623 		  unsigned int format)
624 {
625 	const char *anyname = format & FMT_NUMERIC ? "*" : "any";
626 	char iface[IFNAMSIZ + 2];
627 
628 	if (!(format & FMT_VIA))
629 		return;
630 
631 	snprintf(iface, IFNAMSIZ + 2, "%s%s",
632 		 invflags & IPT_INV_VIA_IN ? "!" : "",
633 		 iniface[0] != '\0' ? iniface : anyname);
634 
635 	printf(FMT(" %-6s ", "in %s "), iface);
636 
637 	snprintf(iface, IFNAMSIZ + 2, "%s%s",
638 		 invflags & IPT_INV_VIA_OUT ? "!" : "",
639 		 outiface[0] != '\0' ? outiface : anyname);
640 
641 	printf(FMT("%-6s ", "out %s "), iface);
642 }
643 
command_match(struct iptables_command_state * cs)644 void command_match(struct iptables_command_state *cs)
645 {
646 	struct option *opts = xt_params->opts;
647 	struct xtables_match *m;
648 	size_t size;
649 
650 	if (cs->invert)
651 		xtables_error(PARAMETER_PROBLEM,
652 			   "unexpected ! flag before --match");
653 
654 	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
655 	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
656 	m->m = xtables_calloc(1, size);
657 	m->m->u.match_size = size;
658 	if (m->real_name == NULL) {
659 		strcpy(m->m->u.user.name, m->name);
660 	} else {
661 		strcpy(m->m->u.user.name, m->real_name);
662 		if (!(m->ext_flags & XTABLES_EXT_ALIAS))
663 			fprintf(stderr, "Notice: the %s match is converted into %s match "
664 				"in rule listing and saving.\n", m->name, m->real_name);
665 	}
666 	m->m->u.user.revision = m->revision;
667 	xs_init_match(m);
668 	if (m == m->next)
669 		return;
670 	/* Merge options for non-cloned matches */
671 	if (m->x6_options != NULL)
672 		opts = xtables_options_xfrm(xt_params->orig_opts, opts,
673 					    m->x6_options, &m->option_offset);
674 	else if (m->extra_opts != NULL)
675 		opts = xtables_merge_options(xt_params->orig_opts, opts,
676 					     m->extra_opts, &m->option_offset);
677 	if (opts == NULL)
678 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
679 	xt_params->opts = opts;
680 }
681 
xt_parse_target(const char * targetname)682 const char *xt_parse_target(const char *targetname)
683 {
684 	const char *ptr;
685 
686 	if (strlen(targetname) < 1)
687 		xtables_error(PARAMETER_PROBLEM,
688 			   "Invalid target name (too short)");
689 
690 	if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
691 		xtables_error(PARAMETER_PROBLEM,
692 			   "Invalid target name `%s' (%u chars max)",
693 			   targetname, XT_EXTENSION_MAXNAMELEN - 1);
694 
695 	for (ptr = targetname; *ptr; ptr++)
696 		if (isspace(*ptr))
697 			xtables_error(PARAMETER_PROBLEM,
698 				   "Invalid target name `%s'", targetname);
699 	return targetname;
700 }
701 
command_jump(struct iptables_command_state * cs,const char * jumpto)702 void command_jump(struct iptables_command_state *cs, const char *jumpto)
703 {
704 	struct option *opts = xt_params->opts;
705 	size_t size;
706 
707 	cs->jumpto = xt_parse_target(jumpto);
708 	/* TRY_LOAD (may be chain name) */
709 	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
710 
711 	if (cs->target == NULL)
712 		return;
713 
714 	size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
715 
716 	cs->target->t = xtables_calloc(1, size);
717 	cs->target->t->u.target_size = size;
718 	if (cs->target->real_name == NULL) {
719 		strcpy(cs->target->t->u.user.name, cs->jumpto);
720 	} else {
721 		/* Alias support for userspace side */
722 		strcpy(cs->target->t->u.user.name, cs->target->real_name);
723 		if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
724 			fprintf(stderr, "Notice: The %s target is converted into %s target "
725 				"in rule listing and saving.\n",
726 				cs->jumpto, cs->target->real_name);
727 	}
728 	cs->target->t->u.user.revision = cs->target->revision;
729 	xs_init_target(cs->target);
730 
731 	if (cs->target->x6_options != NULL)
732 		opts = xtables_options_xfrm(xt_params->orig_opts, opts,
733 					    cs->target->x6_options,
734 					    &cs->target->option_offset);
735 	else
736 		opts = xtables_merge_options(xt_params->orig_opts, opts,
737 					     cs->target->extra_opts,
738 					     &cs->target->option_offset);
739 	if (opts == NULL)
740 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
741 	xt_params->opts = opts;
742 }
743 
cmd2char(int option)744 char cmd2char(int option)
745 {
746 	/* cmdflags index corresponds with position of bit in CMD_* values */
747 	static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
748 					 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
749 	int i;
750 
751 	for (i = 0; option > 1; option >>= 1, i++)
752 		;
753 	if (i >= ARRAY_SIZE(cmdflags))
754 		xtables_error(OTHER_PROBLEM,
755 			      "cmd2char(): Invalid command number %u.\n",
756 			      1 << i);
757 	return cmdflags[i];
758 }
759 
add_command(unsigned int * cmd,const int newcmd,const int othercmds,int invert)760 void add_command(unsigned int *cmd, const int newcmd,
761 		 const int othercmds, int invert)
762 {
763 	if (invert)
764 		xtables_error(PARAMETER_PROBLEM, "unexpected '!' flag");
765 	if (*cmd & (~othercmds))
766 		xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
767 			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
768 	*cmd |= newcmd;
769 }
770 
771 /* Can't be zero. */
parse_rulenumber(const char * rule)772 int parse_rulenumber(const char *rule)
773 {
774 	unsigned int rulenum;
775 
776 	if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
777 		xtables_error(PARAMETER_PROBLEM,
778 			   "Invalid rule number `%s'", rule);
779 
780 	return rulenum;
781 }
782 
783 /* Table of legal combinations of commands and options.  If any of the
784  * given commands make an option legal, that option is legal (applies to
785  * CMD_LIST and CMD_ZERO only).
786  * Key:
787  *  +  compulsory
788  *  x  illegal
789  *     optional
790  */
791 static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
792 /* Well, it's better than "Re: Linux vs FreeBSD" */
793 {
794 	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o --line -c -f 2 3 l 4 5 6 */
795 /*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
796 /*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
797 /*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
798 /*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
799 /*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
800 /*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x','x','x','x','x','x','x','x'},
801 /*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
802 /*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
803 /*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
804 /*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
805 /*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x','x','x','x','x','x','x'},
806 /*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
807 /*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
808 /*ZERO_NUM*/  {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
809 /*CHECK*/     {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
810 };
811 
generic_opt_check(int command,int options)812 void generic_opt_check(int command, int options)
813 {
814 	int i, j, legal = 0;
815 
816 	/* Check that commands are valid with options. Complicated by the
817 	 * fact that if an option is legal with *any* command given, it is
818 	 * legal overall (ie. -z and -l).
819 	 */
820 	for (i = 0; i < NUMBER_OF_OPT; i++) {
821 		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
822 
823 		for (j = 0; j < NUMBER_OF_CMD; j++) {
824 			if (!(command & (1<<j)))
825 				continue;
826 
827 			if (!(options & (1<<i))) {
828 				if (commands_v_options[j][i] == '+')
829 					xtables_error(PARAMETER_PROBLEM,
830 						   "You need to supply the `-%c' "
831 						   "option for this command\n",
832 						   optflags[i]);
833 			} else {
834 				if (commands_v_options[j][i] != 'x')
835 					legal = 1;
836 				else if (legal == 0)
837 					legal = -1;
838 			}
839 		}
840 		if (legal == -1)
841 			xtables_error(PARAMETER_PROBLEM,
842 				   "Illegal option `-%c' with this command\n",
843 				   optflags[i]);
844 	}
845 }
846 
opt2char(int option)847 char opt2char(int option)
848 {
849 	const char *ptr;
850 
851 	for (ptr = optflags; option > 1; option >>= 1, ptr++)
852 		;
853 
854 	return *ptr;
855 }
856