1 /*
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" */
41 struct 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" */
46 struct 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  */
action_http_req_custom(const char * kw)53 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  */
action_http_res_custom(const char * kw)61 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 */
parse_http_req_cond(const char ** args,const char * file,int linenum,struct proxy * proxy)67 struct 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 	           strcmp(args[0], "early-hint") == 0) {
213 		char **hdr_name;
214 		int *hdr_name_len;
215 		struct list *fmt;
216 
217 		rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR :
218 		               *args[0] == 's' ? ACT_HTTP_SET_HDR : ACT_HTTP_EARLY_HINT;
219 
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 
224 		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 
233 		*hdr_name = strdup(args[cur_arg]);
234 		*hdr_name_len = strlen(*hdr_name);
235 		LIST_INIT(fmt);
236 
237 		proxy->conf.args.ctx = ARGC_HRQ;
238 		error = NULL;
239 		if (!parse_logformat_string(args[cur_arg + 1], proxy, fmt, LOG_OPT_HTTP,
240 		                            (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 (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 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',"
560 			 " 'deny_status' for 'deny', or"
561 			 " 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 */
parse_http_res_cond(const char ** args,const char * file,int linenum,struct proxy * proxy)573 struct 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;
717 		if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
718 			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  */
http_parse_redirect_rule(const char * file,int linenum,struct proxy * curproxy,const char ** args,char ** errmsg,int use_fmt,int dir)1030 struct 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;
1040 	unsigned int flags = (!dir ? REDIRECT_FLAG_FROM_REQ : REDIRECT_FLAG_NONE);
1041 	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 	if (!rule) {
1133 		memprintf(errmsg, "parsing [%s:%d]: out of memory.", file, linenum);
1134 		return NULL;
1135 	}
1136 	rule->cond = cond;
1137 	LIST_INIT(&rule->rdr_fmt);
1138 
1139 	if (!use_fmt) {
1140 		/* old-style static redirect rule */
1141 		rule->rdr_str = strdup(destination);
1142 		rule->rdr_len = strlen(destination);
1143 	}
1144 	else {
1145 		/* log-format based redirect rule */
1146 
1147 		/* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
1148 		 * if prefix == "/", we don't want to add anything, otherwise it
1149 		 * makes it hard for the user to configure a self-redirection.
1150 		 */
1151 		curproxy->conf.args.ctx = ARGC_RDR;
1152 		if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
1153 			if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
1154 			                            dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR
1155 			                                : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
1156 			                            errmsg)) {
1157 				return  NULL;
1158 			}
1159 			free(curproxy->conf.lfs_file);
1160 			curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
1161 			curproxy->conf.lfs_line = curproxy->conf.args.line;
1162 		}
1163 	}
1164 
1165 	if (cookie) {
1166 		/* depending on cookie_set, either we want to set the cookie, or to clear it.
1167 		 * a clear consists in appending "; path=/; Max-Age=0;" at the end.
1168 		 */
1169 		rule->cookie_len = strlen(cookie);
1170 		if (cookie_set) {
1171 			rule->cookie_str = malloc(rule->cookie_len + 10);
1172 			memcpy(rule->cookie_str, cookie, rule->cookie_len);
1173 			memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
1174 			rule->cookie_len += 9;
1175 		} else {
1176 			rule->cookie_str = malloc(rule->cookie_len + 21);
1177 			memcpy(rule->cookie_str, cookie, rule->cookie_len);
1178 			memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
1179 			rule->cookie_len += 20;
1180 		}
1181 	}
1182 	rule->type = type;
1183 	rule->code = code;
1184 	rule->flags = flags;
1185 	LIST_INIT(&rule->list);
1186 	return rule;
1187 
1188  missing_arg:
1189 	memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
1190 	return NULL;
1191 }
1192 
1193 __attribute__((constructor))
__http_rules_init(void)1194 static void __http_rules_init(void)
1195 {
1196 }
1197 
1198 /*
1199  * Local variables:
1200  *  c-indent-level: 8
1201  *  c-basic-offset: 8
1202  * End:
1203  */
1204