blob: 2ac11fe818bf17020a4c5e5cf1cf3a12f9d32d36 [file] [log] [blame]
Willy Tarreau61c112a2018-10-02 16:43:32 +02001/*
2 * HTTP rules parsing and registration
3 *
4 * Copyright 2000-2018 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
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
19#include <common/cfgparse.h>
20#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
24#include <common/http.h>
25#include <common/memory.h>
26#include <common/standard.h>
27#include <common/version.h>
28
29#include <types/capture.h>
30#include <types/global.h>
31
32#include <proto/acl.h>
33#include <proto/action.h>
34#include <proto/arg.h>
35#include <proto/http_rules.h>
36#include <proto/proto_http.h>
37#include <proto/sample.h>
38
39
40/* List head of all known action keywords for "http-request" */
41struct action_kw_list http_req_keywords = {
42 .list = LIST_HEAD_INIT(http_req_keywords.list)
43};
44
45/* List head of all known action keywords for "http-response" */
46struct action_kw_list http_res_keywords = {
47 .list = LIST_HEAD_INIT(http_res_keywords.list)
48};
49
50/*
51 * Return the struct http_req_action_kw associated to a keyword.
52 */
53static struct action_kw *action_http_req_custom(const char *kw)
54{
55 return action_lookup(&http_req_keywords.list, kw);
56}
57
58/*
59 * Return the struct http_res_action_kw associated to a keyword.
60 */
61static struct action_kw *action_http_res_custom(const char *kw)
62{
63 return action_lookup(&http_res_keywords.list, kw);
64}
65
66/* parse an "http-request" rule */
67struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
68{
69 struct act_rule *rule;
70 struct action_kw *custom = NULL;
71 int cur_arg;
72 char *error;
73
74 rule = calloc(1, sizeof(*rule));
75 if (!rule) {
76 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
77 goto out_err;
78 }
79
80 if (!strcmp(args[0], "allow")) {
81 rule->action = ACT_ACTION_ALLOW;
82 cur_arg = 1;
83 } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block") || !strcmp(args[0], "tarpit")) {
84 int code;
85 int hc;
86
87 if (!strcmp(args[0], "tarpit")) {
88 rule->action = ACT_HTTP_REQ_TARPIT;
89 rule->deny_status = HTTP_ERR_500;
90 }
91 else {
92 rule->action = ACT_ACTION_DENY;
93 rule->deny_status = HTTP_ERR_403;
94 }
95 cur_arg = 1;
96 if (strcmp(args[cur_arg], "deny_status") == 0) {
97 cur_arg++;
98 if (!args[cur_arg]) {
99 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing status code.\n",
100 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
101 goto out_err;
102 }
103
104 code = atol(args[cur_arg]);
105 cur_arg++;
106 for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
107 if (http_err_codes[hc] == code) {
108 rule->deny_status = hc;
109 break;
110 }
111 }
112
113 if (hc >= HTTP_ERR_SIZE) {
114 ha_warning("parsing [%s:%d] : status code %d not handled, using default code %d.\n",
115 file, linenum, code, http_err_codes[rule->deny_status]);
116 }
117 }
118 } else if (!strcmp(args[0], "auth")) {
119 rule->action = ACT_HTTP_REQ_AUTH;
120 cur_arg = 1;
121
122 while(*args[cur_arg]) {
123 if (!strcmp(args[cur_arg], "realm")) {
124 rule->arg.auth.realm = strdup(args[cur_arg + 1]);
125 cur_arg+=2;
126 continue;
127 } else
128 break;
129 }
130 } else if (!strcmp(args[0], "set-nice")) {
131 rule->action = ACT_HTTP_SET_NICE;
132 cur_arg = 1;
133
134 if (!*args[cur_arg] ||
135 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
136 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer value).\n",
137 file, linenum, args[0]);
138 goto out_err;
139 }
140 rule->arg.nice = atoi(args[cur_arg]);
141 if (rule->arg.nice < -1024)
142 rule->arg.nice = -1024;
143 else if (rule->arg.nice > 1024)
144 rule->arg.nice = 1024;
145 cur_arg++;
146 } else if (!strcmp(args[0], "set-tos")) {
147#ifdef IP_TOS
148 char *err;
149 rule->action = ACT_HTTP_SET_TOS;
150 cur_arg = 1;
151
152 if (!*args[cur_arg] ||
153 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
154 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n",
155 file, linenum, args[0]);
156 goto out_err;
157 }
158
159 rule->arg.tos = strtol(args[cur_arg], &err, 0);
160 if (err && *err != '\0') {
161 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n",
162 file, linenum, err, args[0]);
163 goto out_err;
164 }
165 cur_arg++;
166#else
167 ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
168 goto out_err;
169#endif
170 } else if (!strcmp(args[0], "set-mark")) {
171#ifdef SO_MARK
172 char *err;
173 rule->action = ACT_HTTP_SET_MARK;
174 cur_arg = 1;
175
176 if (!*args[cur_arg] ||
177 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
178 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n",
179 file, linenum, args[0]);
180 goto out_err;
181 }
182
183 rule->arg.mark = strtoul(args[cur_arg], &err, 0);
184 if (err && *err != '\0') {
185 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n",
186 file, linenum, err, args[0]);
187 goto out_err;
188 }
189 cur_arg++;
190 global.last_checks |= LSTCHK_NETADM;
191#else
192 ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
193 goto out_err;
194#endif
195 } else if (!strcmp(args[0], "set-log-level")) {
196 rule->action = ACT_HTTP_SET_LOGL;
197 cur_arg = 1;
198
199 if (!*args[cur_arg] ||
200 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
201 bad_log_level:
202 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (log level name or 'silent').\n",
203 file, linenum, args[0]);
204 goto out_err;
205 }
206 if (strcmp(args[cur_arg], "silent") == 0)
207 rule->arg.loglevel = -1;
208 else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
209 goto bad_log_level;
210 cur_arg++;
Frédéric Lécaillea985e382018-11-06 10:55:34 +0100211 } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0 ||
212 strcmp(args[0], "early-hint") == 0) {
Frédéric Lécaille0ebbcb62018-11-06 14:30:19 +0100213 char **hdr_name;
214 int *hdr_name_len;
215 struct list *fmt;
216
Frédéric Lécaillea985e382018-11-06 10:55:34 +0100217 rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR :
218 *args[0] == 's' ? ACT_HTTP_SET_HDR : ACT_HTTP_EARLY_HINT;
Frédéric Lécaille0ebbcb62018-11-06 14:30:19 +0100219
220 hdr_name = *args[0] == 'e' ? &rule->arg.early_hint.name : &rule->arg.hdr_add.name;
221 hdr_name_len = *args[0] == 'e' ? &rule->arg.early_hint.name_len : &rule->arg.hdr_add.name_len;
222 fmt = *args[0] == 'e' ? &rule->arg.early_hint.fmt : &rule->arg.hdr_add.fmt;
223
Willy Tarreau61c112a2018-10-02 16:43:32 +0200224 cur_arg = 1;
225
226 if (!*args[cur_arg] || !*args[cur_arg+1] ||
227 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
228 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
229 file, linenum, args[0]);
230 goto out_err;
231 }
232
Frédéric Lécaille0ebbcb62018-11-06 14:30:19 +0100233 *hdr_name = strdup(args[cur_arg]);
234 *hdr_name_len = strlen(*hdr_name);
235 LIST_INIT(fmt);
Willy Tarreau61c112a2018-10-02 16:43:32 +0200236
237 proxy->conf.args.ctx = ARGC_HRQ;
238 error = NULL;
Frédéric Lécaille0ebbcb62018-11-06 14:30:19 +0100239 if (!parse_logformat_string(args[cur_arg + 1], proxy, fmt, LOG_OPT_HTTP,
Willy Tarreau61c112a2018-10-02 16:43:32 +0200240 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
241 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
242 file, linenum, args[0], error);
243 free(error);
244 goto out_err;
245 }
246 free(proxy->conf.lfs_file);
247 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
248 proxy->conf.lfs_line = proxy->conf.args.line;
249 cur_arg += 2;
250 } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
251 rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
252 cur_arg = 1;
253
254 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
255 (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
256 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 3 arguments.\n",
257 file, linenum, args[0]);
258 goto out_err;
259 }
260
261 rule->arg.hdr_add.name = strdup(args[cur_arg]);
262 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
263 LIST_INIT(&rule->arg.hdr_add.fmt);
264
265 error = NULL;
Dragan Dosen26743032019-04-30 15:54:36 +0200266 if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
Willy Tarreau61c112a2018-10-02 16:43:32 +0200267 ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
268 args[cur_arg + 1], error);
269 free(error);
270 goto out_err;
271 }
272
273 proxy->conf.args.ctx = ARGC_HRQ;
274 error = NULL;
275 if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
276 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
277 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
278 file, linenum, args[0], error);
279 free(error);
280 goto out_err;
281 }
282
283 free(proxy->conf.lfs_file);
284 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
285 proxy->conf.lfs_line = proxy->conf.args.line;
286 cur_arg += 3;
287 } else if (strcmp(args[0], "del-header") == 0) {
288 rule->action = ACT_HTTP_DEL_HDR;
289 cur_arg = 1;
290
291 if (!*args[cur_arg] ||
292 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
293 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
294 file, linenum, args[0]);
295 goto out_err;
296 }
297
298 rule->arg.hdr_add.name = strdup(args[cur_arg]);
299 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
300
301 proxy->conf.args.ctx = ARGC_HRQ;
302 free(proxy->conf.lfs_file);
303 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
304 proxy->conf.lfs_line = proxy->conf.args.line;
305 cur_arg += 1;
306 } else if (strncmp(args[0], "track-sc", 8) == 0) {
307 struct sample_expr *expr;
308 unsigned int where;
309 char *err = NULL;
310 unsigned int tsc_num;
311 const char *tsc_num_str;
312
313 cur_arg = 1;
314 proxy->conf.args.ctx = ARGC_TRK;
315
316 tsc_num_str = &args[0][8];
317 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
318 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
319 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
320 free(err);
321 goto out_err;
322 }
323
324 expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
325 if (!expr) {
326 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
327 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
328 free(err);
329 goto out_err;
330 }
331
332 where = 0;
333 if (proxy->cap & PR_CAP_FE)
334 where |= SMP_VAL_FE_HRQ_HDR;
335 if (proxy->cap & PR_CAP_BE)
336 where |= SMP_VAL_BE_HRQ_HDR;
337
338 if (!(expr->fetch->val & where)) {
339 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :"
340 " fetch method '%s' extracts information from '%s', none of which is available here.\n",
341 file, linenum, proxy_type_str(proxy), proxy->id, args[0],
342 args[cur_arg-1], sample_src_names(expr->fetch->use));
343 free(expr);
344 goto out_err;
345 }
346
347 if (strcmp(args[cur_arg], "table") == 0) {
348 cur_arg++;
349 if (!args[cur_arg]) {
350 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing table name.\n",
351 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
352 free(expr);
353 goto out_err;
354 }
355 /* we copy the table name for now, it will be resolved later */
356 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
357 cur_arg++;
358 }
359 rule->arg.trk_ctr.expr = expr;
360 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
361 rule->check_ptr = check_trk_action;
362 } else if (strcmp(args[0], "redirect") == 0) {
363 struct redirect_rule *redir;
364 char *errmsg = NULL;
365
366 if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 0)) == NULL) {
367 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
368 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
369 goto out_err;
370 }
371
372 /* this redirect rule might already contain a parsed condition which
373 * we'll pass to the http-request rule.
374 */
375 rule->action = ACT_HTTP_REDIR;
376 rule->arg.redir = redir;
377 rule->cond = redir->cond;
378 redir->cond = NULL;
379 cur_arg = 2;
380 return rule;
381 } else if (strncmp(args[0], "add-acl", 7) == 0) {
382 /* http-request add-acl(<reference (acl name)>) <key pattern> */
383 rule->action = ACT_HTTP_ADD_ACL;
384 /*
385 * '+ 8' for 'add-acl('
386 * '- 9' for 'add-acl(' + trailing ')'
387 */
388 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
389
390 cur_arg = 1;
391
392 if (!*args[cur_arg] ||
393 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
394 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
395 file, linenum, args[0]);
396 goto out_err;
397 }
398
399 LIST_INIT(&rule->arg.map.key);
400 proxy->conf.args.ctx = ARGC_HRQ;
401 error = NULL;
402 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
403 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
404 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
405 file, linenum, args[0], error);
406 free(error);
407 goto out_err;
408 }
409 free(proxy->conf.lfs_file);
410 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
411 proxy->conf.lfs_line = proxy->conf.args.line;
412 cur_arg += 1;
413 } else if (strncmp(args[0], "del-acl", 7) == 0) {
414 /* http-request del-acl(<reference (acl name)>) <key pattern> */
415 rule->action = ACT_HTTP_DEL_ACL;
416 /*
417 * '+ 8' for 'del-acl('
418 * '- 9' for 'del-acl(' + trailing ')'
419 */
420 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
421
422 cur_arg = 1;
423
424 if (!*args[cur_arg] ||
425 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
426 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
427 file, linenum, args[0]);
428 goto out_err;
429 }
430
431 LIST_INIT(&rule->arg.map.key);
432 proxy->conf.args.ctx = ARGC_HRQ;
433 error = NULL;
434 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
435 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
436 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
437 file, linenum, args[0], error);
438 free(error);
439 goto out_err;
440 }
441 free(proxy->conf.lfs_file);
442 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
443 proxy->conf.lfs_line = proxy->conf.args.line;
444 cur_arg += 1;
445 } else if (strncmp(args[0], "del-map", 7) == 0) {
446 /* http-request del-map(<reference (map name)>) <key pattern> */
447 rule->action = ACT_HTTP_DEL_MAP;
448 /*
449 * '+ 8' for 'del-map('
450 * '- 9' for 'del-map(' + trailing ')'
451 */
452 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
453
454 cur_arg = 1;
455
456 if (!*args[cur_arg] ||
457 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
458 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
459 file, linenum, args[0]);
460 goto out_err;
461 }
462
463 LIST_INIT(&rule->arg.map.key);
464 proxy->conf.args.ctx = ARGC_HRQ;
465 error = NULL;
466 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
467 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
468 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
469 file, linenum, args[0], error);
470 free(error);
471 goto out_err;
472 }
473 free(proxy->conf.lfs_file);
474 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
475 proxy->conf.lfs_line = proxy->conf.args.line;
476 cur_arg += 1;
477 } else if (strncmp(args[0], "set-map", 7) == 0) {
478 /* http-request set-map(<reference (map name)>) <key pattern> <value pattern> */
479 rule->action = ACT_HTTP_SET_MAP;
480 /*
481 * '+ 8' for 'set-map('
482 * '- 9' for 'set-map(' + trailing ')'
483 */
484 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
485
486 cur_arg = 1;
487
488 if (!*args[cur_arg] || !*args[cur_arg+1] ||
489 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
490 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
491 file, linenum, args[0]);
492 goto out_err;
493 }
494
495 LIST_INIT(&rule->arg.map.key);
496 LIST_INIT(&rule->arg.map.value);
497 proxy->conf.args.ctx = ARGC_HRQ;
498
499 /* key pattern */
500 error = NULL;
501 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
502 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
503 ha_alert("parsing [%s:%d]: 'http-request %s' key: %s.\n",
504 file, linenum, args[0], error);
505 free(error);
506 goto out_err;
507 }
508
509 /* value pattern */
510 error = NULL;
511 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
512 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
513 ha_alert("parsing [%s:%d]: 'http-request %s' pattern: %s.\n",
514 file, linenum, args[0], error);
515 free(error);
516 goto out_err;
517 }
518 free(proxy->conf.lfs_file);
519 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
520 proxy->conf.lfs_line = proxy->conf.args.line;
521
522 cur_arg += 2;
523 } else if (((custom = action_http_req_custom(args[0])) != NULL)) {
524 char *errmsg = NULL;
525 cur_arg = 1;
526 /* try in the module list */
527 rule->from = ACT_F_HTTP_REQ;
528 rule->kw = custom;
529 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
530 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
531 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
532 free(errmsg);
533 goto out_err;
534 }
535 } else {
536 action_build_list(&http_req_keywords.list, &trash);
537 ha_alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', "
538 "'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
539 "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
540 "%s%s, but got '%s'%s.\n",
541 file, linenum, *trash.area ? ", " : "", trash.area,
542 args[0], *args[0] ? "" : " (missing argument)");
543 goto out_err;
544 }
545
546 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
547 struct acl_cond *cond;
548 char *errmsg = NULL;
549
550 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
551 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n",
552 file, linenum, args[0], errmsg);
553 free(errmsg);
554 goto out_err;
555 }
556 rule->cond = cond;
557 }
558 else if (*args[cur_arg]) {
Willy Tarreau5abdc762019-06-11 16:01:56 +0200559 ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth',"
560 " 'deny_status' for 'deny', or"
Willy Tarreau61c112a2018-10-02 16:43:32 +0200561 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
562 file, linenum, args[0], args[cur_arg]);
563 goto out_err;
564 }
565
566 return rule;
567 out_err:
568 free(rule);
569 return NULL;
570}
571
572/* parse an "http-respose" rule */
573struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
574{
575 struct act_rule *rule;
576 struct action_kw *custom = NULL;
577 int cur_arg;
578 char *error;
579
580 rule = calloc(1, sizeof(*rule));
581 if (!rule) {
582 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
583 goto out_err;
584 }
585
586 if (!strcmp(args[0], "allow")) {
587 rule->action = ACT_ACTION_ALLOW;
588 cur_arg = 1;
589 } else if (!strcmp(args[0], "deny")) {
590 rule->action = ACT_ACTION_DENY;
591 cur_arg = 1;
592 } else if (!strcmp(args[0], "set-nice")) {
593 rule->action = ACT_HTTP_SET_NICE;
594 cur_arg = 1;
595
596 if (!*args[cur_arg] ||
597 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
598 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n",
599 file, linenum, args[0]);
600 goto out_err;
601 }
602 rule->arg.nice = atoi(args[cur_arg]);
603 if (rule->arg.nice < -1024)
604 rule->arg.nice = -1024;
605 else if (rule->arg.nice > 1024)
606 rule->arg.nice = 1024;
607 cur_arg++;
608 } else if (!strcmp(args[0], "set-tos")) {
609#ifdef IP_TOS
610 char *err;
611 rule->action = ACT_HTTP_SET_TOS;
612 cur_arg = 1;
613
614 if (!*args[cur_arg] ||
615 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
616 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
617 file, linenum, args[0]);
618 goto out_err;
619 }
620
621 rule->arg.tos = strtol(args[cur_arg], &err, 0);
622 if (err && *err != '\0') {
623 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
624 file, linenum, err, args[0]);
625 goto out_err;
626 }
627 cur_arg++;
628#else
629 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
630 goto out_err;
631#endif
632 } else if (!strcmp(args[0], "set-mark")) {
633#ifdef SO_MARK
634 char *err;
635 rule->action = ACT_HTTP_SET_MARK;
636 cur_arg = 1;
637
638 if (!*args[cur_arg] ||
639 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
640 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
641 file, linenum, args[0]);
642 goto out_err;
643 }
644
645 rule->arg.mark = strtoul(args[cur_arg], &err, 0);
646 if (err && *err != '\0') {
647 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
648 file, linenum, err, args[0]);
649 goto out_err;
650 }
651 cur_arg++;
652 global.last_checks |= LSTCHK_NETADM;
653#else
654 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
655 goto out_err;
656#endif
657 } else if (!strcmp(args[0], "set-log-level")) {
658 rule->action = ACT_HTTP_SET_LOGL;
659 cur_arg = 1;
660
661 if (!*args[cur_arg] ||
662 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
663 bad_log_level:
664 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n",
665 file, linenum, args[0]);
666 goto out_err;
667 }
668 if (strcmp(args[cur_arg], "silent") == 0)
669 rule->arg.loglevel = -1;
670 else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
671 goto bad_log_level;
672 cur_arg++;
673 } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
674 rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR;
675 cur_arg = 1;
676
677 if (!*args[cur_arg] || !*args[cur_arg+1] ||
678 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
679 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
680 file, linenum, args[0]);
681 goto out_err;
682 }
683
684 rule->arg.hdr_add.name = strdup(args[cur_arg]);
685 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
686 LIST_INIT(&rule->arg.hdr_add.fmt);
687
688 proxy->conf.args.ctx = ARGC_HRS;
689 error = NULL;
690 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
691 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
692 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
693 file, linenum, args[0], error);
694 free(error);
695 goto out_err;
696 }
697 free(proxy->conf.lfs_file);
698 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
699 proxy->conf.lfs_line = proxy->conf.args.line;
700 cur_arg += 2;
701 } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
702 rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
703 cur_arg = 1;
704
705 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
706 (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
707 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n",
708 file, linenum, args[0]);
709 goto out_err;
710 }
711
712 rule->arg.hdr_add.name = strdup(args[cur_arg]);
713 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
714 LIST_INIT(&rule->arg.hdr_add.fmt);
715
716 error = NULL;
Dragan Dosen26743032019-04-30 15:54:36 +0200717 if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
Willy Tarreau61c112a2018-10-02 16:43:32 +0200718 ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
719 args[cur_arg + 1], error);
720 free(error);
721 goto out_err;
722 }
723
724 proxy->conf.args.ctx = ARGC_HRQ;
725 error = NULL;
726 if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
727 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
728 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
729 file, linenum, args[0], error);
730 free(error);
731 goto out_err;
732 }
733
734 free(proxy->conf.lfs_file);
735 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
736 proxy->conf.lfs_line = proxy->conf.args.line;
737 cur_arg += 3;
738 } else if (strcmp(args[0], "del-header") == 0) {
739 rule->action = ACT_HTTP_DEL_HDR;
740 cur_arg = 1;
741
742 if (!*args[cur_arg] ||
743 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
744 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
745 file, linenum, args[0]);
746 goto out_err;
747 }
748
749 rule->arg.hdr_add.name = strdup(args[cur_arg]);
750 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
751
752 proxy->conf.args.ctx = ARGC_HRS;
753 free(proxy->conf.lfs_file);
754 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
755 proxy->conf.lfs_line = proxy->conf.args.line;
756 cur_arg += 1;
757 } else if (strncmp(args[0], "add-acl", 7) == 0) {
758 /* http-request add-acl(<reference (acl name)>) <key pattern> */
759 rule->action = ACT_HTTP_ADD_ACL;
760 /*
761 * '+ 8' for 'add-acl('
762 * '- 9' for 'add-acl(' + trailing ')'
763 */
764 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
765
766 cur_arg = 1;
767
768 if (!*args[cur_arg] ||
769 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
770 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
771 file, linenum, args[0]);
772 goto out_err;
773 }
774
775 LIST_INIT(&rule->arg.map.key);
776 proxy->conf.args.ctx = ARGC_HRS;
777 error = NULL;
778 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
779 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
780 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
781 file, linenum, args[0], error);
782 free(error);
783 goto out_err;
784 }
785 free(proxy->conf.lfs_file);
786 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
787 proxy->conf.lfs_line = proxy->conf.args.line;
788
789 cur_arg += 1;
790 } else if (strncmp(args[0], "del-acl", 7) == 0) {
791 /* http-response del-acl(<reference (acl name)>) <key pattern> */
792 rule->action = ACT_HTTP_DEL_ACL;
793 /*
794 * '+ 8' for 'del-acl('
795 * '- 9' for 'del-acl(' + trailing ')'
796 */
797 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
798
799 cur_arg = 1;
800
801 if (!*args[cur_arg] ||
802 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
803 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
804 file, linenum, args[0]);
805 goto out_err;
806 }
807
808 LIST_INIT(&rule->arg.map.key);
809 proxy->conf.args.ctx = ARGC_HRS;
810 error = NULL;
811 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
812 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
813 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
814 file, linenum, args[0], error);
815 free(error);
816 goto out_err;
817 }
818 free(proxy->conf.lfs_file);
819 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
820 proxy->conf.lfs_line = proxy->conf.args.line;
821 cur_arg += 1;
822 } else if (strncmp(args[0], "del-map", 7) == 0) {
823 /* http-response del-map(<reference (map name)>) <key pattern> */
824 rule->action = ACT_HTTP_DEL_MAP;
825 /*
826 * '+ 8' for 'del-map('
827 * '- 9' for 'del-map(' + trailing ')'
828 */
829 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
830
831 cur_arg = 1;
832
833 if (!*args[cur_arg] ||
834 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
835 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
836 file, linenum, args[0]);
837 goto out_err;
838 }
839
840 LIST_INIT(&rule->arg.map.key);
841 proxy->conf.args.ctx = ARGC_HRS;
842 error = NULL;
843 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
844 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
845 ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n",
846 file, linenum, args[0], error);
847 free(error);
848 goto out_err;
849 }
850 free(proxy->conf.lfs_file);
851 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
852 proxy->conf.lfs_line = proxy->conf.args.line;
853 cur_arg += 1;
854 } else if (strncmp(args[0], "set-map", 7) == 0) {
855 /* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
856 rule->action = ACT_HTTP_SET_MAP;
857 /*
858 * '+ 8' for 'set-map('
859 * '- 9' for 'set-map(' + trailing ')'
860 */
861 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
862
863 cur_arg = 1;
864
865 if (!*args[cur_arg] || !*args[cur_arg+1] ||
866 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
867 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
868 file, linenum, args[0]);
869 goto out_err;
870 }
871
872 LIST_INIT(&rule->arg.map.key);
873 LIST_INIT(&rule->arg.map.value);
874
875 proxy->conf.args.ctx = ARGC_HRS;
876
877 /* key pattern */
878 error = NULL;
879 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
880 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
881 ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n",
882 file, linenum, args[0], error);
883 free(error);
884 goto out_err;
885 }
886
887 /* value pattern */
888 error = NULL;
889 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
890 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
891 ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n",
892 file, linenum, args[0], error);
893 free(error);
894 goto out_err;
895 }
896
897 free(proxy->conf.lfs_file);
898 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
899 proxy->conf.lfs_line = proxy->conf.args.line;
900
901 cur_arg += 2;
902 } else if (strcmp(args[0], "redirect") == 0) {
903 struct redirect_rule *redir;
904 char *errmsg = NULL;
905
906 if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) {
907 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
908 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
909 goto out_err;
910 }
911
912 /* this redirect rule might already contain a parsed condition which
913 * we'll pass to the http-request rule.
914 */
915 rule->action = ACT_HTTP_REDIR;
916 rule->arg.redir = redir;
917 rule->cond = redir->cond;
918 redir->cond = NULL;
919 cur_arg = 2;
920 return rule;
921 } else if (strncmp(args[0], "track-sc", 8) == 0) {
922 struct sample_expr *expr;
923 unsigned int where;
924 char *err = NULL;
925 unsigned int tsc_num;
926 const char *tsc_num_str;
927
928 cur_arg = 1;
929 proxy->conf.args.ctx = ARGC_TRK;
930
931 tsc_num_str = &args[0][8];
932 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
933 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
934 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
935 free(err);
936 goto out_err;
937 }
938
939 expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
940 if (!expr) {
941 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
942 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
943 free(err);
944 goto out_err;
945 }
946
947 where = 0;
948 if (proxy->cap & PR_CAP_FE)
949 where |= SMP_VAL_FE_HRS_HDR;
950 if (proxy->cap & PR_CAP_BE)
951 where |= SMP_VAL_BE_HRS_HDR;
952
953 if (!(expr->fetch->val & where)) {
954 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule :"
955 " fetch method '%s' extracts information from '%s', none of which is available here.\n",
956 file, linenum, proxy_type_str(proxy), proxy->id, args[0],
957 args[cur_arg-1], sample_src_names(expr->fetch->use));
958 free(expr);
959 goto out_err;
960 }
961
962 if (strcmp(args[cur_arg], "table") == 0) {
963 cur_arg++;
964 if (!args[cur_arg]) {
965 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : missing table name.\n",
966 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
967 free(expr);
968 goto out_err;
969 }
970 /* we copy the table name for now, it will be resolved later */
971 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
972 cur_arg++;
973 }
974 rule->arg.trk_ctr.expr = expr;
975 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
976 rule->check_ptr = check_trk_action;
977 } else if (((custom = action_http_res_custom(args[0])) != NULL)) {
978 char *errmsg = NULL;
979 cur_arg = 1;
980 /* try in the module list */
981 rule->from = ACT_F_HTTP_RES;
982 rule->kw = custom;
983 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
984 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
985 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
986 free(errmsg);
987 goto out_err;
988 }
989 } else {
990 action_build_list(&http_res_keywords.list, &trash);
991 ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', "
992 "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
993 "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
994 "%s%s, but got '%s'%s.\n",
995 file, linenum, *trash.area ? ", " : "", trash.area,
996 args[0], *args[0] ? "" : " (missing argument)");
997 goto out_err;
998 }
999
1000 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
1001 struct acl_cond *cond;
1002 char *errmsg = NULL;
1003
1004 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
1005 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n",
1006 file, linenum, args[0], errmsg);
1007 free(errmsg);
1008 goto out_err;
1009 }
1010 rule->cond = cond;
1011 }
1012 else if (*args[cur_arg]) {
1013 ha_alert("parsing [%s:%d]: 'http-response %s' expects"
1014 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
1015 file, linenum, args[0], args[cur_arg]);
1016 goto out_err;
1017 }
1018
1019 return rule;
1020 out_err:
1021 free(rule);
1022 return NULL;
1023}
1024
1025/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
1026 * with <err> filled with the error message. If <use_fmt> is not null, builds a
1027 * dynamic log-format rule instead of a static string. Parameter <dir> indicates
1028 * the direction of the rule, and equals 0 for request, non-zero for responses.
1029 */
1030struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
1031 const char **args, char **errmsg, int use_fmt, int dir)
1032{
1033 struct redirect_rule *rule;
1034 int cur_arg;
1035 int type = REDIRECT_TYPE_NONE;
1036 int code = 302;
1037 const char *destination = NULL;
1038 const char *cookie = NULL;
1039 int cookie_set = 0;
Christopher Fauletcba5a882020-01-28 09:13:41 +01001040 unsigned int flags = (!dir ? REDIRECT_FLAG_FROM_REQ : REDIRECT_FLAG_NONE);
Willy Tarreau61c112a2018-10-02 16:43:32 +02001041 struct acl_cond *cond = NULL;
1042
1043 cur_arg = 0;
1044 while (*(args[cur_arg])) {
1045 if (strcmp(args[cur_arg], "location") == 0) {
1046 if (!*args[cur_arg + 1])
1047 goto missing_arg;
1048
1049 type = REDIRECT_TYPE_LOCATION;
1050 cur_arg++;
1051 destination = args[cur_arg];
1052 }
1053 else if (strcmp(args[cur_arg], "prefix") == 0) {
1054 if (!*args[cur_arg + 1])
1055 goto missing_arg;
1056 type = REDIRECT_TYPE_PREFIX;
1057 cur_arg++;
1058 destination = args[cur_arg];
1059 }
1060 else if (strcmp(args[cur_arg], "scheme") == 0) {
1061 if (!*args[cur_arg + 1])
1062 goto missing_arg;
1063
1064 type = REDIRECT_TYPE_SCHEME;
1065 cur_arg++;
1066 destination = args[cur_arg];
1067 }
1068 else if (strcmp(args[cur_arg], "set-cookie") == 0) {
1069 if (!*args[cur_arg + 1])
1070 goto missing_arg;
1071
1072 cur_arg++;
1073 cookie = args[cur_arg];
1074 cookie_set = 1;
1075 }
1076 else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
1077 if (!*args[cur_arg + 1])
1078 goto missing_arg;
1079
1080 cur_arg++;
1081 cookie = args[cur_arg];
1082 cookie_set = 0;
1083 }
1084 else if (strcmp(args[cur_arg], "code") == 0) {
1085 if (!*args[cur_arg + 1])
1086 goto missing_arg;
1087
1088 cur_arg++;
1089 code = atol(args[cur_arg]);
1090 if (code < 301 || code > 308 || (code > 303 && code < 307)) {
1091 memprintf(errmsg,
1092 "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)",
1093 args[cur_arg - 1], args[cur_arg]);
1094 return NULL;
1095 }
1096 }
1097 else if (!strcmp(args[cur_arg],"drop-query")) {
1098 flags |= REDIRECT_FLAG_DROP_QS;
1099 }
1100 else if (!strcmp(args[cur_arg],"append-slash")) {
1101 flags |= REDIRECT_FLAG_APPEND_SLASH;
1102 }
1103 else if (strcmp(args[cur_arg], "if") == 0 ||
1104 strcmp(args[cur_arg], "unless") == 0) {
1105 cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg);
1106 if (!cond) {
1107 memprintf(errmsg, "error in condition: %s", *errmsg);
1108 return NULL;
1109 }
1110 break;
1111 }
1112 else {
1113 memprintf(errmsg,
1114 "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
1115 args[cur_arg]);
1116 return NULL;
1117 }
1118 cur_arg++;
1119 }
1120
1121 if (type == REDIRECT_TYPE_NONE) {
1122 memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
1123 return NULL;
1124 }
1125
1126 if (dir && type != REDIRECT_TYPE_LOCATION) {
1127 memprintf(errmsg, "response only supports redirect type 'location'");
1128 return NULL;
1129 }
1130
1131 rule = calloc(1, sizeof(*rule));
1132 rule->cond = cond;
1133 LIST_INIT(&rule->rdr_fmt);
1134
1135 if (!use_fmt) {
1136 /* old-style static redirect rule */
1137 rule->rdr_str = strdup(destination);
1138 rule->rdr_len = strlen(destination);
1139 }
1140 else {
1141 /* log-format based redirect rule */
1142
1143 /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
1144 * if prefix == "/", we don't want to add anything, otherwise it
1145 * makes it hard for the user to configure a self-redirection.
1146 */
1147 curproxy->conf.args.ctx = ARGC_RDR;
1148 if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
1149 if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
1150 dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR
1151 : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
1152 errmsg)) {
1153 return NULL;
1154 }
1155 free(curproxy->conf.lfs_file);
1156 curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
1157 curproxy->conf.lfs_line = curproxy->conf.args.line;
1158 }
1159 }
1160
1161 if (cookie) {
1162 /* depending on cookie_set, either we want to set the cookie, or to clear it.
1163 * a clear consists in appending "; path=/; Max-Age=0;" at the end.
1164 */
1165 rule->cookie_len = strlen(cookie);
1166 if (cookie_set) {
1167 rule->cookie_str = malloc(rule->cookie_len + 10);
1168 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1169 memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
1170 rule->cookie_len += 9;
1171 } else {
1172 rule->cookie_str = malloc(rule->cookie_len + 21);
1173 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1174 memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
1175 rule->cookie_len += 20;
1176 }
1177 }
1178 rule->type = type;
1179 rule->code = code;
1180 rule->flags = flags;
1181 LIST_INIT(&rule->list);
1182 return rule;
1183
1184 missing_arg:
1185 memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
1186 return NULL;
1187}
1188
Willy Tarreau61c112a2018-10-02 16:43:32 +02001189__attribute__((constructor))
1190static void __http_rules_init(void)
1191{
1192}
1193
1194/*
1195 * Local variables:
1196 * c-indent-level: 8
1197 * c-basic-offset: 8
1198 * End:
1199 */