/src/haproxy/src/http_rules.c
Line | Count | Source |
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 <haproxy/acl.h> |
20 | | #include <haproxy/action.h> |
21 | | #include <haproxy/api.h> |
22 | | #include <haproxy/arg.h> |
23 | | #include <haproxy/capture-t.h> |
24 | | #include <haproxy/cfgparse.h> |
25 | | #include <haproxy/chunk.h> |
26 | | #include <haproxy/global.h> |
27 | | #include <haproxy/http.h> |
28 | | #include <haproxy/http_ana-t.h> |
29 | | #include <haproxy/http_rules.h> |
30 | | #include <haproxy/log.h> |
31 | | #include <haproxy/pool.h> |
32 | | #include <haproxy/proxy.h> |
33 | | #include <haproxy/sample.h> |
34 | | #include <haproxy/tools.h> |
35 | | #include <haproxy/version.h> |
36 | | |
37 | | |
38 | | /* List head of all known action keywords for "http-request" */ |
39 | | struct action_kw_list http_req_keywords = { |
40 | | .list = LIST_HEAD_INIT(http_req_keywords.list) |
41 | | }; |
42 | | |
43 | | /* List head of all known action keywords for "http-response" */ |
44 | | struct action_kw_list http_res_keywords = { |
45 | | .list = LIST_HEAD_INIT(http_res_keywords.list) |
46 | | }; |
47 | | |
48 | | /* List head of all known action keywords for "http-after-response" */ |
49 | | struct action_kw_list http_after_res_keywords = { |
50 | | .list = LIST_HEAD_INIT(http_after_res_keywords.list) |
51 | | }; |
52 | | |
53 | | void http_req_keywords_register(struct action_kw_list *kw_list) |
54 | 0 | { |
55 | 0 | LIST_APPEND(&http_req_keywords.list, &kw_list->list); |
56 | 0 | } |
57 | | |
58 | | void http_res_keywords_register(struct action_kw_list *kw_list) |
59 | 0 | { |
60 | 0 | LIST_APPEND(&http_res_keywords.list, &kw_list->list); |
61 | 0 | } |
62 | | |
63 | | void http_after_res_keywords_register(struct action_kw_list *kw_list) |
64 | 0 | { |
65 | 0 | LIST_APPEND(&http_after_res_keywords.list, &kw_list->list); |
66 | 0 | } |
67 | | |
68 | | /* |
69 | | * Return the struct http_req_action_kw associated to a keyword. |
70 | | */ |
71 | | struct action_kw *action_http_req_custom(const char *kw) |
72 | 0 | { |
73 | 0 | return action_lookup(&http_req_keywords.list, kw); |
74 | 0 | } |
75 | | |
76 | | /* |
77 | | * Return the struct http_res_action_kw associated to a keyword. |
78 | | */ |
79 | | struct action_kw *action_http_res_custom(const char *kw) |
80 | 0 | { |
81 | 0 | return action_lookup(&http_res_keywords.list, kw); |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * Return the struct http_after_res_action_kw associated to a keyword. |
86 | | */ |
87 | | struct action_kw *action_http_after_res_custom(const char *kw) |
88 | 0 | { |
89 | 0 | return action_lookup(&http_after_res_keywords.list, kw); |
90 | 0 | } |
91 | | |
92 | | /* parse an "http-request" rule */ |
93 | | struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy) |
94 | 0 | { |
95 | 0 | struct act_rule *rule; |
96 | 0 | const struct action_kw *custom = NULL; |
97 | 0 | int cur_arg; |
98 | |
|
99 | 0 | rule = new_act_rule(ACT_F_HTTP_REQ, file, linenum); |
100 | 0 | if (!rule) { |
101 | 0 | ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); |
102 | 0 | goto out; |
103 | 0 | } |
104 | | |
105 | 0 | if (((custom = action_http_req_custom(args[0])) != NULL)) { |
106 | 0 | char *errmsg = NULL; |
107 | |
|
108 | 0 | cur_arg = 1; |
109 | | /* try in the module list */ |
110 | 0 | rule->kw = custom; |
111 | |
|
112 | 0 | if (custom->flags & KWF_EXPERIMENTAL) { |
113 | 0 | if (!experimental_directives_allowed) { |
114 | 0 | ha_alert("parsing [%s:%d] : '%s' action is experimental, must be allowed via a global 'expose-experimental-directives'\n", |
115 | 0 | file, linenum, custom->kw); |
116 | 0 | goto out_err; |
117 | 0 | } |
118 | 0 | mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED); |
119 | 0 | } |
120 | | |
121 | 0 | if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { |
122 | 0 | ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", |
123 | 0 | file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); |
124 | 0 | free(errmsg); |
125 | 0 | goto out_err; |
126 | 0 | } |
127 | 0 | else if (errmsg) { |
128 | 0 | ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg); |
129 | 0 | free(errmsg); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | else { |
133 | 0 | const char *best = action_suggest(args[0], &http_req_keywords.list, NULL); |
134 | |
|
135 | 0 | action_build_list(&http_req_keywords.list, &trash); |
136 | 0 | ha_alert("parsing [%s:%d]: 'http-request' expects %s, but got '%s'%s.%s%s%s\n", |
137 | 0 | file, linenum, trash.area, |
138 | 0 | args[0], *args[0] ? "" : " (missing argument)", |
139 | 0 | best ? " Did you mean '" : "", |
140 | 0 | best ? best : "", |
141 | 0 | best ? "' maybe ?" : ""); |
142 | 0 | goto out_err; |
143 | 0 | } |
144 | | |
145 | 0 | if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { |
146 | 0 | struct acl_cond *cond; |
147 | 0 | char *errmsg = NULL; |
148 | |
|
149 | 0 | if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { |
150 | 0 | ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n", |
151 | 0 | file, linenum, args[0], errmsg); |
152 | 0 | free(errmsg); |
153 | 0 | goto out_err; |
154 | 0 | } |
155 | 0 | rule->cond = cond; |
156 | 0 | } |
157 | 0 | else if (*args[cur_arg]) { |
158 | 0 | ha_alert("parsing [%s:%d]: 'http-request %s' expects" |
159 | 0 | " either 'if' or 'unless' followed by a condition but found '%s'.\n", |
160 | 0 | file, linenum, args[0], args[cur_arg]); |
161 | 0 | goto out_err; |
162 | 0 | } |
163 | | |
164 | 0 | return rule; |
165 | 0 | out_err: |
166 | 0 | free_act_rule(rule); |
167 | 0 | out: |
168 | 0 | return NULL; |
169 | 0 | } |
170 | | |
171 | | /* parse an "http-respose" rule */ |
172 | | struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy) |
173 | 0 | { |
174 | 0 | struct act_rule *rule; |
175 | 0 | const struct action_kw *custom = NULL; |
176 | 0 | int cur_arg; |
177 | |
|
178 | 0 | rule = new_act_rule(ACT_F_HTTP_RES, file, linenum); |
179 | 0 | if (!rule) { |
180 | 0 | ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); |
181 | 0 | goto out; |
182 | 0 | } |
183 | | |
184 | 0 | if (((custom = action_http_res_custom(args[0])) != NULL)) { |
185 | 0 | char *errmsg = NULL; |
186 | |
|
187 | 0 | cur_arg = 1; |
188 | | /* try in the module list */ |
189 | 0 | rule->kw = custom; |
190 | |
|
191 | 0 | if (custom->flags & KWF_EXPERIMENTAL) { |
192 | 0 | if (!experimental_directives_allowed) { |
193 | 0 | ha_alert("parsing [%s:%d] : '%s' action is experimental, must be allowed via a global 'expose-experimental-directives'\n", |
194 | 0 | file, linenum, custom->kw); |
195 | 0 | goto out_err; |
196 | 0 | } |
197 | 0 | mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED); |
198 | 0 | } |
199 | | |
200 | 0 | if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { |
201 | 0 | ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", |
202 | 0 | file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); |
203 | 0 | free(errmsg); |
204 | 0 | goto out_err; |
205 | 0 | } |
206 | 0 | else if (errmsg) { |
207 | 0 | ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg); |
208 | 0 | free(errmsg); |
209 | 0 | } |
210 | 0 | } |
211 | 0 | else { |
212 | 0 | const char *best = action_suggest(args[0], &http_res_keywords.list, NULL); |
213 | |
|
214 | 0 | action_build_list(&http_res_keywords.list, &trash); |
215 | 0 | ha_alert("parsing [%s:%d]: 'http-response' expects %s, but got '%s'%s.%s%s%s\n", |
216 | 0 | file, linenum, trash.area, |
217 | 0 | args[0], *args[0] ? "" : " (missing argument)", |
218 | 0 | best ? " Did you mean '" : "", |
219 | 0 | best ? best : "", |
220 | 0 | best ? "' maybe ?" : ""); |
221 | 0 | goto out_err; |
222 | 0 | } |
223 | | |
224 | 0 | if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { |
225 | 0 | struct acl_cond *cond; |
226 | 0 | char *errmsg = NULL; |
227 | |
|
228 | 0 | if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { |
229 | 0 | ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n", |
230 | 0 | file, linenum, args[0], errmsg); |
231 | 0 | free(errmsg); |
232 | 0 | goto out_err; |
233 | 0 | } |
234 | 0 | rule->cond = cond; |
235 | 0 | } |
236 | 0 | else if (*args[cur_arg]) { |
237 | 0 | ha_alert("parsing [%s:%d]: 'http-response %s' expects" |
238 | 0 | " either 'if' or 'unless' followed by a condition but found '%s'.\n", |
239 | 0 | file, linenum, args[0], args[cur_arg]); |
240 | 0 | goto out_err; |
241 | 0 | } |
242 | | |
243 | 0 | return rule; |
244 | 0 | out_err: |
245 | 0 | free_act_rule(rule); |
246 | 0 | out: |
247 | 0 | return NULL; |
248 | 0 | } |
249 | | |
250 | | |
251 | | /* parse an "http-after-response" rule */ |
252 | | struct act_rule *parse_http_after_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy) |
253 | 0 | { |
254 | 0 | struct act_rule *rule; |
255 | 0 | const struct action_kw *custom = NULL; |
256 | 0 | int cur_arg; |
257 | |
|
258 | 0 | rule = new_act_rule(ACT_F_HTTP_RES, file, linenum); |
259 | 0 | if (!rule) { |
260 | 0 | ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); |
261 | 0 | goto out; |
262 | 0 | } |
263 | | |
264 | 0 | if (((custom = action_http_after_res_custom(args[0])) != NULL)) { |
265 | 0 | char *errmsg = NULL; |
266 | |
|
267 | 0 | cur_arg = 1; |
268 | | /* try in the module list */ |
269 | 0 | rule->kw = custom; |
270 | 0 | if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { |
271 | 0 | ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-after-response %s' rule : %s.\n", |
272 | 0 | file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); |
273 | 0 | free(errmsg); |
274 | 0 | goto out_err; |
275 | 0 | } |
276 | 0 | else if (errmsg) { |
277 | 0 | ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg); |
278 | 0 | free(errmsg); |
279 | 0 | } |
280 | 0 | } |
281 | 0 | else { |
282 | 0 | const char *best = action_suggest(args[0], &http_after_res_keywords.list, NULL); |
283 | |
|
284 | 0 | action_build_list(&http_after_res_keywords.list, &trash); |
285 | 0 | ha_alert("parsing [%s:%d]: 'http-after-response' expects %s, but got '%s'%s.%s%s%s\n", |
286 | 0 | file, linenum, trash.area, |
287 | 0 | args[0], *args[0] ? "" : " (missing argument)", |
288 | 0 | best ? " Did you mean '" : "", |
289 | 0 | best ? best : "", |
290 | 0 | best ? "' maybe ?" : ""); |
291 | 0 | goto out_err; |
292 | 0 | } |
293 | | |
294 | 0 | if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { |
295 | 0 | struct acl_cond *cond; |
296 | 0 | char *errmsg = NULL; |
297 | |
|
298 | 0 | if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { |
299 | 0 | ha_alert("parsing [%s:%d] : error detected while parsing an 'http-after-response %s' condition : %s.\n", |
300 | 0 | file, linenum, args[0], errmsg); |
301 | 0 | free(errmsg); |
302 | 0 | goto out_err; |
303 | 0 | } |
304 | 0 | rule->cond = cond; |
305 | 0 | } |
306 | 0 | else if (*args[cur_arg]) { |
307 | 0 | ha_alert("parsing [%s:%d]: 'http-after-response %s' expects" |
308 | 0 | " either 'if' or 'unless' followed by a condition but found '%s'.\n", |
309 | 0 | file, linenum, args[0], args[cur_arg]); |
310 | 0 | goto out_err; |
311 | 0 | } |
312 | | |
313 | 0 | return rule; |
314 | 0 | out_err: |
315 | 0 | free_act_rule(rule); |
316 | 0 | out: |
317 | 0 | return NULL; |
318 | 0 | } |
319 | | |
320 | | /* completely free redirect rule */ |
321 | | void http_free_redirect_rule(struct redirect_rule *rdr) |
322 | 0 | { |
323 | 0 | free_acl_cond(rdr->cond); |
324 | 0 | free(rdr->rdr_str); |
325 | 0 | if ((rdr->flags & REDIRECT_FLAG_COOKIE_FMT)) |
326 | 0 | lf_expr_deinit(&rdr->cookie.fmt); |
327 | 0 | else |
328 | 0 | istfree(&rdr->cookie.str); |
329 | 0 | lf_expr_deinit(&rdr->rdr_fmt); |
330 | 0 | free(rdr); |
331 | 0 | } |
332 | | |
333 | | /* Parses a redirect rule. Returns the redirect rule on success or NULL on error, |
334 | | * with <err> filled with the error message. If <use_fmt> is not null, builds a |
335 | | * dynamic log-format rule instead of a static string. Parameter <dir> indicates |
336 | | * the direction of the rule, and equals 0 for request, non-zero for responses. |
337 | | */ |
338 | | struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy, |
339 | | const char **args, char **errmsg, int use_fmt, int dir) |
340 | 0 | { |
341 | 0 | struct redirect_rule *rule = NULL; |
342 | 0 | int cur_arg; |
343 | 0 | int type = REDIRECT_TYPE_NONE; |
344 | 0 | int code = 302; |
345 | 0 | const char *destination = NULL; |
346 | 0 | const char *cookie = NULL; |
347 | 0 | int cookie_set = 0; |
348 | 0 | size_t cookie_len = 0; |
349 | 0 | unsigned int flags = (!dir ? REDIRECT_FLAG_FROM_REQ : REDIRECT_FLAG_NONE); |
350 | 0 | struct acl_cond *cond = NULL; |
351 | |
|
352 | 0 | cur_arg = 0; |
353 | 0 | while (*(args[cur_arg])) { |
354 | 0 | if (strcmp(args[cur_arg], "location") == 0) { |
355 | 0 | if (!*args[cur_arg + 1]) |
356 | 0 | goto missing_arg; |
357 | | |
358 | 0 | type = REDIRECT_TYPE_LOCATION; |
359 | 0 | cur_arg++; |
360 | 0 | destination = args[cur_arg]; |
361 | 0 | } |
362 | 0 | else if (strcmp(args[cur_arg], "prefix") == 0) { |
363 | 0 | if (!*args[cur_arg + 1]) |
364 | 0 | goto missing_arg; |
365 | 0 | type = REDIRECT_TYPE_PREFIX; |
366 | 0 | cur_arg++; |
367 | 0 | destination = args[cur_arg]; |
368 | 0 | } |
369 | 0 | else if (strcmp(args[cur_arg], "scheme") == 0) { |
370 | 0 | if (!*args[cur_arg + 1]) |
371 | 0 | goto missing_arg; |
372 | | |
373 | 0 | type = REDIRECT_TYPE_SCHEME; |
374 | 0 | cur_arg++; |
375 | 0 | destination = args[cur_arg]; |
376 | 0 | } |
377 | 0 | else if (strcmp(args[cur_arg], "set-cookie") == 0) { |
378 | 0 | if (!*args[cur_arg + 1]) |
379 | 0 | goto missing_arg; |
380 | | |
381 | 0 | cur_arg++; |
382 | 0 | cookie = args[cur_arg]; |
383 | 0 | cookie_set = 1; |
384 | 0 | } |
385 | 0 | else if (strcmp(args[cur_arg], "set-cookie-fmt") == 0) { |
386 | 0 | if (!*args[cur_arg + 1]) |
387 | 0 | goto missing_arg; |
388 | | |
389 | 0 | cur_arg++; |
390 | 0 | cookie = args[cur_arg]; |
391 | 0 | cookie_set = 2; |
392 | 0 | } |
393 | 0 | else if (strcmp(args[cur_arg], "clear-cookie") == 0) { |
394 | 0 | if (!*args[cur_arg + 1]) |
395 | 0 | goto missing_arg; |
396 | | |
397 | 0 | cur_arg++; |
398 | 0 | cookie = args[cur_arg]; |
399 | 0 | cookie_set = 0; |
400 | 0 | } |
401 | 0 | else if (strcmp(args[cur_arg], "code") == 0) { |
402 | 0 | if (!*args[cur_arg + 1]) |
403 | 0 | goto missing_arg; |
404 | | |
405 | 0 | cur_arg++; |
406 | 0 | code = atol(args[cur_arg]); |
407 | 0 | if (code < 301 || code > 308 || (code > 303 && code < 307)) { |
408 | 0 | memprintf(errmsg, |
409 | 0 | "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)", |
410 | 0 | args[cur_arg - 1], args[cur_arg]); |
411 | 0 | goto err; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | else if (strcmp(args[cur_arg], "drop-query") == 0) { |
415 | 0 | flags |= REDIRECT_FLAG_DROP_QS; |
416 | 0 | } |
417 | 0 | else if (strcmp(args[cur_arg], "keep-query") == 0) { |
418 | 0 | flags |= REDIRECT_FLAG_KEEP_QS; |
419 | 0 | } |
420 | 0 | else if (strcmp(args[cur_arg], "append-slash") == 0) { |
421 | 0 | flags |= REDIRECT_FLAG_APPEND_SLASH; |
422 | 0 | } |
423 | 0 | else if (strcmp(args[cur_arg], "ignore-empty") == 0) { |
424 | 0 | flags |= REDIRECT_FLAG_IGNORE_EMPTY; |
425 | 0 | } |
426 | 0 | else if (strcmp(args[cur_arg], "if") == 0 || |
427 | 0 | strcmp(args[cur_arg], "unless") == 0) { |
428 | 0 | cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg); |
429 | 0 | if (!cond) { |
430 | 0 | memprintf(errmsg, "error in condition: %s", *errmsg); |
431 | 0 | goto err; |
432 | 0 | } |
433 | 0 | break; |
434 | 0 | } |
435 | 0 | else { |
436 | 0 | memprintf(errmsg, |
437 | 0 | "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'set-cookie-fmt'," |
438 | 0 | " 'clear-cookie', 'drop-query', 'keep-query', 'ignore-empty' or 'append-slash' (was '%s')", |
439 | 0 | args[cur_arg]); |
440 | 0 | goto err; |
441 | 0 | } |
442 | 0 | cur_arg++; |
443 | 0 | } |
444 | | |
445 | 0 | if (type == REDIRECT_TYPE_NONE) { |
446 | 0 | memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')"); |
447 | 0 | goto err; |
448 | 0 | } |
449 | | |
450 | 0 | if (dir && type != REDIRECT_TYPE_LOCATION) { |
451 | 0 | memprintf(errmsg, "response only supports redirect type 'location'"); |
452 | 0 | goto err; |
453 | 0 | } |
454 | | |
455 | 0 | rule = calloc(1, sizeof(*rule)); |
456 | 0 | if (!rule) |
457 | 0 | goto out_of_memory; |
458 | 0 | rule->cond = cond; |
459 | 0 | lf_expr_init(&rule->rdr_fmt); |
460 | |
|
461 | 0 | if (!use_fmt) { |
462 | | /* old-style static redirect rule */ |
463 | 0 | rule->rdr_str = strdup(destination); |
464 | 0 | if (!rule->rdr_str) |
465 | 0 | goto out_of_memory; |
466 | 0 | rule->rdr_len = strlen(destination); |
467 | 0 | } |
468 | 0 | else { |
469 | | /* log-format based redirect rule */ |
470 | 0 | int cap = 0; |
471 | | |
472 | | /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case, |
473 | | * if prefix == "/", we don't want to add anything, otherwise it |
474 | | * makes it hard for the user to configure a self-redirection. |
475 | | */ |
476 | 0 | curproxy->conf.args.ctx = ARGC_RDR; |
477 | 0 | if (curproxy->cap & PR_CAP_FE) |
478 | 0 | cap |= (dir ? SMP_VAL_FE_HRS_HDR : SMP_VAL_FE_HRQ_HDR); |
479 | 0 | if (curproxy->cap & PR_CAP_BE) |
480 | 0 | cap |= (dir ? SMP_VAL_BE_HRS_HDR : SMP_VAL_BE_HRQ_HDR); |
481 | 0 | if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) { |
482 | 0 | if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP, cap, errmsg)) { |
483 | 0 | goto err; |
484 | 0 | } |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | 0 | if (cookie) { |
489 | | /* depending on cookie_set, either we want to set the cookie, or to clear it. |
490 | | * a clear consists in appending "; path=/; Max-Age=0;" at the end. |
491 | | */ |
492 | 0 | cookie_len = strlen(cookie); |
493 | 0 | if (cookie_set == 1) { // set-cookie |
494 | 0 | rule->cookie.str = istalloc(cookie_len+9); |
495 | 0 | if (!isttest(rule->cookie.str)) |
496 | 0 | goto out_of_memory; |
497 | 0 | istcpy(&rule->cookie.str, ist2(cookie, cookie_len), cookie_len); |
498 | 0 | istcat(&rule->cookie.str, ist2("; path=/;", 9), cookie_len+10); |
499 | 0 | } |
500 | 0 | else if (cookie_set == 2) { // set-cookie-fmt |
501 | 0 | int cap = 0; |
502 | |
|
503 | 0 | lf_expr_init(&rule->cookie.fmt); |
504 | 0 | curproxy->conf.args.ctx = ARGC_RDR; |
505 | 0 | if (curproxy->cap & PR_CAP_FE) |
506 | 0 | cap |= (dir ? SMP_VAL_FE_HRS_HDR : SMP_VAL_FE_HRQ_HDR); |
507 | 0 | if (curproxy->cap & PR_CAP_BE) |
508 | 0 | cap |= (dir ? SMP_VAL_BE_HRS_HDR : SMP_VAL_BE_HRQ_HDR); |
509 | |
|
510 | 0 | chunk_memcpy(&trash, cookie, cookie_len); |
511 | 0 | chunk_strcat(&trash, "; path=/;"); |
512 | 0 | if (!parse_logformat_string(trash.area, curproxy, &rule->cookie.fmt, LOG_OPT_HTTP, cap, errmsg)) { |
513 | 0 | goto err; |
514 | 0 | } |
515 | | |
516 | 0 | flags |= REDIRECT_FLAG_COOKIE_FMT; |
517 | 0 | } |
518 | 0 | else { // clear-cookie |
519 | 0 | rule->cookie.str = istalloc(cookie_len+20); |
520 | 0 | if (!isttest(rule->cookie.str)) |
521 | 0 | goto out_of_memory; |
522 | 0 | istcpy(&rule->cookie.str, ist2(cookie, cookie_len), cookie_len); |
523 | 0 | istcat(&rule->cookie.str, ist2("; path=/; Max-Age=0;", 20), cookie_len+21); |
524 | 0 | } |
525 | 0 | } |
526 | 0 | rule->type = type; |
527 | 0 | rule->code = code; |
528 | 0 | rule->flags = flags; |
529 | 0 | LIST_INIT(&rule->list); |
530 | 0 | return rule; |
531 | | |
532 | 0 | missing_arg: |
533 | 0 | memprintf(errmsg, "missing argument for '%s'", args[cur_arg]); |
534 | 0 | goto err; |
535 | 0 | out_of_memory: |
536 | 0 | memprintf(errmsg, "parsing [%s:%d]: out of memory.", file, linenum); |
537 | 0 | err: |
538 | 0 | if (rule) |
539 | 0 | http_free_redirect_rule(rule); |
540 | 0 | else if (cond) { |
541 | | /* rule not yet allocated, but cond already is */ |
542 | 0 | free_acl_cond(cond); |
543 | 0 | } |
544 | |
|
545 | | return NULL; |
546 | 0 | } |
547 | | |
548 | | /* |
549 | | * Local variables: |
550 | | * c-indent-level: 8 |
551 | | * c-basic-offset: 8 |
552 | | * End: |
553 | | */ |