blob: 90742265cc477ac06cb6d01d24241b2ca9246420 [file] [log] [blame]
Willy Tarreau2ac57182012-04-19 15:24:50 +02001/*
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 <common/standard.h>
Willy Tarreau338c6702020-02-14 11:34:35 +010018#include <common/chunk.h>
19#include <types/global.h>
Willy Tarreau2ac57182012-04-19 15:24:50 +020020#include <proto/arg.h>
21
Thierry FOURNIER49f45af2014-12-08 19:50:43 +010022const char *arg_type_names[ARGT_NBTYPES] = {
Willy Tarreau2ac57182012-04-19 15:24:50 +020023 [ARGT_STOP] = "end of arguments",
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +020024 [ARGT_SINT] = "integer",
Willy Tarreau2ac57182012-04-19 15:24:50 +020025 [ARGT_STR] = "string",
26 [ARGT_IPV4] = "IPv4 address",
27 [ARGT_MSK4] = "IPv4 mask",
28 [ARGT_IPV6] = "IPv6 address",
29 [ARGT_MSK6] = "IPv6 mask",
30 [ARGT_TIME] = "delay",
31 [ARGT_SIZE] = "size",
32 [ARGT_FE] = "frontend",
33 [ARGT_BE] = "backend",
34 [ARGT_TAB] = "table",
35 [ARGT_SRV] = "server",
36 [ARGT_USR] = "user list",
Willy Tarreau53c250e2015-01-19 18:58:20 +010037 [ARGT_MAP] = "map",
Willy Tarreau46947782015-01-19 19:00:58 +010038 [ARGT_REG] = "regex",
Willy Tarreauf7ead612015-09-21 20:57:12 +020039 [ARGT_VAR] = "variable",
Frédéric Lécaille3a463c92019-02-25 15:20:35 +010040 [ARGT_PBUF_FNUM] = "Protocol buffers field number",
Willy Tarreau2ac57182012-04-19 15:24:50 +020041 /* Unassigned types must never happen. Better crash during parsing if they do. */
42};
43
Willy Tarreau2e845be2012-10-19 19:49:09 +020044/* This dummy arg list may be used by default when no arg is found, it helps
45 * parsers by removing pointer checks.
46 */
Willy Tarreau3d241e72015-01-19 18:44:07 +010047struct arg empty_arg_list[ARGM_NBARGS] = { };
Willy Tarreau2e845be2012-10-19 19:49:09 +020048
Willy Tarreaua4312fa2013-04-02 16:34:32 +020049/* This function clones a struct arg_list template into a new one which is
50 * returned.
51 */
52struct arg_list *arg_list_clone(const struct arg_list *orig)
53{
54 struct arg_list *new;
55
56 if ((new = calloc(1, sizeof(*new))) != NULL) {
57 /* ->list will be set by the caller when inserting the element.
58 * ->arg and ->arg_pos will be set by the caller.
59 */
60 new->ctx = orig->ctx;
61 new->kw = orig->kw;
62 new->conv = orig->conv;
63 new->file = orig->file;
64 new->line = orig->line;
65 }
66 return new;
67}
68
69/* This function clones a struct <arg_list> template into a new one which is
70 * set to point to arg <arg> at pos <pos>, and which is returned if the caller
71 * wants to apply further changes.
72 */
73struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
74{
75 struct arg_list *new;
76
77 new = arg_list_clone(orig);
Willy Tarreaua9e2e4b2017-04-12 22:28:52 +020078 if (new) {
79 new->arg = arg;
80 new->arg_pos = pos;
81 LIST_ADDQ(&orig->list, &new->list);
82 }
Willy Tarreaua4312fa2013-04-02 16:34:32 +020083 return new;
84}
85
Willy Tarreau80b53ff2020-02-14 08:40:37 +010086/* This function builds an argument list from a config line, and stops at the
87 * first non-matching character, which is pointed to in <end_ptr>. A valid arg
88 * list starts with an opening parenthesis '(', contains a number of comma-
89 * delimited words, and ends with the closing parenthesis ')'. An empty list
90 * (with or without the parenthesis) will lead to a valid empty argument if the
91 * keyword has a mandatory one. The function returns the number of arguments
92 * emitted, or <0 in case of any error. Everything needed it automatically
93 * allocated. A pointer to an error message might be returned in err_msg if not
94 * NULL, in which case it would be allocated and the caller will have to check
95 * it and free it. The output arg list is returned in argp which must be valid.
96 * The returned array is always terminated by an arg of type ARGT_STOP (0),
97 * unless the mask indicates that no argument is supported. Unresolved arguments
98 * are appended to arg list <al>, which also serves as a template to create new
99 * entries. The mask is composed of a number of mandatory arguments in its lower
100 * ARGM_BITS bits, and a concatenation of each argument type in each subsequent
101 * ARGT_BITS-bit sblock. If <err_msg> is not NULL, it must point to a freeable
102 * or NULL pointer. The caller is expected to restart the parsing from the new
103 * pointer set in <end_ptr>, which is the first character considered as not
104 * being part of the arg list. The input string ends on the first between <len>
105 * characters (when len is positive) or the first NUL character. Placing -1 in
106 * <len> will make it virtually unbounded (~2GB long strings).
Willy Tarreau2ac57182012-04-19 15:24:50 +0200107 */
David Carlier15073a32016-03-15 19:00:35 +0000108int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100109 char **err_msg, const char **end_ptr, int *err_arg,
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200110 struct arg_list *al)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200111{
112 int nbarg;
113 int pos;
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200114 struct arg *arg;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200115 const char *beg;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200116 const char *ptr_err = NULL;
117 int min_arg;
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100118 int empty;
Willy Tarreau0622f022017-04-12 22:32:04 +0200119 struct arg_list *new_al = al;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200120
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200121 *argp = NULL;
122
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100123 empty = 0;
124 if (!len || *in != '(') {
125 /* it's already not for us, stop here */
126 empty = 1;
127 len = 0;
128 } else {
129 /* skip opening parenthesis */
130 len--;
131 in++;
132 }
133
Willy Tarreau3d241e72015-01-19 18:44:07 +0100134 min_arg = mask & ARGM_MASK;
135 mask >>= ARGM_BITS;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200136
137 pos = 0;
Willy Tarreau3d241e72015-01-19 18:44:07 +0100138 /* find between 0 and NBARGS the max number of args supported by the mask */
139 for (nbarg = 0; nbarg < ARGM_NBARGS && ((mask >> (nbarg * ARGT_BITS)) & ARGT_MASK); nbarg++);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200140
141 if (!nbarg)
142 goto end_parse;
143
144 /* Note: an empty input string contains an empty argument if this argument
145 * is marked mandatory. Otherwise we can ignore it.
146 */
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100147 if (empty && !min_arg)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200148 goto end_parse;
149
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200150 arg = *argp = calloc(nbarg + 1, sizeof(*arg));
Willy Tarreau2ac57182012-04-19 15:24:50 +0200151
152 /* Note: empty arguments after a comma always exist. */
153 while (pos < nbarg) {
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200154 unsigned int uint;
Willy Tarreauef21fac2020-02-14 13:37:20 +0100155 int squote = 0, dquote = 0;
156 char *out;
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200157
Willy Tarreauef21fac2020-02-14 13:37:20 +0100158 chunk_reset(&trash);
159 out = trash.area;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200160
Willy Tarreauef21fac2020-02-14 13:37:20 +0100161 while (len && *in && trash.data < trash.size - 1) {
162 if (*in == '"' && !squote) { /* double quote outside single quotes */
163 if (dquote)
164 dquote = 0;
165 else
166 dquote = 1;
167 in++; len--;
168 continue;
169 }
170 else if (*in == '\'' && !dquote) { /* single quote outside double quotes */
171 if (squote)
172 squote = 0;
173 else
174 squote = 1;
175 in++; len--;
176 continue;
177 }
178 else if (*in == '\\' && !squote && len != 1) {
179 /* '\', ', ' ', '"' support being escaped by '\' */
180 if (len == 1 || in[1] == 0)
181 goto unquote_err;
182
183 if (in[1] == '\\' || in[1] == ' ' || in[1] == '"' || in[1] == '\'') {
184 in++; len--;
185 *out++ = *in;
186 }
187 else if (in[1] == 'r') {
188 in++; len--;
189 *out++ = '\r';
190 }
191 else if (in[1] == 'n') {
192 in++; len--;
193 *out++ = '\n';
194 }
195 else if (in[1] == 't') {
196 in++; len--;
197 *out++ = '\t';
198 }
199 else {
200 /* just a lone '\' */
201 *out++ = *in;
202 }
203 in++; len--;
204 }
205 else {
206 if (!squote && !dquote && (*in == ',' || *in == ')')) {
207 /* end of argument */
208 break;
209 }
210 /* verbatim copy */
211 *out++ = *in++;
212 len--;
213 }
214 trash.data = out - trash.area;
215 }
Willy Tarreau807aef82020-02-15 14:54:28 +0100216
Willy Tarreau9af749b2020-02-16 10:46:37 +0100217 if (len && *in && *in != ',' && *in != ')')
Willy Tarreau807aef82020-02-15 14:54:28 +0100218 goto buffer_err;
219
Willy Tarreauef21fac2020-02-14 13:37:20 +0100220 trash.area[trash.data] = 0;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200221
Willy Tarreau3d241e72015-01-19 18:44:07 +0100222 arg->type = (mask >> (pos * ARGT_BITS)) & ARGT_MASK;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200223
224 switch (arg->type) {
225 case ARGT_SINT:
Willy Tarreau338c6702020-02-14 11:34:35 +0100226 if (!trash.data) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200227 goto empty_err;
Willy Tarreau338c6702020-02-14 11:34:35 +0100228 beg = trash.area;
229 arg->data.sint = read_int64(&beg, trash.area + trash.data);
230 if (beg < trash.area + trash.data)
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200231 goto parse_err;
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200232 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200233 break;
234
235 case ARGT_FE:
236 case ARGT_BE:
237 case ARGT_TAB:
238 case ARGT_SRV:
239 case ARGT_USR:
Willy Tarreau46947782015-01-19 19:00:58 +0100240 case ARGT_REG:
Willy Tarreau496aa012012-06-01 10:38:29 +0200241 /* These argument types need to be stored as strings during
242 * parsing then resolved later.
243 */
244 arg->unresolved = 1;
Willy Tarreau0622f022017-04-12 22:32:04 +0200245 new_al = arg_list_add(al, arg, pos);
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200246
Willy Tarreau496aa012012-06-01 10:38:29 +0200247 /* fall through */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200248 case ARGT_STR:
249 /* all types that must be resolved are stored as strings
250 * during the parsing. The caller must at one point resolve
251 * them and free the string.
252 */
Willy Tarreau338c6702020-02-14 11:34:35 +0100253 arg->data.str.area = my_strndup(trash.area, trash.data);
254 arg->data.str.data = trash.data;
255 arg->data.str.size = trash.data + 1;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200256 break;
257
258 case ARGT_IPV4:
Willy Tarreau338c6702020-02-14 11:34:35 +0100259 if (!trash.data) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200260 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200261
Willy Tarreau338c6702020-02-14 11:34:35 +0100262 if (inet_pton(AF_INET, trash.area, &arg->data.ipv4) <= 0)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200263 goto parse_err;
264 break;
265
266 case ARGT_MSK4:
Willy Tarreau338c6702020-02-14 11:34:35 +0100267 if (!trash.data) // empty mask
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200268 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200269
Willy Tarreau338c6702020-02-14 11:34:35 +0100270 if (!str2mask(trash.area, &arg->data.ipv4))
Willy Tarreau2ac57182012-04-19 15:24:50 +0200271 goto parse_err;
272
273 arg->type = ARGT_IPV4;
274 break;
275
276 case ARGT_IPV6:
Willy Tarreau338c6702020-02-14 11:34:35 +0100277 if (!trash.data) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200278 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200279
Willy Tarreau338c6702020-02-14 11:34:35 +0100280 if (inet_pton(AF_INET6, trash.area, &arg->data.ipv6) <= 0)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200281 goto parse_err;
282 break;
283
Tim Duesterhusb814da62018-01-25 16:24:50 +0100284 case ARGT_MSK6:
Willy Tarreau338c6702020-02-14 11:34:35 +0100285 if (!trash.data) // empty mask
Tim Duesterhusb814da62018-01-25 16:24:50 +0100286 goto empty_err;
287
Willy Tarreau338c6702020-02-14 11:34:35 +0100288 if (!str2mask6(trash.area, &arg->data.ipv6))
Tim Duesterhusb814da62018-01-25 16:24:50 +0100289 goto parse_err;
290
291 arg->type = ARGT_IPV6;
292 break;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200293
294 case ARGT_TIME:
Willy Tarreau338c6702020-02-14 11:34:35 +0100295 if (!trash.data) // empty time
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200296 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200297
Willy Tarreau338c6702020-02-14 11:34:35 +0100298 ptr_err = parse_time_err(trash.area, &uint, TIME_UNIT_MS);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200299 if (ptr_err) {
300 if (ptr_err == PARSE_TIME_OVER || ptr_err == PARSE_TIME_UNDER)
Willy Tarreau338c6702020-02-14 11:34:35 +0100301 ptr_err = trash.area;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200302 goto parse_err;
Willy Tarreau9faebe32019-06-07 19:00:37 +0200303 }
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200304 arg->data.sint = uint;
305 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200306 break;
307
308 case ARGT_SIZE:
Willy Tarreau338c6702020-02-14 11:34:35 +0100309 if (!trash.data) // empty size
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200310 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200311
Willy Tarreau338c6702020-02-14 11:34:35 +0100312 ptr_err = parse_size_err(trash.area, &uint);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200313 if (ptr_err)
314 goto parse_err;
315
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200316 arg->data.sint = uint;
317 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200318 break;
319
Frédéric Lécaille3a463c92019-02-25 15:20:35 +0100320 case ARGT_PBUF_FNUM:
Willy Tarreau338c6702020-02-14 11:34:35 +0100321 if (!trash.data)
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100322 goto empty_err;
323
Willy Tarreau338c6702020-02-14 11:34:35 +0100324 if (!parse_dotted_uints(trash.area, &arg->data.fid.ids, &arg->data.fid.sz))
Frédéric Lécaille3a463c92019-02-25 15:20:35 +0100325 goto parse_err;
326
327 break;
328
Willy Tarreau2ac57182012-04-19 15:24:50 +0200329 /* FIXME: other types need to be implemented here */
330 default:
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200331 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200332 }
333
334 pos++;
335 arg++;
336
337 /* don't go back to parsing if we reached end */
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100338 if (!len || !*in || *in == ')' || pos >= nbarg)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200339 break;
340
341 /* skip comma */
342 in++; len--;
343 }
344
345 end_parse:
Willy Tarreau2ac57182012-04-19 15:24:50 +0200346 if (pos < min_arg) {
347 /* not enough arguments */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200348 memprintf(err_msg,
Willy Tarreaub111d422013-12-13 00:38:47 +0100349 "missing arguments (got %d/%d), type '%s' expected",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100350 pos, min_arg, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200351 goto err;
352 }
353
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100354 if (empty) {
355 /* nothing to do */
356 } else if (*in == ')') {
357 /* skip the expected closing parenthesis */
358 in++;
359 } else {
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200360 /* the caller is responsible for freeing this message */
Willy Tarreau338c6702020-02-14 11:34:35 +0100361 char *word = (len > 0) ? my_strndup(in, len) : (char *)in;
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100362 memprintf(err_msg, "expected ')' before '%s'", word);
363 if (len > 0)
364 free(word);
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100365 /* when we're missing a right paren, the empty part preceeding
366 * already created an empty arg, adding one to the position, so
367 * let's fix the reporting to avoid being confusing.
368 */
369 if (pos > 1)
370 pos--;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200371 goto err;
372 }
373
374 /* note that pos might be < nbarg and this is not an error, it's up to the
375 * caller to decide what to do with optional args.
376 */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200377 if (err_arg)
378 *err_arg = pos;
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100379 if (end_ptr)
380 *end_ptr = in;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200381 return pos;
382
Willy Tarreau2ac57182012-04-19 15:24:50 +0200383 err:
Willy Tarreau0622f022017-04-12 22:32:04 +0200384 if (new_al == al) {
385 /* only free the arg area if we have not queued unresolved args
386 * still pointing to it.
387 */
388 free(*argp);
389 }
Willy Tarreau681e49d2013-12-06 15:30:05 +0100390 *argp = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200391 if (err_arg)
392 *err_arg = pos;
Willy Tarreau80b53ff2020-02-14 08:40:37 +0100393 if (end_ptr)
394 *end_ptr = in;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200395 return -1;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200396
397 empty_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200398 memprintf(err_msg, "expected type '%s' at position %d, but got nothing",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100399 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200400 goto err;
401
402 parse_err:
Willy Tarreau338c6702020-02-14 11:34:35 +0100403 /* come here with the word attempted to parse in trash */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200404 memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d",
Willy Tarreau338c6702020-02-14 11:34:35 +0100405 trash.area, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200406 goto err;
407
408 not_impl:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200409 memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100410 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200411 goto err;
Willy Tarreau338c6702020-02-14 11:34:35 +0100412
413 buffer_err:
414 memprintf(err_msg, "too small buffer size to store decoded argument %d, increase bufsize ?",
415 pos + 1);
416 goto err;
Willy Tarreauef21fac2020-02-14 13:37:20 +0100417
418 unquote_err:
419 /* come here with the parsed part in <trash.area>:<trash.data> and the
420 * unparsable part in <in>.
421 */
422 trash.area[trash.data] = 0;
423 memprintf(err_msg, "failed to parse '%s' after '%s' as type '%s' at position %d",
424 in, trash.area, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
425 goto err;
426
Willy Tarreau2ac57182012-04-19 15:24:50 +0200427}