blob: 3286aa109e98741a4d14b3877825eb6b2c94860f [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
83static void (*const xtopt_subparse[])(struct xt_option_call *) = {
84 [XTTYPE_NONE] = NULL,
85};
86
87static const size_t xtopt_psize[] = {
88 [XTTYPE_NONE] = 0,
89};
90
91/**
92 * The master option parsing routine. May be used for the ".x6_parse"
93 * function pointer in extensions if fully automatic parsing is desired.
94 * It may be also called manually from a custom x6_parse function.
95 */
96void xtables_option_parse(struct xt_option_call *cb)
97{
98 const struct xt_option_entry *entry = cb->entry;
99 unsigned int eflag = 1 << cb->entry->id;
100
101 /*
102 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
103 * prevention. Though it turned out that this is too much typing (most
104 * of the options are one-time use only), so now we also have
105 * %XTOPT_MULTI.
106 */
107 if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
108 cb->xflags & eflag)
109 xt_params->exit_err(PARAMETER_PROBLEM,
110 "%s: option \"--%s\" can only be used once.\n",
111 cb->ext_name, cb->entry->name);
112 if (cb->invert && !(entry->flags & XTOPT_INVERT))
113 xt_params->exit_err(PARAMETER_PROBLEM,
114 "%s: option \"--%s\" cannot be inverted.\n",
115 cb->ext_name, entry->name);
116 if (entry->type != XTTYPE_NONE && optarg == NULL)
117 xt_params->exit_err(PARAMETER_PROBLEM,
118 "%s: option \"--%s\" requires an argument.\n",
119 cb->ext_name, entry->name);
120 if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
121 xtopt_subparse[entry->type] != NULL)
122 xtopt_subparse[entry->type](cb);
123 /* Exclusion with other flags tested later in finalize. */
124 cb->xflags |= 1 << entry->id;
125}
126
127/**
128 * Verifies that an extension's option map descriptor is valid, and ought to
129 * be called right after the extension has been loaded, and before option
130 * merging/xfrm.
131 */
132void xtables_option_metavalidate(const char *name,
133 const struct xt_option_entry *entry)
134{
135 for (; entry->name != NULL; ++entry) {
136 if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
137 entry->id >= XT_OPTION_OFFSET_SCALE)
138 xt_params->exit_err(OTHER_PROBLEM,
139 "Extension %s uses invalid ID %u\n",
140 name, entry->id);
141 if (!(entry->flags & XTOPT_PUT))
142 continue;
143 if (entry->type >= ARRAY_SIZE(xtopt_psize))
144 xt_params->exit_err(OTHER_PROBLEM,
145 "%s: entry type of option \"--%s\" cannot be "
146 "combined with XTOPT_PUT\n",
147 name, entry->name);
148 if (xtopt_psize[entry->type] != entry->size)
149 xt_params->exit_err(OTHER_PROBLEM,
150 "%s: option \"--%s\" points to a memory block "
151 "of wrong size (expected %zu, got %zu)\n",
152 name, entry->name,
153 xtopt_psize[entry->type], entry->size);
154 }
155}
156
157/**
158 * Find an option entry by its id.
159 */
160static const struct xt_option_entry *
161xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
162{
163 for (; entry->name != NULL; ++entry)
164 if (entry->id == id)
165 return entry;
166 return NULL;
167}
168
169/**
170 * @c: getopt id (i.e. with offset)
171 * @fw: struct ipt_entry or ip6t_entry
172 *
173 * Dispatch arguments to the appropriate parse function, based upon the
174 * extension's choice of API.
175 */
176void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
177 struct xtables_target *t, void *fw)
178{
179 struct xt_option_call cb;
180
181 if (t->x6_parse == NULL) {
182 if (t->parse != NULL)
183 t->parse(c - t->option_offset, argv, invert,
184 &t->tflags, fw, &t->t);
185 return;
186 }
187
188 c -= t->option_offset;
189 cb.entry = xtables_option_lookup(t->x6_options, c);
190 if (cb.entry == NULL)
191 xtables_error(OTHER_PROBLEM,
192 "Extension does not know id %u\n", c);
193 cb.arg = optarg;
194 cb.invert = invert;
195 cb.ext_name = t->name;
196 cb.data = t->t->data;
197 cb.xflags = t->tflags;
198 t->x6_parse(&cb);
199 t->tflags = cb.xflags;
200}
201
202/**
203 * @c: getopt id (i.e. with offset)
204 * @fw: struct ipt_entry or ip6t_entry
205 *
206 * Dispatch arguments to the appropriate parse function, based upon the
207 * extension's choice of API.
208 */
209void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
210 struct xtables_match *m, void *fw)
211{
212 struct xt_option_call cb;
213
214 if (m->x6_parse == NULL) {
215 if (m->parse != NULL)
216 m->parse(c - m->option_offset, argv, invert,
217 &m->mflags, fw, &m->m);
218 return;
219 }
220
221 c -= m->option_offset;
222 cb.entry = xtables_option_lookup(m->x6_options, c);
223 if (cb.entry == NULL)
224 xtables_error(OTHER_PROBLEM,
225 "Extension does not know id %u\n", c);
226 cb.arg = optarg;
227 cb.invert = invert;
228 cb.ext_name = m->name;
229 cb.data = m->m->data;
230 cb.xflags = m->mflags;
231 m->x6_parse(&cb);
232 m->mflags = cb.xflags;
233}
234
235/**
236 * @name: name of extension
237 * @entry: current option (from all ext's entries) being validated
238 * @xflags: flags the extension has collected
239 * @i: conflicting option (id) to test for
240 */
241static void
242xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
243 const struct xt_option_entry *other,
244 unsigned int xflags)
245{
246 unsigned int ef = 1 << entry->id, of = 1 << other->id;
247
248 if (entry->also & of && !(xflags & of))
249 xt_params->exit_err(PARAMETER_PROBLEM,
250 "%s: option \"--%s\" also requires \"--%s\".\n",
251 name, entry->name, other->name);
252
253 if (!(entry->excl & of))
254 /* Use of entry does not collide with other option, good. */
255 return;
256 if ((xflags & (ef | of)) != (ef | of))
257 /* Conflicting options were not used. */
258 return;
259
260 xt_params->exit_err(PARAMETER_PROBLEM,
261 "%s: option \"--%s\" cannot be used together with \"--%s\".\n",
262 name, entry->name, other->name);
263}
264
265/**
266 * @name: name of extension
267 * @xflags: accumulated flags
268 * @entry: extension's option table
269 *
270 * Check that all option constraints have been met. This effectively replaces
271 * ->final_check of the older API.
272 */
273void xtables_options_fcheck(const char *name, unsigned int xflags,
274 const struct xt_option_entry *table)
275{
276 const struct xt_option_entry *entry, *other;
277 unsigned int i;
278
279 for (entry = table; entry->name != NULL; ++entry) {
280 if (entry->flags & XTOPT_MAND &&
281 !(xflags & (1 << entry->id)))
282 xt_params->exit_err(PARAMETER_PROBLEM,
283 "%s: option \"--%s\" must be specified\n",
284 name, entry->name);
285
286 for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
287 if (entry->id == i)
288 /*
289 * Avoid conflict with self. Multi-use check
290 * was done earlier in xtables_option_parse.
291 */
292 continue;
293 other = xtables_option_lookup(table, i);
294 if (other == NULL)
295 continue;
296 xtables_option_fcheck2(name, entry, other, xflags);
297 }
298 }
299}