blob: ce6724049ff72410a805308266af89c7adb352f2 [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++;
211 } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
212 rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR;
213 cur_arg = 1;
214
215 if (!*args[cur_arg] || !*args[cur_arg+1] ||
216 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
217 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
218 file, linenum, args[0]);
219 goto out_err;
220 }
221
222 rule->arg.hdr_add.name = strdup(args[cur_arg]);
223 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
224 LIST_INIT(&rule->arg.hdr_add.fmt);
225
226 proxy->conf.args.ctx = ARGC_HRQ;
227 error = NULL;
228 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
229 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
230 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
231 file, linenum, args[0], error);
232 free(error);
233 goto out_err;
234 }
235 free(proxy->conf.lfs_file);
236 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
237 proxy->conf.lfs_line = proxy->conf.args.line;
238 cur_arg += 2;
239 } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
240 rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
241 cur_arg = 1;
242
243 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
244 (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
245 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 3 arguments.\n",
246 file, linenum, args[0]);
247 goto out_err;
248 }
249
250 rule->arg.hdr_add.name = strdup(args[cur_arg]);
251 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
252 LIST_INIT(&rule->arg.hdr_add.fmt);
253
254 error = NULL;
255 if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) {
256 ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
257 args[cur_arg + 1], error);
258 free(error);
259 goto out_err;
260 }
261
262 proxy->conf.args.ctx = ARGC_HRQ;
263 error = NULL;
264 if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
265 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
266 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
267 file, linenum, args[0], error);
268 free(error);
269 goto out_err;
270 }
271
272 free(proxy->conf.lfs_file);
273 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
274 proxy->conf.lfs_line = proxy->conf.args.line;
275 cur_arg += 3;
276 } else if (strcmp(args[0], "del-header") == 0) {
277 rule->action = ACT_HTTP_DEL_HDR;
278 cur_arg = 1;
279
280 if (!*args[cur_arg] ||
281 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
282 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
283 file, linenum, args[0]);
284 goto out_err;
285 }
286
287 rule->arg.hdr_add.name = strdup(args[cur_arg]);
288 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
289
290 proxy->conf.args.ctx = ARGC_HRQ;
291 free(proxy->conf.lfs_file);
292 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
293 proxy->conf.lfs_line = proxy->conf.args.line;
294 cur_arg += 1;
295 } else if (strncmp(args[0], "track-sc", 8) == 0) {
296 struct sample_expr *expr;
297 unsigned int where;
298 char *err = NULL;
299 unsigned int tsc_num;
300 const char *tsc_num_str;
301
302 cur_arg = 1;
303 proxy->conf.args.ctx = ARGC_TRK;
304
305 tsc_num_str = &args[0][8];
306 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
307 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
308 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
309 free(err);
310 goto out_err;
311 }
312
313 expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
314 if (!expr) {
315 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
316 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
317 free(err);
318 goto out_err;
319 }
320
321 where = 0;
322 if (proxy->cap & PR_CAP_FE)
323 where |= SMP_VAL_FE_HRQ_HDR;
324 if (proxy->cap & PR_CAP_BE)
325 where |= SMP_VAL_BE_HRQ_HDR;
326
327 if (!(expr->fetch->val & where)) {
328 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :"
329 " fetch method '%s' extracts information from '%s', none of which is available here.\n",
330 file, linenum, proxy_type_str(proxy), proxy->id, args[0],
331 args[cur_arg-1], sample_src_names(expr->fetch->use));
332 free(expr);
333 goto out_err;
334 }
335
336 if (strcmp(args[cur_arg], "table") == 0) {
337 cur_arg++;
338 if (!args[cur_arg]) {
339 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing table name.\n",
340 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
341 free(expr);
342 goto out_err;
343 }
344 /* we copy the table name for now, it will be resolved later */
345 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
346 cur_arg++;
347 }
348 rule->arg.trk_ctr.expr = expr;
349 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
350 rule->check_ptr = check_trk_action;
351 } else if (strcmp(args[0], "redirect") == 0) {
352 struct redirect_rule *redir;
353 char *errmsg = NULL;
354
355 if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 0)) == NULL) {
356 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
357 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
358 goto out_err;
359 }
360
361 /* this redirect rule might already contain a parsed condition which
362 * we'll pass to the http-request rule.
363 */
364 rule->action = ACT_HTTP_REDIR;
365 rule->arg.redir = redir;
366 rule->cond = redir->cond;
367 redir->cond = NULL;
368 cur_arg = 2;
369 return rule;
370 } else if (strncmp(args[0], "add-acl", 7) == 0) {
371 /* http-request add-acl(<reference (acl name)>) <key pattern> */
372 rule->action = ACT_HTTP_ADD_ACL;
373 /*
374 * '+ 8' for 'add-acl('
375 * '- 9' for 'add-acl(' + trailing ')'
376 */
377 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
378
379 cur_arg = 1;
380
381 if (!*args[cur_arg] ||
382 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
383 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
384 file, linenum, args[0]);
385 goto out_err;
386 }
387
388 LIST_INIT(&rule->arg.map.key);
389 proxy->conf.args.ctx = ARGC_HRQ;
390 error = NULL;
391 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
392 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
393 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
394 file, linenum, args[0], error);
395 free(error);
396 goto out_err;
397 }
398 free(proxy->conf.lfs_file);
399 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
400 proxy->conf.lfs_line = proxy->conf.args.line;
401 cur_arg += 1;
402 } else if (strncmp(args[0], "del-acl", 7) == 0) {
403 /* http-request del-acl(<reference (acl name)>) <key pattern> */
404 rule->action = ACT_HTTP_DEL_ACL;
405 /*
406 * '+ 8' for 'del-acl('
407 * '- 9' for 'del-acl(' + trailing ')'
408 */
409 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
410
411 cur_arg = 1;
412
413 if (!*args[cur_arg] ||
414 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
415 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
416 file, linenum, args[0]);
417 goto out_err;
418 }
419
420 LIST_INIT(&rule->arg.map.key);
421 proxy->conf.args.ctx = ARGC_HRQ;
422 error = NULL;
423 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
424 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
425 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
426 file, linenum, args[0], error);
427 free(error);
428 goto out_err;
429 }
430 free(proxy->conf.lfs_file);
431 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
432 proxy->conf.lfs_line = proxy->conf.args.line;
433 cur_arg += 1;
434 } else if (strncmp(args[0], "del-map", 7) == 0) {
435 /* http-request del-map(<reference (map name)>) <key pattern> */
436 rule->action = ACT_HTTP_DEL_MAP;
437 /*
438 * '+ 8' for 'del-map('
439 * '- 9' for 'del-map(' + trailing ')'
440 */
441 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
442
443 cur_arg = 1;
444
445 if (!*args[cur_arg] ||
446 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
447 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
448 file, linenum, args[0]);
449 goto out_err;
450 }
451
452 LIST_INIT(&rule->arg.map.key);
453 proxy->conf.args.ctx = ARGC_HRQ;
454 error = NULL;
455 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
456 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
457 ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
458 file, linenum, args[0], error);
459 free(error);
460 goto out_err;
461 }
462 free(proxy->conf.lfs_file);
463 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
464 proxy->conf.lfs_line = proxy->conf.args.line;
465 cur_arg += 1;
466 } else if (strncmp(args[0], "set-map", 7) == 0) {
467 /* http-request set-map(<reference (map name)>) <key pattern> <value pattern> */
468 rule->action = ACT_HTTP_SET_MAP;
469 /*
470 * '+ 8' for 'set-map('
471 * '- 9' for 'set-map(' + trailing ')'
472 */
473 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
474
475 cur_arg = 1;
476
477 if (!*args[cur_arg] || !*args[cur_arg+1] ||
478 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
479 ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
480 file, linenum, args[0]);
481 goto out_err;
482 }
483
484 LIST_INIT(&rule->arg.map.key);
485 LIST_INIT(&rule->arg.map.value);
486 proxy->conf.args.ctx = ARGC_HRQ;
487
488 /* key pattern */
489 error = NULL;
490 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
491 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
492 ha_alert("parsing [%s:%d]: 'http-request %s' key: %s.\n",
493 file, linenum, args[0], error);
494 free(error);
495 goto out_err;
496 }
497
498 /* value pattern */
499 error = NULL;
500 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
501 (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
502 ha_alert("parsing [%s:%d]: 'http-request %s' pattern: %s.\n",
503 file, linenum, args[0], error);
504 free(error);
505 goto out_err;
506 }
507 free(proxy->conf.lfs_file);
508 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
509 proxy->conf.lfs_line = proxy->conf.args.line;
510
511 cur_arg += 2;
512 } else if (((custom = action_http_req_custom(args[0])) != NULL)) {
513 char *errmsg = NULL;
514 cur_arg = 1;
515 /* try in the module list */
516 rule->from = ACT_F_HTTP_REQ;
517 rule->kw = custom;
518 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
519 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
520 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
521 free(errmsg);
522 goto out_err;
523 }
524 } else {
525 action_build_list(&http_req_keywords.list, &trash);
526 ha_alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', "
527 "'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
528 "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
529 "%s%s, but got '%s'%s.\n",
530 file, linenum, *trash.area ? ", " : "", trash.area,
531 args[0], *args[0] ? "" : " (missing argument)");
532 goto out_err;
533 }
534
535 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
536 struct acl_cond *cond;
537 char *errmsg = NULL;
538
539 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
540 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n",
541 file, linenum, args[0], errmsg);
542 free(errmsg);
543 goto out_err;
544 }
545 rule->cond = cond;
546 }
547 else if (*args[cur_arg]) {
548 ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or"
549 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
550 file, linenum, args[0], args[cur_arg]);
551 goto out_err;
552 }
553
554 return rule;
555 out_err:
556 free(rule);
557 return NULL;
558}
559
560/* parse an "http-respose" rule */
561struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
562{
563 struct act_rule *rule;
564 struct action_kw *custom = NULL;
565 int cur_arg;
566 char *error;
567
568 rule = calloc(1, sizeof(*rule));
569 if (!rule) {
570 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
571 goto out_err;
572 }
573
574 if (!strcmp(args[0], "allow")) {
575 rule->action = ACT_ACTION_ALLOW;
576 cur_arg = 1;
577 } else if (!strcmp(args[0], "deny")) {
578 rule->action = ACT_ACTION_DENY;
579 cur_arg = 1;
580 } else if (!strcmp(args[0], "set-nice")) {
581 rule->action = ACT_HTTP_SET_NICE;
582 cur_arg = 1;
583
584 if (!*args[cur_arg] ||
585 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
586 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n",
587 file, linenum, args[0]);
588 goto out_err;
589 }
590 rule->arg.nice = atoi(args[cur_arg]);
591 if (rule->arg.nice < -1024)
592 rule->arg.nice = -1024;
593 else if (rule->arg.nice > 1024)
594 rule->arg.nice = 1024;
595 cur_arg++;
596 } else if (!strcmp(args[0], "set-tos")) {
597#ifdef IP_TOS
598 char *err;
599 rule->action = ACT_HTTP_SET_TOS;
600 cur_arg = 1;
601
602 if (!*args[cur_arg] ||
603 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
604 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
605 file, linenum, args[0]);
606 goto out_err;
607 }
608
609 rule->arg.tos = strtol(args[cur_arg], &err, 0);
610 if (err && *err != '\0') {
611 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
612 file, linenum, err, args[0]);
613 goto out_err;
614 }
615 cur_arg++;
616#else
617 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
618 goto out_err;
619#endif
620 } else if (!strcmp(args[0], "set-mark")) {
621#ifdef SO_MARK
622 char *err;
623 rule->action = ACT_HTTP_SET_MARK;
624 cur_arg = 1;
625
626 if (!*args[cur_arg] ||
627 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
628 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
629 file, linenum, args[0]);
630 goto out_err;
631 }
632
633 rule->arg.mark = strtoul(args[cur_arg], &err, 0);
634 if (err && *err != '\0') {
635 ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
636 file, linenum, err, args[0]);
637 goto out_err;
638 }
639 cur_arg++;
640 global.last_checks |= LSTCHK_NETADM;
641#else
642 ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
643 goto out_err;
644#endif
645 } else if (!strcmp(args[0], "set-log-level")) {
646 rule->action = ACT_HTTP_SET_LOGL;
647 cur_arg = 1;
648
649 if (!*args[cur_arg] ||
650 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
651 bad_log_level:
652 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n",
653 file, linenum, args[0]);
654 goto out_err;
655 }
656 if (strcmp(args[cur_arg], "silent") == 0)
657 rule->arg.loglevel = -1;
658 else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
659 goto bad_log_level;
660 cur_arg++;
661 } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
662 rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR;
663 cur_arg = 1;
664
665 if (!*args[cur_arg] || !*args[cur_arg+1] ||
666 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
667 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
668 file, linenum, args[0]);
669 goto out_err;
670 }
671
672 rule->arg.hdr_add.name = strdup(args[cur_arg]);
673 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
674 LIST_INIT(&rule->arg.hdr_add.fmt);
675
676 proxy->conf.args.ctx = ARGC_HRS;
677 error = NULL;
678 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
679 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
680 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
681 file, linenum, args[0], error);
682 free(error);
683 goto out_err;
684 }
685 free(proxy->conf.lfs_file);
686 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
687 proxy->conf.lfs_line = proxy->conf.args.line;
688 cur_arg += 2;
689 } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
690 rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
691 cur_arg = 1;
692
693 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
694 (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
695 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n",
696 file, linenum, args[0]);
697 goto out_err;
698 }
699
700 rule->arg.hdr_add.name = strdup(args[cur_arg]);
701 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
702 LIST_INIT(&rule->arg.hdr_add.fmt);
703
704 error = NULL;
705 if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) {
706 ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
707 args[cur_arg + 1], error);
708 free(error);
709 goto out_err;
710 }
711
712 proxy->conf.args.ctx = ARGC_HRQ;
713 error = NULL;
714 if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
715 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
716 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
717 file, linenum, args[0], error);
718 free(error);
719 goto out_err;
720 }
721
722 free(proxy->conf.lfs_file);
723 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
724 proxy->conf.lfs_line = proxy->conf.args.line;
725 cur_arg += 3;
726 } else if (strcmp(args[0], "del-header") == 0) {
727 rule->action = ACT_HTTP_DEL_HDR;
728 cur_arg = 1;
729
730 if (!*args[cur_arg] ||
731 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
732 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
733 file, linenum, args[0]);
734 goto out_err;
735 }
736
737 rule->arg.hdr_add.name = strdup(args[cur_arg]);
738 rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
739
740 proxy->conf.args.ctx = ARGC_HRS;
741 free(proxy->conf.lfs_file);
742 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
743 proxy->conf.lfs_line = proxy->conf.args.line;
744 cur_arg += 1;
745 } else if (strncmp(args[0], "add-acl", 7) == 0) {
746 /* http-request add-acl(<reference (acl name)>) <key pattern> */
747 rule->action = ACT_HTTP_ADD_ACL;
748 /*
749 * '+ 8' for 'add-acl('
750 * '- 9' for 'add-acl(' + trailing ')'
751 */
752 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
753
754 cur_arg = 1;
755
756 if (!*args[cur_arg] ||
757 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
758 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
759 file, linenum, args[0]);
760 goto out_err;
761 }
762
763 LIST_INIT(&rule->arg.map.key);
764 proxy->conf.args.ctx = ARGC_HRS;
765 error = NULL;
766 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
767 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
768 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
769 file, linenum, args[0], error);
770 free(error);
771 goto out_err;
772 }
773 free(proxy->conf.lfs_file);
774 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
775 proxy->conf.lfs_line = proxy->conf.args.line;
776
777 cur_arg += 1;
778 } else if (strncmp(args[0], "del-acl", 7) == 0) {
779 /* http-response del-acl(<reference (acl name)>) <key pattern> */
780 rule->action = ACT_HTTP_DEL_ACL;
781 /*
782 * '+ 8' for 'del-acl('
783 * '- 9' for 'del-acl(' + trailing ')'
784 */
785 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
786
787 cur_arg = 1;
788
789 if (!*args[cur_arg] ||
790 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
791 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
792 file, linenum, args[0]);
793 goto out_err;
794 }
795
796 LIST_INIT(&rule->arg.map.key);
797 proxy->conf.args.ctx = ARGC_HRS;
798 error = NULL;
799 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
800 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
801 ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
802 file, linenum, args[0], error);
803 free(error);
804 goto out_err;
805 }
806 free(proxy->conf.lfs_file);
807 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
808 proxy->conf.lfs_line = proxy->conf.args.line;
809 cur_arg += 1;
810 } else if (strncmp(args[0], "del-map", 7) == 0) {
811 /* http-response del-map(<reference (map name)>) <key pattern> */
812 rule->action = ACT_HTTP_DEL_MAP;
813 /*
814 * '+ 8' for 'del-map('
815 * '- 9' for 'del-map(' + trailing ')'
816 */
817 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
818
819 cur_arg = 1;
820
821 if (!*args[cur_arg] ||
822 (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
823 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
824 file, linenum, args[0]);
825 goto out_err;
826 }
827
828 LIST_INIT(&rule->arg.map.key);
829 proxy->conf.args.ctx = ARGC_HRS;
830 error = NULL;
831 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
832 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
833 ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n",
834 file, linenum, args[0], error);
835 free(error);
836 goto out_err;
837 }
838 free(proxy->conf.lfs_file);
839 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
840 proxy->conf.lfs_line = proxy->conf.args.line;
841 cur_arg += 1;
842 } else if (strncmp(args[0], "set-map", 7) == 0) {
843 /* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
844 rule->action = ACT_HTTP_SET_MAP;
845 /*
846 * '+ 8' for 'set-map('
847 * '- 9' for 'set-map(' + trailing ')'
848 */
849 rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
850
851 cur_arg = 1;
852
853 if (!*args[cur_arg] || !*args[cur_arg+1] ||
854 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
855 ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
856 file, linenum, args[0]);
857 goto out_err;
858 }
859
860 LIST_INIT(&rule->arg.map.key);
861 LIST_INIT(&rule->arg.map.value);
862
863 proxy->conf.args.ctx = ARGC_HRS;
864
865 /* key pattern */
866 error = NULL;
867 if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
868 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
869 ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n",
870 file, linenum, args[0], error);
871 free(error);
872 goto out_err;
873 }
874
875 /* value pattern */
876 error = NULL;
877 if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
878 (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
879 ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n",
880 file, linenum, args[0], error);
881 free(error);
882 goto out_err;
883 }
884
885 free(proxy->conf.lfs_file);
886 proxy->conf.lfs_file = strdup(proxy->conf.args.file);
887 proxy->conf.lfs_line = proxy->conf.args.line;
888
889 cur_arg += 2;
890 } else if (strcmp(args[0], "redirect") == 0) {
891 struct redirect_rule *redir;
892 char *errmsg = NULL;
893
894 if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) {
895 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
896 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
897 goto out_err;
898 }
899
900 /* this redirect rule might already contain a parsed condition which
901 * we'll pass to the http-request rule.
902 */
903 rule->action = ACT_HTTP_REDIR;
904 rule->arg.redir = redir;
905 rule->cond = redir->cond;
906 redir->cond = NULL;
907 cur_arg = 2;
908 return rule;
909 } else if (strncmp(args[0], "track-sc", 8) == 0) {
910 struct sample_expr *expr;
911 unsigned int where;
912 char *err = NULL;
913 unsigned int tsc_num;
914 const char *tsc_num_str;
915
916 cur_arg = 1;
917 proxy->conf.args.ctx = ARGC_TRK;
918
919 tsc_num_str = &args[0][8];
920 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
921 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
922 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
923 free(err);
924 goto out_err;
925 }
926
927 expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
928 if (!expr) {
929 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
930 file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
931 free(err);
932 goto out_err;
933 }
934
935 where = 0;
936 if (proxy->cap & PR_CAP_FE)
937 where |= SMP_VAL_FE_HRS_HDR;
938 if (proxy->cap & PR_CAP_BE)
939 where |= SMP_VAL_BE_HRS_HDR;
940
941 if (!(expr->fetch->val & where)) {
942 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule :"
943 " fetch method '%s' extracts information from '%s', none of which is available here.\n",
944 file, linenum, proxy_type_str(proxy), proxy->id, args[0],
945 args[cur_arg-1], sample_src_names(expr->fetch->use));
946 free(expr);
947 goto out_err;
948 }
949
950 if (strcmp(args[cur_arg], "table") == 0) {
951 cur_arg++;
952 if (!args[cur_arg]) {
953 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : missing table name.\n",
954 file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
955 free(expr);
956 goto out_err;
957 }
958 /* we copy the table name for now, it will be resolved later */
959 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
960 cur_arg++;
961 }
962 rule->arg.trk_ctr.expr = expr;
963 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
964 rule->check_ptr = check_trk_action;
965 } else if (((custom = action_http_res_custom(args[0])) != NULL)) {
966 char *errmsg = NULL;
967 cur_arg = 1;
968 /* try in the module list */
969 rule->from = ACT_F_HTTP_RES;
970 rule->kw = custom;
971 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
972 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
973 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
974 free(errmsg);
975 goto out_err;
976 }
977 } else {
978 action_build_list(&http_res_keywords.list, &trash);
979 ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', "
980 "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
981 "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
982 "%s%s, but got '%s'%s.\n",
983 file, linenum, *trash.area ? ", " : "", trash.area,
984 args[0], *args[0] ? "" : " (missing argument)");
985 goto out_err;
986 }
987
988 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
989 struct acl_cond *cond;
990 char *errmsg = NULL;
991
992 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
993 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n",
994 file, linenum, args[0], errmsg);
995 free(errmsg);
996 goto out_err;
997 }
998 rule->cond = cond;
999 }
1000 else if (*args[cur_arg]) {
1001 ha_alert("parsing [%s:%d]: 'http-response %s' expects"
1002 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
1003 file, linenum, args[0], args[cur_arg]);
1004 goto out_err;
1005 }
1006
1007 return rule;
1008 out_err:
1009 free(rule);
1010 return NULL;
1011}
1012
1013/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
1014 * with <err> filled with the error message. If <use_fmt> is not null, builds a
1015 * dynamic log-format rule instead of a static string. Parameter <dir> indicates
1016 * the direction of the rule, and equals 0 for request, non-zero for responses.
1017 */
1018struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
1019 const char **args, char **errmsg, int use_fmt, int dir)
1020{
1021 struct redirect_rule *rule;
1022 int cur_arg;
1023 int type = REDIRECT_TYPE_NONE;
1024 int code = 302;
1025 const char *destination = NULL;
1026 const char *cookie = NULL;
1027 int cookie_set = 0;
1028 unsigned int flags = REDIRECT_FLAG_NONE;
1029 struct acl_cond *cond = NULL;
1030
1031 cur_arg = 0;
1032 while (*(args[cur_arg])) {
1033 if (strcmp(args[cur_arg], "location") == 0) {
1034 if (!*args[cur_arg + 1])
1035 goto missing_arg;
1036
1037 type = REDIRECT_TYPE_LOCATION;
1038 cur_arg++;
1039 destination = args[cur_arg];
1040 }
1041 else if (strcmp(args[cur_arg], "prefix") == 0) {
1042 if (!*args[cur_arg + 1])
1043 goto missing_arg;
1044 type = REDIRECT_TYPE_PREFIX;
1045 cur_arg++;
1046 destination = args[cur_arg];
1047 }
1048 else if (strcmp(args[cur_arg], "scheme") == 0) {
1049 if (!*args[cur_arg + 1])
1050 goto missing_arg;
1051
1052 type = REDIRECT_TYPE_SCHEME;
1053 cur_arg++;
1054 destination = args[cur_arg];
1055 }
1056 else if (strcmp(args[cur_arg], "set-cookie") == 0) {
1057 if (!*args[cur_arg + 1])
1058 goto missing_arg;
1059
1060 cur_arg++;
1061 cookie = args[cur_arg];
1062 cookie_set = 1;
1063 }
1064 else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
1065 if (!*args[cur_arg + 1])
1066 goto missing_arg;
1067
1068 cur_arg++;
1069 cookie = args[cur_arg];
1070 cookie_set = 0;
1071 }
1072 else if (strcmp(args[cur_arg], "code") == 0) {
1073 if (!*args[cur_arg + 1])
1074 goto missing_arg;
1075
1076 cur_arg++;
1077 code = atol(args[cur_arg]);
1078 if (code < 301 || code > 308 || (code > 303 && code < 307)) {
1079 memprintf(errmsg,
1080 "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)",
1081 args[cur_arg - 1], args[cur_arg]);
1082 return NULL;
1083 }
1084 }
1085 else if (!strcmp(args[cur_arg],"drop-query")) {
1086 flags |= REDIRECT_FLAG_DROP_QS;
1087 }
1088 else if (!strcmp(args[cur_arg],"append-slash")) {
1089 flags |= REDIRECT_FLAG_APPEND_SLASH;
1090 }
1091 else if (strcmp(args[cur_arg], "if") == 0 ||
1092 strcmp(args[cur_arg], "unless") == 0) {
1093 cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg);
1094 if (!cond) {
1095 memprintf(errmsg, "error in condition: %s", *errmsg);
1096 return NULL;
1097 }
1098 break;
1099 }
1100 else {
1101 memprintf(errmsg,
1102 "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
1103 args[cur_arg]);
1104 return NULL;
1105 }
1106 cur_arg++;
1107 }
1108
1109 if (type == REDIRECT_TYPE_NONE) {
1110 memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
1111 return NULL;
1112 }
1113
1114 if (dir && type != REDIRECT_TYPE_LOCATION) {
1115 memprintf(errmsg, "response only supports redirect type 'location'");
1116 return NULL;
1117 }
1118
1119 rule = calloc(1, sizeof(*rule));
1120 rule->cond = cond;
1121 LIST_INIT(&rule->rdr_fmt);
1122
1123 if (!use_fmt) {
1124 /* old-style static redirect rule */
1125 rule->rdr_str = strdup(destination);
1126 rule->rdr_len = strlen(destination);
1127 }
1128 else {
1129 /* log-format based redirect rule */
1130
1131 /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
1132 * if prefix == "/", we don't want to add anything, otherwise it
1133 * makes it hard for the user to configure a self-redirection.
1134 */
1135 curproxy->conf.args.ctx = ARGC_RDR;
1136 if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
1137 if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
1138 dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR
1139 : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
1140 errmsg)) {
1141 return NULL;
1142 }
1143 free(curproxy->conf.lfs_file);
1144 curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
1145 curproxy->conf.lfs_line = curproxy->conf.args.line;
1146 }
1147 }
1148
1149 if (cookie) {
1150 /* depending on cookie_set, either we want to set the cookie, or to clear it.
1151 * a clear consists in appending "; path=/; Max-Age=0;" at the end.
1152 */
1153 rule->cookie_len = strlen(cookie);
1154 if (cookie_set) {
1155 rule->cookie_str = malloc(rule->cookie_len + 10);
1156 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1157 memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
1158 rule->cookie_len += 9;
1159 } else {
1160 rule->cookie_str = malloc(rule->cookie_len + 21);
1161 memcpy(rule->cookie_str, cookie, rule->cookie_len);
1162 memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
1163 rule->cookie_len += 20;
1164 }
1165 }
1166 rule->type = type;
1167 rule->code = code;
1168 rule->flags = flags;
1169 LIST_INIT(&rule->list);
1170 return rule;
1171
1172 missing_arg:
1173 memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
1174 return NULL;
1175}
1176
1177void free_http_res_rules(struct list *r)
1178{
1179 struct act_rule *tr, *pr;
1180
1181 list_for_each_entry_safe(pr, tr, r, list) {
1182 LIST_DEL(&pr->list);
1183 regex_free(&pr->arg.hdr_add.re);
1184 free(pr);
1185 }
1186}
1187
1188void free_http_req_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 if (pr->action == ACT_HTTP_REQ_AUTH)
1195 free(pr->arg.auth.realm);
1196
1197 regex_free(&pr->arg.hdr_add.re);
1198 free(pr);
1199 }
1200}
1201
1202__attribute__((constructor))
1203static void __http_rules_init(void)
1204{
1205}
1206
1207/*
1208 * Local variables:
1209 * c-indent-level: 8
1210 * c-basic-offset: 8
1211 * End:
1212 */