blob: c2e3857371dbfc23dfbf6fc686b8af582cdfd2ea [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP actions
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020019#include <haproxy/api.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010020#include <common/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020021#include <haproxy/chunk.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020022#include <common/http.h>
Willy Tarreaud0ef4392020-06-02 09:38:52 +020023#include <haproxy/pool.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020024#include <common/standard.h>
Christopher Fauletb3048832020-05-27 15:26:43 +020025#include <common/uri_auth.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020026#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020027
28#include <types/capture.h>
29#include <types/global.h>
30
31#include <proto/acl.h>
32#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010033#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020034#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020035#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020036#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020037#include <proto/http_ana.h>
Christopher Faulet046cf442019-12-17 15:45:23 +010038#include <proto/pattern.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010039#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020040
Christopher Faulet2eb53962020-01-14 14:47:34 +010041/* Release memory allocated by most of HTTP actions. Concretly, it releases
42 * <arg.http>.
43 */
44static void release_http_action(struct act_rule *rule)
45{
46 struct logformat_node *lf, *lfb;
47
Tim Duesterhused526372020-03-05 17:56:33 +010048 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010049 if (rule->arg.http.re)
50 regex_free(rule->arg.http.re);
51 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
52 LIST_DEL(&lf->list);
53 release_sample_expr(lf->expr);
54 free(lf->arg);
55 free(lf);
56 }
57}
58
Christopher Faulet5cb513a2020-05-13 17:56:56 +020059/* Release memory allocated by HTTP actions relying on an http reply. Concretly,
60 * it releases <.arg.http_reply>
61 */
62static void release_act_http_reply(struct act_rule *rule)
63{
64 release_http_reply(rule->arg.http_reply);
65 rule->arg.http_reply = NULL;
66}
67
68
69/* Check function for HTTP actions relying on an http reply. The function
70 * returns 1 in success case, otherwise, it returns 0 and err is filled.
71 */
72static int check_act_http_reply(struct act_rule *rule, struct proxy *px, char **err)
73{
74 struct http_reply *reply = rule->arg.http_reply;
75
76 if (!http_check_http_reply(reply, px, err)) {
77 release_act_http_reply(rule);
78 return 0;
79 }
80 return 1;
81}
82
Willy Tarreau79e57332018-10-02 16:01:16 +020083
84/* This function executes one of the set-{method,path,query,uri} actions. It
85 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010086 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050087 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010088 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
89 * occurs while soft rewrites are enabled, the action is canceled, but the rule
90 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020091 */
92static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
93 struct session *sess, struct stream *s, int flags)
94{
95 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010096 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020097
98 replace = alloc_trash_chunk();
99 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100100 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +0200101
102 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +0100103 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200104 replace->area[replace->data++] = '?';
105 replace->data += build_logline(s, replace->area + replace->data,
106 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +0100107 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200108
Christopher Faulet2c22a692019-12-18 15:39:56 +0100109 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100110 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +0200111
Christopher Faulete00d06c2019-12-16 17:18:42 +0100112 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +0200113 free_trash_chunk(replace);
114 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100115
116 fail_alloc:
117 if (!(s->flags & SF_ERR_MASK))
118 s->flags |= SF_ERR_RESOURCE;
119 ret = ACT_RET_ERR;
120 goto leave;
121
122 fail_rewrite:
123 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
124 if (s->flags & SF_BE_ASSIGNED)
125 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
126 if (sess->listener->counters)
127 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
128 if (objt_server(s->target))
129 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
130
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100131 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100132 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100133 if (!(s->flags & SF_ERR_MASK))
134 s->flags |= SF_ERR_PRXCOND;
135 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100136 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200137}
138
139/* parse an http-request action among :
140 * set-method
141 * set-path
142 * set-query
143 * set-uri
144 *
145 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100146 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100147 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
148 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200149 */
150static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
151 struct act_rule *rule, char **err)
152{
153 int cur_arg = *orig_arg;
154
Willy Tarreau79e57332018-10-02 16:01:16 +0200155 switch (args[0][4]) {
156 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100157 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200158 break;
159 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100160 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200161 break;
162 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100163 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200164 break;
165 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100166 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200167 break;
168 default:
169 memprintf(err, "internal error: unhandled action '%s'", args[0]);
170 return ACT_RET_PRS_ERR;
171 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100172 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100173 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200174
175 if (!*args[cur_arg] ||
176 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
177 memprintf(err, "expects exactly 1 argument <format>");
178 return ACT_RET_PRS_ERR;
179 }
180
Christopher Faulet96bff762019-12-17 13:46:18 +0100181 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200182 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100183 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200184 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
185 return ACT_RET_PRS_ERR;
186 }
187
188 (*orig_arg)++;
189 return ACT_RET_PRS_OK;
190}
191
Willy Tarreau33810222019-06-12 17:44:02 +0200192/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100193 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200194 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100195 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100196 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100197 * contains 1 for the path or 3 for the URI (values used by
198 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
199 * occurs while soft rewrites are enabled, the action is canceled, but the rule
200 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200201 */
202static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
203 struct session *sess, struct stream *s, int flags)
204{
Christopher Faulet13403762019-12-13 09:01:57 +0100205 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200206 struct buffer *replace, *output;
207 struct ist uri;
208 int len;
209
210 replace = alloc_trash_chunk();
211 output = alloc_trash_chunk();
212 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100213 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200214 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100215
Christopher Faulet2c22a692019-12-18 15:39:56 +0100216 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100217 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100218
Christopher Faulet96bff762019-12-17 13:46:18 +0100219 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200220 goto leave;
221
Christopher Faulet96bff762019-12-17 13:46:18 +0100222 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200223
224 /* note: uri.ptr doesn't need to be zero-terminated because it will
225 * only be used to pick pmatch references.
226 */
227 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
228 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100229 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200230
Christopher Faulet2c22a692019-12-18 15:39:56 +0100231 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100232 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200233
Christopher Faulete00d06c2019-12-16 17:18:42 +0100234 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200235 free_trash_chunk(output);
236 free_trash_chunk(replace);
237 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100238
239 fail_alloc:
240 if (!(s->flags & SF_ERR_MASK))
241 s->flags |= SF_ERR_RESOURCE;
242 ret = ACT_RET_ERR;
243 goto leave;
244
245 fail_rewrite:
246 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
247 if (s->flags & SF_BE_ASSIGNED)
248 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
249 if (sess->listener->counters)
250 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
251 if (objt_server(s->target))
252 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
253
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100254 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100255 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100256 if (!(s->flags & SF_ERR_MASK))
257 s->flags |= SF_ERR_PRXCOND;
258 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100259 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200260}
261
Willy Tarreau262c3f12019-12-17 06:52:51 +0100262/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200263 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100264 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100265 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200266 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
267 */
268static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
269 struct act_rule *rule, char **err)
270{
271 int cur_arg = *orig_arg;
272 char *error = NULL;
273
Willy Tarreau262c3f12019-12-17 06:52:51 +0100274 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100275 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100276 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100277 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100278
Willy Tarreau33810222019-06-12 17:44:02 +0200279 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100280 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200281
282 if (!*args[cur_arg] || !*args[cur_arg+1] ||
283 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
284 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
285 return ACT_RET_PRS_ERR;
286 }
287
Christopher Faulet96bff762019-12-17 13:46:18 +0100288 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200289 memprintf(err, "failed to parse the regex : %s", error);
290 free(error);
291 return ACT_RET_PRS_ERR;
292 }
293
Christopher Faulet96bff762019-12-17 13:46:18 +0100294 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200295 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100296 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200297 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100298 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200299 return ACT_RET_PRS_ERR;
300 }
301
302 (*orig_arg) += 2;
303 return ACT_RET_PRS_OK;
304}
305
Willy Tarreau79e57332018-10-02 16:01:16 +0200306/* This function is just a compliant action wrapper for "set-status". */
307static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
308 struct session *sess, struct stream *s, int flags)
309{
Christopher Faulet96bff762019-12-17 13:46:18 +0100310 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100311 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
312 if (s->flags & SF_BE_ASSIGNED)
313 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
314 if (sess->listener->counters)
315 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
316 if (objt_server(s->target))
317 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
318
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100319 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100320 if (!(s->flags & SF_ERR_MASK))
321 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100322 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100323 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100324 }
325
Willy Tarreau79e57332018-10-02 16:01:16 +0200326 return ACT_RET_CONT;
327}
328
329/* parse set-status action:
330 * This action accepts a single argument of type int representing
331 * an http status code. It returns ACT_RET_PRS_OK on success,
332 * ACT_RET_PRS_ERR on error.
333 */
334static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
335 struct act_rule *rule, char **err)
336{
337 char *error;
338
339 rule->action = ACT_CUSTOM;
340 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100341 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200342
343 /* Check if an argument is available */
344 if (!*args[*orig_arg]) {
345 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
346 return ACT_RET_PRS_ERR;
347 }
348
349 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100350 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
351 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200352 memprintf(err, "expects an integer status code between 100 and 999");
353 return ACT_RET_PRS_ERR;
354 }
355
356 (*orig_arg)++;
357
358 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100359 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200360 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
361 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
362 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100363 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
364 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200365 (*orig_arg)++;
366 }
367
Christopher Fauletc20b3712020-01-27 15:51:56 +0100368 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200369 return ACT_RET_PRS_OK;
370}
371
372/* This function executes the "reject" HTTP action. It clears the request and
373 * response buffer without sending any response. It can be useful as an HTTP
374 * alternative to the silent-drop action to defend against DoS attacks, and may
375 * also be used with HTTP/2 to close a connection instead of just a stream.
376 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100377 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200378 */
379static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
380 struct session *sess, struct stream *s, int flags)
381{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100382 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200383 channel_abort(&s->req);
384 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100385 s->req.analysers &= AN_REQ_FLT_END;
386 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200387
Olivier Houcharda798bf52019-03-08 18:52:00 +0100388 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
389 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200390 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100391 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200392
393 if (!(s->flags & SF_ERR_MASK))
394 s->flags |= SF_ERR_PRXCOND;
395 if (!(s->flags & SF_FINST_MASK))
396 s->flags |= SF_FINST_R;
397
Christopher Faulet90d22a82020-03-06 11:18:39 +0100398 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200399}
400
401/* parse the "reject" action:
402 * This action takes no argument and returns ACT_RET_PRS_OK on success,
403 * ACT_RET_PRS_ERR on error.
404 */
405static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
406 struct act_rule *rule, char **err)
407{
408 rule->action = ACT_CUSTOM;
409 rule->action_ptr = http_action_reject;
410 return ACT_RET_PRS_OK;
411}
412
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200413/* This function executes the "disable-l7-retry" HTTP action.
414 * It disables L7 retries (all retry except for a connection failure). This
415 * can be useful for example to avoid retrying on POST requests.
416 * It just removes the L7 retry flag on the stream_interface, and always
417 * return ACT_RET_CONT;
418 */
419static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
420 struct session *sess, struct stream *s, int flags)
421{
422 struct stream_interface *si = &s->si[1];
423
424 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
425 * let's be future-proof and remove it anyway.
426 */
427 si->flags &= ~SI_FL_L7_RETRY;
428 si->flags |= SI_FL_D_L7_RETRY;
429 return ACT_RET_CONT;
430}
431
432/* parse the "disable-l7-retry" action:
433 * This action takes no argument and returns ACT_RET_PRS_OK on success,
434 * ACT_RET_PRS_ERR on error.
435 */
436static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
437 int *orig_args, struct proxy *px,
438 struct act_rule *rule, char **err)
439{
440 rule->action = ACT_CUSTOM;
441 rule->action_ptr = http_req_disable_l7_retry;
442 return ACT_RET_PRS_OK;
443}
444
Willy Tarreau79e57332018-10-02 16:01:16 +0200445/* This function executes the "capture" action. It executes a fetch expression,
446 * turns the result into a string and puts it in a capture slot. It always
447 * returns 1. If an error occurs the action is cancelled, but the rule
448 * processing continues.
449 */
450static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
451 struct session *sess, struct stream *s, int flags)
452{
453 struct sample *key;
454 struct cap_hdr *h = rule->arg.cap.hdr;
455 char **cap = s->req_cap;
456 int len;
457
458 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
459 if (!key)
460 return ACT_RET_CONT;
461
462 if (cap[h->index] == NULL)
463 cap[h->index] = pool_alloc(h->pool);
464
465 if (cap[h->index] == NULL) /* no more capture memory */
466 return ACT_RET_CONT;
467
468 len = key->data.u.str.data;
469 if (len > h->len)
470 len = h->len;
471
472 memcpy(cap[h->index], key->data.u.str.area, len);
473 cap[h->index][len] = 0;
474 return ACT_RET_CONT;
475}
476
477/* This function executes the "capture" action and store the result in a
478 * capture slot if exists. It executes a fetch expression, turns the result
479 * into a string and puts it in a capture slot. It always returns 1. If an
480 * error occurs the action is cancelled, but the rule processing continues.
481 */
482static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
483 struct session *sess, struct stream *s, int flags)
484{
485 struct sample *key;
486 struct cap_hdr *h;
487 char **cap = s->req_cap;
488 struct proxy *fe = strm_fe(s);
489 int len;
490 int i;
491
492 /* Look for the original configuration. */
493 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
494 h != NULL && i != rule->arg.capid.idx ;
495 i--, h = h->next);
496 if (!h)
497 return ACT_RET_CONT;
498
499 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
500 if (!key)
501 return ACT_RET_CONT;
502
503 if (cap[h->index] == NULL)
504 cap[h->index] = pool_alloc(h->pool);
505
506 if (cap[h->index] == NULL) /* no more capture memory */
507 return ACT_RET_CONT;
508
509 len = key->data.u.str.data;
510 if (len > h->len)
511 len = h->len;
512
513 memcpy(cap[h->index], key->data.u.str.area, len);
514 cap[h->index][len] = 0;
515 return ACT_RET_CONT;
516}
517
518/* Check an "http-request capture" action.
519 *
520 * The function returns 1 in success case, otherwise, it returns 0 and err is
521 * filled.
522 */
523static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
524{
525 if (rule->action_ptr != http_action_req_capture_by_id)
526 return 1;
527
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100528 /* capture slots can only be declared in frontends, so we can't check their
529 * existence in backends at configuration parsing step
530 */
531 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200532 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
533 rule->arg.capid.idx);
534 return 0;
535 }
536
537 return 1;
538}
539
Christopher Faulet2eb53962020-01-14 14:47:34 +0100540/* Release memory allocate by an http capture action */
541static void release_http_capture(struct act_rule *rule)
542{
543 if (rule->action_ptr == http_action_req_capture)
544 release_sample_expr(rule->arg.cap.expr);
545 else
546 release_sample_expr(rule->arg.capid.expr);
547}
548
Willy Tarreau79e57332018-10-02 16:01:16 +0200549/* parse an "http-request capture" action. It takes a single argument which is
550 * a sample fetch expression. It stores the expression into arg->act.p[0] and
551 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
552 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
553 */
554static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
555 struct act_rule *rule, char **err)
556{
557 struct sample_expr *expr;
558 struct cap_hdr *hdr;
559 int cur_arg;
560 int len = 0;
561
562 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
563 if (strcmp(args[cur_arg], "if") == 0 ||
564 strcmp(args[cur_arg], "unless") == 0)
565 break;
566
567 if (cur_arg < *orig_arg + 3) {
568 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
569 return ACT_RET_PRS_ERR;
570 }
571
572 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100573 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 +0200574 if (!expr)
575 return ACT_RET_PRS_ERR;
576
577 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
578 memprintf(err,
579 "fetch method '%s' extracts information from '%s', none of which is available here",
580 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100581 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200582 return ACT_RET_PRS_ERR;
583 }
584
585 if (!args[cur_arg] || !*args[cur_arg]) {
586 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100587 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200588 return ACT_RET_PRS_ERR;
589 }
590
591 if (strcmp(args[cur_arg], "len") == 0) {
592 cur_arg++;
593
594 if (!(px->cap & PR_CAP_FE)) {
595 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100596 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200597 return ACT_RET_PRS_ERR;
598 }
599
600 px->conf.args.ctx = ARGC_CAP;
601
602 if (!args[cur_arg]) {
603 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100604 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200605 return ACT_RET_PRS_ERR;
606 }
607 /* we copy the table name for now, it will be resolved later */
608 len = atoi(args[cur_arg]);
609 if (len <= 0) {
610 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100611 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200612 return ACT_RET_PRS_ERR;
613 }
614 cur_arg++;
615
Willy Tarreau79e57332018-10-02 16:01:16 +0200616 hdr = calloc(1, sizeof(*hdr));
617 hdr->next = px->req_cap;
618 hdr->name = NULL; /* not a header capture */
619 hdr->namelen = 0;
620 hdr->len = len;
621 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
622 hdr->index = px->nb_req_cap++;
623
624 px->req_cap = hdr;
625 px->to_log |= LW_REQHDR;
626
627 rule->action = ACT_CUSTOM;
628 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100629 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200630 rule->arg.cap.expr = expr;
631 rule->arg.cap.hdr = hdr;
632 }
633
634 else if (strcmp(args[cur_arg], "id") == 0) {
635 int id;
636 char *error;
637
638 cur_arg++;
639
640 if (!args[cur_arg]) {
641 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100642 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200643 return ACT_RET_PRS_ERR;
644 }
645
646 id = strtol(args[cur_arg], &error, 10);
647 if (*error != '\0') {
648 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100649 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200650 return ACT_RET_PRS_ERR;
651 }
652 cur_arg++;
653
654 px->conf.args.ctx = ARGC_CAP;
655
656 rule->action = ACT_CUSTOM;
657 rule->action_ptr = http_action_req_capture_by_id;
658 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100659 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200660 rule->arg.capid.expr = expr;
661 rule->arg.capid.idx = id;
662 }
663
664 else {
665 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100666 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200667 return ACT_RET_PRS_ERR;
668 }
669
670 *orig_arg = cur_arg;
671 return ACT_RET_PRS_OK;
672}
673
674/* This function executes the "capture" action and store the result in a
675 * capture slot if exists. It executes a fetch expression, turns the result
676 * into a string and puts it in a capture slot. It always returns 1. If an
677 * error occurs the action is cancelled, but the rule processing continues.
678 */
679static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
680 struct session *sess, struct stream *s, int flags)
681{
682 struct sample *key;
683 struct cap_hdr *h;
684 char **cap = s->res_cap;
685 struct proxy *fe = strm_fe(s);
686 int len;
687 int i;
688
689 /* Look for the original configuration. */
690 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
691 h != NULL && i != rule->arg.capid.idx ;
692 i--, h = h->next);
693 if (!h)
694 return ACT_RET_CONT;
695
696 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
697 if (!key)
698 return ACT_RET_CONT;
699
700 if (cap[h->index] == NULL)
701 cap[h->index] = pool_alloc(h->pool);
702
703 if (cap[h->index] == NULL) /* no more capture memory */
704 return ACT_RET_CONT;
705
706 len = key->data.u.str.data;
707 if (len > h->len)
708 len = h->len;
709
710 memcpy(cap[h->index], key->data.u.str.area, len);
711 cap[h->index][len] = 0;
712 return ACT_RET_CONT;
713}
714
715/* Check an "http-response capture" action.
716 *
717 * The function returns 1 in success case, otherwise, it returns 0 and err is
718 * filled.
719 */
720static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
721{
722 if (rule->action_ptr != http_action_res_capture_by_id)
723 return 1;
724
725 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
726 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
727 rule->arg.capid.idx);
728 return 0;
729 }
730
731 return 1;
732}
733
734/* parse an "http-response capture" action. It takes a single argument which is
735 * a sample fetch expression. It stores the expression into arg->act.p[0] and
736 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
737 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
738 */
739static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
740 struct act_rule *rule, char **err)
741{
742 struct sample_expr *expr;
743 int cur_arg;
744 int id;
745 char *error;
746
747 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
748 if (strcmp(args[cur_arg], "if") == 0 ||
749 strcmp(args[cur_arg], "unless") == 0)
750 break;
751
752 if (cur_arg < *orig_arg + 3) {
753 memprintf(err, "expects <expression> id <idx>");
754 return ACT_RET_PRS_ERR;
755 }
756
757 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100758 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 +0200759 if (!expr)
760 return ACT_RET_PRS_ERR;
761
762 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
763 memprintf(err,
764 "fetch method '%s' extracts information from '%s', none of which is available here",
765 args[cur_arg-1], sample_src_names(expr->fetch->use));
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 if (!args[cur_arg] || !*args[cur_arg]) {
771 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100772 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200773 return ACT_RET_PRS_ERR;
774 }
775
776 if (strcmp(args[cur_arg], "id") != 0) {
777 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100778 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200779 return ACT_RET_PRS_ERR;
780 }
781
782 cur_arg++;
783
784 if (!args[cur_arg]) {
785 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100786 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200787 return ACT_RET_PRS_ERR;
788 }
789
790 id = strtol(args[cur_arg], &error, 10);
791 if (*error != '\0') {
792 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100793 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200794 return ACT_RET_PRS_ERR;
795 }
796 cur_arg++;
797
798 px->conf.args.ctx = ARGC_CAP;
799
800 rule->action = ACT_CUSTOM;
801 rule->action_ptr = http_action_res_capture_by_id;
802 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100803 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200804 rule->arg.capid.expr = expr;
805 rule->arg.capid.idx = id;
806
807 *orig_arg = cur_arg;
808 return ACT_RET_PRS_OK;
809}
810
Christopher Faulet81e20172019-12-12 16:40:30 +0100811/* Parse a "allow" action for a request or a response rule. It takes no argument. It
812 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
813 */
814static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
815 struct act_rule *rule, char **err)
816{
817 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100818 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100819 return ACT_RET_PRS_OK;
820}
821
Christopher Faulete0fca292020-01-13 21:49:03 +0100822/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200823 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
824 * error. It relies on http_parse_http_reply() to set
825 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100826 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100827static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
828 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100829{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200830 int default_status;
831 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100832
833 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100834 if (rule->from == ACT_F_HTTP_REQ) {
835 if (!strcmp(args[cur_arg-1], "tarpit")) {
836 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200837 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100838 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100839 else {
840 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200841 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100842 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100843 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100844 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100845 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200846 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100847 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100848
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200849 /* If no args or only a deny_status specified, fallback on the legacy
850 * mode and use default error files despite the fact that
851 * default-errorfiles is not used. Otherwise, parse an http reply.
852 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100853
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200854 /* Prepare parsing of log-format strings */
855 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100856
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200857 if (!*(args[cur_arg])) {
858 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
859 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100860 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200861
862 if (strcmp(args[cur_arg], "deny_status") == 0) {
863 if (!*(args[cur_arg+2]) ||
864 (strcmp(args[cur_arg+2], "errorfile") != 0 && strcmp(args[cur_arg+2], "errorfiles") != 0)) {
865 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
866 &arg, px, default_status, err);
867 *orig_arg += 2;
868 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100869 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200870 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100871 }
872
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200873 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100874
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200875 end:
876 if (!rule->arg.http_reply)
877 return ACT_RET_PRS_ERR;
878
879 rule->flags |= ACT_FLAG_FINAL;
880 rule->check_ptr = check_act_http_reply;
881 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +0100882 return ACT_RET_PRS_OK;
883}
884
Christopher Fauletb3048832020-05-27 15:26:43 +0200885
886/* This function executes a auth action. It builds an 401/407 HTX message using
887 * the corresponding proxy's error message. On success, it returns
888 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
889 */
890static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
891 struct session *sess, struct stream *s, int flags)
892{
893 struct channel *req = &s->req;
894 struct channel *res = &s->res;
895 struct htx *htx = htx_from_buf(&res->buf);
896 struct http_reply *reply;
897 const char *auth_realm;
898 struct http_hdr_ctx ctx;
899 struct ist hdr;
900
901 /* Auth might be performed on regular http-req rules as well as on stats */
902 auth_realm = rule->arg.http.str.ptr;
903 if (!auth_realm) {
904 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
905 auth_realm = STATS_DEFAULT_REALM;
906 else
907 auth_realm = px->id;
908 }
909
910 if (!(s->txn->flags & TX_USE_PX_CONN)) {
911 s->txn->status = 401;
912 hdr = ist("WWW-Authenticate");
913 }
914 else {
915 s->txn->status = 407;
916 hdr = ist("Proxy-Authenticate");
917 }
918 reply = http_error_message(s);
919 channel_htx_truncate(res, htx);
920
921 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
922 goto fail;
923
924 /* Write the generic 40x message */
925 if (http_reply_to_htx(s, htx, reply) == -1)
926 goto fail;
927
928 /* Remove all existing occurrences of the XXX-Authenticate header */
929 ctx.blk = NULL;
930 while (http_find_header(htx, hdr, &ctx, 1))
931 http_remove_header(htx, &ctx);
932
933 /* Now a the right XXX-Authenticate header */
934 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
935 goto fail;
936
937 /* Finally forward the reply */
938 htx_to_buf(htx, &res->buf);
939 if (!http_forward_proxy_resp(s, 1))
940 goto fail;
941
942 /* Note: Only eval on the request */
943 s->logs.tv_request = now;
944 req->analysers &= AN_REQ_FLT_END;
945
946 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
947 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
948
949 if (!(s->flags & SF_ERR_MASK))
950 s->flags |= SF_ERR_LOCAL;
951 if (!(s->flags & SF_FINST_MASK))
952 s->flags |= SF_FINST_R;
953
954 stream_inc_http_err_ctr(s);
955 return ACT_RET_ABRT;
956
957 fail:
958 /* If an error occurred, remove the incomplete HTTP response from the
959 * buffer */
960 channel_htx_truncate(res, htx);
961 return ACT_RET_ERR;
962}
963
Christopher Faulet81e20172019-12-12 16:40:30 +0100964/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
965 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
966 */
967static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
968 struct act_rule *rule, char **err)
969{
970 int cur_arg;
971
Christopher Fauletb3048832020-05-27 15:26:43 +0200972 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +0100973 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +0200974 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100975 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100976
977 cur_arg = *orig_arg;
978 if (!strcmp(args[cur_arg], "realm")) {
979 cur_arg++;
980 if (!*args[cur_arg]) {
981 memprintf(err, "missing realm value.\n");
982 return ACT_RET_PRS_ERR;
983 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100984 rule->arg.http.str.ptr = strdup(args[cur_arg]);
985 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100986 cur_arg++;
987 }
988
Christopher Fauletc20b3712020-01-27 15:51:56 +0100989 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100990 *orig_arg = cur_arg;
991 return ACT_RET_PRS_OK;
992}
993
994/* Parse a "set-nice" action. It takes the nice value as argument. It returns
995 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
996 */
997static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
998 struct act_rule *rule, char **err)
999{
1000 int cur_arg;
1001
1002 rule->action = ACT_HTTP_SET_NICE;
1003
1004 cur_arg = *orig_arg;
1005 if (!*args[cur_arg]) {
1006 memprintf(err, "expects exactly 1 argument (integer value)");
1007 return ACT_RET_PRS_ERR;
1008 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001009 rule->arg.http.i = atoi(args[cur_arg]);
1010 if (rule->arg.http.i < -1024)
1011 rule->arg.http.i = -1024;
1012 else if (rule->arg.http.i > 1024)
1013 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001014
Christopher Fauletc20b3712020-01-27 15:51:56 +01001015 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001016 *orig_arg = cur_arg + 1;
1017 return ACT_RET_PRS_OK;
1018}
1019
1020/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1021 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1022 */
1023static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1024 struct act_rule *rule, char **err)
1025{
1026#ifdef IP_TOS
1027 char *endp;
1028 int cur_arg;
1029
1030 rule->action = ACT_HTTP_SET_TOS;
1031
1032 cur_arg = *orig_arg;
1033 if (!*args[cur_arg]) {
1034 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1035 return ACT_RET_PRS_ERR;
1036 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001037 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001038 if (endp && *endp != '\0') {
1039 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1040 return ACT_RET_PRS_ERR;
1041 }
1042
Christopher Fauletc20b3712020-01-27 15:51:56 +01001043 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001044 *orig_arg = cur_arg + 1;
1045 return ACT_RET_PRS_OK;
1046#else
1047 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1048 return ACT_RET_PRS_ERR;
1049#endif
1050}
1051
1052/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1053 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1054 */
1055static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1056 struct act_rule *rule, char **err)
1057{
1058#ifdef SO_MARK
1059 char *endp;
1060 int cur_arg;
1061
1062 rule->action = ACT_HTTP_SET_MARK;
1063
1064 cur_arg = *orig_arg;
1065 if (!*args[cur_arg]) {
1066 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1067 return ACT_RET_PRS_ERR;
1068 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001069 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001070 if (endp && *endp != '\0') {
1071 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1072 return ACT_RET_PRS_ERR;
1073 }
1074
Christopher Fauletc20b3712020-01-27 15:51:56 +01001075 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001076 *orig_arg = cur_arg + 1;
1077 global.last_checks |= LSTCHK_NETADM;
1078 return ACT_RET_PRS_OK;
1079#else
1080 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1081 return ACT_RET_PRS_ERR;
1082#endif
1083}
1084
1085/* Parse a "set-log-level" action. It takes the level value as argument. It
1086 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1087 */
1088static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1089 struct act_rule *rule, char **err)
1090{
1091 int cur_arg;
1092
1093 rule->action = ACT_HTTP_SET_LOGL;
1094
1095 cur_arg = *orig_arg;
1096 if (!*args[cur_arg]) {
1097 bad_log_level:
1098 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1099 return ACT_RET_PRS_ERR;
1100 }
1101 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001102 rule->arg.http.i = -1;
1103 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001104 goto bad_log_level;
1105
Christopher Fauletc20b3712020-01-27 15:51:56 +01001106 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001107 *orig_arg = cur_arg + 1;
1108 return ACT_RET_PRS_OK;
1109}
1110
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001111/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1112 * 103 response header with <.arg.http.str> name and with a value built
1113 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001114 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001115 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1116 * block is added to terminate the response. On success, it returns
1117 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1118 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1119 * returned.
1120 */
1121static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1122 struct session *sess, struct stream *s, int flags)
1123{
1124 struct act_rule *prev_rule, *next_rule;
1125 struct channel *res = &s->res;
1126 struct htx *htx = htx_from_buf(&res->buf);
1127 struct buffer *value = alloc_trash_chunk();
1128 enum act_return ret = ACT_RET_CONT;
1129
1130 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1131 goto leave;
1132
1133 if (!value) {
1134 if (!(s->flags & SF_ERR_MASK))
1135 s->flags |= SF_ERR_RESOURCE;
1136 goto error;
1137 }
1138
1139 /* get previous and next rules */
1140 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1141 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1142
1143 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1144 * continue to add link to a previously started response */
1145 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1146 struct htx_sl *sl;
1147 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1148 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1149
1150 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1151 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1152 if (!sl)
1153 goto error;
1154 sl->info.res.status = 103;
1155 }
1156
1157 /* Add the HTTP Early Hint HTTP 103 response heade */
1158 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1159 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1160 goto error;
1161
1162 /* if it is the last rule or the next one is not an early-hint, terminate the current
1163 * response. */
1164 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001165 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1166 /* If an error occurred during an Early-hint rule,
1167 * remove the incomplete HTTP 103 response from the
1168 * buffer */
1169 goto error;
1170 }
1171
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001172 if (!http_forward_proxy_resp(s, 0))
1173 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001174 }
1175
1176 leave:
1177 free_trash_chunk(value);
1178 return ret;
1179
1180 error:
1181 /* If an error occurred during an Early-hint rule, remove the incomplete
1182 * HTTP 103 response from the buffer */
1183 channel_htx_truncate(res, htx);
1184 ret = ACT_RET_ERR;
1185 goto leave;
1186}
1187
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001188/* This function executes a set-header or add-header actions. It builds a string
1189 * in the trash from the specified format string. It finds the action to be
1190 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001191 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001192 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1193 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1194 * ACT_RET_ERR is returned.
1195 */
1196static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1197 struct session *sess, struct stream *s, int flags)
1198{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001199 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1200 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001201 enum act_return ret = ACT_RET_CONT;
1202 struct buffer *replace;
1203 struct http_hdr_ctx ctx;
1204 struct ist n, v;
1205
1206 replace = alloc_trash_chunk();
1207 if (!replace)
1208 goto fail_alloc;
1209
1210 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1211 n = rule->arg.http.str;
1212 v = ist2(replace->area, replace->data);
1213
1214 if (rule->action == 0) { // set-header
1215 /* remove all occurrences of the header */
1216 ctx.blk = NULL;
1217 while (http_find_header(htx, n, &ctx, 1))
1218 http_remove_header(htx, &ctx);
1219 }
1220
1221 /* Now add header */
1222 if (!http_add_header(htx, n, v))
1223 goto fail_rewrite;
1224
1225 leave:
1226 free_trash_chunk(replace);
1227 return ret;
1228
1229 fail_alloc:
1230 if (!(s->flags & SF_ERR_MASK))
1231 s->flags |= SF_ERR_RESOURCE;
1232 ret = ACT_RET_ERR;
1233 goto leave;
1234
1235 fail_rewrite:
1236 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1237 if (s->flags & SF_BE_ASSIGNED)
1238 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1239 if (sess->listener->counters)
1240 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1241 if (objt_server(s->target))
1242 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1243
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001244 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001245 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001246 if (!(s->flags & SF_ERR_MASK))
1247 s->flags |= SF_ERR_PRXCOND;
1248 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001249 goto leave;
1250}
1251
Christopher Faulet81e20172019-12-12 16:40:30 +01001252/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1253 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1254 * on success, ACT_RET_PRS_ERR on error.
1255 *
1256 * Note: same function is used for the request and the response. However
1257 * "early-hint" rules are only supported for request rules.
1258 */
1259static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1260 struct act_rule *rule, char **err)
1261{
Christopher Faulet81e20172019-12-12 16:40:30 +01001262 int cap, cur_arg;
1263
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001264 if (args[*orig_arg-1][0] == 'e') {
1265 rule->action = ACT_CUSTOM;
1266 rule->action_ptr = http_action_early_hint;
1267 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001268 else {
1269 if (args[*orig_arg-1][0] == 's')
1270 rule->action = 0; // set-header
1271 else
1272 rule->action = 1; // add-header
1273 rule->action_ptr = http_action_set_header;
1274 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001275 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001276
1277 cur_arg = *orig_arg;
1278 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1279 memprintf(err, "expects exactly 2 arguments");
1280 return ACT_RET_PRS_ERR;
1281 }
1282
Christopher Faulet81e20172019-12-12 16:40:30 +01001283
Christopher Faulet96bff762019-12-17 13:46:18 +01001284 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1285 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1286 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001287
1288 if (rule->from == ACT_F_HTTP_REQ) {
1289 px->conf.args.ctx = ARGC_HRQ;
1290 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1291 }
1292 else{
1293 px->conf.args.ctx = ARGC_HRS;
1294 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1295 }
1296
1297 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001298 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001299 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001300 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001301 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001302
1303 free(px->conf.lfs_file);
1304 px->conf.lfs_file = strdup(px->conf.args.file);
1305 px->conf.lfs_line = px->conf.args.line;
1306
1307 *orig_arg = cur_arg + 1;
1308 return ACT_RET_PRS_OK;
1309}
1310
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001311/* This function executes a replace-header or replace-value actions. It
1312 * builds a string in the trash from the specified format string. It finds
1313 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001314 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001315 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1316 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1317 * processing continue. Otherwsize ACT_RET_ERR is returned.
1318 */
1319static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1320 struct session *sess, struct stream *s, int flags)
1321{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001322 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1323 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001324 enum act_return ret = ACT_RET_CONT;
1325 struct buffer *replace;
1326 int r;
1327
1328 replace = alloc_trash_chunk();
1329 if (!replace)
1330 goto fail_alloc;
1331
1332 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1333
1334 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1335 if (r == -1)
1336 goto fail_rewrite;
1337
1338 leave:
1339 free_trash_chunk(replace);
1340 return ret;
1341
1342 fail_alloc:
1343 if (!(s->flags & SF_ERR_MASK))
1344 s->flags |= SF_ERR_RESOURCE;
1345 ret = ACT_RET_ERR;
1346 goto leave;
1347
1348 fail_rewrite:
1349 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1350 if (s->flags & SF_BE_ASSIGNED)
1351 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1352 if (sess->listener->counters)
1353 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1354 if (objt_server(s->target))
1355 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1356
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001357 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001358 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001359 if (!(s->flags & SF_ERR_MASK))
1360 s->flags |= SF_ERR_PRXCOND;
1361 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001362 goto leave;
1363}
1364
Christopher Faulet81e20172019-12-12 16:40:30 +01001365/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1366 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1367 * success, ACT_RET_PRS_ERR on error.
1368 */
1369static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1370 struct act_rule *rule, char **err)
1371{
1372 int cap, cur_arg;
1373
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001374 if (args[*orig_arg-1][8] == 'h')
1375 rule->action = 0; // replace-header
1376 else
1377 rule->action = 1; // replace-value
1378 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001379 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001380
1381 cur_arg = *orig_arg;
1382 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1383 memprintf(err, "expects exactly 3 arguments");
1384 return ACT_RET_PRS_ERR;
1385 }
1386
Christopher Faulet96bff762019-12-17 13:46:18 +01001387 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1388 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1389 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001390
1391 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001392 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001393 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001394 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001395 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001396
1397 if (rule->from == ACT_F_HTTP_REQ) {
1398 px->conf.args.ctx = ARGC_HRQ;
1399 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1400 }
1401 else{
1402 px->conf.args.ctx = ARGC_HRS;
1403 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1404 }
1405
1406 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001407 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001408 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001409 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001410 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001411 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001412
1413 free(px->conf.lfs_file);
1414 px->conf.lfs_file = strdup(px->conf.args.file);
1415 px->conf.lfs_line = px->conf.args.line;
1416
1417 *orig_arg = cur_arg + 1;
1418 return ACT_RET_PRS_OK;
1419}
1420
1421/* Parse a "del-header" action. It takes an header name as argument. It returns
1422 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1423 */
1424static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1425 struct act_rule *rule, char **err)
1426{
1427 int cur_arg;
1428
1429 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001430 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001431
1432 cur_arg = *orig_arg;
1433 if (!*args[cur_arg]) {
1434 memprintf(err, "expects exactly 1 arguments");
1435 return ACT_RET_PRS_ERR;
1436 }
1437
Christopher Faulet96bff762019-12-17 13:46:18 +01001438 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1439 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001440 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1441
Christopher Fauletc20b3712020-01-27 15:51:56 +01001442 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001443 *orig_arg = cur_arg + 1;
1444 return ACT_RET_PRS_OK;
1445}
1446
Christopher Faulet2eb53962020-01-14 14:47:34 +01001447/* Release memory allocated by an http redirect action. */
1448static void release_http_redir(struct act_rule *rule)
1449{
1450 struct logformat_node *lf, *lfb;
1451 struct redirect_rule *redir;
1452
1453 redir = rule->arg.redir;
1454 LIST_DEL(&redir->list);
1455 if (redir->cond) {
1456 prune_acl_cond(redir->cond);
1457 free(redir->cond);
1458 }
1459 free(redir->rdr_str);
1460 free(redir->cookie_str);
1461 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1462 LIST_DEL(&lf->list);
1463 free(lf);
1464 }
1465 free(redir);
1466}
1467
Christopher Faulet81e20172019-12-12 16:40:30 +01001468/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1469 * ACT_RET_PRS_ERR on error.
1470 */
1471static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1472 struct act_rule *rule, char **err)
1473{
1474 struct redirect_rule *redir;
1475 int dir, cur_arg;
1476
1477 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001478 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001479 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001480
1481 cur_arg = *orig_arg;
1482
1483 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1484 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1485 return ACT_RET_PRS_ERR;
1486
1487 rule->arg.redir = redir;
1488 rule->cond = redir->cond;
1489 redir->cond = NULL;
1490
1491 /* skip all arguments */
1492 while (*args[cur_arg])
1493 cur_arg++;
1494
1495 *orig_arg = cur_arg;
1496 return ACT_RET_PRS_OK;
1497}
1498
Christopher Faulet046cf442019-12-17 15:45:23 +01001499/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1500 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1501 */
1502static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1503 struct session *sess, struct stream *s, int flags)
1504{
1505 struct pat_ref *ref;
1506 struct buffer *key = NULL, *value = NULL;
1507 enum act_return ret = ACT_RET_CONT;
1508
1509 /* collect reference */
1510 ref = pat_ref_lookup(rule->arg.map.ref);
1511 if (!ref)
1512 goto leave;
1513
1514 /* allocate key */
1515 key = alloc_trash_chunk();
1516 if (!key)
1517 goto fail_alloc;
1518
1519 /* collect key */
1520 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1521 key->area[key->data] = '\0';
1522
1523 switch (rule->action) {
1524 case 0: // add-acl
1525 /* add entry only if it does not already exist */
1526 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1527 if (pat_ref_find_elt(ref, key->area) == NULL)
1528 pat_ref_add(ref, key->area, NULL, NULL);
1529 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1530 break;
1531
1532 case 1: // set-map
1533 /* allocate value */
1534 value = alloc_trash_chunk();
1535 if (!value)
1536 goto fail_alloc;
1537
1538 /* collect value */
1539 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1540 value->area[value->data] = '\0';
1541
1542 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1543 if (pat_ref_find_elt(ref, key->area) != NULL) {
1544 /* update entry if it exists */
1545 pat_ref_set(ref, key->area, value->area, NULL);
1546 }
1547 else {
1548 /* insert a new entry */
1549 pat_ref_add(ref, key->area, value->area, NULL);
1550 }
1551 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1552 break;
1553
1554 case 2: // del-acl
1555 case 3: // del-map
1556 /* returned code: 1=ok, 0=ko */
1557 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1558 pat_ref_delete(ref, key->area);
1559 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1560 break;
1561
1562 default:
1563 ret = ACT_RET_ERR;
1564 }
1565
1566
1567 leave:
1568 free_trash_chunk(key);
1569 free_trash_chunk(value);
1570 return ret;
1571
1572 fail_alloc:
1573 if (!(s->flags & SF_ERR_MASK))
1574 s->flags |= SF_ERR_RESOURCE;
1575 ret = ACT_RET_ERR;
1576 goto leave;
1577}
1578
Christopher Faulet2eb53962020-01-14 14:47:34 +01001579/* Release memory allocated by an http map/acl action. */
1580static void release_http_map(struct act_rule *rule)
1581{
1582 struct logformat_node *lf, *lfb;
1583
1584 free(rule->arg.map.ref);
1585 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1586 LIST_DEL(&lf->list);
1587 release_sample_expr(lf->expr);
1588 free(lf->arg);
1589 free(lf);
1590 }
1591 if (rule->action == 1) {
1592 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1593 LIST_DEL(&lf->list);
1594 release_sample_expr(lf->expr);
1595 free(lf->arg);
1596 free(lf);
1597 }
1598 }
1599}
1600
Christopher Faulet81e20172019-12-12 16:40:30 +01001601/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001602 * two log-format string as argument depending on the action. The action is
1603 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1604 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001605 */
1606static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1607 struct act_rule *rule, char **err)
1608{
1609 int cap, cur_arg;
1610
Christopher Faulet046cf442019-12-17 15:45:23 +01001611 if (args[*orig_arg-1][0] == 'a') // add-acl
1612 rule->action = 0;
1613 else if (args[*orig_arg-1][0] == 's') // set-map
1614 rule->action = 1;
1615 else if (args[*orig_arg-1][4] == 'a') // del-acl
1616 rule->action = 2;
1617 else if (args[*orig_arg-1][4] == 'm') // del-map
1618 rule->action = 3;
1619 else {
1620 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1621 return ACT_RET_PRS_ERR;
1622 }
1623 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001624 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001625
1626 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001627 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1628 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001629 memprintf(err, "expects exactly 2 arguments");
1630 return ACT_RET_PRS_ERR;
1631 }
1632 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001633 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001634 memprintf(err, "expects exactly 1 arguments");
1635 return ACT_RET_PRS_ERR;
1636 }
1637
1638 /*
1639 * '+ 8' for 'set-map(' (same for del-map)
1640 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1641 */
1642 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1643
1644 if (rule->from == ACT_F_HTTP_REQ) {
1645 px->conf.args.ctx = ARGC_HRQ;
1646 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1647 }
1648 else{
1649 px->conf.args.ctx = ARGC_HRS;
1650 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1651 }
1652
1653 /* key pattern */
1654 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001655 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1656 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001657 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001658 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001659
Christopher Faulet046cf442019-12-17 15:45:23 +01001660 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001661 /* value pattern for set-map only */
1662 cur_arg++;
1663 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001664 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1665 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001666 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001667 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001668 }
1669
1670 free(px->conf.lfs_file);
1671 px->conf.lfs_file = strdup(px->conf.args.file);
1672 px->conf.lfs_line = px->conf.args.line;
1673
1674 *orig_arg = cur_arg + 1;
1675 return ACT_RET_PRS_OK;
1676}
1677
Christopher Fauletac98d812019-12-18 09:20:16 +01001678/* This function executes a track-sc* actions. On success, it returns
1679 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1680 */
1681static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1682 struct session *sess, struct stream *s, int flags)
1683{
1684 struct stktable *t;
1685 struct stksess *ts;
1686 struct stktable_key *key;
1687 void *ptr1, *ptr2, *ptr3, *ptr4;
1688 int opt;
1689
1690 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1691 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1692
1693 t = rule->arg.trk_ctr.table.t;
1694 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1695
1696 if (!key)
1697 goto end;
1698 ts = stktable_get_entry(t, key);
1699 if (!ts)
1700 goto end;
1701
1702 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1703
1704 /* let's count a new HTTP request as it's the first time we do it */
1705 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1706 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1707
1708 /* When the client triggers a 4xx from the server, it's most often due
1709 * to a missing object or permission. These events should be tracked
1710 * because if they happen often, it may indicate a brute force or a
1711 * vulnerability scan. Normally this is done when receiving the response
1712 * but here we're tracking after this ought to have been done so we have
1713 * to do it on purpose.
1714 */
1715 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1716 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1717 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1718 }
1719
1720 if (ptr1 || ptr2 || ptr3 || ptr4) {
1721 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1722
1723 if (ptr1)
1724 stktable_data_cast(ptr1, http_req_cnt)++;
1725 if (ptr2)
1726 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1727 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1728 if (ptr3)
1729 stktable_data_cast(ptr3, http_err_cnt)++;
1730 if (ptr4)
1731 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1732 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1733
1734 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1735
1736 /* If data was modified, we need to touch to re-schedule sync */
1737 stktable_touch_local(t, ts, 0);
1738 }
1739
1740 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1741 if (sess->fe != s->be)
1742 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1743
1744 end:
1745 return ACT_RET_CONT;
1746}
Christopher Faulet81e20172019-12-12 16:40:30 +01001747
Christopher Faulet2eb53962020-01-14 14:47:34 +01001748static void release_http_track_sc(struct act_rule *rule)
1749{
1750 release_sample_expr(rule->arg.trk_ctr.expr);
1751}
1752
Christopher Faulet81e20172019-12-12 16:40:30 +01001753/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1754 * ACT_RET_PRS_ERR on error.
1755 */
1756static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1757 struct act_rule *rule, char **err)
1758{
1759 struct sample_expr *expr;
1760 unsigned int where;
1761 unsigned int tsc_num;
1762 const char *tsc_num_str;
1763 int cur_arg;
1764
1765 tsc_num_str = &args[*orig_arg-1][8];
1766 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1767 return ACT_RET_PRS_ERR;
1768
1769 cur_arg = *orig_arg;
1770 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001771 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001772 if (!expr)
1773 return ACT_RET_PRS_ERR;
1774
1775 where = 0;
1776 if (px->cap & PR_CAP_FE)
1777 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1778 if (px->cap & PR_CAP_BE)
1779 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1780
1781 if (!(expr->fetch->val & where)) {
1782 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1783 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001784 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001785 return ACT_RET_PRS_ERR;
1786 }
1787
1788 if (strcmp(args[cur_arg], "table") == 0) {
1789 cur_arg++;
1790 if (!*args[cur_arg]) {
1791 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001792 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001793 return ACT_RET_PRS_ERR;
1794 }
1795
1796 /* we copy the table name for now, it will be resolved later */
1797 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1798 cur_arg++;
1799 }
1800
Christopher Fauletac98d812019-12-18 09:20:16 +01001801 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001802 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001803 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001804 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001805 rule->check_ptr = check_trk_action;
1806
1807 *orig_arg = cur_arg;
1808 return ACT_RET_PRS_OK;
1809}
1810
Christopher Faulet46f95542019-12-20 10:07:22 +01001811/* This function executes a strict-mode actions. On success, it always returns
1812 * ACT_RET_CONT
1813 */
1814static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1815 struct session *sess, struct stream *s, int flags)
1816{
1817 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1818
1819 if (rule->action == 0) // strict-mode on
1820 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1821 else // strict-mode off
1822 msg->flags |= HTTP_MSGF_SOFT_RW;
1823 return ACT_RET_CONT;
1824}
1825
1826/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1827 * ACT_RET_PRS_ERR on error.
1828 */
1829static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1830 struct act_rule *rule, char **err)
1831{
1832 int cur_arg;
1833
Christopher Faulet46f95542019-12-20 10:07:22 +01001834 cur_arg = *orig_arg;
1835 if (!*args[cur_arg]) {
1836 memprintf(err, "expects exactly 1 arguments");
1837 return ACT_RET_PRS_ERR;
1838 }
1839
1840 if (strcasecmp(args[cur_arg], "on") == 0)
1841 rule->action = 0; // strict-mode on
1842 else if (strcasecmp(args[cur_arg], "off") == 0)
1843 rule->action = 1; // strict-mode off
1844 else {
1845 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1846 return ACT_RET_PRS_ERR;
1847 }
1848 rule->action_ptr = http_action_strict_mode;
1849
1850 *orig_arg = cur_arg + 1;
1851 return ACT_RET_PRS_OK;
1852}
1853
Christopher Faulet24231ab2020-01-24 17:44:23 +01001854/* This function executes a return action. It builds an HTX message from an
1855 * errorfile, an raw file or a log-format string, depending on <.action>
1856 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1857 * returned.
1858 */
1859static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1860 struct session *sess, struct stream *s, int flags)
1861{
1862 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001863
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001864 if (http_reply_message(s, rule->arg.http_reply) == -1)
1865 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001866
Christopher Faulet24231ab2020-01-24 17:44:23 +01001867 if (rule->from == ACT_F_HTTP_REQ) {
1868 /* let's log the request time */
1869 s->logs.tv_request = now;
1870 req->analysers &= AN_REQ_FLT_END;
1871
1872 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1873 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1874 }
1875
1876 if (!(s->flags & SF_ERR_MASK))
1877 s->flags |= SF_ERR_LOCAL;
1878 if (!(s->flags & SF_FINST_MASK))
1879 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1880
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001881 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001882}
1883
Christopher Faulet24231ab2020-01-24 17:44:23 +01001884/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001885 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1886 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001887 */
1888static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1889 struct act_rule *rule, char **err)
1890{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001891 /* Prepare parsing of log-format strings */
1892 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1893 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1894 if (!rule->arg.http_reply)
1895 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001896
Christopher Fauletba946bf2020-05-13 08:50:07 +02001897 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001898 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001899 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001900 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001901 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001902 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001903}
1904
Willy Tarreau79e57332018-10-02 16:01:16 +02001905/************************************************************************/
1906/* All supported http-request action keywords must be declared here. */
1907/************************************************************************/
1908
1909static struct action_kw_list http_req_actions = {
1910 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001911 { "add-acl", parse_http_set_map, 1 },
1912 { "add-header", parse_http_set_header, 0 },
1913 { "allow", parse_http_allow, 0 },
1914 { "auth", parse_http_auth, 0 },
1915 { "capture", parse_http_req_capture, 0 },
1916 { "del-acl", parse_http_set_map, 1 },
1917 { "del-header", parse_http_del_header, 0 },
1918 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001919 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001920 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1921 { "early-hint", parse_http_set_header, 0 },
1922 { "redirect", parse_http_redirect, 0 },
1923 { "reject", parse_http_action_reject, 0 },
1924 { "replace-header", parse_http_replace_header, 0 },
1925 { "replace-path", parse_replace_uri, 0 },
1926 { "replace-uri", parse_replace_uri, 0 },
1927 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001928 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001929 { "set-header", parse_http_set_header, 0 },
1930 { "set-log-level", parse_http_set_log_level, 0 },
1931 { "set-map", parse_http_set_map, 1 },
1932 { "set-method", parse_set_req_line, 0 },
1933 { "set-mark", parse_http_set_mark, 0 },
1934 { "set-nice", parse_http_set_nice, 0 },
1935 { "set-path", parse_set_req_line, 0 },
1936 { "set-query", parse_set_req_line, 0 },
1937 { "set-tos", parse_http_set_tos, 0 },
1938 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001939 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001940 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001941 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001942 { NULL, NULL }
1943 }
1944};
1945
Willy Tarreau0108d902018-11-25 19:14:37 +01001946INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1947
Willy Tarreau79e57332018-10-02 16:01:16 +02001948static struct action_kw_list http_res_actions = {
1949 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001950 { "add-acl", parse_http_set_map, 1 },
1951 { "add-header", parse_http_set_header, 0 },
1952 { "allow", parse_http_allow, 0 },
1953 { "capture", parse_http_res_capture, 0 },
1954 { "del-acl", parse_http_set_map, 1 },
1955 { "del-header", parse_http_del_header, 0 },
1956 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001957 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001958 { "redirect", parse_http_redirect, 0 },
1959 { "replace-header", parse_http_replace_header, 0 },
1960 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001961 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001962 { "set-header", parse_http_set_header, 0 },
1963 { "set-log-level", parse_http_set_log_level, 0 },
1964 { "set-map", parse_http_set_map, 1 },
1965 { "set-mark", parse_http_set_mark, 0 },
1966 { "set-nice", parse_http_set_nice, 0 },
1967 { "set-status", parse_http_set_status, 0 },
1968 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001969 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001970 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001971 { NULL, NULL }
1972 }
1973};
1974
Willy Tarreau0108d902018-11-25 19:14:37 +01001975INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001976
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001977static struct action_kw_list http_after_res_actions = {
1978 .kw = {
1979 { "add-header", parse_http_set_header, 0 },
1980 { "allow", parse_http_allow, 0 },
1981 { "del-header", parse_http_del_header, 0 },
1982 { "replace-header", parse_http_replace_header, 0 },
1983 { "replace-value", parse_http_replace_header, 0 },
1984 { "set-header", parse_http_set_header, 0 },
1985 { "set-status", parse_http_set_status, 0 },
1986 { "strict-mode", parse_http_strict_mode, 0 },
1987 { NULL, NULL }
1988 }
1989};
1990
1991INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
1992
Willy Tarreau79e57332018-10-02 16:01:16 +02001993/*
1994 * Local variables:
1995 * c-indent-level: 8
1996 * c-basic-offset: 8
1997 * End:
1998 */