blob: 134c9037bdf0db013ebb45ee6a6ed857e44a1793 [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) {
218 case ACT_NORMALIZE_URI_PLACEHOLDER:
219 (void) uri;
220 }
221
222 switch (err) {
223 case URI_NORMALIZER_ERR_NONE:
224 break;
225 case URI_NORMALIZER_ERR_INTERNAL_ERROR:
226 ret = ACT_RET_ERR;
227 break;
228 case URI_NORMALIZER_ERR_INVALID_INPUT:
229 ret = ACT_RET_INV;
230 break;
231 case URI_NORMALIZER_ERR_ALLOC:
232 goto fail_alloc;
233 }
234
235 leave:
236 free_trash_chunk(replace);
237 return ret;
238
239 fail_alloc:
240 if (!(s->flags & SF_ERR_MASK))
241 s->flags |= SF_ERR_RESOURCE;
242 ret = ACT_RET_ERR;
243 goto leave;
244
245 fail_rewrite:
246 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
247 if (s->flags & SF_BE_ASSIGNED)
248 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
249 if (sess->listener && sess->listener->counters)
250 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
251 if (objt_server(s->target))
252 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
253
254 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
255 ret = ACT_RET_ERR;
256 if (!(s->flags & SF_ERR_MASK))
257 s->flags |= SF_ERR_PRXCOND;
258 }
259 goto leave;
260}
261
262/* Parses the http-request normalize-uri action. It expects a single <normalizer>
263 * argument, corresponding too a value in `enum act_normalize_uri`.
264 *
265 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
266 */
267static enum act_parse_ret parse_http_normalize_uri(const char **args, int *orig_arg, struct proxy *px,
268 struct act_rule *rule, char **err)
269{
270 int cur_arg = *orig_arg;
271
272 rule->action_ptr = http_action_normalize_uri;
273 rule->release_ptr = NULL;
274
275 if (!*args[cur_arg]) {
276 memprintf(err, "missing argument <normalizer>");
277 return ACT_RET_PRS_ERR;
278 }
279
280 if (0) {
281
282 }
283 else {
284 memprintf(err, "unknown normalizer '%s'", args[cur_arg]);
285 return ACT_RET_PRS_ERR;
286 }
287
288 *orig_arg = cur_arg;
289 return ACT_RET_PRS_OK;
290}
291
Willy Tarreau33810222019-06-12 17:44:02 +0200292/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100293 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200294 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100295 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100296 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100297 * contains 1 for the path or 3 for the URI (values used by
298 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
299 * occurs while soft rewrites are enabled, the action is canceled, but the rule
300 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200301 */
302static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
303 struct session *sess, struct stream *s, int flags)
304{
Christopher Faulet13403762019-12-13 09:01:57 +0100305 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200306 struct buffer *replace, *output;
307 struct ist uri;
308 int len;
309
310 replace = alloc_trash_chunk();
311 output = alloc_trash_chunk();
312 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100313 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200314 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100315
Christopher Faulet1fa0cc12020-09-02 11:10:38 +0200316 if (rule->action == 1) // replace-path
317 uri = iststop(http_get_path(uri), '?');
Christopher Faulet312294f2020-09-02 17:17:44 +0200318 else if (rule->action == 4) // replace-pathq
319 uri = http_get_path(uri);
Willy Tarreau262c3f12019-12-17 06:52:51 +0100320
Christopher Faulet96bff762019-12-17 13:46:18 +0100321 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200322 goto leave;
323
Christopher Faulet96bff762019-12-17 13:46:18 +0100324 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200325
326 /* note: uri.ptr doesn't need to be zero-terminated because it will
327 * only be used to pick pmatch references.
328 */
329 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
330 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100331 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200332
Christopher Faulet2c22a692019-12-18 15:39:56 +0100333 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100334 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200335
Christopher Faulete00d06c2019-12-16 17:18:42 +0100336 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200337 free_trash_chunk(output);
338 free_trash_chunk(replace);
339 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100340
341 fail_alloc:
342 if (!(s->flags & SF_ERR_MASK))
343 s->flags |= SF_ERR_RESOURCE;
344 ret = ACT_RET_ERR;
345 goto leave;
346
347 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +0200348 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100349 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +0200350 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +0100351 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200352 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100353 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +0200354 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100355
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100356 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100357 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100358 if (!(s->flags & SF_ERR_MASK))
359 s->flags |= SF_ERR_PRXCOND;
360 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100361 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200362}
363
Christopher Faulet312294f2020-09-02 17:17:44 +0200364/* parse a "replace-uri", "replace-path" or "replace-pathq"
365 * http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200366 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100367 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100368 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200369 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
370 */
371static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
372 struct act_rule *rule, char **err)
373{
374 int cur_arg = *orig_arg;
375 char *error = NULL;
376
Christopher Faulet312294f2020-09-02 17:17:44 +0200377 switch (args[0][8]) {
378 case 'p':
379 if (args[0][12] == 'q')
380 rule->action = 4; // replace-pathq, same as set-pathq
381 else
382 rule->action = 1; // replace-path, same as set-path
383 break;
384 case 'u':
Christopher Faulet2c22a692019-12-18 15:39:56 +0100385 rule->action = 3; // replace-uri, same as set-uri
Christopher Faulet312294f2020-09-02 17:17:44 +0200386 break;
387 default:
388 memprintf(err, "internal error: unhandled action '%s'", args[0]);
389 return ACT_RET_PRS_ERR;
390 }
Willy Tarreau262c3f12019-12-17 06:52:51 +0100391
Willy Tarreau33810222019-06-12 17:44:02 +0200392 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100393 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200394
395 if (!*args[cur_arg] || !*args[cur_arg+1] ||
396 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
397 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
398 return ACT_RET_PRS_ERR;
399 }
400
Christopher Faulet96bff762019-12-17 13:46:18 +0100401 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200402 memprintf(err, "failed to parse the regex : %s", error);
403 free(error);
404 return ACT_RET_PRS_ERR;
405 }
406
Christopher Faulet96bff762019-12-17 13:46:18 +0100407 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200408 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100409 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200410 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100411 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200412 return ACT_RET_PRS_ERR;
413 }
414
415 (*orig_arg) += 2;
416 return ACT_RET_PRS_OK;
417}
418
Willy Tarreau79e57332018-10-02 16:01:16 +0200419/* This function is just a compliant action wrapper for "set-status". */
420static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
421 struct session *sess, struct stream *s, int flags)
422{
Christopher Faulet96bff762019-12-17 13:46:18 +0100423 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Willy Tarreau4781b152021-04-06 13:53:36 +0200424 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100425 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +0200426 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +0100427 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200428 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100429 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +0200430 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulete00d06c2019-12-16 17:18:42 +0100431
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100432 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100433 if (!(s->flags & SF_ERR_MASK))
434 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100435 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100436 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100437 }
438
Willy Tarreau79e57332018-10-02 16:01:16 +0200439 return ACT_RET_CONT;
440}
441
442/* parse set-status action:
443 * This action accepts a single argument of type int representing
444 * an http status code. It returns ACT_RET_PRS_OK on success,
445 * ACT_RET_PRS_ERR on error.
446 */
447static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
448 struct act_rule *rule, char **err)
449{
450 char *error;
451
452 rule->action = ACT_CUSTOM;
453 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100454 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200455
456 /* Check if an argument is available */
457 if (!*args[*orig_arg]) {
458 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
459 return ACT_RET_PRS_ERR;
460 }
461
462 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100463 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
464 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200465 memprintf(err, "expects an integer status code between 100 and 999");
466 return ACT_RET_PRS_ERR;
467 }
468
469 (*orig_arg)++;
470
471 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100472 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200473 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
474 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
475 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100476 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
477 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200478 (*orig_arg)++;
479 }
480
Christopher Fauletc20b3712020-01-27 15:51:56 +0100481 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200482 return ACT_RET_PRS_OK;
483}
484
485/* This function executes the "reject" HTTP action. It clears the request and
486 * response buffer without sending any response. It can be useful as an HTTP
487 * alternative to the silent-drop action to defend against DoS attacks, and may
488 * also be used with HTTP/2 to close a connection instead of just a stream.
489 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100490 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200491 */
492static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
493 struct session *sess, struct stream *s, int flags)
494{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100495 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200496 channel_abort(&s->req);
497 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100498 s->req.analysers &= AN_REQ_FLT_END;
499 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200500
Willy Tarreau4781b152021-04-06 13:53:36 +0200501 _HA_ATOMIC_INC(&s->be->be_counters.denied_req);
502 _HA_ATOMIC_INC(&sess->fe->fe_counters.denied_req);
Willy Tarreau79e57332018-10-02 16:01:16 +0200503 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +0200504 _HA_ATOMIC_INC(&sess->listener->counters->denied_req);
Willy Tarreau79e57332018-10-02 16:01:16 +0200505
506 if (!(s->flags & SF_ERR_MASK))
507 s->flags |= SF_ERR_PRXCOND;
508 if (!(s->flags & SF_FINST_MASK))
509 s->flags |= SF_FINST_R;
510
Christopher Faulet90d22a82020-03-06 11:18:39 +0100511 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200512}
513
514/* parse the "reject" action:
515 * This action takes no argument and returns ACT_RET_PRS_OK on success,
516 * ACT_RET_PRS_ERR on error.
517 */
518static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
519 struct act_rule *rule, char **err)
520{
521 rule->action = ACT_CUSTOM;
522 rule->action_ptr = http_action_reject;
523 return ACT_RET_PRS_OK;
524}
525
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200526/* This function executes the "disable-l7-retry" HTTP action.
527 * It disables L7 retries (all retry except for a connection failure). This
528 * can be useful for example to avoid retrying on POST requests.
529 * It just removes the L7 retry flag on the stream_interface, and always
530 * return ACT_RET_CONT;
531 */
532static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
533 struct session *sess, struct stream *s, int flags)
534{
535 struct stream_interface *si = &s->si[1];
536
537 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
538 * let's be future-proof and remove it anyway.
539 */
540 si->flags &= ~SI_FL_L7_RETRY;
541 si->flags |= SI_FL_D_L7_RETRY;
542 return ACT_RET_CONT;
543}
544
545/* parse the "disable-l7-retry" action:
546 * This action takes no argument and returns ACT_RET_PRS_OK on success,
547 * ACT_RET_PRS_ERR on error.
548 */
549static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
550 int *orig_args, struct proxy *px,
551 struct act_rule *rule, char **err)
552{
553 rule->action = ACT_CUSTOM;
554 rule->action_ptr = http_req_disable_l7_retry;
555 return ACT_RET_PRS_OK;
556}
557
Willy Tarreau79e57332018-10-02 16:01:16 +0200558/* This function executes the "capture" action. It executes a fetch expression,
559 * turns the result into a string and puts it in a capture slot. It always
560 * returns 1. If an error occurs the action is cancelled, but the rule
561 * processing continues.
562 */
563static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
564 struct session *sess, struct stream *s, int flags)
565{
566 struct sample *key;
567 struct cap_hdr *h = rule->arg.cap.hdr;
568 char **cap = s->req_cap;
569 int len;
570
571 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
572 if (!key)
573 return ACT_RET_CONT;
574
575 if (cap[h->index] == NULL)
576 cap[h->index] = pool_alloc(h->pool);
577
578 if (cap[h->index] == NULL) /* no more capture memory */
579 return ACT_RET_CONT;
580
581 len = key->data.u.str.data;
582 if (len > h->len)
583 len = h->len;
584
585 memcpy(cap[h->index], key->data.u.str.area, len);
586 cap[h->index][len] = 0;
587 return ACT_RET_CONT;
588}
589
590/* This function executes the "capture" action and store the result in a
591 * capture slot if exists. It executes a fetch expression, turns the result
592 * into a string and puts it in a capture slot. It always returns 1. If an
593 * error occurs the action is cancelled, but the rule processing continues.
594 */
595static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
596 struct session *sess, struct stream *s, int flags)
597{
598 struct sample *key;
599 struct cap_hdr *h;
600 char **cap = s->req_cap;
601 struct proxy *fe = strm_fe(s);
602 int len;
603 int i;
604
605 /* Look for the original configuration. */
606 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
607 h != NULL && i != rule->arg.capid.idx ;
608 i--, h = h->next);
609 if (!h)
610 return ACT_RET_CONT;
611
612 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
613 if (!key)
614 return ACT_RET_CONT;
615
616 if (cap[h->index] == NULL)
617 cap[h->index] = pool_alloc(h->pool);
618
619 if (cap[h->index] == NULL) /* no more capture memory */
620 return ACT_RET_CONT;
621
622 len = key->data.u.str.data;
623 if (len > h->len)
624 len = h->len;
625
626 memcpy(cap[h->index], key->data.u.str.area, len);
627 cap[h->index][len] = 0;
628 return ACT_RET_CONT;
629}
630
631/* Check an "http-request capture" action.
632 *
633 * The function returns 1 in success case, otherwise, it returns 0 and err is
634 * filled.
635 */
636static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
637{
638 if (rule->action_ptr != http_action_req_capture_by_id)
639 return 1;
640
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100641 /* capture slots can only be declared in frontends, so we can't check their
642 * existence in backends at configuration parsing step
643 */
644 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200645 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
646 rule->arg.capid.idx);
647 return 0;
648 }
649
650 return 1;
651}
652
Christopher Faulet2eb53962020-01-14 14:47:34 +0100653/* Release memory allocate by an http capture action */
654static void release_http_capture(struct act_rule *rule)
655{
656 if (rule->action_ptr == http_action_req_capture)
657 release_sample_expr(rule->arg.cap.expr);
658 else
659 release_sample_expr(rule->arg.capid.expr);
660}
661
Willy Tarreau79e57332018-10-02 16:01:16 +0200662/* parse an "http-request capture" action. It takes a single argument which is
663 * a sample fetch expression. It stores the expression into arg->act.p[0] and
664 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
665 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
666 */
667static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
668 struct act_rule *rule, char **err)
669{
670 struct sample_expr *expr;
671 struct cap_hdr *hdr;
672 int cur_arg;
673 int len = 0;
674
675 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
676 if (strcmp(args[cur_arg], "if") == 0 ||
677 strcmp(args[cur_arg], "unless") == 0)
678 break;
679
680 if (cur_arg < *orig_arg + 3) {
681 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
682 return ACT_RET_PRS_ERR;
683 }
684
685 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100686 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 +0200687 if (!expr)
688 return ACT_RET_PRS_ERR;
689
690 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
691 memprintf(err,
692 "fetch method '%s' extracts information from '%s', none of which is available here",
693 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100694 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200695 return ACT_RET_PRS_ERR;
696 }
697
698 if (!args[cur_arg] || !*args[cur_arg]) {
699 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100700 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200701 return ACT_RET_PRS_ERR;
702 }
703
704 if (strcmp(args[cur_arg], "len") == 0) {
705 cur_arg++;
706
707 if (!(px->cap & PR_CAP_FE)) {
708 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100709 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200710 return ACT_RET_PRS_ERR;
711 }
712
713 px->conf.args.ctx = ARGC_CAP;
714
715 if (!args[cur_arg]) {
716 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100717 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200718 return ACT_RET_PRS_ERR;
719 }
720 /* we copy the table name for now, it will be resolved later */
721 len = atoi(args[cur_arg]);
722 if (len <= 0) {
723 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100724 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200725 return ACT_RET_PRS_ERR;
726 }
727 cur_arg++;
728
Willy Tarreau79e57332018-10-02 16:01:16 +0200729 hdr = calloc(1, sizeof(*hdr));
730 hdr->next = px->req_cap;
731 hdr->name = NULL; /* not a header capture */
732 hdr->namelen = 0;
733 hdr->len = len;
734 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
735 hdr->index = px->nb_req_cap++;
736
737 px->req_cap = hdr;
738 px->to_log |= LW_REQHDR;
739
740 rule->action = ACT_CUSTOM;
741 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100742 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200743 rule->arg.cap.expr = expr;
744 rule->arg.cap.hdr = hdr;
745 }
746
747 else if (strcmp(args[cur_arg], "id") == 0) {
748 int id;
749 char *error;
750
751 cur_arg++;
752
753 if (!args[cur_arg]) {
754 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100755 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200756 return ACT_RET_PRS_ERR;
757 }
758
759 id = strtol(args[cur_arg], &error, 10);
760 if (*error != '\0') {
761 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100762 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200763 return ACT_RET_PRS_ERR;
764 }
765 cur_arg++;
766
767 px->conf.args.ctx = ARGC_CAP;
768
769 rule->action = ACT_CUSTOM;
770 rule->action_ptr = http_action_req_capture_by_id;
771 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100772 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200773 rule->arg.capid.expr = expr;
774 rule->arg.capid.idx = id;
775 }
776
777 else {
778 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100779 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200780 return ACT_RET_PRS_ERR;
781 }
782
783 *orig_arg = cur_arg;
784 return ACT_RET_PRS_OK;
785}
786
787/* This function executes the "capture" action and store the result in a
788 * capture slot if exists. It executes a fetch expression, turns the result
789 * into a string and puts it in a capture slot. It always returns 1. If an
790 * error occurs the action is cancelled, but the rule processing continues.
791 */
792static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
793 struct session *sess, struct stream *s, int flags)
794{
795 struct sample *key;
796 struct cap_hdr *h;
797 char **cap = s->res_cap;
798 struct proxy *fe = strm_fe(s);
799 int len;
800 int i;
801
802 /* Look for the original configuration. */
803 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
804 h != NULL && i != rule->arg.capid.idx ;
805 i--, h = h->next);
806 if (!h)
807 return ACT_RET_CONT;
808
809 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
810 if (!key)
811 return ACT_RET_CONT;
812
813 if (cap[h->index] == NULL)
814 cap[h->index] = pool_alloc(h->pool);
815
816 if (cap[h->index] == NULL) /* no more capture memory */
817 return ACT_RET_CONT;
818
819 len = key->data.u.str.data;
820 if (len > h->len)
821 len = h->len;
822
823 memcpy(cap[h->index], key->data.u.str.area, len);
824 cap[h->index][len] = 0;
825 return ACT_RET_CONT;
826}
827
828/* Check an "http-response capture" action.
829 *
830 * The function returns 1 in success case, otherwise, it returns 0 and err is
831 * filled.
832 */
833static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
834{
835 if (rule->action_ptr != http_action_res_capture_by_id)
836 return 1;
837
Tim Duesterhusf3f4aa02020-07-03 13:43:42 +0200838 /* capture slots can only be declared in frontends, so we can't check their
839 * existence in backends at configuration parsing step
840 */
841 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_rsp_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200842 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
843 rule->arg.capid.idx);
844 return 0;
845 }
846
847 return 1;
848}
849
850/* parse an "http-response capture" action. It takes a single argument which is
851 * a sample fetch expression. It stores the expression into arg->act.p[0] and
Thayne McCombs8f0cc5c2021-01-07 21:35:52 -0700852 * the allocated hdr_cap struct of the preallocated id into arg->act.p[1].
Willy Tarreau79e57332018-10-02 16:01:16 +0200853 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
854 */
855static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
856 struct act_rule *rule, char **err)
857{
858 struct sample_expr *expr;
859 int cur_arg;
860 int id;
861 char *error;
862
863 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
864 if (strcmp(args[cur_arg], "if") == 0 ||
865 strcmp(args[cur_arg], "unless") == 0)
866 break;
867
868 if (cur_arg < *orig_arg + 3) {
869 memprintf(err, "expects <expression> id <idx>");
870 return ACT_RET_PRS_ERR;
871 }
872
873 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100874 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 +0200875 if (!expr)
876 return ACT_RET_PRS_ERR;
877
878 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
879 memprintf(err,
880 "fetch method '%s' extracts information from '%s', none of which is available here",
881 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100882 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200883 return ACT_RET_PRS_ERR;
884 }
885
886 if (!args[cur_arg] || !*args[cur_arg]) {
887 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100888 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200889 return ACT_RET_PRS_ERR;
890 }
891
892 if (strcmp(args[cur_arg], "id") != 0) {
893 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100894 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200895 return ACT_RET_PRS_ERR;
896 }
897
898 cur_arg++;
899
900 if (!args[cur_arg]) {
901 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100902 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200903 return ACT_RET_PRS_ERR;
904 }
905
906 id = strtol(args[cur_arg], &error, 10);
907 if (*error != '\0') {
908 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100909 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200910 return ACT_RET_PRS_ERR;
911 }
912 cur_arg++;
913
914 px->conf.args.ctx = ARGC_CAP;
915
916 rule->action = ACT_CUSTOM;
917 rule->action_ptr = http_action_res_capture_by_id;
918 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100919 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200920 rule->arg.capid.expr = expr;
921 rule->arg.capid.idx = id;
922
923 *orig_arg = cur_arg;
924 return ACT_RET_PRS_OK;
925}
926
Christopher Faulet81e20172019-12-12 16:40:30 +0100927/* Parse a "allow" action for a request or a response rule. It takes no argument. It
928 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
929 */
930static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
931 struct act_rule *rule, char **err)
932{
933 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100934 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100935 return ACT_RET_PRS_OK;
936}
937
Christopher Faulete0fca292020-01-13 21:49:03 +0100938/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200939 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
940 * error. It relies on http_parse_http_reply() to set
941 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100942 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100943static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
944 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100945{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200946 int default_status;
947 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100948
949 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100950 if (rule->from == ACT_F_HTTP_REQ) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100951 if (strcmp(args[cur_arg - 1], "tarpit") == 0) {
Christopher Faulete0fca292020-01-13 21:49:03 +0100952 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200953 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100954 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100955 else {
956 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200957 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100958 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100959 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100960 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100961 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200962 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100963 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100964
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200965 /* If no args or only a deny_status specified, fallback on the legacy
966 * mode and use default error files despite the fact that
967 * default-errorfiles is not used. Otherwise, parse an http reply.
968 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100969
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200970 /* Prepare parsing of log-format strings */
971 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100972
Christopher Faulet9467f182020-06-30 09:32:01 +0200973 if (!*(args[cur_arg]) || strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200974 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
975 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100976 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200977
978 if (strcmp(args[cur_arg], "deny_status") == 0) {
Christopher Faulet9467f182020-06-30 09:32:01 +0200979 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 +0200980 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
981 &arg, px, default_status, err);
982 *orig_arg += 2;
983 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100984 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200985 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100986 }
987
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200988 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100989
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200990 end:
991 if (!rule->arg.http_reply)
992 return ACT_RET_PRS_ERR;
993
994 rule->flags |= ACT_FLAG_FINAL;
995 rule->check_ptr = check_act_http_reply;
996 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +0100997 return ACT_RET_PRS_OK;
998}
999
Christopher Fauletb3048832020-05-27 15:26:43 +02001000
1001/* This function executes a auth action. It builds an 401/407 HTX message using
1002 * the corresponding proxy's error message. On success, it returns
1003 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
1004 */
1005static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
1006 struct session *sess, struct stream *s, int flags)
1007{
1008 struct channel *req = &s->req;
1009 struct channel *res = &s->res;
1010 struct htx *htx = htx_from_buf(&res->buf);
1011 struct http_reply *reply;
1012 const char *auth_realm;
1013 struct http_hdr_ctx ctx;
1014 struct ist hdr;
1015
1016 /* Auth might be performed on regular http-req rules as well as on stats */
1017 auth_realm = rule->arg.http.str.ptr;
1018 if (!auth_realm) {
1019 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
1020 auth_realm = STATS_DEFAULT_REALM;
1021 else
1022 auth_realm = px->id;
1023 }
1024
1025 if (!(s->txn->flags & TX_USE_PX_CONN)) {
1026 s->txn->status = 401;
1027 hdr = ist("WWW-Authenticate");
1028 }
1029 else {
1030 s->txn->status = 407;
1031 hdr = ist("Proxy-Authenticate");
1032 }
1033 reply = http_error_message(s);
1034 channel_htx_truncate(res, htx);
1035
1036 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
1037 goto fail;
1038
1039 /* Write the generic 40x message */
1040 if (http_reply_to_htx(s, htx, reply) == -1)
1041 goto fail;
1042
1043 /* Remove all existing occurrences of the XXX-Authenticate header */
1044 ctx.blk = NULL;
1045 while (http_find_header(htx, hdr, &ctx, 1))
1046 http_remove_header(htx, &ctx);
1047
1048 /* Now a the right XXX-Authenticate header */
1049 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
1050 goto fail;
1051
1052 /* Finally forward the reply */
1053 htx_to_buf(htx, &res->buf);
1054 if (!http_forward_proxy_resp(s, 1))
1055 goto fail;
1056
1057 /* Note: Only eval on the request */
1058 s->logs.tv_request = now;
1059 req->analysers &= AN_REQ_FLT_END;
1060
1061 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
Willy Tarreau4781b152021-04-06 13:53:36 +02001062 _HA_ATOMIC_INC(&s->sess->fe->fe_counters.intercepted_req);
Christopher Fauletb3048832020-05-27 15:26:43 +02001063
1064 if (!(s->flags & SF_ERR_MASK))
1065 s->flags |= SF_ERR_LOCAL;
1066 if (!(s->flags & SF_FINST_MASK))
1067 s->flags |= SF_FINST_R;
1068
1069 stream_inc_http_err_ctr(s);
1070 return ACT_RET_ABRT;
1071
1072 fail:
1073 /* If an error occurred, remove the incomplete HTTP response from the
1074 * buffer */
1075 channel_htx_truncate(res, htx);
1076 return ACT_RET_ERR;
1077}
1078
Christopher Faulet81e20172019-12-12 16:40:30 +01001079/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
1080 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1081 */
1082static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
1083 struct act_rule *rule, char **err)
1084{
1085 int cur_arg;
1086
Christopher Fauletb3048832020-05-27 15:26:43 +02001087 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +01001088 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +02001089 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001090 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001091
1092 cur_arg = *orig_arg;
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001093 if (strcmp(args[cur_arg], "realm") == 0) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001094 cur_arg++;
1095 if (!*args[cur_arg]) {
1096 memprintf(err, "missing realm value.\n");
1097 return ACT_RET_PRS_ERR;
1098 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001099 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1100 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001101 cur_arg++;
1102 }
1103
Christopher Fauletc20b3712020-01-27 15:51:56 +01001104 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001105 *orig_arg = cur_arg;
1106 return ACT_RET_PRS_OK;
1107}
1108
1109/* Parse a "set-nice" action. It takes the nice value as argument. It returns
1110 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1111 */
1112static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
1113 struct act_rule *rule, char **err)
1114{
1115 int cur_arg;
1116
1117 rule->action = ACT_HTTP_SET_NICE;
1118
1119 cur_arg = *orig_arg;
1120 if (!*args[cur_arg]) {
1121 memprintf(err, "expects exactly 1 argument (integer value)");
1122 return ACT_RET_PRS_ERR;
1123 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001124 rule->arg.http.i = atoi(args[cur_arg]);
1125 if (rule->arg.http.i < -1024)
1126 rule->arg.http.i = -1024;
1127 else if (rule->arg.http.i > 1024)
1128 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001129
Christopher Fauletc20b3712020-01-27 15:51:56 +01001130 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001131 *orig_arg = cur_arg + 1;
1132 return ACT_RET_PRS_OK;
1133}
1134
1135/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1136 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1137 */
1138static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1139 struct act_rule *rule, char **err)
1140{
1141#ifdef IP_TOS
1142 char *endp;
1143 int cur_arg;
1144
1145 rule->action = ACT_HTTP_SET_TOS;
1146
1147 cur_arg = *orig_arg;
1148 if (!*args[cur_arg]) {
1149 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1150 return ACT_RET_PRS_ERR;
1151 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001152 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001153 if (endp && *endp != '\0') {
1154 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1155 return ACT_RET_PRS_ERR;
1156 }
1157
Christopher Fauletc20b3712020-01-27 15:51:56 +01001158 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001159 *orig_arg = cur_arg + 1;
1160 return ACT_RET_PRS_OK;
1161#else
1162 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1163 return ACT_RET_PRS_ERR;
1164#endif
1165}
1166
1167/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1168 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1169 */
1170static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1171 struct act_rule *rule, char **err)
1172{
1173#ifdef SO_MARK
1174 char *endp;
1175 int cur_arg;
1176
1177 rule->action = ACT_HTTP_SET_MARK;
1178
1179 cur_arg = *orig_arg;
1180 if (!*args[cur_arg]) {
1181 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1182 return ACT_RET_PRS_ERR;
1183 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001184 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001185 if (endp && *endp != '\0') {
1186 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1187 return ACT_RET_PRS_ERR;
1188 }
1189
Christopher Fauletc20b3712020-01-27 15:51:56 +01001190 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001191 *orig_arg = cur_arg + 1;
1192 global.last_checks |= LSTCHK_NETADM;
1193 return ACT_RET_PRS_OK;
1194#else
1195 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1196 return ACT_RET_PRS_ERR;
1197#endif
1198}
1199
1200/* Parse a "set-log-level" action. It takes the level value as argument. It
1201 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1202 */
1203static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1204 struct act_rule *rule, char **err)
1205{
1206 int cur_arg;
1207
1208 rule->action = ACT_HTTP_SET_LOGL;
1209
1210 cur_arg = *orig_arg;
1211 if (!*args[cur_arg]) {
1212 bad_log_level:
1213 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1214 return ACT_RET_PRS_ERR;
1215 }
1216 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001217 rule->arg.http.i = -1;
1218 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001219 goto bad_log_level;
1220
Christopher Fauletc20b3712020-01-27 15:51:56 +01001221 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001222 *orig_arg = cur_arg + 1;
1223 return ACT_RET_PRS_OK;
1224}
1225
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001226/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1227 * 103 response header with <.arg.http.str> name and with a value built
1228 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001229 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001230 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1231 * block is added to terminate the response. On success, it returns
1232 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1233 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1234 * returned.
1235 */
1236static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1237 struct session *sess, struct stream *s, int flags)
1238{
1239 struct act_rule *prev_rule, *next_rule;
1240 struct channel *res = &s->res;
1241 struct htx *htx = htx_from_buf(&res->buf);
1242 struct buffer *value = alloc_trash_chunk();
1243 enum act_return ret = ACT_RET_CONT;
1244
1245 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1246 goto leave;
1247
1248 if (!value) {
1249 if (!(s->flags & SF_ERR_MASK))
1250 s->flags |= SF_ERR_RESOURCE;
1251 goto error;
1252 }
1253
1254 /* get previous and next rules */
1255 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1256 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1257
1258 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1259 * continue to add link to a previously started response */
1260 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1261 struct htx_sl *sl;
1262 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1263 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1264
1265 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1266 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1267 if (!sl)
1268 goto error;
1269 sl->info.res.status = 103;
1270 }
1271
1272 /* Add the HTTP Early Hint HTTP 103 response heade */
1273 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1274 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1275 goto error;
1276
1277 /* if it is the last rule or the next one is not an early-hint, terminate the current
1278 * response. */
1279 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001280 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1281 /* If an error occurred during an Early-hint rule,
1282 * remove the incomplete HTTP 103 response from the
1283 * buffer */
1284 goto error;
1285 }
1286
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001287 if (!http_forward_proxy_resp(s, 0))
1288 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001289 }
1290
1291 leave:
1292 free_trash_chunk(value);
1293 return ret;
1294
1295 error:
1296 /* If an error occurred during an Early-hint rule, remove the incomplete
1297 * HTTP 103 response from the buffer */
1298 channel_htx_truncate(res, htx);
1299 ret = ACT_RET_ERR;
1300 goto leave;
1301}
1302
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001303/* This function executes a set-header or add-header actions. It builds a string
1304 * in the trash from the specified format string. It finds the action to be
1305 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001306 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001307 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1308 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1309 * ACT_RET_ERR is returned.
1310 */
1311static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1312 struct session *sess, struct stream *s, int flags)
1313{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001314 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1315 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001316 enum act_return ret = ACT_RET_CONT;
1317 struct buffer *replace;
1318 struct http_hdr_ctx ctx;
1319 struct ist n, v;
1320
1321 replace = alloc_trash_chunk();
1322 if (!replace)
1323 goto fail_alloc;
1324
1325 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1326 n = rule->arg.http.str;
1327 v = ist2(replace->area, replace->data);
1328
1329 if (rule->action == 0) { // set-header
1330 /* remove all occurrences of the header */
1331 ctx.blk = NULL;
1332 while (http_find_header(htx, n, &ctx, 1))
1333 http_remove_header(htx, &ctx);
1334 }
1335
1336 /* Now add header */
1337 if (!http_add_header(htx, n, v))
1338 goto fail_rewrite;
1339
1340 leave:
1341 free_trash_chunk(replace);
1342 return ret;
1343
1344 fail_alloc:
1345 if (!(s->flags & SF_ERR_MASK))
1346 s->flags |= SF_ERR_RESOURCE;
1347 ret = ACT_RET_ERR;
1348 goto leave;
1349
1350 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +02001351 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001352 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +02001353 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +01001354 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02001355 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001356 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +02001357 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001358
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001359 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001360 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001361 if (!(s->flags & SF_ERR_MASK))
1362 s->flags |= SF_ERR_PRXCOND;
1363 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001364 goto leave;
1365}
1366
Christopher Faulet81e20172019-12-12 16:40:30 +01001367/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1368 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1369 * on success, ACT_RET_PRS_ERR on error.
1370 *
1371 * Note: same function is used for the request and the response. However
1372 * "early-hint" rules are only supported for request rules.
1373 */
1374static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1375 struct act_rule *rule, char **err)
1376{
Christopher Faulet81e20172019-12-12 16:40:30 +01001377 int cap, cur_arg;
1378
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001379 if (args[*orig_arg-1][0] == 'e') {
1380 rule->action = ACT_CUSTOM;
1381 rule->action_ptr = http_action_early_hint;
1382 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001383 else {
1384 if (args[*orig_arg-1][0] == 's')
1385 rule->action = 0; // set-header
1386 else
1387 rule->action = 1; // add-header
1388 rule->action_ptr = http_action_set_header;
1389 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001390 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001391
1392 cur_arg = *orig_arg;
1393 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1394 memprintf(err, "expects exactly 2 arguments");
1395 return ACT_RET_PRS_ERR;
1396 }
1397
Christopher Faulet81e20172019-12-12 16:40:30 +01001398
Christopher Faulet96bff762019-12-17 13:46:18 +01001399 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1400 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1401 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001402
1403 if (rule->from == ACT_F_HTTP_REQ) {
1404 px->conf.args.ctx = ARGC_HRQ;
1405 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1406 }
1407 else{
1408 px->conf.args.ctx = ARGC_HRS;
1409 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1410 }
1411
1412 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001413 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001414 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001415 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001416 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001417
1418 free(px->conf.lfs_file);
1419 px->conf.lfs_file = strdup(px->conf.args.file);
1420 px->conf.lfs_line = px->conf.args.line;
1421
1422 *orig_arg = cur_arg + 1;
1423 return ACT_RET_PRS_OK;
1424}
1425
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001426/* This function executes a replace-header or replace-value actions. It
1427 * builds a string in the trash from the specified format string. It finds
1428 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001429 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001430 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1431 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1432 * processing continue. Otherwsize ACT_RET_ERR is returned.
1433 */
1434static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1435 struct session *sess, struct stream *s, int flags)
1436{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001437 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1438 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001439 enum act_return ret = ACT_RET_CONT;
1440 struct buffer *replace;
1441 int r;
1442
1443 replace = alloc_trash_chunk();
1444 if (!replace)
1445 goto fail_alloc;
1446
1447 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1448
1449 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1450 if (r == -1)
1451 goto fail_rewrite;
1452
1453 leave:
1454 free_trash_chunk(replace);
1455 return ret;
1456
1457 fail_alloc:
1458 if (!(s->flags & SF_ERR_MASK))
1459 s->flags |= SF_ERR_RESOURCE;
1460 ret = ACT_RET_ERR;
1461 goto leave;
1462
1463 fail_rewrite:
Willy Tarreau4781b152021-04-06 13:53:36 +02001464 _HA_ATOMIC_INC(&sess->fe->fe_counters.failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001465 if (s->flags & SF_BE_ASSIGNED)
Willy Tarreau4781b152021-04-06 13:53:36 +02001466 _HA_ATOMIC_INC(&s->be->be_counters.failed_rewrites);
William Lallemand36119de2021-03-08 15:26:48 +01001467 if (sess->listener && sess->listener->counters)
Willy Tarreau4781b152021-04-06 13:53:36 +02001468 _HA_ATOMIC_INC(&sess->listener->counters->failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001469 if (objt_server(s->target))
Willy Tarreau4781b152021-04-06 13:53:36 +02001470 _HA_ATOMIC_INC(&__objt_server(s->target)->counters.failed_rewrites);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001471
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001472 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001473 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001474 if (!(s->flags & SF_ERR_MASK))
1475 s->flags |= SF_ERR_PRXCOND;
1476 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001477 goto leave;
1478}
1479
Christopher Faulet81e20172019-12-12 16:40:30 +01001480/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1481 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1482 * success, ACT_RET_PRS_ERR on error.
1483 */
1484static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1485 struct act_rule *rule, char **err)
1486{
1487 int cap, cur_arg;
1488
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001489 if (args[*orig_arg-1][8] == 'h')
1490 rule->action = 0; // replace-header
1491 else
1492 rule->action = 1; // replace-value
1493 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001494 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001495
1496 cur_arg = *orig_arg;
1497 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1498 memprintf(err, "expects exactly 3 arguments");
1499 return ACT_RET_PRS_ERR;
1500 }
1501
Christopher Faulet96bff762019-12-17 13:46:18 +01001502 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1503 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1504 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001505
1506 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001507 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001508 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001509 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001510 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001511
1512 if (rule->from == ACT_F_HTTP_REQ) {
1513 px->conf.args.ctx = ARGC_HRQ;
1514 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1515 }
1516 else{
1517 px->conf.args.ctx = ARGC_HRS;
1518 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1519 }
1520
1521 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001522 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001523 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001524 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001525 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001526 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001527
1528 free(px->conf.lfs_file);
1529 px->conf.lfs_file = strdup(px->conf.args.file);
1530 px->conf.lfs_line = px->conf.args.line;
1531
1532 *orig_arg = cur_arg + 1;
1533 return ACT_RET_PRS_OK;
1534}
1535
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001536/* This function executes a del-header action with selected matching mode for
1537 * header name. It finds the matching method to be performed in <.action>, previously
1538 * filled by function parse_http_del_header(). On success, it returns ACT_RET_CONT.
1539 * Otherwise ACT_RET_ERR is returned.
1540 */
1541static enum act_return http_action_del_header(struct act_rule *rule, struct proxy *px,
1542 struct session *sess, struct stream *s, int flags)
1543{
1544 struct http_hdr_ctx ctx;
1545 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1546 struct htx *htx = htxbuf(&msg->chn->buf);
1547 enum act_return ret = ACT_RET_CONT;
1548
1549 /* remove all occurrences of the header */
1550 ctx.blk = NULL;
1551 switch (rule->action) {
1552 case PAT_MATCH_STR:
1553 while (http_find_header(htx, rule->arg.http.str, &ctx, 1))
1554 http_remove_header(htx, &ctx);
1555 break;
1556 case PAT_MATCH_BEG:
1557 while (http_find_pfx_header(htx, rule->arg.http.str, &ctx, 1))
1558 http_remove_header(htx, &ctx);
1559 break;
1560 case PAT_MATCH_END:
1561 while (http_find_sfx_header(htx, rule->arg.http.str, &ctx, 1))
1562 http_remove_header(htx, &ctx);
1563 break;
1564 case PAT_MATCH_SUB:
1565 while (http_find_sub_header(htx, rule->arg.http.str, &ctx, 1))
1566 http_remove_header(htx, &ctx);
1567 break;
1568 case PAT_MATCH_REG:
1569 while (http_match_header(htx, rule->arg.http.re, &ctx, 1))
1570 http_remove_header(htx, &ctx);
1571 break;
1572 default:
1573 return ACT_RET_ERR;
1574 }
1575 return ret;
1576}
1577
1578/* Parse a "del-header" action. It takes string as a required argument,
1579 * optional flag (currently only -m) and optional matching method of input string
1580 * with header name to be deleted. Default matching method is exact match (-m str).
1581 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001582 */
1583static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1584 struct act_rule *rule, char **err)
1585{
1586 int cur_arg;
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001587 int pat_idx;
Christopher Faulet81e20172019-12-12 16:40:30 +01001588
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001589 /* set exact matching (-m str) as default */
1590 rule->action = PAT_MATCH_STR;
1591 rule->action_ptr = http_action_del_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001592 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001593
1594 cur_arg = *orig_arg;
1595 if (!*args[cur_arg]) {
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001596 memprintf(err, "expects at least 1 argument");
Christopher Faulet81e20172019-12-12 16:40:30 +01001597 return ACT_RET_PRS_ERR;
1598 }
1599
Christopher Faulet96bff762019-12-17 13:46:18 +01001600 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1601 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001602 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1603
Maciej Zdeb6dee9962020-11-23 16:03:09 +00001604 LIST_INIT(&rule->arg.http.fmt);
Maciej Zdebebdd4c52020-11-20 13:58:48 +00001605 if (strcmp(args[cur_arg+1], "-m") == 0) {
1606 cur_arg++;
1607 if (!*args[cur_arg+1]) {
1608 memprintf(err, "-m flag expects exactly 1 argument");
1609 return ACT_RET_PRS_ERR;
1610 }
1611
1612 cur_arg++;
1613 pat_idx = pat_find_match_name(args[cur_arg]);
1614 switch (pat_idx) {
1615 case PAT_MATCH_REG:
1616 if (!(rule->arg.http.re = regex_comp(rule->arg.http.str.ptr, 1, 1, err)))
1617 return ACT_RET_PRS_ERR;
1618 /* fall through */
1619 case PAT_MATCH_STR:
1620 case PAT_MATCH_BEG:
1621 case PAT_MATCH_END:
1622 case PAT_MATCH_SUB:
1623 rule->action = pat_idx;
1624 break;
1625 default:
1626 memprintf(err, "-m with unsupported matching method '%s'", args[cur_arg]);
1627 return ACT_RET_PRS_ERR;
1628 }
1629 }
1630
Christopher Faulet81e20172019-12-12 16:40:30 +01001631 *orig_arg = cur_arg + 1;
1632 return ACT_RET_PRS_OK;
1633}
1634
Christopher Faulet2eb53962020-01-14 14:47:34 +01001635/* Release memory allocated by an http redirect action. */
1636static void release_http_redir(struct act_rule *rule)
1637{
1638 struct logformat_node *lf, *lfb;
1639 struct redirect_rule *redir;
1640
1641 redir = rule->arg.redir;
1642 LIST_DEL(&redir->list);
1643 if (redir->cond) {
1644 prune_acl_cond(redir->cond);
1645 free(redir->cond);
1646 }
1647 free(redir->rdr_str);
1648 free(redir->cookie_str);
1649 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1650 LIST_DEL(&lf->list);
1651 free(lf);
1652 }
1653 free(redir);
1654}
1655
Christopher Faulet81e20172019-12-12 16:40:30 +01001656/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1657 * ACT_RET_PRS_ERR on error.
1658 */
1659static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1660 struct act_rule *rule, char **err)
1661{
1662 struct redirect_rule *redir;
1663 int dir, cur_arg;
1664
1665 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001666 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001667 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001668
1669 cur_arg = *orig_arg;
1670
1671 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1672 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1673 return ACT_RET_PRS_ERR;
1674
1675 rule->arg.redir = redir;
1676 rule->cond = redir->cond;
1677 redir->cond = NULL;
1678
1679 /* skip all arguments */
1680 while (*args[cur_arg])
1681 cur_arg++;
1682
1683 *orig_arg = cur_arg;
1684 return ACT_RET_PRS_OK;
1685}
1686
Christopher Faulet046cf442019-12-17 15:45:23 +01001687/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1688 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1689 */
1690static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1691 struct session *sess, struct stream *s, int flags)
1692{
1693 struct pat_ref *ref;
1694 struct buffer *key = NULL, *value = NULL;
1695 enum act_return ret = ACT_RET_CONT;
1696
1697 /* collect reference */
1698 ref = pat_ref_lookup(rule->arg.map.ref);
1699 if (!ref)
1700 goto leave;
1701
1702 /* allocate key */
1703 key = alloc_trash_chunk();
1704 if (!key)
1705 goto fail_alloc;
1706
1707 /* collect key */
1708 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1709 key->area[key->data] = '\0';
1710
1711 switch (rule->action) {
1712 case 0: // add-acl
1713 /* add entry only if it does not already exist */
1714 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1715 if (pat_ref_find_elt(ref, key->area) == NULL)
1716 pat_ref_add(ref, key->area, NULL, NULL);
1717 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1718 break;
1719
1720 case 1: // set-map
1721 /* allocate value */
1722 value = alloc_trash_chunk();
1723 if (!value)
1724 goto fail_alloc;
1725
1726 /* collect value */
1727 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1728 value->area[value->data] = '\0';
1729
1730 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1731 if (pat_ref_find_elt(ref, key->area) != NULL) {
1732 /* update entry if it exists */
1733 pat_ref_set(ref, key->area, value->area, NULL);
1734 }
1735 else {
1736 /* insert a new entry */
1737 pat_ref_add(ref, key->area, value->area, NULL);
1738 }
1739 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1740 break;
1741
1742 case 2: // del-acl
1743 case 3: // del-map
1744 /* returned code: 1=ok, 0=ko */
1745 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1746 pat_ref_delete(ref, key->area);
1747 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1748 break;
1749
1750 default:
1751 ret = ACT_RET_ERR;
1752 }
1753
1754
1755 leave:
1756 free_trash_chunk(key);
1757 free_trash_chunk(value);
1758 return ret;
1759
1760 fail_alloc:
1761 if (!(s->flags & SF_ERR_MASK))
1762 s->flags |= SF_ERR_RESOURCE;
1763 ret = ACT_RET_ERR;
1764 goto leave;
1765}
1766
Christopher Faulet2eb53962020-01-14 14:47:34 +01001767/* Release memory allocated by an http map/acl action. */
1768static void release_http_map(struct act_rule *rule)
1769{
1770 struct logformat_node *lf, *lfb;
1771
1772 free(rule->arg.map.ref);
1773 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1774 LIST_DEL(&lf->list);
1775 release_sample_expr(lf->expr);
1776 free(lf->arg);
1777 free(lf);
1778 }
1779 if (rule->action == 1) {
1780 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1781 LIST_DEL(&lf->list);
1782 release_sample_expr(lf->expr);
1783 free(lf->arg);
1784 free(lf);
1785 }
1786 }
1787}
1788
Christopher Faulet81e20172019-12-12 16:40:30 +01001789/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001790 * two log-format string as argument depending on the action. The action is
1791 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1792 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001793 */
1794static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1795 struct act_rule *rule, char **err)
1796{
1797 int cap, cur_arg;
1798
Christopher Faulet046cf442019-12-17 15:45:23 +01001799 if (args[*orig_arg-1][0] == 'a') // add-acl
1800 rule->action = 0;
1801 else if (args[*orig_arg-1][0] == 's') // set-map
1802 rule->action = 1;
1803 else if (args[*orig_arg-1][4] == 'a') // del-acl
1804 rule->action = 2;
1805 else if (args[*orig_arg-1][4] == 'm') // del-map
1806 rule->action = 3;
1807 else {
1808 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1809 return ACT_RET_PRS_ERR;
1810 }
1811 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001812 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001813
1814 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001815 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1816 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001817 memprintf(err, "expects exactly 2 arguments");
1818 return ACT_RET_PRS_ERR;
1819 }
1820 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001821 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001822 memprintf(err, "expects exactly 1 arguments");
1823 return ACT_RET_PRS_ERR;
1824 }
1825
1826 /*
1827 * '+ 8' for 'set-map(' (same for del-map)
1828 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1829 */
1830 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1831
1832 if (rule->from == ACT_F_HTTP_REQ) {
1833 px->conf.args.ctx = ARGC_HRQ;
1834 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1835 }
1836 else{
1837 px->conf.args.ctx = ARGC_HRS;
1838 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1839 }
1840
1841 /* key pattern */
1842 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001843 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1844 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001845 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001846 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001847
Christopher Faulet046cf442019-12-17 15:45:23 +01001848 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001849 /* value pattern for set-map only */
1850 cur_arg++;
1851 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001852 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1853 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001854 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001855 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001856 }
1857
1858 free(px->conf.lfs_file);
1859 px->conf.lfs_file = strdup(px->conf.args.file);
1860 px->conf.lfs_line = px->conf.args.line;
1861
1862 *orig_arg = cur_arg + 1;
1863 return ACT_RET_PRS_OK;
1864}
1865
Christopher Fauletac98d812019-12-18 09:20:16 +01001866/* This function executes a track-sc* actions. On success, it returns
1867 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1868 */
1869static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1870 struct session *sess, struct stream *s, int flags)
1871{
1872 struct stktable *t;
1873 struct stksess *ts;
1874 struct stktable_key *key;
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001875 void *ptr1, *ptr2, *ptr3, *ptr4, *ptr5, *ptr6;
Christopher Fauletac98d812019-12-18 09:20:16 +01001876 int opt;
1877
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001878 ptr1 = ptr2 = ptr3 = ptr4 = ptr5 = ptr6 = NULL;
Christopher Fauletac98d812019-12-18 09:20:16 +01001879 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1880
1881 t = rule->arg.trk_ctr.table.t;
Emeric Brun362d25e2021-03-10 16:58:03 +01001882
1883 if (stkctr_entry(&s->stkctr[rule->action]))
1884 goto end;
1885
Christopher Fauletac98d812019-12-18 09:20:16 +01001886 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1887
1888 if (!key)
1889 goto end;
1890 ts = stktable_get_entry(t, key);
1891 if (!ts)
1892 goto end;
1893
1894 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1895
1896 /* let's count a new HTTP request as it's the first time we do it */
1897 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1898 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1899
1900 /* When the client triggers a 4xx from the server, it's most often due
1901 * to a missing object or permission. These events should be tracked
1902 * because if they happen often, it may indicate a brute force or a
1903 * vulnerability scan. Normally this is done when receiving the response
1904 * but here we're tracking after this ought to have been done so we have
1905 * to do it on purpose.
1906 */
1907 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1908 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1909 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1910 }
1911
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001912 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 500) < 100 &&
1913 s->txn->status != 501 && s->txn->status != 505) {
1914 ptr5 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_FAIL_CNT);
1915 ptr6 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_FAIL_RATE);
1916 }
1917
1918 if (ptr1 || ptr2 || ptr3 || ptr4 || ptr5 || ptr6) {
Christopher Fauletac98d812019-12-18 09:20:16 +01001919 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1920
1921 if (ptr1)
1922 stktable_data_cast(ptr1, http_req_cnt)++;
1923 if (ptr2)
1924 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1925 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1926 if (ptr3)
1927 stktable_data_cast(ptr3, http_err_cnt)++;
1928 if (ptr4)
1929 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1930 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
Willy Tarreau826f3ab2021-02-10 12:07:15 +01001931 if (ptr5)
1932 stktable_data_cast(ptr5, http_fail_cnt)++;
1933 if (ptr6)
1934 update_freq_ctr_period(&stktable_data_cast(ptr6, http_fail_rate),
1935 t->data_arg[STKTABLE_DT_HTTP_FAIL_RATE].u, 1);
Christopher Fauletac98d812019-12-18 09:20:16 +01001936
1937 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1938
1939 /* If data was modified, we need to touch to re-schedule sync */
1940 stktable_touch_local(t, ts, 0);
1941 }
1942
1943 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1944 if (sess->fe != s->be)
1945 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1946
1947 end:
1948 return ACT_RET_CONT;
1949}
Christopher Faulet81e20172019-12-12 16:40:30 +01001950
Christopher Faulet2eb53962020-01-14 14:47:34 +01001951static void release_http_track_sc(struct act_rule *rule)
1952{
1953 release_sample_expr(rule->arg.trk_ctr.expr);
1954}
1955
Christopher Faulet81e20172019-12-12 16:40:30 +01001956/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1957 * ACT_RET_PRS_ERR on error.
1958 */
1959static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1960 struct act_rule *rule, char **err)
1961{
1962 struct sample_expr *expr;
1963 unsigned int where;
1964 unsigned int tsc_num;
1965 const char *tsc_num_str;
1966 int cur_arg;
1967
1968 tsc_num_str = &args[*orig_arg-1][8];
1969 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1970 return ACT_RET_PRS_ERR;
1971
1972 cur_arg = *orig_arg;
1973 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001974 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001975 if (!expr)
1976 return ACT_RET_PRS_ERR;
1977
1978 where = 0;
1979 if (px->cap & PR_CAP_FE)
1980 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1981 if (px->cap & PR_CAP_BE)
1982 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1983
1984 if (!(expr->fetch->val & where)) {
1985 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1986 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001987 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001988 return ACT_RET_PRS_ERR;
1989 }
1990
1991 if (strcmp(args[cur_arg], "table") == 0) {
1992 cur_arg++;
1993 if (!*args[cur_arg]) {
1994 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001995 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001996 return ACT_RET_PRS_ERR;
1997 }
1998
1999 /* we copy the table name for now, it will be resolved later */
2000 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
2001 cur_arg++;
2002 }
2003
Christopher Fauletac98d812019-12-18 09:20:16 +01002004 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01002005 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01002006 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01002007 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01002008 rule->check_ptr = check_trk_action;
2009
2010 *orig_arg = cur_arg;
2011 return ACT_RET_PRS_OK;
2012}
2013
Amaury Denoyelle8d228232020-12-10 13:43:54 +01002014static enum act_return action_timeout_set_stream_timeout(struct act_rule *rule,
2015 struct proxy *px,
2016 struct session *sess,
2017 struct stream *s,
2018 int flags)
2019{
2020 struct sample *key;
2021
2022 if (rule->arg.timeout.expr) {
2023 key = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.timeout.expr, SMP_T_SINT);
2024 if (!key)
2025 return ACT_RET_CONT;
2026
2027 stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(key->data.u.sint));
2028 }
2029 else {
2030 stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(rule->arg.timeout.value));
2031 }
2032
2033 return ACT_RET_CONT;
2034}
2035
2036/* Parse a "set-timeout" action. Returns ACT_RET_PRS_ERR if parsing error.
2037 */
2038static enum act_parse_ret parse_http_set_timeout(const char **args,
2039 int *orig_arg,
2040 struct proxy *px,
2041 struct act_rule *rule, char **err)
2042{
2043 int cur_arg;
2044
2045 rule->action = ACT_CUSTOM;
2046 rule->action_ptr = action_timeout_set_stream_timeout;
2047 rule->release_ptr = release_timeout_action;
2048
2049 cur_arg = *orig_arg;
2050 if (!*args[cur_arg] || !*args[cur_arg + 1]) {
2051 memprintf(err, "expects exactly 2 arguments");
2052 return ACT_RET_PRS_ERR;
2053 }
2054
2055 if (!(px->cap & PR_CAP_BE)) {
2056 memprintf(err, "proxy '%s' has no backend capability", px->id);
2057 return ACT_RET_PRS_ERR;
2058 }
2059
2060 if (cfg_parse_rule_set_timeout(args, cur_arg,
2061 &rule->arg.timeout.value,
2062 &rule->arg.timeout.type,
2063 &rule->arg.timeout.expr,
2064 err,
2065 px->conf.args.file,
2066 px->conf.args.line, &px->conf.args) == -1) {
2067 return ACT_RET_PRS_ERR;
2068 }
2069
2070 *orig_arg = cur_arg + 2;
2071
2072 return ACT_RET_PRS_OK;
2073}
2074
Christopher Faulet46f95542019-12-20 10:07:22 +01002075/* This function executes a strict-mode actions. On success, it always returns
2076 * ACT_RET_CONT
2077 */
2078static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
2079 struct session *sess, struct stream *s, int flags)
2080{
2081 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
2082
2083 if (rule->action == 0) // strict-mode on
2084 msg->flags &= ~HTTP_MSGF_SOFT_RW;
2085 else // strict-mode off
2086 msg->flags |= HTTP_MSGF_SOFT_RW;
2087 return ACT_RET_CONT;
2088}
2089
2090/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
2091 * ACT_RET_PRS_ERR on error.
2092 */
2093static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
2094 struct act_rule *rule, char **err)
2095{
2096 int cur_arg;
2097
Christopher Faulet46f95542019-12-20 10:07:22 +01002098 cur_arg = *orig_arg;
2099 if (!*args[cur_arg]) {
2100 memprintf(err, "expects exactly 1 arguments");
2101 return ACT_RET_PRS_ERR;
2102 }
2103
2104 if (strcasecmp(args[cur_arg], "on") == 0)
2105 rule->action = 0; // strict-mode on
2106 else if (strcasecmp(args[cur_arg], "off") == 0)
2107 rule->action = 1; // strict-mode off
2108 else {
2109 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
2110 return ACT_RET_PRS_ERR;
2111 }
2112 rule->action_ptr = http_action_strict_mode;
2113
2114 *orig_arg = cur_arg + 1;
2115 return ACT_RET_PRS_OK;
2116}
2117
Christopher Faulet24231ab2020-01-24 17:44:23 +01002118/* This function executes a return action. It builds an HTX message from an
2119 * errorfile, an raw file or a log-format string, depending on <.action>
2120 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
2121 * returned.
2122 */
2123static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
2124 struct session *sess, struct stream *s, int flags)
2125{
2126 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002127
Christopher Faulet2d36df22021-02-19 11:41:01 +01002128 s->txn->status = rule->arg.http_reply->status;
Christopher Faulet0e2ad612020-05-13 16:38:37 +02002129 if (http_reply_message(s, rule->arg.http_reply) == -1)
2130 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002131
Christopher Faulet24231ab2020-01-24 17:44:23 +01002132 if (rule->from == ACT_F_HTTP_REQ) {
2133 /* let's log the request time */
2134 s->logs.tv_request = now;
2135 req->analysers &= AN_REQ_FLT_END;
2136
2137 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
Willy Tarreau4781b152021-04-06 13:53:36 +02002138 _HA_ATOMIC_INC(&s->sess->fe->fe_counters.intercepted_req);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002139 }
2140
2141 if (!(s->flags & SF_ERR_MASK))
2142 s->flags |= SF_ERR_LOCAL;
2143 if (!(s->flags & SF_FINST_MASK))
2144 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
2145
Christopher Faulet0e2ad612020-05-13 16:38:37 +02002146 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002147}
2148
Christopher Faulet24231ab2020-01-24 17:44:23 +01002149/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02002150 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
2151 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01002152 */
2153static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
2154 struct act_rule *rule, char **err)
2155{
Christopher Faulet47e791e2020-05-13 14:36:55 +02002156 /* Prepare parsing of log-format strings */
2157 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
2158 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
2159 if (!rule->arg.http_reply)
2160 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002161
Christopher Fauletba946bf2020-05-13 08:50:07 +02002162 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002163 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02002164 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002165 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02002166 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002167 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002168}
2169
Christopher Faulet021a8e42021-03-29 10:46:38 +02002170
2171
2172/* This function executes a wait-for-body action. It waits for the message
2173 * payload for a max configured time (.arg.p[0]) and eventually for only first
2174 * <arg.p[1]> bytes (0 means no limit). It relies on http_wait_for_msg_body()
2175 * function. it returns ACT_RET_CONT when conditions are met to stop to wait.
2176 * Otherwise ACT_RET_YIELD is returned to wait for more data. ACT_RET_INV is
2177 * returned if a parsing error is raised by lower level and ACT_RET_ERR if an
2178 * internal error occured. Finally ACT_RET_ABRT is returned when a timeout
2179 * occured.
2180 */
2181static enum act_return http_action_wait_for_body(struct act_rule *rule, struct proxy *px,
2182 struct session *sess, struct stream *s, int flags)
2183{
2184 struct channel *chn = ((rule->from == ACT_F_HTTP_REQ) ? &s->req : &s->res);
2185 unsigned int time = (uintptr_t)rule->arg.act.p[0];
2186 unsigned int bytes = (uintptr_t)rule->arg.act.p[1];
2187
2188 switch (http_wait_for_msg_body(s, chn, time, bytes)) {
2189 case HTTP_RULE_RES_CONT:
2190 return ACT_RET_CONT;
2191 case HTTP_RULE_RES_YIELD:
2192 return ACT_RET_YIELD;
2193 case HTTP_RULE_RES_BADREQ:
2194 return ACT_RET_INV;
2195 case HTTP_RULE_RES_ERROR:
2196 return ACT_RET_ERR;
2197 case HTTP_RULE_RES_ABRT:
2198 return ACT_RET_ABRT;
2199 default:
2200 return ACT_RET_ERR;
2201 }
2202}
2203
2204/* Parse a "wait-for-body" action. It returns ACT_RET_PRS_OK on success,
2205 * ACT_RET_PRS_ERR on error.
2206 */
2207static enum act_parse_ret parse_http_wait_for_body(const char **args, int *orig_arg, struct proxy *px,
2208 struct act_rule *rule, char **err)
2209{
2210 int cur_arg;
2211 unsigned int time, bytes;
2212 const char *res;
2213
2214 cur_arg = *orig_arg;
2215 if (!*args[cur_arg]) {
2216 memprintf(err, "expects time <time> [ at-least <bytes> ]");
2217 return ACT_RET_PRS_ERR;
2218 }
2219
2220 time = UINT_MAX; /* To be sure it is set */
2221 bytes = 0; /* Default value, wait all the body */
2222 while (*(args[cur_arg])) {
2223 if (strcmp(args[cur_arg], "time") == 0) {
2224 if (!*args[cur_arg + 1]) {
2225 memprintf(err, "missing argument for '%s'", args[cur_arg]);
2226 return ACT_RET_PRS_ERR;
2227 }
2228 res = parse_time_err(args[cur_arg+1], &time, TIME_UNIT_MS);
2229 if (res == PARSE_TIME_OVER) {
2230 memprintf(err, "time overflow (maximum value is 2147483647 ms or ~24.8 days)");
2231 return ACT_RET_PRS_ERR;
2232 }
2233 if (res == PARSE_TIME_UNDER) {
2234 memprintf(err, "time underflow (minimum non-null value is 1 ms)");
2235 return ACT_RET_PRS_ERR;
2236 }
2237 if (res) {
2238 memprintf(err, "unexpected character '%c'", *res);
2239 return ACT_RET_PRS_ERR;
2240 }
2241 cur_arg++;
2242 }
2243 else if (strcmp(args[cur_arg], "at-least") == 0) {
2244 if (!*args[cur_arg + 1]) {
2245 memprintf(err, "missing argument for '%s'", args[cur_arg]);
2246 return ACT_RET_PRS_ERR;
2247 }
2248 res = parse_size_err(args[cur_arg+1], &bytes);
2249 if (res) {
2250 memprintf(err, "unexpected character '%c'", *res);
2251 return ACT_RET_PRS_ERR;
2252 }
2253 cur_arg++;
2254 }
2255 else
2256 break;
2257 cur_arg++;
2258 }
2259
2260 if (time == UINT_MAX) {
2261 memprintf(err, "expects time <time> [ at-least <bytes> ]");
2262 return ACT_RET_PRS_ERR;
2263 }
2264
2265 rule->arg.act.p[0] = (void *)(uintptr_t)time;
2266 rule->arg.act.p[1] = (void *)(uintptr_t)bytes;
2267
2268 *orig_arg = cur_arg;
2269
2270 rule->action = ACT_CUSTOM;
2271 rule->action_ptr = http_action_wait_for_body;
2272 return ACT_RET_PRS_OK;
2273}
2274
Willy Tarreau79e57332018-10-02 16:01:16 +02002275/************************************************************************/
2276/* All supported http-request action keywords must be declared here. */
2277/************************************************************************/
2278
2279static struct action_kw_list http_req_actions = {
2280 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002281 { "add-acl", parse_http_set_map, 1 },
2282 { "add-header", parse_http_set_header, 0 },
2283 { "allow", parse_http_allow, 0 },
2284 { "auth", parse_http_auth, 0 },
2285 { "capture", parse_http_req_capture, 0 },
2286 { "del-acl", parse_http_set_map, 1 },
2287 { "del-header", parse_http_del_header, 0 },
2288 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002289 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002290 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
2291 { "early-hint", parse_http_set_header, 0 },
Tim Duesterhusd2bedcc2021-04-15 21:45:57 +02002292 { "normalize-uri", parse_http_normalize_uri, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002293 { "redirect", parse_http_redirect, 0 },
2294 { "reject", parse_http_action_reject, 0 },
2295 { "replace-header", parse_http_replace_header, 0 },
2296 { "replace-path", parse_replace_uri, 0 },
Christopher Faulet312294f2020-09-02 17:17:44 +02002297 { "replace-pathq", parse_replace_uri, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002298 { "replace-uri", parse_replace_uri, 0 },
2299 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002300 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002301 { "set-header", parse_http_set_header, 0 },
2302 { "set-log-level", parse_http_set_log_level, 0 },
2303 { "set-map", parse_http_set_map, 1 },
2304 { "set-method", parse_set_req_line, 0 },
2305 { "set-mark", parse_http_set_mark, 0 },
2306 { "set-nice", parse_http_set_nice, 0 },
2307 { "set-path", parse_set_req_line, 0 },
Christopher Faulet312294f2020-09-02 17:17:44 +02002308 { "set-pathq", parse_set_req_line, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002309 { "set-query", parse_set_req_line, 0 },
2310 { "set-tos", parse_http_set_tos, 0 },
2311 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002312 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002313 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002314 { "track-sc", parse_http_track_sc, 1 },
Amaury Denoyelle8d228232020-12-10 13:43:54 +01002315 { "set-timeout", parse_http_set_timeout, 0 },
Christopher Faulet021a8e42021-03-29 10:46:38 +02002316 { "wait-for-body", parse_http_wait_for_body, 0 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002317 { NULL, NULL }
2318 }
2319};
2320
Willy Tarreau0108d902018-11-25 19:14:37 +01002321INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
2322
Willy Tarreau79e57332018-10-02 16:01:16 +02002323static struct action_kw_list http_res_actions = {
2324 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002325 { "add-acl", parse_http_set_map, 1 },
2326 { "add-header", parse_http_set_header, 0 },
2327 { "allow", parse_http_allow, 0 },
2328 { "capture", parse_http_res_capture, 0 },
2329 { "del-acl", parse_http_set_map, 1 },
2330 { "del-header", parse_http_del_header, 0 },
2331 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002332 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002333 { "redirect", parse_http_redirect, 0 },
2334 { "replace-header", parse_http_replace_header, 0 },
2335 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002336 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002337 { "set-header", parse_http_set_header, 0 },
2338 { "set-log-level", parse_http_set_log_level, 0 },
2339 { "set-map", parse_http_set_map, 1 },
2340 { "set-mark", parse_http_set_mark, 0 },
2341 { "set-nice", parse_http_set_nice, 0 },
2342 { "set-status", parse_http_set_status, 0 },
2343 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002344 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002345 { "track-sc", parse_http_track_sc, 1 },
Christopher Faulet021a8e42021-03-29 10:46:38 +02002346 { "wait-for-body", parse_http_wait_for_body, 0 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002347 { NULL, NULL }
2348 }
2349};
2350
Willy Tarreau0108d902018-11-25 19:14:37 +01002351INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02002352
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01002353static struct action_kw_list http_after_res_actions = {
2354 .kw = {
2355 { "add-header", parse_http_set_header, 0 },
2356 { "allow", parse_http_allow, 0 },
2357 { "del-header", parse_http_del_header, 0 },
2358 { "replace-header", parse_http_replace_header, 0 },
2359 { "replace-value", parse_http_replace_header, 0 },
2360 { "set-header", parse_http_set_header, 0 },
2361 { "set-status", parse_http_set_status, 0 },
2362 { "strict-mode", parse_http_strict_mode, 0 },
2363 { NULL, NULL }
2364 }
2365};
2366
2367INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
2368
Willy Tarreau79e57332018-10-02 16:01:16 +02002369/*
2370 * Local variables:
2371 * c-indent-level: 8
2372 * c-basic-offset: 8
2373 * End:
2374 */