blob: 3751b002f8a79f9952ae89406e1b9a491f4c4334 [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP actions
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
Willy Tarreaudcc048a2020-06-04 19:11:43 +020019#include <haproxy/acl.h>
Willy Tarreau122eba92020-06-04 10:15:32 +020020#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020021#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020022#include <haproxy/arg.h>
23#include <haproxy/capture-t.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020024#include <haproxy/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020025#include <haproxy/chunk.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020026#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020027#include <haproxy/http.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020028#include <haproxy/http_ana.h>
Willy Tarreau87735332020-06-04 09:08:41 +020029#include <haproxy/http_htx.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020030#include <haproxy/http_rules.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020031#include <haproxy/log.h>
Willy Tarreau225a90a2020-06-04 15:06:28 +020032#include <haproxy/pattern.h>
Willy Tarreaud0ef4392020-06-02 09:38:52 +020033#include <haproxy/pool.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020034#include <haproxy/regex.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020035#include <haproxy/sample.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020036#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020037#include <haproxy/tools.h>
Willy Tarreau8c42b8a2020-06-04 19:27:34 +020038#include <haproxy/uri_auth-t.h>
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +020039#include <haproxy/uri_normalizer.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020040#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020041
Willy Tarreau79e57332018-10-02 16:01:16 +020042
Christopher Faulet2eb53962020-01-14 14:47:34 +010043/* Release memory allocated by most of HTTP actions. Concretly, it releases
44 * <arg.http>.
45 */
46static void release_http_action(struct act_rule *rule)
47{
48 struct logformat_node *lf, *lfb;
49
Tim Duesterhused526372020-03-05 17:56:33 +010050 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010051 if (rule->arg.http.re)
52 regex_free(rule->arg.http.re);
53 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
54 LIST_DEL(&lf->list);
55 release_sample_expr(lf->expr);
56 free(lf->arg);
57 free(lf);
58 }
59}
60
Christopher Faulet5cb513a2020-05-13 17:56:56 +020061/* Release memory allocated by HTTP actions relying on an http reply. Concretly,
62 * it releases <.arg.http_reply>
63 */
64static void release_act_http_reply(struct act_rule *rule)
65{
66 release_http_reply(rule->arg.http_reply);
67 rule->arg.http_reply = NULL;
68}
69
70
71/* Check function for HTTP actions relying on an http reply. The function
72 * returns 1 in success case, otherwise, it returns 0 and err is filled.
73 */
74static int check_act_http_reply(struct act_rule *rule, struct proxy *px, char **err)
75{
76 struct http_reply *reply = rule->arg.http_reply;
77
78 if (!http_check_http_reply(reply, px, err)) {
79 release_act_http_reply(rule);
80 return 0;
81 }
82 return 1;
83}
84
Willy Tarreau79e57332018-10-02 16:01:16 +020085
86/* This function executes one of the set-{method,path,query,uri} actions. It
87 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010088 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050089 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010090 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
91 * occurs while soft rewrites are enabled, the action is canceled, but the rule
92 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020093 */
94static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
95 struct session *sess, struct stream *s, int flags)
96{
97 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010098 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020099
100 replace = alloc_trash_chunk();
101 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100102 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +0200103
104 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +0100105 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200106 replace->area[replace->data++] = '?';
107 replace->data += build_logline(s, replace->area + replace->data,
108 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +0100109 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200110
Christopher Faulet2c22a692019-12-18 15:39:56 +0100111 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100112 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +0200113
Christopher Faulete00d06c2019-12-16 17:18:42 +0100114 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +0200115 free_trash_chunk(replace);
116 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100117
118 fail_alloc:
119 if (!(s->flags & SF_ERR_MASK))
120 s->flags |= SF_ERR_RESOURCE;
121 ret = ACT_RET_ERR;
122 goto leave;
123
124 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +0200125 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100126 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +0200127 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +0100128 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200129 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100130 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +0200131 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100132
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100133 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100134 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100135 if (!(s->flags & SF_ERR_MASK))
136 s->flags |= SF_ERR_PRXCOND;
137 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100138 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200139}
140
141/* parse an http-request action among :
142 * set-method
143 * set-path
Christopher Faulet312294f2020-09-02 17:17:44 +0200144 * set-pathq
Willy Tarreau79e57332018-10-02 16:01:16 +0200145 * set-query
146 * set-uri
147 *
148 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100149 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100150 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
151 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200152 */
153static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
154 struct act_rule *rule, char **err)
155{
156 int cur_arg = *orig_arg;
157
Willy Tarreau79e57332018-10-02 16:01:16 +0200158 switch (args[0][4]) {
159 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100160 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200161 break;
162 case 'p' :
Christopher Faulet312294f2020-09-02 17:17:44 +0200163 if (args[0][8] == 'q')
164 rule->action = 4; // set-pathq
165 else
166 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200167 break;
168 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100169 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200170 break;
171 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100172 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200173 break;
174 default:
175 memprintf(err, "internal error: unhandled action '%s'", args[0]);
176 return ACT_RET_PRS_ERR;
177 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100178 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100179 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200180
181 if (!*args[cur_arg] ||
182 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
183 memprintf(err, "expects exactly 1 argument <format>");
184 return ACT_RET_PRS_ERR;
185 }
186
Christopher Faulet96bff762019-12-17 13:46:18 +0100187 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200188 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100189 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200190 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
191 return ACT_RET_PRS_ERR;
192 }
193
194 (*orig_arg)++;
195 return ACT_RET_PRS_OK;
196}
197
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +0200198/* This function executes the http-request normalize-uri action.
199 * `rule->action` is expected to be a value from `enum act_normalize_uri`.
200 *
201 * On success, it returns ACT_RET_CONT. If an error
202 * occurs while soft rewrites are enabled, the action is canceled, but the rule
203 * processing continue. Otherwsize ACT_RET_ERR is returned.
204 */
205static enum act_return http_action_normalize_uri(struct act_rule *rule, struct proxy *px,
206 struct session *sess, struct stream *s, int flags)
207{
208 enum act_return ret = ACT_RET_CONT;
209 struct htx *htx = htxbuf(&s->req.buf);
210 const struct ist uri = htx_sl_req_uri(http_get_stline(htx));
211 struct buffer *replace = alloc_trash_chunk();
212 enum uri_normalizer_err err = URI_NORMALIZER_ERR_INTERNAL_ERROR;
213
214 if (!replace)
215 goto fail_alloc;
216
217 switch ((enum act_normalize_uri) rule->action) {
Tim Duesterhusd371e992021-04-15 21:45:58 +0200218 case ACT_NORMALIZE_URI_MERGE_SLASHES: {
219 const struct ist path = http_get_path(uri);
220 struct ist newpath = ist2(replace->area, replace->size);
221
222 if (!isttest(path))
223 goto leave;
224
225 err = uri_normalizer_path_merge_slashes(iststop(path, '?'), &newpath);
226
227 if (err != URI_NORMALIZER_ERR_NONE)
228 break;
229
230 if (!http_replace_req_path(htx, newpath, 0))
231 goto fail_rewrite;
232
233 break;
234 }
Tim Duesterhus560e1a62021-04-15 21:46:00 +0200235 case ACT_NORMALIZE_URI_DOTDOT:
236 case ACT_NORMALIZE_URI_DOTDOT_FULL: {
Tim Duesterhus9982fc22021-04-15 21:45:59 +0200237 const struct ist path = http_get_path(uri);
238 struct ist newpath = ist2(replace->area, replace->size);
239
240 if (!isttest(path))
241 goto leave;
242
Tim Duesterhus560e1a62021-04-15 21:46:00 +0200243 err = uri_normalizer_path_dotdot(iststop(path, '?'), rule->action == ACT_NORMALIZE_URI_DOTDOT_FULL, &newpath);
Tim Duesterhus9982fc22021-04-15 21:45:59 +0200244
245 if (err != URI_NORMALIZER_ERR_NONE)
246 break;
247
248 if (!http_replace_req_path(htx, newpath, 0))
249 goto fail_rewrite;
250
251 break;
252 }
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +0200253 }
254
255 switch (err) {
256 case URI_NORMALIZER_ERR_NONE:
257 break;
258 case URI_NORMALIZER_ERR_INTERNAL_ERROR:
259 ret = ACT_RET_ERR;
260 break;
261 case URI_NORMALIZER_ERR_INVALID_INPUT:
262 ret = ACT_RET_INV;
263 break;
264 case URI_NORMALIZER_ERR_ALLOC:
265 goto fail_alloc;
266 }
267
268 leave:
269 free_trash_chunk(replace);
270 return ret;
271
272 fail_alloc:
273 if (!(s->flags & SF_ERR_MASK))
274 s->flags |= SF_ERR_RESOURCE;
275 ret = ACT_RET_ERR;
276 goto leave;
277
278 fail_rewrite:
279 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
280 if (s->flags & SF_BE_ASSIGNED)
281 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
282 if (sess->listener && sess->listener->counters)
283 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
284 if (objt_server(s->target))
285 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
286
287 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
288 ret = ACT_RET_ERR;
289 if (!(s->flags & SF_ERR_MASK))
290 s->flags |= SF_ERR_PRXCOND;
291 }
292 goto leave;
293}
294
295/* Parses the http-request normalize-uri action. It expects a single <normalizer>
296 * argument, corresponding too a value in `enum act_normalize_uri`.
297 *
298 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
299 */
300static enum act_parse_ret parse_http_normalize_uri(const char **args, int *orig_arg, struct proxy *px,
301 struct act_rule *rule, char **err)
302{
303 int cur_arg = *orig_arg;
304
305 rule->action_ptr = http_action_normalize_uri;
306 rule->release_ptr = NULL;
307
308 if (!*args[cur_arg]) {
309 memprintf(err, "missing argument <normalizer>");
310 return ACT_RET_PRS_ERR;
311 }
312
Tim Duesterhusd371e992021-04-15 21:45:58 +0200313 if (strcmp(args[cur_arg], "merge-slashes") == 0) {
314 cur_arg++;
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +0200315
Tim Duesterhusd371e992021-04-15 21:45:58 +0200316 rule->action = ACT_NORMALIZE_URI_MERGE_SLASHES;
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +0200317 }
Tim Duesterhus9982fc22021-04-15 21:45:59 +0200318 else if (strcmp(args[cur_arg], "dotdot") == 0) {
319 cur_arg++;
320
Tim Duesterhus560e1a62021-04-15 21:46:00 +0200321 if (strcmp(args[cur_arg], "full") == 0) {
322 cur_arg++;
323 rule->action = ACT_NORMALIZE_URI_DOTDOT_FULL;
324 }
325 else if (!*args[cur_arg]) {
326 rule->action = ACT_NORMALIZE_URI_DOTDOT;
327 }
328 else if (strcmp(args[cur_arg], "if") != 0 && strcmp(args[cur_arg], "unless") != 0) {
329 memprintf(err, "unknown argument '%s' for 'dotdot' normalizer", args[cur_arg]);
330 return ACT_RET_PRS_ERR;
331 }
Tim Duesterhus9982fc22021-04-15 21:45:59 +0200332 }
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +0200333 else {
334 memprintf(err, "unknown normalizer '%s'", args[cur_arg]);
335 return ACT_RET_PRS_ERR;
336 }
337
338 *orig_arg = cur_arg;
339 return ACT_RET_PRS_OK;
340}
341
Willy Tarreau33810222019-06-12 17:44:02 +0200342/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100343 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200344 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100345 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100346 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100347 * contains 1 for the path or 3 for the URI (values used by
348 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
349 * occurs while soft rewrites are enabled, the action is canceled, but the rule
350 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200351 */
352static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
353 struct session *sess, struct stream *s, int flags)
354{
Christopher Faulet13403762019-12-13 09:01:57 +0100355 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200356 struct buffer *replace, *output;
357 struct ist uri;
358 int len;
359
360 replace = alloc_trash_chunk();
361 output = alloc_trash_chunk();
362 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100363 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200364 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100365
Christopher Faulet1fa0cc12020-09-02 11:10:38 +0200366 if (rule->action == 1) // replace-path
367 uri = iststop(http_get_path(uri), '?');
Christopher Faulet312294f2020-09-02 17:17:44 +0200368 else if (rule->action == 4) // replace-pathq
369 uri = http_get_path(uri);
Willy Tarreau262c3f12019-12-17 06:52:51 +0100370
Christopher Faulet96bff762019-12-17 13:46:18 +0100371 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200372 goto leave;
373
Christopher Faulet96bff762019-12-17 13:46:18 +0100374 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200375
376 /* note: uri.ptr doesn't need to be zero-terminated because it will
377 * only be used to pick pmatch references.
378 */
379 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
380 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100381 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200382
Christopher Faulet2c22a692019-12-18 15:39:56 +0100383 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100384 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200385
Christopher Faulete00d06c2019-12-16 17:18:42 +0100386 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200387 free_trash_chunk(output);
388 free_trash_chunk(replace);
389 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100390
391 fail_alloc:
392 if (!(s->flags & SF_ERR_MASK))
393 s->flags |= SF_ERR_RESOURCE;
394 ret = ACT_RET_ERR;
395 goto leave;
396
397 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +0200398 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100399 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +0200400 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +0100401 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200402 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100403 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +0200404 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100405
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100406 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100407 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100408 if (!(s->flags & SF_ERR_MASK))
409 s->flags |= SF_ERR_PRXCOND;
410 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100411 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200412}
413
Christopher Faulet312294f2020-09-02 17:17:44 +0200414/* parse a "replace-uri", "replace-path" or "replace-pathq"
415 * http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200416 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100417 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100418 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200419 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
420 */
421static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
422 struct act_rule *rule, char **err)
423{
424 int cur_arg = *orig_arg;
425 char *error = NULL;
426
Christopher Faulet312294f2020-09-02 17:17:44 +0200427 switch (args[0][8]) {
428 case 'p':
429 if (args[0][12] == 'q')
430 rule->action = 4; // replace-pathq, same as set-pathq
431 else
432 rule->action = 1; // replace-path, same as set-path
433 break;
434 case 'u':
Christopher Faulet2c22a692019-12-18 15:39:56 +0100435 rule->action = 3; // replace-uri, same as set-uri
Christopher Faulet312294f2020-09-02 17:17:44 +0200436 break;
437 default:
438 memprintf(err, "internal error: unhandled action '%s'", args[0]);
439 return ACT_RET_PRS_ERR;
440 }
Willy Tarreau262c3f12019-12-17 06:52:51 +0100441
Willy Tarreau33810222019-06-12 17:44:02 +0200442 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100443 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200444
445 if (!*args[cur_arg] || !*args[cur_arg+1] ||
446 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
447 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
448 return ACT_RET_PRS_ERR;
449 }
450
Christopher Faulet96bff762019-12-17 13:46:18 +0100451 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200452 memprintf(err, "failed to parse the regex : %s", error);
453 free(error);
454 return ACT_RET_PRS_ERR;
455 }
456
Christopher Faulet96bff762019-12-17 13:46:18 +0100457 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200458 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100459 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200460 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100461 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200462 return ACT_RET_PRS_ERR;
463 }
464
465 (*orig_arg) += 2;
466 return ACT_RET_PRS_OK;
467}
468
Willy Tarreau79e57332018-10-02 16:01:16 +0200469/* This function is just a compliant action wrapper for "set-status". */
470static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
471 struct session *sess, struct stream *s, int flags)
472{
Christopher Faulet96bff762019-12-17 13:46:18 +0100473 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Willy Tarreau4781b152021-04-06 13:53:36 +0200474 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100475 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +0200476 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +0100477 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200478 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100479 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +0200480 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100481
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100482 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100483 if (!(s->flags & SF_ERR_MASK))
484 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100485 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100486 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100487 }
488
Willy Tarreau79e57332018-10-02 16:01:16 +0200489 return ACT_RET_CONT;
490}
491
492/* parse set-status action:
493 * This action accepts a single argument of type int representing
494 * an http status code. It returns ACT_RET_PRS_OK on success,
495 * ACT_RET_PRS_ERR on error.
496 */
497static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
498 struct act_rule *rule, char **err)
499{
500 char *error;
501
502 rule->action = ACT_CUSTOM;
503 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100504 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200505
506 /* Check if an argument is available */
507 if (!*args[*orig_arg]) {
508 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
509 return ACT_RET_PRS_ERR;
510 }
511
512 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100513 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
514 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200515 memprintf(err, "expects an integer status code between 100 and 999");
516 return ACT_RET_PRS_ERR;
517 }
518
519 (*orig_arg)++;
520
521 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100522 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200523 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
524 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
525 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100526 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
527 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200528 (*orig_arg)++;
529 }
530
Christopher Fauletc20b3712020-01-27 15:51:56 +0100531 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200532 return ACT_RET_PRS_OK;
533}
534
535/* This function executes the "reject" HTTP action. It clears the request and
536 * response buffer without sending any response. It can be useful as an HTTP
537 * alternative to the silent-drop action to defend against DoS attacks, and may
538 * also be used with HTTP/2 to close a connection instead of just a stream.
539 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100540 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200541 */
542static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
543 struct session *sess, struct stream *s, int flags)
544{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100545 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200546 channel_abort(&s->req);
547 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100548 s->req.analysers &= AN_REQ_FLT_END;
549 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200550
Willy Tarreau4781b152021-04-06 13:53:36 +0200551 _HA_ATOMIC_INC(&s->be->be_counters.denied_req);
552 _HA_ATOMIC_INC(&sess->fe->fe_counters.denied_req);
Willy Tarreau79e57332018-10-02 16:01:16 +0200553 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200554 _HA_ATOMIC_INC(&sess->listener->counters->denied_req);
Willy Tarreau79e57332018-10-02 16:01:16 +0200555
556 if (!(s->flags & SF_ERR_MASK))
557 s->flags |= SF_ERR_PRXCOND;
558 if (!(s->flags & SF_FINST_MASK))
559 s->flags |= SF_FINST_R;
560
Christopher Faulet90d22a82020-03-06 11:18:39 +0100561 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200562}
563
564/* parse the "reject" action:
565 * This action takes no argument and returns ACT_RET_PRS_OK on success,
566 * ACT_RET_PRS_ERR on error.
567 */
568static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
569 struct act_rule *rule, char **err)
570{
571 rule->action = ACT_CUSTOM;
572 rule->action_ptr = http_action_reject;
573 return ACT_RET_PRS_OK;
574}
575
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200576/* This function executes the "disable-l7-retry" HTTP action.
577 * It disables L7 retries (all retry except for a connection failure). This
578 * can be useful for example to avoid retrying on POST requests.
579 * It just removes the L7 retry flag on the stream_interface, and always
580 * return ACT_RET_CONT;
581 */
582static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
583 struct session *sess, struct stream *s, int flags)
584{
585 struct stream_interface *si = &s->si[1];
586
587 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
588 * let's be future-proof and remove it anyway.
589 */
590 si->flags &= ~SI_FL_L7_RETRY;
591 si->flags |= SI_FL_D_L7_RETRY;
592 return ACT_RET_CONT;
593}
594
595/* parse the "disable-l7-retry" action:
596 * This action takes no argument and returns ACT_RET_PRS_OK on success,
597 * ACT_RET_PRS_ERR on error.
598 */
599static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
600 int *orig_args, struct proxy *px,
601 struct act_rule *rule, char **err)
602{
603 rule->action = ACT_CUSTOM;
604 rule->action_ptr = http_req_disable_l7_retry;
605 return ACT_RET_PRS_OK;
606}
607
Willy Tarreau79e57332018-10-02 16:01:16 +0200608/* This function executes the "capture" action. It executes a fetch expression,
609 * turns the result into a string and puts it in a capture slot. It always
610 * returns 1. If an error occurs the action is cancelled, but the rule
611 * processing continues.
612 */
613static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
614 struct session *sess, struct stream *s, int flags)
615{
616 struct sample *key;
617 struct cap_hdr *h = rule->arg.cap.hdr;
618 char **cap = s->req_cap;
619 int len;
620
621 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
622 if (!key)
623 return ACT_RET_CONT;
624
625 if (cap[h->index] == NULL)
626 cap[h->index] = pool_alloc(h->pool);
627
628 if (cap[h->index] == NULL) /* no more capture memory */
629 return ACT_RET_CONT;
630
631 len = key->data.u.str.data;
632 if (len > h->len)
633 len = h->len;
634
635 memcpy(cap[h->index], key->data.u.str.area, len);
636 cap[h->index][len] = 0;
637 return ACT_RET_CONT;
638}
639
640/* This function executes the "capture" action and store the result in a
641 * capture slot if exists. It executes a fetch expression, turns the result
642 * into a string and puts it in a capture slot. It always returns 1. If an
643 * error occurs the action is cancelled, but the rule processing continues.
644 */
645static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
646 struct session *sess, struct stream *s, int flags)
647{
648 struct sample *key;
649 struct cap_hdr *h;
650 char **cap = s->req_cap;
651 struct proxy *fe = strm_fe(s);
652 int len;
653 int i;
654
655 /* Look for the original configuration. */
656 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
657 h != NULL && i != rule->arg.capid.idx ;
658 i--, h = h->next);
659 if (!h)
660 return ACT_RET_CONT;
661
662 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
663 if (!key)
664 return ACT_RET_CONT;
665
666 if (cap[h->index] == NULL)
667 cap[h->index] = pool_alloc(h->pool);
668
669 if (cap[h->index] == NULL) /* no more capture memory */
670 return ACT_RET_CONT;
671
672 len = key->data.u.str.data;
673 if (len > h->len)
674 len = h->len;
675
676 memcpy(cap[h->index], key->data.u.str.area, len);
677 cap[h->index][len] = 0;
678 return ACT_RET_CONT;
679}
680
681/* Check an "http-request capture" action.
682 *
683 * The function returns 1 in success case, otherwise, it returns 0 and err is
684 * filled.
685 */
686static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
687{
688 if (rule->action_ptr != http_action_req_capture_by_id)
689 return 1;
690
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100691 /* capture slots can only be declared in frontends, so we can't check their
692 * existence in backends at configuration parsing step
693 */
694 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200695 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
696 rule->arg.capid.idx);
697 return 0;
698 }
699
700 return 1;
701}
702
Christopher Faulet2eb53962020-01-14 14:47:34 +0100703/* Release memory allocate by an http capture action */
704static void release_http_capture(struct act_rule *rule)
705{
706 if (rule->action_ptr == http_action_req_capture)
707 release_sample_expr(rule->arg.cap.expr);
708 else
709 release_sample_expr(rule->arg.capid.expr);
710}
711
Willy Tarreau79e57332018-10-02 16:01:16 +0200712/* parse an "http-request capture" action. It takes a single argument which is
713 * a sample fetch expression. It stores the expression into arg->act.p[0] and
714 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
715 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
716 */
717static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
718 struct act_rule *rule, char **err)
719{
720 struct sample_expr *expr;
721 struct cap_hdr *hdr;
722 int cur_arg;
723 int len = 0;
724
725 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
726 if (strcmp(args[cur_arg], "if") == 0 ||
727 strcmp(args[cur_arg], "unless") == 0)
728 break;
729
730 if (cur_arg < *orig_arg + 3) {
731 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
732 return ACT_RET_PRS_ERR;
733 }
734
735 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100736 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +0200737 if (!expr)
738 return ACT_RET_PRS_ERR;
739
740 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
741 memprintf(err,
742 "fetch method '%s' extracts information from '%s', none of which is available here",
743 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100744 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200745 return ACT_RET_PRS_ERR;
746 }
747
748 if (!args[cur_arg] || !*args[cur_arg]) {
749 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100750 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200751 return ACT_RET_PRS_ERR;
752 }
753
754 if (strcmp(args[cur_arg], "len") == 0) {
755 cur_arg++;
756
757 if (!(px->cap & PR_CAP_FE)) {
758 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100759 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200760 return ACT_RET_PRS_ERR;
761 }
762
763 px->conf.args.ctx = ARGC_CAP;
764
765 if (!args[cur_arg]) {
766 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100767 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200768 return ACT_RET_PRS_ERR;
769 }
770 /* we copy the table name for now, it will be resolved later */
771 len = atoi(args[cur_arg]);
772 if (len <= 0) {
773 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100774 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200775 return ACT_RET_PRS_ERR;
776 }
777 cur_arg++;
778
Willy Tarreau79e57332018-10-02 16:01:16 +0200779 hdr = calloc(1, sizeof(*hdr));
780 hdr->next = px->req_cap;
781 hdr->name = NULL; /* not a header capture */
782 hdr->namelen = 0;
783 hdr->len = len;
784 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
785 hdr->index = px->nb_req_cap++;
786
787 px->req_cap = hdr;
788 px->to_log |= LW_REQHDR;
789
790 rule->action = ACT_CUSTOM;
791 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100792 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200793 rule->arg.cap.expr = expr;
794 rule->arg.cap.hdr = hdr;
795 }
796
797 else if (strcmp(args[cur_arg], "id") == 0) {
798 int id;
799 char *error;
800
801 cur_arg++;
802
803 if (!args[cur_arg]) {
804 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100805 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200806 return ACT_RET_PRS_ERR;
807 }
808
809 id = strtol(args[cur_arg], &error, 10);
810 if (*error != '\0') {
811 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100812 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200813 return ACT_RET_PRS_ERR;
814 }
815 cur_arg++;
816
817 px->conf.args.ctx = ARGC_CAP;
818
819 rule->action = ACT_CUSTOM;
820 rule->action_ptr = http_action_req_capture_by_id;
821 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100822 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200823 rule->arg.capid.expr = expr;
824 rule->arg.capid.idx = id;
825 }
826
827 else {
828 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100829 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200830 return ACT_RET_PRS_ERR;
831 }
832
833 *orig_arg = cur_arg;
834 return ACT_RET_PRS_OK;
835}
836
837/* This function executes the "capture" action and store the result in a
838 * capture slot if exists. It executes a fetch expression, turns the result
839 * into a string and puts it in a capture slot. It always returns 1. If an
840 * error occurs the action is cancelled, but the rule processing continues.
841 */
842static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
843 struct session *sess, struct stream *s, int flags)
844{
845 struct sample *key;
846 struct cap_hdr *h;
847 char **cap = s->res_cap;
848 struct proxy *fe = strm_fe(s);
849 int len;
850 int i;
851
852 /* Look for the original configuration. */
853 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
854 h != NULL && i != rule->arg.capid.idx ;
855 i--, h = h->next);
856 if (!h)
857 return ACT_RET_CONT;
858
859 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
860 if (!key)
861 return ACT_RET_CONT;
862
863 if (cap[h->index] == NULL)
864 cap[h->index] = pool_alloc(h->pool);
865
866 if (cap[h->index] == NULL) /* no more capture memory */
867 return ACT_RET_CONT;
868
869 len = key->data.u.str.data;
870 if (len > h->len)
871 len = h->len;
872
873 memcpy(cap[h->index], key->data.u.str.area, len);
874 cap[h->index][len] = 0;
875 return ACT_RET_CONT;
876}
877
878/* Check an "http-response capture" action.
879 *
880 * The function returns 1 in success case, otherwise, it returns 0 and err is
881 * filled.
882 */
883static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
884{
885 if (rule->action_ptr != http_action_res_capture_by_id)
886 return 1;
887
Tim Duesterhusf3f4aa02020-07-03 13:43:42 +0200888 /* capture slots can only be declared in frontends, so we can't check their
889 * existence in backends at configuration parsing step
890 */
891 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_rsp_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200892 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
893 rule->arg.capid.idx);
894 return 0;
895 }
896
897 return 1;
898}
899
900/* parse an "http-response capture" action. It takes a single argument which is
901 * a sample fetch expression. It stores the expression into arg->act.p[0] and
Thayne McCombs8f0cc5c2021-01-07 21:35:52 -0700902 * the allocated hdr_cap struct of the preallocated id into arg->act.p[1].
Willy Tarreau79e57332018-10-02 16:01:16 +0200903 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
904 */
905static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
906 struct act_rule *rule, char **err)
907{
908 struct sample_expr *expr;
909 int cur_arg;
910 int id;
911 char *error;
912
913 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
914 if (strcmp(args[cur_arg], "if") == 0 ||
915 strcmp(args[cur_arg], "unless") == 0)
916 break;
917
918 if (cur_arg < *orig_arg + 3) {
919 memprintf(err, "expects <expression> id <idx>");
920 return ACT_RET_PRS_ERR;
921 }
922
923 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100924 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +0200925 if (!expr)
926 return ACT_RET_PRS_ERR;
927
928 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
929 memprintf(err,
930 "fetch method '%s' extracts information from '%s', none of which is available here",
931 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100932 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200933 return ACT_RET_PRS_ERR;
934 }
935
936 if (!args[cur_arg] || !*args[cur_arg]) {
937 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100938 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200939 return ACT_RET_PRS_ERR;
940 }
941
942 if (strcmp(args[cur_arg], "id") != 0) {
943 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100944 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200945 return ACT_RET_PRS_ERR;
946 }
947
948 cur_arg++;
949
950 if (!args[cur_arg]) {
951 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100952 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200953 return ACT_RET_PRS_ERR;
954 }
955
956 id = strtol(args[cur_arg], &error, 10);
957 if (*error != '\0') {
958 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100959 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200960 return ACT_RET_PRS_ERR;
961 }
962 cur_arg++;
963
964 px->conf.args.ctx = ARGC_CAP;
965
966 rule->action = ACT_CUSTOM;
967 rule->action_ptr = http_action_res_capture_by_id;
968 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100969 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200970 rule->arg.capid.expr = expr;
971 rule->arg.capid.idx = id;
972
973 *orig_arg = cur_arg;
974 return ACT_RET_PRS_OK;
975}
976
Christopher Faulet81e20172019-12-12 16:40:30 +0100977/* Parse a "allow" action for a request or a response rule. It takes no argument. It
978 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
979 */
980static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
981 struct act_rule *rule, char **err)
982{
983 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100984 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100985 return ACT_RET_PRS_OK;
986}
987
Christopher Faulete0fca292020-01-13 21:49:03 +0100988/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200989 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
990 * error. It relies on http_parse_http_reply() to set
991 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100992 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100993static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
994 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100995{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200996 int default_status;
997 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100998
999 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +01001000 if (rule->from == ACT_F_HTTP_REQ) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001001 if (strcmp(args[cur_arg - 1], "tarpit") == 0) {
Christopher Faulete0fca292020-01-13 21:49:03 +01001002 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001003 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +01001004 }
Christopher Faulete0fca292020-01-13 21:49:03 +01001005 else {
1006 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001007 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +01001008 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001009 }
Christopher Faulete0fca292020-01-13 21:49:03 +01001010 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001011 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001012 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +01001013 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +01001014
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001015 /* If no args or only a deny_status specified, fallback on the legacy
1016 * mode and use default error files despite the fact that
1017 * default-errorfiles is not used. Otherwise, parse an http reply.
1018 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +01001019
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001020 /* Prepare parsing of log-format strings */
1021 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001022
Christopher Faulet9467f182020-06-30 09:32:01 +02001023 if (!*(args[cur_arg]) || strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001024 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
1025 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001026 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001027
1028 if (strcmp(args[cur_arg], "deny_status") == 0) {
Christopher Faulet9467f182020-06-30 09:32:01 +02001029 if (!*(args[cur_arg+2]) || strcmp(args[cur_arg+2], "if") == 0 || strcmp(args[cur_arg+2], "unless") == 0) {
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001030 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
1031 &arg, px, default_status, err);
1032 *orig_arg += 2;
1033 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001034 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001035 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001036 }
1037
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001038 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +01001039
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001040 end:
1041 if (!rule->arg.http_reply)
1042 return ACT_RET_PRS_ERR;
1043
1044 rule->flags |= ACT_FLAG_FINAL;
1045 rule->check_ptr = check_act_http_reply;
1046 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +01001047 return ACT_RET_PRS_OK;
1048}
1049
Christopher Fauletb3048832020-05-27 15:26:43 +02001050
1051/* This function executes a auth action. It builds an 401/407 HTX message using
1052 * the corresponding proxy's error message. On success, it returns
1053 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
1054 */
1055static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
1056 struct session *sess, struct stream *s, int flags)
1057{
1058 struct channel *req = &s->req;
1059 struct channel *res = &s->res;
1060 struct htx *htx = htx_from_buf(&res->buf);
1061 struct http_reply *reply;
1062 const char *auth_realm;
1063 struct http_hdr_ctx ctx;
1064 struct ist hdr;
1065
1066 /* Auth might be performed on regular http-req rules as well as on stats */
1067 auth_realm = rule->arg.http.str.ptr;
1068 if (!auth_realm) {
1069 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
1070 auth_realm = STATS_DEFAULT_REALM;
1071 else
1072 auth_realm = px->id;
1073 }
1074
1075 if (!(s->txn->flags & TX_USE_PX_CONN)) {
1076 s->txn->status = 401;
1077 hdr = ist("WWW-Authenticate");
1078 }
1079 else {
1080 s->txn->status = 407;
1081 hdr = ist("Proxy-Authenticate");
1082 }
1083 reply = http_error_message(s);
1084 channel_htx_truncate(res, htx);
1085
1086 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
1087 goto fail;
1088
1089 /* Write the generic 40x message */
1090 if (http_reply_to_htx(s, htx, reply) == -1)
1091 goto fail;
1092
1093 /* Remove all existing occurrences of the XXX-Authenticate header */
1094 ctx.blk = NULL;
1095 while (http_find_header(htx, hdr, &ctx, 1))
1096 http_remove_header(htx, &ctx);
1097
1098 /* Now a the right XXX-Authenticate header */
1099 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
1100 goto fail;
1101
1102 /* Finally forward the reply */
1103 htx_to_buf(htx, &res->buf);
1104 if (!http_forward_proxy_resp(s, 1))
1105 goto fail;
1106
1107 /* Note: Only eval on the request */
1108 s->logs.tv_request = now;
1109 req->analysers &= AN_REQ_FLT_END;
1110
1111 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
Willy Tarreau4781b152021-04-06 13:53:36 +02001112 _HA_ATOMIC_INC(&s->sess->fe->fe_counters.intercepted_req);
Christopher Fauletb3048832020-05-27 15:26:43 +02001113
1114 if (!(s->flags & SF_ERR_MASK))
1115 s->flags |= SF_ERR_LOCAL;
1116 if (!(s->flags & SF_FINST_MASK))
1117 s->flags |= SF_FINST_R;
1118
1119 stream_inc_http_err_ctr(s);
1120 return ACT_RET_ABRT;
1121
1122 fail:
1123 /* If an error occurred, remove the incomplete HTTP response from the
1124 * buffer */
1125 channel_htx_truncate(res, htx);
1126 return ACT_RET_ERR;
1127}
1128
Christopher Faulet81e20172019-12-12 16:40:30 +01001129/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
1130 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1131 */
1132static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
1133 struct act_rule *rule, char **err)
1134{
1135 int cur_arg;
1136
Christopher Fauletb3048832020-05-27 15:26:43 +02001137 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +01001138 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +02001139 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001140 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001141
1142 cur_arg = *orig_arg;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001143 if (strcmp(args[cur_arg], "realm") == 0) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001144 cur_arg++;
1145 if (!*args[cur_arg]) {
1146 memprintf(err, "missing realm value.\n");
1147 return ACT_RET_PRS_ERR;
1148 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001149 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1150 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001151 cur_arg++;
1152 }
1153
Christopher Fauletc20b3712020-01-27 15:51:56 +01001154 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001155 *orig_arg = cur_arg;
1156 return ACT_RET_PRS_OK;
1157}
1158
1159/* Parse a "set-nice" action. It takes the nice value as argument. It returns
1160 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1161 */
1162static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
1163 struct act_rule *rule, char **err)
1164{
1165 int cur_arg;
1166
1167 rule->action = ACT_HTTP_SET_NICE;
1168
1169 cur_arg = *orig_arg;
1170 if (!*args[cur_arg]) {
1171 memprintf(err, "expects exactly 1 argument (integer value)");
1172 return ACT_RET_PRS_ERR;
1173 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001174 rule->arg.http.i = atoi(args[cur_arg]);
1175 if (rule->arg.http.i < -1024)
1176 rule->arg.http.i = -1024;
1177 else if (rule->arg.http.i > 1024)
1178 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001179
Christopher Fauletc20b3712020-01-27 15:51:56 +01001180 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001181 *orig_arg = cur_arg + 1;
1182 return ACT_RET_PRS_OK;
1183}
1184
1185/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1186 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1187 */
1188static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1189 struct act_rule *rule, char **err)
1190{
1191#ifdef IP_TOS
1192 char *endp;
1193 int cur_arg;
1194
1195 rule->action = ACT_HTTP_SET_TOS;
1196
1197 cur_arg = *orig_arg;
1198 if (!*args[cur_arg]) {
1199 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1200 return ACT_RET_PRS_ERR;
1201 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001202 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001203 if (endp && *endp != '\0') {
1204 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1205 return ACT_RET_PRS_ERR;
1206 }
1207
Christopher Fauletc20b3712020-01-27 15:51:56 +01001208 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001209 *orig_arg = cur_arg + 1;
1210 return ACT_RET_PRS_OK;
1211#else
1212 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1213 return ACT_RET_PRS_ERR;
1214#endif
1215}
1216
1217/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1218 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1219 */
1220static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1221 struct act_rule *rule, char **err)
1222{
1223#ifdef SO_MARK
1224 char *endp;
1225 int cur_arg;
1226
1227 rule->action = ACT_HTTP_SET_MARK;
1228
1229 cur_arg = *orig_arg;
1230 if (!*args[cur_arg]) {
1231 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1232 return ACT_RET_PRS_ERR;
1233 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001234 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001235 if (endp && *endp != '\0') {
1236 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1237 return ACT_RET_PRS_ERR;
1238 }
1239
Christopher Fauletc20b3712020-01-27 15:51:56 +01001240 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001241 *orig_arg = cur_arg + 1;
1242 global.last_checks |= LSTCHK_NETADM;
1243 return ACT_RET_PRS_OK;
1244#else
1245 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1246 return ACT_RET_PRS_ERR;
1247#endif
1248}
1249
1250/* Parse a "set-log-level" action. It takes the level value as argument. It
1251 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1252 */
1253static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1254 struct act_rule *rule, char **err)
1255{
1256 int cur_arg;
1257
1258 rule->action = ACT_HTTP_SET_LOGL;
1259
1260 cur_arg = *orig_arg;
1261 if (!*args[cur_arg]) {
1262 bad_log_level:
1263 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1264 return ACT_RET_PRS_ERR;
1265 }
1266 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001267 rule->arg.http.i = -1;
1268 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001269 goto bad_log_level;
1270
Christopher Fauletc20b3712020-01-27 15:51:56 +01001271 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001272 *orig_arg = cur_arg + 1;
1273 return ACT_RET_PRS_OK;
1274}
1275
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001276/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1277 * 103 response header with <.arg.http.str> name and with a value built
1278 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001279 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001280 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1281 * block is added to terminate the response. On success, it returns
1282 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1283 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1284 * returned.
1285 */
1286static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1287 struct session *sess, struct stream *s, int flags)
1288{
1289 struct act_rule *prev_rule, *next_rule;
1290 struct channel *res = &s->res;
1291 struct htx *htx = htx_from_buf(&res->buf);
1292 struct buffer *value = alloc_trash_chunk();
1293 enum act_return ret = ACT_RET_CONT;
1294
1295 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1296 goto leave;
1297
1298 if (!value) {
1299 if (!(s->flags & SF_ERR_MASK))
1300 s->flags |= SF_ERR_RESOURCE;
1301 goto error;
1302 }
1303
1304 /* get previous and next rules */
1305 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1306 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1307
1308 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1309 * continue to add link to a previously started response */
1310 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1311 struct htx_sl *sl;
1312 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1313 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1314
1315 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1316 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1317 if (!sl)
1318 goto error;
1319 sl->info.res.status = 103;
1320 }
1321
1322 /* Add the HTTP Early Hint HTTP 103 response heade */
1323 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1324 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1325 goto error;
1326
1327 /* if it is the last rule or the next one is not an early-hint, terminate the current
1328 * response. */
1329 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001330 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1331 /* If an error occurred during an Early-hint rule,
1332 * remove the incomplete HTTP 103 response from the
1333 * buffer */
1334 goto error;
1335 }
1336
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001337 if (!http_forward_proxy_resp(s, 0))
1338 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001339 }
1340
1341 leave:
1342 free_trash_chunk(value);
1343 return ret;
1344
1345 error:
1346 /* If an error occurred during an Early-hint rule, remove the incomplete
1347 * HTTP 103 response from the buffer */
1348 channel_htx_truncate(res, htx);
1349 ret = ACT_RET_ERR;
1350 goto leave;
1351}
1352
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001353/* This function executes a set-header or add-header actions. It builds a string
1354 * in the trash from the specified format string. It finds the action to be
1355 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001356 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001357 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1358 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1359 * ACT_RET_ERR is returned.
1360 */
1361static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1362 struct session *sess, struct stream *s, int flags)
1363{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001364 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1365 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001366 enum act_return ret = ACT_RET_CONT;
1367 struct buffer *replace;
1368 struct http_hdr_ctx ctx;
1369 struct ist n, v;
1370
1371 replace = alloc_trash_chunk();
1372 if (!replace)
1373 goto fail_alloc;
1374
1375 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1376 n = rule->arg.http.str;
1377 v = ist2(replace->area, replace->data);
1378
1379 if (rule->action == 0) { // set-header
1380 /* remove all occurrences of the header */
1381 ctx.blk = NULL;
1382 while (http_find_header(htx, n, &ctx, 1))
1383 http_remove_header(htx, &ctx);
1384 }
1385
1386 /* Now add header */
1387 if (!http_add_header(htx, n, v))
1388 goto fail_rewrite;
1389
1390 leave:
1391 free_trash_chunk(replace);
1392 return ret;
1393
1394 fail_alloc:
1395 if (!(s->flags & SF_ERR_MASK))
1396 s->flags |= SF_ERR_RESOURCE;
1397 ret = ACT_RET_ERR;
1398 goto leave;
1399
1400 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +02001401 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001402 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +02001403 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +01001404 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02001405 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001406 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +02001407 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001408
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001409 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001410 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001411 if (!(s->flags & SF_ERR_MASK))
1412 s->flags |= SF_ERR_PRXCOND;
1413 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001414 goto leave;
1415}
1416
Christopher Faulet81e20172019-12-12 16:40:30 +01001417/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1418 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1419 * on success, ACT_RET_PRS_ERR on error.
1420 *
1421 * Note: same function is used for the request and the response. However
1422 * "early-hint" rules are only supported for request rules.
1423 */
1424static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1425 struct act_rule *rule, char **err)
1426{
Christopher Faulet81e20172019-12-12 16:40:30 +01001427 int cap, cur_arg;
1428
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001429 if (args[*orig_arg-1][0] == 'e') {
1430 rule->action = ACT_CUSTOM;
1431 rule->action_ptr = http_action_early_hint;
1432 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001433 else {
1434 if (args[*orig_arg-1][0] == 's')
1435 rule->action = 0; // set-header
1436 else
1437 rule->action = 1; // add-header
1438 rule->action_ptr = http_action_set_header;
1439 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001440 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001441
1442 cur_arg = *orig_arg;
1443 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1444 memprintf(err, "expects exactly 2 arguments");
1445 return ACT_RET_PRS_ERR;
1446 }
1447
Christopher Faulet81e20172019-12-12 16:40:30 +01001448
Christopher Faulet96bff762019-12-17 13:46:18 +01001449 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1450 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1451 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001452
1453 if (rule->from == ACT_F_HTTP_REQ) {
1454 px->conf.args.ctx = ARGC_HRQ;
1455 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1456 }
1457 else{
1458 px->conf.args.ctx = ARGC_HRS;
1459 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1460 }
1461
1462 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001463 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001464 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001465 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001466 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001467
1468 free(px->conf.lfs_file);
1469 px->conf.lfs_file = strdup(px->conf.args.file);
1470 px->conf.lfs_line = px->conf.args.line;
1471
1472 *orig_arg = cur_arg + 1;
1473 return ACT_RET_PRS_OK;
1474}
1475
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001476/* This function executes a replace-header or replace-value actions. It
1477 * builds a string in the trash from the specified format string. It finds
1478 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001479 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001480 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1481 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1482 * processing continue. Otherwsize ACT_RET_ERR is returned.
1483 */
1484static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1485 struct session *sess, struct stream *s, int flags)
1486{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001487 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1488 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001489 enum act_return ret = ACT_RET_CONT;
1490 struct buffer *replace;
1491 int r;
1492
1493 replace = alloc_trash_chunk();
1494 if (!replace)
1495 goto fail_alloc;
1496
1497 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1498
1499 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1500 if (r == -1)
1501 goto fail_rewrite;
1502
1503 leave:
1504 free_trash_chunk(replace);
1505 return ret;
1506
1507 fail_alloc:
1508 if (!(s->flags & SF_ERR_MASK))
1509 s->flags |= SF_ERR_RESOURCE;
1510 ret = ACT_RET_ERR;
1511 goto leave;
1512
1513 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +02001514 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001515 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +02001516 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +01001517 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02001518 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001519 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +02001520 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001521
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001522 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001523 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001524 if (!(s->flags & SF_ERR_MASK))
1525 s->flags |= SF_ERR_PRXCOND;
1526 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001527 goto leave;
1528}
1529
Christopher Faulet81e20172019-12-12 16:40:30 +01001530/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1531 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1532 * success, ACT_RET_PRS_ERR on error.
1533 */
1534static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1535 struct act_rule *rule, char **err)
1536{
1537 int cap, cur_arg;
1538
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001539 if (args[*orig_arg-1][8] == 'h')
1540 rule->action = 0; // replace-header
1541 else
1542 rule->action = 1; // replace-value
1543 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001544 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001545
1546 cur_arg = *orig_arg;
1547 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1548 memprintf(err, "expects exactly 3 arguments");
1549 return ACT_RET_PRS_ERR;
1550 }
1551
Christopher Faulet96bff762019-12-17 13:46:18 +01001552 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1553 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1554 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001555
1556 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001557 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001558 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001559 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001560 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001561
1562 if (rule->from == ACT_F_HTTP_REQ) {
1563 px->conf.args.ctx = ARGC_HRQ;
1564 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1565 }
1566 else{
1567 px->conf.args.ctx = ARGC_HRS;
1568 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1569 }
1570
1571 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001572 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001573 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001574 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001575 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001576 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001577
1578 free(px->conf.lfs_file);
1579 px->conf.lfs_file = strdup(px->conf.args.file);
1580 px->conf.lfs_line = px->conf.args.line;
1581
1582 *orig_arg = cur_arg + 1;
1583 return ACT_RET_PRS_OK;
1584}
1585
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001586/* This function executes a del-header action with selected matching mode for
1587 * header name. It finds the matching method to be performed in <.action>, previously
1588 * filled by function parse_http_del_header(). On success, it returns ACT_RET_CONT.
1589 * Otherwise ACT_RET_ERR is returned.
1590 */
1591static enum act_return http_action_del_header(struct act_rule *rule, struct proxy *px,
1592 struct session *sess, struct stream *s, int flags)
1593{
1594 struct http_hdr_ctx ctx;
1595 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1596 struct htx *htx = htxbuf(&msg->chn->buf);
1597 enum act_return ret = ACT_RET_CONT;
1598
1599 /* remove all occurrences of the header */
1600 ctx.blk = NULL;
1601 switch (rule->action) {
1602 case PAT_MATCH_STR:
1603 while (http_find_header(htx, rule->arg.http.str, &ctx, 1))
1604 http_remove_header(htx, &ctx);
1605 break;
1606 case PAT_MATCH_BEG:
1607 while (http_find_pfx_header(htx, rule->arg.http.str, &ctx, 1))
1608 http_remove_header(htx, &ctx);
1609 break;
1610 case PAT_MATCH_END:
1611 while (http_find_sfx_header(htx, rule->arg.http.str, &ctx, 1))
1612 http_remove_header(htx, &ctx);
1613 break;
1614 case PAT_MATCH_SUB:
1615 while (http_find_sub_header(htx, rule->arg.http.str, &ctx, 1))
1616 http_remove_header(htx, &ctx);
1617 break;
1618 case PAT_MATCH_REG:
1619 while (http_match_header(htx, rule->arg.http.re, &ctx, 1))
1620 http_remove_header(htx, &ctx);
1621 break;
1622 default:
1623 return ACT_RET_ERR;
1624 }
1625 return ret;
1626}
1627
1628/* Parse a "del-header" action. It takes string as a required argument,
1629 * optional flag (currently only -m) and optional matching method of input string
1630 * with header name to be deleted. Default matching method is exact match (-m str).
1631 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001632 */
1633static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1634 struct act_rule *rule, char **err)
1635{
1636 int cur_arg;
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001637 int pat_idx;
Christopher Faulet81e20172019-12-12 16:40:30 +01001638
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001639 /* set exact matching (-m str) as default */
1640 rule->action = PAT_MATCH_STR;
1641 rule->action_ptr = http_action_del_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001642 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001643
1644 cur_arg = *orig_arg;
1645 if (!*args[cur_arg]) {
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001646 memprintf(err, "expects at least 1 argument");
Christopher Faulet81e20172019-12-12 16:40:30 +01001647 return ACT_RET_PRS_ERR;
1648 }
1649
Christopher Faulet96bff762019-12-17 13:46:18 +01001650 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1651 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001652 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1653
Maciej Zdeb6dee9962020-11-23 16:03:09 +00001654 LIST_INIT(&rule->arg.http.fmt);
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001655 if (strcmp(args[cur_arg+1], "-m") == 0) {
1656 cur_arg++;
1657 if (!*args[cur_arg+1]) {
1658 memprintf(err, "-m flag expects exactly 1 argument");
1659 return ACT_RET_PRS_ERR;
1660 }
1661
1662 cur_arg++;
1663 pat_idx = pat_find_match_name(args[cur_arg]);
1664 switch (pat_idx) {
1665 case PAT_MATCH_REG:
1666 if (!(rule->arg.http.re = regex_comp(rule->arg.http.str.ptr, 1, 1, err)))
1667 return ACT_RET_PRS_ERR;
1668 /* fall through */
1669 case PAT_MATCH_STR:
1670 case PAT_MATCH_BEG:
1671 case PAT_MATCH_END:
1672 case PAT_MATCH_SUB:
1673 rule->action = pat_idx;
1674 break;
1675 default:
1676 memprintf(err, "-m with unsupported matching method '%s'", args[cur_arg]);
1677 return ACT_RET_PRS_ERR;
1678 }
1679 }
1680
Christopher Faulet81e20172019-12-12 16:40:30 +01001681 *orig_arg = cur_arg + 1;
1682 return ACT_RET_PRS_OK;
1683}
1684
Christopher Faulet2eb53962020-01-14 14:47:34 +01001685/* Release memory allocated by an http redirect action. */
1686static void release_http_redir(struct act_rule *rule)
1687{
1688 struct logformat_node *lf, *lfb;
1689 struct redirect_rule *redir;
1690
1691 redir = rule->arg.redir;
1692 LIST_DEL(&redir->list);
1693 if (redir->cond) {
1694 prune_acl_cond(redir->cond);
1695 free(redir->cond);
1696 }
1697 free(redir->rdr_str);
1698 free(redir->cookie_str);
1699 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1700 LIST_DEL(&lf->list);
1701 free(lf);
1702 }
1703 free(redir);
1704}
1705
Christopher Faulet81e20172019-12-12 16:40:30 +01001706/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1707 * ACT_RET_PRS_ERR on error.
1708 */
1709static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1710 struct act_rule *rule, char **err)
1711{
1712 struct redirect_rule *redir;
1713 int dir, cur_arg;
1714
1715 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001716 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001717 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001718
1719 cur_arg = *orig_arg;
1720
1721 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1722 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1723 return ACT_RET_PRS_ERR;
1724
1725 rule->arg.redir = redir;
1726 rule->cond = redir->cond;
1727 redir->cond = NULL;
1728
1729 /* skip all arguments */
1730 while (*args[cur_arg])
1731 cur_arg++;
1732
1733 *orig_arg = cur_arg;
1734 return ACT_RET_PRS_OK;
1735}
1736
Christopher Faulet046cf442019-12-17 15:45:23 +01001737/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1738 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1739 */
1740static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1741 struct session *sess, struct stream *s, int flags)
1742{
1743 struct pat_ref *ref;
1744 struct buffer *key = NULL, *value = NULL;
1745 enum act_return ret = ACT_RET_CONT;
1746
1747 /* collect reference */
1748 ref = pat_ref_lookup(rule->arg.map.ref);
1749 if (!ref)
1750 goto leave;
1751
1752 /* allocate key */
1753 key = alloc_trash_chunk();
1754 if (!key)
1755 goto fail_alloc;
1756
1757 /* collect key */
1758 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1759 key->area[key->data] = '\0';
1760
1761 switch (rule->action) {
1762 case 0: // add-acl
1763 /* add entry only if it does not already exist */
1764 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1765 if (pat_ref_find_elt(ref, key->area) == NULL)
1766 pat_ref_add(ref, key->area, NULL, NULL);
1767 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1768 break;
1769
1770 case 1: // set-map
1771 /* allocate value */
1772 value = alloc_trash_chunk();
1773 if (!value)
1774 goto fail_alloc;
1775
1776 /* collect value */
1777 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1778 value->area[value->data] = '\0';
1779
1780 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1781 if (pat_ref_find_elt(ref, key->area) != NULL) {
1782 /* update entry if it exists */
1783 pat_ref_set(ref, key->area, value->area, NULL);
1784 }
1785 else {
1786 /* insert a new entry */
1787 pat_ref_add(ref, key->area, value->area, NULL);
1788 }
1789 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1790 break;
1791
1792 case 2: // del-acl
1793 case 3: // del-map
1794 /* returned code: 1=ok, 0=ko */
1795 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1796 pat_ref_delete(ref, key->area);
1797 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1798 break;
1799
1800 default:
1801 ret = ACT_RET_ERR;
1802 }
1803
1804
1805 leave:
1806 free_trash_chunk(key);
1807 free_trash_chunk(value);
1808 return ret;
1809
1810 fail_alloc:
1811 if (!(s->flags & SF_ERR_MASK))
1812 s->flags |= SF_ERR_RESOURCE;
1813 ret = ACT_RET_ERR;
1814 goto leave;
1815}
1816
Christopher Faulet2eb53962020-01-14 14:47:34 +01001817/* Release memory allocated by an http map/acl action. */
1818static void release_http_map(struct act_rule *rule)
1819{
1820 struct logformat_node *lf, *lfb;
1821
1822 free(rule->arg.map.ref);
1823 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1824 LIST_DEL(&lf->list);
1825 release_sample_expr(lf->expr);
1826 free(lf->arg);
1827 free(lf);
1828 }
1829 if (rule->action == 1) {
1830 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1831 LIST_DEL(&lf->list);
1832 release_sample_expr(lf->expr);
1833 free(lf->arg);
1834 free(lf);
1835 }
1836 }
1837}
1838
Christopher Faulet81e20172019-12-12 16:40:30 +01001839/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001840 * two log-format string as argument depending on the action. The action is
1841 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1842 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001843 */
1844static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1845 struct act_rule *rule, char **err)
1846{
1847 int cap, cur_arg;
1848
Christopher Faulet046cf442019-12-17 15:45:23 +01001849 if (args[*orig_arg-1][0] == 'a') // add-acl
1850 rule->action = 0;
1851 else if (args[*orig_arg-1][0] == 's') // set-map
1852 rule->action = 1;
1853 else if (args[*orig_arg-1][4] == 'a') // del-acl
1854 rule->action = 2;
1855 else if (args[*orig_arg-1][4] == 'm') // del-map
1856 rule->action = 3;
1857 else {
1858 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1859 return ACT_RET_PRS_ERR;
1860 }
1861 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001862 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001863
1864 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001865 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1866 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001867 memprintf(err, "expects exactly 2 arguments");
1868 return ACT_RET_PRS_ERR;
1869 }
1870 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001871 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001872 memprintf(err, "expects exactly 1 arguments");
1873 return ACT_RET_PRS_ERR;
1874 }
1875
1876 /*
1877 * '+ 8' for 'set-map(' (same for del-map)
1878 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1879 */
1880 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1881
1882 if (rule->from == ACT_F_HTTP_REQ) {
1883 px->conf.args.ctx = ARGC_HRQ;
1884 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1885 }
1886 else{
1887 px->conf.args.ctx = ARGC_HRS;
1888 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1889 }
1890
1891 /* key pattern */
1892 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001893 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1894 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001895 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001896 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001897
Christopher Faulet046cf442019-12-17 15:45:23 +01001898 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001899 /* value pattern for set-map only */
1900 cur_arg++;
1901 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001902 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1903 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001904 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001905 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001906 }
1907
1908 free(px->conf.lfs_file);
1909 px->conf.lfs_file = strdup(px->conf.args.file);
1910 px->conf.lfs_line = px->conf.args.line;
1911
1912 *orig_arg = cur_arg + 1;
1913 return ACT_RET_PRS_OK;
1914}
1915
Christopher Fauletac98d812019-12-18 09:20:16 +01001916/* This function executes a track-sc* actions. On success, it returns
1917 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1918 */
1919static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1920 struct session *sess, struct stream *s, int flags)
1921{
1922 struct stktable *t;
1923 struct stksess *ts;
1924 struct stktable_key *key;
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001925 void *ptr1, *ptr2, *ptr3, *ptr4, *ptr5, *ptr6;
Christopher Fauletac98d812019-12-18 09:20:16 +01001926 int opt;
1927
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001928 ptr1 = ptr2 = ptr3 = ptr4 = ptr5 = ptr6 = NULL;
Christopher Fauletac98d812019-12-18 09:20:16 +01001929 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1930
1931 t = rule->arg.trk_ctr.table.t;
Emeric Brun362d25e2021-03-10 16:58:03 +01001932
1933 if (stkctr_entry(&s->stkctr[rule->action]))
1934 goto end;
1935
Christopher Fauletac98d812019-12-18 09:20:16 +01001936 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1937
1938 if (!key)
1939 goto end;
1940 ts = stktable_get_entry(t, key);
1941 if (!ts)
1942 goto end;
1943
1944 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1945
1946 /* let's count a new HTTP request as it's the first time we do it */
1947 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1948 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1949
1950 /* When the client triggers a 4xx from the server, it's most often due
1951 * to a missing object or permission. These events should be tracked
1952 * because if they happen often, it may indicate a brute force or a
1953 * vulnerability scan. Normally this is done when receiving the response
1954 * but here we're tracking after this ought to have been done so we have
1955 * to do it on purpose.
1956 */
1957 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1958 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1959 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1960 }
1961
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001962 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 500) < 100 &&
1963 s->txn->status != 501 && s->txn->status != 505) {
1964 ptr5 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_FAIL_CNT);
1965 ptr6 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_FAIL_RATE);
1966 }
1967
1968 if (ptr1 || ptr2 || ptr3 || ptr4 || ptr5 || ptr6) {
Christopher Fauletac98d812019-12-18 09:20:16 +01001969 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1970
1971 if (ptr1)
1972 stktable_data_cast(ptr1, http_req_cnt)++;
1973 if (ptr2)
1974 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1975 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1976 if (ptr3)
1977 stktable_data_cast(ptr3, http_err_cnt)++;
1978 if (ptr4)
1979 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1980 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001981 if (ptr5)
1982 stktable_data_cast(ptr5, http_fail_cnt)++;
1983 if (ptr6)
1984 update_freq_ctr_period(&stktable_data_cast(ptr6, http_fail_rate),
1985 t->data_arg[STKTABLE_DT_HTTP_FAIL_RATE].u, 1);
Christopher Fauletac98d812019-12-18 09:20:16 +01001986
1987 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1988
1989 /* If data was modified, we need to touch to re-schedule sync */
1990 stktable_touch_local(t, ts, 0);
1991 }
1992
1993 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1994 if (sess->fe != s->be)
1995 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1996
1997 end:
1998 return ACT_RET_CONT;
1999}
Christopher Faulet81e20172019-12-12 16:40:30 +01002000
Christopher Faulet2eb53962020-01-14 14:47:34 +01002001static void release_http_track_sc(struct act_rule *rule)
2002{
2003 release_sample_expr(rule->arg.trk_ctr.expr);
2004}
2005
Christopher Faulet81e20172019-12-12 16:40:30 +01002006/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
2007 * ACT_RET_PRS_ERR on error.
2008 */
2009static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
2010 struct act_rule *rule, char **err)
2011{
2012 struct sample_expr *expr;
2013 unsigned int where;
2014 unsigned int tsc_num;
2015 const char *tsc_num_str;
2016 int cur_arg;
2017
2018 tsc_num_str = &args[*orig_arg-1][8];
2019 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
2020 return ACT_RET_PRS_ERR;
2021
2022 cur_arg = *orig_arg;
2023 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01002024 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01002025 if (!expr)
2026 return ACT_RET_PRS_ERR;
2027
2028 where = 0;
2029 if (px->cap & PR_CAP_FE)
2030 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
2031 if (px->cap & PR_CAP_BE)
2032 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
2033
2034 if (!(expr->fetch->val & where)) {
2035 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
2036 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01002037 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01002038 return ACT_RET_PRS_ERR;
2039 }
2040
2041 if (strcmp(args[cur_arg], "table") == 0) {
2042 cur_arg++;
2043 if (!*args[cur_arg]) {
2044 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01002045 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01002046 return ACT_RET_PRS_ERR;
2047 }
2048
2049 /* we copy the table name for now, it will be resolved later */
2050 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
2051 cur_arg++;
2052 }
2053
Christopher Fauletac98d812019-12-18 09:20:16 +01002054 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01002055 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01002056 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01002057 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01002058 rule->check_ptr = check_trk_action;
2059
2060 *orig_arg = cur_arg;
2061 return ACT_RET_PRS_OK;
2062}
2063
Amaury Denoyelle8d228232020-12-10 13:43:54 +01002064static enum act_return action_timeout_set_stream_timeout(struct act_rule *rule,
2065 struct proxy *px,
2066 struct session *sess,
2067 struct stream *s,
2068 int flags)
2069{
2070 struct sample *key;
2071
2072 if (rule->arg.timeout.expr) {
2073 key = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.timeout.expr, SMP_T_SINT);
2074 if (!key)
2075 return ACT_RET_CONT;
2076
2077 stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(key->data.u.sint));
2078 }
2079 else {
2080 stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(rule->arg.timeout.value));
2081 }
2082
2083 return ACT_RET_CONT;
2084}
2085
2086/* Parse a "set-timeout" action. Returns ACT_RET_PRS_ERR if parsing error.
2087 */
2088static enum act_parse_ret parse_http_set_timeout(const char **args,
2089 int *orig_arg,
2090 struct proxy *px,
2091 struct act_rule *rule, char **err)
2092{
2093 int cur_arg;
2094
2095 rule->action = ACT_CUSTOM;
2096 rule->action_ptr = action_timeout_set_stream_timeout;
2097 rule->release_ptr = release_timeout_action;
2098
2099 cur_arg = *orig_arg;
2100 if (!*args[cur_arg] || !*args[cur_arg + 1]) {
2101 memprintf(err, "expects exactly 2 arguments");
2102 return ACT_RET_PRS_ERR;
2103 }
2104
2105 if (!(px->cap & PR_CAP_BE)) {
2106 memprintf(err, "proxy '%s' has no backend capability", px->id);
2107 return ACT_RET_PRS_ERR;
2108 }
2109
2110 if (cfg_parse_rule_set_timeout(args, cur_arg,
2111 &rule->arg.timeout.value,
2112 &rule->arg.timeout.type,
2113 &rule->arg.timeout.expr,
2114 err,
2115 px->conf.args.file,
2116 px->conf.args.line, &px->conf.args) == -1) {
2117 return ACT_RET_PRS_ERR;
2118 }
2119
2120 *orig_arg = cur_arg + 2;
2121
2122 return ACT_RET_PRS_OK;
2123}
2124
Christopher Faulet46f95542019-12-20 10:07:22 +01002125/* This function executes a strict-mode actions. On success, it always returns
2126 * ACT_RET_CONT
2127 */
2128static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
2129 struct session *sess, struct stream *s, int flags)
2130{
2131 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
2132
2133 if (rule->action == 0) // strict-mode on
2134 msg->flags &= ~HTTP_MSGF_SOFT_RW;
2135 else // strict-mode off
2136 msg->flags |= HTTP_MSGF_SOFT_RW;
2137 return ACT_RET_CONT;
2138}
2139
2140/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
2141 * ACT_RET_PRS_ERR on error.
2142 */
2143static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
2144 struct act_rule *rule, char **err)
2145{
2146 int cur_arg;
2147
Christopher Faulet46f95542019-12-20 10:07:22 +01002148 cur_arg = *orig_arg;
2149 if (!*args[cur_arg]) {
2150 memprintf(err, "expects exactly 1 arguments");
2151 return ACT_RET_PRS_ERR;
2152 }
2153
2154 if (strcasecmp(args[cur_arg], "on") == 0)
2155 rule->action = 0; // strict-mode on
2156 else if (strcasecmp(args[cur_arg], "off") == 0)
2157 rule->action = 1; // strict-mode off
2158 else {
2159 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
2160 return ACT_RET_PRS_ERR;
2161 }
2162 rule->action_ptr = http_action_strict_mode;
2163
2164 *orig_arg = cur_arg + 1;
2165 return ACT_RET_PRS_OK;
2166}
2167
Christopher Faulet24231ab2020-01-24 17:44:23 +01002168/* This function executes a return action. It builds an HTX message from an
2169 * errorfile, an raw file or a log-format string, depending on <.action>
2170 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
2171 * returned.
2172 */
2173static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
2174 struct session *sess, struct stream *s, int flags)
2175{
2176 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002177
Christopher Faulet2d36df22021-02-19 11:41:01 +01002178 s->txn->status = rule->arg.http_reply->status;
Christopher Faulet0e2ad612020-05-13 16:38:37 +02002179 if (http_reply_message(s, rule->arg.http_reply) == -1)
2180 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002181
Christopher Faulet24231ab2020-01-24 17:44:23 +01002182 if (rule->from == ACT_F_HTTP_REQ) {
2183 /* let's log the request time */
2184 s->logs.tv_request = now;
2185 req->analysers &= AN_REQ_FLT_END;
2186
2187 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
Willy Tarreau4781b152021-04-06 13:53:36 +02002188 _HA_ATOMIC_INC(&s->sess->fe->fe_counters.intercepted_req);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002189 }
2190
2191 if (!(s->flags & SF_ERR_MASK))
2192 s->flags |= SF_ERR_LOCAL;
2193 if (!(s->flags & SF_FINST_MASK))
2194 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
2195
Christopher Faulet0e2ad612020-05-13 16:38:37 +02002196 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002197}
2198
Christopher Faulet24231ab2020-01-24 17:44:23 +01002199/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02002200 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
2201 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01002202 */
2203static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
2204 struct act_rule *rule, char **err)
2205{
Christopher Faulet47e791e2020-05-13 14:36:55 +02002206 /* Prepare parsing of log-format strings */
2207 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
2208 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
2209 if (!rule->arg.http_reply)
2210 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002211
Christopher Fauletba946bf2020-05-13 08:50:07 +02002212 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002213 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02002214 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002215 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02002216 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002217 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002218}
2219
Christopher Faulet021a8e42021-03-29 10:46:38 +02002220
2221
2222/* This function executes a wait-for-body action. It waits for the message
2223 * payload for a max configured time (.arg.p[0]) and eventually for only first
2224 * <arg.p[1]> bytes (0 means no limit). It relies on http_wait_for_msg_body()
2225 * function. it returns ACT_RET_CONT when conditions are met to stop to wait.
2226 * Otherwise ACT_RET_YIELD is returned to wait for more data. ACT_RET_INV is
2227 * returned if a parsing error is raised by lower level and ACT_RET_ERR if an
2228 * internal error occured. Finally ACT_RET_ABRT is returned when a timeout
2229 * occured.
2230 */
2231static enum act_return http_action_wait_for_body(struct act_rule *rule, struct proxy *px,
2232 struct session *sess, struct stream *s, int flags)
2233{
2234 struct channel *chn = ((rule->from == ACT_F_HTTP_REQ) ? &s->req : &s->res);
2235 unsigned int time = (uintptr_t)rule->arg.act.p[0];
2236 unsigned int bytes = (uintptr_t)rule->arg.act.p[1];
2237
2238 switch (http_wait_for_msg_body(s, chn, time, bytes)) {
2239 case HTTP_RULE_RES_CONT:
2240 return ACT_RET_CONT;
2241 case HTTP_RULE_RES_YIELD:
2242 return ACT_RET_YIELD;
2243 case HTTP_RULE_RES_BADREQ:
2244 return ACT_RET_INV;
2245 case HTTP_RULE_RES_ERROR:
2246 return ACT_RET_ERR;
2247 case HTTP_RULE_RES_ABRT:
2248 return ACT_RET_ABRT;
2249 default:
2250 return ACT_RET_ERR;
2251 }
2252}
2253
2254/* Parse a "wait-for-body" action. It returns ACT_RET_PRS_OK on success,
2255 * ACT_RET_PRS_ERR on error.
2256 */
2257static enum act_parse_ret parse_http_wait_for_body(const char **args, int *orig_arg, struct proxy *px,
2258 struct act_rule *rule, char **err)
2259{
2260 int cur_arg;
2261 unsigned int time, bytes;
2262 const char *res;
2263
2264 cur_arg = *orig_arg;
2265 if (!*args[cur_arg]) {
2266 memprintf(err, "expects time <time> [ at-least <bytes> ]");
2267 return ACT_RET_PRS_ERR;
2268 }
2269
2270 time = UINT_MAX; /* To be sure it is set */
2271 bytes = 0; /* Default value, wait all the body */
2272 while (*(args[cur_arg])) {
2273 if (strcmp(args[cur_arg], "time") == 0) {
2274 if (!*args[cur_arg + 1]) {
2275 memprintf(err, "missing argument for '%s'", args[cur_arg]);
2276 return ACT_RET_PRS_ERR;
2277 }
2278 res = parse_time_err(args[cur_arg+1], &time, TIME_UNIT_MS);
2279 if (res == PARSE_TIME_OVER) {
2280 memprintf(err, "time overflow (maximum value is 2147483647 ms or ~24.8 days)");
2281 return ACT_RET_PRS_ERR;
2282 }
2283 if (res == PARSE_TIME_UNDER) {
2284 memprintf(err, "time underflow (minimum non-null value is 1 ms)");
2285 return ACT_RET_PRS_ERR;
2286 }
2287 if (res) {
2288 memprintf(err, "unexpected character '%c'", *res);
2289 return ACT_RET_PRS_ERR;
2290 }
2291 cur_arg++;
2292 }
2293 else if (strcmp(args[cur_arg], "at-least") == 0) {
2294 if (!*args[cur_arg + 1]) {
2295 memprintf(err, "missing argument for '%s'", args[cur_arg]);
2296 return ACT_RET_PRS_ERR;
2297 }
2298 res = parse_size_err(args[cur_arg+1], &bytes);
2299 if (res) {
2300 memprintf(err, "unexpected character '%c'", *res);
2301 return ACT_RET_PRS_ERR;
2302 }
2303 cur_arg++;
2304 }
2305 else
2306 break;
2307 cur_arg++;
2308 }
2309
2310 if (time == UINT_MAX) {
2311 memprintf(err, "expects time <time> [ at-least <bytes> ]");
2312 return ACT_RET_PRS_ERR;
2313 }
2314
2315 rule->arg.act.p[0] = (void *)(uintptr_t)time;
2316 rule->arg.act.p[1] = (void *)(uintptr_t)bytes;
2317
2318 *orig_arg = cur_arg;
2319
2320 rule->action = ACT_CUSTOM;
2321 rule->action_ptr = http_action_wait_for_body;
2322 return ACT_RET_PRS_OK;
2323}
2324
Willy Tarreau79e57332018-10-02 16:01:16 +02002325/************************************************************************/
2326/* All supported http-request action keywords must be declared here. */
2327/************************************************************************/
2328
2329static struct action_kw_list http_req_actions = {
2330 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002331 { "add-acl", parse_http_set_map, 1 },
2332 { "add-header", parse_http_set_header, 0 },
2333 { "allow", parse_http_allow, 0 },
2334 { "auth", parse_http_auth, 0 },
2335 { "capture", parse_http_req_capture, 0 },
2336 { "del-acl", parse_http_set_map, 1 },
2337 { "del-header", parse_http_del_header, 0 },
2338 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002339 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002340 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
2341 { "early-hint", parse_http_set_header, 0 },
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +02002342 { "normalize-uri", parse_http_normalize_uri, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002343 { "redirect", parse_http_redirect, 0 },
2344 { "reject", parse_http_action_reject, 0 },
2345 { "replace-header", parse_http_replace_header, 0 },
2346 { "replace-path", parse_replace_uri, 0 },
Christopher Faulet312294f2020-09-02 17:17:44 +02002347 { "replace-pathq", parse_replace_uri, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002348 { "replace-uri", parse_replace_uri, 0 },
2349 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002350 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002351 { "set-header", parse_http_set_header, 0 },
2352 { "set-log-level", parse_http_set_log_level, 0 },
2353 { "set-map", parse_http_set_map, 1 },
2354 { "set-method", parse_set_req_line, 0 },
2355 { "set-mark", parse_http_set_mark, 0 },
2356 { "set-nice", parse_http_set_nice, 0 },
2357 { "set-path", parse_set_req_line, 0 },
Christopher Faulet312294f2020-09-02 17:17:44 +02002358 { "set-pathq", parse_set_req_line, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002359 { "set-query", parse_set_req_line, 0 },
2360 { "set-tos", parse_http_set_tos, 0 },
2361 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002362 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002363 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002364 { "track-sc", parse_http_track_sc, 1 },
Amaury Denoyelle8d228232020-12-10 13:43:54 +01002365 { "set-timeout", parse_http_set_timeout, 0 },
Christopher Faulet021a8e42021-03-29 10:46:38 +02002366 { "wait-for-body", parse_http_wait_for_body, 0 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002367 { NULL, NULL }
2368 }
2369};
2370
Willy Tarreau0108d902018-11-25 19:14:37 +01002371INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
2372
Willy Tarreau79e57332018-10-02 16:01:16 +02002373static struct action_kw_list http_res_actions = {
2374 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002375 { "add-acl", parse_http_set_map, 1 },
2376 { "add-header", parse_http_set_header, 0 },
2377 { "allow", parse_http_allow, 0 },
2378 { "capture", parse_http_res_capture, 0 },
2379 { "del-acl", parse_http_set_map, 1 },
2380 { "del-header", parse_http_del_header, 0 },
2381 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002382 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002383 { "redirect", parse_http_redirect, 0 },
2384 { "replace-header", parse_http_replace_header, 0 },
2385 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002386 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002387 { "set-header", parse_http_set_header, 0 },
2388 { "set-log-level", parse_http_set_log_level, 0 },
2389 { "set-map", parse_http_set_map, 1 },
2390 { "set-mark", parse_http_set_mark, 0 },
2391 { "set-nice", parse_http_set_nice, 0 },
2392 { "set-status", parse_http_set_status, 0 },
2393 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002394 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002395 { "track-sc", parse_http_track_sc, 1 },
Christopher Faulet021a8e42021-03-29 10:46:38 +02002396 { "wait-for-body", parse_http_wait_for_body, 0 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002397 { NULL, NULL }
2398 }
2399};
2400
Willy Tarreau0108d902018-11-25 19:14:37 +01002401INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02002402
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01002403static struct action_kw_list http_after_res_actions = {
2404 .kw = {
2405 { "add-header", parse_http_set_header, 0 },
2406 { "allow", parse_http_allow, 0 },
2407 { "del-header", parse_http_del_header, 0 },
2408 { "replace-header", parse_http_replace_header, 0 },
2409 { "replace-value", parse_http_replace_header, 0 },
2410 { "set-header", parse_http_set_header, 0 },
2411 { "set-status", parse_http_set_status, 0 },
2412 { "strict-mode", parse_http_strict_mode, 0 },
2413 { NULL, NULL }
2414 }
2415};
2416
2417INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
2418
Willy Tarreau79e57332018-10-02 16:01:16 +02002419/*
2420 * Local variables:
2421 * c-indent-level: 8
2422 * c-basic-offset: 8
2423 * End:
2424 */