blob: c7b5a71e71740c4b3430f39465f57731e8291e97 [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;
266 if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) {
267 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]) {
559 ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or"
560 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
561 file, linenum, args[0], args[cur_arg]);
562 goto out_err;
563 }
564
565 return rule;
566 out_err:
567 free(rule);
568 return NULL;
569}
570
571/* parse an "http-respose" rule */
572struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
573{
574 struct act_rule *rule;
575 struct action_kw *custom = NULL;
576 int cur_arg;
577 char *error;
578
579 rule = calloc(1, sizeof(*rule));
580 if (!rule) {
581 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
582 goto out_err;
583 }
584
585 if (!strcmp(args[0], "allow")) {
586 rule->action = ACT_ACTION_ALLOW;
587 cur_arg = 1;
588 } else if (!strcmp(args[0], "deny")) {
589 rule->action = ACT_ACTION_DENY;
590 cur_arg = 1;
591 } else if (!strcmp(args[0], "set-nice")) {
592 rule->action = ACT_HTTP_SET_NICE;
593 cur_arg = 1;
594
595 if (!*args[cur_arg] ||
596 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
597 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n",
598 file, linenum, args[0]);
599 goto out_err;
600 }
601 rule->arg.nice = atoi(args[cur_arg]);
602 if (rule->arg.nice < -1024)
603 rule->arg.nice = -1024;
604 else if (rule->arg.nice > 1024)
605 rule->arg.nice = 1024;
606 cur_arg++;
607 } else if (!strcmp(args[0], "set-tos")) {
608#ifdef IP_TOS
609 char *err;
610 rule->action = ACT_HTTP_SET_TOS;
611 cur_arg = 1;
612
613 if (!*args[cur_arg] ||
614 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
615 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
616 file, linenum, args[0]);
617 goto out_err;
618 }
619
620 rule->arg.tos = strtol(args[cur_arg], &err, 0);
621 if (err && *err != '\0') {
622 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
623 file, linenum, err, args[0]);
624 goto out_err;
625 }
626 cur_arg++;
627#else
628 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
629 goto out_err;
630#endif
631 } else if (!strcmp(args[0], "set-mark")) {
632#ifdef SO_MARK
633 char *err;
634 rule->action = ACT_HTTP_SET_MARK;
635 cur_arg = 1;
636
637 if (!*args[cur_arg] ||
638 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
639 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
640 file, linenum, args[0]);
641 goto out_err;
642 }
643
644 rule->arg.mark = strtoul(args[cur_arg], &err, 0);
645 if (err && *err != '\0') {
646 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
647 file, linenum, err, args[0]);
648 goto out_err;
649 }
650 cur_arg++;
651 global.last_checks |= LSTCHK_NETADM;
652#else
653 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
654 goto out_err;
655#endif
656 } else if (!strcmp(args[0], "set-log-level")) {
657 rule->action = ACT_HTTP_SET_LOGL;
658 cur_arg = 1;
659
660 if (!*args[cur_arg] ||
661 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
662 bad_log_level:
663 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n",
664 file, linenum, args[0]);
665 goto out_err;
666 }
667 if (strcmp(args[cur_arg], "silent") == 0)
668 rule->arg.loglevel = -1;
669 else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
670 goto bad_log_level;
671 cur_arg++;
672 } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
673 rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR;
674 cur_arg = 1;
675
676 if (!*args[cur_arg] || !*args[cur_arg+1] ||
677 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
678 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
679 file, linenum, args[0]);
680 goto out_err;
681 }
682
683 rule->arg.hdr_add.name = strdup(args[cur_arg]);
684 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
685 LIST_INIT(&rule->arg.hdr_add.fmt);
686
687 proxy->conf.args.ctx = ARGC_HRS;
688 error = NULL;
689 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
690 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
691 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
692 file, linenum, args[0], error);
693 free(error);
694 goto out_err;
695 }
696 free(proxy->conf.lfs_file);
697 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
698 proxy->conf.lfs_line = proxy->conf.args.line;
699 cur_arg += 2;
700 } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
701 rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
702 cur_arg = 1;
703
704 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
705 (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
706 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n",
707 file, linenum, args[0]);
708 goto out_err;
709 }
710
711 rule->arg.hdr_add.name = strdup(args[cur_arg]);
712 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
713 LIST_INIT(&rule->arg.hdr_add.fmt);
714
715 error = NULL;
716 if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) {
717 ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
718 args[cur_arg + 1], error);
719 free(error);
720 goto out_err;
721 }
722
723 proxy->conf.args.ctx = ARGC_HRQ;
724 error = NULL;
725 if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
726 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
727 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
728 file, linenum, args[0], error);
729 free(error);
730 goto out_err;
731 }
732
733 free(proxy->conf.lfs_file);
734 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
735 proxy->conf.lfs_line = proxy->conf.args.line;
736 cur_arg += 3;
737 } else if (strcmp(args[0], "del-header") == 0) {
738 rule->action = ACT_HTTP_DEL_HDR;
739 cur_arg = 1;
740
741 if (!*args[cur_arg] ||
742 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
743 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
744 file, linenum, args[0]);
745 goto out_err;
746 }
747
748 rule->arg.hdr_add.name = strdup(args[cur_arg]);
749 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
750
751 proxy->conf.args.ctx = ARGC_HRS;
752 free(proxy->conf.lfs_file);
753 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
754 proxy->conf.lfs_line = proxy->conf.args.line;
755 cur_arg += 1;
756 } else if (strncmp(args[0], "add-acl", 7) == 0) {
757 /* http-request add-acl(<reference (acl name)>) <key pattern> */
758 rule->action = ACT_HTTP_ADD_ACL;
759 /*
760 * '+ 8' for 'add-acl('
761 * '- 9' for 'add-acl(' + trailing ')'
762 */
763 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
764
765 cur_arg = 1;
766
767 if (!*args[cur_arg] ||
768 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
769 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
770 file, linenum, args[0]);
771 goto out_err;
772 }
773
774 LIST_INIT(&rule->arg.map.key);
775 proxy->conf.args.ctx = ARGC_HRS;
776 error = NULL;
777 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
778 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
779 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
780 file, linenum, args[0], error);
781 free(error);
782 goto out_err;
783 }
784 free(proxy->conf.lfs_file);
785 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
786 proxy->conf.lfs_line = proxy->conf.args.line;
787
788 cur_arg += 1;
789 } else if (strncmp(args[0], "del-acl", 7) == 0) {
790 /* http-response del-acl(<reference (acl name)>) <key pattern> */
791 rule->action = ACT_HTTP_DEL_ACL;
792 /*
793 * '+ 8' for 'del-acl('
794 * '- 9' for 'del-acl(' + trailing ')'
795 */
796 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
797
798 cur_arg = 1;
799
800 if (!*args[cur_arg] ||
801 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
802 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
803 file, linenum, args[0]);
804 goto out_err;
805 }
806
807 LIST_INIT(&rule->arg.map.key);
808 proxy->conf.args.ctx = ARGC_HRS;
809 error = NULL;
810 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
811 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
812 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
813 file, linenum, args[0], error);
814 free(error);
815 goto out_err;
816 }
817 free(proxy->conf.lfs_file);
818 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
819 proxy->conf.lfs_line = proxy->conf.args.line;
820 cur_arg += 1;
821 } else if (strncmp(args[0], "del-map", 7) == 0) {
822 /* http-response del-map(<reference (map name)>) <key pattern> */
823 rule->action = ACT_HTTP_DEL_MAP;
824 /*
825 * '+ 8' for 'del-map('
826 * '- 9' for 'del-map(' + trailing ')'
827 */
828 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
829
830 cur_arg = 1;
831
832 if (!*args[cur_arg] ||
833 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
834 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
835 file, linenum, args[0]);
836 goto out_err;
837 }
838
839 LIST_INIT(&rule->arg.map.key);
840 proxy->conf.args.ctx = ARGC_HRS;
841 error = NULL;
842 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
843 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
844 ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n",
845 file, linenum, args[0], error);
846 free(error);
847 goto out_err;
848 }
849 free(proxy->conf.lfs_file);
850 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
851 proxy->conf.lfs_line = proxy->conf.args.line;
852 cur_arg += 1;
853 } else if (strncmp(args[0], "set-map", 7) == 0) {
854 /* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
855 rule->action = ACT_HTTP_SET_MAP;
856 /*
857 * '+ 8' for 'set-map('
858 * '- 9' for 'set-map(' + trailing ')'
859 */
860 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
861
862 cur_arg = 1;
863
864 if (!*args[cur_arg] || !*args[cur_arg+1] ||
865 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
866 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
867 file, linenum, args[0]);
868 goto out_err;
869 }
870
871 LIST_INIT(&rule->arg.map.key);
872 LIST_INIT(&rule->arg.map.value);
873
874 proxy->conf.args.ctx = ARGC_HRS;
875
876 /* key pattern */
877 error = NULL;
878 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
879 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
880 ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n",
881 file, linenum, args[0], error);
882 free(error);
883 goto out_err;
884 }
885
886 /* value pattern */
887 error = NULL;
888 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
889 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
890 ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n",
891 file, linenum, args[0], error);
892 free(error);
893 goto out_err;
894 }
895
896 free(proxy->conf.lfs_file);
897 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
898 proxy->conf.lfs_line = proxy->conf.args.line;
899
900 cur_arg += 2;
901 } else if (strcmp(args[0], "redirect") == 0) {
902 struct redirect_rule *redir;
903 char *errmsg = NULL;
904
905 if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) {
906 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
907 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
908 goto out_err;
909 }
910
911 /* this redirect rule might already contain a parsed condition which
912 * we'll pass to the http-request rule.
913 */
914 rule->action = ACT_HTTP_REDIR;
915 rule->arg.redir = redir;
916 rule->cond = redir->cond;
917 redir->cond = NULL;
918 cur_arg = 2;
919 return rule;
920 } else if (strncmp(args[0], "track-sc", 8) == 0) {
921 struct sample_expr *expr;
922 unsigned int where;
923 char *err = NULL;
924 unsigned int tsc_num;
925 const char *tsc_num_str;
926
927 cur_arg = 1;
928 proxy->conf.args.ctx = ARGC_TRK;
929
930 tsc_num_str = &args[0][8];
931 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
932 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
933 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
934 free(err);
935 goto out_err;
936 }
937
938 expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
939 if (!expr) {
940 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
941 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
942 free(err);
943 goto out_err;
944 }
945
946 where = 0;
947 if (proxy->cap & PR_CAP_FE)
948 where |= SMP_VAL_FE_HRS_HDR;
949 if (proxy->cap & PR_CAP_BE)
950 where |= SMP_VAL_BE_HRS_HDR;
951
952 if (!(expr->fetch->val & where)) {
953 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule :"
954 " fetch method '%s' extracts information from '%s', none of which is available here.\n",
955 file, linenum, proxy_type_str(proxy), proxy->id, args[0],
956 args[cur_arg-1], sample_src_names(expr->fetch->use));
957 free(expr);
958 goto out_err;
959 }
960
961 if (strcmp(args[cur_arg], "table") == 0) {
962 cur_arg++;
963 if (!args[cur_arg]) {
964 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : missing table name.\n",
965 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
966 free(expr);
967 goto out_err;
968 }
969 /* we copy the table name for now, it will be resolved later */
970 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
971 cur_arg++;
972 }
973 rule->arg.trk_ctr.expr = expr;
974 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
975 rule->check_ptr = check_trk_action;
976 } else if (((custom = action_http_res_custom(args[0])) != NULL)) {
977 char *errmsg = NULL;
978 cur_arg = 1;
979 /* try in the module list */
980 rule->from = ACT_F_HTTP_RES;
981 rule->kw = custom;
982 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
983 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
984 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
985 free(errmsg);
986 goto out_err;
987 }
988 } else {
989 action_build_list(&http_res_keywords.list, &trash);
990 ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', "
991 "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
992 "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
993 "%s%s, but got '%s'%s.\n",
994 file, linenum, *trash.area ? ", " : "", trash.area,
995 args[0], *args[0] ? "" : " (missing argument)");
996 goto out_err;
997 }
998
999 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
1000 struct acl_cond *cond;
1001 char *errmsg = NULL;
1002
1003 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
1004 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n",
1005 file, linenum, args[0], errmsg);
1006 free(errmsg);
1007 goto out_err;
1008 }
1009 rule->cond = cond;
1010 }
1011 else if (*args[cur_arg]) {
1012 ha_alert("parsing [%s:%d]: 'http-response %s' expects"
1013 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
1014 file, linenum, args[0], args[cur_arg]);
1015 goto out_err;
1016 }
1017
1018 return rule;
1019 out_err:
1020 free(rule);
1021 return NULL;
1022}
1023
1024/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
1025 * with <err> filled with the error message. If <use_fmt> is not null, builds a
1026 * dynamic log-format rule instead of a static string. Parameter <dir> indicates
1027 * the direction of the rule, and equals 0 for request, non-zero for responses.
1028 */
1029struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
1030 const char **args, char **errmsg, int use_fmt, int dir)
1031{
1032 struct redirect_rule *rule;
1033 int cur_arg;
1034 int type = REDIRECT_TYPE_NONE;
1035 int code = 302;
1036 const char *destination = NULL;
1037 const char *cookie = NULL;
1038 int cookie_set = 0;
1039 unsigned int flags = REDIRECT_FLAG_NONE;
1040 struct acl_cond *cond = NULL;
1041
1042 cur_arg = 0;
1043 while (*(args[cur_arg])) {
1044 if (strcmp(args[cur_arg], "location") == 0) {
1045 if (!*args[cur_arg + 1])
1046 goto missing_arg;
1047
1048 type = REDIRECT_TYPE_LOCATION;
1049 cur_arg++;
1050 destination = args[cur_arg];
1051 }
1052 else if (strcmp(args[cur_arg], "prefix") == 0) {
1053 if (!*args[cur_arg + 1])
1054 goto missing_arg;
1055 type = REDIRECT_TYPE_PREFIX;
1056 cur_arg++;
1057 destination = args[cur_arg];
1058 }
1059 else if (strcmp(args[cur_arg], "scheme") == 0) {
1060 if (!*args[cur_arg + 1])
1061 goto missing_arg;
1062
1063 type = REDIRECT_TYPE_SCHEME;
1064 cur_arg++;
1065 destination = args[cur_arg];
1066 }
1067 else if (strcmp(args[cur_arg], "set-cookie") == 0) {
1068 if (!*args[cur_arg + 1])
1069 goto missing_arg;
1070
1071 cur_arg++;
1072 cookie = args[cur_arg];
1073 cookie_set = 1;
1074 }
1075 else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
1076 if (!*args[cur_arg + 1])
1077 goto missing_arg;
1078
1079 cur_arg++;
1080 cookie = args[cur_arg];
1081 cookie_set = 0;
1082 }
1083 else if (strcmp(args[cur_arg], "code") == 0) {
1084 if (!*args[cur_arg + 1])
1085 goto missing_arg;
1086
1087 cur_arg++;
1088 code = atol(args[cur_arg]);
1089 if (code < 301 || code > 308 || (code > 303 && code < 307)) {
1090 memprintf(errmsg,
1091 "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)",
1092 args[cur_arg - 1], args[cur_arg]);
1093 return NULL;
1094 }
1095 }
1096 else if (!strcmp(args[cur_arg],"drop-query")) {
1097 flags |= REDIRECT_FLAG_DROP_QS;
1098 }
1099 else if (!strcmp(args[cur_arg],"append-slash")) {
1100 flags |= REDIRECT_FLAG_APPEND_SLASH;
1101 }
1102 else if (strcmp(args[cur_arg], "if") == 0 ||
1103 strcmp(args[cur_arg], "unless") == 0) {
1104 cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg);
1105 if (!cond) {
1106 memprintf(errmsg, "error in condition: %s", *errmsg);
1107 return NULL;
1108 }
1109 break;
1110 }
1111 else {
1112 memprintf(errmsg,
1113 "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
1114 args[cur_arg]);
1115 return NULL;
1116 }
1117 cur_arg++;
1118 }
1119
1120 if (type == REDIRECT_TYPE_NONE) {
1121 memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
1122 return NULL;
1123 }
1124
1125 if (dir && type != REDIRECT_TYPE_LOCATION) {
1126 memprintf(errmsg, "response only supports redirect type 'location'");
1127 return NULL;
1128 }
1129
1130 rule = calloc(1, sizeof(*rule));
1131 rule->cond = cond;
1132 LIST_INIT(&rule->rdr_fmt);
1133
1134 if (!use_fmt) {
1135 /* old-style static redirect rule */
1136 rule->rdr_str = strdup(destination);
1137 rule->rdr_len = strlen(destination);
1138 }
1139 else {
1140 /* log-format based redirect rule */
1141
1142 /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
1143 * if prefix == "/", we don't want to add anything, otherwise it
1144 * makes it hard for the user to configure a self-redirection.
1145 */
1146 curproxy->conf.args.ctx = ARGC_RDR;
1147 if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
1148 if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
1149 dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR
1150 : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
1151 errmsg)) {
1152 return NULL;
1153 }
1154 free(curproxy->conf.lfs_file);
1155 curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
1156 curproxy->conf.lfs_line = curproxy->conf.args.line;
1157 }
1158 }
1159
1160 if (cookie) {
1161 /* depending on cookie_set, either we want to set the cookie, or to clear it.
1162 * a clear consists in appending "; path=/; Max-Age=0;" at the end.
1163 */
1164 rule->cookie_len = strlen(cookie);
1165 if (cookie_set) {
1166 rule->cookie_str = malloc(rule->cookie_len + 10);
1167 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1168 memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
1169 rule->cookie_len += 9;
1170 } else {
1171 rule->cookie_str = malloc(rule->cookie_len + 21);
1172 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1173 memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
1174 rule->cookie_len += 20;
1175 }
1176 }
1177 rule->type = type;
1178 rule->code = code;
1179 rule->flags = flags;
1180 LIST_INIT(&rule->list);
1181 return rule;
1182
1183 missing_arg:
1184 memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
1185 return NULL;
1186}
1187
1188void free_http_res_rules(struct list *r)
1189{
1190 struct act_rule *tr, *pr;
1191
1192 list_for_each_entry_safe(pr, tr, r, list) {
1193 LIST_DEL(&pr->list);
1194 regex_free(&pr->arg.hdr_add.re);
1195 free(pr);
1196 }
1197}
1198
1199void free_http_req_rules(struct list *r)
1200{
1201 struct act_rule *tr, *pr;
1202
1203 list_for_each_entry_safe(pr, tr, r, list) {
1204 LIST_DEL(&pr->list);
1205 if (pr->action == ACT_HTTP_REQ_AUTH)
1206 free(pr->arg.auth.realm);
1207
1208 regex_free(&pr->arg.hdr_add.re);
1209 free(pr);
1210 }
1211}
1212
1213__attribute__((constructor))
1214static void __http_rules_init(void)
1215{
1216}
1217
1218/*
1219 * Local variables:
1220 * c-indent-level: 8
1221 * c-basic-offset: 8
1222 * End:
1223 */