blob: 6a119ec7d99c12797f354c062b070335673ed6d7 [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;
89 unsigned int lmin = 0, lmax = UINT32_MAX;
90 unsigned int value;
91
Jan Engelhardtd78254d2011-02-27 17:38:34 +010092 if (cb->entry->min != 0)
93 lmin = cb->entry->min;
94 if (cb->entry->max != 0)
95 lmax = cb->entry->max;
96
Jan Engelhardta93142d2011-02-16 01:22:25 +010097 if (!xtables_strtoui(cb->arg, NULL, &value, lmin, lmax))
98 xt_params->exit_err(PARAMETER_PROBLEM,
99 "%s: bad value for option \"--%s\", "
100 "or out of range (%u-%u).\n",
101 cb->ext_name, entry->name, lmin, lmax);
102
103 if (entry->type == XTTYPE_UINT32) {
104 cb->val.u32 = value;
105 if (entry->flags & XTOPT_PUT)
106 *(uint32_t *)XTOPT_MKPTR(cb) = cb->val.u32;
107 }
108}
109
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100110static void (*const xtopt_subparse[])(struct xt_option_call *) = {
Jan Engelhardta93142d2011-02-16 01:22:25 +0100111 [XTTYPE_UINT32] = xtopt_parse_int,
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100112};
113
114static const size_t xtopt_psize[] = {
Jan Engelhardta93142d2011-02-16 01:22:25 +0100115 [XTTYPE_UINT32] = sizeof(uint32_t),
Jan Engelhardtaa37acc2011-02-07 04:00:50 +0100116};
117
118/**
119 * The master option parsing routine. May be used for the ".x6_parse"
120 * function pointer in extensions if fully automatic parsing is desired.
121 * It may be also called manually from a custom x6_parse function.
122 */
123void xtables_option_parse(struct xt_option_call *cb)
124{
125 const struct xt_option_entry *entry = cb->entry;
126 unsigned int eflag = 1 << cb->entry->id;
127
128 /*
129 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
130 * prevention. Though it turned out that this is too much typing (most
131 * of the options are one-time use only), so now we also have
132 * %XTOPT_MULTI.
133 */
134 if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
135 cb->xflags & eflag)
136 xt_params->exit_err(PARAMETER_PROBLEM,
137 "%s: option \"--%s\" can only be used once.\n",
138 cb->ext_name, cb->entry->name);
139 if (cb->invert && !(entry->flags & XTOPT_INVERT))
140 xt_params->exit_err(PARAMETER_PROBLEM,
141 "%s: option \"--%s\" cannot be inverted.\n",
142 cb->ext_name, entry->name);
143 if (entry->type != XTTYPE_NONE && optarg == NULL)
144 xt_params->exit_err(PARAMETER_PROBLEM,
145 "%s: option \"--%s\" requires an argument.\n",
146 cb->ext_name, entry->name);
147 if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
148 xtopt_subparse[entry->type] != NULL)
149 xtopt_subparse[entry->type](cb);
150 /* Exclusion with other flags tested later in finalize. */
151 cb->xflags |= 1 << entry->id;
152}
153
154/**
155 * Verifies that an extension's option map descriptor is valid, and ought to
156 * be called right after the extension has been loaded, and before option
157 * merging/xfrm.
158 */
159void xtables_option_metavalidate(const char *name,
160 const struct xt_option_entry *entry)
161{
162 for (; entry->name != NULL; ++entry) {
163 if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
164 entry->id >= XT_OPTION_OFFSET_SCALE)
165 xt_params->exit_err(OTHER_PROBLEM,
166 "Extension %s uses invalid ID %u\n",
167 name, entry->id);
168 if (!(entry->flags & XTOPT_PUT))
169 continue;
170 if (entry->type >= ARRAY_SIZE(xtopt_psize))
171 xt_params->exit_err(OTHER_PROBLEM,
172 "%s: entry type of option \"--%s\" cannot be "
173 "combined with XTOPT_PUT\n",
174 name, entry->name);
175 if (xtopt_psize[entry->type] != entry->size)
176 xt_params->exit_err(OTHER_PROBLEM,
177 "%s: option \"--%s\" points to a memory block "
178 "of wrong size (expected %zu, got %zu)\n",
179 name, entry->name,
180 xtopt_psize[entry->type], entry->size);
181 }
182}
183
184/**
185 * Find an option entry by its id.
186 */
187static const struct xt_option_entry *
188xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
189{
190 for (; entry->name != NULL; ++entry)
191 if (entry->id == id)
192 return entry;
193 return NULL;
194}
195
196/**
197 * @c: getopt id (i.e. with offset)
198 * @fw: struct ipt_entry or ip6t_entry
199 *
200 * Dispatch arguments to the appropriate parse function, based upon the
201 * extension's choice of API.
202 */
203void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
204 struct xtables_target *t, void *fw)
205{
206 struct xt_option_call cb;
207
208 if (t->x6_parse == NULL) {
209 if (t->parse != NULL)
210 t->parse(c - t->option_offset, argv, invert,
211 &t->tflags, fw, &t->t);
212 return;
213 }
214
215 c -= t->option_offset;
216 cb.entry = xtables_option_lookup(t->x6_options, c);
217 if (cb.entry == NULL)
218 xtables_error(OTHER_PROBLEM,
219 "Extension does not know id %u\n", c);
220 cb.arg = optarg;
221 cb.invert = invert;
222 cb.ext_name = t->name;
223 cb.data = t->t->data;
224 cb.xflags = t->tflags;
225 t->x6_parse(&cb);
226 t->tflags = cb.xflags;
227}
228
229/**
230 * @c: getopt id (i.e. with offset)
231 * @fw: struct ipt_entry or ip6t_entry
232 *
233 * Dispatch arguments to the appropriate parse function, based upon the
234 * extension's choice of API.
235 */
236void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
237 struct xtables_match *m, void *fw)
238{
239 struct xt_option_call cb;
240
241 if (m->x6_parse == NULL) {
242 if (m->parse != NULL)
243 m->parse(c - m->option_offset, argv, invert,
244 &m->mflags, fw, &m->m);
245 return;
246 }
247
248 c -= m->option_offset;
249 cb.entry = xtables_option_lookup(m->x6_options, c);
250 if (cb.entry == NULL)
251 xtables_error(OTHER_PROBLEM,
252 "Extension does not know id %u\n", c);
253 cb.arg = optarg;
254 cb.invert = invert;
255 cb.ext_name = m->name;
256 cb.data = m->m->data;
257 cb.xflags = m->mflags;
258 m->x6_parse(&cb);
259 m->mflags = cb.xflags;
260}
261
262/**
263 * @name: name of extension
264 * @entry: current option (from all ext's entries) being validated
265 * @xflags: flags the extension has collected
266 * @i: conflicting option (id) to test for
267 */
268static void
269xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
270 const struct xt_option_entry *other,
271 unsigned int xflags)
272{
273 unsigned int ef = 1 << entry->id, of = 1 << other->id;
274
275 if (entry->also & of && !(xflags & of))
276 xt_params->exit_err(PARAMETER_PROBLEM,
277 "%s: option \"--%s\" also requires \"--%s\".\n",
278 name, entry->name, other->name);
279
280 if (!(entry->excl & of))
281 /* Use of entry does not collide with other option, good. */
282 return;
283 if ((xflags & (ef | of)) != (ef | of))
284 /* Conflicting options were not used. */
285 return;
286
287 xt_params->exit_err(PARAMETER_PROBLEM,
288 "%s: option \"--%s\" cannot be used together with \"--%s\".\n",
289 name, entry->name, other->name);
290}
291
292/**
293 * @name: name of extension
294 * @xflags: accumulated flags
295 * @entry: extension's option table
296 *
297 * Check that all option constraints have been met. This effectively replaces
298 * ->final_check of the older API.
299 */
300void xtables_options_fcheck(const char *name, unsigned int xflags,
301 const struct xt_option_entry *table)
302{
303 const struct xt_option_entry *entry, *other;
304 unsigned int i;
305
306 for (entry = table; entry->name != NULL; ++entry) {
307 if (entry->flags & XTOPT_MAND &&
308 !(xflags & (1 << entry->id)))
309 xt_params->exit_err(PARAMETER_PROBLEM,
310 "%s: option \"--%s\" must be specified\n",
311 name, entry->name);
312
313 for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
314 if (entry->id == i)
315 /*
316 * Avoid conflict with self. Multi-use check
317 * was done earlier in xtables_option_parse.
318 */
319 continue;
320 other = xtables_option_lookup(table, i);
321 if (other == NULL)
322 continue;
323 xtables_option_fcheck2(name, entry, other, xflags);
324 }
325 }
326}
Jan Engelhardt3af739b2011-02-10 16:57:37 +0100327
328/**
329 * Dispatch arguments to the appropriate final_check function, based upon the
330 * extension's choice of API.
331 */
332void xtables_option_tfcall(struct xtables_target *t)
333{
334 if (t->x6_fcheck != NULL) {
335 struct xt_fcheck_call cb;
336
337 cb.ext_name = t->name;
338 cb.data = t->t->data;
339 cb.xflags = t->tflags;
340 t->x6_fcheck(&cb);
341 } else if (t->final_check != NULL) {
342 t->final_check(t->tflags);
343 }
344 if (t->x6_options != NULL)
345 xtables_options_fcheck(t->name, t->tflags, t->x6_options);
346}
347
348/**
349 * Dispatch arguments to the appropriate final_check function, based upon the
350 * extension's choice of API.
351 */
352void xtables_option_mfcall(struct xtables_match *m)
353{
354 if (m->x6_fcheck != NULL) {
355 struct xt_fcheck_call cb;
356
357 cb.ext_name = m->name;
358 cb.data = m->m->data;
359 cb.xflags = m->mflags;
360 m->x6_fcheck(&cb);
361 } else if (m->final_check != NULL) {
362 m->final_check(m->mflags);
363 }
364 if (m->x6_options != NULL)
365 xtables_options_fcheck(m->name, m->mflags, m->x6_options);
366}