xtoptions.c revision d25e217578492d17f7752bf77cfab5f2c2509795
1/*
2 *	Argument parser
3 *	Copyright © Jan Engelhardt, 2011
4 *
5 *	This program is free software; you can redistribute it and/or
6 *	modify it under the terms of the GNU General Public License as
7 *	published by the Free Software Foundation; either version 2 of
8 *	the License, or (at your option) any later version.
9 */
10#include <errno.h>
11#include <getopt.h>
12#include <limits.h>
13#include <netdb.h>
14#include <stdbool.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <arpa/inet.h>
20#include "xtables.h"
21#include "xshared.h"
22
23#define XTOPT_MKPTR(cb) \
24	((void *)((char *)(cb)->data + (cb)->entry->ptroff))
25
26/**
27 * Creates getopt options from the x6-style option map, and assigns each a
28 * getopt id.
29 */
30struct option *
31xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
32		     const struct xt_option_entry *entry, unsigned int *offset)
33{
34	unsigned int num_orig, num_old = 0, num_new, i;
35	struct option *merge, *mp;
36
37	if (entry == NULL)
38		return oldopts;
39	for (num_orig = 0; orig_opts[num_orig].name != NULL; ++num_orig)
40		;
41	if (oldopts != NULL)
42		for (num_old = 0; oldopts[num_old].name != NULL; ++num_old)
43			;
44	for (num_new = 0; entry[num_new].name != NULL; ++num_new)
45		;
46
47	/*
48	 * Since @oldopts also has @orig_opts already (and does so at the
49	 * start), skip these entries.
50	 */
51	oldopts += num_orig;
52	num_old -= num_orig;
53
54	merge = malloc(sizeof(*mp) * (num_orig + num_old + num_new + 1));
55	if (merge == NULL)
56		return NULL;
57
58	/* Let the base options -[ADI...] have precedence over everything */
59	memcpy(merge, orig_opts, sizeof(*mp) * num_orig);
60	mp = merge + num_orig;
61
62	/* Second, the new options */
63	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
64	*offset = xt_params->option_offset;
65
66	for (i = 0; i < num_new; ++i, ++mp, ++entry) {
67		mp->name         = entry->name;
68		mp->has_arg      = entry->type != XTTYPE_NONE;
69		mp->flag         = NULL;
70		mp->val          = entry->id + *offset;
71	}
72
73	/* Third, the old options */
74	memcpy(mp, oldopts, sizeof(*mp) * num_old);
75	mp += num_old;
76	xtables_free_opts(0);
77
78	/* Clear trailing entry */
79	memset(mp, 0, sizeof(*mp));
80	return merge;
81}
82
83/**
84 * Require a simple integer.
85 */
86static void xtopt_parse_int(struct xt_option_call *cb)
87{
88	const struct xt_option_entry *entry = cb->entry;
89	unsigned int lmin = 0, lmax = UINT32_MAX;
90	unsigned int value;
91
92	if (entry->type == XTTYPE_UINT8)
93		lmax = UINT8_MAX;
94	if (cb->entry->min != 0)
95		lmin = cb->entry->min;
96	if (cb->entry->max != 0)
97		lmax = cb->entry->max;
98
99	if (!xtables_strtoui(cb->arg, NULL, &value, lmin, lmax))
100		xt_params->exit_err(PARAMETER_PROBLEM,
101			"%s: bad value for option \"--%s\", "
102			"or out of range (%u-%u).\n",
103			cb->ext_name, entry->name, lmin, lmax);
104
105	if (entry->type == XTTYPE_UINT8) {
106		cb->val.u8 = value;
107		if (entry->flags & XTOPT_PUT)
108			*(uint8_t *)XTOPT_MKPTR(cb) = cb->val.u8;
109	} else if (entry->type == XTTYPE_UINT32) {
110		cb->val.u32 = value;
111		if (entry->flags & XTOPT_PUT)
112			*(uint32_t *)XTOPT_MKPTR(cb) = cb->val.u32;
113	}
114}
115
116/**
117 * Multiple integer parse routine.
118 *
119 * This function is capable of parsing any number of fields. Only the first
120 * two values from the string will be put into @cb however (and as such,
121 * @cb->val.uXX_range is just that large) to cater for the few extensions that
122 * do not have a range[2] field, but {min, max}, and which cannot use
123 * XTOPT_POINTER.
124 */
125static void xtopt_parse_mint(struct xt_option_call *cb)
126{
127	const struct xt_option_entry *entry = cb->entry;
128	const char *arg = cb->arg;
129	uint32_t *put = XTOPT_MKPTR(cb);
130	unsigned int maxiter, value;
131	char *end = "";
132	char sep = ':';
133
134	maxiter = entry->size / sizeof(uint32_t);
135	if (maxiter == 0)
136		maxiter = 2; /* ARRAY_SIZE(cb->val.uXX_range) */
137	if (entry->size % sizeof(uint32_t) != 0)
138		xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
139			"not have proper size\n", __func__);
140
141	cb->nvals = 0;
142	for (arg = cb->arg; ; arg = end + 1) {
143		if (cb->nvals == maxiter)
144			xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
145				"components for option \"--%s\" (max: %u)\n",
146				cb->ext_name, entry->name, maxiter);
147		if (!xtables_strtoui(arg, &end, &value, 0, UINT32_MAX))
148			xt_params->exit_err(PARAMETER_PROBLEM,
149				"%s: bad value for option \"--%s\", "
150				"or out of range (0-%u).\n",
151				cb->ext_name, entry->name, UINT32_MAX);
152		if (*end != '\0' && *end != sep)
153			xt_params->exit_err(PARAMETER_PROBLEM,
154				"%s: Argument to \"--%s\" has unexpected "
155				"characters.\n", cb->ext_name, entry->name);
156		++cb->nvals;
157		if (cb->nvals < ARRAY_SIZE(cb->val.u32_range))
158			cb->val.u32_range[cb->nvals] = value;
159		if (entry->flags & XTOPT_PUT)
160			*put++ = value;
161		if (*end == '\0')
162			break;
163	}
164}
165
166static void xtopt_parse_string(struct xt_option_call *cb)
167{
168	const struct xt_option_entry *entry = cb->entry;
169	size_t z = strlen(cb->arg);
170	char *p;
171
172	if (entry->min != 0 && z < entry->min)
173		xt_params->exit_err(PARAMETER_PROBLEM,
174			"Argument must have a minimum length of "
175			"%u characters\n", entry->min);
176	if (entry->max != 0 && z > entry->max)
177		xt_params->exit_err(PARAMETER_PROBLEM,
178			"Argument must have a maximum length of "
179			"%u characters\n", entry->max);
180	if (!(entry->flags & XTOPT_PUT))
181		return;
182	if (z >= entry->size)
183		z = entry->size - 1;
184	p = XTOPT_MKPTR(cb);
185	strncpy(p, cb->arg, z);
186	p[z] = '\0';
187}
188
189/**
190 * Validate the input for being conformant to "mark[/mask]".
191 */
192static void xtopt_parse_markmask(struct xt_option_call *cb)
193{
194	unsigned int mark = 0, mask = ~0U;
195	char *end;
196
197	if (!xtables_strtoui(cb->arg, &end, &mark, 0, UINT32_MAX))
198		xt_params->exit_err(PARAMETER_PROBLEM,
199			"%s: bad mark value for option \"--%s\", "
200			"or out of range.\n",
201			cb->ext_name, cb->entry->name);
202	if (*end == '/' &&
203	    !xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
204		xt_params->exit_err(PARAMETER_PROBLEM,
205			"%s: bad mask value for option \"--%s\", "
206			"or out of range.\n",
207			cb->ext_name, cb->entry->name);
208	if (*end != '\0')
209		xt_params->exit_err(PARAMETER_PROBLEM,
210			"%s: trailing garbage after value "
211			"for option \"--%s\".\n",
212			cb->ext_name, cb->entry->name);
213	cb->val.mark = mark;
214	cb->val.mask = mask;
215}
216
217static void (*const xtopt_subparse[])(struct xt_option_call *) = {
218	[XTTYPE_UINT8]       = xtopt_parse_int,
219	[XTTYPE_UINT32]      = xtopt_parse_int,
220	[XTTYPE_UINT32RC]    = xtopt_parse_mint,
221	[XTTYPE_STRING]      = xtopt_parse_string,
222	[XTTYPE_MARKMASK32]  = xtopt_parse_markmask,
223};
224
225static const size_t xtopt_psize[] = {
226	[XTTYPE_UINT8]       = sizeof(uint8_t),
227	[XTTYPE_UINT32]      = sizeof(uint32_t),
228	[XTTYPE_UINT32RC]    = sizeof(uint32_t[2]),
229	[XTTYPE_STRING]      = -1,
230};
231
232/**
233 * The master option parsing routine. May be used for the ".x6_parse"
234 * function pointer in extensions if fully automatic parsing is desired.
235 * It may be also called manually from a custom x6_parse function.
236 */
237void xtables_option_parse(struct xt_option_call *cb)
238{
239	const struct xt_option_entry *entry = cb->entry;
240	unsigned int eflag = 1 << cb->entry->id;
241
242	/*
243	 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
244	 * prevention. Though it turned out that this is too much typing (most
245	 * of the options are one-time use only), so now we also have
246	 * %XTOPT_MULTI.
247	 */
248	if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
249	    cb->xflags & eflag)
250		xt_params->exit_err(PARAMETER_PROBLEM,
251			"%s: option \"--%s\" can only be used once.\n",
252			cb->ext_name, cb->entry->name);
253	if (cb->invert && !(entry->flags & XTOPT_INVERT))
254		xt_params->exit_err(PARAMETER_PROBLEM,
255			"%s: option \"--%s\" cannot be inverted.\n",
256			cb->ext_name, entry->name);
257	if (entry->type != XTTYPE_NONE && optarg == NULL)
258		xt_params->exit_err(PARAMETER_PROBLEM,
259			"%s: option \"--%s\" requires an argument.\n",
260			cb->ext_name, entry->name);
261	if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
262	    xtopt_subparse[entry->type] != NULL)
263		xtopt_subparse[entry->type](cb);
264	/* Exclusion with other flags tested later in finalize. */
265	cb->xflags |= 1 << entry->id;
266}
267
268/**
269 * Verifies that an extension's option map descriptor is valid, and ought to
270 * be called right after the extension has been loaded, and before option
271 * merging/xfrm.
272 */
273void xtables_option_metavalidate(const char *name,
274				 const struct xt_option_entry *entry)
275{
276	for (; entry->name != NULL; ++entry) {
277		if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
278		    entry->id >= XT_OPTION_OFFSET_SCALE)
279			xt_params->exit_err(OTHER_PROBLEM,
280				"Extension %s uses invalid ID %u\n",
281				name, entry->id);
282		if (!(entry->flags & XTOPT_PUT))
283			continue;
284		if (entry->type >= ARRAY_SIZE(xtopt_psize))
285			xt_params->exit_err(OTHER_PROBLEM,
286				"%s: entry type of option \"--%s\" cannot be "
287				"combined with XTOPT_PUT\n",
288				name, entry->name);
289		if (xtopt_psize[entry->type] != -1 &&
290		    xtopt_psize[entry->type] != entry->size)
291			xt_params->exit_err(OTHER_PROBLEM,
292				"%s: option \"--%s\" points to a memory block "
293				"of wrong size (expected %zu, got %zu)\n",
294				name, entry->name,
295				xtopt_psize[entry->type], entry->size);
296	}
297}
298
299/**
300 * Find an option entry by its id.
301 */
302static const struct xt_option_entry *
303xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
304{
305	for (; entry->name != NULL; ++entry)
306		if (entry->id == id)
307			return entry;
308	return NULL;
309}
310
311/**
312 * @c:		getopt id (i.e. with offset)
313 * @fw:		struct ipt_entry or ip6t_entry
314 *
315 * Dispatch arguments to the appropriate parse function, based upon the
316 * extension's choice of API.
317 */
318void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
319			   struct xtables_target *t, void *fw)
320{
321	struct xt_option_call cb;
322
323	if (t->x6_parse == NULL) {
324		if (t->parse != NULL)
325			t->parse(c - t->option_offset, argv, invert,
326				 &t->tflags, fw, &t->t);
327		return;
328	}
329
330	c -= t->option_offset;
331	cb.entry = xtables_option_lookup(t->x6_options, c);
332	if (cb.entry == NULL)
333		xtables_error(OTHER_PROBLEM,
334			"Extension does not know id %u\n", c);
335	cb.arg      = optarg;
336	cb.invert   = invert;
337	cb.ext_name = t->name;
338	cb.data     = t->t->data;
339	cb.xflags   = t->tflags;
340	t->x6_parse(&cb);
341	t->tflags = cb.xflags;
342}
343
344/**
345 * @c:		getopt id (i.e. with offset)
346 * @fw:		struct ipt_entry or ip6t_entry
347 *
348 * Dispatch arguments to the appropriate parse function, based upon the
349 * extension's choice of API.
350 */
351void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
352			   struct xtables_match *m, void *fw)
353{
354	struct xt_option_call cb;
355
356	if (m->x6_parse == NULL) {
357		if (m->parse != NULL)
358			m->parse(c - m->option_offset, argv, invert,
359				 &m->mflags, fw, &m->m);
360		return;
361	}
362
363	c -= m->option_offset;
364	cb.entry = xtables_option_lookup(m->x6_options, c);
365	if (cb.entry == NULL)
366		xtables_error(OTHER_PROBLEM,
367			"Extension does not know id %u\n", c);
368	cb.arg      = optarg;
369	cb.invert   = invert;
370	cb.ext_name = m->name;
371	cb.data     = m->m->data;
372	cb.xflags   = m->mflags;
373	m->x6_parse(&cb);
374	m->mflags = cb.xflags;
375}
376
377/**
378 * @name:	name of extension
379 * @entry:	current option (from all ext's entries) being validated
380 * @xflags:	flags the extension has collected
381 * @i:		conflicting option (id) to test for
382 */
383static void
384xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
385		       const struct xt_option_entry *other,
386		       unsigned int xflags)
387{
388	unsigned int ef = 1 << entry->id, of = 1 << other->id;
389
390	if (entry->also & of && !(xflags & of))
391		xt_params->exit_err(PARAMETER_PROBLEM,
392			"%s: option \"--%s\" also requires \"--%s\".\n",
393			name, entry->name, other->name);
394
395	if (!(entry->excl & of))
396		/* Use of entry does not collide with other option, good. */
397		return;
398	if ((xflags & (ef | of)) != (ef | of))
399		/* Conflicting options were not used. */
400		return;
401
402	xt_params->exit_err(PARAMETER_PROBLEM,
403		"%s: option \"--%s\" cannot be used together with \"--%s\".\n",
404		name, entry->name, other->name);
405}
406
407/**
408 * @name:	name of extension
409 * @xflags:	accumulated flags
410 * @entry:	extension's option table
411 *
412 * Check that all option constraints have been met. This effectively replaces
413 * ->final_check of the older API.
414 */
415void xtables_options_fcheck(const char *name, unsigned int xflags,
416			    const struct xt_option_entry *table)
417{
418	const struct xt_option_entry *entry, *other;
419	unsigned int i;
420
421	for (entry = table; entry->name != NULL; ++entry) {
422		if (entry->flags & XTOPT_MAND &&
423		    !(xflags & (1 << entry->id)))
424			xt_params->exit_err(PARAMETER_PROBLEM,
425				"%s: option \"--%s\" must be specified\n",
426				name, entry->name);
427
428		for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
429			if (entry->id == i)
430				/*
431				 * Avoid conflict with self. Multi-use check
432				 * was done earlier in xtables_option_parse.
433				 */
434				continue;
435			other = xtables_option_lookup(table, i);
436			if (other == NULL)
437				continue;
438			xtables_option_fcheck2(name, entry, other, xflags);
439		}
440	}
441}
442
443/**
444 * Dispatch arguments to the appropriate final_check function, based upon the
445 * extension's choice of API.
446 */
447void xtables_option_tfcall(struct xtables_target *t)
448{
449	if (t->x6_fcheck != NULL) {
450		struct xt_fcheck_call cb;
451
452		cb.ext_name = t->name;
453		cb.data     = t->t->data;
454		cb.xflags   = t->tflags;
455		t->x6_fcheck(&cb);
456	} else if (t->final_check != NULL) {
457		t->final_check(t->tflags);
458	}
459	if (t->x6_options != NULL)
460		xtables_options_fcheck(t->name, t->tflags, t->x6_options);
461}
462
463/**
464 * Dispatch arguments to the appropriate final_check function, based upon the
465 * extension's choice of API.
466 */
467void xtables_option_mfcall(struct xtables_match *m)
468{
469	if (m->x6_fcheck != NULL) {
470		struct xt_fcheck_call cb;
471
472		cb.ext_name = m->name;
473		cb.data     = m->m->data;
474		cb.xflags   = m->mflags;
475		m->x6_fcheck(&cb);
476	} else if (m->final_check != NULL) {
477		m->final_check(m->mflags);
478	}
479	if (m->x6_options != NULL)
480		xtables_options_fcheck(m->name, m->mflags, m->x6_options);
481}
482