blob: 0549bc018da02e2d746b4031e238bc3549f7a933 [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 Tarreau122eba92020-06-04 10:15:32 +020019#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020020#include <haproxy/api.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010021#include <common/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020022#include <haproxy/chunk.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020023#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020024#include <haproxy/http_htx.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020025#include <haproxy/http_rules.h>
Willy Tarreau225a90a2020-06-04 15:06:28 +020026#include <haproxy/pattern.h>
Willy Tarreaud0ef4392020-06-02 09:38:52 +020027#include <haproxy/pool.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020028#include <haproxy/regex.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020029#include <haproxy/sample.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020030#include <haproxy/tools.h>
Christopher Fauletb3048832020-05-27 15:26:43 +020031#include <common/uri_auth.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020032#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020033
Willy Tarreau278161c2020-06-04 11:18:28 +020034#include <haproxy/capture-t.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020035#include <types/global.h>
36
37#include <proto/acl.h>
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020038#include <haproxy/arg.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020039#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020040#include <proto/http_ana.h>
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
Christopher Fauletb3048832020-05-27 15:26:43 +0200887
888/* This function executes a auth action. It builds an 401/407 HTX message using
889 * the corresponding proxy's error message. On success, it returns
890 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
891 */
892static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
893 struct session *sess, struct stream *s, int flags)
894{
895 struct channel *req = &s->req;
896 struct channel *res = &s->res;
897 struct htx *htx = htx_from_buf(&res->buf);
898 struct http_reply *reply;
899 const char *auth_realm;
900 struct http_hdr_ctx ctx;
901 struct ist hdr;
902
903 /* Auth might be performed on regular http-req rules as well as on stats */
904 auth_realm = rule->arg.http.str.ptr;
905 if (!auth_realm) {
906 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
907 auth_realm = STATS_DEFAULT_REALM;
908 else
909 auth_realm = px->id;
910 }
911
912 if (!(s->txn->flags & TX_USE_PX_CONN)) {
913 s->txn->status = 401;
914 hdr = ist("WWW-Authenticate");
915 }
916 else {
917 s->txn->status = 407;
918 hdr = ist("Proxy-Authenticate");
919 }
920 reply = http_error_message(s);
921 channel_htx_truncate(res, htx);
922
923 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
924 goto fail;
925
926 /* Write the generic 40x message */
927 if (http_reply_to_htx(s, htx, reply) == -1)
928 goto fail;
929
930 /* Remove all existing occurrences of the XXX-Authenticate header */
931 ctx.blk = NULL;
932 while (http_find_header(htx, hdr, &ctx, 1))
933 http_remove_header(htx, &ctx);
934
935 /* Now a the right XXX-Authenticate header */
936 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
937 goto fail;
938
939 /* Finally forward the reply */
940 htx_to_buf(htx, &res->buf);
941 if (!http_forward_proxy_resp(s, 1))
942 goto fail;
943
944 /* Note: Only eval on the request */
945 s->logs.tv_request = now;
946 req->analysers &= AN_REQ_FLT_END;
947
948 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
949 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
950
951 if (!(s->flags & SF_ERR_MASK))
952 s->flags |= SF_ERR_LOCAL;
953 if (!(s->flags & SF_FINST_MASK))
954 s->flags |= SF_FINST_R;
955
956 stream_inc_http_err_ctr(s);
957 return ACT_RET_ABRT;
958
959 fail:
960 /* If an error occurred, remove the incomplete HTTP response from the
961 * buffer */
962 channel_htx_truncate(res, htx);
963 return ACT_RET_ERR;
964}
965
Christopher Faulet81e20172019-12-12 16:40:30 +0100966/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
967 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
968 */
969static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
970 struct act_rule *rule, char **err)
971{
972 int cur_arg;
973
Christopher Fauletb3048832020-05-27 15:26:43 +0200974 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +0100975 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +0200976 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100977 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100978
979 cur_arg = *orig_arg;
980 if (!strcmp(args[cur_arg], "realm")) {
981 cur_arg++;
982 if (!*args[cur_arg]) {
983 memprintf(err, "missing realm value.\n");
984 return ACT_RET_PRS_ERR;
985 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100986 rule->arg.http.str.ptr = strdup(args[cur_arg]);
987 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100988 cur_arg++;
989 }
990
Christopher Fauletc20b3712020-01-27 15:51:56 +0100991 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100992 *orig_arg = cur_arg;
993 return ACT_RET_PRS_OK;
994}
995
996/* Parse a "set-nice" action. It takes the nice value as argument. It returns
997 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
998 */
999static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
1000 struct act_rule *rule, char **err)
1001{
1002 int cur_arg;
1003
1004 rule->action = ACT_HTTP_SET_NICE;
1005
1006 cur_arg = *orig_arg;
1007 if (!*args[cur_arg]) {
1008 memprintf(err, "expects exactly 1 argument (integer value)");
1009 return ACT_RET_PRS_ERR;
1010 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001011 rule->arg.http.i = atoi(args[cur_arg]);
1012 if (rule->arg.http.i < -1024)
1013 rule->arg.http.i = -1024;
1014 else if (rule->arg.http.i > 1024)
1015 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001016
Christopher Fauletc20b3712020-01-27 15:51:56 +01001017 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001018 *orig_arg = cur_arg + 1;
1019 return ACT_RET_PRS_OK;
1020}
1021
1022/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1023 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1024 */
1025static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1026 struct act_rule *rule, char **err)
1027{
1028#ifdef IP_TOS
1029 char *endp;
1030 int cur_arg;
1031
1032 rule->action = ACT_HTTP_SET_TOS;
1033
1034 cur_arg = *orig_arg;
1035 if (!*args[cur_arg]) {
1036 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1037 return ACT_RET_PRS_ERR;
1038 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001039 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001040 if (endp && *endp != '\0') {
1041 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1042 return ACT_RET_PRS_ERR;
1043 }
1044
Christopher Fauletc20b3712020-01-27 15:51:56 +01001045 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001046 *orig_arg = cur_arg + 1;
1047 return ACT_RET_PRS_OK;
1048#else
1049 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1050 return ACT_RET_PRS_ERR;
1051#endif
1052}
1053
1054/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1055 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1056 */
1057static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1058 struct act_rule *rule, char **err)
1059{
1060#ifdef SO_MARK
1061 char *endp;
1062 int cur_arg;
1063
1064 rule->action = ACT_HTTP_SET_MARK;
1065
1066 cur_arg = *orig_arg;
1067 if (!*args[cur_arg]) {
1068 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1069 return ACT_RET_PRS_ERR;
1070 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001071 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001072 if (endp && *endp != '\0') {
1073 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1074 return ACT_RET_PRS_ERR;
1075 }
1076
Christopher Fauletc20b3712020-01-27 15:51:56 +01001077 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001078 *orig_arg = cur_arg + 1;
1079 global.last_checks |= LSTCHK_NETADM;
1080 return ACT_RET_PRS_OK;
1081#else
1082 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1083 return ACT_RET_PRS_ERR;
1084#endif
1085}
1086
1087/* Parse a "set-log-level" action. It takes the level value as argument. It
1088 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1089 */
1090static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1091 struct act_rule *rule, char **err)
1092{
1093 int cur_arg;
1094
1095 rule->action = ACT_HTTP_SET_LOGL;
1096
1097 cur_arg = *orig_arg;
1098 if (!*args[cur_arg]) {
1099 bad_log_level:
1100 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1101 return ACT_RET_PRS_ERR;
1102 }
1103 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001104 rule->arg.http.i = -1;
1105 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001106 goto bad_log_level;
1107
Christopher Fauletc20b3712020-01-27 15:51:56 +01001108 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001109 *orig_arg = cur_arg + 1;
1110 return ACT_RET_PRS_OK;
1111}
1112
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001113/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1114 * 103 response header with <.arg.http.str> name and with a value built
1115 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001116 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001117 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1118 * block is added to terminate the response. On success, it returns
1119 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1120 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1121 * returned.
1122 */
1123static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1124 struct session *sess, struct stream *s, int flags)
1125{
1126 struct act_rule *prev_rule, *next_rule;
1127 struct channel *res = &s->res;
1128 struct htx *htx = htx_from_buf(&res->buf);
1129 struct buffer *value = alloc_trash_chunk();
1130 enum act_return ret = ACT_RET_CONT;
1131
1132 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1133 goto leave;
1134
1135 if (!value) {
1136 if (!(s->flags & SF_ERR_MASK))
1137 s->flags |= SF_ERR_RESOURCE;
1138 goto error;
1139 }
1140
1141 /* get previous and next rules */
1142 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1143 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1144
1145 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1146 * continue to add link to a previously started response */
1147 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1148 struct htx_sl *sl;
1149 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1150 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1151
1152 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1153 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1154 if (!sl)
1155 goto error;
1156 sl->info.res.status = 103;
1157 }
1158
1159 /* Add the HTTP Early Hint HTTP 103 response heade */
1160 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1161 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1162 goto error;
1163
1164 /* if it is the last rule or the next one is not an early-hint, terminate the current
1165 * response. */
1166 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001167 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1168 /* If an error occurred during an Early-hint rule,
1169 * remove the incomplete HTTP 103 response from the
1170 * buffer */
1171 goto error;
1172 }
1173
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001174 if (!http_forward_proxy_resp(s, 0))
1175 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001176 }
1177
1178 leave:
1179 free_trash_chunk(value);
1180 return ret;
1181
1182 error:
1183 /* If an error occurred during an Early-hint rule, remove the incomplete
1184 * HTTP 103 response from the buffer */
1185 channel_htx_truncate(res, htx);
1186 ret = ACT_RET_ERR;
1187 goto leave;
1188}
1189
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001190/* This function executes a set-header or add-header actions. It builds a string
1191 * in the trash from the specified format string. It finds the action to be
1192 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001193 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001194 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1195 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1196 * ACT_RET_ERR is returned.
1197 */
1198static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1199 struct session *sess, struct stream *s, int flags)
1200{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001201 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1202 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001203 enum act_return ret = ACT_RET_CONT;
1204 struct buffer *replace;
1205 struct http_hdr_ctx ctx;
1206 struct ist n, v;
1207
1208 replace = alloc_trash_chunk();
1209 if (!replace)
1210 goto fail_alloc;
1211
1212 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1213 n = rule->arg.http.str;
1214 v = ist2(replace->area, replace->data);
1215
1216 if (rule->action == 0) { // set-header
1217 /* remove all occurrences of the header */
1218 ctx.blk = NULL;
1219 while (http_find_header(htx, n, &ctx, 1))
1220 http_remove_header(htx, &ctx);
1221 }
1222
1223 /* Now add header */
1224 if (!http_add_header(htx, n, v))
1225 goto fail_rewrite;
1226
1227 leave:
1228 free_trash_chunk(replace);
1229 return ret;
1230
1231 fail_alloc:
1232 if (!(s->flags & SF_ERR_MASK))
1233 s->flags |= SF_ERR_RESOURCE;
1234 ret = ACT_RET_ERR;
1235 goto leave;
1236
1237 fail_rewrite:
1238 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1239 if (s->flags & SF_BE_ASSIGNED)
1240 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1241 if (sess->listener->counters)
1242 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1243 if (objt_server(s->target))
1244 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1245
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001246 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001247 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001248 if (!(s->flags & SF_ERR_MASK))
1249 s->flags |= SF_ERR_PRXCOND;
1250 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001251 goto leave;
1252}
1253
Christopher Faulet81e20172019-12-12 16:40:30 +01001254/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1255 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1256 * on success, ACT_RET_PRS_ERR on error.
1257 *
1258 * Note: same function is used for the request and the response. However
1259 * "early-hint" rules are only supported for request rules.
1260 */
1261static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1262 struct act_rule *rule, char **err)
1263{
Christopher Faulet81e20172019-12-12 16:40:30 +01001264 int cap, cur_arg;
1265
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001266 if (args[*orig_arg-1][0] == 'e') {
1267 rule->action = ACT_CUSTOM;
1268 rule->action_ptr = http_action_early_hint;
1269 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001270 else {
1271 if (args[*orig_arg-1][0] == 's')
1272 rule->action = 0; // set-header
1273 else
1274 rule->action = 1; // add-header
1275 rule->action_ptr = http_action_set_header;
1276 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001277 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001278
1279 cur_arg = *orig_arg;
1280 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1281 memprintf(err, "expects exactly 2 arguments");
1282 return ACT_RET_PRS_ERR;
1283 }
1284
Christopher Faulet81e20172019-12-12 16:40:30 +01001285
Christopher Faulet96bff762019-12-17 13:46:18 +01001286 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1287 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1288 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001289
1290 if (rule->from == ACT_F_HTTP_REQ) {
1291 px->conf.args.ctx = ARGC_HRQ;
1292 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1293 }
1294 else{
1295 px->conf.args.ctx = ARGC_HRS;
1296 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1297 }
1298
1299 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001300 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001301 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001302 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001303 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001304
1305 free(px->conf.lfs_file);
1306 px->conf.lfs_file = strdup(px->conf.args.file);
1307 px->conf.lfs_line = px->conf.args.line;
1308
1309 *orig_arg = cur_arg + 1;
1310 return ACT_RET_PRS_OK;
1311}
1312
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001313/* This function executes a replace-header or replace-value actions. It
1314 * builds a string in the trash from the specified format string. It finds
1315 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001316 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001317 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1318 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1319 * processing continue. Otherwsize ACT_RET_ERR is returned.
1320 */
1321static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1322 struct session *sess, struct stream *s, int flags)
1323{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001324 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1325 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001326 enum act_return ret = ACT_RET_CONT;
1327 struct buffer *replace;
1328 int r;
1329
1330 replace = alloc_trash_chunk();
1331 if (!replace)
1332 goto fail_alloc;
1333
1334 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1335
1336 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1337 if (r == -1)
1338 goto fail_rewrite;
1339
1340 leave:
1341 free_trash_chunk(replace);
1342 return ret;
1343
1344 fail_alloc:
1345 if (!(s->flags & SF_ERR_MASK))
1346 s->flags |= SF_ERR_RESOURCE;
1347 ret = ACT_RET_ERR;
1348 goto leave;
1349
1350 fail_rewrite:
1351 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1352 if (s->flags & SF_BE_ASSIGNED)
1353 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1354 if (sess->listener->counters)
1355 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1356 if (objt_server(s->target))
1357 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1358
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001359 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001360 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001361 if (!(s->flags & SF_ERR_MASK))
1362 s->flags |= SF_ERR_PRXCOND;
1363 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001364 goto leave;
1365}
1366
Christopher Faulet81e20172019-12-12 16:40:30 +01001367/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1368 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1369 * success, ACT_RET_PRS_ERR on error.
1370 */
1371static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1372 struct act_rule *rule, char **err)
1373{
1374 int cap, cur_arg;
1375
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001376 if (args[*orig_arg-1][8] == 'h')
1377 rule->action = 0; // replace-header
1378 else
1379 rule->action = 1; // replace-value
1380 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001381 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001382
1383 cur_arg = *orig_arg;
1384 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1385 memprintf(err, "expects exactly 3 arguments");
1386 return ACT_RET_PRS_ERR;
1387 }
1388
Christopher Faulet96bff762019-12-17 13:46:18 +01001389 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1390 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1391 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001392
1393 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001394 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001395 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001396 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001397 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001398
1399 if (rule->from == ACT_F_HTTP_REQ) {
1400 px->conf.args.ctx = ARGC_HRQ;
1401 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1402 }
1403 else{
1404 px->conf.args.ctx = ARGC_HRS;
1405 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1406 }
1407
1408 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001409 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001410 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001411 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001412 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001413 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001414
1415 free(px->conf.lfs_file);
1416 px->conf.lfs_file = strdup(px->conf.args.file);
1417 px->conf.lfs_line = px->conf.args.line;
1418
1419 *orig_arg = cur_arg + 1;
1420 return ACT_RET_PRS_OK;
1421}
1422
1423/* Parse a "del-header" action. It takes an header name as argument. It returns
1424 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1425 */
1426static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1427 struct act_rule *rule, char **err)
1428{
1429 int cur_arg;
1430
1431 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001432 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001433
1434 cur_arg = *orig_arg;
1435 if (!*args[cur_arg]) {
1436 memprintf(err, "expects exactly 1 arguments");
1437 return ACT_RET_PRS_ERR;
1438 }
1439
Christopher Faulet96bff762019-12-17 13:46:18 +01001440 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1441 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001442 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1443
Christopher Fauletc20b3712020-01-27 15:51:56 +01001444 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001445 *orig_arg = cur_arg + 1;
1446 return ACT_RET_PRS_OK;
1447}
1448
Christopher Faulet2eb53962020-01-14 14:47:34 +01001449/* Release memory allocated by an http redirect action. */
1450static void release_http_redir(struct act_rule *rule)
1451{
1452 struct logformat_node *lf, *lfb;
1453 struct redirect_rule *redir;
1454
1455 redir = rule->arg.redir;
1456 LIST_DEL(&redir->list);
1457 if (redir->cond) {
1458 prune_acl_cond(redir->cond);
1459 free(redir->cond);
1460 }
1461 free(redir->rdr_str);
1462 free(redir->cookie_str);
1463 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1464 LIST_DEL(&lf->list);
1465 free(lf);
1466 }
1467 free(redir);
1468}
1469
Christopher Faulet81e20172019-12-12 16:40:30 +01001470/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1471 * ACT_RET_PRS_ERR on error.
1472 */
1473static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1474 struct act_rule *rule, char **err)
1475{
1476 struct redirect_rule *redir;
1477 int dir, cur_arg;
1478
1479 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001480 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001481 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001482
1483 cur_arg = *orig_arg;
1484
1485 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1486 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1487 return ACT_RET_PRS_ERR;
1488
1489 rule->arg.redir = redir;
1490 rule->cond = redir->cond;
1491 redir->cond = NULL;
1492
1493 /* skip all arguments */
1494 while (*args[cur_arg])
1495 cur_arg++;
1496
1497 *orig_arg = cur_arg;
1498 return ACT_RET_PRS_OK;
1499}
1500
Christopher Faulet046cf442019-12-17 15:45:23 +01001501/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1502 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1503 */
1504static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1505 struct session *sess, struct stream *s, int flags)
1506{
1507 struct pat_ref *ref;
1508 struct buffer *key = NULL, *value = NULL;
1509 enum act_return ret = ACT_RET_CONT;
1510
1511 /* collect reference */
1512 ref = pat_ref_lookup(rule->arg.map.ref);
1513 if (!ref)
1514 goto leave;
1515
1516 /* allocate key */
1517 key = alloc_trash_chunk();
1518 if (!key)
1519 goto fail_alloc;
1520
1521 /* collect key */
1522 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1523 key->area[key->data] = '\0';
1524
1525 switch (rule->action) {
1526 case 0: // add-acl
1527 /* add entry only if it does not already exist */
1528 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1529 if (pat_ref_find_elt(ref, key->area) == NULL)
1530 pat_ref_add(ref, key->area, NULL, NULL);
1531 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1532 break;
1533
1534 case 1: // set-map
1535 /* allocate value */
1536 value = alloc_trash_chunk();
1537 if (!value)
1538 goto fail_alloc;
1539
1540 /* collect value */
1541 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1542 value->area[value->data] = '\0';
1543
1544 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1545 if (pat_ref_find_elt(ref, key->area) != NULL) {
1546 /* update entry if it exists */
1547 pat_ref_set(ref, key->area, value->area, NULL);
1548 }
1549 else {
1550 /* insert a new entry */
1551 pat_ref_add(ref, key->area, value->area, NULL);
1552 }
1553 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1554 break;
1555
1556 case 2: // del-acl
1557 case 3: // del-map
1558 /* returned code: 1=ok, 0=ko */
1559 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1560 pat_ref_delete(ref, key->area);
1561 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1562 break;
1563
1564 default:
1565 ret = ACT_RET_ERR;
1566 }
1567
1568
1569 leave:
1570 free_trash_chunk(key);
1571 free_trash_chunk(value);
1572 return ret;
1573
1574 fail_alloc:
1575 if (!(s->flags & SF_ERR_MASK))
1576 s->flags |= SF_ERR_RESOURCE;
1577 ret = ACT_RET_ERR;
1578 goto leave;
1579}
1580
Christopher Faulet2eb53962020-01-14 14:47:34 +01001581/* Release memory allocated by an http map/acl action. */
1582static void release_http_map(struct act_rule *rule)
1583{
1584 struct logformat_node *lf, *lfb;
1585
1586 free(rule->arg.map.ref);
1587 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1588 LIST_DEL(&lf->list);
1589 release_sample_expr(lf->expr);
1590 free(lf->arg);
1591 free(lf);
1592 }
1593 if (rule->action == 1) {
1594 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1595 LIST_DEL(&lf->list);
1596 release_sample_expr(lf->expr);
1597 free(lf->arg);
1598 free(lf);
1599 }
1600 }
1601}
1602
Christopher Faulet81e20172019-12-12 16:40:30 +01001603/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001604 * two log-format string as argument depending on the action. The action is
1605 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1606 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001607 */
1608static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1609 struct act_rule *rule, char **err)
1610{
1611 int cap, cur_arg;
1612
Christopher Faulet046cf442019-12-17 15:45:23 +01001613 if (args[*orig_arg-1][0] == 'a') // add-acl
1614 rule->action = 0;
1615 else if (args[*orig_arg-1][0] == 's') // set-map
1616 rule->action = 1;
1617 else if (args[*orig_arg-1][4] == 'a') // del-acl
1618 rule->action = 2;
1619 else if (args[*orig_arg-1][4] == 'm') // del-map
1620 rule->action = 3;
1621 else {
1622 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1623 return ACT_RET_PRS_ERR;
1624 }
1625 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001626 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001627
1628 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001629 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1630 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001631 memprintf(err, "expects exactly 2 arguments");
1632 return ACT_RET_PRS_ERR;
1633 }
1634 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001635 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001636 memprintf(err, "expects exactly 1 arguments");
1637 return ACT_RET_PRS_ERR;
1638 }
1639
1640 /*
1641 * '+ 8' for 'set-map(' (same for del-map)
1642 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1643 */
1644 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1645
1646 if (rule->from == ACT_F_HTTP_REQ) {
1647 px->conf.args.ctx = ARGC_HRQ;
1648 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1649 }
1650 else{
1651 px->conf.args.ctx = ARGC_HRS;
1652 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1653 }
1654
1655 /* key pattern */
1656 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001657 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1658 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001659 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001660 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001661
Christopher Faulet046cf442019-12-17 15:45:23 +01001662 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001663 /* value pattern for set-map only */
1664 cur_arg++;
1665 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001666 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1667 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001668 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001669 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001670 }
1671
1672 free(px->conf.lfs_file);
1673 px->conf.lfs_file = strdup(px->conf.args.file);
1674 px->conf.lfs_line = px->conf.args.line;
1675
1676 *orig_arg = cur_arg + 1;
1677 return ACT_RET_PRS_OK;
1678}
1679
Christopher Fauletac98d812019-12-18 09:20:16 +01001680/* This function executes a track-sc* actions. On success, it returns
1681 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1682 */
1683static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1684 struct session *sess, struct stream *s, int flags)
1685{
1686 struct stktable *t;
1687 struct stksess *ts;
1688 struct stktable_key *key;
1689 void *ptr1, *ptr2, *ptr3, *ptr4;
1690 int opt;
1691
1692 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1693 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1694
1695 t = rule->arg.trk_ctr.table.t;
1696 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1697
1698 if (!key)
1699 goto end;
1700 ts = stktable_get_entry(t, key);
1701 if (!ts)
1702 goto end;
1703
1704 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1705
1706 /* let's count a new HTTP request as it's the first time we do it */
1707 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1708 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1709
1710 /* When the client triggers a 4xx from the server, it's most often due
1711 * to a missing object or permission. These events should be tracked
1712 * because if they happen often, it may indicate a brute force or a
1713 * vulnerability scan. Normally this is done when receiving the response
1714 * but here we're tracking after this ought to have been done so we have
1715 * to do it on purpose.
1716 */
1717 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1718 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1719 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1720 }
1721
1722 if (ptr1 || ptr2 || ptr3 || ptr4) {
1723 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1724
1725 if (ptr1)
1726 stktable_data_cast(ptr1, http_req_cnt)++;
1727 if (ptr2)
1728 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1729 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1730 if (ptr3)
1731 stktable_data_cast(ptr3, http_err_cnt)++;
1732 if (ptr4)
1733 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1734 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1735
1736 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1737
1738 /* If data was modified, we need to touch to re-schedule sync */
1739 stktable_touch_local(t, ts, 0);
1740 }
1741
1742 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1743 if (sess->fe != s->be)
1744 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1745
1746 end:
1747 return ACT_RET_CONT;
1748}
Christopher Faulet81e20172019-12-12 16:40:30 +01001749
Christopher Faulet2eb53962020-01-14 14:47:34 +01001750static void release_http_track_sc(struct act_rule *rule)
1751{
1752 release_sample_expr(rule->arg.trk_ctr.expr);
1753}
1754
Christopher Faulet81e20172019-12-12 16:40:30 +01001755/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1756 * ACT_RET_PRS_ERR on error.
1757 */
1758static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1759 struct act_rule *rule, char **err)
1760{
1761 struct sample_expr *expr;
1762 unsigned int where;
1763 unsigned int tsc_num;
1764 const char *tsc_num_str;
1765 int cur_arg;
1766
1767 tsc_num_str = &args[*orig_arg-1][8];
1768 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1769 return ACT_RET_PRS_ERR;
1770
1771 cur_arg = *orig_arg;
1772 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001773 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001774 if (!expr)
1775 return ACT_RET_PRS_ERR;
1776
1777 where = 0;
1778 if (px->cap & PR_CAP_FE)
1779 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1780 if (px->cap & PR_CAP_BE)
1781 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1782
1783 if (!(expr->fetch->val & where)) {
1784 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1785 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001786 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001787 return ACT_RET_PRS_ERR;
1788 }
1789
1790 if (strcmp(args[cur_arg], "table") == 0) {
1791 cur_arg++;
1792 if (!*args[cur_arg]) {
1793 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001794 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001795 return ACT_RET_PRS_ERR;
1796 }
1797
1798 /* we copy the table name for now, it will be resolved later */
1799 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1800 cur_arg++;
1801 }
1802
Christopher Fauletac98d812019-12-18 09:20:16 +01001803 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001804 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001805 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001806 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001807 rule->check_ptr = check_trk_action;
1808
1809 *orig_arg = cur_arg;
1810 return ACT_RET_PRS_OK;
1811}
1812
Christopher Faulet46f95542019-12-20 10:07:22 +01001813/* This function executes a strict-mode actions. On success, it always returns
1814 * ACT_RET_CONT
1815 */
1816static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1817 struct session *sess, struct stream *s, int flags)
1818{
1819 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1820
1821 if (rule->action == 0) // strict-mode on
1822 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1823 else // strict-mode off
1824 msg->flags |= HTTP_MSGF_SOFT_RW;
1825 return ACT_RET_CONT;
1826}
1827
1828/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1829 * ACT_RET_PRS_ERR on error.
1830 */
1831static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1832 struct act_rule *rule, char **err)
1833{
1834 int cur_arg;
1835
Christopher Faulet46f95542019-12-20 10:07:22 +01001836 cur_arg = *orig_arg;
1837 if (!*args[cur_arg]) {
1838 memprintf(err, "expects exactly 1 arguments");
1839 return ACT_RET_PRS_ERR;
1840 }
1841
1842 if (strcasecmp(args[cur_arg], "on") == 0)
1843 rule->action = 0; // strict-mode on
1844 else if (strcasecmp(args[cur_arg], "off") == 0)
1845 rule->action = 1; // strict-mode off
1846 else {
1847 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1848 return ACT_RET_PRS_ERR;
1849 }
1850 rule->action_ptr = http_action_strict_mode;
1851
1852 *orig_arg = cur_arg + 1;
1853 return ACT_RET_PRS_OK;
1854}
1855
Christopher Faulet24231ab2020-01-24 17:44:23 +01001856/* This function executes a return action. It builds an HTX message from an
1857 * errorfile, an raw file or a log-format string, depending on <.action>
1858 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1859 * returned.
1860 */
1861static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1862 struct session *sess, struct stream *s, int flags)
1863{
1864 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001865
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001866 if (http_reply_message(s, rule->arg.http_reply) == -1)
1867 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001868
Christopher Faulet24231ab2020-01-24 17:44:23 +01001869 if (rule->from == ACT_F_HTTP_REQ) {
1870 /* let's log the request time */
1871 s->logs.tv_request = now;
1872 req->analysers &= AN_REQ_FLT_END;
1873
1874 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1875 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1876 }
1877
1878 if (!(s->flags & SF_ERR_MASK))
1879 s->flags |= SF_ERR_LOCAL;
1880 if (!(s->flags & SF_FINST_MASK))
1881 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1882
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001883 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001884}
1885
Christopher Faulet24231ab2020-01-24 17:44:23 +01001886/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001887 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1888 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001889 */
1890static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1891 struct act_rule *rule, char **err)
1892{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001893 /* Prepare parsing of log-format strings */
1894 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1895 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1896 if (!rule->arg.http_reply)
1897 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001898
Christopher Fauletba946bf2020-05-13 08:50:07 +02001899 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001900 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001901 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001902 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001903 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001904 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001905}
1906
Willy Tarreau79e57332018-10-02 16:01:16 +02001907/************************************************************************/
1908/* All supported http-request action keywords must be declared here. */
1909/************************************************************************/
1910
1911static struct action_kw_list http_req_actions = {
1912 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001913 { "add-acl", parse_http_set_map, 1 },
1914 { "add-header", parse_http_set_header, 0 },
1915 { "allow", parse_http_allow, 0 },
1916 { "auth", parse_http_auth, 0 },
1917 { "capture", parse_http_req_capture, 0 },
1918 { "del-acl", parse_http_set_map, 1 },
1919 { "del-header", parse_http_del_header, 0 },
1920 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001921 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001922 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1923 { "early-hint", parse_http_set_header, 0 },
1924 { "redirect", parse_http_redirect, 0 },
1925 { "reject", parse_http_action_reject, 0 },
1926 { "replace-header", parse_http_replace_header, 0 },
1927 { "replace-path", parse_replace_uri, 0 },
1928 { "replace-uri", parse_replace_uri, 0 },
1929 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001930 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001931 { "set-header", parse_http_set_header, 0 },
1932 { "set-log-level", parse_http_set_log_level, 0 },
1933 { "set-map", parse_http_set_map, 1 },
1934 { "set-method", parse_set_req_line, 0 },
1935 { "set-mark", parse_http_set_mark, 0 },
1936 { "set-nice", parse_http_set_nice, 0 },
1937 { "set-path", parse_set_req_line, 0 },
1938 { "set-query", parse_set_req_line, 0 },
1939 { "set-tos", parse_http_set_tos, 0 },
1940 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001941 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001942 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001943 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001944 { NULL, NULL }
1945 }
1946};
1947
Willy Tarreau0108d902018-11-25 19:14:37 +01001948INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1949
Willy Tarreau79e57332018-10-02 16:01:16 +02001950static struct action_kw_list http_res_actions = {
1951 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001952 { "add-acl", parse_http_set_map, 1 },
1953 { "add-header", parse_http_set_header, 0 },
1954 { "allow", parse_http_allow, 0 },
1955 { "capture", parse_http_res_capture, 0 },
1956 { "del-acl", parse_http_set_map, 1 },
1957 { "del-header", parse_http_del_header, 0 },
1958 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001959 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001960 { "redirect", parse_http_redirect, 0 },
1961 { "replace-header", parse_http_replace_header, 0 },
1962 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001963 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001964 { "set-header", parse_http_set_header, 0 },
1965 { "set-log-level", parse_http_set_log_level, 0 },
1966 { "set-map", parse_http_set_map, 1 },
1967 { "set-mark", parse_http_set_mark, 0 },
1968 { "set-nice", parse_http_set_nice, 0 },
1969 { "set-status", parse_http_set_status, 0 },
1970 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001971 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001972 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001973 { NULL, NULL }
1974 }
1975};
1976
Willy Tarreau0108d902018-11-25 19:14:37 +01001977INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001978
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001979static struct action_kw_list http_after_res_actions = {
1980 .kw = {
1981 { "add-header", parse_http_set_header, 0 },
1982 { "allow", parse_http_allow, 0 },
1983 { "del-header", parse_http_del_header, 0 },
1984 { "replace-header", parse_http_replace_header, 0 },
1985 { "replace-value", parse_http_replace_header, 0 },
1986 { "set-header", parse_http_set_header, 0 },
1987 { "set-status", parse_http_set_status, 0 },
1988 { "strict-mode", parse_http_strict_mode, 0 },
1989 { NULL, NULL }
1990 }
1991};
1992
1993INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
1994
Willy Tarreau79e57332018-10-02 16:01:16 +02001995/*
1996 * Local variables:
1997 * c-indent-level: 8
1998 * c-basic-offset: 8
1999 * End:
2000 */