Line | Count | Source |
1 | | /* |
2 | | * Functions used to parse typed argument lists |
3 | | * |
4 | | * Copyright 2012 Willy Tarreau <w@1wt.eu> |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU General Public License |
8 | | * as published by the Free Software Foundation; either version |
9 | | * 2 of the License, or (at your option) any later version. |
10 | | * |
11 | | */ |
12 | | |
13 | | #include <sys/types.h> |
14 | | #include <sys/socket.h> |
15 | | #include <arpa/inet.h> |
16 | | |
17 | | #include <haproxy/arg.h> |
18 | | #include <haproxy/chunk.h> |
19 | | #include <haproxy/global.h> |
20 | | #include <haproxy/regex.h> |
21 | | #include <haproxy/tools.h> |
22 | | |
23 | | const char *arg_type_names[ARGT_NBTYPES] = { |
24 | | [ARGT_STOP] = "end of arguments", |
25 | | [ARGT_SINT] = "integer", |
26 | | [ARGT_STR] = "string", |
27 | | [ARGT_ID] = "identifier", |
28 | | [ARGT_IPV4] = "IPv4 address", |
29 | | [ARGT_MSK4] = "IPv4 mask", |
30 | | [ARGT_IPV6] = "IPv6 address", |
31 | | [ARGT_MSK6] = "IPv6 mask", |
32 | | [ARGT_TIME] = "delay", |
33 | | [ARGT_SIZE] = "size", |
34 | | [ARGT_FE] = "frontend", |
35 | | [ARGT_BE] = "backend", |
36 | | [ARGT_TAB] = "table", |
37 | | [ARGT_SRV] = "server", |
38 | | [ARGT_USR] = "user list", |
39 | | [ARGT_MAP] = "map", |
40 | | [ARGT_REG] = "regex", |
41 | | [ARGT_VAR] = "variable", |
42 | | [ARGT_PBUF_FNUM] = "Protocol buffers field number", |
43 | | /* Unassigned types must never happen. Better crash during parsing if they do. */ |
44 | | }; |
45 | | |
46 | | /* This dummy arg list may be used by default when no arg is found, it helps |
47 | | * parsers by removing pointer checks. |
48 | | */ |
49 | | struct arg empty_arg_list[ARGM_NBARGS] = { }; |
50 | | |
51 | | /* This function clones a struct arg_list template into a new one which is |
52 | | * returned. |
53 | | */ |
54 | | struct arg_list *arg_list_clone(const struct arg_list *orig) |
55 | 0 | { |
56 | 0 | struct arg_list *new; |
57 | |
|
58 | 0 | if ((new = calloc(1, sizeof(*new))) != NULL) { |
59 | | /* ->list will be set by the caller when inserting the element. |
60 | | * ->arg and ->arg_pos will be set by the caller. |
61 | | */ |
62 | 0 | new->ctx = orig->ctx; |
63 | 0 | new->kw = orig->kw; |
64 | 0 | new->conv = orig->conv; |
65 | 0 | new->file = orig->file; |
66 | 0 | new->line = orig->line; |
67 | 0 | } |
68 | 0 | return new; |
69 | 0 | } |
70 | | |
71 | | /* This function clones a struct <arg_list> template into a new one which is |
72 | | * set to point to arg <arg> at pos <pos>, and which is returned if the caller |
73 | | * wants to apply further changes. |
74 | | */ |
75 | | struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos) |
76 | 0 | { |
77 | 0 | struct arg_list *new; |
78 | |
|
79 | 0 | new = arg_list_clone(orig); |
80 | 0 | if (new) { |
81 | 0 | new->arg = arg; |
82 | 0 | new->arg_pos = pos; |
83 | 0 | LIST_APPEND(&orig->list, &new->list); |
84 | 0 | } |
85 | 0 | return new; |
86 | 0 | } |
87 | | |
88 | | /* This function builds an argument list from a config line, and stops at the |
89 | | * first non-matching character, which is pointed to in <end_ptr>. A valid arg |
90 | | * list starts with an opening parenthesis '(', contains a number of comma- |
91 | | * delimited words, and ends with the closing parenthesis ')'. An empty list |
92 | | * (with or without the parenthesis) will lead to a valid empty argument if the |
93 | | * keyword has a mandatory one. The function returns the number of arguments |
94 | | * emitted, or <0 in case of any error. Everything needed it automatically |
95 | | * allocated. A pointer to an error message might be returned in err_msg if not |
96 | | * NULL, in which case it would be allocated and the caller will have to check |
97 | | * it and free it. The output arg list is returned in argp which must be valid. |
98 | | * The returned array is always terminated by an arg of type ARGT_STOP (0), |
99 | | * unless the mask indicates that no argument is supported. Unresolved arguments |
100 | | * are appended to arg list <al>, which also serves as a template to create new |
101 | | * entries. <al> may be NULL if unresolved arguments are not allowed. The mask |
102 | | * is composed of a number of mandatory arguments in its lower ARGM_BITS bits, |
103 | | * and a concatenation of each argument type in each subsequent ARGT_BITS-bit |
104 | | * sblock. If <err_msg> is not NULL, it must point to a freeable or NULL |
105 | | * pointer. The caller is expected to restart the parsing from the new pointer |
106 | | * set in <end_ptr>, which is the first character considered as not being part |
107 | | * of the arg list. The input string ends on the first between <len> characters |
108 | | * (when len is positive) or the first NUL character. Placing -1 in <len> will |
109 | | * make it virtually unbounded (~2GB long strings). |
110 | | */ |
111 | | int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp, |
112 | | char **err_msg, const char **end_ptr, int *err_arg, |
113 | | struct arg_list *al) |
114 | 13.0k | { |
115 | 13.0k | int nbarg; |
116 | 13.0k | int pos; |
117 | 13.0k | struct arg *arg; |
118 | 13.0k | const char *beg; |
119 | 13.0k | const char *ptr_err = NULL; |
120 | 13.0k | int min_arg; |
121 | 13.0k | int empty; |
122 | 13.0k | struct arg_list *new_al = al; |
123 | | |
124 | 13.0k | *argp = NULL; |
125 | | |
126 | 13.0k | empty = 0; |
127 | 13.0k | if (!len || *in != '(') { |
128 | | /* it's already not for us, stop here */ |
129 | 895 | empty = 1; |
130 | 895 | len = 0; |
131 | 12.2k | } else { |
132 | | /* skip opening parenthesis */ |
133 | 12.2k | len--; |
134 | 12.2k | in++; |
135 | 12.2k | } |
136 | | |
137 | 13.0k | min_arg = mask & ARGM_MASK; |
138 | 13.0k | mask >>= ARGM_BITS; |
139 | | |
140 | 13.0k | pos = 0; |
141 | | /* find between 0 and NBARGS the max number of args supported by the mask */ |
142 | 27.5k | for (nbarg = 0; nbarg < ARGM_NBARGS && ((mask >> (nbarg * ARGT_BITS)) & ARGT_MASK); nbarg++); |
143 | | |
144 | 13.0k | if (!nbarg) |
145 | 201 | goto end_parse; |
146 | | |
147 | | /* Note: an empty input string contains an empty argument if this argument |
148 | | * is marked mandatory. Otherwise we can ignore it. |
149 | | */ |
150 | 12.8k | if (empty && !min_arg) |
151 | 0 | goto end_parse; |
152 | | |
153 | 12.8k | arg = *argp = calloc(nbarg + 1, sizeof(**argp)); |
154 | | |
155 | 12.8k | if (!arg) |
156 | 0 | goto alloc_err; |
157 | | |
158 | | /* Note: empty arguments after a comma always exist. */ |
159 | 14.3k | while (pos < nbarg) { |
160 | 14.3k | unsigned int uint; |
161 | 14.3k | int squote = 0, dquote = 0; |
162 | 14.3k | char *out; |
163 | | |
164 | 14.3k | chunk_reset(&trash); |
165 | 14.3k | out = trash.area; |
166 | | |
167 | 19.1M | while (len && *in && trash.data < trash.size - 1) { |
168 | 19.1M | if (*in == '"' && !squote) { /* double quote outside single quotes */ |
169 | 965 | if (dquote) |
170 | 467 | dquote = 0; |
171 | 498 | else |
172 | 498 | dquote = 1; |
173 | 965 | in++; len--; |
174 | 965 | continue; |
175 | 965 | } |
176 | 19.1M | else if (*in == '\'' && !dquote) { /* single quote outside double quotes */ |
177 | 1.92k | if (squote) |
178 | 944 | squote = 0; |
179 | 980 | else |
180 | 980 | squote = 1; |
181 | 1.92k | in++; len--; |
182 | 1.92k | continue; |
183 | 1.92k | } |
184 | 19.1M | else if (*in == '\\' && !squote && len != 1) { |
185 | | /* '\', ', ' ', '"' support being escaped by '\' */ |
186 | 3.73k | if (in[1] == 0) |
187 | 14 | goto unquote_err; |
188 | | |
189 | 3.71k | if (in[1] == '\\' || in[1] == ' ' || in[1] == '"' || in[1] == '\'') { |
190 | 2.24k | in++; len--; |
191 | 2.24k | *out++ = *in; |
192 | 2.24k | } |
193 | 1.46k | else if (in[1] == 'r') { |
194 | 201 | in++; len--; |
195 | 201 | *out++ = '\r'; |
196 | 201 | } |
197 | 1.26k | else if (in[1] == 'n') { |
198 | 368 | in++; len--; |
199 | 368 | *out++ = '\n'; |
200 | 368 | } |
201 | 900 | else if (in[1] == 't') { |
202 | 205 | in++; len--; |
203 | 205 | *out++ = '\t'; |
204 | 205 | } |
205 | 695 | else { |
206 | | /* just a lone '\' */ |
207 | 695 | *out++ = *in; |
208 | 695 | } |
209 | 3.71k | in++; len--; |
210 | 3.71k | } |
211 | 19.1M | else { |
212 | 19.1M | if (!squote && !dquote && (*in == ',' || *in == ')')) { |
213 | | /* end of argument */ |
214 | 13.4k | break; |
215 | 13.4k | } |
216 | | /* verbatim copy */ |
217 | 19.1M | *out++ = *in++; |
218 | 19.1M | len--; |
219 | 19.1M | } |
220 | 19.1M | trash.data = out - trash.area; |
221 | 19.1M | } |
222 | | |
223 | 14.3k | if (len && *in && *in != ',' && *in != ')') |
224 | 51 | goto buffer_err; |
225 | | |
226 | 14.2k | trash.area[trash.data] = 0; |
227 | | |
228 | 14.2k | arg->type = (mask >> (pos * ARGT_BITS)) & ARGT_MASK; |
229 | | |
230 | 14.2k | switch (arg->type) { |
231 | 0 | case ARGT_SINT: |
232 | 0 | if (!trash.data) // empty number |
233 | 0 | goto empty_err; |
234 | 0 | beg = trash.area; |
235 | 0 | arg->data.sint = read_int64(&beg, trash.area + trash.data); |
236 | 0 | if (beg < trash.area + trash.data) |
237 | 0 | goto parse_err; |
238 | 0 | arg->type = ARGT_SINT; |
239 | 0 | break; |
240 | | |
241 | 0 | case ARGT_ID: |
242 | 0 | case ARGT_FE: |
243 | 0 | case ARGT_BE: |
244 | 0 | case ARGT_TAB: |
245 | 0 | case ARGT_SRV: |
246 | 0 | case ARGT_USR: |
247 | 0 | case ARGT_REG: |
248 | | /* These argument types need to be stored as strings during |
249 | | * parsing then resolved later. |
250 | | */ |
251 | 0 | if (!al) |
252 | 0 | goto resolve_err; |
253 | 0 | arg->unresolved = 1; |
254 | 0 | new_al = arg_list_add(al, arg, pos); |
255 | 0 | __fallthrough; |
256 | |
|
257 | 14.2k | case ARGT_STR: |
258 | | /* all types that must be resolved are stored as strings |
259 | | * during the parsing. The caller must at one point resolve |
260 | | * them and free the string. |
261 | | */ |
262 | 14.2k | arg->data.str.area = my_strndup(trash.area, trash.data); |
263 | 14.2k | arg->data.str.data = trash.data; |
264 | 14.2k | arg->data.str.size = trash.data + 1; |
265 | 14.2k | break; |
266 | | |
267 | 0 | case ARGT_IPV4: |
268 | 0 | if (!trash.data) // empty address |
269 | 0 | goto empty_err; |
270 | | |
271 | 0 | if (inet_pton(AF_INET, trash.area, &arg->data.ipv4) <= 0) |
272 | 0 | goto parse_err; |
273 | 0 | break; |
274 | | |
275 | 0 | case ARGT_MSK4: |
276 | 0 | if (!trash.data) // empty mask |
277 | 0 | goto empty_err; |
278 | | |
279 | 0 | if (!str2mask(trash.area, &arg->data.ipv4)) |
280 | 0 | goto parse_err; |
281 | | |
282 | 0 | arg->type = ARGT_IPV4; |
283 | 0 | break; |
284 | | |
285 | 0 | case ARGT_IPV6: |
286 | 0 | if (!trash.data) // empty address |
287 | 0 | goto empty_err; |
288 | | |
289 | 0 | if (inet_pton(AF_INET6, trash.area, &arg->data.ipv6) <= 0) |
290 | 0 | goto parse_err; |
291 | 0 | break; |
292 | | |
293 | 0 | case ARGT_MSK6: |
294 | 0 | if (!trash.data) // empty mask |
295 | 0 | goto empty_err; |
296 | | |
297 | 0 | if (!str2mask6(trash.area, &arg->data.ipv6)) |
298 | 0 | goto parse_err; |
299 | | |
300 | 0 | arg->type = ARGT_IPV6; |
301 | 0 | break; |
302 | | |
303 | 0 | case ARGT_TIME: |
304 | 0 | if (!trash.data) // empty time |
305 | 0 | goto empty_err; |
306 | | |
307 | 0 | ptr_err = parse_time_err(trash.area, &uint, TIME_UNIT_MS); |
308 | 0 | if (ptr_err) { |
309 | 0 | if (ptr_err == PARSE_TIME_OVER || ptr_err == PARSE_TIME_UNDER) |
310 | 0 | ptr_err = trash.area; |
311 | 0 | goto parse_err; |
312 | 0 | } |
313 | 0 | arg->data.sint = uint; |
314 | 0 | arg->type = ARGT_SINT; |
315 | 0 | break; |
316 | | |
317 | 0 | case ARGT_SIZE: |
318 | 0 | if (!trash.data) // empty size |
319 | 0 | goto empty_err; |
320 | | |
321 | 0 | ptr_err = parse_size_err(trash.area, &uint); |
322 | 0 | if (ptr_err) |
323 | 0 | goto parse_err; |
324 | | |
325 | 0 | arg->data.sint = uint; |
326 | 0 | arg->type = ARGT_SINT; |
327 | 0 | break; |
328 | | |
329 | 0 | case ARGT_PBUF_FNUM: |
330 | 0 | if (!trash.data) |
331 | 0 | goto empty_err; |
332 | | |
333 | 0 | if (!parse_dotted_uints(trash.area, &arg->data.fid.ids, &arg->data.fid.sz)) |
334 | 0 | goto parse_err; |
335 | | |
336 | 0 | break; |
337 | | |
338 | | /* FIXME: other types need to be implemented here */ |
339 | 0 | default: |
340 | 0 | goto not_impl; |
341 | 14.2k | } |
342 | | |
343 | 14.2k | pos++; |
344 | 14.2k | arg++; |
345 | | |
346 | | /* don't go back to parsing if we reached end */ |
347 | 14.2k | if (!len || !*in || *in == ')' || pos >= nbarg) |
348 | 12.8k | break; |
349 | | |
350 | | /* skip comma */ |
351 | 1.44k | in++; len--; |
352 | 1.44k | } |
353 | | |
354 | 13.0k | end_parse: |
355 | 13.0k | if (pos < min_arg) { |
356 | | /* not enough arguments */ |
357 | 66 | memprintf(err_msg, |
358 | 66 | "missing arguments (got %d/%d), type '%s' expected", |
359 | 66 | pos, min_arg, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]); |
360 | 66 | goto err; |
361 | 66 | } |
362 | | |
363 | 12.9k | if (empty) { |
364 | | /* nothing to do */ |
365 | 12.0k | } else if (*in == ')') { |
366 | | /* skip the expected closing parenthesis */ |
367 | 11.9k | in++; |
368 | 11.9k | } else { |
369 | | /* the caller is responsible for freeing this message */ |
370 | 118 | char *word = (len > 0) ? my_strndup(in, len) : (char *)in; |
371 | | |
372 | 118 | if (*word) |
373 | 14 | memprintf(err_msg, "expected ')' before '%s'", word); |
374 | 104 | else |
375 | 104 | memprintf(err_msg, "expected ')'"); |
376 | | |
377 | 118 | if (len > 0) |
378 | 0 | free(word); |
379 | | /* when we're missing a right paren, the empty part preceding |
380 | | * already created an empty arg, adding one to the position, so |
381 | | * let's fix the reporting to avoid being confusing. |
382 | | */ |
383 | 118 | if (pos > 1) |
384 | 6 | pos--; |
385 | 118 | goto err; |
386 | 118 | } |
387 | | |
388 | | /* note that pos might be < nbarg and this is not an error, it's up to the |
389 | | * caller to decide what to do with optional args. |
390 | | */ |
391 | 12.8k | if (err_arg) |
392 | 12.8k | *err_arg = pos; |
393 | 12.8k | if (end_ptr) |
394 | 12.8k | *end_ptr = in; |
395 | 12.8k | return pos; |
396 | | |
397 | 249 | err: |
398 | 249 | if (new_al == al) { |
399 | | /* only free the arg area if we have not queued unresolved args |
400 | | * still pointing to it. |
401 | | */ |
402 | 249 | free_args(*argp); |
403 | 249 | free(*argp); |
404 | 249 | } |
405 | 249 | *argp = NULL; |
406 | 249 | if (err_arg) |
407 | 249 | *err_arg = pos; |
408 | 249 | if (end_ptr) |
409 | 249 | *end_ptr = in; |
410 | 249 | return -1; |
411 | | |
412 | 0 | empty_err: |
413 | | /* If we've only got an empty set of parenthesis with nothing |
414 | | * in between, there is no arg at all. |
415 | | */ |
416 | 0 | if (!pos) { |
417 | 0 | ha_free(argp); |
418 | 0 | } |
419 | |
|
420 | 0 | if (pos >= min_arg) |
421 | 0 | goto end_parse; |
422 | | |
423 | 0 | memprintf(err_msg, "expected type '%s' at position %d, but got nothing", |
424 | 0 | arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1); |
425 | 0 | goto err; |
426 | | |
427 | 0 | parse_err: |
428 | | /* come here with the word attempted to parse in trash */ |
429 | 0 | memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d", |
430 | 0 | trash.area, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1); |
431 | 0 | goto err; |
432 | | |
433 | 0 | not_impl: |
434 | 0 | memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug", |
435 | 0 | arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]); |
436 | 0 | goto err; |
437 | | |
438 | 51 | buffer_err: |
439 | 51 | memprintf(err_msg, "too small buffer size to store decoded argument %d, increase bufsize ?", |
440 | 51 | pos + 1); |
441 | 51 | goto err; |
442 | | |
443 | 14 | unquote_err: |
444 | | /* come here with the parsed part in <trash.area>:<trash.data> and the |
445 | | * unparsable part in <in>. |
446 | | */ |
447 | 14 | trash.area[trash.data] = 0; |
448 | 14 | memprintf(err_msg, "failed to parse '%s' after '%s' as type '%s' at position %d", |
449 | 14 | in, trash.area, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1); |
450 | 14 | goto err; |
451 | | |
452 | 0 | alloc_err: |
453 | 0 | memprintf(err_msg, "out of memory"); |
454 | 0 | goto err; |
455 | | |
456 | 0 | resolve_err: |
457 | 0 | memprintf(err_msg, "unresolved argument of type '%s' at position %d not allowed", |
458 | 0 | arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1); |
459 | 0 | goto err; |
460 | 0 | } |
461 | | |
462 | | /* Free all args of an args array, taking care of unresolved arguments as well. |
463 | | * It stops at the ARGT_STOP, which must be present. The array itself is not |
464 | | * freed, it's up to the caller to do it. However it is returned, allowing to |
465 | | * call free(free_args(argptr)). It is valid to call it with a NULL args, and |
466 | | * nothing will be done). |
467 | | */ |
468 | | struct arg *free_args(struct arg *args) |
469 | 28.4k | { |
470 | 28.4k | struct arg *arg; |
471 | | |
472 | 42.7k | for (arg = args; arg && arg->type != ARGT_STOP; arg++) { |
473 | 14.2k | if (arg->type == ARGT_STR || arg->unresolved) |
474 | 14.2k | chunk_destroy(&arg->data.str); |
475 | 0 | else if (arg->type == ARGT_REG) |
476 | 0 | regex_free(arg->data.reg); |
477 | 0 | else if (arg->type == ARGT_PBUF_FNUM) |
478 | | ha_free(&arg->data.fid.ids); |
479 | 14.2k | } |
480 | 28.4k | return args; |
481 | 28.4k | } |