blob: 04c27eaee684dada3027916109a609d4af357c50 [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>
Christopher Faulet24231ab2020-01-24 17:44:23 +010014#include <sys/stat.h>
15#include <fcntl.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020016
17#include <ctype.h>
18#include <string.h>
19#include <time.h>
20
Christopher Faulet81e20172019-12-12 16:40:30 +010021#include <common/cfgparse.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020022#include <common/chunk.h>
23#include <common/compat.h>
24#include <common/config.h>
25#include <common/debug.h>
26#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010027#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020028#include <common/memory.h>
29#include <common/standard.h>
30#include <common/version.h>
31
32#include <types/capture.h>
33#include <types/global.h>
34
35#include <proto/acl.h>
36#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010037#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020038#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020039#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020040#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020041#include <proto/http_ana.h>
Christopher Faulet046cf442019-12-17 15:45:23 +010042#include <proto/pattern.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010043#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020044
Christopher Faulet2eb53962020-01-14 14:47:34 +010045/* Release memory allocated by most of HTTP actions. Concretly, it releases
46 * <arg.http>.
47 */
48static void release_http_action(struct act_rule *rule)
49{
50 struct logformat_node *lf, *lfb;
51
Tim Duesterhused526372020-03-05 17:56:33 +010052 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010053 if (rule->arg.http.re)
54 regex_free(rule->arg.http.re);
55 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
56 LIST_DEL(&lf->list);
57 release_sample_expr(lf->expr);
58 free(lf->arg);
59 free(lf);
60 }
61}
62
Willy Tarreau79e57332018-10-02 16:01:16 +020063
64/* This function executes one of the set-{method,path,query,uri} actions. It
65 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010066 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050067 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010068 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
69 * occurs while soft rewrites are enabled, the action is canceled, but the rule
70 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020071 */
72static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
73 struct session *sess, struct stream *s, int flags)
74{
75 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010076 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020077
78 replace = alloc_trash_chunk();
79 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +010080 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +020081
82 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +010083 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +020084 replace->area[replace->data++] = '?';
85 replace->data += build_logline(s, replace->area + replace->data,
86 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +010087 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +020088
Christopher Faulet2c22a692019-12-18 15:39:56 +010089 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +010090 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +020091
Christopher Faulete00d06c2019-12-16 17:18:42 +010092 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +020093 free_trash_chunk(replace);
94 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +010095
96 fail_alloc:
97 if (!(s->flags & SF_ERR_MASK))
98 s->flags |= SF_ERR_RESOURCE;
99 ret = ACT_RET_ERR;
100 goto leave;
101
102 fail_rewrite:
103 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
104 if (s->flags & SF_BE_ASSIGNED)
105 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
106 if (sess->listener->counters)
107 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
108 if (objt_server(s->target))
109 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
110
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100111 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100112 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100113 if (!(s->flags & SF_ERR_MASK))
114 s->flags |= SF_ERR_PRXCOND;
115 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100116 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200117}
118
119/* parse an http-request action among :
120 * set-method
121 * set-path
122 * set-query
123 * set-uri
124 *
125 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100126 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100127 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
128 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200129 */
130static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
131 struct act_rule *rule, char **err)
132{
133 int cur_arg = *orig_arg;
134
Willy Tarreau79e57332018-10-02 16:01:16 +0200135 switch (args[0][4]) {
136 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100137 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200138 break;
139 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100140 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200141 break;
142 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100143 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200144 break;
145 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100146 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200147 break;
148 default:
149 memprintf(err, "internal error: unhandled action '%s'", args[0]);
150 return ACT_RET_PRS_ERR;
151 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100152 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100153 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200154
155 if (!*args[cur_arg] ||
156 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
157 memprintf(err, "expects exactly 1 argument <format>");
158 return ACT_RET_PRS_ERR;
159 }
160
Christopher Faulet96bff762019-12-17 13:46:18 +0100161 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200162 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100163 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200164 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
165 return ACT_RET_PRS_ERR;
166 }
167
168 (*orig_arg)++;
169 return ACT_RET_PRS_OK;
170}
171
Willy Tarreau33810222019-06-12 17:44:02 +0200172/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100173 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200174 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100175 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100176 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100177 * contains 1 for the path or 3 for the URI (values used by
178 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
179 * occurs while soft rewrites are enabled, the action is canceled, but the rule
180 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200181 */
182static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
183 struct session *sess, struct stream *s, int flags)
184{
Christopher Faulet13403762019-12-13 09:01:57 +0100185 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200186 struct buffer *replace, *output;
187 struct ist uri;
188 int len;
189
190 replace = alloc_trash_chunk();
191 output = alloc_trash_chunk();
192 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100193 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200194 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100195
Christopher Faulet2c22a692019-12-18 15:39:56 +0100196 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100197 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100198
Christopher Faulet96bff762019-12-17 13:46:18 +0100199 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200200 goto leave;
201
Christopher Faulet96bff762019-12-17 13:46:18 +0100202 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200203
204 /* note: uri.ptr doesn't need to be zero-terminated because it will
205 * only be used to pick pmatch references.
206 */
207 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
208 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100209 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200210
Christopher Faulet2c22a692019-12-18 15:39:56 +0100211 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100212 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200213
Christopher Faulete00d06c2019-12-16 17:18:42 +0100214 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200215 free_trash_chunk(output);
216 free_trash_chunk(replace);
217 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100218
219 fail_alloc:
220 if (!(s->flags & SF_ERR_MASK))
221 s->flags |= SF_ERR_RESOURCE;
222 ret = ACT_RET_ERR;
223 goto leave;
224
225 fail_rewrite:
226 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
227 if (s->flags & SF_BE_ASSIGNED)
228 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
229 if (sess->listener->counters)
230 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
231 if (objt_server(s->target))
232 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
233
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100234 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100235 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100236 if (!(s->flags & SF_ERR_MASK))
237 s->flags |= SF_ERR_PRXCOND;
238 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100239 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200240}
241
Willy Tarreau262c3f12019-12-17 06:52:51 +0100242/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200243 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100244 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100245 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200246 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
247 */
248static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
249 struct act_rule *rule, char **err)
250{
251 int cur_arg = *orig_arg;
252 char *error = NULL;
253
Willy Tarreau262c3f12019-12-17 06:52:51 +0100254 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100255 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100256 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100257 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100258
Willy Tarreau33810222019-06-12 17:44:02 +0200259 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100260 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200261
262 if (!*args[cur_arg] || !*args[cur_arg+1] ||
263 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
264 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
265 return ACT_RET_PRS_ERR;
266 }
267
Christopher Faulet96bff762019-12-17 13:46:18 +0100268 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200269 memprintf(err, "failed to parse the regex : %s", error);
270 free(error);
271 return ACT_RET_PRS_ERR;
272 }
273
Christopher Faulet96bff762019-12-17 13:46:18 +0100274 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200275 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100276 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200277 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100278 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200279 return ACT_RET_PRS_ERR;
280 }
281
282 (*orig_arg) += 2;
283 return ACT_RET_PRS_OK;
284}
285
Willy Tarreau79e57332018-10-02 16:01:16 +0200286/* This function is just a compliant action wrapper for "set-status". */
287static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
288 struct session *sess, struct stream *s, int flags)
289{
Christopher Faulet96bff762019-12-17 13:46:18 +0100290 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100291 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
292 if (s->flags & SF_BE_ASSIGNED)
293 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
294 if (sess->listener->counters)
295 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
296 if (objt_server(s->target))
297 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
298
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100299 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100300 if (!(s->flags & SF_ERR_MASK))
301 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100302 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100303 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100304 }
305
Willy Tarreau79e57332018-10-02 16:01:16 +0200306 return ACT_RET_CONT;
307}
308
309/* parse set-status action:
310 * This action accepts a single argument of type int representing
311 * an http status code. It returns ACT_RET_PRS_OK on success,
312 * ACT_RET_PRS_ERR on error.
313 */
314static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
315 struct act_rule *rule, char **err)
316{
317 char *error;
318
319 rule->action = ACT_CUSTOM;
320 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100321 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200322
323 /* Check if an argument is available */
324 if (!*args[*orig_arg]) {
325 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
326 return ACT_RET_PRS_ERR;
327 }
328
329 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100330 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
331 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200332 memprintf(err, "expects an integer status code between 100 and 999");
333 return ACT_RET_PRS_ERR;
334 }
335
336 (*orig_arg)++;
337
338 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100339 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200340 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
341 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
342 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100343 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
344 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200345 (*orig_arg)++;
346 }
347
Christopher Fauletc20b3712020-01-27 15:51:56 +0100348 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200349 return ACT_RET_PRS_OK;
350}
351
352/* This function executes the "reject" HTTP action. It clears the request and
353 * response buffer without sending any response. It can be useful as an HTTP
354 * alternative to the silent-drop action to defend against DoS attacks, and may
355 * also be used with HTTP/2 to close a connection instead of just a stream.
356 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100357 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200358 */
359static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
360 struct session *sess, struct stream *s, int flags)
361{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100362 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200363 channel_abort(&s->req);
364 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100365 s->req.analysers &= AN_REQ_FLT_END;
366 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200367
Olivier Houcharda798bf52019-03-08 18:52:00 +0100368 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
369 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200370 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100371 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200372
373 if (!(s->flags & SF_ERR_MASK))
374 s->flags |= SF_ERR_PRXCOND;
375 if (!(s->flags & SF_FINST_MASK))
376 s->flags |= SF_FINST_R;
377
Christopher Faulet90d22a82020-03-06 11:18:39 +0100378 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200379}
380
381/* parse the "reject" action:
382 * This action takes no argument and returns ACT_RET_PRS_OK on success,
383 * ACT_RET_PRS_ERR on error.
384 */
385static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
386 struct act_rule *rule, char **err)
387{
388 rule->action = ACT_CUSTOM;
389 rule->action_ptr = http_action_reject;
390 return ACT_RET_PRS_OK;
391}
392
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200393/* This function executes the "disable-l7-retry" HTTP action.
394 * It disables L7 retries (all retry except for a connection failure). This
395 * can be useful for example to avoid retrying on POST requests.
396 * It just removes the L7 retry flag on the stream_interface, and always
397 * return ACT_RET_CONT;
398 */
399static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
400 struct session *sess, struct stream *s, int flags)
401{
402 struct stream_interface *si = &s->si[1];
403
404 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
405 * let's be future-proof and remove it anyway.
406 */
407 si->flags &= ~SI_FL_L7_RETRY;
408 si->flags |= SI_FL_D_L7_RETRY;
409 return ACT_RET_CONT;
410}
411
412/* parse the "disable-l7-retry" action:
413 * This action takes no argument and returns ACT_RET_PRS_OK on success,
414 * ACT_RET_PRS_ERR on error.
415 */
416static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
417 int *orig_args, struct proxy *px,
418 struct act_rule *rule, char **err)
419{
420 rule->action = ACT_CUSTOM;
421 rule->action_ptr = http_req_disable_l7_retry;
422 return ACT_RET_PRS_OK;
423}
424
Willy Tarreau79e57332018-10-02 16:01:16 +0200425/* This function executes the "capture" action. It executes a fetch expression,
426 * turns the result into a string and puts it in a capture slot. It always
427 * returns 1. If an error occurs the action is cancelled, but the rule
428 * processing continues.
429 */
430static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
431 struct session *sess, struct stream *s, int flags)
432{
433 struct sample *key;
434 struct cap_hdr *h = rule->arg.cap.hdr;
435 char **cap = s->req_cap;
436 int len;
437
438 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
439 if (!key)
440 return ACT_RET_CONT;
441
442 if (cap[h->index] == NULL)
443 cap[h->index] = pool_alloc(h->pool);
444
445 if (cap[h->index] == NULL) /* no more capture memory */
446 return ACT_RET_CONT;
447
448 len = key->data.u.str.data;
449 if (len > h->len)
450 len = h->len;
451
452 memcpy(cap[h->index], key->data.u.str.area, len);
453 cap[h->index][len] = 0;
454 return ACT_RET_CONT;
455}
456
457/* This function executes the "capture" action and store the result in a
458 * capture slot if exists. It executes a fetch expression, turns the result
459 * into a string and puts it in a capture slot. It always returns 1. If an
460 * error occurs the action is cancelled, but the rule processing continues.
461 */
462static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
463 struct session *sess, struct stream *s, int flags)
464{
465 struct sample *key;
466 struct cap_hdr *h;
467 char **cap = s->req_cap;
468 struct proxy *fe = strm_fe(s);
469 int len;
470 int i;
471
472 /* Look for the original configuration. */
473 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
474 h != NULL && i != rule->arg.capid.idx ;
475 i--, h = h->next);
476 if (!h)
477 return ACT_RET_CONT;
478
479 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
480 if (!key)
481 return ACT_RET_CONT;
482
483 if (cap[h->index] == NULL)
484 cap[h->index] = pool_alloc(h->pool);
485
486 if (cap[h->index] == NULL) /* no more capture memory */
487 return ACT_RET_CONT;
488
489 len = key->data.u.str.data;
490 if (len > h->len)
491 len = h->len;
492
493 memcpy(cap[h->index], key->data.u.str.area, len);
494 cap[h->index][len] = 0;
495 return ACT_RET_CONT;
496}
497
498/* Check an "http-request capture" action.
499 *
500 * The function returns 1 in success case, otherwise, it returns 0 and err is
501 * filled.
502 */
503static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
504{
505 if (rule->action_ptr != http_action_req_capture_by_id)
506 return 1;
507
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100508 /* capture slots can only be declared in frontends, so we can't check their
509 * existence in backends at configuration parsing step
510 */
511 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200512 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
513 rule->arg.capid.idx);
514 return 0;
515 }
516
517 return 1;
518}
519
Christopher Faulet2eb53962020-01-14 14:47:34 +0100520/* Release memory allocate by an http capture action */
521static void release_http_capture(struct act_rule *rule)
522{
523 if (rule->action_ptr == http_action_req_capture)
524 release_sample_expr(rule->arg.cap.expr);
525 else
526 release_sample_expr(rule->arg.capid.expr);
527}
528
Willy Tarreau79e57332018-10-02 16:01:16 +0200529/* parse an "http-request capture" action. It takes a single argument which is
530 * a sample fetch expression. It stores the expression into arg->act.p[0] and
531 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
532 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
533 */
534static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
535 struct act_rule *rule, char **err)
536{
537 struct sample_expr *expr;
538 struct cap_hdr *hdr;
539 int cur_arg;
540 int len = 0;
541
542 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
543 if (strcmp(args[cur_arg], "if") == 0 ||
544 strcmp(args[cur_arg], "unless") == 0)
545 break;
546
547 if (cur_arg < *orig_arg + 3) {
548 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
549 return ACT_RET_PRS_ERR;
550 }
551
552 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100553 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 +0200554 if (!expr)
555 return ACT_RET_PRS_ERR;
556
557 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
558 memprintf(err,
559 "fetch method '%s' extracts information from '%s', none of which is available here",
560 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100561 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200562 return ACT_RET_PRS_ERR;
563 }
564
565 if (!args[cur_arg] || !*args[cur_arg]) {
566 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100567 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200568 return ACT_RET_PRS_ERR;
569 }
570
571 if (strcmp(args[cur_arg], "len") == 0) {
572 cur_arg++;
573
574 if (!(px->cap & PR_CAP_FE)) {
575 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100576 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200577 return ACT_RET_PRS_ERR;
578 }
579
580 px->conf.args.ctx = ARGC_CAP;
581
582 if (!args[cur_arg]) {
583 memprintf(err, "missing length value");
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 /* we copy the table name for now, it will be resolved later */
588 len = atoi(args[cur_arg]);
589 if (len <= 0) {
590 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100591 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200592 return ACT_RET_PRS_ERR;
593 }
594 cur_arg++;
595
Willy Tarreau79e57332018-10-02 16:01:16 +0200596 hdr = calloc(1, sizeof(*hdr));
597 hdr->next = px->req_cap;
598 hdr->name = NULL; /* not a header capture */
599 hdr->namelen = 0;
600 hdr->len = len;
601 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
602 hdr->index = px->nb_req_cap++;
603
604 px->req_cap = hdr;
605 px->to_log |= LW_REQHDR;
606
607 rule->action = ACT_CUSTOM;
608 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100609 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200610 rule->arg.cap.expr = expr;
611 rule->arg.cap.hdr = hdr;
612 }
613
614 else if (strcmp(args[cur_arg], "id") == 0) {
615 int id;
616 char *error;
617
618 cur_arg++;
619
620 if (!args[cur_arg]) {
621 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100622 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200623 return ACT_RET_PRS_ERR;
624 }
625
626 id = strtol(args[cur_arg], &error, 10);
627 if (*error != '\0') {
628 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100629 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200630 return ACT_RET_PRS_ERR;
631 }
632 cur_arg++;
633
634 px->conf.args.ctx = ARGC_CAP;
635
636 rule->action = ACT_CUSTOM;
637 rule->action_ptr = http_action_req_capture_by_id;
638 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100639 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200640 rule->arg.capid.expr = expr;
641 rule->arg.capid.idx = id;
642 }
643
644 else {
645 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100646 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200647 return ACT_RET_PRS_ERR;
648 }
649
650 *orig_arg = cur_arg;
651 return ACT_RET_PRS_OK;
652}
653
654/* This function executes the "capture" action and store the result in a
655 * capture slot if exists. It executes a fetch expression, turns the result
656 * into a string and puts it in a capture slot. It always returns 1. If an
657 * error occurs the action is cancelled, but the rule processing continues.
658 */
659static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
660 struct session *sess, struct stream *s, int flags)
661{
662 struct sample *key;
663 struct cap_hdr *h;
664 char **cap = s->res_cap;
665 struct proxy *fe = strm_fe(s);
666 int len;
667 int i;
668
669 /* Look for the original configuration. */
670 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
671 h != NULL && i != rule->arg.capid.idx ;
672 i--, h = h->next);
673 if (!h)
674 return ACT_RET_CONT;
675
676 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
677 if (!key)
678 return ACT_RET_CONT;
679
680 if (cap[h->index] == NULL)
681 cap[h->index] = pool_alloc(h->pool);
682
683 if (cap[h->index] == NULL) /* no more capture memory */
684 return ACT_RET_CONT;
685
686 len = key->data.u.str.data;
687 if (len > h->len)
688 len = h->len;
689
690 memcpy(cap[h->index], key->data.u.str.area, len);
691 cap[h->index][len] = 0;
692 return ACT_RET_CONT;
693}
694
695/* Check an "http-response capture" action.
696 *
697 * The function returns 1 in success case, otherwise, it returns 0 and err is
698 * filled.
699 */
700static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
701{
702 if (rule->action_ptr != http_action_res_capture_by_id)
703 return 1;
704
705 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
706 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
707 rule->arg.capid.idx);
708 return 0;
709 }
710
711 return 1;
712}
713
714/* parse an "http-response capture" action. It takes a single argument which is
715 * a sample fetch expression. It stores the expression into arg->act.p[0] and
716 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
717 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
718 */
719static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
720 struct act_rule *rule, char **err)
721{
722 struct sample_expr *expr;
723 int cur_arg;
724 int id;
725 char *error;
726
727 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
728 if (strcmp(args[cur_arg], "if") == 0 ||
729 strcmp(args[cur_arg], "unless") == 0)
730 break;
731
732 if (cur_arg < *orig_arg + 3) {
733 memprintf(err, "expects <expression> id <idx>");
734 return ACT_RET_PRS_ERR;
735 }
736
737 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100738 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 +0200739 if (!expr)
740 return ACT_RET_PRS_ERR;
741
742 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
743 memprintf(err,
744 "fetch method '%s' extracts information from '%s', none of which is available here",
745 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100746 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200747 return ACT_RET_PRS_ERR;
748 }
749
750 if (!args[cur_arg] || !*args[cur_arg]) {
751 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100752 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200753 return ACT_RET_PRS_ERR;
754 }
755
756 if (strcmp(args[cur_arg], "id") != 0) {
757 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100758 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200759 return ACT_RET_PRS_ERR;
760 }
761
762 cur_arg++;
763
764 if (!args[cur_arg]) {
765 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100766 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200767 return ACT_RET_PRS_ERR;
768 }
769
770 id = strtol(args[cur_arg], &error, 10);
771 if (*error != '\0') {
772 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100773 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200774 return ACT_RET_PRS_ERR;
775 }
776 cur_arg++;
777
778 px->conf.args.ctx = ARGC_CAP;
779
780 rule->action = ACT_CUSTOM;
781 rule->action_ptr = http_action_res_capture_by_id;
782 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100783 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200784 rule->arg.capid.expr = expr;
785 rule->arg.capid.idx = id;
786
787 *orig_arg = cur_arg;
788 return ACT_RET_PRS_OK;
789}
790
Christopher Faulet81e20172019-12-12 16:40:30 +0100791/* Parse a "allow" action for a request or a response rule. It takes no argument. It
792 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
793 */
794static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
795 struct act_rule *rule, char **err)
796{
797 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100798 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100799 return ACT_RET_PRS_OK;
800}
801
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100802/* Check an "http-request deny" action when an http-errors section is referenced.
803 *
804 * The function returns 1 in success case, otherwise, it returns 0 and err is
805 * filled.
806 */
807static int check_http_deny_action(struct act_rule *rule, struct proxy *px, char **err)
808{
809 struct http_errors *http_errs;
810 int status = (intptr_t)(rule->arg.act.p[0]);
811 int ret = 1;
812
813 list_for_each_entry(http_errs, &http_errors_list, list) {
814 if (strcmp(http_errs->id, (char *)rule->arg.act.p[1]) == 0) {
815 free(rule->arg.act.p[1]);
816 rule->arg.http_deny.status = status;
817 rule->arg.http_deny.errmsg = http_errs->errmsg[http_get_status_idx(status)];
818 if (!rule->arg.http_deny.errmsg)
819 ha_warning("Proxy '%s': status '%d' referenced by http deny rule "
820 "not declared in http-errors section '%s'.\n",
821 px->id, status, http_errs->id);
822 break;
823 }
824 }
825
826 if (&http_errs->list == &http_errors_list) {
827 memprintf(err, "unknown http-errors section '%s' referenced by http deny rule",
828 (char *)rule->arg.act.p[1]);
829 free(rule->arg.act.p[1]);
830 ret = 0;
831 }
832
833 return ret;
834}
835
Christopher Faulete0fca292020-01-13 21:49:03 +0100836/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100837 * response rule. It may take optional arguments to define the status code, the
838 * error file or the http-errors section to use. It returns ACT_RET_PRS_OK on
839 * success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +0100840 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100841static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
842 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100843{
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100844 int default_status, status, hc, cur_arg;
845
Christopher Faulet81e20172019-12-12 16:40:30 +0100846
847 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100848 if (rule->from == ACT_F_HTTP_REQ) {
849 if (!strcmp(args[cur_arg-1], "tarpit")) {
850 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100851 default_status = status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100852 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100853 else {
854 rule->action = ACT_ACTION_DENY;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100855 default_status = status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100856 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100857 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100858 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100859 rule->action = ACT_ACTION_DENY;
860 default_status = status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100861 }
Christopher Faulet245cf792019-12-18 14:58:12 +0100862 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100863
864 if (strcmp(args[cur_arg], "deny_status") == 0) {
865 cur_arg++;
866 if (!*args[cur_arg]) {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100867 memprintf(err, "'%s' expects <status_code> as argument", args[cur_arg-1]);
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100868 return ACT_RET_PRS_ERR;
869 }
870
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100871 status = atol(args[cur_arg]);
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100872 cur_arg++;
873 for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100874 if (http_err_codes[hc] == status)
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100875 break;
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100876 }
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100877 if (hc >= HTTP_ERR_SIZE) {
878 memprintf(err, "status code '%d' not handled, using default code '%d'",
879 status, default_status);
880 status = default_status;
881 hc = http_get_status_idx(status);
882 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100883 }
884
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100885 if (strcmp(args[cur_arg], "errorfile") == 0) {
886 cur_arg++;
887 if (!*args[cur_arg]) {
888 memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
889 return ACT_RET_PRS_ERR;
890 }
891
892 rule->arg.http_deny.errmsg = http_load_errorfile(args[cur_arg], err);
893 if (!rule->arg.http_deny.errmsg)
894 return ACT_RET_PRS_ERR;
895 cur_arg++;
896 }
897 else if (strcmp(args[cur_arg], "errorfiles") == 0) {
898 cur_arg++;
899 if (!*args[cur_arg]) {
900 memprintf(err, "'%s' expects <http_errors_name> as argument", args[cur_arg-1]);
901 return ACT_RET_PRS_ERR;
902 }
903 /* Must be resolved during the config validity check */
904 rule->arg.act.p[0] = (void *)((intptr_t)status);
905 rule->arg.act.p[1] = strdup(args[cur_arg]);
906 rule->check_ptr = check_http_deny_action;
907 cur_arg++;
908 goto out;
909 }
910
911 rule->arg.http_deny.status = status;
912
913 out:
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100914 *orig_arg = cur_arg;
Christopher Faulet81e20172019-12-12 16:40:30 +0100915 return ACT_RET_PRS_OK;
916}
917
918/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
919 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
920 */
921static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
922 struct act_rule *rule, char **err)
923{
924 int cur_arg;
925
926 rule->action = ACT_HTTP_REQ_AUTH;
Christopher Faulet245cf792019-12-18 14:58:12 +0100927 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100928 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100929
930 cur_arg = *orig_arg;
931 if (!strcmp(args[cur_arg], "realm")) {
932 cur_arg++;
933 if (!*args[cur_arg]) {
934 memprintf(err, "missing realm value.\n");
935 return ACT_RET_PRS_ERR;
936 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100937 rule->arg.http.str.ptr = strdup(args[cur_arg]);
938 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100939 cur_arg++;
940 }
941
Christopher Fauletc20b3712020-01-27 15:51:56 +0100942 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100943 *orig_arg = cur_arg;
944 return ACT_RET_PRS_OK;
945}
946
947/* Parse a "set-nice" action. It takes the nice value as argument. It returns
948 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
949 */
950static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
951 struct act_rule *rule, char **err)
952{
953 int cur_arg;
954
955 rule->action = ACT_HTTP_SET_NICE;
956
957 cur_arg = *orig_arg;
958 if (!*args[cur_arg]) {
959 memprintf(err, "expects exactly 1 argument (integer value)");
960 return ACT_RET_PRS_ERR;
961 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100962 rule->arg.http.i = atoi(args[cur_arg]);
963 if (rule->arg.http.i < -1024)
964 rule->arg.http.i = -1024;
965 else if (rule->arg.http.i > 1024)
966 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +0100967
Christopher Fauletc20b3712020-01-27 15:51:56 +0100968 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100969 *orig_arg = cur_arg + 1;
970 return ACT_RET_PRS_OK;
971}
972
973/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
974 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
975 */
976static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
977 struct act_rule *rule, char **err)
978{
979#ifdef IP_TOS
980 char *endp;
981 int cur_arg;
982
983 rule->action = ACT_HTTP_SET_TOS;
984
985 cur_arg = *orig_arg;
986 if (!*args[cur_arg]) {
987 memprintf(err, "expects exactly 1 argument (integer/hex value)");
988 return ACT_RET_PRS_ERR;
989 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100990 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100991 if (endp && *endp != '\0') {
992 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
993 return ACT_RET_PRS_ERR;
994 }
995
Christopher Fauletc20b3712020-01-27 15:51:56 +0100996 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100997 *orig_arg = cur_arg + 1;
998 return ACT_RET_PRS_OK;
999#else
1000 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1001 return ACT_RET_PRS_ERR;
1002#endif
1003}
1004
1005/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1006 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1007 */
1008static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1009 struct act_rule *rule, char **err)
1010{
1011#ifdef SO_MARK
1012 char *endp;
1013 int cur_arg;
1014
1015 rule->action = ACT_HTTP_SET_MARK;
1016
1017 cur_arg = *orig_arg;
1018 if (!*args[cur_arg]) {
1019 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1020 return ACT_RET_PRS_ERR;
1021 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001022 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001023 if (endp && *endp != '\0') {
1024 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1025 return ACT_RET_PRS_ERR;
1026 }
1027
Christopher Fauletc20b3712020-01-27 15:51:56 +01001028 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001029 *orig_arg = cur_arg + 1;
1030 global.last_checks |= LSTCHK_NETADM;
1031 return ACT_RET_PRS_OK;
1032#else
1033 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1034 return ACT_RET_PRS_ERR;
1035#endif
1036}
1037
1038/* Parse a "set-log-level" action. It takes the level value as argument. It
1039 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1040 */
1041static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1042 struct act_rule *rule, char **err)
1043{
1044 int cur_arg;
1045
1046 rule->action = ACT_HTTP_SET_LOGL;
1047
1048 cur_arg = *orig_arg;
1049 if (!*args[cur_arg]) {
1050 bad_log_level:
1051 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1052 return ACT_RET_PRS_ERR;
1053 }
1054 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001055 rule->arg.http.i = -1;
1056 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001057 goto bad_log_level;
1058
Christopher Fauletc20b3712020-01-27 15:51:56 +01001059 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001060 *orig_arg = cur_arg + 1;
1061 return ACT_RET_PRS_OK;
1062}
1063
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001064/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1065 * 103 response header with <.arg.http.str> name and with a value built
1066 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001067 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001068 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1069 * block is added to terminate the response. On success, it returns
1070 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1071 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1072 * returned.
1073 */
1074static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1075 struct session *sess, struct stream *s, int flags)
1076{
1077 struct act_rule *prev_rule, *next_rule;
1078 struct channel *res = &s->res;
1079 struct htx *htx = htx_from_buf(&res->buf);
1080 struct buffer *value = alloc_trash_chunk();
1081 enum act_return ret = ACT_RET_CONT;
1082
1083 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1084 goto leave;
1085
1086 if (!value) {
1087 if (!(s->flags & SF_ERR_MASK))
1088 s->flags |= SF_ERR_RESOURCE;
1089 goto error;
1090 }
1091
1092 /* get previous and next rules */
1093 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1094 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1095
1096 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1097 * continue to add link to a previously started response */
1098 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1099 struct htx_sl *sl;
1100 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1101 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1102
1103 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1104 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1105 if (!sl)
1106 goto error;
1107 sl->info.res.status = 103;
1108 }
1109
1110 /* Add the HTTP Early Hint HTTP 103 response heade */
1111 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1112 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1113 goto error;
1114
1115 /* if it is the last rule or the next one is not an early-hint, terminate the current
1116 * response. */
1117 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001118 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1119 /* If an error occurred during an Early-hint rule,
1120 * remove the incomplete HTTP 103 response from the
1121 * buffer */
1122 goto error;
1123 }
1124
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001125 if (!http_forward_proxy_resp(s, 0))
1126 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001127 }
1128
1129 leave:
1130 free_trash_chunk(value);
1131 return ret;
1132
1133 error:
1134 /* If an error occurred during an Early-hint rule, remove the incomplete
1135 * HTTP 103 response from the buffer */
1136 channel_htx_truncate(res, htx);
1137 ret = ACT_RET_ERR;
1138 goto leave;
1139}
1140
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001141/* This function executes a set-header or add-header actions. It builds a string
1142 * in the trash from the specified format string. It finds the action to be
1143 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001144 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001145 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1146 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1147 * ACT_RET_ERR is returned.
1148 */
1149static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1150 struct session *sess, struct stream *s, int flags)
1151{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001152 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1153 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001154 enum act_return ret = ACT_RET_CONT;
1155 struct buffer *replace;
1156 struct http_hdr_ctx ctx;
1157 struct ist n, v;
1158
1159 replace = alloc_trash_chunk();
1160 if (!replace)
1161 goto fail_alloc;
1162
1163 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1164 n = rule->arg.http.str;
1165 v = ist2(replace->area, replace->data);
1166
1167 if (rule->action == 0) { // set-header
1168 /* remove all occurrences of the header */
1169 ctx.blk = NULL;
1170 while (http_find_header(htx, n, &ctx, 1))
1171 http_remove_header(htx, &ctx);
1172 }
1173
1174 /* Now add header */
1175 if (!http_add_header(htx, n, v))
1176 goto fail_rewrite;
1177
1178 leave:
1179 free_trash_chunk(replace);
1180 return ret;
1181
1182 fail_alloc:
1183 if (!(s->flags & SF_ERR_MASK))
1184 s->flags |= SF_ERR_RESOURCE;
1185 ret = ACT_RET_ERR;
1186 goto leave;
1187
1188 fail_rewrite:
1189 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1190 if (s->flags & SF_BE_ASSIGNED)
1191 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1192 if (sess->listener->counters)
1193 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1194 if (objt_server(s->target))
1195 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1196
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001197 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001198 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001199 if (!(s->flags & SF_ERR_MASK))
1200 s->flags |= SF_ERR_PRXCOND;
1201 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001202 goto leave;
1203}
1204
Christopher Faulet81e20172019-12-12 16:40:30 +01001205/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1206 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1207 * on success, ACT_RET_PRS_ERR on error.
1208 *
1209 * Note: same function is used for the request and the response. However
1210 * "early-hint" rules are only supported for request rules.
1211 */
1212static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1213 struct act_rule *rule, char **err)
1214{
Christopher Faulet81e20172019-12-12 16:40:30 +01001215 int cap, cur_arg;
1216
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001217 if (args[*orig_arg-1][0] == 'e') {
1218 rule->action = ACT_CUSTOM;
1219 rule->action_ptr = http_action_early_hint;
1220 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001221 else {
1222 if (args[*orig_arg-1][0] == 's')
1223 rule->action = 0; // set-header
1224 else
1225 rule->action = 1; // add-header
1226 rule->action_ptr = http_action_set_header;
1227 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001228 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001229
1230 cur_arg = *orig_arg;
1231 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1232 memprintf(err, "expects exactly 2 arguments");
1233 return ACT_RET_PRS_ERR;
1234 }
1235
Christopher Faulet81e20172019-12-12 16:40:30 +01001236
Christopher Faulet96bff762019-12-17 13:46:18 +01001237 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1238 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1239 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001240
1241 if (rule->from == ACT_F_HTTP_REQ) {
1242 px->conf.args.ctx = ARGC_HRQ;
1243 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1244 }
1245 else{
1246 px->conf.args.ctx = ARGC_HRS;
1247 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1248 }
1249
1250 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001251 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001252 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001253 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001254 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001255
1256 free(px->conf.lfs_file);
1257 px->conf.lfs_file = strdup(px->conf.args.file);
1258 px->conf.lfs_line = px->conf.args.line;
1259
1260 *orig_arg = cur_arg + 1;
1261 return ACT_RET_PRS_OK;
1262}
1263
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001264/* This function executes a replace-header or replace-value actions. It
1265 * builds a string in the trash from the specified format string. It finds
1266 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001267 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001268 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1269 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1270 * processing continue. Otherwsize ACT_RET_ERR is returned.
1271 */
1272static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1273 struct session *sess, struct stream *s, int flags)
1274{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001275 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1276 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001277 enum act_return ret = ACT_RET_CONT;
1278 struct buffer *replace;
1279 int r;
1280
1281 replace = alloc_trash_chunk();
1282 if (!replace)
1283 goto fail_alloc;
1284
1285 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1286
1287 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1288 if (r == -1)
1289 goto fail_rewrite;
1290
1291 leave:
1292 free_trash_chunk(replace);
1293 return ret;
1294
1295 fail_alloc:
1296 if (!(s->flags & SF_ERR_MASK))
1297 s->flags |= SF_ERR_RESOURCE;
1298 ret = ACT_RET_ERR;
1299 goto leave;
1300
1301 fail_rewrite:
1302 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1303 if (s->flags & SF_BE_ASSIGNED)
1304 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1305 if (sess->listener->counters)
1306 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1307 if (objt_server(s->target))
1308 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1309
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001310 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001311 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001312 if (!(s->flags & SF_ERR_MASK))
1313 s->flags |= SF_ERR_PRXCOND;
1314 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001315 goto leave;
1316}
1317
Christopher Faulet81e20172019-12-12 16:40:30 +01001318/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1319 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1320 * success, ACT_RET_PRS_ERR on error.
1321 */
1322static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1323 struct act_rule *rule, char **err)
1324{
1325 int cap, cur_arg;
1326
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001327 if (args[*orig_arg-1][8] == 'h')
1328 rule->action = 0; // replace-header
1329 else
1330 rule->action = 1; // replace-value
1331 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001332 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001333
1334 cur_arg = *orig_arg;
1335 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1336 memprintf(err, "expects exactly 3 arguments");
1337 return ACT_RET_PRS_ERR;
1338 }
1339
Christopher Faulet96bff762019-12-17 13:46:18 +01001340 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1341 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1342 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001343
1344 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001345 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001346 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001347 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001348 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001349
1350 if (rule->from == ACT_F_HTTP_REQ) {
1351 px->conf.args.ctx = ARGC_HRQ;
1352 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1353 }
1354 else{
1355 px->conf.args.ctx = ARGC_HRS;
1356 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1357 }
1358
1359 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001360 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001361 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001362 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001363 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001364 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001365
1366 free(px->conf.lfs_file);
1367 px->conf.lfs_file = strdup(px->conf.args.file);
1368 px->conf.lfs_line = px->conf.args.line;
1369
1370 *orig_arg = cur_arg + 1;
1371 return ACT_RET_PRS_OK;
1372}
1373
1374/* Parse a "del-header" action. It takes an header name as argument. It returns
1375 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1376 */
1377static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1378 struct act_rule *rule, char **err)
1379{
1380 int cur_arg;
1381
1382 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001383 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001384
1385 cur_arg = *orig_arg;
1386 if (!*args[cur_arg]) {
1387 memprintf(err, "expects exactly 1 arguments");
1388 return ACT_RET_PRS_ERR;
1389 }
1390
Christopher Faulet96bff762019-12-17 13:46:18 +01001391 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1392 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001393 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1394
Christopher Fauletc20b3712020-01-27 15:51:56 +01001395 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001396 *orig_arg = cur_arg + 1;
1397 return ACT_RET_PRS_OK;
1398}
1399
Christopher Faulet2eb53962020-01-14 14:47:34 +01001400/* Release memory allocated by an http redirect action. */
1401static void release_http_redir(struct act_rule *rule)
1402{
1403 struct logformat_node *lf, *lfb;
1404 struct redirect_rule *redir;
1405
1406 redir = rule->arg.redir;
1407 LIST_DEL(&redir->list);
1408 if (redir->cond) {
1409 prune_acl_cond(redir->cond);
1410 free(redir->cond);
1411 }
1412 free(redir->rdr_str);
1413 free(redir->cookie_str);
1414 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1415 LIST_DEL(&lf->list);
1416 free(lf);
1417 }
1418 free(redir);
1419}
1420
Christopher Faulet81e20172019-12-12 16:40:30 +01001421/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1422 * ACT_RET_PRS_ERR on error.
1423 */
1424static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1425 struct act_rule *rule, char **err)
1426{
1427 struct redirect_rule *redir;
1428 int dir, cur_arg;
1429
1430 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001431 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001432 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001433
1434 cur_arg = *orig_arg;
1435
1436 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1437 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1438 return ACT_RET_PRS_ERR;
1439
1440 rule->arg.redir = redir;
1441 rule->cond = redir->cond;
1442 redir->cond = NULL;
1443
1444 /* skip all arguments */
1445 while (*args[cur_arg])
1446 cur_arg++;
1447
1448 *orig_arg = cur_arg;
1449 return ACT_RET_PRS_OK;
1450}
1451
Christopher Faulet046cf442019-12-17 15:45:23 +01001452/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1453 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1454 */
1455static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1456 struct session *sess, struct stream *s, int flags)
1457{
1458 struct pat_ref *ref;
1459 struct buffer *key = NULL, *value = NULL;
1460 enum act_return ret = ACT_RET_CONT;
1461
1462 /* collect reference */
1463 ref = pat_ref_lookup(rule->arg.map.ref);
1464 if (!ref)
1465 goto leave;
1466
1467 /* allocate key */
1468 key = alloc_trash_chunk();
1469 if (!key)
1470 goto fail_alloc;
1471
1472 /* collect key */
1473 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1474 key->area[key->data] = '\0';
1475
1476 switch (rule->action) {
1477 case 0: // add-acl
1478 /* add entry only if it does not already exist */
1479 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1480 if (pat_ref_find_elt(ref, key->area) == NULL)
1481 pat_ref_add(ref, key->area, NULL, NULL);
1482 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1483 break;
1484
1485 case 1: // set-map
1486 /* allocate value */
1487 value = alloc_trash_chunk();
1488 if (!value)
1489 goto fail_alloc;
1490
1491 /* collect value */
1492 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1493 value->area[value->data] = '\0';
1494
1495 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1496 if (pat_ref_find_elt(ref, key->area) != NULL) {
1497 /* update entry if it exists */
1498 pat_ref_set(ref, key->area, value->area, NULL);
1499 }
1500 else {
1501 /* insert a new entry */
1502 pat_ref_add(ref, key->area, value->area, NULL);
1503 }
1504 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1505 break;
1506
1507 case 2: // del-acl
1508 case 3: // del-map
1509 /* returned code: 1=ok, 0=ko */
1510 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1511 pat_ref_delete(ref, key->area);
1512 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1513 break;
1514
1515 default:
1516 ret = ACT_RET_ERR;
1517 }
1518
1519
1520 leave:
1521 free_trash_chunk(key);
1522 free_trash_chunk(value);
1523 return ret;
1524
1525 fail_alloc:
1526 if (!(s->flags & SF_ERR_MASK))
1527 s->flags |= SF_ERR_RESOURCE;
1528 ret = ACT_RET_ERR;
1529 goto leave;
1530}
1531
Christopher Faulet2eb53962020-01-14 14:47:34 +01001532/* Release memory allocated by an http map/acl action. */
1533static void release_http_map(struct act_rule *rule)
1534{
1535 struct logformat_node *lf, *lfb;
1536
1537 free(rule->arg.map.ref);
1538 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1539 LIST_DEL(&lf->list);
1540 release_sample_expr(lf->expr);
1541 free(lf->arg);
1542 free(lf);
1543 }
1544 if (rule->action == 1) {
1545 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1546 LIST_DEL(&lf->list);
1547 release_sample_expr(lf->expr);
1548 free(lf->arg);
1549 free(lf);
1550 }
1551 }
1552}
1553
Christopher Faulet81e20172019-12-12 16:40:30 +01001554/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001555 * two log-format string as argument depending on the action. The action is
1556 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1557 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001558 */
1559static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1560 struct act_rule *rule, char **err)
1561{
1562 int cap, cur_arg;
1563
Christopher Faulet046cf442019-12-17 15:45:23 +01001564 if (args[*orig_arg-1][0] == 'a') // add-acl
1565 rule->action = 0;
1566 else if (args[*orig_arg-1][0] == 's') // set-map
1567 rule->action = 1;
1568 else if (args[*orig_arg-1][4] == 'a') // del-acl
1569 rule->action = 2;
1570 else if (args[*orig_arg-1][4] == 'm') // del-map
1571 rule->action = 3;
1572 else {
1573 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1574 return ACT_RET_PRS_ERR;
1575 }
1576 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001577 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001578
1579 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001580 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1581 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001582 memprintf(err, "expects exactly 2 arguments");
1583 return ACT_RET_PRS_ERR;
1584 }
1585 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001586 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001587 memprintf(err, "expects exactly 1 arguments");
1588 return ACT_RET_PRS_ERR;
1589 }
1590
1591 /*
1592 * '+ 8' for 'set-map(' (same for del-map)
1593 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1594 */
1595 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1596
1597 if (rule->from == ACT_F_HTTP_REQ) {
1598 px->conf.args.ctx = ARGC_HRQ;
1599 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1600 }
1601 else{
1602 px->conf.args.ctx = ARGC_HRS;
1603 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1604 }
1605
1606 /* key pattern */
1607 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001608 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1609 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001610 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001611 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001612
Christopher Faulet046cf442019-12-17 15:45:23 +01001613 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001614 /* value pattern for set-map only */
1615 cur_arg++;
1616 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001617 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1618 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001619 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001620 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001621 }
1622
1623 free(px->conf.lfs_file);
1624 px->conf.lfs_file = strdup(px->conf.args.file);
1625 px->conf.lfs_line = px->conf.args.line;
1626
1627 *orig_arg = cur_arg + 1;
1628 return ACT_RET_PRS_OK;
1629}
1630
Christopher Fauletac98d812019-12-18 09:20:16 +01001631/* This function executes a track-sc* actions. On success, it returns
1632 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1633 */
1634static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1635 struct session *sess, struct stream *s, int flags)
1636{
1637 struct stktable *t;
1638 struct stksess *ts;
1639 struct stktable_key *key;
1640 void *ptr1, *ptr2, *ptr3, *ptr4;
1641 int opt;
1642
1643 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1644 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1645
1646 t = rule->arg.trk_ctr.table.t;
1647 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1648
1649 if (!key)
1650 goto end;
1651 ts = stktable_get_entry(t, key);
1652 if (!ts)
1653 goto end;
1654
1655 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1656
1657 /* let's count a new HTTP request as it's the first time we do it */
1658 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1659 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1660
1661 /* When the client triggers a 4xx from the server, it's most often due
1662 * to a missing object or permission. These events should be tracked
1663 * because if they happen often, it may indicate a brute force or a
1664 * vulnerability scan. Normally this is done when receiving the response
1665 * but here we're tracking after this ought to have been done so we have
1666 * to do it on purpose.
1667 */
1668 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1669 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1670 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1671 }
1672
1673 if (ptr1 || ptr2 || ptr3 || ptr4) {
1674 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1675
1676 if (ptr1)
1677 stktable_data_cast(ptr1, http_req_cnt)++;
1678 if (ptr2)
1679 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1680 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1681 if (ptr3)
1682 stktable_data_cast(ptr3, http_err_cnt)++;
1683 if (ptr4)
1684 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1685 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1686
1687 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1688
1689 /* If data was modified, we need to touch to re-schedule sync */
1690 stktable_touch_local(t, ts, 0);
1691 }
1692
1693 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1694 if (sess->fe != s->be)
1695 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1696
1697 end:
1698 return ACT_RET_CONT;
1699}
Christopher Faulet81e20172019-12-12 16:40:30 +01001700
Christopher Faulet2eb53962020-01-14 14:47:34 +01001701static void release_http_track_sc(struct act_rule *rule)
1702{
1703 release_sample_expr(rule->arg.trk_ctr.expr);
1704}
1705
Christopher Faulet81e20172019-12-12 16:40:30 +01001706/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1707 * ACT_RET_PRS_ERR on error.
1708 */
1709static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1710 struct act_rule *rule, char **err)
1711{
1712 struct sample_expr *expr;
1713 unsigned int where;
1714 unsigned int tsc_num;
1715 const char *tsc_num_str;
1716 int cur_arg;
1717
1718 tsc_num_str = &args[*orig_arg-1][8];
1719 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1720 return ACT_RET_PRS_ERR;
1721
1722 cur_arg = *orig_arg;
1723 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001724 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001725 if (!expr)
1726 return ACT_RET_PRS_ERR;
1727
1728 where = 0;
1729 if (px->cap & PR_CAP_FE)
1730 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1731 if (px->cap & PR_CAP_BE)
1732 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1733
1734 if (!(expr->fetch->val & where)) {
1735 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1736 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001737 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001738 return ACT_RET_PRS_ERR;
1739 }
1740
1741 if (strcmp(args[cur_arg], "table") == 0) {
1742 cur_arg++;
1743 if (!*args[cur_arg]) {
1744 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001745 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001746 return ACT_RET_PRS_ERR;
1747 }
1748
1749 /* we copy the table name for now, it will be resolved later */
1750 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1751 cur_arg++;
1752 }
1753
Christopher Fauletac98d812019-12-18 09:20:16 +01001754 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001755 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001756 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001757 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001758 rule->check_ptr = check_trk_action;
1759
1760 *orig_arg = cur_arg;
1761 return ACT_RET_PRS_OK;
1762}
1763
Christopher Faulet46f95542019-12-20 10:07:22 +01001764/* This function executes a strict-mode actions. On success, it always returns
1765 * ACT_RET_CONT
1766 */
1767static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1768 struct session *sess, struct stream *s, int flags)
1769{
1770 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1771
1772 if (rule->action == 0) // strict-mode on
1773 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1774 else // strict-mode off
1775 msg->flags |= HTTP_MSGF_SOFT_RW;
1776 return ACT_RET_CONT;
1777}
1778
1779/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1780 * ACT_RET_PRS_ERR on error.
1781 */
1782static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1783 struct act_rule *rule, char **err)
1784{
1785 int cur_arg;
1786
Christopher Faulet46f95542019-12-20 10:07:22 +01001787 cur_arg = *orig_arg;
1788 if (!*args[cur_arg]) {
1789 memprintf(err, "expects exactly 1 arguments");
1790 return ACT_RET_PRS_ERR;
1791 }
1792
1793 if (strcasecmp(args[cur_arg], "on") == 0)
1794 rule->action = 0; // strict-mode on
1795 else if (strcasecmp(args[cur_arg], "off") == 0)
1796 rule->action = 1; // strict-mode off
1797 else {
1798 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1799 return ACT_RET_PRS_ERR;
1800 }
1801 rule->action_ptr = http_action_strict_mode;
1802
1803 *orig_arg = cur_arg + 1;
1804 return ACT_RET_PRS_OK;
1805}
1806
Christopher Faulet18630642020-05-12 18:57:28 +02001807/* Release <.arg.http_reply> */
Christopher Faulet24231ab2020-01-24 17:44:23 +01001808static void release_http_return(struct act_rule *rule)
1809{
Christopher Faulet18630642020-05-12 18:57:28 +02001810 release_http_reply(rule->arg.http_reply);
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001811 rule->arg.http_reply = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001812}
1813
1814/* This function executes a return action. It builds an HTX message from an
1815 * errorfile, an raw file or a log-format string, depending on <.action>
1816 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1817 * returned.
1818 */
1819static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1820 struct session *sess, struct stream *s, int flags)
1821{
1822 struct channel *req = &s->req;
1823 struct channel *res = &s->res;
1824 struct buffer *errmsg;
1825 struct htx *htx = htx_from_buf(&res->buf);
1826 struct htx_sl *sl;
1827 struct buffer *body = NULL;
1828 const char *status, *reason, *clen, *ctype;
1829 unsigned int slflags;
Christopher Faulet90d22a82020-03-06 11:18:39 +01001830 enum act_return ret = ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001831
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001832 s->txn->status = rule->arg.http_reply->status;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001833 channel_htx_truncate(res, htx);
1834
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001835 /* HTTP_REPLY_ERRFILES unexpected here. handled as no payload if so */
1836
1837 if (rule->arg.http_reply->type == HTTP_REPLY_ERRMSG) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001838 /* implicit or explicit error message*/
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001839 errmsg = rule->arg.http_reply->body.errmsg;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001840 if (!errmsg) {
1841 /* get default error message */
1842 errmsg = http_error_message(s);
1843 }
1844 if (b_is_null(errmsg))
1845 goto end;
1846 if (!channel_htx_copy_msg(res, htx, errmsg))
1847 goto fail;
1848 }
1849 else {
1850 /* no payload, file or log-format string */
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001851 if (rule->arg.http_reply->type == HTTP_REPLY_RAW) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001852 /* file */
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001853 body = &rule->arg.http_reply->body.obj;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001854 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001855 else if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01001856 /* log-format string */
1857 body = alloc_trash_chunk();
1858 if (!body)
1859 goto fail_alloc;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001860 body->data = build_logline(s, body->area, body->size, &rule->arg.http_reply->body.fmt);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001861 }
1862 /* else no payload */
1863
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001864 status = ultoa(rule->arg.http_reply->status);
1865 reason = http_get_reason(rule->arg.http_reply->status);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001866 slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1867 if (!body || !b_data(body))
1868 slflags |= HTX_SL_F_BODYLESS;
1869 sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason));
1870 if (!sl)
1871 goto fail;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001872 sl->info.res.status = rule->arg.http_reply->status;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001873
1874 clen = (body ? ultoa(b_data(body)) : "0");
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001875 ctype = rule->arg.http_reply->ctype;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001876
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001877 if (!LIST_ISEMPTY(&rule->arg.http_reply->hdrs)) {
1878 struct http_reply_hdr *hdr;
Christopher Faulet4a2c1422020-01-31 17:36:01 +01001879 struct buffer *value = alloc_trash_chunk();
1880
1881 if (!value)
1882 goto fail;
1883
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001884 list_for_each_entry(hdr, &rule->arg.http_reply->hdrs, list) {
Christopher Faulet4a2c1422020-01-31 17:36:01 +01001885 chunk_reset(value);
1886 value->data = build_logline(s, value->area, value->size, &hdr->value);
1887 if (b_data(value) && !htx_add_header(htx, hdr->name, ist2(b_head(value), b_data(value)))) {
1888 free_trash_chunk(value);
1889 goto fail;
1890 }
1891 chunk_reset(value);
1892 }
1893 free_trash_chunk(value);
1894 }
1895
Christopher Faulet24231ab2020-01-24 17:44:23 +01001896 if (!htx_add_header(htx, ist("content-length"), ist(clen)) ||
1897 (body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) ||
1898 !htx_add_endof(htx, HTX_BLK_EOH) ||
1899 (body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body)))) ||
1900 !htx_add_endof(htx, HTX_BLK_EOM))
1901 goto fail;
1902 }
1903
1904 htx_to_buf(htx, &s->res.buf);
1905 if (!http_forward_proxy_resp(s, 1))
1906 goto fail;
1907
1908 end:
1909 if (rule->from == ACT_F_HTTP_REQ) {
1910 /* let's log the request time */
1911 s->logs.tv_request = now;
1912 req->analysers &= AN_REQ_FLT_END;
1913
1914 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1915 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1916 }
1917
1918 if (!(s->flags & SF_ERR_MASK))
1919 s->flags |= SF_ERR_LOCAL;
1920 if (!(s->flags & SF_FINST_MASK))
1921 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1922
1923 leave:
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001924 if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT)
Christopher Faulet24231ab2020-01-24 17:44:23 +01001925 free_trash_chunk(body);
1926 return ret;
1927
1928 fail_alloc:
1929 if (!(s->flags & SF_ERR_MASK))
1930 s->flags |= SF_ERR_RESOURCE;
1931 ret = ACT_RET_ERR;
1932 goto leave;
1933
1934 fail:
1935 /* If an error occurred, remove the incomplete HTTP response from the
1936 * buffer */
1937 channel_htx_truncate(res, htx);
1938 ret = ACT_RET_ERR;
1939 if (!(s->flags & SF_ERR_MASK))
1940 s->flags |= SF_ERR_PRXCOND;
1941 goto leave;
1942}
1943
1944/* Check an "http-request return" action when an http-errors section is referenced.
1945 *
1946 * The function returns 1 in success case, otherwise, it returns 0 and err is
1947 * filled.
1948 */
1949static int check_http_return_action(struct act_rule *rule, struct proxy *px, char **err)
1950{
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001951 struct http_reply *reply = rule->arg.http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001952 struct http_errors *http_errs;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001953 int ret = 1;
1954
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001955 if (reply->type != HTTP_REPLY_ERRFILES)
1956 goto end;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001957
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001958 list_for_each_entry(http_errs, &http_errors_list, list) {
1959 if (strcmp(http_errs->id, reply->body.http_errors) == 0) {
1960 reply->type = HTTP_REPLY_ERRMSG;
1961 free(reply->body.http_errors);
1962 reply->body.errmsg = http_errs->errmsg[http_get_status_idx(reply->status)];
1963 if (!reply->body.errmsg)
Christopher Faulet24231ab2020-01-24 17:44:23 +01001964 ha_warning("Proxy '%s': status '%d' referenced by http return rule "
1965 "not declared in http-errors section '%s'.\n",
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001966 px->id, reply->status, http_errs->id);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001967 break;
1968 }
1969 }
1970
1971 if (&http_errs->list == &http_errors_list) {
1972 memprintf(err, "unknown http-errors section '%s' referenced by http return rule",
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001973 reply->body.http_errors);
1974 release_http_return(rule);
Christopher Faulet24231ab2020-01-24 17:44:23 +01001975 ret = 0;
1976 }
1977
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001978 end:
Christopher Faulet24231ab2020-01-24 17:44:23 +01001979 return ret;
1980}
1981
1982/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001983 * ACT_RET_PRS_ERR on error. This function creates one of the following http
1984 * replies :
Christopher Faulet24231ab2020-01-24 17:44:23 +01001985 *
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001986 * - HTTP_REPLY_EMPTY : dummy response, no payload
1987 * - HTTP_REPLY_ERRMSG : implicit error message depending on the status code or explicit one
1988 * - HTTP_REPLY_ERRFILES : points on an http-errors section (resolved during post-parsing)
1989 * - HTTP_REPLY_RAW : explicit file object ('file' argument)
1990 * - HTTP_REPLY_LOGFMT : explicit log-format string ('content' argument)
Christopher Faulet24231ab2020-01-24 17:44:23 +01001991 *
1992 * The content-type must be defined for non-empty payload. It is ignored for
1993 * error messages (implicit or explicit). When an http-errors section is
1994 * referenced, action is set to -1 and the real action is resolved during the
1995 * configuration validity check.
1996 */
1997static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1998 struct act_rule *rule, char **err)
1999{
2000 struct logformat_node *lf, *lfb;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002001 struct http_reply *reply = NULL;
2002 struct http_reply_hdr *hdr, *hdrb;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002003 struct stat stat;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002004 const char *act_arg = NULL;
2005 char *obj = NULL;
2006 int cur_arg, cap, objlen = 0, fd = -1;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002007
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002008 reply = calloc(1, sizeof(*reply));
2009 if (!reply) {
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002010 memprintf(err, "out of memory");
2011 goto error;
2012 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002013 LIST_INIT(&reply->hdrs);
2014 reply->type = HTTP_REPLY_EMPTY;
2015 reply->status = 200;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002016
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002017 cur_arg = *orig_arg;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002018 while (*args[cur_arg]) {
2019 if (strcmp(args[cur_arg], "status") == 0) {
2020 cur_arg++;
2021 if (!*args[cur_arg]) {
2022 memprintf(err, "'%s' expects <status_code> as argument", args[cur_arg-1]);
Christopher Faulet817c4e32020-02-07 10:26:23 +01002023 goto error;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002024 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002025 reply->status = atol(args[cur_arg]);
2026 if (reply->status < 200 || reply->status > 599) {
2027 memprintf(err, "Unexpected status code '%d'", reply->status);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002028 goto error;
2029 }
2030 cur_arg++;
2031 }
2032 else if (strcmp(args[cur_arg], "content-type") == 0) {
2033 cur_arg++;
2034 if (!*args[cur_arg]) {
2035 memprintf(err, "'%s' expects <ctype> as argument", args[cur_arg-1]);
2036 goto error;
2037 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002038 free(reply->ctype);
2039 reply->ctype = strdup(args[cur_arg]);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002040 cur_arg++;
2041 }
2042 else if (strcmp(args[cur_arg], "errorfiles") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002043 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002044 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2045 goto error;
2046 }
2047 act_arg = args[cur_arg];
2048 cur_arg++;
2049 if (!*args[cur_arg]) {
2050 memprintf(err, "'%s' expects <name> as argument", args[cur_arg-1]);
2051 goto error;
2052 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002053 reply->body.http_errors = strdup(args[cur_arg]);
2054 if (!reply->body.http_errors) {
2055 memprintf(err, "out of memory");
2056 goto error;
2057 }
2058 reply->type = HTTP_REPLY_ERRFILES;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002059 cur_arg++;
2060 }
2061 else if (strcmp(args[cur_arg], "default-errorfiles") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002062 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002063 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2064 goto error;
2065 }
2066 act_arg = args[cur_arg];
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002067 reply->type = HTTP_REPLY_ERRMSG;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002068 cur_arg++;
2069 }
2070 else if (strcmp(args[cur_arg], "errorfile") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002071 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002072 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2073 goto error;
2074 }
2075 act_arg = args[cur_arg];
2076 cur_arg++;
2077 if (!*args[cur_arg]) {
2078 memprintf(err, "'%s' expects <fmt> as argument", args[cur_arg-1]);
2079 goto error;
2080 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002081 reply->body.errmsg = http_load_errorfile(args[cur_arg], err);
2082 if (!reply->body.errmsg) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002083 goto error;
2084 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002085 reply->type = HTTP_REPLY_ERRMSG;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002086 cur_arg++;
2087 }
2088 else if (strcmp(args[cur_arg], "file") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002089 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002090 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2091 goto error;
2092 }
2093 act_arg = args[cur_arg];
2094 cur_arg++;
2095 if (!*args[cur_arg]) {
2096 memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
2097 goto error;
2098 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002099 fd = open(args[cur_arg], O_RDONLY);
2100 if ((fd < 0) || (fstat(fd, &stat) < 0)) {
2101 memprintf(err, "error opening file '%s'", args[cur_arg]);
2102 goto error;
2103 }
2104 if (stat.st_size > global.tune.bufsize) {
Willy Tarreaue35d1d42020-02-11 10:58:56 +01002105 memprintf(err, "file '%s' exceeds the buffer size (%lld > %d)",
2106 args[cur_arg], (long long)stat.st_size, global.tune.bufsize);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002107 goto error;
2108 }
2109 objlen = stat.st_size;
2110 obj = malloc(objlen);
2111 if (!obj || read(fd, obj, objlen) != objlen) {
2112 memprintf(err, "error reading file '%s'", args[cur_arg]);
2113 goto error;
2114 }
2115 close(fd);
2116 fd = -1;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002117 reply->type = HTTP_REPLY_RAW;
2118 chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
2119 obj = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002120 cur_arg++;
2121 }
2122 else if (strcmp(args[cur_arg], "string") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002123 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002124 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2125 goto error;
2126 }
2127 act_arg = args[cur_arg];
2128 cur_arg++;
2129 if (!*args[cur_arg]) {
2130 memprintf(err, "'%s' expects <str> as argument", args[cur_arg-1]);
2131 goto error;
2132 }
2133 obj = strdup(args[cur_arg]);
2134 objlen = strlen(args[cur_arg]);
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002135 if (!obj) {
2136 memprintf(err, "out of memory");
2137 goto error;
2138 }
2139 reply->type = HTTP_REPLY_RAW;
2140 chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
2141 obj = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002142 cur_arg++;
2143 }
2144 else if (strcmp(args[cur_arg], "lf-file") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002145 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002146 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2147 goto error;
2148 }
2149 act_arg = args[cur_arg];
2150 cur_arg++;
2151 if (!*args[cur_arg]) {
2152 memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
2153 goto error;
2154 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002155 fd = open(args[cur_arg], O_RDONLY);
2156 if ((fd < 0) || (fstat(fd, &stat) < 0)) {
2157 memprintf(err, "error opening file '%s'", args[cur_arg]);
2158 goto error;
2159 }
2160 if (stat.st_size > global.tune.bufsize) {
Willy Tarreaue35d1d42020-02-11 10:58:56 +01002161 memprintf(err, "file '%s' exceeds the buffer size (%lld > %d)",
2162 args[cur_arg], (long long)stat.st_size, global.tune.bufsize);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002163 goto error;
2164 }
2165 objlen = stat.st_size;
2166 obj = malloc(objlen + 1);
2167 if (!obj || read(fd, obj, objlen) != objlen) {
2168 memprintf(err, "error reading file '%s'", args[cur_arg]);
2169 goto error;
2170 }
2171 close(fd);
2172 fd = -1;
2173 obj[objlen] = '\0';
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002174 reply->type = HTTP_REPLY_LOGFMT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002175 cur_arg++;
2176 }
2177 else if (strcmp(args[cur_arg], "lf-string") == 0) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002178 if (reply->type != HTTP_REPLY_EMPTY) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002179 memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
2180 goto error;
2181 }
2182 act_arg = args[cur_arg];
2183 cur_arg++;
2184 if (!*args[cur_arg]) {
2185 memprintf(err, "'%s' expects <fmt> as argument", args[cur_arg-1]);
2186 goto error;
2187 }
2188 obj = strdup(args[cur_arg]);
2189 objlen = strlen(args[cur_arg]);
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002190 reply->type = HTTP_REPLY_LOGFMT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002191 cur_arg++;
2192 }
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002193 else if (strcmp(args[cur_arg], "hdr") == 0) {
2194 cur_arg++;
2195 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2196 memprintf(err, "'%s' expects <name> and <value> as arguments", args[cur_arg-1]);
2197 goto error;
2198 }
2199 if (strcasecmp(args[cur_arg], "content-length") == 0 ||
2200 strcasecmp(args[cur_arg], "transfer-encoding") == 0 ||
2201 strcasecmp(args[cur_arg], "content-type") == 0) {
2202 ha_warning("parsing [%s:%d] : 'http-%s return' : header '%s' ignored.\n",
2203 px->conf.args.file, px->conf.args.line,
2204 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
2205 args[cur_arg]);
2206 cur_arg += 2;
2207 continue;
2208 }
2209 hdr = calloc(1, sizeof(*hdr));
2210 if (!hdr) {
2211 memprintf(err, "'%s' : out of memory", args[cur_arg-1]);
2212 goto error;
2213 }
2214 LIST_INIT(&hdr->value);
2215 hdr->name = ist(strdup(args[cur_arg]));
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002216 if (!isttest(hdr->name)) {
2217 memprintf(err, "out of memory");
2218 goto error;
2219 }
2220 LIST_ADDQ(&reply->hdrs, &hdr->list);
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002221
2222 if (rule->from == ACT_F_HTTP_REQ) {
2223 px->conf.args.ctx = ARGC_HRQ;
2224 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
2225 }
2226 else {
2227 px->conf.args.ctx = ARGC_HRS;
2228 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
2229 }
2230 if (!parse_logformat_string(args[cur_arg+1], px, &hdr->value, LOG_OPT_HTTP, cap, err))
2231 goto error;
2232
2233 free(px->conf.lfs_file);
2234 px->conf.lfs_file = strdup(px->conf.args.file);
2235 px->conf.lfs_line = px->conf.args.line;
2236 cur_arg += 2;
2237 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002238 else
2239 break;
2240 }
2241
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002242 if (reply->type == HTTP_REPLY_EMPTY) { /* no payload */
2243 if (reply->ctype) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002244 ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored because"
2245 " neither errorfile nor payload defined.\n",
2246 px->conf.args.file, px->conf.args.line,
2247 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002248 reply->ctype);
2249 free(reply->ctype);
2250 reply->ctype = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002251 }
2252 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002253 else if (reply->type == HTTP_REPLY_ERRFILES || reply->type == HTTP_REPLY_ERRMSG) { /* errorfiles or errorfile */
2254
2255 if (reply->type != HTTP_REPLY_ERRMSG || !reply->body.errmsg) {
2256 /* default errorfile or errorfiles: check the status */
Christopher Faulet24231ab2020-01-24 17:44:23 +01002257 int rc;
2258
2259 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002260 if (http_err_codes[rc] == reply->status)
Christopher Faulet24231ab2020-01-24 17:44:23 +01002261 break;
2262 }
2263
2264 if (rc >= HTTP_ERR_SIZE) {
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002265 memprintf(err, "status code '%d' not handled by default with '%s' argument.",
2266 reply->status, act_arg);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002267 goto error;
2268 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002269 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002270
2271 if (reply->ctype) {
2272 ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored when the "
2273 "returned response is an erorrfile.\n",
2274 px->conf.args.file, px->conf.args.line,
2275 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
2276 reply->ctype);
2277 free(reply->ctype);
2278 reply->ctype = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002279 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002280 if (!LIST_ISEMPTY(&reply->hdrs)) {
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002281 ha_warning("parsing [%s:%d] : 'http-%s return' : hdr parameters ignored when the "
2282 "returned response is an erorrfile.\n",
2283 px->conf.args.file, px->conf.args.line,
2284 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"));
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002285 list_for_each_entry_safe(hdr, hdrb, &reply->hdrs, list) {
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002286 LIST_DEL(&hdr->list);
2287 list_for_each_entry_safe(lf, lfb, &hdr->value, list) {
2288 LIST_DEL(&lf->list);
2289 release_sample_expr(lf->expr);
2290 free(lf->arg);
2291 free(lf);
2292 }
Tim Duesterhused526372020-03-05 17:56:33 +01002293 istfree(&hdr->name);
Christopher Faulet4a2c1422020-01-31 17:36:01 +01002294 free(hdr);
2295 }
2296 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002297 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002298 else if (reply->type == HTTP_REPLY_RAW) { /* explicit parameter using 'file' parameter*/
2299 if (!reply->ctype && objlen) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002300 memprintf(err, "a content type must be defined when non-empty payload is configured");
2301 goto error;
2302 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002303 if (reply->ctype && !b_data(&reply->body.obj)) {
2304 ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored when the "
2305 "configured payload is empty.\n",
2306 px->conf.args.file, px->conf.args.line,
2307 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
2308 reply->ctype);
2309 free(reply->ctype);
2310 reply->ctype = NULL;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002311 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002312 if (b_room(&reply->body.obj) < global.tune.maxrewrite) {
Dominik Froehlich30d49ab2020-04-17 11:04:35 +02002313 ha_warning("parsing [%s:%d] : 'http-%s return' : the payload runs over the buffer space reserved to headers rewriting."
2314 " It may lead to internal errors if strict rewriting mode is enabled.\n",
Christopher Faulet24231ab2020-01-24 17:44:23 +01002315 px->conf.args.file, px->conf.args.line,
2316 (rule->from == ACT_F_HTTP_REQ ? "request" : "response"));
2317 }
Christopher Faulet24231ab2020-01-24 17:44:23 +01002318 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002319 else if (reply->type == HTTP_REPLY_LOGFMT) { /* log-format payload using 'lf-file' of 'lf-string' parameter */
2320 LIST_INIT(&reply->body.fmt);
2321 if (!reply->ctype) {
Christopher Faulet24231ab2020-01-24 17:44:23 +01002322 memprintf(err, "a content type must be defined with a log-format payload");
2323 goto error;
2324 }
2325 if (rule->from == ACT_F_HTTP_REQ) {
2326 px->conf.args.ctx = ARGC_HRQ;
2327 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
2328 }
2329 else {
2330 px->conf.args.ctx = ARGC_HRS;
2331 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
2332 }
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002333 if (!parse_logformat_string(obj, px, &reply->body.fmt, LOG_OPT_HTTP, cap, err))
Christopher Faulet24231ab2020-01-24 17:44:23 +01002334 goto error;
2335
2336 free(px->conf.lfs_file);
2337 px->conf.lfs_file = strdup(px->conf.args.file);
2338 px->conf.lfs_line = px->conf.args.line;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002339 }
2340
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002341 rule->arg.http_reply = reply;
Christopher Fauletba946bf2020-05-13 08:50:07 +02002342 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002343 rule->action = ACT_CUSTOM;
2344 rule->check_ptr = check_http_return_action;
Christopher Faulet24231ab2020-01-24 17:44:23 +01002345 rule->action_ptr = http_action_return;
2346 rule->release_ptr = release_http_return;
2347
Christopher Faulet5ff0c642020-05-12 18:33:37 +02002348 free(obj);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002349 *orig_arg = cur_arg;
2350 return ACT_RET_PRS_OK;
2351
2352 error:
2353 free(obj);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002354 if (fd >= 0)
2355 close(fd);
Christopher Faulet18630642020-05-12 18:57:28 +02002356 release_http_reply(reply);
Christopher Faulet24231ab2020-01-24 17:44:23 +01002357 return ACT_RET_PRS_ERR;
2358}
2359
Willy Tarreau79e57332018-10-02 16:01:16 +02002360/************************************************************************/
2361/* All supported http-request action keywords must be declared here. */
2362/************************************************************************/
2363
2364static struct action_kw_list http_req_actions = {
2365 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002366 { "add-acl", parse_http_set_map, 1 },
2367 { "add-header", parse_http_set_header, 0 },
2368 { "allow", parse_http_allow, 0 },
2369 { "auth", parse_http_auth, 0 },
2370 { "capture", parse_http_req_capture, 0 },
2371 { "del-acl", parse_http_set_map, 1 },
2372 { "del-header", parse_http_del_header, 0 },
2373 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002374 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002375 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
2376 { "early-hint", parse_http_set_header, 0 },
2377 { "redirect", parse_http_redirect, 0 },
2378 { "reject", parse_http_action_reject, 0 },
2379 { "replace-header", parse_http_replace_header, 0 },
2380 { "replace-path", parse_replace_uri, 0 },
2381 { "replace-uri", parse_replace_uri, 0 },
2382 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002383 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002384 { "set-header", parse_http_set_header, 0 },
2385 { "set-log-level", parse_http_set_log_level, 0 },
2386 { "set-map", parse_http_set_map, 1 },
2387 { "set-method", parse_set_req_line, 0 },
2388 { "set-mark", parse_http_set_mark, 0 },
2389 { "set-nice", parse_http_set_nice, 0 },
2390 { "set-path", parse_set_req_line, 0 },
2391 { "set-query", parse_set_req_line, 0 },
2392 { "set-tos", parse_http_set_tos, 0 },
2393 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002394 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002395 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002396 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002397 { NULL, NULL }
2398 }
2399};
2400
Willy Tarreau0108d902018-11-25 19:14:37 +01002401INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
2402
Willy Tarreau79e57332018-10-02 16:01:16 +02002403static struct action_kw_list http_res_actions = {
2404 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01002405 { "add-acl", parse_http_set_map, 1 },
2406 { "add-header", parse_http_set_header, 0 },
2407 { "allow", parse_http_allow, 0 },
2408 { "capture", parse_http_res_capture, 0 },
2409 { "del-acl", parse_http_set_map, 1 },
2410 { "del-header", parse_http_del_header, 0 },
2411 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01002412 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002413 { "redirect", parse_http_redirect, 0 },
2414 { "replace-header", parse_http_replace_header, 0 },
2415 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01002416 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002417 { "set-header", parse_http_set_header, 0 },
2418 { "set-log-level", parse_http_set_log_level, 0 },
2419 { "set-map", parse_http_set_map, 1 },
2420 { "set-mark", parse_http_set_mark, 0 },
2421 { "set-nice", parse_http_set_nice, 0 },
2422 { "set-status", parse_http_set_status, 0 },
2423 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01002424 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01002425 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02002426 { NULL, NULL }
2427 }
2428};
2429
Willy Tarreau0108d902018-11-25 19:14:37 +01002430INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02002431
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01002432static struct action_kw_list http_after_res_actions = {
2433 .kw = {
2434 { "add-header", parse_http_set_header, 0 },
2435 { "allow", parse_http_allow, 0 },
2436 { "del-header", parse_http_del_header, 0 },
2437 { "replace-header", parse_http_replace_header, 0 },
2438 { "replace-value", parse_http_replace_header, 0 },
2439 { "set-header", parse_http_set_header, 0 },
2440 { "set-status", parse_http_set_status, 0 },
2441 { "strict-mode", parse_http_strict_mode, 0 },
2442 { NULL, NULL }
2443 }
2444};
2445
2446INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
2447
Willy Tarreau79e57332018-10-02 16:01:16 +02002448/*
2449 * Local variables:
2450 * c-indent-level: 8
2451 * c-basic-offset: 8
2452 * End:
2453 */