blob: 160c70539f08f09cc553e11824662894015fe579 [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>
18#include <proto/arg.h>
19
Thierry FOURNIER49f45af2014-12-08 19:50:43 +010020const char *arg_type_names[ARGT_NBTYPES] = {
Willy Tarreau2ac57182012-04-19 15:24:50 +020021 [ARGT_STOP] = "end of arguments",
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +020022 [ARGT_SINT] = "integer",
Willy Tarreau2ac57182012-04-19 15:24:50 +020023 [ARGT_STR] = "string",
24 [ARGT_IPV4] = "IPv4 address",
25 [ARGT_MSK4] = "IPv4 mask",
26 [ARGT_IPV6] = "IPv6 address",
27 [ARGT_MSK6] = "IPv6 mask",
28 [ARGT_TIME] = "delay",
29 [ARGT_SIZE] = "size",
30 [ARGT_FE] = "frontend",
31 [ARGT_BE] = "backend",
32 [ARGT_TAB] = "table",
33 [ARGT_SRV] = "server",
34 [ARGT_USR] = "user list",
Willy Tarreau53c250e2015-01-19 18:58:20 +010035 [ARGT_MAP] = "map",
Willy Tarreau46947782015-01-19 19:00:58 +010036 [ARGT_REG] = "regex",
Willy Tarreauf7ead612015-09-21 20:57:12 +020037 [ARGT_VAR] = "variable",
Frédéric Lécaille3a463c92019-02-25 15:20:35 +010038 [ARGT_PBUF_FNUM] = "Protocol buffers field number",
Willy Tarreau2ac57182012-04-19 15:24:50 +020039 /* Unassigned types must never happen. Better crash during parsing if they do. */
40};
41
Willy Tarreau2e845be2012-10-19 19:49:09 +020042/* This dummy arg list may be used by default when no arg is found, it helps
43 * parsers by removing pointer checks.
44 */
Willy Tarreau3d241e72015-01-19 18:44:07 +010045struct arg empty_arg_list[ARGM_NBARGS] = { };
Willy Tarreau2e845be2012-10-19 19:49:09 +020046
Willy Tarreaua4312fa2013-04-02 16:34:32 +020047/* This function clones a struct arg_list template into a new one which is
48 * returned.
49 */
50struct arg_list *arg_list_clone(const struct arg_list *orig)
51{
52 struct arg_list *new;
53
54 if ((new = calloc(1, sizeof(*new))) != NULL) {
55 /* ->list will be set by the caller when inserting the element.
56 * ->arg and ->arg_pos will be set by the caller.
57 */
58 new->ctx = orig->ctx;
59 new->kw = orig->kw;
60 new->conv = orig->conv;
61 new->file = orig->file;
62 new->line = orig->line;
63 }
64 return new;
65}
66
67/* This function clones a struct <arg_list> template into a new one which is
68 * set to point to arg <arg> at pos <pos>, and which is returned if the caller
69 * wants to apply further changes.
70 */
71struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
72{
73 struct arg_list *new;
74
75 new = arg_list_clone(orig);
Willy Tarreaua9e2e4b2017-04-12 22:28:52 +020076 if (new) {
77 new->arg = arg;
78 new->arg_pos = pos;
79 LIST_ADDQ(&orig->list, &new->list);
80 }
Willy Tarreaua4312fa2013-04-02 16:34:32 +020081 return new;
82}
83
Willy Tarreau2ac57182012-04-19 15:24:50 +020084/* This function builds an argument list from a config line. It returns the
85 * number of arguments found, or <0 in case of any error. Everything needed
86 * it automatically allocated. A pointer to an error message might be returned
87 * in err_msg if not NULL, in which case it would be allocated and the caller
88 * will have to check it and free it. The output arg list is returned in argp
89 * which must be valid. The returned array is always terminated by an arg of
90 * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
Willy Tarreaua4312fa2013-04-02 16:34:32 +020091 * Unresolved arguments are appended to arg list <al>, which also serves as a
92 * template to create new entries. The mask is composed of a number of
Willy Tarreau3d241e72015-01-19 18:44:07 +010093 * mandatory arguments in its lower ARGM_BITS bits, and a concatenation of each
94 * argument type in each subsequent ARGT_BITS-bit sblock. If <err_msg> is not
95 * NULL, it must point to a freeable or NULL pointer.
Willy Tarreau2ac57182012-04-19 15:24:50 +020096 */
David Carlier15073a32016-03-15 19:00:35 +000097int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
Willy Tarreaua4312fa2013-04-02 16:34:32 +020098 char **err_msg, const char **err_ptr, int *err_arg,
99 struct arg_list *al)
Willy Tarreau2ac57182012-04-19 15:24:50 +0200100{
101 int nbarg;
102 int pos;
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200103 struct arg *arg;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200104 const char *beg;
105 char *word = NULL;
106 const char *ptr_err = NULL;
107 int min_arg;
Willy Tarreau0622f022017-04-12 22:32:04 +0200108 struct arg_list *new_al = al;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200109
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200110 *argp = NULL;
111
Willy Tarreau3d241e72015-01-19 18:44:07 +0100112 min_arg = mask & ARGM_MASK;
113 mask >>= ARGM_BITS;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200114
115 pos = 0;
Willy Tarreau3d241e72015-01-19 18:44:07 +0100116 /* find between 0 and NBARGS the max number of args supported by the mask */
117 for (nbarg = 0; nbarg < ARGM_NBARGS && ((mask >> (nbarg * ARGT_BITS)) & ARGT_MASK); nbarg++);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200118
119 if (!nbarg)
120 goto end_parse;
121
122 /* Note: an empty input string contains an empty argument if this argument
123 * is marked mandatory. Otherwise we can ignore it.
124 */
125 if (!len && !min_arg)
126 goto end_parse;
127
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200128 arg = *argp = calloc(nbarg + 1, sizeof(*arg));
Willy Tarreau2ac57182012-04-19 15:24:50 +0200129
Remi Tricot-Le Breton4b28a5f2021-05-19 12:00:54 +0200130 if (!arg)
131 goto alloc_err;
132
Willy Tarreau2ac57182012-04-19 15:24:50 +0200133 /* Note: empty arguments after a comma always exist. */
134 while (pos < nbarg) {
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200135 unsigned int uint;
136
Willy Tarreau2ac57182012-04-19 15:24:50 +0200137 beg = in;
138 while (len && *in != ',') {
139 in++;
140 len--;
141 }
142
143 /* we have a new argument between <beg> and <in> (not included).
144 * For ease of handling, we copy it into a zero-terminated word.
145 * By default, the output argument will be the same type of the
146 * expected one.
147 */
148 free(word);
149 word = my_strndup(beg, in - beg);
150
Willy Tarreau3d241e72015-01-19 18:44:07 +0100151 arg->type = (mask >> (pos * ARGT_BITS)) & ARGT_MASK;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200152
153 switch (arg->type) {
154 case ARGT_SINT:
155 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200156 goto empty_err;
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200157 arg->data.sint = read_int64(&beg, in);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200158 if (beg < in)
159 goto parse_err;
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200160 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200161 break;
162
163 case ARGT_FE:
164 case ARGT_BE:
165 case ARGT_TAB:
166 case ARGT_SRV:
167 case ARGT_USR:
Willy Tarreau46947782015-01-19 19:00:58 +0100168 case ARGT_REG:
Willy Tarreau496aa012012-06-01 10:38:29 +0200169 /* These argument types need to be stored as strings during
170 * parsing then resolved later.
171 */
172 arg->unresolved = 1;
Willy Tarreau0622f022017-04-12 22:32:04 +0200173 new_al = arg_list_add(al, arg, pos);
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200174
Willy Tarreau496aa012012-06-01 10:38:29 +0200175 /* fall through */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200176 case ARGT_STR:
177 /* all types that must be resolved are stored as strings
178 * during the parsing. The caller must at one point resolve
179 * them and free the string.
180 */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200181 arg->data.str.area = word;
182 arg->data.str.data = in - beg;
183 arg->data.str.size = arg->data.str.data + 1;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200184 word = NULL;
185 break;
186
187 case ARGT_IPV4:
188 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200189 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200190
191 if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
192 goto parse_err;
193 break;
194
195 case ARGT_MSK4:
196 if (in == beg) // empty mask
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200197 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200198
199 if (!str2mask(word, &arg->data.ipv4))
200 goto parse_err;
201
202 arg->type = ARGT_IPV4;
203 break;
204
205 case ARGT_IPV6:
206 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200207 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200208
209 if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
210 goto parse_err;
211 break;
212
Tim Duesterhusb814da62018-01-25 16:24:50 +0100213 case ARGT_MSK6:
214 if (in == beg) // empty mask
215 goto empty_err;
216
217 if (!str2mask6(word, &arg->data.ipv6))
218 goto parse_err;
219
220 arg->type = ARGT_IPV6;
221 break;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200222
223 case ARGT_TIME:
224 if (in == beg) // empty time
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200225 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200226
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200227 ptr_err = parse_time_err(word, &uint, TIME_UNIT_MS);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200228 if (ptr_err) {
229 if (ptr_err == PARSE_TIME_OVER || ptr_err == PARSE_TIME_UNDER)
230 ptr_err = word;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200231 goto parse_err;
Willy Tarreau9faebe32019-06-07 19:00:37 +0200232 }
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200233 arg->data.sint = uint;
234 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200235 break;
236
237 case ARGT_SIZE:
238 if (in == beg) // empty size
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200239 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200240
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200241 ptr_err = parse_size_err(word, &uint);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200242 if (ptr_err)
243 goto parse_err;
244
Thierry FOURNIERbf65cd42015-07-20 17:45:02 +0200245 arg->data.sint = uint;
246 arg->type = ARGT_SINT;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200247 break;
248
Frédéric Lécaille3a463c92019-02-25 15:20:35 +0100249 case ARGT_PBUF_FNUM:
Frédéric Lécaille756d97f2019-03-04 19:03:48 +0100250 if (in == beg)
251 goto empty_err;
252
Frédéric Lécaille3a463c92019-02-25 15:20:35 +0100253 if (!parse_dotted_uints(word, &arg->data.fid.ids, &arg->data.fid.sz))
254 goto parse_err;
255
256 break;
257
Willy Tarreau2ac57182012-04-19 15:24:50 +0200258 /* FIXME: other types need to be implemented here */
259 default:
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200260 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200261 }
262
263 pos++;
264 arg++;
265
266 /* don't go back to parsing if we reached end */
267 if (!len || pos >= nbarg)
268 break;
269
270 /* skip comma */
271 in++; len--;
272 }
273
274 end_parse:
275 free(word); word = NULL;
276
277 if (pos < min_arg) {
278 /* not enough arguments */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200279 memprintf(err_msg,
Willy Tarreaub111d422013-12-13 00:38:47 +0100280 "missing arguments (got %d/%d), type '%s' expected",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100281 pos, min_arg, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200282 goto err;
283 }
284
285 if (len) {
286 /* too many arguments, starting at <in> */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200287 /* the caller is responsible for freeing this message */
288 word = my_strndup(in, len);
Willy Tarreaub111d422013-12-13 00:38:47 +0100289 if (nbarg)
290 memprintf(err_msg, "end of arguments expected at position %d, but got '%s'",
291 pos + 1, word);
292 else
293 memprintf(err_msg, "no argument supported, but got '%s'", word);
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200294 free(word); word = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200295 goto err;
296 }
297
298 /* note that pos might be < nbarg and this is not an error, it's up to the
299 * caller to decide what to do with optional args.
300 */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200301 if (err_arg)
302 *err_arg = pos;
303 if (err_ptr)
304 *err_ptr = in;
305 return pos;
306
Willy Tarreau2ac57182012-04-19 15:24:50 +0200307 err:
308 free(word);
Willy Tarreau0622f022017-04-12 22:32:04 +0200309 if (new_al == al) {
310 /* only free the arg area if we have not queued unresolved args
311 * still pointing to it.
312 */
313 free(*argp);
314 }
Willy Tarreau681e49d2013-12-06 15:30:05 +0100315 *argp = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200316 if (err_arg)
317 *err_arg = pos;
318 if (err_ptr)
319 *err_ptr = in;
320 return -1;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200321
322 empty_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200323 memprintf(err_msg, "expected type '%s' at position %d, but got nothing",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100324 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200325 goto err;
326
327 parse_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200328 memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100329 word, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200330 goto err;
331
332 not_impl:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200333 memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100334 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200335 goto err;
Remi Tricot-Le Breton4b28a5f2021-05-19 12:00:54 +0200336
337 alloc_err:
338 memprintf(err_msg, "out of memory");
339 goto err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200340}