blob: ab4a8bb94a817b2b020e7c17d8b9c68d2d34c7a0 [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP actions
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
Christopher Faulet81e20172019-12-12 16:40:30 +010019#include <common/cfgparse.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020020#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
24#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010025#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020026#include <common/memory.h>
27#include <common/standard.h>
28#include <common/version.h>
29
30#include <types/capture.h>
31#include <types/global.h>
32
33#include <proto/acl.h>
34#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010035#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020036#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020037#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020038#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020039#include <proto/http_ana.h>
Christopher Faulet046cf442019-12-17 15:45:23 +010040#include <proto/pattern.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010041#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020042
Christopher Faulet2eb53962020-01-14 14:47:34 +010043/* Release memory allocated by most of HTTP actions. Concretly, it releases
44 * <arg.http>.
45 */
46static void release_http_action(struct act_rule *rule)
47{
48 struct logformat_node *lf, *lfb;
49
Tim Duesterhused526372020-03-05 17:56:33 +010050 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010051 if (rule->arg.http.re)
52 regex_free(rule->arg.http.re);
53 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
54 LIST_DEL(&lf->list);
55 release_sample_expr(lf->expr);
56 free(lf->arg);
57 free(lf);
58 }
59}
60
Christopher Faulet5cb513a2020-05-13 17:56:56 +020061/* Release memory allocated by HTTP actions relying on an http reply. Concretly,
62 * it releases <.arg.http_reply>
63 */
64static void release_act_http_reply(struct act_rule *rule)
65{
66 release_http_reply(rule->arg.http_reply);
67 rule->arg.http_reply = NULL;
68}
69
70
71/* Check function for HTTP actions relying on an http reply. The function
72 * returns 1 in success case, otherwise, it returns 0 and err is filled.
73 */
74static int check_act_http_reply(struct act_rule *rule, struct proxy *px, char **err)
75{
76 struct http_reply *reply = rule->arg.http_reply;
77
78 if (!http_check_http_reply(reply, px, err)) {
79 release_act_http_reply(rule);
80 return 0;
81 }
82 return 1;
83}
84
Willy Tarreau79e57332018-10-02 16:01:16 +020085
86/* This function executes one of the set-{method,path,query,uri} actions. It
87 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010088 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050089 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010090 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
91 * occurs while soft rewrites are enabled, the action is canceled, but the rule
92 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020093 */
94static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
95 struct session *sess, struct stream *s, int flags)
96{
97 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010098 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020099
100 replace = alloc_trash_chunk();
101 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100102 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +0200103
104 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +0100105 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200106 replace->area[replace->data++] = '?';
107 replace->data += build_logline(s, replace->area + replace->data,
108 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +0100109 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200110
Christopher Faulet2c22a692019-12-18 15:39:56 +0100111 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100112 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +0200113
Christopher Faulete00d06c2019-12-16 17:18:42 +0100114 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +0200115 free_trash_chunk(replace);
116 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100117
118 fail_alloc:
119 if (!(s->flags & SF_ERR_MASK))
120 s->flags |= SF_ERR_RESOURCE;
121 ret = ACT_RET_ERR;
122 goto leave;
123
124 fail_rewrite:
125 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
126 if (s->flags & SF_BE_ASSIGNED)
127 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
128 if (sess->listener->counters)
129 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
130 if (objt_server(s->target))
131 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
132
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100133 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100134 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100135 if (!(s->flags & SF_ERR_MASK))
136 s->flags |= SF_ERR_PRXCOND;
137 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100138 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200139}
140
141/* parse an http-request action among :
142 * set-method
143 * set-path
144 * set-query
145 * set-uri
146 *
147 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100148 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100149 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
150 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200151 */
152static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
153 struct act_rule *rule, char **err)
154{
155 int cur_arg = *orig_arg;
156
Willy Tarreau79e57332018-10-02 16:01:16 +0200157 switch (args[0][4]) {
158 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100159 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200160 break;
161 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100162 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200163 break;
164 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100165 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200166 break;
167 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100168 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200169 break;
170 default:
171 memprintf(err, "internal error: unhandled action '%s'", args[0]);
172 return ACT_RET_PRS_ERR;
173 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100174 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100175 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200176
177 if (!*args[cur_arg] ||
178 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
179 memprintf(err, "expects exactly 1 argument <format>");
180 return ACT_RET_PRS_ERR;
181 }
182
Christopher Faulet96bff762019-12-17 13:46:18 +0100183 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200184 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100185 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200186 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
187 return ACT_RET_PRS_ERR;
188 }
189
190 (*orig_arg)++;
191 return ACT_RET_PRS_OK;
192}
193
Willy Tarreau33810222019-06-12 17:44:02 +0200194/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100195 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200196 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100197 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100198 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100199 * contains 1 for the path or 3 for the URI (values used by
200 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
201 * occurs while soft rewrites are enabled, the action is canceled, but the rule
202 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200203 */
204static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
205 struct session *sess, struct stream *s, int flags)
206{
Christopher Faulet13403762019-12-13 09:01:57 +0100207 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200208 struct buffer *replace, *output;
209 struct ist uri;
210 int len;
211
212 replace = alloc_trash_chunk();
213 output = alloc_trash_chunk();
214 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100215 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200216 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100217
Christopher Faulet2c22a692019-12-18 15:39:56 +0100218 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100219 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100220
Christopher Faulet96bff762019-12-17 13:46:18 +0100221 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200222 goto leave;
223
Christopher Faulet96bff762019-12-17 13:46:18 +0100224 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200225
226 /* note: uri.ptr doesn't need to be zero-terminated because it will
227 * only be used to pick pmatch references.
228 */
229 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
230 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100231 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200232
Christopher Faulet2c22a692019-12-18 15:39:56 +0100233 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100234 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200235
Christopher Faulete00d06c2019-12-16 17:18:42 +0100236 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200237 free_trash_chunk(output);
238 free_trash_chunk(replace);
239 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100240
241 fail_alloc:
242 if (!(s->flags & SF_ERR_MASK))
243 s->flags |= SF_ERR_RESOURCE;
244 ret = ACT_RET_ERR;
245 goto leave;
246
247 fail_rewrite:
248 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
249 if (s->flags & SF_BE_ASSIGNED)
250 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
251 if (sess->listener->counters)
252 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
253 if (objt_server(s->target))
254 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
255
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100256 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100257 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100258 if (!(s->flags & SF_ERR_MASK))
259 s->flags |= SF_ERR_PRXCOND;
260 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100261 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200262}
263
Willy Tarreau262c3f12019-12-17 06:52:51 +0100264/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200265 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100266 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100267 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200268 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
269 */
270static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
271 struct act_rule *rule, char **err)
272{
273 int cur_arg = *orig_arg;
274 char *error = NULL;
275
Willy Tarreau262c3f12019-12-17 06:52:51 +0100276 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100277 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100278 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100279 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100280
Willy Tarreau33810222019-06-12 17:44:02 +0200281 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100282 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200283
284 if (!*args[cur_arg] || !*args[cur_arg+1] ||
285 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
286 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
287 return ACT_RET_PRS_ERR;
288 }
289
Christopher Faulet96bff762019-12-17 13:46:18 +0100290 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200291 memprintf(err, "failed to parse the regex : %s", error);
292 free(error);
293 return ACT_RET_PRS_ERR;
294 }
295
Christopher Faulet96bff762019-12-17 13:46:18 +0100296 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200297 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100298 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200299 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100300 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200301 return ACT_RET_PRS_ERR;
302 }
303
304 (*orig_arg) += 2;
305 return ACT_RET_PRS_OK;
306}
307
Willy Tarreau79e57332018-10-02 16:01:16 +0200308/* This function is just a compliant action wrapper for "set-status". */
309static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
310 struct session *sess, struct stream *s, int flags)
311{
Christopher Faulet96bff762019-12-17 13:46:18 +0100312 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100313 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
314 if (s->flags & SF_BE_ASSIGNED)
315 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
316 if (sess->listener->counters)
317 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
318 if (objt_server(s->target))
319 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
320
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100321 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100322 if (!(s->flags & SF_ERR_MASK))
323 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100324 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100325 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100326 }
327
Willy Tarreau79e57332018-10-02 16:01:16 +0200328 return ACT_RET_CONT;
329}
330
331/* parse set-status action:
332 * This action accepts a single argument of type int representing
333 * an http status code. It returns ACT_RET_PRS_OK on success,
334 * ACT_RET_PRS_ERR on error.
335 */
336static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
337 struct act_rule *rule, char **err)
338{
339 char *error;
340
341 rule->action = ACT_CUSTOM;
342 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100343 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200344
345 /* Check if an argument is available */
346 if (!*args[*orig_arg]) {
347 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
348 return ACT_RET_PRS_ERR;
349 }
350
351 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100352 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
353 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200354 memprintf(err, "expects an integer status code between 100 and 999");
355 return ACT_RET_PRS_ERR;
356 }
357
358 (*orig_arg)++;
359
360 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100361 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200362 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
363 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
364 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100365 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
366 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200367 (*orig_arg)++;
368 }
369
Christopher Fauletc20b3712020-01-27 15:51:56 +0100370 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200371 return ACT_RET_PRS_OK;
372}
373
374/* This function executes the "reject" HTTP action. It clears the request and
375 * response buffer without sending any response. It can be useful as an HTTP
376 * alternative to the silent-drop action to defend against DoS attacks, and may
377 * also be used with HTTP/2 to close a connection instead of just a stream.
378 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100379 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200380 */
381static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
382 struct session *sess, struct stream *s, int flags)
383{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100384 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200385 channel_abort(&s->req);
386 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100387 s->req.analysers &= AN_REQ_FLT_END;
388 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200389
Olivier Houcharda798bf52019-03-08 18:52:00 +0100390 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
391 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200392 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100393 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200394
395 if (!(s->flags & SF_ERR_MASK))
396 s->flags |= SF_ERR_PRXCOND;
397 if (!(s->flags & SF_FINST_MASK))
398 s->flags |= SF_FINST_R;
399
Christopher Faulet90d22a82020-03-06 11:18:39 +0100400 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200401}
402
403/* parse the "reject" action:
404 * This action takes no argument and returns ACT_RET_PRS_OK on success,
405 * ACT_RET_PRS_ERR on error.
406 */
407static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
408 struct act_rule *rule, char **err)
409{
410 rule->action = ACT_CUSTOM;
411 rule->action_ptr = http_action_reject;
412 return ACT_RET_PRS_OK;
413}
414
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200415/* This function executes the "disable-l7-retry" HTTP action.
416 * It disables L7 retries (all retry except for a connection failure). This
417 * can be useful for example to avoid retrying on POST requests.
418 * It just removes the L7 retry flag on the stream_interface, and always
419 * return ACT_RET_CONT;
420 */
421static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
422 struct session *sess, struct stream *s, int flags)
423{
424 struct stream_interface *si = &s->si[1];
425
426 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
427 * let's be future-proof and remove it anyway.
428 */
429 si->flags &= ~SI_FL_L7_RETRY;
430 si->flags |= SI_FL_D_L7_RETRY;
431 return ACT_RET_CONT;
432}
433
434/* parse the "disable-l7-retry" action:
435 * This action takes no argument and returns ACT_RET_PRS_OK on success,
436 * ACT_RET_PRS_ERR on error.
437 */
438static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
439 int *orig_args, struct proxy *px,
440 struct act_rule *rule, char **err)
441{
442 rule->action = ACT_CUSTOM;
443 rule->action_ptr = http_req_disable_l7_retry;
444 return ACT_RET_PRS_OK;
445}
446
Willy Tarreau79e57332018-10-02 16:01:16 +0200447/* This function executes the "capture" action. It executes a fetch expression,
448 * turns the result into a string and puts it in a capture slot. It always
449 * returns 1. If an error occurs the action is cancelled, but the rule
450 * processing continues.
451 */
452static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
453 struct session *sess, struct stream *s, int flags)
454{
455 struct sample *key;
456 struct cap_hdr *h = rule->arg.cap.hdr;
457 char **cap = s->req_cap;
458 int len;
459
460 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
461 if (!key)
462 return ACT_RET_CONT;
463
464 if (cap[h->index] == NULL)
465 cap[h->index] = pool_alloc(h->pool);
466
467 if (cap[h->index] == NULL) /* no more capture memory */
468 return ACT_RET_CONT;
469
470 len = key->data.u.str.data;
471 if (len > h->len)
472 len = h->len;
473
474 memcpy(cap[h->index], key->data.u.str.area, len);
475 cap[h->index][len] = 0;
476 return ACT_RET_CONT;
477}
478
479/* This function executes the "capture" action and store the result in a
480 * capture slot if exists. It executes a fetch expression, turns the result
481 * into a string and puts it in a capture slot. It always returns 1. If an
482 * error occurs the action is cancelled, but the rule processing continues.
483 */
484static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
485 struct session *sess, struct stream *s, int flags)
486{
487 struct sample *key;
488 struct cap_hdr *h;
489 char **cap = s->req_cap;
490 struct proxy *fe = strm_fe(s);
491 int len;
492 int i;
493
494 /* Look for the original configuration. */
495 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
496 h != NULL && i != rule->arg.capid.idx ;
497 i--, h = h->next);
498 if (!h)
499 return ACT_RET_CONT;
500
501 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
502 if (!key)
503 return ACT_RET_CONT;
504
505 if (cap[h->index] == NULL)
506 cap[h->index] = pool_alloc(h->pool);
507
508 if (cap[h->index] == NULL) /* no more capture memory */
509 return ACT_RET_CONT;
510
511 len = key->data.u.str.data;
512 if (len > h->len)
513 len = h->len;
514
515 memcpy(cap[h->index], key->data.u.str.area, len);
516 cap[h->index][len] = 0;
517 return ACT_RET_CONT;
518}
519
520/* Check an "http-request capture" action.
521 *
522 * The function returns 1 in success case, otherwise, it returns 0 and err is
523 * filled.
524 */
525static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
526{
527 if (rule->action_ptr != http_action_req_capture_by_id)
528 return 1;
529
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100530 /* capture slots can only be declared in frontends, so we can't check their
531 * existence in backends at configuration parsing step
532 */
533 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200534 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
535 rule->arg.capid.idx);
536 return 0;
537 }
538
539 return 1;
540}
541
Christopher Faulet2eb53962020-01-14 14:47:34 +0100542/* Release memory allocate by an http capture action */
543static void release_http_capture(struct act_rule *rule)
544{
545 if (rule->action_ptr == http_action_req_capture)
546 release_sample_expr(rule->arg.cap.expr);
547 else
548 release_sample_expr(rule->arg.capid.expr);
549}
550
Willy Tarreau79e57332018-10-02 16:01:16 +0200551/* parse an "http-request capture" action. It takes a single argument which is
552 * a sample fetch expression. It stores the expression into arg->act.p[0] and
553 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
554 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
555 */
556static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
557 struct act_rule *rule, char **err)
558{
559 struct sample_expr *expr;
560 struct cap_hdr *hdr;
561 int cur_arg;
562 int len = 0;
563
564 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
565 if (strcmp(args[cur_arg], "if") == 0 ||
566 strcmp(args[cur_arg], "unless") == 0)
567 break;
568
569 if (cur_arg < *orig_arg + 3) {
570 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
571 return ACT_RET_PRS_ERR;
572 }
573
574 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100575 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 +0200576 if (!expr)
577 return ACT_RET_PRS_ERR;
578
579 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
580 memprintf(err,
581 "fetch method '%s' extracts information from '%s', none of which is available here",
582 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100583 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200584 return ACT_RET_PRS_ERR;
585 }
586
587 if (!args[cur_arg] || !*args[cur_arg]) {
588 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100589 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200590 return ACT_RET_PRS_ERR;
591 }
592
593 if (strcmp(args[cur_arg], "len") == 0) {
594 cur_arg++;
595
596 if (!(px->cap & PR_CAP_FE)) {
597 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100598 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200599 return ACT_RET_PRS_ERR;
600 }
601
602 px->conf.args.ctx = ARGC_CAP;
603
604 if (!args[cur_arg]) {
605 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100606 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200607 return ACT_RET_PRS_ERR;
608 }
609 /* we copy the table name for now, it will be resolved later */
610 len = atoi(args[cur_arg]);
611 if (len <= 0) {
612 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100613 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200614 return ACT_RET_PRS_ERR;
615 }
616 cur_arg++;
617
Willy Tarreau79e57332018-10-02 16:01:16 +0200618 hdr = calloc(1, sizeof(*hdr));
619 hdr->next = px->req_cap;
620 hdr->name = NULL; /* not a header capture */
621 hdr->namelen = 0;
622 hdr->len = len;
623 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
624 hdr->index = px->nb_req_cap++;
625
626 px->req_cap = hdr;
627 px->to_log |= LW_REQHDR;
628
629 rule->action = ACT_CUSTOM;
630 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100631 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200632 rule->arg.cap.expr = expr;
633 rule->arg.cap.hdr = hdr;
634 }
635
636 else if (strcmp(args[cur_arg], "id") == 0) {
637 int id;
638 char *error;
639
640 cur_arg++;
641
642 if (!args[cur_arg]) {
643 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100644 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200645 return ACT_RET_PRS_ERR;
646 }
647
648 id = strtol(args[cur_arg], &error, 10);
649 if (*error != '\0') {
650 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100651 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200652 return ACT_RET_PRS_ERR;
653 }
654 cur_arg++;
655
656 px->conf.args.ctx = ARGC_CAP;
657
658 rule->action = ACT_CUSTOM;
659 rule->action_ptr = http_action_req_capture_by_id;
660 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100661 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200662 rule->arg.capid.expr = expr;
663 rule->arg.capid.idx = id;
664 }
665
666 else {
667 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100668 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200669 return ACT_RET_PRS_ERR;
670 }
671
672 *orig_arg = cur_arg;
673 return ACT_RET_PRS_OK;
674}
675
676/* This function executes the "capture" action and store the result in a
677 * capture slot if exists. It executes a fetch expression, turns the result
678 * into a string and puts it in a capture slot. It always returns 1. If an
679 * error occurs the action is cancelled, but the rule processing continues.
680 */
681static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
682 struct session *sess, struct stream *s, int flags)
683{
684 struct sample *key;
685 struct cap_hdr *h;
686 char **cap = s->res_cap;
687 struct proxy *fe = strm_fe(s);
688 int len;
689 int i;
690
691 /* Look for the original configuration. */
692 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
693 h != NULL && i != rule->arg.capid.idx ;
694 i--, h = h->next);
695 if (!h)
696 return ACT_RET_CONT;
697
698 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
699 if (!key)
700 return ACT_RET_CONT;
701
702 if (cap[h->index] == NULL)
703 cap[h->index] = pool_alloc(h->pool);
704
705 if (cap[h->index] == NULL) /* no more capture memory */
706 return ACT_RET_CONT;
707
708 len = key->data.u.str.data;
709 if (len > h->len)
710 len = h->len;
711
712 memcpy(cap[h->index], key->data.u.str.area, len);
713 cap[h->index][len] = 0;
714 return ACT_RET_CONT;
715}
716
717/* Check an "http-response capture" action.
718 *
719 * The function returns 1 in success case, otherwise, it returns 0 and err is
720 * filled.
721 */
722static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
723{
724 if (rule->action_ptr != http_action_res_capture_by_id)
725 return 1;
726
727 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
728 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
729 rule->arg.capid.idx);
730 return 0;
731 }
732
733 return 1;
734}
735
736/* parse an "http-response capture" action. It takes a single argument which is
737 * a sample fetch expression. It stores the expression into arg->act.p[0] and
738 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
739 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
740 */
741static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
742 struct act_rule *rule, char **err)
743{
744 struct sample_expr *expr;
745 int cur_arg;
746 int id;
747 char *error;
748
749 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
750 if (strcmp(args[cur_arg], "if") == 0 ||
751 strcmp(args[cur_arg], "unless") == 0)
752 break;
753
754 if (cur_arg < *orig_arg + 3) {
755 memprintf(err, "expects <expression> id <idx>");
756 return ACT_RET_PRS_ERR;
757 }
758
759 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100760 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 +0200761 if (!expr)
762 return ACT_RET_PRS_ERR;
763
764 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
765 memprintf(err,
766 "fetch method '%s' extracts information from '%s', none of which is available here",
767 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100768 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200769 return ACT_RET_PRS_ERR;
770 }
771
772 if (!args[cur_arg] || !*args[cur_arg]) {
773 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100774 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200775 return ACT_RET_PRS_ERR;
776 }
777
778 if (strcmp(args[cur_arg], "id") != 0) {
779 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100780 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200781 return ACT_RET_PRS_ERR;
782 }
783
784 cur_arg++;
785
786 if (!args[cur_arg]) {
787 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100788 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200789 return ACT_RET_PRS_ERR;
790 }
791
792 id = strtol(args[cur_arg], &error, 10);
793 if (*error != '\0') {
794 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100795 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200796 return ACT_RET_PRS_ERR;
797 }
798 cur_arg++;
799
800 px->conf.args.ctx = ARGC_CAP;
801
802 rule->action = ACT_CUSTOM;
803 rule->action_ptr = http_action_res_capture_by_id;
804 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100805 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200806 rule->arg.capid.expr = expr;
807 rule->arg.capid.idx = id;
808
809 *orig_arg = cur_arg;
810 return ACT_RET_PRS_OK;
811}
812
Christopher Faulet81e20172019-12-12 16:40:30 +0100813/* Parse a "allow" action for a request or a response rule. It takes no argument. It
814 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
815 */
816static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
817 struct act_rule *rule, char **err)
818{
819 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100820 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100821 return ACT_RET_PRS_OK;
822}
823
Christopher Faulete0fca292020-01-13 21:49:03 +0100824/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200825 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
826 * error. It relies on http_parse_http_reply() to set
827 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100828 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100829static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
830 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100831{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200832 int default_status;
833 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100834
835 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100836 if (rule->from == ACT_F_HTTP_REQ) {
837 if (!strcmp(args[cur_arg-1], "tarpit")) {
838 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200839 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100840 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100841 else {
842 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200843 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100844 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100845 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100846 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100847 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200848 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100849 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100850
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200851 /* If no args or only a deny_status specified, fallback on the legacy
852 * mode and use default error files despite the fact that
853 * default-errorfiles is not used. Otherwise, parse an http reply.
854 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100855
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200856 /* Prepare parsing of log-format strings */
857 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100858
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200859 if (!*(args[cur_arg])) {
860 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
861 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100862 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200863
864 if (strcmp(args[cur_arg], "deny_status") == 0) {
865 if (!*(args[cur_arg+2]) ||
866 (strcmp(args[cur_arg+2], "errorfile") != 0 && strcmp(args[cur_arg+2], "errorfiles") != 0)) {
867 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
868 &arg, px, default_status, err);
869 *orig_arg += 2;
870 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100871 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200872 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100873 }
874
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200875 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100876
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200877 end:
878 if (!rule->arg.http_reply)
879 return ACT_RET_PRS_ERR;
880
881 rule->flags |= ACT_FLAG_FINAL;
882 rule->check_ptr = check_act_http_reply;
883 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +0100884 return ACT_RET_PRS_OK;
885}
886
887/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
888 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
889 */
890static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
891 struct act_rule *rule, char **err)
892{
893 int cur_arg;
894
895 rule->action = ACT_HTTP_REQ_AUTH;
Christopher Faulet245cf792019-12-18 14:58:12 +0100896 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100897 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100898
899 cur_arg = *orig_arg;
900 if (!strcmp(args[cur_arg], "realm")) {
901 cur_arg++;
902 if (!*args[cur_arg]) {
903 memprintf(err, "missing realm value.\n");
904 return ACT_RET_PRS_ERR;
905 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100906 rule->arg.http.str.ptr = strdup(args[cur_arg]);
907 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100908 cur_arg++;
909 }
910
Christopher Fauletc20b3712020-01-27 15:51:56 +0100911 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100912 *orig_arg = cur_arg;
913 return ACT_RET_PRS_OK;
914}
915
916/* Parse a "set-nice" action. It takes the nice value as argument. It returns
917 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
918 */
919static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
920 struct act_rule *rule, char **err)
921{
922 int cur_arg;
923
924 rule->action = ACT_HTTP_SET_NICE;
925
926 cur_arg = *orig_arg;
927 if (!*args[cur_arg]) {
928 memprintf(err, "expects exactly 1 argument (integer value)");
929 return ACT_RET_PRS_ERR;
930 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100931 rule->arg.http.i = atoi(args[cur_arg]);
932 if (rule->arg.http.i < -1024)
933 rule->arg.http.i = -1024;
934 else if (rule->arg.http.i > 1024)
935 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +0100936
Christopher Fauletc20b3712020-01-27 15:51:56 +0100937 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100938 *orig_arg = cur_arg + 1;
939 return ACT_RET_PRS_OK;
940}
941
942/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
943 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
944 */
945static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
946 struct act_rule *rule, char **err)
947{
948#ifdef IP_TOS
949 char *endp;
950 int cur_arg;
951
952 rule->action = ACT_HTTP_SET_TOS;
953
954 cur_arg = *orig_arg;
955 if (!*args[cur_arg]) {
956 memprintf(err, "expects exactly 1 argument (integer/hex value)");
957 return ACT_RET_PRS_ERR;
958 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100959 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100960 if (endp && *endp != '\0') {
961 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
962 return ACT_RET_PRS_ERR;
963 }
964
Christopher Fauletc20b3712020-01-27 15:51:56 +0100965 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100966 *orig_arg = cur_arg + 1;
967 return ACT_RET_PRS_OK;
968#else
969 memprintf(err, "not supported on this platform (IP_TOS undefined)");
970 return ACT_RET_PRS_ERR;
971#endif
972}
973
974/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
975 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
976 */
977static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
978 struct act_rule *rule, char **err)
979{
980#ifdef SO_MARK
981 char *endp;
982 int cur_arg;
983
984 rule->action = ACT_HTTP_SET_MARK;
985
986 cur_arg = *orig_arg;
987 if (!*args[cur_arg]) {
988 memprintf(err, "expects exactly 1 argument (integer/hex value)");
989 return ACT_RET_PRS_ERR;
990 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100991 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100992 if (endp && *endp != '\0') {
993 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
994 return ACT_RET_PRS_ERR;
995 }
996
Christopher Fauletc20b3712020-01-27 15:51:56 +0100997 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100998 *orig_arg = cur_arg + 1;
999 global.last_checks |= LSTCHK_NETADM;
1000 return ACT_RET_PRS_OK;
1001#else
1002 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1003 return ACT_RET_PRS_ERR;
1004#endif
1005}
1006
1007/* Parse a "set-log-level" action. It takes the level value as argument. It
1008 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1009 */
1010static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1011 struct act_rule *rule, char **err)
1012{
1013 int cur_arg;
1014
1015 rule->action = ACT_HTTP_SET_LOGL;
1016
1017 cur_arg = *orig_arg;
1018 if (!*args[cur_arg]) {
1019 bad_log_level:
1020 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1021 return ACT_RET_PRS_ERR;
1022 }
1023 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001024 rule->arg.http.i = -1;
1025 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001026 goto bad_log_level;
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 return ACT_RET_PRS_OK;
1031}
1032
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001033/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1034 * 103 response header with <.arg.http.str> name and with a value built
1035 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001036 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001037 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1038 * block is added to terminate the response. On success, it returns
1039 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1040 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1041 * returned.
1042 */
1043static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1044 struct session *sess, struct stream *s, int flags)
1045{
1046 struct act_rule *prev_rule, *next_rule;
1047 struct channel *res = &s->res;
1048 struct htx *htx = htx_from_buf(&res->buf);
1049 struct buffer *value = alloc_trash_chunk();
1050 enum act_return ret = ACT_RET_CONT;
1051
1052 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1053 goto leave;
1054
1055 if (!value) {
1056 if (!(s->flags & SF_ERR_MASK))
1057 s->flags |= SF_ERR_RESOURCE;
1058 goto error;
1059 }
1060
1061 /* get previous and next rules */
1062 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1063 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1064
1065 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1066 * continue to add link to a previously started response */
1067 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1068 struct htx_sl *sl;
1069 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1070 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1071
1072 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1073 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1074 if (!sl)
1075 goto error;
1076 sl->info.res.status = 103;
1077 }
1078
1079 /* Add the HTTP Early Hint HTTP 103 response heade */
1080 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1081 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1082 goto error;
1083
1084 /* if it is the last rule or the next one is not an early-hint, terminate the current
1085 * response. */
1086 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001087 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1088 /* If an error occurred during an Early-hint rule,
1089 * remove the incomplete HTTP 103 response from the
1090 * buffer */
1091 goto error;
1092 }
1093
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001094 if (!http_forward_proxy_resp(s, 0))
1095 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001096 }
1097
1098 leave:
1099 free_trash_chunk(value);
1100 return ret;
1101
1102 error:
1103 /* If an error occurred during an Early-hint rule, remove the incomplete
1104 * HTTP 103 response from the buffer */
1105 channel_htx_truncate(res, htx);
1106 ret = ACT_RET_ERR;
1107 goto leave;
1108}
1109
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001110/* This function executes a set-header or add-header actions. It builds a string
1111 * in the trash from the specified format string. It finds the action to be
1112 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001113 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001114 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1115 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1116 * ACT_RET_ERR is returned.
1117 */
1118static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1119 struct session *sess, struct stream *s, int flags)
1120{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001121 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1122 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001123 enum act_return ret = ACT_RET_CONT;
1124 struct buffer *replace;
1125 struct http_hdr_ctx ctx;
1126 struct ist n, v;
1127
1128 replace = alloc_trash_chunk();
1129 if (!replace)
1130 goto fail_alloc;
1131
1132 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1133 n = rule->arg.http.str;
1134 v = ist2(replace->area, replace->data);
1135
1136 if (rule->action == 0) { // set-header
1137 /* remove all occurrences of the header */
1138 ctx.blk = NULL;
1139 while (http_find_header(htx, n, &ctx, 1))
1140 http_remove_header(htx, &ctx);
1141 }
1142
1143 /* Now add header */
1144 if (!http_add_header(htx, n, v))
1145 goto fail_rewrite;
1146
1147 leave:
1148 free_trash_chunk(replace);
1149 return ret;
1150
1151 fail_alloc:
1152 if (!(s->flags & SF_ERR_MASK))
1153 s->flags |= SF_ERR_RESOURCE;
1154 ret = ACT_RET_ERR;
1155 goto leave;
1156
1157 fail_rewrite:
1158 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1159 if (s->flags & SF_BE_ASSIGNED)
1160 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1161 if (sess->listener->counters)
1162 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1163 if (objt_server(s->target))
1164 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1165
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001166 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001167 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001168 if (!(s->flags & SF_ERR_MASK))
1169 s->flags |= SF_ERR_PRXCOND;
1170 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001171 goto leave;
1172}
1173
Christopher Faulet81e20172019-12-12 16:40:30 +01001174/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1175 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1176 * on success, ACT_RET_PRS_ERR on error.
1177 *
1178 * Note: same function is used for the request and the response. However
1179 * "early-hint" rules are only supported for request rules.
1180 */
1181static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1182 struct act_rule *rule, char **err)
1183{
Christopher Faulet81e20172019-12-12 16:40:30 +01001184 int cap, cur_arg;
1185
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001186 if (args[*orig_arg-1][0] == 'e') {
1187 rule->action = ACT_CUSTOM;
1188 rule->action_ptr = http_action_early_hint;
1189 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001190 else {
1191 if (args[*orig_arg-1][0] == 's')
1192 rule->action = 0; // set-header
1193 else
1194 rule->action = 1; // add-header
1195 rule->action_ptr = http_action_set_header;
1196 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001197 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001198
1199 cur_arg = *orig_arg;
1200 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1201 memprintf(err, "expects exactly 2 arguments");
1202 return ACT_RET_PRS_ERR;
1203 }
1204
Christopher Faulet81e20172019-12-12 16:40:30 +01001205
Christopher Faulet96bff762019-12-17 13:46:18 +01001206 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1207 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1208 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001209
1210 if (rule->from == ACT_F_HTTP_REQ) {
1211 px->conf.args.ctx = ARGC_HRQ;
1212 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1213 }
1214 else{
1215 px->conf.args.ctx = ARGC_HRS;
1216 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1217 }
1218
1219 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001220 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001221 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001222 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001223 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001224
1225 free(px->conf.lfs_file);
1226 px->conf.lfs_file = strdup(px->conf.args.file);
1227 px->conf.lfs_line = px->conf.args.line;
1228
1229 *orig_arg = cur_arg + 1;
1230 return ACT_RET_PRS_OK;
1231}
1232
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001233/* This function executes a replace-header or replace-value actions. It
1234 * builds a string in the trash from the specified format string. It finds
1235 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001236 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001237 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1238 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1239 * processing continue. Otherwsize ACT_RET_ERR is returned.
1240 */
1241static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1242 struct session *sess, struct stream *s, int flags)
1243{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001244 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1245 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001246 enum act_return ret = ACT_RET_CONT;
1247 struct buffer *replace;
1248 int r;
1249
1250 replace = alloc_trash_chunk();
1251 if (!replace)
1252 goto fail_alloc;
1253
1254 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1255
1256 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1257 if (r == -1)
1258 goto fail_rewrite;
1259
1260 leave:
1261 free_trash_chunk(replace);
1262 return ret;
1263
1264 fail_alloc:
1265 if (!(s->flags & SF_ERR_MASK))
1266 s->flags |= SF_ERR_RESOURCE;
1267 ret = ACT_RET_ERR;
1268 goto leave;
1269
1270 fail_rewrite:
1271 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1272 if (s->flags & SF_BE_ASSIGNED)
1273 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1274 if (sess->listener->counters)
1275 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1276 if (objt_server(s->target))
1277 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1278
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001279 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001280 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001281 if (!(s->flags & SF_ERR_MASK))
1282 s->flags |= SF_ERR_PRXCOND;
1283 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001284 goto leave;
1285}
1286
Christopher Faulet81e20172019-12-12 16:40:30 +01001287/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1288 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1289 * success, ACT_RET_PRS_ERR on error.
1290 */
1291static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1292 struct act_rule *rule, char **err)
1293{
1294 int cap, cur_arg;
1295
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001296 if (args[*orig_arg-1][8] == 'h')
1297 rule->action = 0; // replace-header
1298 else
1299 rule->action = 1; // replace-value
1300 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001301 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001302
1303 cur_arg = *orig_arg;
1304 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1305 memprintf(err, "expects exactly 3 arguments");
1306 return ACT_RET_PRS_ERR;
1307 }
1308
Christopher Faulet96bff762019-12-17 13:46:18 +01001309 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1310 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1311 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001312
1313 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001314 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001315 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001316 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001317 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001318
1319 if (rule->from == ACT_F_HTTP_REQ) {
1320 px->conf.args.ctx = ARGC_HRQ;
1321 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1322 }
1323 else{
1324 px->conf.args.ctx = ARGC_HRS;
1325 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1326 }
1327
1328 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001329 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001330 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001331 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001332 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001333 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001334
1335 free(px->conf.lfs_file);
1336 px->conf.lfs_file = strdup(px->conf.args.file);
1337 px->conf.lfs_line = px->conf.args.line;
1338
1339 *orig_arg = cur_arg + 1;
1340 return ACT_RET_PRS_OK;
1341}
1342
1343/* Parse a "del-header" action. It takes an header name as argument. It returns
1344 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1345 */
1346static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1347 struct act_rule *rule, char **err)
1348{
1349 int cur_arg;
1350
1351 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001352 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001353
1354 cur_arg = *orig_arg;
1355 if (!*args[cur_arg]) {
1356 memprintf(err, "expects exactly 1 arguments");
1357 return ACT_RET_PRS_ERR;
1358 }
1359
Christopher Faulet96bff762019-12-17 13:46:18 +01001360 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1361 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001362 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1363
Christopher Fauletc20b3712020-01-27 15:51:56 +01001364 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001365 *orig_arg = cur_arg + 1;
1366 return ACT_RET_PRS_OK;
1367}
1368
Christopher Faulet2eb53962020-01-14 14:47:34 +01001369/* Release memory allocated by an http redirect action. */
1370static void release_http_redir(struct act_rule *rule)
1371{
1372 struct logformat_node *lf, *lfb;
1373 struct redirect_rule *redir;
1374
1375 redir = rule->arg.redir;
1376 LIST_DEL(&redir->list);
1377 if (redir->cond) {
1378 prune_acl_cond(redir->cond);
1379 free(redir->cond);
1380 }
1381 free(redir->rdr_str);
1382 free(redir->cookie_str);
1383 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1384 LIST_DEL(&lf->list);
1385 free(lf);
1386 }
1387 free(redir);
1388}
1389
Christopher Faulet81e20172019-12-12 16:40:30 +01001390/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1391 * ACT_RET_PRS_ERR on error.
1392 */
1393static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1394 struct act_rule *rule, char **err)
1395{
1396 struct redirect_rule *redir;
1397 int dir, cur_arg;
1398
1399 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001400 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001401 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001402
1403 cur_arg = *orig_arg;
1404
1405 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1406 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1407 return ACT_RET_PRS_ERR;
1408
1409 rule->arg.redir = redir;
1410 rule->cond = redir->cond;
1411 redir->cond = NULL;
1412
1413 /* skip all arguments */
1414 while (*args[cur_arg])
1415 cur_arg++;
1416
1417 *orig_arg = cur_arg;
1418 return ACT_RET_PRS_OK;
1419}
1420
Christopher Faulet046cf442019-12-17 15:45:23 +01001421/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1422 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1423 */
1424static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1425 struct session *sess, struct stream *s, int flags)
1426{
1427 struct pat_ref *ref;
1428 struct buffer *key = NULL, *value = NULL;
1429 enum act_return ret = ACT_RET_CONT;
1430
1431 /* collect reference */
1432 ref = pat_ref_lookup(rule->arg.map.ref);
1433 if (!ref)
1434 goto leave;
1435
1436 /* allocate key */
1437 key = alloc_trash_chunk();
1438 if (!key)
1439 goto fail_alloc;
1440
1441 /* collect key */
1442 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1443 key->area[key->data] = '\0';
1444
1445 switch (rule->action) {
1446 case 0: // add-acl
1447 /* add entry only if it does not already exist */
1448 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1449 if (pat_ref_find_elt(ref, key->area) == NULL)
1450 pat_ref_add(ref, key->area, NULL, NULL);
1451 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1452 break;
1453
1454 case 1: // set-map
1455 /* allocate value */
1456 value = alloc_trash_chunk();
1457 if (!value)
1458 goto fail_alloc;
1459
1460 /* collect value */
1461 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1462 value->area[value->data] = '\0';
1463
1464 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1465 if (pat_ref_find_elt(ref, key->area) != NULL) {
1466 /* update entry if it exists */
1467 pat_ref_set(ref, key->area, value->area, NULL);
1468 }
1469 else {
1470 /* insert a new entry */
1471 pat_ref_add(ref, key->area, value->area, NULL);
1472 }
1473 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1474 break;
1475
1476 case 2: // del-acl
1477 case 3: // del-map
1478 /* returned code: 1=ok, 0=ko */
1479 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1480 pat_ref_delete(ref, key->area);
1481 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1482 break;
1483
1484 default:
1485 ret = ACT_RET_ERR;
1486 }
1487
1488
1489 leave:
1490 free_trash_chunk(key);
1491 free_trash_chunk(value);
1492 return ret;
1493
1494 fail_alloc:
1495 if (!(s->flags & SF_ERR_MASK))
1496 s->flags |= SF_ERR_RESOURCE;
1497 ret = ACT_RET_ERR;
1498 goto leave;
1499}
1500
Christopher Faulet2eb53962020-01-14 14:47:34 +01001501/* Release memory allocated by an http map/acl action. */
1502static void release_http_map(struct act_rule *rule)
1503{
1504 struct logformat_node *lf, *lfb;
1505
1506 free(rule->arg.map.ref);
1507 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1508 LIST_DEL(&lf->list);
1509 release_sample_expr(lf->expr);
1510 free(lf->arg);
1511 free(lf);
1512 }
1513 if (rule->action == 1) {
1514 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1515 LIST_DEL(&lf->list);
1516 release_sample_expr(lf->expr);
1517 free(lf->arg);
1518 free(lf);
1519 }
1520 }
1521}
1522
Christopher Faulet81e20172019-12-12 16:40:30 +01001523/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001524 * two log-format string as argument depending on the action. The action is
1525 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1526 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001527 */
1528static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1529 struct act_rule *rule, char **err)
1530{
1531 int cap, cur_arg;
1532
Christopher Faulet046cf442019-12-17 15:45:23 +01001533 if (args[*orig_arg-1][0] == 'a') // add-acl
1534 rule->action = 0;
1535 else if (args[*orig_arg-1][0] == 's') // set-map
1536 rule->action = 1;
1537 else if (args[*orig_arg-1][4] == 'a') // del-acl
1538 rule->action = 2;
1539 else if (args[*orig_arg-1][4] == 'm') // del-map
1540 rule->action = 3;
1541 else {
1542 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1543 return ACT_RET_PRS_ERR;
1544 }
1545 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001546 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001547
1548 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001549 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1550 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001551 memprintf(err, "expects exactly 2 arguments");
1552 return ACT_RET_PRS_ERR;
1553 }
1554 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001555 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001556 memprintf(err, "expects exactly 1 arguments");
1557 return ACT_RET_PRS_ERR;
1558 }
1559
1560 /*
1561 * '+ 8' for 'set-map(' (same for del-map)
1562 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1563 */
1564 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1565
1566 if (rule->from == ACT_F_HTTP_REQ) {
1567 px->conf.args.ctx = ARGC_HRQ;
1568 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1569 }
1570 else{
1571 px->conf.args.ctx = ARGC_HRS;
1572 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1573 }
1574
1575 /* key pattern */
1576 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001577 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1578 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001579 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001580 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001581
Christopher Faulet046cf442019-12-17 15:45:23 +01001582 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001583 /* value pattern for set-map only */
1584 cur_arg++;
1585 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001586 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1587 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001588 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001589 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001590 }
1591
1592 free(px->conf.lfs_file);
1593 px->conf.lfs_file = strdup(px->conf.args.file);
1594 px->conf.lfs_line = px->conf.args.line;
1595
1596 *orig_arg = cur_arg + 1;
1597 return ACT_RET_PRS_OK;
1598}
1599
Christopher Fauletac98d812019-12-18 09:20:16 +01001600/* This function executes a track-sc* actions. On success, it returns
1601 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1602 */
1603static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1604 struct session *sess, struct stream *s, int flags)
1605{
1606 struct stktable *t;
1607 struct stksess *ts;
1608 struct stktable_key *key;
1609 void *ptr1, *ptr2, *ptr3, *ptr4;
1610 int opt;
1611
1612 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1613 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1614
1615 t = rule->arg.trk_ctr.table.t;
1616 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1617
1618 if (!key)
1619 goto end;
1620 ts = stktable_get_entry(t, key);
1621 if (!ts)
1622 goto end;
1623
1624 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1625
1626 /* let's count a new HTTP request as it's the first time we do it */
1627 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1628 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1629
1630 /* When the client triggers a 4xx from the server, it's most often due
1631 * to a missing object or permission. These events should be tracked
1632 * because if they happen often, it may indicate a brute force or a
1633 * vulnerability scan. Normally this is done when receiving the response
1634 * but here we're tracking after this ought to have been done so we have
1635 * to do it on purpose.
1636 */
1637 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1638 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1639 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1640 }
1641
1642 if (ptr1 || ptr2 || ptr3 || ptr4) {
1643 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1644
1645 if (ptr1)
1646 stktable_data_cast(ptr1, http_req_cnt)++;
1647 if (ptr2)
1648 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1649 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1650 if (ptr3)
1651 stktable_data_cast(ptr3, http_err_cnt)++;
1652 if (ptr4)
1653 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1654 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1655
1656 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1657
1658 /* If data was modified, we need to touch to re-schedule sync */
1659 stktable_touch_local(t, ts, 0);
1660 }
1661
1662 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1663 if (sess->fe != s->be)
1664 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1665
1666 end:
1667 return ACT_RET_CONT;
1668}
Christopher Faulet81e20172019-12-12 16:40:30 +01001669
Christopher Faulet2eb53962020-01-14 14:47:34 +01001670static void release_http_track_sc(struct act_rule *rule)
1671{
1672 release_sample_expr(rule->arg.trk_ctr.expr);
1673}
1674
Christopher Faulet81e20172019-12-12 16:40:30 +01001675/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1676 * ACT_RET_PRS_ERR on error.
1677 */
1678static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1679 struct act_rule *rule, char **err)
1680{
1681 struct sample_expr *expr;
1682 unsigned int where;
1683 unsigned int tsc_num;
1684 const char *tsc_num_str;
1685 int cur_arg;
1686
1687 tsc_num_str = &args[*orig_arg-1][8];
1688 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1689 return ACT_RET_PRS_ERR;
1690
1691 cur_arg = *orig_arg;
1692 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001693 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001694 if (!expr)
1695 return ACT_RET_PRS_ERR;
1696
1697 where = 0;
1698 if (px->cap & PR_CAP_FE)
1699 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1700 if (px->cap & PR_CAP_BE)
1701 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1702
1703 if (!(expr->fetch->val & where)) {
1704 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1705 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001706 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001707 return ACT_RET_PRS_ERR;
1708 }
1709
1710 if (strcmp(args[cur_arg], "table") == 0) {
1711 cur_arg++;
1712 if (!*args[cur_arg]) {
1713 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001714 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001715 return ACT_RET_PRS_ERR;
1716 }
1717
1718 /* we copy the table name for now, it will be resolved later */
1719 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1720 cur_arg++;
1721 }
1722
Christopher Fauletac98d812019-12-18 09:20:16 +01001723 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001724 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001725 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001726 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001727 rule->check_ptr = check_trk_action;
1728
1729 *orig_arg = cur_arg;
1730 return ACT_RET_PRS_OK;
1731}
1732
Christopher Faulet46f95542019-12-20 10:07:22 +01001733/* This function executes a strict-mode actions. On success, it always returns
1734 * ACT_RET_CONT
1735 */
1736static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1737 struct session *sess, struct stream *s, int flags)
1738{
1739 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1740
1741 if (rule->action == 0) // strict-mode on
1742 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1743 else // strict-mode off
1744 msg->flags |= HTTP_MSGF_SOFT_RW;
1745 return ACT_RET_CONT;
1746}
1747
1748/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1749 * ACT_RET_PRS_ERR on error.
1750 */
1751static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1752 struct act_rule *rule, char **err)
1753{
1754 int cur_arg;
1755
Christopher Faulet46f95542019-12-20 10:07:22 +01001756 cur_arg = *orig_arg;
1757 if (!*args[cur_arg]) {
1758 memprintf(err, "expects exactly 1 arguments");
1759 return ACT_RET_PRS_ERR;
1760 }
1761
1762 if (strcasecmp(args[cur_arg], "on") == 0)
1763 rule->action = 0; // strict-mode on
1764 else if (strcasecmp(args[cur_arg], "off") == 0)
1765 rule->action = 1; // strict-mode off
1766 else {
1767 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1768 return ACT_RET_PRS_ERR;
1769 }
1770 rule->action_ptr = http_action_strict_mode;
1771
1772 *orig_arg = cur_arg + 1;
1773 return ACT_RET_PRS_OK;
1774}
1775
Christopher Faulet24231ab2020-01-24 17:44:23 +01001776/* This function executes a return action. It builds an HTX message from an
1777 * errorfile, an raw file or a log-format string, depending on <.action>
1778 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1779 * returned.
1780 */
1781static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1782 struct session *sess, struct stream *s, int flags)
1783{
1784 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001785
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001786 if (http_reply_message(s, rule->arg.http_reply) == -1)
1787 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001788
Christopher Faulet24231ab2020-01-24 17:44:23 +01001789 if (rule->from == ACT_F_HTTP_REQ) {
1790 /* let's log the request time */
1791 s->logs.tv_request = now;
1792 req->analysers &= AN_REQ_FLT_END;
1793
1794 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1795 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1796 }
1797
1798 if (!(s->flags & SF_ERR_MASK))
1799 s->flags |= SF_ERR_LOCAL;
1800 if (!(s->flags & SF_FINST_MASK))
1801 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1802
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001803 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001804}
1805
Christopher Faulet24231ab2020-01-24 17:44:23 +01001806/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001807 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1808 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001809 */
1810static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1811 struct act_rule *rule, char **err)
1812{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001813 /* Prepare parsing of log-format strings */
1814 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1815 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1816 if (!rule->arg.http_reply)
1817 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001818
Christopher Fauletba946bf2020-05-13 08:50:07 +02001819 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001820 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001821 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001822 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001823 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001824 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001825}
1826
Willy Tarreau79e57332018-10-02 16:01:16 +02001827/************************************************************************/
1828/* All supported http-request action keywords must be declared here. */
1829/************************************************************************/
1830
1831static struct action_kw_list http_req_actions = {
1832 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001833 { "add-acl", parse_http_set_map, 1 },
1834 { "add-header", parse_http_set_header, 0 },
1835 { "allow", parse_http_allow, 0 },
1836 { "auth", parse_http_auth, 0 },
1837 { "capture", parse_http_req_capture, 0 },
1838 { "del-acl", parse_http_set_map, 1 },
1839 { "del-header", parse_http_del_header, 0 },
1840 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001841 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001842 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1843 { "early-hint", parse_http_set_header, 0 },
1844 { "redirect", parse_http_redirect, 0 },
1845 { "reject", parse_http_action_reject, 0 },
1846 { "replace-header", parse_http_replace_header, 0 },
1847 { "replace-path", parse_replace_uri, 0 },
1848 { "replace-uri", parse_replace_uri, 0 },
1849 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001850 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001851 { "set-header", parse_http_set_header, 0 },
1852 { "set-log-level", parse_http_set_log_level, 0 },
1853 { "set-map", parse_http_set_map, 1 },
1854 { "set-method", parse_set_req_line, 0 },
1855 { "set-mark", parse_http_set_mark, 0 },
1856 { "set-nice", parse_http_set_nice, 0 },
1857 { "set-path", parse_set_req_line, 0 },
1858 { "set-query", parse_set_req_line, 0 },
1859 { "set-tos", parse_http_set_tos, 0 },
1860 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001861 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001862 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001863 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001864 { NULL, NULL }
1865 }
1866};
1867
Willy Tarreau0108d902018-11-25 19:14:37 +01001868INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1869
Willy Tarreau79e57332018-10-02 16:01:16 +02001870static struct action_kw_list http_res_actions = {
1871 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001872 { "add-acl", parse_http_set_map, 1 },
1873 { "add-header", parse_http_set_header, 0 },
1874 { "allow", parse_http_allow, 0 },
1875 { "capture", parse_http_res_capture, 0 },
1876 { "del-acl", parse_http_set_map, 1 },
1877 { "del-header", parse_http_del_header, 0 },
1878 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001879 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001880 { "redirect", parse_http_redirect, 0 },
1881 { "replace-header", parse_http_replace_header, 0 },
1882 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001883 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001884 { "set-header", parse_http_set_header, 0 },
1885 { "set-log-level", parse_http_set_log_level, 0 },
1886 { "set-map", parse_http_set_map, 1 },
1887 { "set-mark", parse_http_set_mark, 0 },
1888 { "set-nice", parse_http_set_nice, 0 },
1889 { "set-status", parse_http_set_status, 0 },
1890 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001891 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001892 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001893 { NULL, NULL }
1894 }
1895};
1896
Willy Tarreau0108d902018-11-25 19:14:37 +01001897INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001898
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001899static struct action_kw_list http_after_res_actions = {
1900 .kw = {
1901 { "add-header", parse_http_set_header, 0 },
1902 { "allow", parse_http_allow, 0 },
1903 { "del-header", parse_http_del_header, 0 },
1904 { "replace-header", parse_http_replace_header, 0 },
1905 { "replace-value", parse_http_replace_header, 0 },
1906 { "set-header", parse_http_set_header, 0 },
1907 { "set-status", parse_http_set_status, 0 },
1908 { "strict-mode", parse_http_strict_mode, 0 },
1909 { NULL, NULL }
1910 }
1911};
1912
1913INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
1914
Willy Tarreau79e57332018-10-02 16:01:16 +02001915/*
1916 * Local variables:
1917 * c-indent-level: 8
1918 * c-basic-offset: 8
1919 * End:
1920 */