blob: b18da6164ef0eb59ffb3cee46a39415ed38ab5b7 [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>
Christopher Fauletb3048832020-05-27 15:26:43 +020028#include <common/uri_auth.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020029#include <common/version.h>
30
31#include <types/capture.h>
32#include <types/global.h>
33
34#include <proto/acl.h>
35#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010036#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020037#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020038#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020039#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020040#include <proto/http_ana.h>
Christopher Faulet046cf442019-12-17 15:45:23 +010041#include <proto/pattern.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010042#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020043
Christopher Faulet2eb53962020-01-14 14:47:34 +010044/* Release memory allocated by most of HTTP actions. Concretly, it releases
45 * <arg.http>.
46 */
47static void release_http_action(struct act_rule *rule)
48{
49 struct logformat_node *lf, *lfb;
50
Tim Duesterhused526372020-03-05 17:56:33 +010051 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010052 if (rule->arg.http.re)
53 regex_free(rule->arg.http.re);
54 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
55 LIST_DEL(&lf->list);
56 release_sample_expr(lf->expr);
57 free(lf->arg);
58 free(lf);
59 }
60}
61
Christopher Faulet5cb513a2020-05-13 17:56:56 +020062/* Release memory allocated by HTTP actions relying on an http reply. Concretly,
63 * it releases <.arg.http_reply>
64 */
65static void release_act_http_reply(struct act_rule *rule)
66{
67 release_http_reply(rule->arg.http_reply);
68 rule->arg.http_reply = NULL;
69}
70
71
72/* Check function for HTTP actions relying on an http reply. The function
73 * returns 1 in success case, otherwise, it returns 0 and err is filled.
74 */
75static int check_act_http_reply(struct act_rule *rule, struct proxy *px, char **err)
76{
77 struct http_reply *reply = rule->arg.http_reply;
78
79 if (!http_check_http_reply(reply, px, err)) {
80 release_act_http_reply(rule);
81 return 0;
82 }
83 return 1;
84}
85
Willy Tarreau79e57332018-10-02 16:01:16 +020086
87/* This function executes one of the set-{method,path,query,uri} actions. It
88 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010089 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050090 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010091 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
92 * occurs while soft rewrites are enabled, the action is canceled, but the rule
93 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020094 */
95static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
96 struct session *sess, struct stream *s, int flags)
97{
98 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010099 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200100
101 replace = alloc_trash_chunk();
102 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100103 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +0200104
105 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +0100106 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200107 replace->area[replace->data++] = '?';
108 replace->data += build_logline(s, replace->area + replace->data,
109 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +0100110 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200111
Christopher Faulet2c22a692019-12-18 15:39:56 +0100112 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100113 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +0200114
Christopher Faulete00d06c2019-12-16 17:18:42 +0100115 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +0200116 free_trash_chunk(replace);
117 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100118
119 fail_alloc:
120 if (!(s->flags & SF_ERR_MASK))
121 s->flags |= SF_ERR_RESOURCE;
122 ret = ACT_RET_ERR;
123 goto leave;
124
125 fail_rewrite:
126 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
127 if (s->flags & SF_BE_ASSIGNED)
128 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
129 if (sess->listener->counters)
130 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
131 if (objt_server(s->target))
132 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
133
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100134 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100135 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100136 if (!(s->flags & SF_ERR_MASK))
137 s->flags |= SF_ERR_PRXCOND;
138 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100139 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200140}
141
142/* parse an http-request action among :
143 * set-method
144 * set-path
145 * 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 Faulet2c22a692019-12-18 15:39:56 +0100163 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200164 break;
165 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100166 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200167 break;
168 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100169 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200170 break;
171 default:
172 memprintf(err, "internal error: unhandled action '%s'", args[0]);
173 return ACT_RET_PRS_ERR;
174 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100175 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100176 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200177
178 if (!*args[cur_arg] ||
179 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
180 memprintf(err, "expects exactly 1 argument <format>");
181 return ACT_RET_PRS_ERR;
182 }
183
Christopher Faulet96bff762019-12-17 13:46:18 +0100184 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200185 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100186 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200187 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
188 return ACT_RET_PRS_ERR;
189 }
190
191 (*orig_arg)++;
192 return ACT_RET_PRS_OK;
193}
194
Willy Tarreau33810222019-06-12 17:44:02 +0200195/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100196 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200197 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100198 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100199 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100200 * contains 1 for the path or 3 for the URI (values used by
201 * http_req_replace_stline()). 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.
Willy Tarreau33810222019-06-12 17:44:02 +0200204 */
205static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
206 struct session *sess, struct stream *s, int flags)
207{
Christopher Faulet13403762019-12-13 09:01:57 +0100208 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200209 struct buffer *replace, *output;
210 struct ist uri;
211 int len;
212
213 replace = alloc_trash_chunk();
214 output = alloc_trash_chunk();
215 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100216 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200217 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100218
Christopher Faulet2c22a692019-12-18 15:39:56 +0100219 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100220 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100221
Christopher Faulet96bff762019-12-17 13:46:18 +0100222 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200223 goto leave;
224
Christopher Faulet96bff762019-12-17 13:46:18 +0100225 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200226
227 /* note: uri.ptr doesn't need to be zero-terminated because it will
228 * only be used to pick pmatch references.
229 */
230 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
231 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100232 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200233
Christopher Faulet2c22a692019-12-18 15:39:56 +0100234 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100235 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200236
Christopher Faulete00d06c2019-12-16 17:18:42 +0100237 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200238 free_trash_chunk(output);
239 free_trash_chunk(replace);
240 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100241
242 fail_alloc:
243 if (!(s->flags & SF_ERR_MASK))
244 s->flags |= SF_ERR_RESOURCE;
245 ret = ACT_RET_ERR;
246 goto leave;
247
248 fail_rewrite:
249 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
250 if (s->flags & SF_BE_ASSIGNED)
251 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
252 if (sess->listener->counters)
253 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
254 if (objt_server(s->target))
255 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
256
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100257 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100258 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100259 if (!(s->flags & SF_ERR_MASK))
260 s->flags |= SF_ERR_PRXCOND;
261 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100262 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200263}
264
Willy Tarreau262c3f12019-12-17 06:52:51 +0100265/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200266 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100267 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100268 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200269 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
270 */
271static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
272 struct act_rule *rule, char **err)
273{
274 int cur_arg = *orig_arg;
275 char *error = NULL;
276
Willy Tarreau262c3f12019-12-17 06:52:51 +0100277 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100278 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100279 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100280 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100281
Willy Tarreau33810222019-06-12 17:44:02 +0200282 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100283 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200284
285 if (!*args[cur_arg] || !*args[cur_arg+1] ||
286 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
287 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
288 return ACT_RET_PRS_ERR;
289 }
290
Christopher Faulet96bff762019-12-17 13:46:18 +0100291 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200292 memprintf(err, "failed to parse the regex : %s", error);
293 free(error);
294 return ACT_RET_PRS_ERR;
295 }
296
Christopher Faulet96bff762019-12-17 13:46:18 +0100297 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200298 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100299 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200300 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100301 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200302 return ACT_RET_PRS_ERR;
303 }
304
305 (*orig_arg) += 2;
306 return ACT_RET_PRS_OK;
307}
308
Willy Tarreau79e57332018-10-02 16:01:16 +0200309/* This function is just a compliant action wrapper for "set-status". */
310static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
311 struct session *sess, struct stream *s, int flags)
312{
Christopher Faulet96bff762019-12-17 13:46:18 +0100313 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100314 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
315 if (s->flags & SF_BE_ASSIGNED)
316 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
317 if (sess->listener->counters)
318 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
319 if (objt_server(s->target))
320 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
321
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100322 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100323 if (!(s->flags & SF_ERR_MASK))
324 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100325 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100326 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100327 }
328
Willy Tarreau79e57332018-10-02 16:01:16 +0200329 return ACT_RET_CONT;
330}
331
332/* parse set-status action:
333 * This action accepts a single argument of type int representing
334 * an http status code. It returns ACT_RET_PRS_OK on success,
335 * ACT_RET_PRS_ERR on error.
336 */
337static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
338 struct act_rule *rule, char **err)
339{
340 char *error;
341
342 rule->action = ACT_CUSTOM;
343 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100344 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200345
346 /* Check if an argument is available */
347 if (!*args[*orig_arg]) {
348 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
349 return ACT_RET_PRS_ERR;
350 }
351
352 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100353 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
354 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200355 memprintf(err, "expects an integer status code between 100 and 999");
356 return ACT_RET_PRS_ERR;
357 }
358
359 (*orig_arg)++;
360
361 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100362 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200363 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
364 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
365 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100366 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
367 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200368 (*orig_arg)++;
369 }
370
Christopher Fauletc20b3712020-01-27 15:51:56 +0100371 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200372 return ACT_RET_PRS_OK;
373}
374
375/* This function executes the "reject" HTTP action. It clears the request and
376 * response buffer without sending any response. It can be useful as an HTTP
377 * alternative to the silent-drop action to defend against DoS attacks, and may
378 * also be used with HTTP/2 to close a connection instead of just a stream.
379 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100380 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200381 */
382static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
383 struct session *sess, struct stream *s, int flags)
384{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100385 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200386 channel_abort(&s->req);
387 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100388 s->req.analysers &= AN_REQ_FLT_END;
389 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200390
Olivier Houcharda798bf52019-03-08 18:52:00 +0100391 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
392 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200393 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100394 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200395
396 if (!(s->flags & SF_ERR_MASK))
397 s->flags |= SF_ERR_PRXCOND;
398 if (!(s->flags & SF_FINST_MASK))
399 s->flags |= SF_FINST_R;
400
Christopher Faulet90d22a82020-03-06 11:18:39 +0100401 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200402}
403
404/* parse the "reject" action:
405 * This action takes no argument and returns ACT_RET_PRS_OK on success,
406 * ACT_RET_PRS_ERR on error.
407 */
408static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
409 struct act_rule *rule, char **err)
410{
411 rule->action = ACT_CUSTOM;
412 rule->action_ptr = http_action_reject;
413 return ACT_RET_PRS_OK;
414}
415
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200416/* This function executes the "disable-l7-retry" HTTP action.
417 * It disables L7 retries (all retry except for a connection failure). This
418 * can be useful for example to avoid retrying on POST requests.
419 * It just removes the L7 retry flag on the stream_interface, and always
420 * return ACT_RET_CONT;
421 */
422static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
423 struct session *sess, struct stream *s, int flags)
424{
425 struct stream_interface *si = &s->si[1];
426
427 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
428 * let's be future-proof and remove it anyway.
429 */
430 si->flags &= ~SI_FL_L7_RETRY;
431 si->flags |= SI_FL_D_L7_RETRY;
432 return ACT_RET_CONT;
433}
434
435/* parse the "disable-l7-retry" action:
436 * This action takes no argument and returns ACT_RET_PRS_OK on success,
437 * ACT_RET_PRS_ERR on error.
438 */
439static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
440 int *orig_args, struct proxy *px,
441 struct act_rule *rule, char **err)
442{
443 rule->action = ACT_CUSTOM;
444 rule->action_ptr = http_req_disable_l7_retry;
445 return ACT_RET_PRS_OK;
446}
447
Willy Tarreau79e57332018-10-02 16:01:16 +0200448/* This function executes the "capture" action. It executes a fetch expression,
449 * turns the result into a string and puts it in a capture slot. It always
450 * returns 1. If an error occurs the action is cancelled, but the rule
451 * processing continues.
452 */
453static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
454 struct session *sess, struct stream *s, int flags)
455{
456 struct sample *key;
457 struct cap_hdr *h = rule->arg.cap.hdr;
458 char **cap = s->req_cap;
459 int len;
460
461 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
462 if (!key)
463 return ACT_RET_CONT;
464
465 if (cap[h->index] == NULL)
466 cap[h->index] = pool_alloc(h->pool);
467
468 if (cap[h->index] == NULL) /* no more capture memory */
469 return ACT_RET_CONT;
470
471 len = key->data.u.str.data;
472 if (len > h->len)
473 len = h->len;
474
475 memcpy(cap[h->index], key->data.u.str.area, len);
476 cap[h->index][len] = 0;
477 return ACT_RET_CONT;
478}
479
480/* This function executes the "capture" action and store the result in a
481 * capture slot if exists. It executes a fetch expression, turns the result
482 * into a string and puts it in a capture slot. It always returns 1. If an
483 * error occurs the action is cancelled, but the rule processing continues.
484 */
485static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
486 struct session *sess, struct stream *s, int flags)
487{
488 struct sample *key;
489 struct cap_hdr *h;
490 char **cap = s->req_cap;
491 struct proxy *fe = strm_fe(s);
492 int len;
493 int i;
494
495 /* Look for the original configuration. */
496 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
497 h != NULL && i != rule->arg.capid.idx ;
498 i--, h = h->next);
499 if (!h)
500 return ACT_RET_CONT;
501
502 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
503 if (!key)
504 return ACT_RET_CONT;
505
506 if (cap[h->index] == NULL)
507 cap[h->index] = pool_alloc(h->pool);
508
509 if (cap[h->index] == NULL) /* no more capture memory */
510 return ACT_RET_CONT;
511
512 len = key->data.u.str.data;
513 if (len > h->len)
514 len = h->len;
515
516 memcpy(cap[h->index], key->data.u.str.area, len);
517 cap[h->index][len] = 0;
518 return ACT_RET_CONT;
519}
520
521/* Check an "http-request capture" action.
522 *
523 * The function returns 1 in success case, otherwise, it returns 0 and err is
524 * filled.
525 */
526static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
527{
528 if (rule->action_ptr != http_action_req_capture_by_id)
529 return 1;
530
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100531 /* capture slots can only be declared in frontends, so we can't check their
532 * existence in backends at configuration parsing step
533 */
534 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200535 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
536 rule->arg.capid.idx);
537 return 0;
538 }
539
540 return 1;
541}
542
Christopher Faulet2eb53962020-01-14 14:47:34 +0100543/* Release memory allocate by an http capture action */
544static void release_http_capture(struct act_rule *rule)
545{
546 if (rule->action_ptr == http_action_req_capture)
547 release_sample_expr(rule->arg.cap.expr);
548 else
549 release_sample_expr(rule->arg.capid.expr);
550}
551
Willy Tarreau79e57332018-10-02 16:01:16 +0200552/* parse an "http-request capture" action. It takes a single argument which is
553 * a sample fetch expression. It stores the expression into arg->act.p[0] and
554 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
555 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
556 */
557static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
558 struct act_rule *rule, char **err)
559{
560 struct sample_expr *expr;
561 struct cap_hdr *hdr;
562 int cur_arg;
563 int len = 0;
564
565 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
566 if (strcmp(args[cur_arg], "if") == 0 ||
567 strcmp(args[cur_arg], "unless") == 0)
568 break;
569
570 if (cur_arg < *orig_arg + 3) {
571 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
572 return ACT_RET_PRS_ERR;
573 }
574
575 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100576 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 +0200577 if (!expr)
578 return ACT_RET_PRS_ERR;
579
580 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
581 memprintf(err,
582 "fetch method '%s' extracts information from '%s', none of which is available here",
583 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100584 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200585 return ACT_RET_PRS_ERR;
586 }
587
588 if (!args[cur_arg] || !*args[cur_arg]) {
589 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100590 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200591 return ACT_RET_PRS_ERR;
592 }
593
594 if (strcmp(args[cur_arg], "len") == 0) {
595 cur_arg++;
596
597 if (!(px->cap & PR_CAP_FE)) {
598 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100599 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200600 return ACT_RET_PRS_ERR;
601 }
602
603 px->conf.args.ctx = ARGC_CAP;
604
605 if (!args[cur_arg]) {
606 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100607 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200608 return ACT_RET_PRS_ERR;
609 }
610 /* we copy the table name for now, it will be resolved later */
611 len = atoi(args[cur_arg]);
612 if (len <= 0) {
613 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100614 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200615 return ACT_RET_PRS_ERR;
616 }
617 cur_arg++;
618
Willy Tarreau79e57332018-10-02 16:01:16 +0200619 hdr = calloc(1, sizeof(*hdr));
620 hdr->next = px->req_cap;
621 hdr->name = NULL; /* not a header capture */
622 hdr->namelen = 0;
623 hdr->len = len;
624 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
625 hdr->index = px->nb_req_cap++;
626
627 px->req_cap = hdr;
628 px->to_log |= LW_REQHDR;
629
630 rule->action = ACT_CUSTOM;
631 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100632 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200633 rule->arg.cap.expr = expr;
634 rule->arg.cap.hdr = hdr;
635 }
636
637 else if (strcmp(args[cur_arg], "id") == 0) {
638 int id;
639 char *error;
640
641 cur_arg++;
642
643 if (!args[cur_arg]) {
644 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100645 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200646 return ACT_RET_PRS_ERR;
647 }
648
649 id = strtol(args[cur_arg], &error, 10);
650 if (*error != '\0') {
651 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100652 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200653 return ACT_RET_PRS_ERR;
654 }
655 cur_arg++;
656
657 px->conf.args.ctx = ARGC_CAP;
658
659 rule->action = ACT_CUSTOM;
660 rule->action_ptr = http_action_req_capture_by_id;
661 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100662 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200663 rule->arg.capid.expr = expr;
664 rule->arg.capid.idx = id;
665 }
666
667 else {
668 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100669 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200670 return ACT_RET_PRS_ERR;
671 }
672
673 *orig_arg = cur_arg;
674 return ACT_RET_PRS_OK;
675}
676
677/* This function executes the "capture" action and store the result in a
678 * capture slot if exists. It executes a fetch expression, turns the result
679 * into a string and puts it in a capture slot. It always returns 1. If an
680 * error occurs the action is cancelled, but the rule processing continues.
681 */
682static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
683 struct session *sess, struct stream *s, int flags)
684{
685 struct sample *key;
686 struct cap_hdr *h;
687 char **cap = s->res_cap;
688 struct proxy *fe = strm_fe(s);
689 int len;
690 int i;
691
692 /* Look for the original configuration. */
693 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
694 h != NULL && i != rule->arg.capid.idx ;
695 i--, h = h->next);
696 if (!h)
697 return ACT_RET_CONT;
698
699 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
700 if (!key)
701 return ACT_RET_CONT;
702
703 if (cap[h->index] == NULL)
704 cap[h->index] = pool_alloc(h->pool);
705
706 if (cap[h->index] == NULL) /* no more capture memory */
707 return ACT_RET_CONT;
708
709 len = key->data.u.str.data;
710 if (len > h->len)
711 len = h->len;
712
713 memcpy(cap[h->index], key->data.u.str.area, len);
714 cap[h->index][len] = 0;
715 return ACT_RET_CONT;
716}
717
718/* Check an "http-response capture" action.
719 *
720 * The function returns 1 in success case, otherwise, it returns 0 and err is
721 * filled.
722 */
723static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
724{
725 if (rule->action_ptr != http_action_res_capture_by_id)
726 return 1;
727
728 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
729 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
730 rule->arg.capid.idx);
731 return 0;
732 }
733
734 return 1;
735}
736
737/* parse an "http-response capture" action. It takes a single argument which is
738 * a sample fetch expression. It stores the expression into arg->act.p[0] and
739 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
740 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
741 */
742static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
743 struct act_rule *rule, char **err)
744{
745 struct sample_expr *expr;
746 int cur_arg;
747 int id;
748 char *error;
749
750 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
751 if (strcmp(args[cur_arg], "if") == 0 ||
752 strcmp(args[cur_arg], "unless") == 0)
753 break;
754
755 if (cur_arg < *orig_arg + 3) {
756 memprintf(err, "expects <expression> id <idx>");
757 return ACT_RET_PRS_ERR;
758 }
759
760 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100761 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 +0200762 if (!expr)
763 return ACT_RET_PRS_ERR;
764
765 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
766 memprintf(err,
767 "fetch method '%s' extracts information from '%s', none of which is available here",
768 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100769 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200770 return ACT_RET_PRS_ERR;
771 }
772
773 if (!args[cur_arg] || !*args[cur_arg]) {
774 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100775 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200776 return ACT_RET_PRS_ERR;
777 }
778
779 if (strcmp(args[cur_arg], "id") != 0) {
780 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100781 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200782 return ACT_RET_PRS_ERR;
783 }
784
785 cur_arg++;
786
787 if (!args[cur_arg]) {
788 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100789 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200790 return ACT_RET_PRS_ERR;
791 }
792
793 id = strtol(args[cur_arg], &error, 10);
794 if (*error != '\0') {
795 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100796 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200797 return ACT_RET_PRS_ERR;
798 }
799 cur_arg++;
800
801 px->conf.args.ctx = ARGC_CAP;
802
803 rule->action = ACT_CUSTOM;
804 rule->action_ptr = http_action_res_capture_by_id;
805 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100806 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200807 rule->arg.capid.expr = expr;
808 rule->arg.capid.idx = id;
809
810 *orig_arg = cur_arg;
811 return ACT_RET_PRS_OK;
812}
813
Christopher Faulet81e20172019-12-12 16:40:30 +0100814/* Parse a "allow" action for a request or a response rule. It takes no argument. It
815 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
816 */
817static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
818 struct act_rule *rule, char **err)
819{
820 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100821 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100822 return ACT_RET_PRS_OK;
823}
824
Christopher Faulete0fca292020-01-13 21:49:03 +0100825/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200826 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
827 * error. It relies on http_parse_http_reply() to set
828 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100829 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100830static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
831 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100832{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200833 int default_status;
834 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100835
836 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100837 if (rule->from == ACT_F_HTTP_REQ) {
838 if (!strcmp(args[cur_arg-1], "tarpit")) {
839 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200840 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100841 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100842 else {
843 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200844 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100845 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100846 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100847 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100848 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200849 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100850 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100851
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200852 /* If no args or only a deny_status specified, fallback on the legacy
853 * mode and use default error files despite the fact that
854 * default-errorfiles is not used. Otherwise, parse an http reply.
855 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100856
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200857 /* Prepare parsing of log-format strings */
858 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100859
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200860 if (!*(args[cur_arg])) {
861 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
862 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100863 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200864
865 if (strcmp(args[cur_arg], "deny_status") == 0) {
866 if (!*(args[cur_arg+2]) ||
867 (strcmp(args[cur_arg+2], "errorfile") != 0 && strcmp(args[cur_arg+2], "errorfiles") != 0)) {
868 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
869 &arg, px, default_status, err);
870 *orig_arg += 2;
871 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100872 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200873 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100874 }
875
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200876 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100877
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200878 end:
879 if (!rule->arg.http_reply)
880 return ACT_RET_PRS_ERR;
881
882 rule->flags |= ACT_FLAG_FINAL;
883 rule->check_ptr = check_act_http_reply;
884 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +0100885 return ACT_RET_PRS_OK;
886}
887
Christopher Fauletb3048832020-05-27 15:26:43 +0200888
889/* This function executes a auth action. It builds an 401/407 HTX message using
890 * the corresponding proxy's error message. On success, it returns
891 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
892 */
893static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
894 struct session *sess, struct stream *s, int flags)
895{
896 struct channel *req = &s->req;
897 struct channel *res = &s->res;
898 struct htx *htx = htx_from_buf(&res->buf);
899 struct http_reply *reply;
900 const char *auth_realm;
901 struct http_hdr_ctx ctx;
902 struct ist hdr;
903
904 /* Auth might be performed on regular http-req rules as well as on stats */
905 auth_realm = rule->arg.http.str.ptr;
906 if (!auth_realm) {
907 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
908 auth_realm = STATS_DEFAULT_REALM;
909 else
910 auth_realm = px->id;
911 }
912
913 if (!(s->txn->flags & TX_USE_PX_CONN)) {
914 s->txn->status = 401;
915 hdr = ist("WWW-Authenticate");
916 }
917 else {
918 s->txn->status = 407;
919 hdr = ist("Proxy-Authenticate");
920 }
921 reply = http_error_message(s);
922 channel_htx_truncate(res, htx);
923
924 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
925 goto fail;
926
927 /* Write the generic 40x message */
928 if (http_reply_to_htx(s, htx, reply) == -1)
929 goto fail;
930
931 /* Remove all existing occurrences of the XXX-Authenticate header */
932 ctx.blk = NULL;
933 while (http_find_header(htx, hdr, &ctx, 1))
934 http_remove_header(htx, &ctx);
935
936 /* Now a the right XXX-Authenticate header */
937 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
938 goto fail;
939
940 /* Finally forward the reply */
941 htx_to_buf(htx, &res->buf);
942 if (!http_forward_proxy_resp(s, 1))
943 goto fail;
944
945 /* Note: Only eval on the request */
946 s->logs.tv_request = now;
947 req->analysers &= AN_REQ_FLT_END;
948
949 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
950 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
951
952 if (!(s->flags & SF_ERR_MASK))
953 s->flags |= SF_ERR_LOCAL;
954 if (!(s->flags & SF_FINST_MASK))
955 s->flags |= SF_FINST_R;
956
957 stream_inc_http_err_ctr(s);
958 return ACT_RET_ABRT;
959
960 fail:
961 /* If an error occurred, remove the incomplete HTTP response from the
962 * buffer */
963 channel_htx_truncate(res, htx);
964 return ACT_RET_ERR;
965}
966
Christopher Faulet81e20172019-12-12 16:40:30 +0100967/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
968 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
969 */
970static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
971 struct act_rule *rule, char **err)
972{
973 int cur_arg;
974
Christopher Fauletb3048832020-05-27 15:26:43 +0200975 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +0100976 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +0200977 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100978 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100979
980 cur_arg = *orig_arg;
981 if (!strcmp(args[cur_arg], "realm")) {
982 cur_arg++;
983 if (!*args[cur_arg]) {
984 memprintf(err, "missing realm value.\n");
985 return ACT_RET_PRS_ERR;
986 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100987 rule->arg.http.str.ptr = strdup(args[cur_arg]);
988 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100989 cur_arg++;
990 }
991
Christopher Fauletc20b3712020-01-27 15:51:56 +0100992 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100993 *orig_arg = cur_arg;
994 return ACT_RET_PRS_OK;
995}
996
997/* Parse a "set-nice" action. It takes the nice value as argument. It returns
998 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
999 */
1000static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
1001 struct act_rule *rule, char **err)
1002{
1003 int cur_arg;
1004
1005 rule->action = ACT_HTTP_SET_NICE;
1006
1007 cur_arg = *orig_arg;
1008 if (!*args[cur_arg]) {
1009 memprintf(err, "expects exactly 1 argument (integer value)");
1010 return ACT_RET_PRS_ERR;
1011 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001012 rule->arg.http.i = atoi(args[cur_arg]);
1013 if (rule->arg.http.i < -1024)
1014 rule->arg.http.i = -1024;
1015 else if (rule->arg.http.i > 1024)
1016 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001017
Christopher Fauletc20b3712020-01-27 15:51:56 +01001018 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001019 *orig_arg = cur_arg + 1;
1020 return ACT_RET_PRS_OK;
1021}
1022
1023/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1024 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1025 */
1026static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1027 struct act_rule *rule, char **err)
1028{
1029#ifdef IP_TOS
1030 char *endp;
1031 int cur_arg;
1032
1033 rule->action = ACT_HTTP_SET_TOS;
1034
1035 cur_arg = *orig_arg;
1036 if (!*args[cur_arg]) {
1037 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1038 return ACT_RET_PRS_ERR;
1039 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001040 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001041 if (endp && *endp != '\0') {
1042 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1043 return ACT_RET_PRS_ERR;
1044 }
1045
Christopher Fauletc20b3712020-01-27 15:51:56 +01001046 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001047 *orig_arg = cur_arg + 1;
1048 return ACT_RET_PRS_OK;
1049#else
1050 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1051 return ACT_RET_PRS_ERR;
1052#endif
1053}
1054
1055/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1056 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1057 */
1058static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1059 struct act_rule *rule, char **err)
1060{
1061#ifdef SO_MARK
1062 char *endp;
1063 int cur_arg;
1064
1065 rule->action = ACT_HTTP_SET_MARK;
1066
1067 cur_arg = *orig_arg;
1068 if (!*args[cur_arg]) {
1069 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1070 return ACT_RET_PRS_ERR;
1071 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001072 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001073 if (endp && *endp != '\0') {
1074 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1075 return ACT_RET_PRS_ERR;
1076 }
1077
Christopher Fauletc20b3712020-01-27 15:51:56 +01001078 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001079 *orig_arg = cur_arg + 1;
1080 global.last_checks |= LSTCHK_NETADM;
1081 return ACT_RET_PRS_OK;
1082#else
1083 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1084 return ACT_RET_PRS_ERR;
1085#endif
1086}
1087
1088/* Parse a "set-log-level" action. It takes the level value as argument. It
1089 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1090 */
1091static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1092 struct act_rule *rule, char **err)
1093{
1094 int cur_arg;
1095
1096 rule->action = ACT_HTTP_SET_LOGL;
1097
1098 cur_arg = *orig_arg;
1099 if (!*args[cur_arg]) {
1100 bad_log_level:
1101 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1102 return ACT_RET_PRS_ERR;
1103 }
1104 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001105 rule->arg.http.i = -1;
1106 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001107 goto bad_log_level;
1108
Christopher Fauletc20b3712020-01-27 15:51:56 +01001109 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001110 *orig_arg = cur_arg + 1;
1111 return ACT_RET_PRS_OK;
1112}
1113
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001114/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1115 * 103 response header with <.arg.http.str> name and with a value built
1116 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001117 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001118 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1119 * block is added to terminate the response. On success, it returns
1120 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1121 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1122 * returned.
1123 */
1124static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1125 struct session *sess, struct stream *s, int flags)
1126{
1127 struct act_rule *prev_rule, *next_rule;
1128 struct channel *res = &s->res;
1129 struct htx *htx = htx_from_buf(&res->buf);
1130 struct buffer *value = alloc_trash_chunk();
1131 enum act_return ret = ACT_RET_CONT;
1132
1133 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1134 goto leave;
1135
1136 if (!value) {
1137 if (!(s->flags & SF_ERR_MASK))
1138 s->flags |= SF_ERR_RESOURCE;
1139 goto error;
1140 }
1141
1142 /* get previous and next rules */
1143 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1144 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1145
1146 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1147 * continue to add link to a previously started response */
1148 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1149 struct htx_sl *sl;
1150 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1151 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1152
1153 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1154 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1155 if (!sl)
1156 goto error;
1157 sl->info.res.status = 103;
1158 }
1159
1160 /* Add the HTTP Early Hint HTTP 103 response heade */
1161 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1162 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1163 goto error;
1164
1165 /* if it is the last rule or the next one is not an early-hint, terminate the current
1166 * response. */
1167 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001168 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1169 /* If an error occurred during an Early-hint rule,
1170 * remove the incomplete HTTP 103 response from the
1171 * buffer */
1172 goto error;
1173 }
1174
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001175 if (!http_forward_proxy_resp(s, 0))
1176 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001177 }
1178
1179 leave:
1180 free_trash_chunk(value);
1181 return ret;
1182
1183 error:
1184 /* If an error occurred during an Early-hint rule, remove the incomplete
1185 * HTTP 103 response from the buffer */
1186 channel_htx_truncate(res, htx);
1187 ret = ACT_RET_ERR;
1188 goto leave;
1189}
1190
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001191/* This function executes a set-header or add-header actions. It builds a string
1192 * in the trash from the specified format string. It finds the action to be
1193 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001194 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001195 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1196 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1197 * ACT_RET_ERR is returned.
1198 */
1199static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1200 struct session *sess, struct stream *s, int flags)
1201{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001202 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1203 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001204 enum act_return ret = ACT_RET_CONT;
1205 struct buffer *replace;
1206 struct http_hdr_ctx ctx;
1207 struct ist n, v;
1208
1209 replace = alloc_trash_chunk();
1210 if (!replace)
1211 goto fail_alloc;
1212
1213 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1214 n = rule->arg.http.str;
1215 v = ist2(replace->area, replace->data);
1216
1217 if (rule->action == 0) { // set-header
1218 /* remove all occurrences of the header */
1219 ctx.blk = NULL;
1220 while (http_find_header(htx, n, &ctx, 1))
1221 http_remove_header(htx, &ctx);
1222 }
1223
1224 /* Now add header */
1225 if (!http_add_header(htx, n, v))
1226 goto fail_rewrite;
1227
1228 leave:
1229 free_trash_chunk(replace);
1230 return ret;
1231
1232 fail_alloc:
1233 if (!(s->flags & SF_ERR_MASK))
1234 s->flags |= SF_ERR_RESOURCE;
1235 ret = ACT_RET_ERR;
1236 goto leave;
1237
1238 fail_rewrite:
1239 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1240 if (s->flags & SF_BE_ASSIGNED)
1241 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1242 if (sess->listener->counters)
1243 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1244 if (objt_server(s->target))
1245 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1246
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001247 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001248 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001249 if (!(s->flags & SF_ERR_MASK))
1250 s->flags |= SF_ERR_PRXCOND;
1251 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001252 goto leave;
1253}
1254
Christopher Faulet81e20172019-12-12 16:40:30 +01001255/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1256 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1257 * on success, ACT_RET_PRS_ERR on error.
1258 *
1259 * Note: same function is used for the request and the response. However
1260 * "early-hint" rules are only supported for request rules.
1261 */
1262static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1263 struct act_rule *rule, char **err)
1264{
Christopher Faulet81e20172019-12-12 16:40:30 +01001265 int cap, cur_arg;
1266
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001267 if (args[*orig_arg-1][0] == 'e') {
1268 rule->action = ACT_CUSTOM;
1269 rule->action_ptr = http_action_early_hint;
1270 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001271 else {
1272 if (args[*orig_arg-1][0] == 's')
1273 rule->action = 0; // set-header
1274 else
1275 rule->action = 1; // add-header
1276 rule->action_ptr = http_action_set_header;
1277 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001278 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001279
1280 cur_arg = *orig_arg;
1281 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1282 memprintf(err, "expects exactly 2 arguments");
1283 return ACT_RET_PRS_ERR;
1284 }
1285
Christopher Faulet81e20172019-12-12 16:40:30 +01001286
Christopher Faulet96bff762019-12-17 13:46:18 +01001287 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1288 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1289 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001290
1291 if (rule->from == ACT_F_HTTP_REQ) {
1292 px->conf.args.ctx = ARGC_HRQ;
1293 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1294 }
1295 else{
1296 px->conf.args.ctx = ARGC_HRS;
1297 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1298 }
1299
1300 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001301 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001302 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001303 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001304 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001305
1306 free(px->conf.lfs_file);
1307 px->conf.lfs_file = strdup(px->conf.args.file);
1308 px->conf.lfs_line = px->conf.args.line;
1309
1310 *orig_arg = cur_arg + 1;
1311 return ACT_RET_PRS_OK;
1312}
1313
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001314/* This function executes a replace-header or replace-value actions. It
1315 * builds a string in the trash from the specified format string. It finds
1316 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001317 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001318 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1319 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1320 * processing continue. Otherwsize ACT_RET_ERR is returned.
1321 */
1322static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1323 struct session *sess, struct stream *s, int flags)
1324{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001325 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1326 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001327 enum act_return ret = ACT_RET_CONT;
1328 struct buffer *replace;
1329 int r;
1330
1331 replace = alloc_trash_chunk();
1332 if (!replace)
1333 goto fail_alloc;
1334
1335 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1336
1337 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1338 if (r == -1)
1339 goto fail_rewrite;
1340
1341 leave:
1342 free_trash_chunk(replace);
1343 return ret;
1344
1345 fail_alloc:
1346 if (!(s->flags & SF_ERR_MASK))
1347 s->flags |= SF_ERR_RESOURCE;
1348 ret = ACT_RET_ERR;
1349 goto leave;
1350
1351 fail_rewrite:
1352 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1353 if (s->flags & SF_BE_ASSIGNED)
1354 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1355 if (sess->listener->counters)
1356 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1357 if (objt_server(s->target))
1358 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1359
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001360 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001361 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001362 if (!(s->flags & SF_ERR_MASK))
1363 s->flags |= SF_ERR_PRXCOND;
1364 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001365 goto leave;
1366}
1367
Christopher Faulet81e20172019-12-12 16:40:30 +01001368/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1369 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1370 * success, ACT_RET_PRS_ERR on error.
1371 */
1372static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1373 struct act_rule *rule, char **err)
1374{
1375 int cap, cur_arg;
1376
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001377 if (args[*orig_arg-1][8] == 'h')
1378 rule->action = 0; // replace-header
1379 else
1380 rule->action = 1; // replace-value
1381 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001382 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001383
1384 cur_arg = *orig_arg;
1385 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1386 memprintf(err, "expects exactly 3 arguments");
1387 return ACT_RET_PRS_ERR;
1388 }
1389
Christopher Faulet96bff762019-12-17 13:46:18 +01001390 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1391 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1392 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001393
1394 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001395 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001396 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001397 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001398 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001399
1400 if (rule->from == ACT_F_HTTP_REQ) {
1401 px->conf.args.ctx = ARGC_HRQ;
1402 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1403 }
1404 else{
1405 px->conf.args.ctx = ARGC_HRS;
1406 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1407 }
1408
1409 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001410 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001411 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001412 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001413 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001414 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001415
1416 free(px->conf.lfs_file);
1417 px->conf.lfs_file = strdup(px->conf.args.file);
1418 px->conf.lfs_line = px->conf.args.line;
1419
1420 *orig_arg = cur_arg + 1;
1421 return ACT_RET_PRS_OK;
1422}
1423
1424/* Parse a "del-header" action. It takes an header name as argument. It returns
1425 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1426 */
1427static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1428 struct act_rule *rule, char **err)
1429{
1430 int cur_arg;
1431
1432 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001433 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001434
1435 cur_arg = *orig_arg;
1436 if (!*args[cur_arg]) {
1437 memprintf(err, "expects exactly 1 arguments");
1438 return ACT_RET_PRS_ERR;
1439 }
1440
Christopher Faulet96bff762019-12-17 13:46:18 +01001441 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1442 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001443 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1444
Christopher Fauletc20b3712020-01-27 15:51:56 +01001445 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001446 *orig_arg = cur_arg + 1;
1447 return ACT_RET_PRS_OK;
1448}
1449
Christopher Faulet2eb53962020-01-14 14:47:34 +01001450/* Release memory allocated by an http redirect action. */
1451static void release_http_redir(struct act_rule *rule)
1452{
1453 struct logformat_node *lf, *lfb;
1454 struct redirect_rule *redir;
1455
1456 redir = rule->arg.redir;
1457 LIST_DEL(&redir->list);
1458 if (redir->cond) {
1459 prune_acl_cond(redir->cond);
1460 free(redir->cond);
1461 }
1462 free(redir->rdr_str);
1463 free(redir->cookie_str);
1464 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1465 LIST_DEL(&lf->list);
1466 free(lf);
1467 }
1468 free(redir);
1469}
1470
Christopher Faulet81e20172019-12-12 16:40:30 +01001471/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1472 * ACT_RET_PRS_ERR on error.
1473 */
1474static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1475 struct act_rule *rule, char **err)
1476{
1477 struct redirect_rule *redir;
1478 int dir, cur_arg;
1479
1480 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001481 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001482 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001483
1484 cur_arg = *orig_arg;
1485
1486 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1487 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1488 return ACT_RET_PRS_ERR;
1489
1490 rule->arg.redir = redir;
1491 rule->cond = redir->cond;
1492 redir->cond = NULL;
1493
1494 /* skip all arguments */
1495 while (*args[cur_arg])
1496 cur_arg++;
1497
1498 *orig_arg = cur_arg;
1499 return ACT_RET_PRS_OK;
1500}
1501
Christopher Faulet046cf442019-12-17 15:45:23 +01001502/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1503 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1504 */
1505static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1506 struct session *sess, struct stream *s, int flags)
1507{
1508 struct pat_ref *ref;
1509 struct buffer *key = NULL, *value = NULL;
1510 enum act_return ret = ACT_RET_CONT;
1511
1512 /* collect reference */
1513 ref = pat_ref_lookup(rule->arg.map.ref);
1514 if (!ref)
1515 goto leave;
1516
1517 /* allocate key */
1518 key = alloc_trash_chunk();
1519 if (!key)
1520 goto fail_alloc;
1521
1522 /* collect key */
1523 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1524 key->area[key->data] = '\0';
1525
1526 switch (rule->action) {
1527 case 0: // add-acl
1528 /* add entry only if it does not already exist */
1529 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1530 if (pat_ref_find_elt(ref, key->area) == NULL)
1531 pat_ref_add(ref, key->area, NULL, NULL);
1532 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1533 break;
1534
1535 case 1: // set-map
1536 /* allocate value */
1537 value = alloc_trash_chunk();
1538 if (!value)
1539 goto fail_alloc;
1540
1541 /* collect value */
1542 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1543 value->area[value->data] = '\0';
1544
1545 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1546 if (pat_ref_find_elt(ref, key->area) != NULL) {
1547 /* update entry if it exists */
1548 pat_ref_set(ref, key->area, value->area, NULL);
1549 }
1550 else {
1551 /* insert a new entry */
1552 pat_ref_add(ref, key->area, value->area, NULL);
1553 }
1554 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1555 break;
1556
1557 case 2: // del-acl
1558 case 3: // del-map
1559 /* returned code: 1=ok, 0=ko */
1560 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1561 pat_ref_delete(ref, key->area);
1562 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1563 break;
1564
1565 default:
1566 ret = ACT_RET_ERR;
1567 }
1568
1569
1570 leave:
1571 free_trash_chunk(key);
1572 free_trash_chunk(value);
1573 return ret;
1574
1575 fail_alloc:
1576 if (!(s->flags & SF_ERR_MASK))
1577 s->flags |= SF_ERR_RESOURCE;
1578 ret = ACT_RET_ERR;
1579 goto leave;
1580}
1581
Christopher Faulet2eb53962020-01-14 14:47:34 +01001582/* Release memory allocated by an http map/acl action. */
1583static void release_http_map(struct act_rule *rule)
1584{
1585 struct logformat_node *lf, *lfb;
1586
1587 free(rule->arg.map.ref);
1588 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1589 LIST_DEL(&lf->list);
1590 release_sample_expr(lf->expr);
1591 free(lf->arg);
1592 free(lf);
1593 }
1594 if (rule->action == 1) {
1595 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1596 LIST_DEL(&lf->list);
1597 release_sample_expr(lf->expr);
1598 free(lf->arg);
1599 free(lf);
1600 }
1601 }
1602}
1603
Christopher Faulet81e20172019-12-12 16:40:30 +01001604/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001605 * two log-format string as argument depending on the action. The action is
1606 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1607 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001608 */
1609static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1610 struct act_rule *rule, char **err)
1611{
1612 int cap, cur_arg;
1613
Christopher Faulet046cf442019-12-17 15:45:23 +01001614 if (args[*orig_arg-1][0] == 'a') // add-acl
1615 rule->action = 0;
1616 else if (args[*orig_arg-1][0] == 's') // set-map
1617 rule->action = 1;
1618 else if (args[*orig_arg-1][4] == 'a') // del-acl
1619 rule->action = 2;
1620 else if (args[*orig_arg-1][4] == 'm') // del-map
1621 rule->action = 3;
1622 else {
1623 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1624 return ACT_RET_PRS_ERR;
1625 }
1626 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001627 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001628
1629 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001630 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1631 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001632 memprintf(err, "expects exactly 2 arguments");
1633 return ACT_RET_PRS_ERR;
1634 }
1635 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001636 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001637 memprintf(err, "expects exactly 1 arguments");
1638 return ACT_RET_PRS_ERR;
1639 }
1640
1641 /*
1642 * '+ 8' for 'set-map(' (same for del-map)
1643 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1644 */
1645 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1646
1647 if (rule->from == ACT_F_HTTP_REQ) {
1648 px->conf.args.ctx = ARGC_HRQ;
1649 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1650 }
1651 else{
1652 px->conf.args.ctx = ARGC_HRS;
1653 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1654 }
1655
1656 /* key pattern */
1657 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001658 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1659 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001660 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001661 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001662
Christopher Faulet046cf442019-12-17 15:45:23 +01001663 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001664 /* value pattern for set-map only */
1665 cur_arg++;
1666 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001667 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1668 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001669 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001670 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001671 }
1672
1673 free(px->conf.lfs_file);
1674 px->conf.lfs_file = strdup(px->conf.args.file);
1675 px->conf.lfs_line = px->conf.args.line;
1676
1677 *orig_arg = cur_arg + 1;
1678 return ACT_RET_PRS_OK;
1679}
1680
Christopher Fauletac98d812019-12-18 09:20:16 +01001681/* This function executes a track-sc* actions. On success, it returns
1682 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1683 */
1684static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1685 struct session *sess, struct stream *s, int flags)
1686{
1687 struct stktable *t;
1688 struct stksess *ts;
1689 struct stktable_key *key;
1690 void *ptr1, *ptr2, *ptr3, *ptr4;
1691 int opt;
1692
1693 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1694 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1695
1696 t = rule->arg.trk_ctr.table.t;
1697 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1698
1699 if (!key)
1700 goto end;
1701 ts = stktable_get_entry(t, key);
1702 if (!ts)
1703 goto end;
1704
1705 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1706
1707 /* let's count a new HTTP request as it's the first time we do it */
1708 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1709 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1710
1711 /* When the client triggers a 4xx from the server, it's most often due
1712 * to a missing object or permission. These events should be tracked
1713 * because if they happen often, it may indicate a brute force or a
1714 * vulnerability scan. Normally this is done when receiving the response
1715 * but here we're tracking after this ought to have been done so we have
1716 * to do it on purpose.
1717 */
1718 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1719 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1720 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1721 }
1722
1723 if (ptr1 || ptr2 || ptr3 || ptr4) {
1724 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1725
1726 if (ptr1)
1727 stktable_data_cast(ptr1, http_req_cnt)++;
1728 if (ptr2)
1729 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1730 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1731 if (ptr3)
1732 stktable_data_cast(ptr3, http_err_cnt)++;
1733 if (ptr4)
1734 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1735 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1736
1737 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1738
1739 /* If data was modified, we need to touch to re-schedule sync */
1740 stktable_touch_local(t, ts, 0);
1741 }
1742
1743 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1744 if (sess->fe != s->be)
1745 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1746
1747 end:
1748 return ACT_RET_CONT;
1749}
Christopher Faulet81e20172019-12-12 16:40:30 +01001750
Christopher Faulet2eb53962020-01-14 14:47:34 +01001751static void release_http_track_sc(struct act_rule *rule)
1752{
1753 release_sample_expr(rule->arg.trk_ctr.expr);
1754}
1755
Christopher Faulet81e20172019-12-12 16:40:30 +01001756/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1757 * ACT_RET_PRS_ERR on error.
1758 */
1759static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1760 struct act_rule *rule, char **err)
1761{
1762 struct sample_expr *expr;
1763 unsigned int where;
1764 unsigned int tsc_num;
1765 const char *tsc_num_str;
1766 int cur_arg;
1767
1768 tsc_num_str = &args[*orig_arg-1][8];
1769 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1770 return ACT_RET_PRS_ERR;
1771
1772 cur_arg = *orig_arg;
1773 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001774 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001775 if (!expr)
1776 return ACT_RET_PRS_ERR;
1777
1778 where = 0;
1779 if (px->cap & PR_CAP_FE)
1780 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1781 if (px->cap & PR_CAP_BE)
1782 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1783
1784 if (!(expr->fetch->val & where)) {
1785 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1786 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001787 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001788 return ACT_RET_PRS_ERR;
1789 }
1790
1791 if (strcmp(args[cur_arg], "table") == 0) {
1792 cur_arg++;
1793 if (!*args[cur_arg]) {
1794 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001795 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001796 return ACT_RET_PRS_ERR;
1797 }
1798
1799 /* we copy the table name for now, it will be resolved later */
1800 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1801 cur_arg++;
1802 }
1803
Christopher Fauletac98d812019-12-18 09:20:16 +01001804 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001805 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001806 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001807 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001808 rule->check_ptr = check_trk_action;
1809
1810 *orig_arg = cur_arg;
1811 return ACT_RET_PRS_OK;
1812}
1813
Christopher Faulet46f95542019-12-20 10:07:22 +01001814/* This function executes a strict-mode actions. On success, it always returns
1815 * ACT_RET_CONT
1816 */
1817static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1818 struct session *sess, struct stream *s, int flags)
1819{
1820 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1821
1822 if (rule->action == 0) // strict-mode on
1823 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1824 else // strict-mode off
1825 msg->flags |= HTTP_MSGF_SOFT_RW;
1826 return ACT_RET_CONT;
1827}
1828
1829/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1830 * ACT_RET_PRS_ERR on error.
1831 */
1832static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1833 struct act_rule *rule, char **err)
1834{
1835 int cur_arg;
1836
Christopher Faulet46f95542019-12-20 10:07:22 +01001837 cur_arg = *orig_arg;
1838 if (!*args[cur_arg]) {
1839 memprintf(err, "expects exactly 1 arguments");
1840 return ACT_RET_PRS_ERR;
1841 }
1842
1843 if (strcasecmp(args[cur_arg], "on") == 0)
1844 rule->action = 0; // strict-mode on
1845 else if (strcasecmp(args[cur_arg], "off") == 0)
1846 rule->action = 1; // strict-mode off
1847 else {
1848 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1849 return ACT_RET_PRS_ERR;
1850 }
1851 rule->action_ptr = http_action_strict_mode;
1852
1853 *orig_arg = cur_arg + 1;
1854 return ACT_RET_PRS_OK;
1855}
1856
Christopher Faulet24231ab2020-01-24 17:44:23 +01001857/* This function executes a return action. It builds an HTX message from an
1858 * errorfile, an raw file or a log-format string, depending on <.action>
1859 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1860 * returned.
1861 */
1862static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1863 struct session *sess, struct stream *s, int flags)
1864{
1865 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001866
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001867 if (http_reply_message(s, rule->arg.http_reply) == -1)
1868 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001869
Christopher Faulet24231ab2020-01-24 17:44:23 +01001870 if (rule->from == ACT_F_HTTP_REQ) {
1871 /* let's log the request time */
1872 s->logs.tv_request = now;
1873 req->analysers &= AN_REQ_FLT_END;
1874
1875 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1876 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1877 }
1878
1879 if (!(s->flags & SF_ERR_MASK))
1880 s->flags |= SF_ERR_LOCAL;
1881 if (!(s->flags & SF_FINST_MASK))
1882 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1883
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001884 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001885}
1886
Christopher Faulet24231ab2020-01-24 17:44:23 +01001887/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001888 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1889 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001890 */
1891static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1892 struct act_rule *rule, char **err)
1893{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001894 /* Prepare parsing of log-format strings */
1895 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1896 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1897 if (!rule->arg.http_reply)
1898 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001899
Christopher Fauletba946bf2020-05-13 08:50:07 +02001900 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001901 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001902 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001903 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001904 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001905 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001906}
1907
Willy Tarreau79e57332018-10-02 16:01:16 +02001908/************************************************************************/
1909/* All supported http-request action keywords must be declared here. */
1910/************************************************************************/
1911
1912static struct action_kw_list http_req_actions = {
1913 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001914 { "add-acl", parse_http_set_map, 1 },
1915 { "add-header", parse_http_set_header, 0 },
1916 { "allow", parse_http_allow, 0 },
1917 { "auth", parse_http_auth, 0 },
1918 { "capture", parse_http_req_capture, 0 },
1919 { "del-acl", parse_http_set_map, 1 },
1920 { "del-header", parse_http_del_header, 0 },
1921 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001922 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001923 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1924 { "early-hint", parse_http_set_header, 0 },
1925 { "redirect", parse_http_redirect, 0 },
1926 { "reject", parse_http_action_reject, 0 },
1927 { "replace-header", parse_http_replace_header, 0 },
1928 { "replace-path", parse_replace_uri, 0 },
1929 { "replace-uri", parse_replace_uri, 0 },
1930 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001931 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001932 { "set-header", parse_http_set_header, 0 },
1933 { "set-log-level", parse_http_set_log_level, 0 },
1934 { "set-map", parse_http_set_map, 1 },
1935 { "set-method", parse_set_req_line, 0 },
1936 { "set-mark", parse_http_set_mark, 0 },
1937 { "set-nice", parse_http_set_nice, 0 },
1938 { "set-path", parse_set_req_line, 0 },
1939 { "set-query", parse_set_req_line, 0 },
1940 { "set-tos", parse_http_set_tos, 0 },
1941 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001942 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001943 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001944 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001945 { NULL, NULL }
1946 }
1947};
1948
Willy Tarreau0108d902018-11-25 19:14:37 +01001949INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1950
Willy Tarreau79e57332018-10-02 16:01:16 +02001951static struct action_kw_list http_res_actions = {
1952 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001953 { "add-acl", parse_http_set_map, 1 },
1954 { "add-header", parse_http_set_header, 0 },
1955 { "allow", parse_http_allow, 0 },
1956 { "capture", parse_http_res_capture, 0 },
1957 { "del-acl", parse_http_set_map, 1 },
1958 { "del-header", parse_http_del_header, 0 },
1959 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001960 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001961 { "redirect", parse_http_redirect, 0 },
1962 { "replace-header", parse_http_replace_header, 0 },
1963 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001964 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001965 { "set-header", parse_http_set_header, 0 },
1966 { "set-log-level", parse_http_set_log_level, 0 },
1967 { "set-map", parse_http_set_map, 1 },
1968 { "set-mark", parse_http_set_mark, 0 },
1969 { "set-nice", parse_http_set_nice, 0 },
1970 { "set-status", parse_http_set_status, 0 },
1971 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001972 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001973 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001974 { NULL, NULL }
1975 }
1976};
1977
Willy Tarreau0108d902018-11-25 19:14:37 +01001978INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001979
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001980static struct action_kw_list http_after_res_actions = {
1981 .kw = {
1982 { "add-header", parse_http_set_header, 0 },
1983 { "allow", parse_http_allow, 0 },
1984 { "del-header", parse_http_del_header, 0 },
1985 { "replace-header", parse_http_replace_header, 0 },
1986 { "replace-value", parse_http_replace_header, 0 },
1987 { "set-header", parse_http_set_header, 0 },
1988 { "set-status", parse_http_set_status, 0 },
1989 { "strict-mode", parse_http_strict_mode, 0 },
1990 { NULL, NULL }
1991 }
1992};
1993
1994INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
1995
Willy Tarreau79e57332018-10-02 16:01:16 +02001996/*
1997 * Local variables:
1998 * c-indent-level: 8
1999 * c-basic-offset: 8
2000 * End:
2001 */