blob: 986e54216641686c0310ce12b1dccce6b5290f47 [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
Christopher Faulet81e20172019-12-12 16:40:30 +010019#include <common/cfgparse.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020020#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
24#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010025#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020026#include <common/memory.h>
27#include <common/standard.h>
28#include <common/version.h>
29
30#include <types/capture.h>
31#include <types/global.h>
32
33#include <proto/acl.h>
34#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010035#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020036#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020037#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020038#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020039#include <proto/http_ana.h>
Christopher Faulet046cf442019-12-17 15:45:23 +010040#include <proto/pattern.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010041#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020042
43
44/* This function executes one of the set-{method,path,query,uri} actions. It
45 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010046 * the action to be performed in <.action>, previously filled by function
Willy Tarreau79e57332018-10-02 16:01:16 +020047 * parse_set_req_line(). The replacement action is excuted by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010048 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
49 * occurs while soft rewrites are enabled, the action is canceled, but the rule
50 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020051 */
52static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
53 struct session *sess, struct stream *s, int flags)
54{
55 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010056 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020057
58 replace = alloc_trash_chunk();
59 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +010060 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +020061
62 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +010063 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +020064 replace->area[replace->data++] = '?';
65 replace->data += build_logline(s, replace->area + replace->data,
66 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +010067 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +020068
Christopher Faulet2c22a692019-12-18 15:39:56 +010069 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +010070 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +020071
Christopher Faulete00d06c2019-12-16 17:18:42 +010072 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +020073 free_trash_chunk(replace);
74 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +010075
76 fail_alloc:
77 if (!(s->flags & SF_ERR_MASK))
78 s->flags |= SF_ERR_RESOURCE;
79 ret = ACT_RET_ERR;
80 goto leave;
81
82 fail_rewrite:
83 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
84 if (s->flags & SF_BE_ASSIGNED)
85 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
86 if (sess->listener->counters)
87 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
88 if (objt_server(s->target))
89 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
90
91 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
92 ret = ACT_RET_ERR;
93 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +020094}
95
96/* parse an http-request action among :
97 * set-method
98 * set-path
99 * set-query
100 * set-uri
101 *
102 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100103 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100104 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
105 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200106 */
107static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
108 struct act_rule *rule, char **err)
109{
110 int cur_arg = *orig_arg;
111
Willy Tarreau79e57332018-10-02 16:01:16 +0200112 switch (args[0][4]) {
113 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100114 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200115 break;
116 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100117 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200118 break;
119 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100120 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200121 break;
122 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100123 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200124 break;
125 default:
126 memprintf(err, "internal error: unhandled action '%s'", args[0]);
127 return ACT_RET_PRS_ERR;
128 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100129 rule->action_ptr = http_action_set_req_line;
Willy Tarreau79e57332018-10-02 16:01:16 +0200130
131 if (!*args[cur_arg] ||
132 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
133 memprintf(err, "expects exactly 1 argument <format>");
134 return ACT_RET_PRS_ERR;
135 }
136
Christopher Faulet96bff762019-12-17 13:46:18 +0100137 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200138 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100139 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200140 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
141 return ACT_RET_PRS_ERR;
142 }
143
144 (*orig_arg)++;
145 return ACT_RET_PRS_OK;
146}
147
Willy Tarreau33810222019-06-12 17:44:02 +0200148/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100149 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200150 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100151 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100152 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100153 * contains 1 for the path or 3 for the URI (values used by
154 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
155 * occurs while soft rewrites are enabled, the action is canceled, but the rule
156 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200157 */
158static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
159 struct session *sess, struct stream *s, int flags)
160{
Christopher Faulet13403762019-12-13 09:01:57 +0100161 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200162 struct buffer *replace, *output;
163 struct ist uri;
164 int len;
165
166 replace = alloc_trash_chunk();
167 output = alloc_trash_chunk();
168 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100169 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200170 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100171
Christopher Faulet2c22a692019-12-18 15:39:56 +0100172 if (rule->action == 1) // replace-path
Christopher Faulet96bff762019-12-17 13:46:18 +0100173 uri = http_get_path(uri);
Willy Tarreau262c3f12019-12-17 06:52:51 +0100174
Christopher Faulet96bff762019-12-17 13:46:18 +0100175 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200176 goto leave;
177
Christopher Faulet96bff762019-12-17 13:46:18 +0100178 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200179
180 /* note: uri.ptr doesn't need to be zero-terminated because it will
181 * only be used to pick pmatch references.
182 */
183 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
184 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100185 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200186
Christopher Faulet2c22a692019-12-18 15:39:56 +0100187 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100188 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200189
Christopher Faulete00d06c2019-12-16 17:18:42 +0100190 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200191 free_trash_chunk(output);
192 free_trash_chunk(replace);
193 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100194
195 fail_alloc:
196 if (!(s->flags & SF_ERR_MASK))
197 s->flags |= SF_ERR_RESOURCE;
198 ret = ACT_RET_ERR;
199 goto leave;
200
201 fail_rewrite:
202 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
203 if (s->flags & SF_BE_ASSIGNED)
204 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
205 if (sess->listener->counters)
206 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
207 if (objt_server(s->target))
208 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
209
210 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
211 ret = ACT_RET_ERR;
212 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200213}
214
Willy Tarreau262c3f12019-12-17 06:52:51 +0100215/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200216 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100217 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100218 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200219 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
220 */
221static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
222 struct act_rule *rule, char **err)
223{
224 int cur_arg = *orig_arg;
225 char *error = NULL;
226
Willy Tarreau262c3f12019-12-17 06:52:51 +0100227 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100228 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100229 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100230 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100231
Willy Tarreau33810222019-06-12 17:44:02 +0200232 rule->action_ptr = http_action_replace_uri;
233
234 if (!*args[cur_arg] || !*args[cur_arg+1] ||
235 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
236 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
237 return ACT_RET_PRS_ERR;
238 }
239
Christopher Faulet96bff762019-12-17 13:46:18 +0100240 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200241 memprintf(err, "failed to parse the regex : %s", error);
242 free(error);
243 return ACT_RET_PRS_ERR;
244 }
245
Christopher Faulet96bff762019-12-17 13:46:18 +0100246 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200247 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100248 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200249 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
250 return ACT_RET_PRS_ERR;
251 }
252
253 (*orig_arg) += 2;
254 return ACT_RET_PRS_OK;
255}
256
Willy Tarreau79e57332018-10-02 16:01:16 +0200257/* This function is just a compliant action wrapper for "set-status". */
258static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
259 struct session *sess, struct stream *s, int flags)
260{
Christopher Faulet96bff762019-12-17 13:46:18 +0100261 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100262 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
263 if (s->flags & SF_BE_ASSIGNED)
264 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
265 if (sess->listener->counters)
266 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
267 if (objt_server(s->target))
268 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
269
270 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
271 return ACT_RET_ERR;
272 }
273
Willy Tarreau79e57332018-10-02 16:01:16 +0200274 return ACT_RET_CONT;
275}
276
277/* parse set-status action:
278 * This action accepts a single argument of type int representing
279 * an http status code. It returns ACT_RET_PRS_OK on success,
280 * ACT_RET_PRS_ERR on error.
281 */
282static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
283 struct act_rule *rule, char **err)
284{
285 char *error;
286
287 rule->action = ACT_CUSTOM;
288 rule->action_ptr = action_http_set_status;
289
290 /* Check if an argument is available */
291 if (!*args[*orig_arg]) {
292 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
293 return ACT_RET_PRS_ERR;
294 }
295
296 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100297 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
298 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200299 memprintf(err, "expects an integer status code between 100 and 999");
300 return ACT_RET_PRS_ERR;
301 }
302
303 (*orig_arg)++;
304
305 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100306 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200307 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
308 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
309 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100310 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
311 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200312 (*orig_arg)++;
313 }
314
315 return ACT_RET_PRS_OK;
316}
317
318/* This function executes the "reject" HTTP action. It clears the request and
319 * response buffer without sending any response. It can be useful as an HTTP
320 * alternative to the silent-drop action to defend against DoS attacks, and may
321 * also be used with HTTP/2 to close a connection instead of just a stream.
322 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200323 * flags will indicate "PR". It always returns ACT_RET_DONE.
Willy Tarreau79e57332018-10-02 16:01:16 +0200324 */
325static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
326 struct session *sess, struct stream *s, int flags)
327{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100328 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200329 channel_abort(&s->req);
330 channel_abort(&s->res);
331 s->req.analysers = 0;
332 s->res.analysers = 0;
333
Olivier Houcharda798bf52019-03-08 18:52:00 +0100334 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
335 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200336 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100337 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200338
339 if (!(s->flags & SF_ERR_MASK))
340 s->flags |= SF_ERR_PRXCOND;
341 if (!(s->flags & SF_FINST_MASK))
342 s->flags |= SF_FINST_R;
343
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200344 return ACT_RET_DONE;
Willy Tarreau79e57332018-10-02 16:01:16 +0200345}
346
347/* parse the "reject" action:
348 * This action takes no argument and returns ACT_RET_PRS_OK on success,
349 * ACT_RET_PRS_ERR on error.
350 */
351static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
352 struct act_rule *rule, char **err)
353{
354 rule->action = ACT_CUSTOM;
355 rule->action_ptr = http_action_reject;
356 return ACT_RET_PRS_OK;
357}
358
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200359/* This function executes the "disable-l7-retry" HTTP action.
360 * It disables L7 retries (all retry except for a connection failure). This
361 * can be useful for example to avoid retrying on POST requests.
362 * It just removes the L7 retry flag on the stream_interface, and always
363 * return ACT_RET_CONT;
364 */
365static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
366 struct session *sess, struct stream *s, int flags)
367{
368 struct stream_interface *si = &s->si[1];
369
370 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
371 * let's be future-proof and remove it anyway.
372 */
373 si->flags &= ~SI_FL_L7_RETRY;
374 si->flags |= SI_FL_D_L7_RETRY;
375 return ACT_RET_CONT;
376}
377
378/* parse the "disable-l7-retry" action:
379 * This action takes no argument and returns ACT_RET_PRS_OK on success,
380 * ACT_RET_PRS_ERR on error.
381 */
382static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
383 int *orig_args, struct proxy *px,
384 struct act_rule *rule, char **err)
385{
386 rule->action = ACT_CUSTOM;
387 rule->action_ptr = http_req_disable_l7_retry;
388 return ACT_RET_PRS_OK;
389}
390
Willy Tarreau79e57332018-10-02 16:01:16 +0200391/* This function executes the "capture" action. It executes a fetch expression,
392 * turns the result into a string and puts it in a capture slot. It always
393 * returns 1. If an error occurs the action is cancelled, but the rule
394 * processing continues.
395 */
396static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
397 struct session *sess, struct stream *s, int flags)
398{
399 struct sample *key;
400 struct cap_hdr *h = rule->arg.cap.hdr;
401 char **cap = s->req_cap;
402 int len;
403
404 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
405 if (!key)
406 return ACT_RET_CONT;
407
408 if (cap[h->index] == NULL)
409 cap[h->index] = pool_alloc(h->pool);
410
411 if (cap[h->index] == NULL) /* no more capture memory */
412 return ACT_RET_CONT;
413
414 len = key->data.u.str.data;
415 if (len > h->len)
416 len = h->len;
417
418 memcpy(cap[h->index], key->data.u.str.area, len);
419 cap[h->index][len] = 0;
420 return ACT_RET_CONT;
421}
422
423/* This function executes the "capture" action and store the result in a
424 * capture slot if exists. It executes a fetch expression, turns the result
425 * into a string and puts it in a capture slot. It always returns 1. If an
426 * error occurs the action is cancelled, but the rule processing continues.
427 */
428static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
429 struct session *sess, struct stream *s, int flags)
430{
431 struct sample *key;
432 struct cap_hdr *h;
433 char **cap = s->req_cap;
434 struct proxy *fe = strm_fe(s);
435 int len;
436 int i;
437
438 /* Look for the original configuration. */
439 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
440 h != NULL && i != rule->arg.capid.idx ;
441 i--, h = h->next);
442 if (!h)
443 return ACT_RET_CONT;
444
445 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
446 if (!key)
447 return ACT_RET_CONT;
448
449 if (cap[h->index] == NULL)
450 cap[h->index] = pool_alloc(h->pool);
451
452 if (cap[h->index] == NULL) /* no more capture memory */
453 return ACT_RET_CONT;
454
455 len = key->data.u.str.data;
456 if (len > h->len)
457 len = h->len;
458
459 memcpy(cap[h->index], key->data.u.str.area, len);
460 cap[h->index][len] = 0;
461 return ACT_RET_CONT;
462}
463
464/* Check an "http-request capture" action.
465 *
466 * The function returns 1 in success case, otherwise, it returns 0 and err is
467 * filled.
468 */
469static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
470{
471 if (rule->action_ptr != http_action_req_capture_by_id)
472 return 1;
473
474 if (rule->arg.capid.idx >= px->nb_req_cap) {
475 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
476 rule->arg.capid.idx);
477 return 0;
478 }
479
480 return 1;
481}
482
483/* parse an "http-request capture" action. It takes a single argument which is
484 * a sample fetch expression. It stores the expression into arg->act.p[0] and
485 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
486 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
487 */
488static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
489 struct act_rule *rule, char **err)
490{
491 struct sample_expr *expr;
492 struct cap_hdr *hdr;
493 int cur_arg;
494 int len = 0;
495
496 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
497 if (strcmp(args[cur_arg], "if") == 0 ||
498 strcmp(args[cur_arg], "unless") == 0)
499 break;
500
501 if (cur_arg < *orig_arg + 3) {
502 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
503 return ACT_RET_PRS_ERR;
504 }
505
506 cur_arg = *orig_arg;
507 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
508 if (!expr)
509 return ACT_RET_PRS_ERR;
510
511 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
512 memprintf(err,
513 "fetch method '%s' extracts information from '%s', none of which is available here",
514 args[cur_arg-1], sample_src_names(expr->fetch->use));
515 free(expr);
516 return ACT_RET_PRS_ERR;
517 }
518
519 if (!args[cur_arg] || !*args[cur_arg]) {
520 memprintf(err, "expects 'len or 'id'");
521 free(expr);
522 return ACT_RET_PRS_ERR;
523 }
524
525 if (strcmp(args[cur_arg], "len") == 0) {
526 cur_arg++;
527
528 if (!(px->cap & PR_CAP_FE)) {
529 memprintf(err, "proxy '%s' has no frontend capability", px->id);
530 return ACT_RET_PRS_ERR;
531 }
532
533 px->conf.args.ctx = ARGC_CAP;
534
535 if (!args[cur_arg]) {
536 memprintf(err, "missing length value");
537 free(expr);
538 return ACT_RET_PRS_ERR;
539 }
540 /* we copy the table name for now, it will be resolved later */
541 len = atoi(args[cur_arg]);
542 if (len <= 0) {
543 memprintf(err, "length must be > 0");
544 free(expr);
545 return ACT_RET_PRS_ERR;
546 }
547 cur_arg++;
548
Willy Tarreau79e57332018-10-02 16:01:16 +0200549 hdr = calloc(1, sizeof(*hdr));
550 hdr->next = px->req_cap;
551 hdr->name = NULL; /* not a header capture */
552 hdr->namelen = 0;
553 hdr->len = len;
554 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
555 hdr->index = px->nb_req_cap++;
556
557 px->req_cap = hdr;
558 px->to_log |= LW_REQHDR;
559
560 rule->action = ACT_CUSTOM;
561 rule->action_ptr = http_action_req_capture;
562 rule->arg.cap.expr = expr;
563 rule->arg.cap.hdr = hdr;
564 }
565
566 else if (strcmp(args[cur_arg], "id") == 0) {
567 int id;
568 char *error;
569
570 cur_arg++;
571
572 if (!args[cur_arg]) {
573 memprintf(err, "missing id value");
574 free(expr);
575 return ACT_RET_PRS_ERR;
576 }
577
578 id = strtol(args[cur_arg], &error, 10);
579 if (*error != '\0') {
580 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
581 free(expr);
582 return ACT_RET_PRS_ERR;
583 }
584 cur_arg++;
585
586 px->conf.args.ctx = ARGC_CAP;
587
588 rule->action = ACT_CUSTOM;
589 rule->action_ptr = http_action_req_capture_by_id;
590 rule->check_ptr = check_http_req_capture;
591 rule->arg.capid.expr = expr;
592 rule->arg.capid.idx = id;
593 }
594
595 else {
596 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
597 free(expr);
598 return ACT_RET_PRS_ERR;
599 }
600
601 *orig_arg = cur_arg;
602 return ACT_RET_PRS_OK;
603}
604
605/* This function executes the "capture" action and store the result in a
606 * capture slot if exists. It executes a fetch expression, turns the result
607 * into a string and puts it in a capture slot. It always returns 1. If an
608 * error occurs the action is cancelled, but the rule processing continues.
609 */
610static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
611 struct session *sess, struct stream *s, int flags)
612{
613 struct sample *key;
614 struct cap_hdr *h;
615 char **cap = s->res_cap;
616 struct proxy *fe = strm_fe(s);
617 int len;
618 int i;
619
620 /* Look for the original configuration. */
621 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
622 h != NULL && i != rule->arg.capid.idx ;
623 i--, h = h->next);
624 if (!h)
625 return ACT_RET_CONT;
626
627 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
628 if (!key)
629 return ACT_RET_CONT;
630
631 if (cap[h->index] == NULL)
632 cap[h->index] = pool_alloc(h->pool);
633
634 if (cap[h->index] == NULL) /* no more capture memory */
635 return ACT_RET_CONT;
636
637 len = key->data.u.str.data;
638 if (len > h->len)
639 len = h->len;
640
641 memcpy(cap[h->index], key->data.u.str.area, len);
642 cap[h->index][len] = 0;
643 return ACT_RET_CONT;
644}
645
646/* Check an "http-response capture" action.
647 *
648 * The function returns 1 in success case, otherwise, it returns 0 and err is
649 * filled.
650 */
651static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
652{
653 if (rule->action_ptr != http_action_res_capture_by_id)
654 return 1;
655
656 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
657 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
658 rule->arg.capid.idx);
659 return 0;
660 }
661
662 return 1;
663}
664
665/* parse an "http-response capture" action. It takes a single argument which is
666 * a sample fetch expression. It stores the expression into arg->act.p[0] and
667 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
668 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
669 */
670static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
671 struct act_rule *rule, char **err)
672{
673 struct sample_expr *expr;
674 int cur_arg;
675 int id;
676 char *error;
677
678 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
679 if (strcmp(args[cur_arg], "if") == 0 ||
680 strcmp(args[cur_arg], "unless") == 0)
681 break;
682
683 if (cur_arg < *orig_arg + 3) {
684 memprintf(err, "expects <expression> id <idx>");
685 return ACT_RET_PRS_ERR;
686 }
687
688 cur_arg = *orig_arg;
689 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
690 if (!expr)
691 return ACT_RET_PRS_ERR;
692
693 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
694 memprintf(err,
695 "fetch method '%s' extracts information from '%s', none of which is available here",
696 args[cur_arg-1], sample_src_names(expr->fetch->use));
697 free(expr);
698 return ACT_RET_PRS_ERR;
699 }
700
701 if (!args[cur_arg] || !*args[cur_arg]) {
702 memprintf(err, "expects 'id'");
703 free(expr);
704 return ACT_RET_PRS_ERR;
705 }
706
707 if (strcmp(args[cur_arg], "id") != 0) {
708 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
709 free(expr);
710 return ACT_RET_PRS_ERR;
711 }
712
713 cur_arg++;
714
715 if (!args[cur_arg]) {
716 memprintf(err, "missing id value");
717 free(expr);
718 return ACT_RET_PRS_ERR;
719 }
720
721 id = strtol(args[cur_arg], &error, 10);
722 if (*error != '\0') {
723 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
724 free(expr);
725 return ACT_RET_PRS_ERR;
726 }
727 cur_arg++;
728
729 px->conf.args.ctx = ARGC_CAP;
730
731 rule->action = ACT_CUSTOM;
732 rule->action_ptr = http_action_res_capture_by_id;
733 rule->check_ptr = check_http_res_capture;
734 rule->arg.capid.expr = expr;
735 rule->arg.capid.idx = id;
736
737 *orig_arg = cur_arg;
738 return ACT_RET_PRS_OK;
739}
740
Christopher Faulet81e20172019-12-12 16:40:30 +0100741/* Parse a "allow" action for a request or a response rule. It takes no argument. It
742 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
743 */
744static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
745 struct act_rule *rule, char **err)
746{
747 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100748 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100749 return ACT_RET_PRS_OK;
750}
751
752/* Parse "deny" or "tarpit" actions for a request rule. It may take 2 optional arguments
753 * to define the status code. It returns ACT_RET_PRS_OK on success,
754 * ACT_RET_PRS_ERR on error.
755 */
756static enum act_parse_ret parse_http_req_deny(const char **args, int *orig_arg, struct proxy *px,
757 struct act_rule *rule, char **err)
758{
759 int code, hc, cur_arg;
760
761 cur_arg = *orig_arg;
762 if (!strcmp(args[cur_arg-1], "tarpit")) {
763 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet96bff762019-12-17 13:46:18 +0100764 rule->arg.http.i = HTTP_ERR_500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100765 }
766 else {
767 rule->action = ACT_ACTION_DENY;
Christopher Faulet96bff762019-12-17 13:46:18 +0100768 rule->arg.http.i = HTTP_ERR_403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100769 }
Christopher Faulet245cf792019-12-18 14:58:12 +0100770 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100771
772 if (strcmp(args[cur_arg], "deny_status") == 0) {
773 cur_arg++;
774 if (!*args[cur_arg]) {
775 memprintf(err, "missing status code.\n");
776 return ACT_RET_PRS_ERR;
777 }
778
779 code = atol(args[cur_arg]);
780 cur_arg++;
781 for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
782 if (http_err_codes[hc] == code) {
Christopher Faulet96bff762019-12-17 13:46:18 +0100783 rule->arg.http.i = hc;
Christopher Faulet81e20172019-12-12 16:40:30 +0100784 break;
785 }
786 }
787 if (hc >= HTTP_ERR_SIZE)
788 memprintf(err, "status code %d not handled, using default code %d",
Christopher Faulet96bff762019-12-17 13:46:18 +0100789 code, http_err_codes[rule->arg.http.i]);
Christopher Faulet81e20172019-12-12 16:40:30 +0100790 }
791
792 *orig_arg = cur_arg;
793 return ACT_RET_PRS_OK;
794}
795
796/* Parse a "deny" action for a response rule. It takes no argument. It returns
797 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
798 */
799static enum act_parse_ret parse_http_res_deny(const char **args, int *orig_arg, struct proxy *px,
800 struct act_rule *rule, char **err)
801{
802 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100803 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100804 return ACT_RET_PRS_OK;
805}
806
807/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
808 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
809 */
810static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
811 struct act_rule *rule, char **err)
812{
813 int cur_arg;
814
815 rule->action = ACT_HTTP_REQ_AUTH;
Christopher Faulet245cf792019-12-18 14:58:12 +0100816 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100817
818 cur_arg = *orig_arg;
819 if (!strcmp(args[cur_arg], "realm")) {
820 cur_arg++;
821 if (!*args[cur_arg]) {
822 memprintf(err, "missing realm value.\n");
823 return ACT_RET_PRS_ERR;
824 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100825 rule->arg.http.str.ptr = strdup(args[cur_arg]);
826 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100827 cur_arg++;
828 }
829
830 *orig_arg = cur_arg;
831 return ACT_RET_PRS_OK;
832}
833
834/* Parse a "set-nice" action. It takes the nice value as argument. It returns
835 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
836 */
837static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
838 struct act_rule *rule, char **err)
839{
840 int cur_arg;
841
842 rule->action = ACT_HTTP_SET_NICE;
843
844 cur_arg = *orig_arg;
845 if (!*args[cur_arg]) {
846 memprintf(err, "expects exactly 1 argument (integer value)");
847 return ACT_RET_PRS_ERR;
848 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100849 rule->arg.http.i = atoi(args[cur_arg]);
850 if (rule->arg.http.i < -1024)
851 rule->arg.http.i = -1024;
852 else if (rule->arg.http.i > 1024)
853 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +0100854
855 *orig_arg = cur_arg + 1;
856 return ACT_RET_PRS_OK;
857}
858
859/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
860 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
861 */
862static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
863 struct act_rule *rule, char **err)
864{
865#ifdef IP_TOS
866 char *endp;
867 int cur_arg;
868
869 rule->action = ACT_HTTP_SET_TOS;
870
871 cur_arg = *orig_arg;
872 if (!*args[cur_arg]) {
873 memprintf(err, "expects exactly 1 argument (integer/hex value)");
874 return ACT_RET_PRS_ERR;
875 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100876 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100877 if (endp && *endp != '\0') {
878 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
879 return ACT_RET_PRS_ERR;
880 }
881
882 *orig_arg = cur_arg + 1;
883 return ACT_RET_PRS_OK;
884#else
885 memprintf(err, "not supported on this platform (IP_TOS undefined)");
886 return ACT_RET_PRS_ERR;
887#endif
888}
889
890/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
891 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
892 */
893static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
894 struct act_rule *rule, char **err)
895{
896#ifdef SO_MARK
897 char *endp;
898 int cur_arg;
899
900 rule->action = ACT_HTTP_SET_MARK;
901
902 cur_arg = *orig_arg;
903 if (!*args[cur_arg]) {
904 memprintf(err, "expects exactly 1 argument (integer/hex value)");
905 return ACT_RET_PRS_ERR;
906 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100907 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100908 if (endp && *endp != '\0') {
909 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
910 return ACT_RET_PRS_ERR;
911 }
912
913 *orig_arg = cur_arg + 1;
914 global.last_checks |= LSTCHK_NETADM;
915 return ACT_RET_PRS_OK;
916#else
917 memprintf(err, "not supported on this platform (SO_MARK undefined)");
918 return ACT_RET_PRS_ERR;
919#endif
920}
921
922/* Parse a "set-log-level" action. It takes the level value as argument. It
923 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
924 */
925static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
926 struct act_rule *rule, char **err)
927{
928 int cur_arg;
929
930 rule->action = ACT_HTTP_SET_LOGL;
931
932 cur_arg = *orig_arg;
933 if (!*args[cur_arg]) {
934 bad_log_level:
935 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
936 return ACT_RET_PRS_ERR;
937 }
938 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +0100939 rule->arg.http.i = -1;
940 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +0100941 goto bad_log_level;
942
943 *orig_arg = cur_arg + 1;
944 return ACT_RET_PRS_OK;
945}
946
Christopher Faulet91b3ec12020-01-17 22:30:06 +0100947/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
948 * 103 response header with <.arg.http.str> name and with a value built
949 * according to <.arg.http.fmt> log line format. If it is the first early-hint
950 * rule of a serie, the 103 response start-line is added first. At the end, if
951 * the next rule is not an early-hint rule or if it is the last rule, the EOH
952 * block is added to terminate the response. On success, it returns
953 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
954 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
955 * returned.
956 */
957static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
958 struct session *sess, struct stream *s, int flags)
959{
960 struct act_rule *prev_rule, *next_rule;
961 struct channel *res = &s->res;
962 struct htx *htx = htx_from_buf(&res->buf);
963 struct buffer *value = alloc_trash_chunk();
964 enum act_return ret = ACT_RET_CONT;
965
966 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
967 goto leave;
968
969 if (!value) {
970 if (!(s->flags & SF_ERR_MASK))
971 s->flags |= SF_ERR_RESOURCE;
972 goto error;
973 }
974
975 /* get previous and next rules */
976 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
977 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
978
979 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
980 * continue to add link to a previously started response */
981 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
982 struct htx_sl *sl;
983 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
984 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
985
986 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
987 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
988 if (!sl)
989 goto error;
990 sl->info.res.status = 103;
991 }
992
993 /* Add the HTTP Early Hint HTTP 103 response heade */
994 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
995 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
996 goto error;
997
998 /* if it is the last rule or the next one is not an early-hint, terminate the current
999 * response. */
1000 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
1001 size_t data;
1002
1003 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1004 /* If an error occurred during an Early-hint rule,
1005 * remove the incomplete HTTP 103 response from the
1006 * buffer */
1007 goto error;
1008 }
1009
1010 data = htx->data - co_data(res);
1011 c_adv(res, data);
1012 res->total += data;
1013 }
1014
1015 leave:
1016 free_trash_chunk(value);
1017 return ret;
1018
1019 error:
1020 /* If an error occurred during an Early-hint rule, remove the incomplete
1021 * HTTP 103 response from the buffer */
1022 channel_htx_truncate(res, htx);
1023 ret = ACT_RET_ERR;
1024 goto leave;
1025}
1026
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001027/* This function executes a set-header or add-header actions. It builds a string
1028 * in the trash from the specified format string. It finds the action to be
1029 * performed in <.action>, previously filled by function parse_set_header(). The
1030 * replacement action is excuted by the function http_action_set_header(). On
1031 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1032 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1033 * ACT_RET_ERR is returned.
1034 */
1035static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1036 struct session *sess, struct stream *s, int flags)
1037{
1038 struct htx *htx = htxbuf((rule->from == ACT_F_HTTP_REQ) ? &s->req.buf : &s->res.buf);
1039 enum act_return ret = ACT_RET_CONT;
1040 struct buffer *replace;
1041 struct http_hdr_ctx ctx;
1042 struct ist n, v;
1043
1044 replace = alloc_trash_chunk();
1045 if (!replace)
1046 goto fail_alloc;
1047
1048 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1049 n = rule->arg.http.str;
1050 v = ist2(replace->area, replace->data);
1051
1052 if (rule->action == 0) { // set-header
1053 /* remove all occurrences of the header */
1054 ctx.blk = NULL;
1055 while (http_find_header(htx, n, &ctx, 1))
1056 http_remove_header(htx, &ctx);
1057 }
1058
1059 /* Now add header */
1060 if (!http_add_header(htx, n, v))
1061 goto fail_rewrite;
1062
1063 leave:
1064 free_trash_chunk(replace);
1065 return ret;
1066
1067 fail_alloc:
1068 if (!(s->flags & SF_ERR_MASK))
1069 s->flags |= SF_ERR_RESOURCE;
1070 ret = ACT_RET_ERR;
1071 goto leave;
1072
1073 fail_rewrite:
1074 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1075 if (s->flags & SF_BE_ASSIGNED)
1076 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1077 if (sess->listener->counters)
1078 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1079 if (objt_server(s->target))
1080 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1081
1082 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
1083 ret = ACT_RET_ERR;
1084 goto leave;
1085}
1086
Christopher Faulet81e20172019-12-12 16:40:30 +01001087/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1088 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1089 * on success, ACT_RET_PRS_ERR on error.
1090 *
1091 * Note: same function is used for the request and the response. However
1092 * "early-hint" rules are only supported for request rules.
1093 */
1094static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1095 struct act_rule *rule, char **err)
1096{
Christopher Faulet81e20172019-12-12 16:40:30 +01001097 int cap, cur_arg;
1098
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001099 if (args[*orig_arg-1][0] == 'e') {
1100 rule->action = ACT_CUSTOM;
1101 rule->action_ptr = http_action_early_hint;
1102 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001103 else {
1104 if (args[*orig_arg-1][0] == 's')
1105 rule->action = 0; // set-header
1106 else
1107 rule->action = 1; // add-header
1108 rule->action_ptr = http_action_set_header;
1109 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001110
1111 cur_arg = *orig_arg;
1112 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1113 memprintf(err, "expects exactly 2 arguments");
1114 return ACT_RET_PRS_ERR;
1115 }
1116
Christopher Faulet81e20172019-12-12 16:40:30 +01001117
Christopher Faulet96bff762019-12-17 13:46:18 +01001118 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1119 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1120 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001121
1122 if (rule->from == ACT_F_HTTP_REQ) {
1123 px->conf.args.ctx = ARGC_HRQ;
1124 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1125 }
1126 else{
1127 px->conf.args.ctx = ARGC_HRS;
1128 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1129 }
1130
1131 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +01001132 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err))
Christopher Faulet81e20172019-12-12 16:40:30 +01001133 return ACT_RET_PRS_ERR;
1134
1135 free(px->conf.lfs_file);
1136 px->conf.lfs_file = strdup(px->conf.args.file);
1137 px->conf.lfs_line = px->conf.args.line;
1138
1139 *orig_arg = cur_arg + 1;
1140 return ACT_RET_PRS_OK;
1141}
1142
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001143/* This function executes a replace-header or replace-value actions. It
1144 * builds a string in the trash from the specified format string. It finds
1145 * the action to be performed in <.action>, previously filled by function
1146 * parse_replace_header(). The replacement action is excuted by the function
1147 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1148 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1149 * processing continue. Otherwsize ACT_RET_ERR is returned.
1150 */
1151static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1152 struct session *sess, struct stream *s, int flags)
1153{
1154 struct htx *htx = htxbuf((rule->from == ACT_F_HTTP_REQ) ? &s->req.buf : &s->res.buf);
1155 enum act_return ret = ACT_RET_CONT;
1156 struct buffer *replace;
1157 int r;
1158
1159 replace = alloc_trash_chunk();
1160 if (!replace)
1161 goto fail_alloc;
1162
1163 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1164
1165 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1166 if (r == -1)
1167 goto fail_rewrite;
1168
1169 leave:
1170 free_trash_chunk(replace);
1171 return ret;
1172
1173 fail_alloc:
1174 if (!(s->flags & SF_ERR_MASK))
1175 s->flags |= SF_ERR_RESOURCE;
1176 ret = ACT_RET_ERR;
1177 goto leave;
1178
1179 fail_rewrite:
1180 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1181 if (s->flags & SF_BE_ASSIGNED)
1182 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1183 if (sess->listener->counters)
1184 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1185 if (objt_server(s->target))
1186 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1187
1188 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
1189 ret = ACT_RET_ERR;
1190 goto leave;
1191}
1192
Christopher Faulet81e20172019-12-12 16:40:30 +01001193/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1194 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1195 * success, ACT_RET_PRS_ERR on error.
1196 */
1197static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1198 struct act_rule *rule, char **err)
1199{
1200 int cap, cur_arg;
1201
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001202 if (args[*orig_arg-1][8] == 'h')
1203 rule->action = 0; // replace-header
1204 else
1205 rule->action = 1; // replace-value
1206 rule->action_ptr = http_action_replace_header;
Christopher Faulet81e20172019-12-12 16:40:30 +01001207
1208 cur_arg = *orig_arg;
1209 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1210 memprintf(err, "expects exactly 3 arguments");
1211 return ACT_RET_PRS_ERR;
1212 }
1213
Christopher Faulet96bff762019-12-17 13:46:18 +01001214 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1215 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1216 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001217
1218 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +01001219 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err)))
Christopher Faulet81e20172019-12-12 16:40:30 +01001220 return ACT_RET_PRS_ERR;
1221
1222 if (rule->from == ACT_F_HTTP_REQ) {
1223 px->conf.args.ctx = ARGC_HRQ;
1224 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1225 }
1226 else{
1227 px->conf.args.ctx = ARGC_HRS;
1228 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1229 }
1230
1231 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +01001232 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err))
Christopher Faulet81e20172019-12-12 16:40:30 +01001233 return ACT_RET_PRS_ERR;
1234
1235 free(px->conf.lfs_file);
1236 px->conf.lfs_file = strdup(px->conf.args.file);
1237 px->conf.lfs_line = px->conf.args.line;
1238
1239 *orig_arg = cur_arg + 1;
1240 return ACT_RET_PRS_OK;
1241}
1242
1243/* Parse a "del-header" action. It takes an header name as argument. It returns
1244 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1245 */
1246static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1247 struct act_rule *rule, char **err)
1248{
1249 int cur_arg;
1250
1251 rule->action = ACT_HTTP_DEL_HDR;
1252
1253 cur_arg = *orig_arg;
1254 if (!*args[cur_arg]) {
1255 memprintf(err, "expects exactly 1 arguments");
1256 return ACT_RET_PRS_ERR;
1257 }
1258
Christopher Faulet96bff762019-12-17 13:46:18 +01001259 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1260 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001261
1262 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1263
1264 *orig_arg = cur_arg + 1;
1265 return ACT_RET_PRS_OK;
1266}
1267
1268/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1269 * ACT_RET_PRS_ERR on error.
1270 */
1271static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1272 struct act_rule *rule, char **err)
1273{
1274 struct redirect_rule *redir;
1275 int dir, cur_arg;
1276
1277 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001278 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +01001279
1280 cur_arg = *orig_arg;
1281
1282 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1283 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1284 return ACT_RET_PRS_ERR;
1285
1286 rule->arg.redir = redir;
1287 rule->cond = redir->cond;
1288 redir->cond = NULL;
1289
1290 /* skip all arguments */
1291 while (*args[cur_arg])
1292 cur_arg++;
1293
1294 *orig_arg = cur_arg;
1295 return ACT_RET_PRS_OK;
1296}
1297
Christopher Faulet046cf442019-12-17 15:45:23 +01001298/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1299 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1300 */
1301static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1302 struct session *sess, struct stream *s, int flags)
1303{
1304 struct pat_ref *ref;
1305 struct buffer *key = NULL, *value = NULL;
1306 enum act_return ret = ACT_RET_CONT;
1307
1308 /* collect reference */
1309 ref = pat_ref_lookup(rule->arg.map.ref);
1310 if (!ref)
1311 goto leave;
1312
1313 /* allocate key */
1314 key = alloc_trash_chunk();
1315 if (!key)
1316 goto fail_alloc;
1317
1318 /* collect key */
1319 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1320 key->area[key->data] = '\0';
1321
1322 switch (rule->action) {
1323 case 0: // add-acl
1324 /* add entry only if it does not already exist */
1325 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1326 if (pat_ref_find_elt(ref, key->area) == NULL)
1327 pat_ref_add(ref, key->area, NULL, NULL);
1328 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1329 break;
1330
1331 case 1: // set-map
1332 /* allocate value */
1333 value = alloc_trash_chunk();
1334 if (!value)
1335 goto fail_alloc;
1336
1337 /* collect value */
1338 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1339 value->area[value->data] = '\0';
1340
1341 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1342 if (pat_ref_find_elt(ref, key->area) != NULL) {
1343 /* update entry if it exists */
1344 pat_ref_set(ref, key->area, value->area, NULL);
1345 }
1346 else {
1347 /* insert a new entry */
1348 pat_ref_add(ref, key->area, value->area, NULL);
1349 }
1350 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1351 break;
1352
1353 case 2: // del-acl
1354 case 3: // del-map
1355 /* returned code: 1=ok, 0=ko */
1356 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1357 pat_ref_delete(ref, key->area);
1358 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1359 break;
1360
1361 default:
1362 ret = ACT_RET_ERR;
1363 }
1364
1365
1366 leave:
1367 free_trash_chunk(key);
1368 free_trash_chunk(value);
1369 return ret;
1370
1371 fail_alloc:
1372 if (!(s->flags & SF_ERR_MASK))
1373 s->flags |= SF_ERR_RESOURCE;
1374 ret = ACT_RET_ERR;
1375 goto leave;
1376}
1377
Christopher Faulet81e20172019-12-12 16:40:30 +01001378/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001379 * two log-format string as argument depending on the action. The action is
1380 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1381 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001382 */
1383static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1384 struct act_rule *rule, char **err)
1385{
1386 int cap, cur_arg;
1387
Christopher Faulet046cf442019-12-17 15:45:23 +01001388 if (args[*orig_arg-1][0] == 'a') // add-acl
1389 rule->action = 0;
1390 else if (args[*orig_arg-1][0] == 's') // set-map
1391 rule->action = 1;
1392 else if (args[*orig_arg-1][4] == 'a') // del-acl
1393 rule->action = 2;
1394 else if (args[*orig_arg-1][4] == 'm') // del-map
1395 rule->action = 3;
1396 else {
1397 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1398 return ACT_RET_PRS_ERR;
1399 }
1400 rule->action_ptr = http_action_set_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001401
1402 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001403 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1404 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001405 memprintf(err, "expects exactly 2 arguments");
1406 return ACT_RET_PRS_ERR;
1407 }
1408 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001409 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001410 memprintf(err, "expects exactly 1 arguments");
1411 return ACT_RET_PRS_ERR;
1412 }
1413
1414 /*
1415 * '+ 8' for 'set-map(' (same for del-map)
1416 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1417 */
1418 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1419
1420 if (rule->from == ACT_F_HTTP_REQ) {
1421 px->conf.args.ctx = ARGC_HRQ;
1422 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1423 }
1424 else{
1425 px->conf.args.ctx = ARGC_HRS;
1426 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1427 }
1428
1429 /* key pattern */
1430 LIST_INIT(&rule->arg.map.key);
1431 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err))
1432 return ACT_RET_PRS_ERR;
1433
Christopher Faulet046cf442019-12-17 15:45:23 +01001434 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001435 /* value pattern for set-map only */
1436 cur_arg++;
1437 LIST_INIT(&rule->arg.map.value);
1438 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err))
1439 return ACT_RET_PRS_ERR;
1440 }
1441
1442 free(px->conf.lfs_file);
1443 px->conf.lfs_file = strdup(px->conf.args.file);
1444 px->conf.lfs_line = px->conf.args.line;
1445
1446 *orig_arg = cur_arg + 1;
1447 return ACT_RET_PRS_OK;
1448}
1449
Christopher Fauletac98d812019-12-18 09:20:16 +01001450/* This function executes a track-sc* actions. On success, it returns
1451 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1452 */
1453static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1454 struct session *sess, struct stream *s, int flags)
1455{
1456 struct stktable *t;
1457 struct stksess *ts;
1458 struct stktable_key *key;
1459 void *ptr1, *ptr2, *ptr3, *ptr4;
1460 int opt;
1461
1462 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1463 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1464
1465 t = rule->arg.trk_ctr.table.t;
1466 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1467
1468 if (!key)
1469 goto end;
1470 ts = stktable_get_entry(t, key);
1471 if (!ts)
1472 goto end;
1473
1474 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1475
1476 /* let's count a new HTTP request as it's the first time we do it */
1477 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1478 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1479
1480 /* When the client triggers a 4xx from the server, it's most often due
1481 * to a missing object or permission. These events should be tracked
1482 * because if they happen often, it may indicate a brute force or a
1483 * vulnerability scan. Normally this is done when receiving the response
1484 * but here we're tracking after this ought to have been done so we have
1485 * to do it on purpose.
1486 */
1487 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1488 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1489 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1490 }
1491
1492 if (ptr1 || ptr2 || ptr3 || ptr4) {
1493 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1494
1495 if (ptr1)
1496 stktable_data_cast(ptr1, http_req_cnt)++;
1497 if (ptr2)
1498 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1499 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1500 if (ptr3)
1501 stktable_data_cast(ptr3, http_err_cnt)++;
1502 if (ptr4)
1503 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1504 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1505
1506 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1507
1508 /* If data was modified, we need to touch to re-schedule sync */
1509 stktable_touch_local(t, ts, 0);
1510 }
1511
1512 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1513 if (sess->fe != s->be)
1514 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1515
1516 end:
1517 return ACT_RET_CONT;
1518}
Christopher Faulet81e20172019-12-12 16:40:30 +01001519
1520/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1521 * ACT_RET_PRS_ERR on error.
1522 */
1523static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1524 struct act_rule *rule, char **err)
1525{
1526 struct sample_expr *expr;
1527 unsigned int where;
1528 unsigned int tsc_num;
1529 const char *tsc_num_str;
1530 int cur_arg;
1531
1532 tsc_num_str = &args[*orig_arg-1][8];
1533 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1534 return ACT_RET_PRS_ERR;
1535
1536 cur_arg = *orig_arg;
1537 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
1538 err, &px->conf.args);
1539 if (!expr)
1540 return ACT_RET_PRS_ERR;
1541
1542 where = 0;
1543 if (px->cap & PR_CAP_FE)
1544 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1545 if (px->cap & PR_CAP_BE)
1546 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1547
1548 if (!(expr->fetch->val & where)) {
1549 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1550 args[cur_arg-1], sample_src_names(expr->fetch->use));
1551 return ACT_RET_PRS_ERR;
1552 }
1553
1554 if (strcmp(args[cur_arg], "table") == 0) {
1555 cur_arg++;
1556 if (!*args[cur_arg]) {
1557 memprintf(err, "missing table name");
1558 return ACT_RET_PRS_ERR;
1559 }
1560
1561 /* we copy the table name for now, it will be resolved later */
1562 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1563 cur_arg++;
1564 }
1565
Christopher Fauletac98d812019-12-18 09:20:16 +01001566 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001567 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001568 rule->action_ptr = http_action_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001569 rule->check_ptr = check_trk_action;
1570
1571 *orig_arg = cur_arg;
1572 return ACT_RET_PRS_OK;
1573}
1574
Christopher Faulet46f95542019-12-20 10:07:22 +01001575/* This function executes a strict-mode actions. On success, it always returns
1576 * ACT_RET_CONT
1577 */
1578static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1579 struct session *sess, struct stream *s, int flags)
1580{
1581 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1582
1583 if (rule->action == 0) // strict-mode on
1584 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1585 else // strict-mode off
1586 msg->flags |= HTTP_MSGF_SOFT_RW;
1587 return ACT_RET_CONT;
1588}
1589
1590/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1591 * ACT_RET_PRS_ERR on error.
1592 */
1593static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1594 struct act_rule *rule, char **err)
1595{
1596 int cur_arg;
1597
1598
1599 cur_arg = *orig_arg;
1600 if (!*args[cur_arg]) {
1601 memprintf(err, "expects exactly 1 arguments");
1602 return ACT_RET_PRS_ERR;
1603 }
1604
1605 if (strcasecmp(args[cur_arg], "on") == 0)
1606 rule->action = 0; // strict-mode on
1607 else if (strcasecmp(args[cur_arg], "off") == 0)
1608 rule->action = 1; // strict-mode off
1609 else {
1610 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1611 return ACT_RET_PRS_ERR;
1612 }
1613 rule->action_ptr = http_action_strict_mode;
1614
1615 *orig_arg = cur_arg + 1;
1616 return ACT_RET_PRS_OK;
1617}
1618
Willy Tarreau79e57332018-10-02 16:01:16 +02001619/************************************************************************/
1620/* All supported http-request action keywords must be declared here. */
1621/************************************************************************/
1622
1623static struct action_kw_list http_req_actions = {
1624 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001625 { "add-acl", parse_http_set_map, 1 },
1626 { "add-header", parse_http_set_header, 0 },
1627 { "allow", parse_http_allow, 0 },
1628 { "auth", parse_http_auth, 0 },
1629 { "capture", parse_http_req_capture, 0 },
1630 { "del-acl", parse_http_set_map, 1 },
1631 { "del-header", parse_http_del_header, 0 },
1632 { "del-map", parse_http_set_map, 1 },
1633 { "deny", parse_http_req_deny, 0 },
1634 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1635 { "early-hint", parse_http_set_header, 0 },
1636 { "redirect", parse_http_redirect, 0 },
1637 { "reject", parse_http_action_reject, 0 },
1638 { "replace-header", parse_http_replace_header, 0 },
1639 { "replace-path", parse_replace_uri, 0 },
1640 { "replace-uri", parse_replace_uri, 0 },
1641 { "replace-value", parse_http_replace_header, 0 },
1642 { "set-header", parse_http_set_header, 0 },
1643 { "set-log-level", parse_http_set_log_level, 0 },
1644 { "set-map", parse_http_set_map, 1 },
1645 { "set-method", parse_set_req_line, 0 },
1646 { "set-mark", parse_http_set_mark, 0 },
1647 { "set-nice", parse_http_set_nice, 0 },
1648 { "set-path", parse_set_req_line, 0 },
1649 { "set-query", parse_set_req_line, 0 },
1650 { "set-tos", parse_http_set_tos, 0 },
1651 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001652 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001653 { "tarpit", parse_http_req_deny, 0 },
1654 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001655 { NULL, NULL }
1656 }
1657};
1658
Willy Tarreau0108d902018-11-25 19:14:37 +01001659INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1660
Willy Tarreau79e57332018-10-02 16:01:16 +02001661static struct action_kw_list http_res_actions = {
1662 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001663 { "add-acl", parse_http_set_map, 1 },
1664 { "add-header", parse_http_set_header, 0 },
1665 { "allow", parse_http_allow, 0 },
1666 { "capture", parse_http_res_capture, 0 },
1667 { "del-acl", parse_http_set_map, 1 },
1668 { "del-header", parse_http_del_header, 0 },
1669 { "del-map", parse_http_set_map, 1 },
1670 { "deny", parse_http_res_deny, 0 },
1671 { "redirect", parse_http_redirect, 0 },
1672 { "replace-header", parse_http_replace_header, 0 },
1673 { "replace-value", parse_http_replace_header, 0 },
1674 { "set-header", parse_http_set_header, 0 },
1675 { "set-log-level", parse_http_set_log_level, 0 },
1676 { "set-map", parse_http_set_map, 1 },
1677 { "set-mark", parse_http_set_mark, 0 },
1678 { "set-nice", parse_http_set_nice, 0 },
1679 { "set-status", parse_http_set_status, 0 },
1680 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001681 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001682 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001683 { NULL, NULL }
1684 }
1685};
1686
Willy Tarreau0108d902018-11-25 19:14:37 +01001687INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001688
1689/*
1690 * Local variables:
1691 * c-indent-level: 8
1692 * c-basic-offset: 8
1693 * End:
1694 */