blob: b7a6603c9fb433bce62fe214b242827021d48e46 [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
Christopher Faulet2eb53962020-01-14 14:47:34 +010043/* Release memory allocated by most of HTTP actions. Concretly, it releases
44 * <arg.http>.
45 */
46static void release_http_action(struct act_rule *rule)
47{
48 struct logformat_node *lf, *lfb;
49
Tim Duesterhused526372020-03-05 17:56:33 +010050 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010051 if (rule->arg.http.re)
52 regex_free(rule->arg.http.re);
53 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
54 LIST_DEL(&lf->list);
55 release_sample_expr(lf->expr);
56 free(lf->arg);
57 free(lf);
58 }
59}
60
Willy Tarreau79e57332018-10-02 16:01:16 +020061
62/* This function executes one of the set-{method,path,query,uri} actions. It
63 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010064 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050065 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010066 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
67 * occurs while soft rewrites are enabled, the action is canceled, but the rule
68 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020069 */
70static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
71 struct session *sess, struct stream *s, int flags)
72{
73 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010074 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020075
76 replace = alloc_trash_chunk();
77 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +010078 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +020079
80 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +010081 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +020082 replace->area[replace->data++] = '?';
83 replace->data += build_logline(s, replace->area + replace->data,
84 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +010085 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +020086
Christopher Faulet2c22a692019-12-18 15:39:56 +010087 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +010088 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +020089
Christopher Faulete00d06c2019-12-16 17:18:42 +010090 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +020091 free_trash_chunk(replace);
92 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +010093
94 fail_alloc:
95 if (!(s->flags & SF_ERR_MASK))
96 s->flags |= SF_ERR_RESOURCE;
97 ret = ACT_RET_ERR;
98 goto leave;
99
100 fail_rewrite:
101 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
102 if (s->flags & SF_BE_ASSIGNED)
103 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
104 if (sess->listener->counters)
105 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
106 if (objt_server(s->target))
107 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
108
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100109 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100110 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100111 if (!(s->flags & SF_ERR_MASK))
112 s->flags |= SF_ERR_PRXCOND;
113 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100114 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200115}
116
117/* parse an http-request action among :
118 * set-method
119 * set-path
120 * set-query
121 * set-uri
122 *
123 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100124 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100125 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
126 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200127 */
128static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
129 struct act_rule *rule, char **err)
130{
131 int cur_arg = *orig_arg;
132
Willy Tarreau79e57332018-10-02 16:01:16 +0200133 switch (args[0][4]) {
134 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100135 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200136 break;
137 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100138 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200139 break;
140 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100141 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200142 break;
143 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100144 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200145 break;
146 default:
147 memprintf(err, "internal error: unhandled action '%s'", args[0]);
148 return ACT_RET_PRS_ERR;
149 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100150 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100151 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200152
153 if (!*args[cur_arg] ||
154 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
155 memprintf(err, "expects exactly 1 argument <format>");
156 return ACT_RET_PRS_ERR;
157 }
158
Christopher Faulet96bff762019-12-17 13:46:18 +0100159 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200160 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100161 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200162 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
163 return ACT_RET_PRS_ERR;
164 }
165
166 (*orig_arg)++;
167 return ACT_RET_PRS_OK;
168}
169
Willy Tarreau33810222019-06-12 17:44:02 +0200170/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100171 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200172 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100173 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100174 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100175 * contains 1 for the path or 3 for the URI (values used by
176 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
177 * occurs while soft rewrites are enabled, the action is canceled, but the rule
178 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200179 */
180static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
181 struct session *sess, struct stream *s, int flags)
182{
Christopher Faulet13403762019-12-13 09:01:57 +0100183 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200184 struct buffer *replace, *output;
185 struct ist uri;
186 int len;
187
188 replace = alloc_trash_chunk();
189 output = alloc_trash_chunk();
190 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100191 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200192 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100193
Christopher Faulet2c22a692019-12-18 15:39:56 +0100194 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100195 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100196
Christopher Faulet96bff762019-12-17 13:46:18 +0100197 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200198 goto leave;
199
Christopher Faulet96bff762019-12-17 13:46:18 +0100200 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200201
202 /* note: uri.ptr doesn't need to be zero-terminated because it will
203 * only be used to pick pmatch references.
204 */
205 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
206 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100207 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200208
Christopher Faulet2c22a692019-12-18 15:39:56 +0100209 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100210 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200211
Christopher Faulete00d06c2019-12-16 17:18:42 +0100212 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200213 free_trash_chunk(output);
214 free_trash_chunk(replace);
215 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100216
217 fail_alloc:
218 if (!(s->flags & SF_ERR_MASK))
219 s->flags |= SF_ERR_RESOURCE;
220 ret = ACT_RET_ERR;
221 goto leave;
222
223 fail_rewrite:
224 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
225 if (s->flags & SF_BE_ASSIGNED)
226 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
227 if (sess->listener->counters)
228 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
229 if (objt_server(s->target))
230 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
231
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100232 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100233 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100234 if (!(s->flags & SF_ERR_MASK))
235 s->flags |= SF_ERR_PRXCOND;
236 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100237 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200238}
239
Willy Tarreau262c3f12019-12-17 06:52:51 +0100240/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200241 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100242 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100243 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200244 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
245 */
246static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
247 struct act_rule *rule, char **err)
248{
249 int cur_arg = *orig_arg;
250 char *error = NULL;
251
Willy Tarreau262c3f12019-12-17 06:52:51 +0100252 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100253 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100254 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100255 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100256
Willy Tarreau33810222019-06-12 17:44:02 +0200257 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100258 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200259
260 if (!*args[cur_arg] || !*args[cur_arg+1] ||
261 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
262 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
263 return ACT_RET_PRS_ERR;
264 }
265
Christopher Faulet96bff762019-12-17 13:46:18 +0100266 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200267 memprintf(err, "failed to parse the regex : %s", error);
268 free(error);
269 return ACT_RET_PRS_ERR;
270 }
271
Christopher Faulet96bff762019-12-17 13:46:18 +0100272 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200273 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100274 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200275 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100276 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200277 return ACT_RET_PRS_ERR;
278 }
279
280 (*orig_arg) += 2;
281 return ACT_RET_PRS_OK;
282}
283
Willy Tarreau79e57332018-10-02 16:01:16 +0200284/* This function is just a compliant action wrapper for "set-status". */
285static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
286 struct session *sess, struct stream *s, int flags)
287{
Christopher Faulet96bff762019-12-17 13:46:18 +0100288 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100289 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
290 if (s->flags & SF_BE_ASSIGNED)
291 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
292 if (sess->listener->counters)
293 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
294 if (objt_server(s->target))
295 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
296
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100297 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100298 if (!(s->flags & SF_ERR_MASK))
299 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100300 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100301 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100302 }
303
Willy Tarreau79e57332018-10-02 16:01:16 +0200304 return ACT_RET_CONT;
305}
306
307/* parse set-status action:
308 * This action accepts a single argument of type int representing
309 * an http status code. It returns ACT_RET_PRS_OK on success,
310 * ACT_RET_PRS_ERR on error.
311 */
312static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
313 struct act_rule *rule, char **err)
314{
315 char *error;
316
317 rule->action = ACT_CUSTOM;
318 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100319 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200320
321 /* Check if an argument is available */
322 if (!*args[*orig_arg]) {
323 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
324 return ACT_RET_PRS_ERR;
325 }
326
327 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100328 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
329 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200330 memprintf(err, "expects an integer status code between 100 and 999");
331 return ACT_RET_PRS_ERR;
332 }
333
334 (*orig_arg)++;
335
336 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100337 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200338 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
339 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
340 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100341 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
342 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200343 (*orig_arg)++;
344 }
345
Christopher Fauletc20b3712020-01-27 15:51:56 +0100346 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200347 return ACT_RET_PRS_OK;
348}
349
350/* This function executes the "reject" HTTP action. It clears the request and
351 * response buffer without sending any response. It can be useful as an HTTP
352 * alternative to the silent-drop action to defend against DoS attacks, and may
353 * also be used with HTTP/2 to close a connection instead of just a stream.
354 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100355 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200356 */
357static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
358 struct session *sess, struct stream *s, int flags)
359{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100360 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200361 channel_abort(&s->req);
362 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100363 s->req.analysers &= AN_REQ_FLT_END;
364 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200365
Olivier Houcharda798bf52019-03-08 18:52:00 +0100366 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
367 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200368 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100369 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200370
371 if (!(s->flags & SF_ERR_MASK))
372 s->flags |= SF_ERR_PRXCOND;
373 if (!(s->flags & SF_FINST_MASK))
374 s->flags |= SF_FINST_R;
375
Christopher Faulet90d22a82020-03-06 11:18:39 +0100376 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200377}
378
379/* parse the "reject" action:
380 * This action takes no argument and returns ACT_RET_PRS_OK on success,
381 * ACT_RET_PRS_ERR on error.
382 */
383static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
384 struct act_rule *rule, char **err)
385{
386 rule->action = ACT_CUSTOM;
387 rule->action_ptr = http_action_reject;
388 return ACT_RET_PRS_OK;
389}
390
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200391/* This function executes the "disable-l7-retry" HTTP action.
392 * It disables L7 retries (all retry except for a connection failure). This
393 * can be useful for example to avoid retrying on POST requests.
394 * It just removes the L7 retry flag on the stream_interface, and always
395 * return ACT_RET_CONT;
396 */
397static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
398 struct session *sess, struct stream *s, int flags)
399{
400 struct stream_interface *si = &s->si[1];
401
402 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
403 * let's be future-proof and remove it anyway.
404 */
405 si->flags &= ~SI_FL_L7_RETRY;
406 si->flags |= SI_FL_D_L7_RETRY;
407 return ACT_RET_CONT;
408}
409
410/* parse the "disable-l7-retry" action:
411 * This action takes no argument and returns ACT_RET_PRS_OK on success,
412 * ACT_RET_PRS_ERR on error.
413 */
414static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
415 int *orig_args, struct proxy *px,
416 struct act_rule *rule, char **err)
417{
418 rule->action = ACT_CUSTOM;
419 rule->action_ptr = http_req_disable_l7_retry;
420 return ACT_RET_PRS_OK;
421}
422
Willy Tarreau79e57332018-10-02 16:01:16 +0200423/* This function executes the "capture" action. It executes a fetch expression,
424 * turns the result into a string and puts it in a capture slot. It always
425 * returns 1. If an error occurs the action is cancelled, but the rule
426 * processing continues.
427 */
428static enum act_return http_action_req_capture(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 = rule->arg.cap.hdr;
433 char **cap = s->req_cap;
434 int len;
435
436 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
437 if (!key)
438 return ACT_RET_CONT;
439
440 if (cap[h->index] == NULL)
441 cap[h->index] = pool_alloc(h->pool);
442
443 if (cap[h->index] == NULL) /* no more capture memory */
444 return ACT_RET_CONT;
445
446 len = key->data.u.str.data;
447 if (len > h->len)
448 len = h->len;
449
450 memcpy(cap[h->index], key->data.u.str.area, len);
451 cap[h->index][len] = 0;
452 return ACT_RET_CONT;
453}
454
455/* This function executes the "capture" action and store the result in a
456 * capture slot if exists. It executes a fetch expression, turns the result
457 * into a string and puts it in a capture slot. It always returns 1. If an
458 * error occurs the action is cancelled, but the rule processing continues.
459 */
460static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
461 struct session *sess, struct stream *s, int flags)
462{
463 struct sample *key;
464 struct cap_hdr *h;
465 char **cap = s->req_cap;
466 struct proxy *fe = strm_fe(s);
467 int len;
468 int i;
469
470 /* Look for the original configuration. */
471 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
472 h != NULL && i != rule->arg.capid.idx ;
473 i--, h = h->next);
474 if (!h)
475 return ACT_RET_CONT;
476
477 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
478 if (!key)
479 return ACT_RET_CONT;
480
481 if (cap[h->index] == NULL)
482 cap[h->index] = pool_alloc(h->pool);
483
484 if (cap[h->index] == NULL) /* no more capture memory */
485 return ACT_RET_CONT;
486
487 len = key->data.u.str.data;
488 if (len > h->len)
489 len = h->len;
490
491 memcpy(cap[h->index], key->data.u.str.area, len);
492 cap[h->index][len] = 0;
493 return ACT_RET_CONT;
494}
495
496/* Check an "http-request capture" action.
497 *
498 * The function returns 1 in success case, otherwise, it returns 0 and err is
499 * filled.
500 */
501static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
502{
503 if (rule->action_ptr != http_action_req_capture_by_id)
504 return 1;
505
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100506 /* capture slots can only be declared in frontends, so we can't check their
507 * existence in backends at configuration parsing step
508 */
509 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200510 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
511 rule->arg.capid.idx);
512 return 0;
513 }
514
515 return 1;
516}
517
Christopher Faulet2eb53962020-01-14 14:47:34 +0100518/* Release memory allocate by an http capture action */
519static void release_http_capture(struct act_rule *rule)
520{
521 if (rule->action_ptr == http_action_req_capture)
522 release_sample_expr(rule->arg.cap.expr);
523 else
524 release_sample_expr(rule->arg.capid.expr);
525}
526
Willy Tarreau79e57332018-10-02 16:01:16 +0200527/* parse an "http-request capture" action. It takes a single argument which is
528 * a sample fetch expression. It stores the expression into arg->act.p[0] and
529 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
530 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
531 */
532static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
533 struct act_rule *rule, char **err)
534{
535 struct sample_expr *expr;
536 struct cap_hdr *hdr;
537 int cur_arg;
538 int len = 0;
539
540 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
541 if (strcmp(args[cur_arg], "if") == 0 ||
542 strcmp(args[cur_arg], "unless") == 0)
543 break;
544
545 if (cur_arg < *orig_arg + 3) {
546 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
547 return ACT_RET_PRS_ERR;
548 }
549
550 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100551 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 +0200552 if (!expr)
553 return ACT_RET_PRS_ERR;
554
555 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
556 memprintf(err,
557 "fetch method '%s' extracts information from '%s', none of which is available here",
558 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100559 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200560 return ACT_RET_PRS_ERR;
561 }
562
563 if (!args[cur_arg] || !*args[cur_arg]) {
564 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100565 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200566 return ACT_RET_PRS_ERR;
567 }
568
569 if (strcmp(args[cur_arg], "len") == 0) {
570 cur_arg++;
571
572 if (!(px->cap & PR_CAP_FE)) {
573 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100574 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200575 return ACT_RET_PRS_ERR;
576 }
577
578 px->conf.args.ctx = ARGC_CAP;
579
580 if (!args[cur_arg]) {
581 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100582 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200583 return ACT_RET_PRS_ERR;
584 }
585 /* we copy the table name for now, it will be resolved later */
586 len = atoi(args[cur_arg]);
587 if (len <= 0) {
588 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100589 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200590 return ACT_RET_PRS_ERR;
591 }
592 cur_arg++;
593
Willy Tarreau79e57332018-10-02 16:01:16 +0200594 hdr = calloc(1, sizeof(*hdr));
595 hdr->next = px->req_cap;
596 hdr->name = NULL; /* not a header capture */
597 hdr->namelen = 0;
598 hdr->len = len;
599 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
600 hdr->index = px->nb_req_cap++;
601
602 px->req_cap = hdr;
603 px->to_log |= LW_REQHDR;
604
605 rule->action = ACT_CUSTOM;
606 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100607 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200608 rule->arg.cap.expr = expr;
609 rule->arg.cap.hdr = hdr;
610 }
611
612 else if (strcmp(args[cur_arg], "id") == 0) {
613 int id;
614 char *error;
615
616 cur_arg++;
617
618 if (!args[cur_arg]) {
619 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100620 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200621 return ACT_RET_PRS_ERR;
622 }
623
624 id = strtol(args[cur_arg], &error, 10);
625 if (*error != '\0') {
626 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100627 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200628 return ACT_RET_PRS_ERR;
629 }
630 cur_arg++;
631
632 px->conf.args.ctx = ARGC_CAP;
633
634 rule->action = ACT_CUSTOM;
635 rule->action_ptr = http_action_req_capture_by_id;
636 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100637 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200638 rule->arg.capid.expr = expr;
639 rule->arg.capid.idx = id;
640 }
641
642 else {
643 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100644 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200645 return ACT_RET_PRS_ERR;
646 }
647
648 *orig_arg = cur_arg;
649 return ACT_RET_PRS_OK;
650}
651
652/* This function executes the "capture" action and store the result in a
653 * capture slot if exists. It executes a fetch expression, turns the result
654 * into a string and puts it in a capture slot. It always returns 1. If an
655 * error occurs the action is cancelled, but the rule processing continues.
656 */
657static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
658 struct session *sess, struct stream *s, int flags)
659{
660 struct sample *key;
661 struct cap_hdr *h;
662 char **cap = s->res_cap;
663 struct proxy *fe = strm_fe(s);
664 int len;
665 int i;
666
667 /* Look for the original configuration. */
668 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
669 h != NULL && i != rule->arg.capid.idx ;
670 i--, h = h->next);
671 if (!h)
672 return ACT_RET_CONT;
673
674 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
675 if (!key)
676 return ACT_RET_CONT;
677
678 if (cap[h->index] == NULL)
679 cap[h->index] = pool_alloc(h->pool);
680
681 if (cap[h->index] == NULL) /* no more capture memory */
682 return ACT_RET_CONT;
683
684 len = key->data.u.str.data;
685 if (len > h->len)
686 len = h->len;
687
688 memcpy(cap[h->index], key->data.u.str.area, len);
689 cap[h->index][len] = 0;
690 return ACT_RET_CONT;
691}
692
693/* Check an "http-response capture" action.
694 *
695 * The function returns 1 in success case, otherwise, it returns 0 and err is
696 * filled.
697 */
698static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
699{
700 if (rule->action_ptr != http_action_res_capture_by_id)
701 return 1;
702
703 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
704 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
705 rule->arg.capid.idx);
706 return 0;
707 }
708
709 return 1;
710}
711
712/* parse an "http-response capture" action. It takes a single argument which is
713 * a sample fetch expression. It stores the expression into arg->act.p[0] and
714 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
715 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
716 */
717static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
718 struct act_rule *rule, char **err)
719{
720 struct sample_expr *expr;
721 int cur_arg;
722 int id;
723 char *error;
724
725 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
726 if (strcmp(args[cur_arg], "if") == 0 ||
727 strcmp(args[cur_arg], "unless") == 0)
728 break;
729
730 if (cur_arg < *orig_arg + 3) {
731 memprintf(err, "expects <expression> id <idx>");
732 return ACT_RET_PRS_ERR;
733 }
734
735 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100736 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +0200737 if (!expr)
738 return ACT_RET_PRS_ERR;
739
740 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
741 memprintf(err,
742 "fetch method '%s' extracts information from '%s', none of which is available here",
743 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100744 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200745 return ACT_RET_PRS_ERR;
746 }
747
748 if (!args[cur_arg] || !*args[cur_arg]) {
749 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100750 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200751 return ACT_RET_PRS_ERR;
752 }
753
754 if (strcmp(args[cur_arg], "id") != 0) {
755 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100756 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200757 return ACT_RET_PRS_ERR;
758 }
759
760 cur_arg++;
761
762 if (!args[cur_arg]) {
763 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100764 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200765 return ACT_RET_PRS_ERR;
766 }
767
768 id = strtol(args[cur_arg], &error, 10);
769 if (*error != '\0') {
770 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100771 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200772 return ACT_RET_PRS_ERR;
773 }
774 cur_arg++;
775
776 px->conf.args.ctx = ARGC_CAP;
777
778 rule->action = ACT_CUSTOM;
779 rule->action_ptr = http_action_res_capture_by_id;
780 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100781 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200782 rule->arg.capid.expr = expr;
783 rule->arg.capid.idx = id;
784
785 *orig_arg = cur_arg;
786 return ACT_RET_PRS_OK;
787}
788
Christopher Faulet81e20172019-12-12 16:40:30 +0100789/* Parse a "allow" action for a request or a response rule. It takes no argument. It
790 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
791 */
792static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
793 struct act_rule *rule, char **err)
794{
795 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100796 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100797 return ACT_RET_PRS_OK;
798}
799
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100800/* Check an "http-request deny" action when an http-errors section is referenced.
801 *
802 * The function returns 1 in success case, otherwise, it returns 0 and err is
803 * filled.
804 */
805static int check_http_deny_action(struct act_rule *rule, struct proxy *px, char **err)
806{
807 struct http_errors *http_errs;
808 int status = (intptr_t)(rule->arg.act.p[0]);
809 int ret = 1;
810
811 list_for_each_entry(http_errs, &http_errors_list, list) {
812 if (strcmp(http_errs->id, (char *)rule->arg.act.p[1]) == 0) {
813 free(rule->arg.act.p[1]);
814 rule->arg.http_deny.status = status;
815 rule->arg.http_deny.errmsg = http_errs->errmsg[http_get_status_idx(status)];
816 if (!rule->arg.http_deny.errmsg)
817 ha_warning("Proxy '%s': status '%d' referenced by http deny rule "
818 "not declared in http-errors section '%s'.\n",
819 px->id, status, http_errs->id);
820 break;
821 }
822 }
823
824 if (&http_errs->list == &http_errors_list) {
825 memprintf(err, "unknown http-errors section '%s' referenced by http deny rule",
826 (char *)rule->arg.act.p[1]);
827 free(rule->arg.act.p[1]);
828 ret = 0;
829 }
830
831 return ret;
832}
833
Christopher Faulete0fca292020-01-13 21:49:03 +0100834/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100835 * response rule. It may take optional arguments to define the status code, the
836 * error file or the http-errors section to use. It returns ACT_RET_PRS_OK on
837 * success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +0100838 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100839static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
840 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100841{
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100842 int default_status, status, hc, cur_arg;
843
Christopher Faulet81e20172019-12-12 16:40:30 +0100844
845 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100846 if (rule->from == ACT_F_HTTP_REQ) {
847 if (!strcmp(args[cur_arg-1], "tarpit")) {
848 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100849 default_status = status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100850 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100851 else {
852 rule->action = ACT_ACTION_DENY;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100853 default_status = status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100854 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100855 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100856 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100857 rule->action = ACT_ACTION_DENY;
858 default_status = status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100859 }
Christopher Faulet245cf792019-12-18 14:58:12 +0100860 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100861
862 if (strcmp(args[cur_arg], "deny_status") == 0) {
863 cur_arg++;
864 if (!*args[cur_arg]) {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100865 memprintf(err, "'%s' expects <status_code> as argument", args[cur_arg-1]);
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100866 return ACT_RET_PRS_ERR;
867 }
868
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100869 status = atol(args[cur_arg]);
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100870 cur_arg++;
871 for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100872 if (http_err_codes[hc] == status)
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100873 break;
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100874 }
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100875 if (hc >= HTTP_ERR_SIZE) {
876 memprintf(err, "status code '%d' not handled, using default code '%d'",
877 status, default_status);
878 status = default_status;
879 hc = http_get_status_idx(status);
880 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100881 }
882
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100883 if (strcmp(args[cur_arg], "errorfile") == 0) {
884 cur_arg++;
885 if (!*args[cur_arg]) {
886 memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
887 return ACT_RET_PRS_ERR;
888 }
889
890 rule->arg.http_deny.errmsg = http_load_errorfile(args[cur_arg], err);
891 if (!rule->arg.http_deny.errmsg)
892 return ACT_RET_PRS_ERR;
893 cur_arg++;
894 }
895 else if (strcmp(args[cur_arg], "errorfiles") == 0) {
896 cur_arg++;
897 if (!*args[cur_arg]) {
898 memprintf(err, "'%s' expects <http_errors_name> as argument", args[cur_arg-1]);
899 return ACT_RET_PRS_ERR;
900 }
901 /* Must be resolved during the config validity check */
902 rule->arg.act.p[0] = (void *)((intptr_t)status);
903 rule->arg.act.p[1] = strdup(args[cur_arg]);
904 rule->check_ptr = check_http_deny_action;
905 cur_arg++;
906 goto out;
907 }
908
909 rule->arg.http_deny.status = status;
910
911 out:
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100912 *orig_arg = cur_arg;
Christopher Faulet81e20172019-12-12 16:40:30 +0100913 return ACT_RET_PRS_OK;
914}
915
916/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
917 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
918 */
919static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
920 struct act_rule *rule, char **err)
921{
922 int cur_arg;
923
924 rule->action = ACT_HTTP_REQ_AUTH;
Christopher Faulet245cf792019-12-18 14:58:12 +0100925 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100926 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100927
928 cur_arg = *orig_arg;
929 if (!strcmp(args[cur_arg], "realm")) {
930 cur_arg++;
931 if (!*args[cur_arg]) {
932 memprintf(err, "missing realm value.\n");
933 return ACT_RET_PRS_ERR;
934 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100935 rule->arg.http.str.ptr = strdup(args[cur_arg]);
936 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100937 cur_arg++;
938 }
939
Christopher Fauletc20b3712020-01-27 15:51:56 +0100940 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100941 *orig_arg = cur_arg;
942 return ACT_RET_PRS_OK;
943}
944
945/* Parse a "set-nice" action. It takes the nice value as argument. It returns
946 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
947 */
948static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
949 struct act_rule *rule, char **err)
950{
951 int cur_arg;
952
953 rule->action = ACT_HTTP_SET_NICE;
954
955 cur_arg = *orig_arg;
956 if (!*args[cur_arg]) {
957 memprintf(err, "expects exactly 1 argument (integer value)");
958 return ACT_RET_PRS_ERR;
959 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100960 rule->arg.http.i = atoi(args[cur_arg]);
961 if (rule->arg.http.i < -1024)
962 rule->arg.http.i = -1024;
963 else if (rule->arg.http.i > 1024)
964 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +0100965
Christopher Fauletc20b3712020-01-27 15:51:56 +0100966 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100967 *orig_arg = cur_arg + 1;
968 return ACT_RET_PRS_OK;
969}
970
971/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
972 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
973 */
974static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
975 struct act_rule *rule, char **err)
976{
977#ifdef IP_TOS
978 char *endp;
979 int cur_arg;
980
981 rule->action = ACT_HTTP_SET_TOS;
982
983 cur_arg = *orig_arg;
984 if (!*args[cur_arg]) {
985 memprintf(err, "expects exactly 1 argument (integer/hex value)");
986 return ACT_RET_PRS_ERR;
987 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100988 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100989 if (endp && *endp != '\0') {
990 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
991 return ACT_RET_PRS_ERR;
992 }
993
Christopher Fauletc20b3712020-01-27 15:51:56 +0100994 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100995 *orig_arg = cur_arg + 1;
996 return ACT_RET_PRS_OK;
997#else
998 memprintf(err, "not supported on this platform (IP_TOS undefined)");
999 return ACT_RET_PRS_ERR;
1000#endif
1001}
1002
1003/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1004 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1005 */
1006static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1007 struct act_rule *rule, char **err)
1008{
1009#ifdef SO_MARK
1010 char *endp;
1011 int cur_arg;
1012
1013 rule->action = ACT_HTTP_SET_MARK;
1014
1015 cur_arg = *orig_arg;
1016 if (!*args[cur_arg]) {
1017 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1018 return ACT_RET_PRS_ERR;
1019 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001020 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001021 if (endp && *endp != '\0') {
1022 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1023 return ACT_RET_PRS_ERR;
1024 }
1025
Christopher Fauletc20b3712020-01-27 15:51:56 +01001026 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001027 *orig_arg = cur_arg + 1;
1028 global.last_checks |= LSTCHK_NETADM;
1029 return ACT_RET_PRS_OK;
1030#else
1031 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1032 return ACT_RET_PRS_ERR;
1033#endif
1034}
1035
1036/* Parse a "set-log-level" action. It takes the level value as argument. It
1037 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1038 */
1039static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1040 struct act_rule *rule, char **err)
1041{
1042 int cur_arg;
1043
1044 rule->action = ACT_HTTP_SET_LOGL;
1045
1046 cur_arg = *orig_arg;
1047 if (!*args[cur_arg]) {
1048 bad_log_level:
1049 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1050 return ACT_RET_PRS_ERR;
1051 }
1052 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001053 rule->arg.http.i = -1;
1054 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001055 goto bad_log_level;
1056
Christopher Fauletc20b3712020-01-27 15:51:56 +01001057 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001058 *orig_arg = cur_arg + 1;
1059 return ACT_RET_PRS_OK;
1060}
1061
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001062/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1063 * 103 response header with <.arg.http.str> name and with a value built
1064 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001065 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001066 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1067 * block is added to terminate the response. On success, it returns
1068 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1069 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1070 * returned.
1071 */
1072static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1073 struct session *sess, struct stream *s, int flags)
1074{
1075 struct act_rule *prev_rule, *next_rule;
1076 struct channel *res = &s->res;
1077 struct htx *htx = htx_from_buf(&res->buf);
1078 struct buffer *value = alloc_trash_chunk();
1079 enum act_return ret = ACT_RET_CONT;
1080
1081 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1082 goto leave;
1083
1084 if (!value) {
1085 if (!(s->flags & SF_ERR_MASK))
1086 s->flags |= SF_ERR_RESOURCE;
1087 goto error;
1088 }
1089
1090 /* get previous and next rules */
1091 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1092 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1093
1094 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1095 * continue to add link to a previously started response */
1096 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1097 struct htx_sl *sl;
1098 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1099 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1100
1101 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1102 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1103 if (!sl)
1104 goto error;
1105 sl->info.res.status = 103;
1106 }
1107
1108 /* Add the HTTP Early Hint HTTP 103 response heade */
1109 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1110 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1111 goto error;
1112
1113 /* if it is the last rule or the next one is not an early-hint, terminate the current
1114 * response. */
1115 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001116 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1117 /* If an error occurred during an Early-hint rule,
1118 * remove the incomplete HTTP 103 response from the
1119 * buffer */
1120 goto error;
1121 }
1122
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001123 if (!http_forward_proxy_resp(s, 0))
1124 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001125 }
1126
1127 leave:
1128 free_trash_chunk(value);
1129 return ret;
1130
1131 error:
1132 /* If an error occurred during an Early-hint rule, remove the incomplete
1133 * HTTP 103 response from the buffer */
1134 channel_htx_truncate(res, htx);
1135 ret = ACT_RET_ERR;
1136 goto leave;
1137}
1138
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001139/* This function executes a set-header or add-header actions. It builds a string
1140 * in the trash from the specified format string. It finds the action to be
1141 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001142 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001143 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1144 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1145 * ACT_RET_ERR is returned.
1146 */
1147static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1148 struct session *sess, struct stream *s, int flags)
1149{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001150 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1151 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001152 enum act_return ret = ACT_RET_CONT;
1153 struct buffer *replace;
1154 struct http_hdr_ctx ctx;
1155 struct ist n, v;
1156
1157 replace = alloc_trash_chunk();
1158 if (!replace)
1159 goto fail_alloc;
1160
1161 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1162 n = rule->arg.http.str;
1163 v = ist2(replace->area, replace->data);
1164
1165 if (rule->action == 0) { // set-header
1166 /* remove all occurrences of the header */
1167 ctx.blk = NULL;
1168 while (http_find_header(htx, n, &ctx, 1))
1169 http_remove_header(htx, &ctx);
1170 }
1171
1172 /* Now add header */
1173 if (!http_add_header(htx, n, v))
1174 goto fail_rewrite;
1175
1176 leave:
1177 free_trash_chunk(replace);
1178 return ret;
1179
1180 fail_alloc:
1181 if (!(s->flags & SF_ERR_MASK))
1182 s->flags |= SF_ERR_RESOURCE;
1183 ret = ACT_RET_ERR;
1184 goto leave;
1185
1186 fail_rewrite:
1187 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1188 if (s->flags & SF_BE_ASSIGNED)
1189 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1190 if (sess->listener->counters)
1191 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1192 if (objt_server(s->target))
1193 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1194
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001195 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001196 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001197 if (!(s->flags & SF_ERR_MASK))
1198 s->flags |= SF_ERR_PRXCOND;
1199 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001200 goto leave;
1201}
1202
Christopher Faulet81e20172019-12-12 16:40:30 +01001203/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1204 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1205 * on success, ACT_RET_PRS_ERR on error.
1206 *
1207 * Note: same function is used for the request and the response. However
1208 * "early-hint" rules are only supported for request rules.
1209 */
1210static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1211 struct act_rule *rule, char **err)
1212{
Christopher Faulet81e20172019-12-12 16:40:30 +01001213 int cap, cur_arg;
1214
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001215 if (args[*orig_arg-1][0] == 'e') {
1216 rule->action = ACT_CUSTOM;
1217 rule->action_ptr = http_action_early_hint;
1218 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001219 else {
1220 if (args[*orig_arg-1][0] == 's')
1221 rule->action = 0; // set-header
1222 else
1223 rule->action = 1; // add-header
1224 rule->action_ptr = http_action_set_header;
1225 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001226 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001227
1228 cur_arg = *orig_arg;
1229 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1230 memprintf(err, "expects exactly 2 arguments");
1231 return ACT_RET_PRS_ERR;
1232 }
1233
Christopher Faulet81e20172019-12-12 16:40:30 +01001234
Christopher Faulet96bff762019-12-17 13:46:18 +01001235 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1236 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1237 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001238
1239 if (rule->from == ACT_F_HTTP_REQ) {
1240 px->conf.args.ctx = ARGC_HRQ;
1241 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1242 }
1243 else{
1244 px->conf.args.ctx = ARGC_HRS;
1245 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1246 }
1247
1248 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001249 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001250 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001251 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001252 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001253
1254 free(px->conf.lfs_file);
1255 px->conf.lfs_file = strdup(px->conf.args.file);
1256 px->conf.lfs_line = px->conf.args.line;
1257
1258 *orig_arg = cur_arg + 1;
1259 return ACT_RET_PRS_OK;
1260}
1261
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001262/* This function executes a replace-header or replace-value actions. It
1263 * builds a string in the trash from the specified format string. It finds
1264 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001265 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001266 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1267 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1268 * processing continue. Otherwsize ACT_RET_ERR is returned.
1269 */
1270static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1271 struct session *sess, struct stream *s, int flags)
1272{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001273 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1274 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001275 enum act_return ret = ACT_RET_CONT;
1276 struct buffer *replace;
1277 int r;
1278
1279 replace = alloc_trash_chunk();
1280 if (!replace)
1281 goto fail_alloc;
1282
1283 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1284
1285 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1286 if (r == -1)
1287 goto fail_rewrite;
1288
1289 leave:
1290 free_trash_chunk(replace);
1291 return ret;
1292
1293 fail_alloc:
1294 if (!(s->flags & SF_ERR_MASK))
1295 s->flags |= SF_ERR_RESOURCE;
1296 ret = ACT_RET_ERR;
1297 goto leave;
1298
1299 fail_rewrite:
1300 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1301 if (s->flags & SF_BE_ASSIGNED)
1302 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1303 if (sess->listener->counters)
1304 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1305 if (objt_server(s->target))
1306 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1307
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001308 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001309 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001310 if (!(s->flags & SF_ERR_MASK))
1311 s->flags |= SF_ERR_PRXCOND;
1312 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001313 goto leave;
1314}
1315
Christopher Faulet81e20172019-12-12 16:40:30 +01001316/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1317 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1318 * success, ACT_RET_PRS_ERR on error.
1319 */
1320static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1321 struct act_rule *rule, char **err)
1322{
1323 int cap, cur_arg;
1324
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001325 if (args[*orig_arg-1][8] == 'h')
1326 rule->action = 0; // replace-header
1327 else
1328 rule->action = 1; // replace-value
1329 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001330 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001331
1332 cur_arg = *orig_arg;
1333 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1334 memprintf(err, "expects exactly 3 arguments");
1335 return ACT_RET_PRS_ERR;
1336 }
1337
Christopher Faulet96bff762019-12-17 13:46:18 +01001338 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1339 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1340 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001341
1342 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001343 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001344 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001345 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001346 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001347
1348 if (rule->from == ACT_F_HTTP_REQ) {
1349 px->conf.args.ctx = ARGC_HRQ;
1350 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1351 }
1352 else{
1353 px->conf.args.ctx = ARGC_HRS;
1354 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1355 }
1356
1357 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001358 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001359 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001360 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001361 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001362 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001363
1364 free(px->conf.lfs_file);
1365 px->conf.lfs_file = strdup(px->conf.args.file);
1366 px->conf.lfs_line = px->conf.args.line;
1367
1368 *orig_arg = cur_arg + 1;
1369 return ACT_RET_PRS_OK;
1370}
1371
1372/* Parse a "del-header" action. It takes an header name as argument. It returns
1373 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1374 */
1375static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1376 struct act_rule *rule, char **err)
1377{
1378 int cur_arg;
1379
1380 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001381 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001382
1383 cur_arg = *orig_arg;
1384 if (!*args[cur_arg]) {
1385 memprintf(err, "expects exactly 1 arguments");
1386 return ACT_RET_PRS_ERR;
1387 }
1388
Christopher Faulet96bff762019-12-17 13:46:18 +01001389 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1390 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001391 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1392
Christopher Fauletc20b3712020-01-27 15:51:56 +01001393 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001394 *orig_arg = cur_arg + 1;
1395 return ACT_RET_PRS_OK;
1396}
1397
Christopher Faulet2eb53962020-01-14 14:47:34 +01001398/* Release memory allocated by an http redirect action. */
1399static void release_http_redir(struct act_rule *rule)
1400{
1401 struct logformat_node *lf, *lfb;
1402 struct redirect_rule *redir;
1403
1404 redir = rule->arg.redir;
1405 LIST_DEL(&redir->list);
1406 if (redir->cond) {
1407 prune_acl_cond(redir->cond);
1408 free(redir->cond);
1409 }
1410 free(redir->rdr_str);
1411 free(redir->cookie_str);
1412 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1413 LIST_DEL(&lf->list);
1414 free(lf);
1415 }
1416 free(redir);
1417}
1418
Christopher Faulet81e20172019-12-12 16:40:30 +01001419/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1420 * ACT_RET_PRS_ERR on error.
1421 */
1422static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1423 struct act_rule *rule, char **err)
1424{
1425 struct redirect_rule *redir;
1426 int dir, cur_arg;
1427
1428 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001429 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001430 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001431
1432 cur_arg = *orig_arg;
1433
1434 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1435 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1436 return ACT_RET_PRS_ERR;
1437
1438 rule->arg.redir = redir;
1439 rule->cond = redir->cond;
1440 redir->cond = NULL;
1441
1442 /* skip all arguments */
1443 while (*args[cur_arg])
1444 cur_arg++;
1445
1446 *orig_arg = cur_arg;
1447 return ACT_RET_PRS_OK;
1448}
1449
Christopher Faulet046cf442019-12-17 15:45:23 +01001450/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1451 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1452 */
1453static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1454 struct session *sess, struct stream *s, int flags)
1455{
1456 struct pat_ref *ref;
1457 struct buffer *key = NULL, *value = NULL;
1458 enum act_return ret = ACT_RET_CONT;
1459
1460 /* collect reference */
1461 ref = pat_ref_lookup(rule->arg.map.ref);
1462 if (!ref)
1463 goto leave;
1464
1465 /* allocate key */
1466 key = alloc_trash_chunk();
1467 if (!key)
1468 goto fail_alloc;
1469
1470 /* collect key */
1471 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1472 key->area[key->data] = '\0';
1473
1474 switch (rule->action) {
1475 case 0: // add-acl
1476 /* add entry only if it does not already exist */
1477 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1478 if (pat_ref_find_elt(ref, key->area) == NULL)
1479 pat_ref_add(ref, key->area, NULL, NULL);
1480 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1481 break;
1482
1483 case 1: // set-map
1484 /* allocate value */
1485 value = alloc_trash_chunk();
1486 if (!value)
1487 goto fail_alloc;
1488
1489 /* collect value */
1490 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1491 value->area[value->data] = '\0';
1492
1493 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1494 if (pat_ref_find_elt(ref, key->area) != NULL) {
1495 /* update entry if it exists */
1496 pat_ref_set(ref, key->area, value->area, NULL);
1497 }
1498 else {
1499 /* insert a new entry */
1500 pat_ref_add(ref, key->area, value->area, NULL);
1501 }
1502 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1503 break;
1504
1505 case 2: // del-acl
1506 case 3: // del-map
1507 /* returned code: 1=ok, 0=ko */
1508 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1509 pat_ref_delete(ref, key->area);
1510 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1511 break;
1512
1513 default:
1514 ret = ACT_RET_ERR;
1515 }
1516
1517
1518 leave:
1519 free_trash_chunk(key);
1520 free_trash_chunk(value);
1521 return ret;
1522
1523 fail_alloc:
1524 if (!(s->flags & SF_ERR_MASK))
1525 s->flags |= SF_ERR_RESOURCE;
1526 ret = ACT_RET_ERR;
1527 goto leave;
1528}
1529
Christopher Faulet2eb53962020-01-14 14:47:34 +01001530/* Release memory allocated by an http map/acl action. */
1531static void release_http_map(struct act_rule *rule)
1532{
1533 struct logformat_node *lf, *lfb;
1534
1535 free(rule->arg.map.ref);
1536 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1537 LIST_DEL(&lf->list);
1538 release_sample_expr(lf->expr);
1539 free(lf->arg);
1540 free(lf);
1541 }
1542 if (rule->action == 1) {
1543 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1544 LIST_DEL(&lf->list);
1545 release_sample_expr(lf->expr);
1546 free(lf->arg);
1547 free(lf);
1548 }
1549 }
1550}
1551
Christopher Faulet81e20172019-12-12 16:40:30 +01001552/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001553 * two log-format string as argument depending on the action. The action is
1554 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1555 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001556 */
1557static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1558 struct act_rule *rule, char **err)
1559{
1560 int cap, cur_arg;
1561
Christopher Faulet046cf442019-12-17 15:45:23 +01001562 if (args[*orig_arg-1][0] == 'a') // add-acl
1563 rule->action = 0;
1564 else if (args[*orig_arg-1][0] == 's') // set-map
1565 rule->action = 1;
1566 else if (args[*orig_arg-1][4] == 'a') // del-acl
1567 rule->action = 2;
1568 else if (args[*orig_arg-1][4] == 'm') // del-map
1569 rule->action = 3;
1570 else {
1571 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1572 return ACT_RET_PRS_ERR;
1573 }
1574 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001575 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001576
1577 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001578 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1579 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001580 memprintf(err, "expects exactly 2 arguments");
1581 return ACT_RET_PRS_ERR;
1582 }
1583 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001584 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001585 memprintf(err, "expects exactly 1 arguments");
1586 return ACT_RET_PRS_ERR;
1587 }
1588
1589 /*
1590 * '+ 8' for 'set-map(' (same for del-map)
1591 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1592 */
1593 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1594
1595 if (rule->from == ACT_F_HTTP_REQ) {
1596 px->conf.args.ctx = ARGC_HRQ;
1597 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1598 }
1599 else{
1600 px->conf.args.ctx = ARGC_HRS;
1601 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1602 }
1603
1604 /* key pattern */
1605 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001606 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1607 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001608 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001609 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001610
Christopher Faulet046cf442019-12-17 15:45:23 +01001611 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001612 /* value pattern for set-map only */
1613 cur_arg++;
1614 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001615 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1616 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001617 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001618 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001619 }
1620
1621 free(px->conf.lfs_file);
1622 px->conf.lfs_file = strdup(px->conf.args.file);
1623 px->conf.lfs_line = px->conf.args.line;
1624
1625 *orig_arg = cur_arg + 1;
1626 return ACT_RET_PRS_OK;
1627}
1628
Christopher Fauletac98d812019-12-18 09:20:16 +01001629/* This function executes a track-sc* actions. On success, it returns
1630 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1631 */
1632static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1633 struct session *sess, struct stream *s, int flags)
1634{
1635 struct stktable *t;
1636 struct stksess *ts;
1637 struct stktable_key *key;
1638 void *ptr1, *ptr2, *ptr3, *ptr4;
1639 int opt;
1640
1641 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1642 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1643
1644 t = rule->arg.trk_ctr.table.t;
1645 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1646
1647 if (!key)
1648 goto end;
1649 ts = stktable_get_entry(t, key);
1650 if (!ts)
1651 goto end;
1652
1653 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1654
1655 /* let's count a new HTTP request as it's the first time we do it */
1656 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1657 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1658
1659 /* When the client triggers a 4xx from the server, it's most often due
1660 * to a missing object or permission. These events should be tracked
1661 * because if they happen often, it may indicate a brute force or a
1662 * vulnerability scan. Normally this is done when receiving the response
1663 * but here we're tracking after this ought to have been done so we have
1664 * to do it on purpose.
1665 */
1666 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1667 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1668 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1669 }
1670
1671 if (ptr1 || ptr2 || ptr3 || ptr4) {
1672 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1673
1674 if (ptr1)
1675 stktable_data_cast(ptr1, http_req_cnt)++;
1676 if (ptr2)
1677 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1678 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1679 if (ptr3)
1680 stktable_data_cast(ptr3, http_err_cnt)++;
1681 if (ptr4)
1682 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1683 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1684
1685 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1686
1687 /* If data was modified, we need to touch to re-schedule sync */
1688 stktable_touch_local(t, ts, 0);
1689 }
1690
1691 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1692 if (sess->fe != s->be)
1693 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1694
1695 end:
1696 return ACT_RET_CONT;
1697}
Christopher Faulet81e20172019-12-12 16:40:30 +01001698
Christopher Faulet2eb53962020-01-14 14:47:34 +01001699static void release_http_track_sc(struct act_rule *rule)
1700{
1701 release_sample_expr(rule->arg.trk_ctr.expr);
1702}
1703
Christopher Faulet81e20172019-12-12 16:40:30 +01001704/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1705 * ACT_RET_PRS_ERR on error.
1706 */
1707static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1708 struct act_rule *rule, char **err)
1709{
1710 struct sample_expr *expr;
1711 unsigned int where;
1712 unsigned int tsc_num;
1713 const char *tsc_num_str;
1714 int cur_arg;
1715
1716 tsc_num_str = &args[*orig_arg-1][8];
1717 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1718 return ACT_RET_PRS_ERR;
1719
1720 cur_arg = *orig_arg;
1721 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001722 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001723 if (!expr)
1724 return ACT_RET_PRS_ERR;
1725
1726 where = 0;
1727 if (px->cap & PR_CAP_FE)
1728 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1729 if (px->cap & PR_CAP_BE)
1730 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1731
1732 if (!(expr->fetch->val & where)) {
1733 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1734 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001735 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001736 return ACT_RET_PRS_ERR;
1737 }
1738
1739 if (strcmp(args[cur_arg], "table") == 0) {
1740 cur_arg++;
1741 if (!*args[cur_arg]) {
1742 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001743 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001744 return ACT_RET_PRS_ERR;
1745 }
1746
1747 /* we copy the table name for now, it will be resolved later */
1748 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1749 cur_arg++;
1750 }
1751
Christopher Fauletac98d812019-12-18 09:20:16 +01001752 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001753 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001754 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001755 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001756 rule->check_ptr = check_trk_action;
1757
1758 *orig_arg = cur_arg;
1759 return ACT_RET_PRS_OK;
1760}
1761
Christopher Faulet46f95542019-12-20 10:07:22 +01001762/* This function executes a strict-mode actions. On success, it always returns
1763 * ACT_RET_CONT
1764 */
1765static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1766 struct session *sess, struct stream *s, int flags)
1767{
1768 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1769
1770 if (rule->action == 0) // strict-mode on
1771 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1772 else // strict-mode off
1773 msg->flags |= HTTP_MSGF_SOFT_RW;
1774 return ACT_RET_CONT;
1775}
1776
1777/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1778 * ACT_RET_PRS_ERR on error.
1779 */
1780static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1781 struct act_rule *rule, char **err)
1782{
1783 int cur_arg;
1784
Christopher Faulet46f95542019-12-20 10:07:22 +01001785 cur_arg = *orig_arg;
1786 if (!*args[cur_arg]) {
1787 memprintf(err, "expects exactly 1 arguments");
1788 return ACT_RET_PRS_ERR;
1789 }
1790
1791 if (strcasecmp(args[cur_arg], "on") == 0)
1792 rule->action = 0; // strict-mode on
1793 else if (strcasecmp(args[cur_arg], "off") == 0)
1794 rule->action = 1; // strict-mode off
1795 else {
1796 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1797 return ACT_RET_PRS_ERR;
1798 }
1799 rule->action_ptr = http_action_strict_mode;
1800
1801 *orig_arg = cur_arg + 1;
1802 return ACT_RET_PRS_OK;
1803}
1804
Christopher Faulet18630642020-05-12 18:57:28 +02001805/* Release <.arg.http_reply> */
Christopher Faulet24231ab2020-01-24 17:44:23 +01001806static void release_http_return(struct act_rule *rule)
1807{
Christopher Faulet18630642020-05-12 18:57:28 +02001808 release_http_reply(rule->arg.http_reply);
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001809 rule->arg.http_reply = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001810}
1811
1812/* This function executes a return action. It builds an HTX message from an
1813 * errorfile, an raw file or a log-format string, depending on <.action>
1814 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1815 * returned.
1816 */
1817static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1818 struct session *sess, struct stream *s, int flags)
1819{
1820 struct channel *req = &s->req;
1821 struct channel *res = &s->res;
1822 struct buffer *errmsg;
1823 struct htx *htx = htx_from_buf(&res->buf);
1824 struct htx_sl *sl;
1825 struct buffer *body = NULL;
1826 const char *status, *reason, *clen, *ctype;
1827 unsigned int slflags;
Christopher Faulet90d22a82020-03-06 11:18:39 +01001828 enum act_return ret = ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001829
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001830 s->txn->status = rule->arg.http_reply->status;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001831 channel_htx_truncate(res, htx);
1832
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001833 /* HTTP_REPLY_ERRFILES unexpected here. handled as no payload if so */
1834
1835 if (rule->arg.http_reply->type == HTTP_REPLY_ERRMSG) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001836 /* implicit or explicit error message*/
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001837 errmsg = rule->arg.http_reply->body.errmsg;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001838 if (!errmsg) {
1839 /* get default error message */
1840 errmsg = http_error_message(s);
1841 }
1842 if (b_is_null(errmsg))
1843 goto end;
1844 if (!channel_htx_copy_msg(res, htx, errmsg))
1845 goto fail;
1846 }
1847 else {
1848 /* no payload, file or log-format string */
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001849 if (rule->arg.http_reply->type == HTTP_REPLY_RAW) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001850 /* file */
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001851 body = &rule->arg.http_reply->body.obj;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001852 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001853 else if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001854 /* log-format string */
1855 body = alloc_trash_chunk();
1856 if (!body)
1857 goto fail_alloc;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001858 body->data = build_logline(s, body->area, body->size, &rule->arg.http_reply->body.fmt);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001859 }
1860 /* else no payload */
1861
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001862 status = ultoa(rule->arg.http_reply->status);
1863 reason = http_get_reason(rule->arg.http_reply->status);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001864 slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1865 if (!body || !b_data(body))
1866 slflags |= HTX_SL_F_BODYLESS;
1867 sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason));
1868 if (!sl)
1869 goto fail;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001870 sl->info.res.status = rule->arg.http_reply->status;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001871
1872 clen = (body ? ultoa(b_data(body)) : "0");
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001873 ctype = rule->arg.http_reply->ctype;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001874
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001875 if (!LIST_ISEMPTY(&rule->arg.http_reply->hdrs)) {
1876 struct http_reply_hdr *hdr;
Christopher Faulet4a2c1422020-01-31 17:36:01 +01001877 struct buffer *value = alloc_trash_chunk();
1878
1879 if (!value)
1880 goto fail;
1881
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001882 list_for_each_entry(hdr, &rule->arg.http_reply->hdrs, list) {
Christopher Faulet4a2c1422020-01-31 17:36:01 +01001883 chunk_reset(value);
1884 value->data = build_logline(s, value->area, value->size, &hdr->value);
1885 if (b_data(value) && !htx_add_header(htx, hdr->name, ist2(b_head(value), b_data(value)))) {
1886 free_trash_chunk(value);
1887 goto fail;
1888 }
1889 chunk_reset(value);
1890 }
1891 free_trash_chunk(value);
1892 }
1893
Christopher Faulet24231ab2020-01-24 17:44:23 +01001894 if (!htx_add_header(htx, ist("content-length"), ist(clen)) ||
1895 (body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) ||
1896 !htx_add_endof(htx, HTX_BLK_EOH) ||
1897 (body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body)))) ||
1898 !htx_add_endof(htx, HTX_BLK_EOM))
1899 goto fail;
1900 }
1901
1902 htx_to_buf(htx, &s->res.buf);
1903 if (!http_forward_proxy_resp(s, 1))
1904 goto fail;
1905
1906 end:
1907 if (rule->from == ACT_F_HTTP_REQ) {
1908 /* let's log the request time */
1909 s->logs.tv_request = now;
1910 req->analysers &= AN_REQ_FLT_END;
1911
1912 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1913 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1914 }
1915
1916 if (!(s->flags & SF_ERR_MASK))
1917 s->flags |= SF_ERR_LOCAL;
1918 if (!(s->flags & SF_FINST_MASK))
1919 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1920
1921 leave:
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001922 if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT)
Christopher Faulet24231ab2020-01-24 17:44:23 +01001923 free_trash_chunk(body);
1924 return ret;
1925
1926 fail_alloc:
1927 if (!(s->flags & SF_ERR_MASK))
1928 s->flags |= SF_ERR_RESOURCE;
1929 ret = ACT_RET_ERR;
1930 goto leave;
1931
1932 fail:
1933 /* If an error occurred, remove the incomplete HTTP response from the
1934 * buffer */
1935 channel_htx_truncate(res, htx);
1936 ret = ACT_RET_ERR;
1937 if (!(s->flags & SF_ERR_MASK))
1938 s->flags |= SF_ERR_PRXCOND;
1939 goto leave;
1940}
1941
Christopher Faulet7eea2412020-05-13 15:02:59 +02001942/* Check an "http-request return" action. The function returns 1 in success
1943 * case, otherwise, it returns 0 and err is filled.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001944 */
1945static int check_http_return_action(struct act_rule *rule, struct proxy *px, char **err)
1946{
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001947 struct http_reply *reply = rule->arg.http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001948
Christopher Faulet7eea2412020-05-13 15:02:59 +02001949 if (!http_check_http_reply(reply, px, err)) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001950 release_http_return(rule);
Christopher Faulet7eea2412020-05-13 15:02:59 +02001951 return 0;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001952 }
Christopher Faulet7eea2412020-05-13 15:02:59 +02001953 return 1;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001954}
1955
1956/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001957 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1958 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001959 */
1960static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1961 struct act_rule *rule, char **err)
1962{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001963 /* Prepare parsing of log-format strings */
1964 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1965 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1966 if (!rule->arg.http_reply)
1967 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001968
Christopher Fauletba946bf2020-05-13 08:50:07 +02001969 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001970 rule->action = ACT_CUSTOM;
1971 rule->check_ptr = check_http_return_action;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001972 rule->action_ptr = http_action_return;
1973 rule->release_ptr = release_http_return;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001974 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001975}
1976
Willy Tarreau79e57332018-10-02 16:01:16 +02001977/************************************************************************/
1978/* All supported http-request action keywords must be declared here. */
1979/************************************************************************/
1980
1981static struct action_kw_list http_req_actions = {
1982 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001983 { "add-acl", parse_http_set_map, 1 },
1984 { "add-header", parse_http_set_header, 0 },
1985 { "allow", parse_http_allow, 0 },
1986 { "auth", parse_http_auth, 0 },
1987 { "capture", parse_http_req_capture, 0 },
1988 { "del-acl", parse_http_set_map, 1 },
1989 { "del-header", parse_http_del_header, 0 },
1990 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001991 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001992 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1993 { "early-hint", parse_http_set_header, 0 },
1994 { "redirect", parse_http_redirect, 0 },
1995 { "reject", parse_http_action_reject, 0 },
1996 { "replace-header", parse_http_replace_header, 0 },
1997 { "replace-path", parse_replace_uri, 0 },
1998 { "replace-uri", parse_replace_uri, 0 },
1999 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002000 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002001 { "set-header", parse_http_set_header, 0 },
2002 { "set-log-level", parse_http_set_log_level, 0 },
2003 { "set-map", parse_http_set_map, 1 },
2004 { "set-method", parse_set_req_line, 0 },
2005 { "set-mark", parse_http_set_mark, 0 },
2006 { "set-nice", parse_http_set_nice, 0 },
2007 { "set-path", parse_set_req_line, 0 },
2008 { "set-query", parse_set_req_line, 0 },
2009 { "set-tos", parse_http_set_tos, 0 },
2010 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002011 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002012 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002013 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002014 { NULL, NULL }
2015 }
2016};
2017
Willy Tarreau0108d902018-11-25 19:14:37 +01002018INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
2019
Willy Tarreau79e57332018-10-02 16:01:16 +02002020static struct action_kw_list http_res_actions = {
2021 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002022 { "add-acl", parse_http_set_map, 1 },
2023 { "add-header", parse_http_set_header, 0 },
2024 { "allow", parse_http_allow, 0 },
2025 { "capture", parse_http_res_capture, 0 },
2026 { "del-acl", parse_http_set_map, 1 },
2027 { "del-header", parse_http_del_header, 0 },
2028 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002029 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002030 { "redirect", parse_http_redirect, 0 },
2031 { "replace-header", parse_http_replace_header, 0 },
2032 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002033 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002034 { "set-header", parse_http_set_header, 0 },
2035 { "set-log-level", parse_http_set_log_level, 0 },
2036 { "set-map", parse_http_set_map, 1 },
2037 { "set-mark", parse_http_set_mark, 0 },
2038 { "set-nice", parse_http_set_nice, 0 },
2039 { "set-status", parse_http_set_status, 0 },
2040 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002041 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002042 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002043 { NULL, NULL }
2044 }
2045};
2046
Willy Tarreau0108d902018-11-25 19:14:37 +01002047INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02002048
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01002049static struct action_kw_list http_after_res_actions = {
2050 .kw = {
2051 { "add-header", parse_http_set_header, 0 },
2052 { "allow", parse_http_allow, 0 },
2053 { "del-header", parse_http_del_header, 0 },
2054 { "replace-header", parse_http_replace_header, 0 },
2055 { "replace-value", parse_http_replace_header, 0 },
2056 { "set-header", parse_http_set_header, 0 },
2057 { "set-status", parse_http_set_status, 0 },
2058 { "strict-mode", parse_http_strict_mode, 0 },
2059 { NULL, NULL }
2060 }
2061};
2062
2063INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
2064
Willy Tarreau79e57332018-10-02 16:01:16 +02002065/*
2066 * Local variables:
2067 * c-indent-level: 8
2068 * c-basic-offset: 8
2069 * End:
2070 */