xtoptions.c revision 4a0a17620017c1f45946b2cde7139ef18ea3d93c
185a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org/*
285a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	Argument parser
385a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	Copyright © Jan Engelhardt, 2011
485a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *
585a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	This program is free software; you can redistribute it and/or
685a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	modify it under the terms of the GNU General Public License as
785a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	published by the Free Software Foundation; either version 2 of
885a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org *	the License, or (at your option) any later version.
985a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org */
1085a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <errno.h>
1185a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <getopt.h>
1285a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <limits.h>
1385a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <netdb.h>
1485a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <stdbool.h>
1585a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <stdint.h>
1685a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <stdio.h>
1785a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <stdlib.h>
1885a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <string.h>
1985a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include <arpa/inet.h>
2085a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include "xtables.h"
2185a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#include "xshared.h"
2285a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org
2385a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org#define XTOPT_MKPTR(cb) \
2485a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org	((void *)((char *)(cb)->data + (cb)->entry->ptroff))
2585a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org
2685a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org/**
2785a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org * Creates getopt options from the x6-style option map, and assigns each a
2885a1dab7adc4a795cf62ccf08d6727569db9e935sergeyu@chromium.org * 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
189static void (*const xtopt_subparse[])(struct xt_option_call *) = {
190	[XTTYPE_UINT8]       = xtopt_parse_int,
191	[XTTYPE_UINT32]      = xtopt_parse_int,
192	[XTTYPE_UINT32RC]    = xtopt_parse_mint,
193	[XTTYPE_STRING]      = xtopt_parse_string,
194};
195
196static const size_t xtopt_psize[] = {
197	[XTTYPE_UINT8]       = sizeof(uint8_t),
198	[XTTYPE_UINT32]      = sizeof(uint32_t),
199	[XTTYPE_UINT32RC]    = sizeof(uint32_t[2]),
200	[XTTYPE_STRING]      = -1,
201};
202
203/**
204 * The master option parsing routine. May be used for the ".x6_parse"
205 * function pointer in extensions if fully automatic parsing is desired.
206 * It may be also called manually from a custom x6_parse function.
207 */
208void xtables_option_parse(struct xt_option_call *cb)
209{
210	const struct xt_option_entry *entry = cb->entry;
211	unsigned int eflag = 1 << cb->entry->id;
212
213	/*
214	 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
215	 * prevention. Though it turned out that this is too much typing (most
216	 * of the options are one-time use only), so now we also have
217	 * %XTOPT_MULTI.
218	 */
219	if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
220	    cb->xflags & eflag)
221		xt_params->exit_err(PARAMETER_PROBLEM,
222			"%s: option \"--%s\" can only be used once.\n",
223			cb->ext_name, cb->entry->name);
224	if (cb->invert && !(entry->flags & XTOPT_INVERT))
225		xt_params->exit_err(PARAMETER_PROBLEM,
226			"%s: option \"--%s\" cannot be inverted.\n",
227			cb->ext_name, entry->name);
228	if (entry->type != XTTYPE_NONE && optarg == NULL)
229		xt_params->exit_err(PARAMETER_PROBLEM,
230			"%s: option \"--%s\" requires an argument.\n",
231			cb->ext_name, entry->name);
232	if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
233	    xtopt_subparse[entry->type] != NULL)
234		xtopt_subparse[entry->type](cb);
235	/* Exclusion with other flags tested later in finalize. */
236	cb->xflags |= 1 << entry->id;
237}
238
239/**
240 * Verifies that an extension's option map descriptor is valid, and ought to
241 * be called right after the extension has been loaded, and before option
242 * merging/xfrm.
243 */
244void xtables_option_metavalidate(const char *name,
245				 const struct xt_option_entry *entry)
246{
247	for (; entry->name != NULL; ++entry) {
248		if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
249		    entry->id >= XT_OPTION_OFFSET_SCALE)
250			xt_params->exit_err(OTHER_PROBLEM,
251				"Extension %s uses invalid ID %u\n",
252				name, entry->id);
253		if (!(entry->flags & XTOPT_PUT))
254			continue;
255		if (entry->type >= ARRAY_SIZE(xtopt_psize))
256			xt_params->exit_err(OTHER_PROBLEM,
257				"%s: entry type of option \"--%s\" cannot be "
258				"combined with XTOPT_PUT\n",
259				name, entry->name);
260		if (xtopt_psize[entry->type] != -1 &&
261		    xtopt_psize[entry->type] != entry->size)
262			xt_params->exit_err(OTHER_PROBLEM,
263				"%s: option \"--%s\" points to a memory block "
264				"of wrong size (expected %zu, got %zu)\n",
265				name, entry->name,
266				xtopt_psize[entry->type], entry->size);
267	}
268}
269
270/**
271 * Find an option entry by its id.
272 */
273static const struct xt_option_entry *
274xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
275{
276	for (; entry->name != NULL; ++entry)
277		if (entry->id == id)
278			return entry;
279	return NULL;
280}
281
282/**
283 * @c:		getopt id (i.e. with offset)
284 * @fw:		struct ipt_entry or ip6t_entry
285 *
286 * Dispatch arguments to the appropriate parse function, based upon the
287 * extension's choice of API.
288 */
289void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
290			   struct xtables_target *t, void *fw)
291{
292	struct xt_option_call cb;
293
294	if (t->x6_parse == NULL) {
295		if (t->parse != NULL)
296			t->parse(c - t->option_offset, argv, invert,
297				 &t->tflags, fw, &t->t);
298		return;
299	}
300
301	c -= t->option_offset;
302	cb.entry = xtables_option_lookup(t->x6_options, c);
303	if (cb.entry == NULL)
304		xtables_error(OTHER_PROBLEM,
305			"Extension does not know id %u\n", c);
306	cb.arg      = optarg;
307	cb.invert   = invert;
308	cb.ext_name = t->name;
309	cb.data     = t->t->data;
310	cb.xflags   = t->tflags;
311	t->x6_parse(&cb);
312	t->tflags = cb.xflags;
313}
314
315/**
316 * @c:		getopt id (i.e. with offset)
317 * @fw:		struct ipt_entry or ip6t_entry
318 *
319 * Dispatch arguments to the appropriate parse function, based upon the
320 * extension's choice of API.
321 */
322void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
323			   struct xtables_match *m, void *fw)
324{
325	struct xt_option_call cb;
326
327	if (m->x6_parse == NULL) {
328		if (m->parse != NULL)
329			m->parse(c - m->option_offset, argv, invert,
330				 &m->mflags, fw, &m->m);
331		return;
332	}
333
334	c -= m->option_offset;
335	cb.entry = xtables_option_lookup(m->x6_options, c);
336	if (cb.entry == NULL)
337		xtables_error(OTHER_PROBLEM,
338			"Extension does not know id %u\n", c);
339	cb.arg      = optarg;
340	cb.invert   = invert;
341	cb.ext_name = m->name;
342	cb.data     = m->m->data;
343	cb.xflags   = m->mflags;
344	m->x6_parse(&cb);
345	m->mflags = cb.xflags;
346}
347
348/**
349 * @name:	name of extension
350 * @entry:	current option (from all ext's entries) being validated
351 * @xflags:	flags the extension has collected
352 * @i:		conflicting option (id) to test for
353 */
354static void
355xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
356		       const struct xt_option_entry *other,
357		       unsigned int xflags)
358{
359	unsigned int ef = 1 << entry->id, of = 1 << other->id;
360
361	if (entry->also & of && !(xflags & of))
362		xt_params->exit_err(PARAMETER_PROBLEM,
363			"%s: option \"--%s\" also requires \"--%s\".\n",
364			name, entry->name, other->name);
365
366	if (!(entry->excl & of))
367		/* Use of entry does not collide with other option, good. */
368		return;
369	if ((xflags & (ef | of)) != (ef | of))
370		/* Conflicting options were not used. */
371		return;
372
373	xt_params->exit_err(PARAMETER_PROBLEM,
374		"%s: option \"--%s\" cannot be used together with \"--%s\".\n",
375		name, entry->name, other->name);
376}
377
378/**
379 * @name:	name of extension
380 * @xflags:	accumulated flags
381 * @entry:	extension's option table
382 *
383 * Check that all option constraints have been met. This effectively replaces
384 * ->final_check of the older API.
385 */
386void xtables_options_fcheck(const char *name, unsigned int xflags,
387			    const struct xt_option_entry *table)
388{
389	const struct xt_option_entry *entry, *other;
390	unsigned int i;
391
392	for (entry = table; entry->name != NULL; ++entry) {
393		if (entry->flags & XTOPT_MAND &&
394		    !(xflags & (1 << entry->id)))
395			xt_params->exit_err(PARAMETER_PROBLEM,
396				"%s: option \"--%s\" must be specified\n",
397				name, entry->name);
398
399		for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
400			if (entry->id == i)
401				/*
402				 * Avoid conflict with self. Multi-use check
403				 * was done earlier in xtables_option_parse.
404				 */
405				continue;
406			other = xtables_option_lookup(table, i);
407			if (other == NULL)
408				continue;
409			xtables_option_fcheck2(name, entry, other, xflags);
410		}
411	}
412}
413
414/**
415 * Dispatch arguments to the appropriate final_check function, based upon the
416 * extension's choice of API.
417 */
418void xtables_option_tfcall(struct xtables_target *t)
419{
420	if (t->x6_fcheck != NULL) {
421		struct xt_fcheck_call cb;
422
423		cb.ext_name = t->name;
424		cb.data     = t->t->data;
425		cb.xflags   = t->tflags;
426		t->x6_fcheck(&cb);
427	} else if (t->final_check != NULL) {
428		t->final_check(t->tflags);
429	}
430	if (t->x6_options != NULL)
431		xtables_options_fcheck(t->name, t->tflags, t->x6_options);
432}
433
434/**
435 * Dispatch arguments to the appropriate final_check function, based upon the
436 * extension's choice of API.
437 */
438void xtables_option_mfcall(struct xtables_match *m)
439{
440	if (m->x6_fcheck != NULL) {
441		struct xt_fcheck_call cb;
442
443		cb.ext_name = m->name;
444		cb.data     = m->m->data;
445		cb.xflags   = m->mflags;
446		m->x6_fcheck(&cb);
447	} else if (m->final_check != NULL) {
448		m->final_check(m->mflags);
449	}
450	if (m->x6_options != NULL)
451		xtables_options_fcheck(m->name, m->mflags, m->x6_options);
452}
453