blob: 0e11c498de221b0e6e033954a819f784a5dac158 [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
20static const char *arg_type_names[ARGT_NBTYPES] = {
21 [ARGT_STOP] = "end of arguments",
22 [ARGT_UINT] = "unsigned integer",
23 [ARGT_SINT] = "signed integer",
24 [ARGT_STR] = "string",
25 [ARGT_IPV4] = "IPv4 address",
26 [ARGT_MSK4] = "IPv4 mask",
27 [ARGT_IPV6] = "IPv6 address",
28 [ARGT_MSK6] = "IPv6 mask",
29 [ARGT_TIME] = "delay",
30 [ARGT_SIZE] = "size",
31 [ARGT_FE] = "frontend",
32 [ARGT_BE] = "backend",
33 [ARGT_TAB] = "table",
34 [ARGT_SRV] = "server",
35 [ARGT_USR] = "user list",
36 /* Unassigned types must never happen. Better crash during parsing if they do. */
37};
38
39/* This function builds an argument list from a config line. It returns the
40 * number of arguments found, or <0 in case of any error. Everything needed
41 * it automatically allocated. A pointer to an error message might be returned
42 * in err_msg if not NULL, in which case it would be allocated and the caller
43 * will have to check it and free it. The output arg list is returned in argp
44 * which must be valid. The returned array is always terminated by an arg of
45 * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
46 * The mask is composed of a number of mandatory arguments in its lower 4 bits,
47 * and a concatenation of each argument type in each subsequent 4-bit block. If
48 * <err_msg> is not NULL, it must point to a freeable or NULL pointer.
49 */
50int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
51 char **err_msg, const char **err_ptr, int *err_arg)
52{
53 int nbarg;
54 int pos;
55 struct arg *arg, *arg_list = NULL;
56 const char *beg;
57 char *word = NULL;
58 const char *ptr_err = NULL;
59 int min_arg;
60
61 min_arg = mask & 15;
62 mask >>= 4;
63
64 pos = 0;
65 /* find between 0 and 8 the max number of args supported by the mask */
66 for (nbarg = 0; nbarg < 8 && ((mask >> (nbarg * 4)) & 0xF); nbarg++);
67
68 if (!nbarg)
69 goto end_parse;
70
71 /* Note: an empty input string contains an empty argument if this argument
72 * is marked mandatory. Otherwise we can ignore it.
73 */
74 if (!len && !min_arg)
75 goto end_parse;
76
77 arg = arg_list = calloc(nbarg + 1, sizeof(*arg));
78
79 /* Note: empty arguments after a comma always exist. */
80 while (pos < nbarg) {
81 beg = in;
82 while (len && *in != ',') {
83 in++;
84 len--;
85 }
86
87 /* we have a new argument between <beg> and <in> (not included).
88 * For ease of handling, we copy it into a zero-terminated word.
89 * By default, the output argument will be the same type of the
90 * expected one.
91 */
92 free(word);
93 word = my_strndup(beg, in - beg);
94
95 arg->type = (mask >> (pos * 4)) & 15;
96
97 switch (arg->type) {
98 case ARGT_SINT:
99 if (in == beg) // empty number
100 goto parse_err;
101 else if (*beg < '0' || *beg > '9') {
102 arg->data.sint = strl2uic(beg + 1, in - beg - 1);
103 if (*beg == '-')
104 arg->data.sint = -arg->data.sint;
105 else if (*beg != '+') // invalid first character
106 goto parse_err;
107 break;
108 }
109
110 arg->type = ARGT_UINT;
111 /* fall through ARGT_UINT if no sign is present */
112
113 case ARGT_UINT:
114 if (in == beg) // empty number
115 goto parse_err;
116
117 arg->data.uint = strl2uic(beg, in - beg);
118 break;
119
120 case ARGT_FE:
121 case ARGT_BE:
122 case ARGT_TAB:
123 case ARGT_SRV:
124 case ARGT_USR:
125 case ARGT_STR:
126 /* all types that must be resolved are stored as strings
127 * during the parsing. The caller must at one point resolve
128 * them and free the string.
129 */
130 arg->data.str.str = word;
131 arg->data.str.len = in - beg;
132 arg->data.str.size = arg->data.str.len + 1;
133 word = NULL;
134 break;
135
136 case ARGT_IPV4:
137 if (in == beg) // empty address
138 goto parse_err;
139
140 if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
141 goto parse_err;
142 break;
143
144 case ARGT_MSK4:
145 if (in == beg) // empty mask
146 goto parse_err;
147
148 if (!str2mask(word, &arg->data.ipv4))
149 goto parse_err;
150
151 arg->type = ARGT_IPV4;
152 break;
153
154 case ARGT_IPV6:
155 if (in == beg) // empty address
156 goto parse_err;
157
158 if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
159 goto parse_err;
160 break;
161
162 case ARGT_MSK6: /* not yet implemented */
163 goto parse_err;
164
165 case ARGT_TIME:
166 if (in == beg) // empty time
167 goto parse_err;
168
169 ptr_err = parse_time_err(word, &arg->data.uint, TIME_UNIT_MS);
170 if (ptr_err)
171 goto parse_err;
172
173 arg->type = ARGT_UINT;
174 break;
175
176 case ARGT_SIZE:
177 if (in == beg) // empty size
178 goto parse_err;
179
180 ptr_err = parse_size_err(word, &arg->data.uint);
181 if (ptr_err)
182 goto parse_err;
183
184 arg->type = ARGT_UINT;
185 break;
186
187 /* FIXME: other types need to be implemented here */
188 default:
189 goto parse_err;
190 }
191
192 pos++;
193 arg++;
194
195 /* don't go back to parsing if we reached end */
196 if (!len || pos >= nbarg)
197 break;
198
199 /* skip comma */
200 in++; len--;
201 }
202
203 end_parse:
204 free(word); word = NULL;
205
206 if (pos < min_arg) {
207 /* not enough arguments */
208 if (err_msg)
209 memprintf(err_msg,
210 "Missing arguments (got %d/%d), type '%s' expected",
211 pos, min_arg, arg_type_names[(mask >> (pos * 4)) & 15]);
212 goto err;
213 }
214
215 if (len) {
216 /* too many arguments, starting at <in> */
217 if (err_msg) {
218 /* the caller is responsible for freeing this message */
219 word = my_strndup(in, len);
220 memprintf(err_msg, "End of arguments expected at '%s'", word);
221 free(word); word = NULL;
222 }
223 goto err;
224 }
225
226 /* note that pos might be < nbarg and this is not an error, it's up to the
227 * caller to decide what to do with optional args.
228 */
229 *argp = arg_list;
230
231 if (err_arg)
232 *err_arg = pos;
233 if (err_ptr)
234 *err_ptr = in;
235 return pos;
236
237 parse_err:
238 if (err_msg) {
239 memprintf(err_msg, "Failed to parse '%s' as type '%s'",
240 word, arg_type_names[(mask >> (pos * 4)) & 15]);
241 }
242
243 err:
244 free(word);
245 free(arg_list);
246 if (err_arg)
247 *err_arg = pos;
248 if (err_ptr)
249 *err_ptr = in;
250 return -1;
251}