blob: 89036ada3faf6b4a8acca5941511e3c5c4a87972 [file] [log] [blame]
Willy Tarreau66243b42021-07-16 15:39:28 +02001/*
2 * Configuration condition preprocessor
3 *
4 * Copyright 2000-2021 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 <haproxy/api.h>
14#include <haproxy/arg.h>
15#include <haproxy/cfgcond.h>
16#include <haproxy/global.h>
17#include <haproxy/tools.h>
18
19/* supported condition predicates */
20const struct cond_pred_kw cond_predicates[] = {
Remi Tricot-Le Bretonb01179a2021-10-11 15:34:12 +020021 { "defined", CFG_PRED_DEFINED, ARG1(1, STR) },
22 { "feature", CFG_PRED_FEATURE, ARG1(1, STR) },
23 { "streq", CFG_PRED_STREQ, ARG2(2, STR, STR) },
24 { "strneq", CFG_PRED_STRNEQ, ARG2(2, STR, STR) },
Christopher Fauleta1fdad72023-02-20 17:55:58 +010025 { "strstr", CFG_PRED_STRSTR, ARG2(2, STR, STR) },
Remi Tricot-Le Bretonb01179a2021-10-11 15:34:12 +020026 { "version_atleast", CFG_PRED_VERSION_ATLEAST, ARG1(1, STR) },
27 { "version_before", CFG_PRED_VERSION_BEFORE, ARG1(1, STR) },
28 { "openssl_version_atleast", CFG_PRED_OSSL_VERSION_ATLEAST, ARG1(1, STR) },
29 { "openssl_version_before", CFG_PRED_OSSL_VERSION_BEFORE, ARG1(1, STR) },
30 { "ssllib_name_startswith", CFG_PRED_SSLLIB_NAME_STARTSWITH, ARG1(1, STR) },
Christopher Fauletc13f3022023-02-21 11:16:08 +010031 { "enabled", CFG_PRED_ENABLED, ARG1(1, STR) },
Willy Tarreau66243b42021-07-16 15:39:28 +020032 { NULL, CFG_PRED_NONE, 0 }
33};
34
35/* looks up a cond predicate matching the keyword in <str>, possibly followed
36 * by a parenthesis. Returns a pointer to it or NULL if not found.
37 */
38const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
39{
40 const struct cond_pred_kw *ret;
41 int len = strcspn(str, " (");
42
43 for (ret = &cond_predicates[0]; ret->word; ret++) {
44 if (len != strlen(ret->word))
45 continue;
46 if (strncmp(str, ret->word, len) != 0)
47 continue;
48 return ret;
49 }
50 return NULL;
51}
52
Willy Tarreau087b2d02021-07-16 14:27:20 +020053/* Frees <term> and its args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +020054void cfg_free_cond_term(struct cfg_cond_term *term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020055{
Willy Tarreauf1db20c2021-07-17 18:46:30 +020056 if (!term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020057 return;
58
Willy Tarreauf1db20c2021-07-17 18:46:30 +020059 if (term->type == CCTT_PAREN) {
60 cfg_free_cond_expr(term->expr);
61 term->expr = NULL;
62 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +020063
Willy Tarreauf1db20c2021-07-17 18:46:30 +020064 free_args(term->args);
65 free(term->args);
66 free(term);
Willy Tarreau087b2d02021-07-16 14:27:20 +020067}
68
Willy Tarreauf8690952021-07-16 12:12:00 +020069/* Parse an indirect input text as a possible config condition term.
70 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
Willy Tarreau087b2d02021-07-16 14:27:20 +020071 * success. <term> is allocated and filled with the parsed info, and <text>
72 * is updated on success to point to the first unparsed character, or is left
73 * untouched on failure. On success, the caller must free <term> using
74 * cfg_free_cond_term(). An error will be set in <err> on error, and only
Willy Tarreauf8690952021-07-16 12:12:00 +020075 * in this case. In this case the first bad character will be reported in
Willy Tarreaudc70c182021-07-20 17:58:34 +020076 * <errptr>. <maxdepth> corresponds to the maximum recursion depth permitted,
77 * it is decremented on each recursive call and the parsing will fail one
78 * reaching <= 0.
Willy Tarreau66243b42021-07-16 15:39:28 +020079 */
Willy Tarreaudc70c182021-07-20 17:58:34 +020080int cfg_parse_cond_term(const char **text, struct cfg_cond_term **term, char **err, const char **errptr, int maxdepth)
Willy Tarreau66243b42021-07-16 15:39:28 +020081{
Willy Tarreau087b2d02021-07-16 14:27:20 +020082 struct cfg_cond_term *t;
Willy Tarreauf8690952021-07-16 12:12:00 +020083 const char *in = *text;
Willy Tarreau66243b42021-07-16 15:39:28 +020084 const char *end_ptr;
Willy Tarreau66243b42021-07-16 15:39:28 +020085 int err_arg;
86 int nbargs;
Willy Tarreau66243b42021-07-16 15:39:28 +020087 char *end;
88 long val;
89
Willy Tarreauf8690952021-07-16 12:12:00 +020090 while (*in == ' ' || *in == '\t')
91 in++;
92
93 if (!*in) /* empty term does not parse */
Willy Tarreau66243b42021-07-16 15:39:28 +020094 return 0;
95
Willy Tarreaudc70c182021-07-20 17:58:34 +020096 *term = NULL;
97 if (maxdepth <= 0)
98 goto fail0;
99
Willy Tarreau087b2d02021-07-16 14:27:20 +0200100 t = *term = calloc(1, sizeof(**term));
101 if (!t) {
102 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
103 goto fail1;
104 }
105
106 t->type = CCTT_NONE;
107 t->args = NULL;
108 t->neg = 0;
109
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200110 /* !<term> negates the term. White spaces permitted */
111 while (*in == '!') {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200112 t->neg = !t->neg;
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200113 do { in++; } while (*in == ' ' || *in == '\t');
114 }
115
Willy Tarreauf8690952021-07-16 12:12:00 +0200116 val = strtol(in, &end, 0);
117 if (end != in) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200118 t->type = val ? CCTT_TRUE : CCTT_FALSE;
Willy Tarreauf8690952021-07-16 12:12:00 +0200119 *text = end;
120 return 1;
121 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200122
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200123 /* Try to parse '(' EXPR ')' */
124 if (*in == '(') {
125 int ret;
126
127 t->type = CCTT_PAREN;
128 t->args = NULL;
129
130 do { in++; } while (*in == ' ' || *in == '\t');
Willy Tarreaudc70c182021-07-20 17:58:34 +0200131 ret = cfg_parse_cond_expr(&in, &t->expr, err, errptr, maxdepth - 1);
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200132 if (ret == -1)
133 goto fail2;
134 if (ret == 0)
135 goto fail0;
136
137 /* find the closing ')' */
138 while (*in == ' ' || *in == '\t')
139 in++;
140 if (*in != ')') {
141 memprintf(err, "expected ')' after conditional expression '%s'", *text);
142 goto fail1;
143 }
144 do { in++; } while (*in == ' ' || *in == '\t');
145 *text = in;
146 return 1;
147 }
148
Willy Tarreau66243b42021-07-16 15:39:28 +0200149 /* below we'll likely all make_arg_list() so we must return only via
150 * the <done> label which frees the arg list.
151 */
Willy Tarreau087b2d02021-07-16 14:27:20 +0200152 t->pred = cfg_lookup_cond_pred(in);
153 if (t->pred) {
154 t->type = CCTT_PRED;
155 nbargs = make_arg_list(in + strlen(t->pred->word), -1,
156 t->pred->arg_mask, &t->args, err,
Willy Tarreau66243b42021-07-16 15:39:28 +0200157 &end_ptr, &err_arg, NULL);
Willy Tarreau66243b42021-07-16 15:39:28 +0200158 if (nbargs < 0) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200159 memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, t->pred->word);
Willy Tarreau66243b42021-07-16 15:39:28 +0200160 if (errptr)
161 *errptr = end_ptr;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200162 goto fail2;
Willy Tarreau66243b42021-07-16 15:39:28 +0200163 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200164 *text = end_ptr;
165 return 1;
166 }
167
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200168 fail0:
Willy Tarreauf8690952021-07-16 12:12:00 +0200169 memprintf(err, "unparsable conditional expression '%s'", *text);
Willy Tarreau087b2d02021-07-16 14:27:20 +0200170 fail1:
Willy Tarreauf8690952021-07-16 12:12:00 +0200171 if (errptr)
172 *errptr = *text;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200173 fail2:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200174 cfg_free_cond_term(*term);
175 *term = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200176 return -1;
177}
178
Christopher Fauletc13f3022023-02-21 11:16:08 +0100179/* evaluate a "enabled" expression. Only a subset of options are matched. It
180 * returns 1 if the option is enabled. 0 is returned is the option is not
181 * enabled or if it is not recognized.
182 */
183static int cfg_eval_cond_enabled(const char *str)
184{
185 if (strcmp(str, "POLL") == 0)
186 return !!(global.tune.options & GTUNE_USE_POLL);
187 else if (strcmp(str, "EPOLL") == 0)
188 return !!(global.tune.options & GTUNE_USE_EPOLL);
189 else if (strcmp(str, "KQUEUE") == 0)
190 return !!(global.tune.options & GTUNE_USE_EPOLL);
191 else if (strcmp(str, "EVPORTS") == 0)
192 return !!(global.tune.options & GTUNE_USE_EVPORTS);
193 else if (strcmp(str, "SPLICE") == 0)
194 return !!(global.tune.options & GTUNE_USE_SPLICE);
195 else if (strcmp(str, "GETADDRINFO") == 0)
196 return !!(global.tune.options & GTUNE_USE_GAI);
197 else if (strcmp(str, "REUSEPORT") == 0)
198 return !!(global.tune.options & GTUNE_USE_REUSEPORT);
199 else if (strcmp(str, "FAST-FORWARD") == 0)
200 return !!(global.tune.options & GTUNE_USE_FAST_FWD);
201 else if (strcmp(str, "SERVER-SSL-VERIFY-NONE") == 0)
202 return !!(global.ssl_server_verify == SSL_SERVER_VERIFY_NONE);
203 return 0;
204}
205
Willy Tarreauf8690952021-07-16 12:12:00 +0200206/* evaluate a condition term on a .if/.elif line. The condition was already
207 * parsed in <term>. Returns -1 on error (in which case err is filled with a
208 * message, and only in this case), 0 if the condition is false, 1 if it's
209 * true.
210 */
211int cfg_eval_cond_term(const struct cfg_cond_term *term, char **err)
212{
213 int ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200214
Willy Tarreauf8690952021-07-16 12:12:00 +0200215 if (term->type == CCTT_FALSE)
216 ret = 0;
217 else if (term->type == CCTT_TRUE)
218 ret = 1;
219 else if (term->type == CCTT_PRED) {
220 /* here we know we have a valid predicate with valid arguments
221 * placed in term->args (which the caller will free).
Willy Tarreau66243b42021-07-16 15:39:28 +0200222 */
Willy Tarreauf8690952021-07-16 12:12:00 +0200223 switch (term->pred->prd) {
Willy Tarreau66243b42021-07-16 15:39:28 +0200224 case CFG_PRED_DEFINED: // checks if arg exists as an environment variable
Willy Tarreauf8690952021-07-16 12:12:00 +0200225 ret = getenv(term->args[0].data.str.area) != NULL;
226 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200227
228 case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
229 const char *p;
230
Willy Tarreauf8690952021-07-16 12:12:00 +0200231 ret = 0; // assume feature not found
232 for (p = build_features; (p = strstr(p, term->args[0].data.str.area)); p++) {
233 if (p > build_features &&
234 (p[term->args[0].data.str.data] == ' ' ||
235 p[term->args[0].data.str.data] == 0)) {
236 if (*(p-1) == '+') { // e.g. "+OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200237 ret = 1;
Willy Tarreauf8690952021-07-16 12:12:00 +0200238 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200239 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200240 else if (*(p-1) == '-') { // e.g. "-OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200241 ret = 0;
Willy Tarreauf8690952021-07-16 12:12:00 +0200242 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200243 }
244 /* it was a sub-word, let's restart from next place */
245 }
246 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200247 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200248 }
249 case CFG_PRED_STREQ: // checks if the two arg are equal
Willy Tarreauf8690952021-07-16 12:12:00 +0200250 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) == 0;
251 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200252
253 case CFG_PRED_STRNEQ: // checks if the two arg are different
Willy Tarreauf8690952021-07-16 12:12:00 +0200254 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) != 0;
255 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200256
Christopher Fauleta1fdad72023-02-20 17:55:58 +0100257 case CFG_PRED_STRSTR: // checks if the 2nd arg is found in the first one
258 ret = strstr(term->args[0].data.str.area, term->args[1].data.str.area) != NULL;
259 break;
260
Willy Tarreau66243b42021-07-16 15:39:28 +0200261 case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200262 ret = compare_current_version(term->args[0].data.str.area) <= 0;
263 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200264
265 case CFG_PRED_VERSION_BEFORE: // checks if the current version is older than this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200266 ret = compare_current_version(term->args[0].data.str.area) > 0;
267 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200268
William Lallemand3aeb3f92021-08-21 23:59:56 +0200269 case CFG_PRED_OSSL_VERSION_ATLEAST: { // checks if the current openssl version is at least this one
270 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
271
272 if (opensslret < -1) /* can't parse the string or no openssl available */
273 ret = -1;
274 else
275 ret = opensslret <= 0;
276 break;
277 }
278 case CFG_PRED_OSSL_VERSION_BEFORE: { // checks if the current openssl version is older than this one
279 int opensslret = openssl_compare_current_version(term->args[0].data.str.area);
280
281 if (opensslret < -1) /* can't parse the string or no openssl available */
282 ret = -1;
283 else
284 ret = opensslret > 0;
285 break;
286 }
Remi Tricot-Le Bretonb01179a2021-10-11 15:34:12 +0200287 case CFG_PRED_SSLLIB_NAME_STARTSWITH: { // checks if the current SSL library's name starts with a specified string (can be used to distinguish OpenSSL from LibreSSL or BoringSSL)
288 ret = openssl_compare_current_name(term->args[0].data.str.area) == 0;
289 break;
290 }
Christopher Fauletc13f3022023-02-21 11:16:08 +0100291 case CFG_PRED_ENABLED: { // checks if the arg matches on a subset of enabled options
292 ret = cfg_eval_cond_enabled(term->args[0].data.str.area) != 0;
293 break;
294 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200295 default:
Willy Tarreauf8690952021-07-16 12:12:00 +0200296 memprintf(err, "internal error: unhandled conditional expression predicate '%s'", term->pred->word);
297 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200298 }
299 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200300 else if (term->type == CCTT_PAREN) {
301 ret = cfg_eval_cond_expr(term->expr, err);
302 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200303 else {
304 memprintf(err, "internal error: unhandled condition term type %d", (int)term->type);
305 }
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200306
307 if (ret >= 0 && term->neg)
308 ret = !ret;
Willy Tarreauf8690952021-07-16 12:12:00 +0200309 return ret;
310}
311
312
Willy Tarreauca818872021-07-16 14:46:09 +0200313/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200314void cfg_free_cond_and(struct cfg_cond_and *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200315{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200316 struct cfg_cond_and *prev;
317
318 while (expr) {
319 cfg_free_cond_term(expr->left);
320 prev = expr;
321 expr = expr->right;
322 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200323 }
324}
325
326/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200327void cfg_free_cond_expr(struct cfg_cond_expr *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200328{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200329 struct cfg_cond_expr *prev;
330
331 while (expr) {
332 cfg_free_cond_and(expr->left);
333 prev = expr;
334 expr = expr->right;
335 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200336 }
337}
338
339/* Parse an indirect input text as a possible config condition sub-expr.
340 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
341 * success. <expr> is filled with the parsed info, and <text> is updated on
342 * success to point to the first unparsed character, or is left untouched
343 * on failure. On success, the caller will have to free all lower-level
344 * allocated structs using cfg_free_cond_expr(). An error will be set in
345 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200346 * character will be reported in <errptr>. <maxdepth> corresponds to the
347 * maximum recursion depth permitted, it is decremented on each recursive
348 * call and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200349 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200350int cfg_parse_cond_and(const char **text, struct cfg_cond_and **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200351{
352 struct cfg_cond_and *e;
353 const char *in = *text;
354 int ret = -1;
355
356 if (!*in) /* empty expr does not parse */
357 return 0;
358
Willy Tarreaudc70c182021-07-20 17:58:34 +0200359 *expr = NULL;
360 if (maxdepth <= 0) {
361 memprintf(err, "unparsable conditional sub-expression '%s'", in);
362 if (errptr)
363 *errptr = in;
364 goto done;
365 }
366
Willy Tarreauca818872021-07-16 14:46:09 +0200367 e = *expr = calloc(1, sizeof(**expr));
368 if (!e) {
369 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
370 goto done;
371 }
372
Willy Tarreaudc70c182021-07-20 17:58:34 +0200373 ret = cfg_parse_cond_term(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200374 if (ret == -1) // parse error, error already reported
375 goto done;
376
377 if (ret == 0) {
378 /* ret == 0, no other way to parse this */
379 memprintf(err, "unparsable conditional sub-expression '%s'", in);
380 if (errptr)
381 *errptr = in;
382 ret = -1;
383 goto done;
384 }
385
386 /* ret=1, we have a term in the left hand set */
387
Ilya Shipitsin01881082021-08-07 14:41:56 +0500388 /* find an optional '&&' */
Willy Tarreauca818872021-07-16 14:46:09 +0200389 while (*in == ' ' || *in == '\t')
390 in++;
391
392 *text = in;
393 if (in[0] != '&' || in[1] != '&')
394 goto done;
395
396 /* we have a '&&', let's parse the right handset's subexp */
397 in += 2;
398 while (*in == ' ' || *in == '\t')
399 in++;
400
Willy Tarreaudc70c182021-07-20 17:58:34 +0200401 ret = cfg_parse_cond_and(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200402 if (ret > 0)
403 *text = in;
404 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200405 if (ret < 0) {
406 cfg_free_cond_and(*expr);
407 *expr = NULL;
408 }
Willy Tarreauca818872021-07-16 14:46:09 +0200409 return ret;
410}
411
412/* Parse an indirect input text as a possible config condition term.
413 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
414 * success. <expr> is filled with the parsed info, and <text> is updated on
415 * success to point to the first unparsed character, or is left untouched
416 * on failure. On success, the caller will have to free all lower-level
417 * allocated structs using cfg_free_cond_expr(). An error will be set in
418 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200419 * character will be reported in <errptr>. <maxdepth> corresponds to the
420 * maximum recursion depth permitted, it is decremented on each recursive call
421 * and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200422 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200423int cfg_parse_cond_expr(const char **text, struct cfg_cond_expr **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200424{
425 struct cfg_cond_expr *e;
426 const char *in = *text;
427 int ret = -1;
428
429 if (!*in) /* empty expr does not parse */
430 return 0;
431
Willy Tarreaudc70c182021-07-20 17:58:34 +0200432 *expr = NULL;
433 if (maxdepth <= 0) {
434 memprintf(err, "unparsable conditional expression '%s'", in);
435 if (errptr)
436 *errptr = in;
437 goto done;
438 }
439
Willy Tarreauca818872021-07-16 14:46:09 +0200440 e = *expr = calloc(1, sizeof(**expr));
441 if (!e) {
442 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
443 goto done;
444 }
445
Willy Tarreaudc70c182021-07-20 17:58:34 +0200446 ret = cfg_parse_cond_and(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200447 if (ret == -1) // parse error, error already reported
448 goto done;
449
450 if (ret == 0) {
451 /* ret == 0, no other way to parse this */
452 memprintf(err, "unparsable conditional expression '%s'", in);
453 if (errptr)
454 *errptr = in;
455 ret = -1;
456 goto done;
457 }
458
459 /* ret=1, we have a sub-expr in the left hand set */
460
Ilya Shipitsin01881082021-08-07 14:41:56 +0500461 /* find an optional '||' */
Willy Tarreauca818872021-07-16 14:46:09 +0200462 while (*in == ' ' || *in == '\t')
463 in++;
464
465 *text = in;
466 if (in[0] != '|' || in[1] != '|')
467 goto done;
468
469 /* we have a '||', let's parse the right handset's subexp */
470 in += 2;
471 while (*in == ' ' || *in == '\t')
472 in++;
473
Willy Tarreaudc70c182021-07-20 17:58:34 +0200474 ret = cfg_parse_cond_expr(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200475 if (ret > 0)
476 *text = in;
477 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200478 if (ret < 0) {
479 cfg_free_cond_expr(*expr);
480 *expr = NULL;
481 }
Willy Tarreauca818872021-07-16 14:46:09 +0200482 return ret;
483}
484
485/* evaluate an sub-expression on a .if/.elif line. The expression is valid and
486 * was already parsed in <expr>. Returns -1 on error (in which case err is
487 * filled with a message, and only in this case), 0 if the condition is false,
488 * 1 if it's true.
489 */
490int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err)
491{
492 int ret;
493
494 /* AND: loop on terms and sub-exp's terms as long as they're TRUE
495 * (stop on FALSE and ERROR).
496 */
497 while ((ret = cfg_eval_cond_term(expr->left, err)) > 0 && expr->right)
498 expr = expr->right;
499 return ret;
500}
501
502/* evaluate an expression on a .if/.elif line. The expression is valid and was
503 * already parsed in <expr>. Returns -1 on error (in which case err is filled
504 * with a message, and only in this case), 0 if the condition is false, 1 if
505 * it's true.
506 */
507int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err)
508{
509 int ret;
510
511 /* OR: loop on sub-exps as long as they're FALSE (stop on TRUE and ERROR) */
512 while ((ret = cfg_eval_cond_and(expr->left, err)) == 0 && expr->right)
513 expr = expr->right;
514 return ret;
515}
516
Willy Tarreauf8690952021-07-16 12:12:00 +0200517/* evaluate a condition on a .if/.elif line. The condition is already tokenized
518 * in <err>. Returns -1 on error (in which case err is filled with a message,
519 * and only in this case), 0 if the condition is false, 1 if it's true. If
520 * <errptr> is not NULL, it's set to the first invalid character on error.
521 */
522int cfg_eval_condition(char **args, char **err, const char **errptr)
523{
Willy Tarreauca818872021-07-16 14:46:09 +0200524 struct cfg_cond_expr *expr = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200525 const char *text = args[0];
526 int ret = -1;
527
528 if (!*text) /* note: empty = false */
529 return 0;
530
Willy Tarreaudc70c182021-07-20 17:58:34 +0200531 ret = cfg_parse_cond_expr(&text, &expr, err, errptr, MAX_CFG_RECURSION);
Willy Tarreauf8690952021-07-16 12:12:00 +0200532 if (ret != 0) {
533 if (ret == -1) // parse error, error already reported
534 goto done;
Willy Tarreau379ceea2021-07-16 16:18:03 +0200535 while (*text == ' ' || *text == '\t')
536 text++;
537
538 if (*text) {
539 ret = -1;
540 memprintf(err, "unexpected character '%c' at the end of conditional expression '%s'",
541 *text, args[0]);
542 goto fail;
543 }
544
Willy Tarreauca818872021-07-16 14:46:09 +0200545 ret = cfg_eval_cond_expr(expr, err);
Willy Tarreauf8690952021-07-16 12:12:00 +0200546 goto done;
547 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200548
Willy Tarreauf8690952021-07-16 12:12:00 +0200549 /* ret == 0, no other way to parse this */
550 ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200551 memprintf(err, "unparsable conditional expression '%s'", args[0]);
Willy Tarreau379ceea2021-07-16 16:18:03 +0200552 fail:
Willy Tarreau66243b42021-07-16 15:39:28 +0200553 if (errptr)
Willy Tarreauf8690952021-07-16 12:12:00 +0200554 *errptr = text;
Willy Tarreau66243b42021-07-16 15:39:28 +0200555 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200556 cfg_free_cond_expr(expr);
Willy Tarreau66243b42021-07-16 15:39:28 +0200557 return ret;
558}