blob: 42063144d49bdf1854739af7722691c1a7536a3f [file] [log] [blame]
Jan Engelhardtaa37acc2011-02-07 04:00:50 +01001/*
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
Jan Engelhardta93142d2011-02-16 01:22:25 +010083/**
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;
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +010089 unsigned long long lmin = 0, lmax = UINT32_MAX;
Jan Engelhardta93142d2011-02-16 01:22:25 +010090 unsigned int value;
91
Jan Engelhardtdfe99f12011-02-27 19:03:28 +010092 if (entry->type == XTTYPE_UINT8)
93 lmax = UINT8_MAX;
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +010094 else if (entry->type == XTTYPE_UINT64)
95 lmax = UINT64_MAX;
Jan Engelhardtd78254d2011-02-27 17:38:34 +010096 if (cb->entry->min != 0)
97 lmin = cb->entry->min;
98 if (cb->entry->max != 0)
99 lmax = cb->entry->max;
100
Jan Engelhardta93142d2011-02-16 01:22:25 +0100101 if (!xtables_strtoui(cb->arg, NULL, &value, lmin, lmax))
102 xt_params->exit_err(PARAMETER_PROBLEM,
103 "%s: bad value for option \"--%s\", "
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +0100104 "or out of range (%llu-%llu).\n",
Jan Engelhardta93142d2011-02-16 01:22:25 +0100105 cb->ext_name, entry->name, lmin, lmax);
106
Jan Engelhardtdfe99f12011-02-27 19:03:28 +0100107 if (entry->type == XTTYPE_UINT8) {
108 cb->val.u8 = value;
109 if (entry->flags & XTOPT_PUT)
110 *(uint8_t *)XTOPT_MKPTR(cb) = cb->val.u8;
111 } else if (entry->type == XTTYPE_UINT32) {
Jan Engelhardta93142d2011-02-16 01:22:25 +0100112 cb->val.u32 = value;
113 if (entry->flags & XTOPT_PUT)
114 *(uint32_t *)XTOPT_MKPTR(cb) = cb->val.u32;
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +0100115 } else if (entry->type == XTTYPE_UINT64) {
116 cb->val.u64 = value;
117 if (entry->flags & XTOPT_PUT)
118 *(uint64_t *)XTOPT_MKPTR(cb) = cb->val.u64;
Jan Engelhardta93142d2011-02-16 01:22:25 +0100119 }
120}
121
Jan Engelhardt04bb9882011-02-27 23:41:10 +0100122/**
123 * Multiple integer parse routine.
124 *
125 * This function is capable of parsing any number of fields. Only the first
126 * two values from the string will be put into @cb however (and as such,
127 * @cb->val.uXX_range is just that large) to cater for the few extensions that
128 * do not have a range[2] field, but {min, max}, and which cannot use
129 * XTOPT_POINTER.
130 */
131static void xtopt_parse_mint(struct xt_option_call *cb)
132{
133 const struct xt_option_entry *entry = cb->entry;
134 const char *arg = cb->arg;
135 uint32_t *put = XTOPT_MKPTR(cb);
136 unsigned int maxiter, value;
137 char *end = "";
138 char sep = ':';
139
140 maxiter = entry->size / sizeof(uint32_t);
141 if (maxiter == 0)
142 maxiter = 2; /* ARRAY_SIZE(cb->val.uXX_range) */
143 if (entry->size % sizeof(uint32_t) != 0)
144 xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
145 "not have proper size\n", __func__);
146
147 cb->nvals = 0;
148 for (arg = cb->arg; ; arg = end + 1) {
149 if (cb->nvals == maxiter)
150 xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
151 "components for option \"--%s\" (max: %u)\n",
152 cb->ext_name, entry->name, maxiter);
153 if (!xtables_strtoui(arg, &end, &value, 0, UINT32_MAX))
154 xt_params->exit_err(PARAMETER_PROBLEM,
155 "%s: bad value for option \"--%s\", "
156 "or out of range (0-%u).\n",
157 cb->ext_name, entry->name, UINT32_MAX);
158 if (*end != '\0' && *end != sep)
159 xt_params->exit_err(PARAMETER_PROBLEM,
160 "%s: Argument to \"--%s\" has unexpected "
161 "characters.\n", cb->ext_name, entry->name);
162 ++cb->nvals;
163 if (cb->nvals < ARRAY_SIZE(cb->val.u32_range))
164 cb->val.u32_range[cb->nvals] = value;
165 if (entry->flags & XTOPT_PUT)
166 *put++ = value;
167 if (*end == '\0')
168 break;
169 }
170}
171
Jan Engelhardt4a0a1762011-02-15 22:09:21 +0100172static void xtopt_parse_string(struct xt_option_call *cb)
173{
174 const struct xt_option_entry *entry = cb->entry;
175 size_t z = strlen(cb->arg);
176 char *p;
177
178 if (entry->min != 0 && z < entry->min)
179 xt_params->exit_err(PARAMETER_PROBLEM,
180 "Argument must have a minimum length of "
181 "%u characters\n", entry->min);
182 if (entry->max != 0 && z > entry->max)
183 xt_params->exit_err(PARAMETER_PROBLEM,
184 "Argument must have a maximum length of "
185 "%u characters\n", entry->max);
186 if (!(entry->flags & XTOPT_PUT))
187 return;
188 if (z >= entry->size)
189 z = entry->size - 1;
190 p = XTOPT_MKPTR(cb);
191 strncpy(p, cb->arg, z);
192 p[z] = '\0';
193}
194
Jan Engelhardtd25e2172011-03-06 14:57:44 +0100195/**
196 * Validate the input for being conformant to "mark[/mask]".
197 */
198static void xtopt_parse_markmask(struct xt_option_call *cb)
199{
200 unsigned int mark = 0, mask = ~0U;
201 char *end;
202
203 if (!xtables_strtoui(cb->arg, &end, &mark, 0, UINT32_MAX))
204 xt_params->exit_err(PARAMETER_PROBLEM,
205 "%s: bad mark value for option \"--%s\", "
206 "or out of range.\n",
207 cb->ext_name, cb->entry->name);
208 if (*end == '/' &&
209 !xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
210 xt_params->exit_err(PARAMETER_PROBLEM,
211 "%s: bad mask value for option \"--%s\", "
212 "or out of range.\n",
213 cb->ext_name, cb->entry->name);
214 if (*end != '\0')
215 xt_params->exit_err(PARAMETER_PROBLEM,
216 "%s: trailing garbage after value "
217 "for option \"--%s\".\n",
218 cb->ext_name, cb->entry->name);
219 cb->val.mark = mark;
220 cb->val.mask = mask;
221}
222
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100223static void (*const xtopt_subparse[])(struct xt_option_call *) = {
Jan Engelhardtdfe99f12011-02-27 19:03:28 +0100224 [XTTYPE_UINT8] = xtopt_parse_int,
Jan Engelhardta93142d2011-02-16 01:22:25 +0100225 [XTTYPE_UINT32] = xtopt_parse_int,
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +0100226 [XTTYPE_UINT64] = xtopt_parse_int,
Jan Engelhardt04bb9882011-02-27 23:41:10 +0100227 [XTTYPE_UINT32RC] = xtopt_parse_mint,
Jan Engelhardt4a0a1762011-02-15 22:09:21 +0100228 [XTTYPE_STRING] = xtopt_parse_string,
Jan Engelhardtd25e2172011-03-06 14:57:44 +0100229 [XTTYPE_MARKMASK32] = xtopt_parse_markmask,
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100230};
231
232static const size_t xtopt_psize[] = {
Jan Engelhardtdfe99f12011-02-27 19:03:28 +0100233 [XTTYPE_UINT8] = sizeof(uint8_t),
Jan Engelhardta93142d2011-02-16 01:22:25 +0100234 [XTTYPE_UINT32] = sizeof(uint32_t),
Jan Engelhardt8b5bdea2011-03-06 16:56:53 +0100235 [XTTYPE_UINT64] = sizeof(uint64_t),
Jan Engelhardt04bb9882011-02-27 23:41:10 +0100236 [XTTYPE_UINT32RC] = sizeof(uint32_t[2]),
Jan Engelhardt4a0a1762011-02-15 22:09:21 +0100237 [XTTYPE_STRING] = -1,
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100238};
239
240/**
241 * The master option parsing routine. May be used for the ".x6_parse"
242 * function pointer in extensions if fully automatic parsing is desired.
243 * It may be also called manually from a custom x6_parse function.
244 */
245void xtables_option_parse(struct xt_option_call *cb)
246{
247 const struct xt_option_entry *entry = cb->entry;
248 unsigned int eflag = 1 << cb->entry->id;
249
250 /*
251 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
252 * prevention. Though it turned out that this is too much typing (most
253 * of the options are one-time use only), so now we also have
254 * %XTOPT_MULTI.
255 */
256 if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
257 cb->xflags & eflag)
258 xt_params->exit_err(PARAMETER_PROBLEM,
259 "%s: option \"--%s\" can only be used once.\n",
260 cb->ext_name, cb->entry->name);
261 if (cb->invert && !(entry->flags & XTOPT_INVERT))
262 xt_params->exit_err(PARAMETER_PROBLEM,
263 "%s: option \"--%s\" cannot be inverted.\n",
264 cb->ext_name, entry->name);
265 if (entry->type != XTTYPE_NONE && optarg == NULL)
266 xt_params->exit_err(PARAMETER_PROBLEM,
267 "%s: option \"--%s\" requires an argument.\n",
268 cb->ext_name, entry->name);
269 if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
270 xtopt_subparse[entry->type] != NULL)
271 xtopt_subparse[entry->type](cb);
272 /* Exclusion with other flags tested later in finalize. */
273 cb->xflags |= 1 << entry->id;
274}
275
276/**
277 * Verifies that an extension's option map descriptor is valid, and ought to
278 * be called right after the extension has been loaded, and before option
279 * merging/xfrm.
280 */
281void xtables_option_metavalidate(const char *name,
282 const struct xt_option_entry *entry)
283{
284 for (; entry->name != NULL; ++entry) {
285 if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
286 entry->id >= XT_OPTION_OFFSET_SCALE)
287 xt_params->exit_err(OTHER_PROBLEM,
288 "Extension %s uses invalid ID %u\n",
289 name, entry->id);
290 if (!(entry->flags & XTOPT_PUT))
291 continue;
292 if (entry->type >= ARRAY_SIZE(xtopt_psize))
293 xt_params->exit_err(OTHER_PROBLEM,
294 "%s: entry type of option \"--%s\" cannot be "
295 "combined with XTOPT_PUT\n",
296 name, entry->name);
Jan Engelhardt04bb9882011-02-27 23:41:10 +0100297 if (xtopt_psize[entry->type] != -1 &&
298 xtopt_psize[entry->type] != entry->size)
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100299 xt_params->exit_err(OTHER_PROBLEM,
300 "%s: option \"--%s\" points to a memory block "
301 "of wrong size (expected %zu, got %zu)\n",
302 name, entry->name,
303 xtopt_psize[entry->type], entry->size);
304 }
305}
306
307/**
308 * Find an option entry by its id.
309 */
310static const struct xt_option_entry *
311xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
312{
313 for (; entry->name != NULL; ++entry)
314 if (entry->id == id)
315 return entry;
316 return NULL;
317}
318
319/**
320 * @c: getopt id (i.e. with offset)
321 * @fw: struct ipt_entry or ip6t_entry
322 *
323 * Dispatch arguments to the appropriate parse function, based upon the
324 * extension's choice of API.
325 */
326void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
327 struct xtables_target *t, void *fw)
328{
329 struct xt_option_call cb;
330
331 if (t->x6_parse == NULL) {
332 if (t->parse != NULL)
333 t->parse(c - t->option_offset, argv, invert,
334 &t->tflags, fw, &t->t);
335 return;
336 }
337
338 c -= t->option_offset;
339 cb.entry = xtables_option_lookup(t->x6_options, c);
340 if (cb.entry == NULL)
341 xtables_error(OTHER_PROBLEM,
342 "Extension does not know id %u\n", c);
343 cb.arg = optarg;
344 cb.invert = invert;
345 cb.ext_name = t->name;
346 cb.data = t->t->data;
347 cb.xflags = t->tflags;
348 t->x6_parse(&cb);
349 t->tflags = cb.xflags;
350}
351
352/**
353 * @c: getopt id (i.e. with offset)
354 * @fw: struct ipt_entry or ip6t_entry
355 *
356 * Dispatch arguments to the appropriate parse function, based upon the
357 * extension's choice of API.
358 */
359void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
360 struct xtables_match *m, void *fw)
361{
362 struct xt_option_call cb;
363
364 if (m->x6_parse == NULL) {
365 if (m->parse != NULL)
366 m->parse(c - m->option_offset, argv, invert,
367 &m->mflags, fw, &m->m);
368 return;
369 }
370
371 c -= m->option_offset;
372 cb.entry = xtables_option_lookup(m->x6_options, c);
373 if (cb.entry == NULL)
374 xtables_error(OTHER_PROBLEM,
375 "Extension does not know id %u\n", c);
376 cb.arg = optarg;
377 cb.invert = invert;
378 cb.ext_name = m->name;
379 cb.data = m->m->data;
380 cb.xflags = m->mflags;
381 m->x6_parse(&cb);
382 m->mflags = cb.xflags;
383}
384
385/**
386 * @name: name of extension
387 * @entry: current option (from all ext's entries) being validated
388 * @xflags: flags the extension has collected
389 * @i: conflicting option (id) to test for
390 */
391static void
392xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
393 const struct xt_option_entry *other,
394 unsigned int xflags)
395{
396 unsigned int ef = 1 << entry->id, of = 1 << other->id;
397
398 if (entry->also & of && !(xflags & of))
399 xt_params->exit_err(PARAMETER_PROBLEM,
400 "%s: option \"--%s\" also requires \"--%s\".\n",
401 name, entry->name, other->name);
402
403 if (!(entry->excl & of))
404 /* Use of entry does not collide with other option, good. */
405 return;
406 if ((xflags & (ef | of)) != (ef | of))
407 /* Conflicting options were not used. */
408 return;
409
410 xt_params->exit_err(PARAMETER_PROBLEM,
411 "%s: option \"--%s\" cannot be used together with \"--%s\".\n",
412 name, entry->name, other->name);
413}
414
415/**
416 * @name: name of extension
417 * @xflags: accumulated flags
418 * @entry: extension's option table
419 *
420 * Check that all option constraints have been met. This effectively replaces
421 * ->final_check of the older API.
422 */
423void xtables_options_fcheck(const char *name, unsigned int xflags,
424 const struct xt_option_entry *table)
425{
426 const struct xt_option_entry *entry, *other;
427 unsigned int i;
428
429 for (entry = table; entry->name != NULL; ++entry) {
430 if (entry->flags & XTOPT_MAND &&
431 !(xflags & (1 << entry->id)))
432 xt_params->exit_err(PARAMETER_PROBLEM,
433 "%s: option \"--%s\" must be specified\n",
434 name, entry->name);
435
436 for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
437 if (entry->id == i)
438 /*
439 * Avoid conflict with self. Multi-use check
440 * was done earlier in xtables_option_parse.
441 */
442 continue;
443 other = xtables_option_lookup(table, i);
444 if (other == NULL)
445 continue;
446 xtables_option_fcheck2(name, entry, other, xflags);
447 }
448 }
449}
Jan Engelhardt3af739b2011-02-10 16:57:37 +0100450
451/**
452 * Dispatch arguments to the appropriate final_check function, based upon the
453 * extension's choice of API.
454 */
455void xtables_option_tfcall(struct xtables_target *t)
456{
457 if (t->x6_fcheck != NULL) {
458 struct xt_fcheck_call cb;
459
460 cb.ext_name = t->name;
461 cb.data = t->t->data;
462 cb.xflags = t->tflags;
463 t->x6_fcheck(&cb);
464 } else if (t->final_check != NULL) {
465 t->final_check(t->tflags);
466 }
467 if (t->x6_options != NULL)
468 xtables_options_fcheck(t->name, t->tflags, t->x6_options);
469}
470
471/**
472 * Dispatch arguments to the appropriate final_check function, based upon the
473 * extension's choice of API.
474 */
475void xtables_option_mfcall(struct xtables_match *m)
476{
477 if (m->x6_fcheck != NULL) {
478 struct xt_fcheck_call cb;
479
480 cb.ext_name = m->name;
481 cb.data = m->m->data;
482 cb.xflags = m->mflags;
483 m->x6_fcheck(&cb);
484 } else if (m->final_check != NULL) {
485 m->final_check(m->mflags);
486 }
487 if (m->x6_options != NULL)
488 xtables_options_fcheck(m->name, m->mflags, m->x6_options);
489}