blob: 76e6d2b2c9f75fe7fa8d1daf718a11cd2627bbe9 [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 Tarreaudcc048a2020-06-04 19:11:43 +020019#include <haproxy/acl.h>
Willy Tarreau122eba92020-06-04 10:15:32 +020020#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020021#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020022#include <haproxy/arg.h>
23#include <haproxy/capture-t.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020024#include <haproxy/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020025#include <haproxy/chunk.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020026#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020027#include <haproxy/http.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020028#include <haproxy/http_ana.h>
Willy Tarreau87735332020-06-04 09:08:41 +020029#include <haproxy/http_htx.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020030#include <haproxy/http_rules.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020031#include <haproxy/log.h>
Willy Tarreau225a90a2020-06-04 15:06:28 +020032#include <haproxy/pattern.h>
Willy Tarreaud0ef4392020-06-02 09:38:52 +020033#include <haproxy/pool.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020034#include <haproxy/regex.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020035#include <haproxy/sample.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020036#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020037#include <haproxy/tools.h>
Willy Tarreau8c42b8a2020-06-04 19:27:34 +020038#include <haproxy/uri_auth-t.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020039#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020040
Willy Tarreau79e57332018-10-02 16:01:16 +020041
Christopher Faulet2eb53962020-01-14 14:47:34 +010042/* Release memory allocated by most of HTTP actions. Concretly, it releases
43 * <arg.http>.
44 */
45static void release_http_action(struct act_rule *rule)
46{
47 struct logformat_node *lf, *lfb;
48
Tim Duesterhused526372020-03-05 17:56:33 +010049 istfree(&rule->arg.http.str);
Christopher Faulet2eb53962020-01-14 14:47:34 +010050 if (rule->arg.http.re)
51 regex_free(rule->arg.http.re);
52 list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
53 LIST_DEL(&lf->list);
54 release_sample_expr(lf->expr);
55 free(lf->arg);
56 free(lf);
57 }
58}
59
Christopher Faulet5cb513a2020-05-13 17:56:56 +020060/* Release memory allocated by HTTP actions relying on an http reply. Concretly,
61 * it releases <.arg.http_reply>
62 */
63static void release_act_http_reply(struct act_rule *rule)
64{
65 release_http_reply(rule->arg.http_reply);
66 rule->arg.http_reply = NULL;
67}
68
69
70/* Check function for HTTP actions relying on an http reply. The function
71 * returns 1 in success case, otherwise, it returns 0 and err is filled.
72 */
73static int check_act_http_reply(struct act_rule *rule, struct proxy *px, char **err)
74{
75 struct http_reply *reply = rule->arg.http_reply;
76
77 if (!http_check_http_reply(reply, px, err)) {
78 release_act_http_reply(rule);
79 return 0;
80 }
81 return 1;
82}
83
Willy Tarreau79e57332018-10-02 16:01:16 +020084
85/* This function executes one of the set-{method,path,query,uri} actions. It
86 * builds a string in the trash from the specified format string. It finds
Christopher Faulet2c22a692019-12-18 15:39:56 +010087 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +050088 * parse_set_req_line(). The replacement action is executed by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010089 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
90 * occurs while soft rewrites are enabled, the action is canceled, but the rule
91 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020092 */
93static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
94 struct session *sess, struct stream *s, int flags)
95{
96 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010097 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020098
99 replace = alloc_trash_chunk();
100 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100101 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +0200102
103 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet2c22a692019-12-18 15:39:56 +0100104 if (rule->action == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200105 replace->area[replace->data++] = '?';
106 replace->data += build_logline(s, replace->area + replace->data,
107 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +0100108 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200109
Christopher Faulet2c22a692019-12-18 15:39:56 +0100110 if (http_req_replace_stline(rule->action, replace->area, replace->data, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100111 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +0200112
Christopher Faulete00d06c2019-12-16 17:18:42 +0100113 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +0200114 free_trash_chunk(replace);
115 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100116
117 fail_alloc:
118 if (!(s->flags & SF_ERR_MASK))
119 s->flags |= SF_ERR_RESOURCE;
120 ret = ACT_RET_ERR;
121 goto leave;
122
123 fail_rewrite:
124 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
125 if (s->flags & SF_BE_ASSIGNED)
126 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
127 if (sess->listener->counters)
128 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
129 if (objt_server(s->target))
130 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
131
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100132 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100133 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100134 if (!(s->flags & SF_ERR_MASK))
135 s->flags |= SF_ERR_PRXCOND;
136 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100137 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +0200138}
139
140/* parse an http-request action among :
141 * set-method
142 * set-path
143 * set-query
144 * set-uri
145 *
146 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100147 * The resulting rule makes use of <http.fmt> to store the log-format list head,
Christopher Faulet2c22a692019-12-18 15:39:56 +0100148 * and <.action> to store the action type as an int (0=method, 1=path, 2=query,
149 * 3=uri). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Willy Tarreau79e57332018-10-02 16:01:16 +0200150 */
151static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
152 struct act_rule *rule, char **err)
153{
154 int cur_arg = *orig_arg;
155
Willy Tarreau79e57332018-10-02 16:01:16 +0200156 switch (args[0][4]) {
157 case 'm' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100158 rule->action = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200159 break;
160 case 'p' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100161 rule->action = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200162 break;
163 case 'q' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100164 rule->action = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200165 break;
166 case 'u' :
Christopher Faulet2c22a692019-12-18 15:39:56 +0100167 rule->action = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200168 break;
169 default:
170 memprintf(err, "internal error: unhandled action '%s'", args[0]);
171 return ACT_RET_PRS_ERR;
172 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100173 rule->action_ptr = http_action_set_req_line;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100174 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200175
176 if (!*args[cur_arg] ||
177 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
178 memprintf(err, "expects exactly 1 argument <format>");
179 return ACT_RET_PRS_ERR;
180 }
181
Christopher Faulet96bff762019-12-17 13:46:18 +0100182 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200183 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100184 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200185 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
186 return ACT_RET_PRS_ERR;
187 }
188
189 (*orig_arg)++;
190 return ACT_RET_PRS_OK;
191}
192
Willy Tarreau33810222019-06-12 17:44:02 +0200193/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100194 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200195 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100196 * in <http.re> to replace the URI. It uses the format string present in
Christopher Faulet2c22a692019-12-18 15:39:56 +0100197 * <http.fmt>. The component to act on (path/uri) is taken from <.action> which
Christopher Faulet96bff762019-12-17 13:46:18 +0100198 * contains 1 for the path or 3 for the URI (values used by
199 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
200 * occurs while soft rewrites are enabled, the action is canceled, but the rule
201 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200202 */
203static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
204 struct session *sess, struct stream *s, int flags)
205{
Christopher Faulet13403762019-12-13 09:01:57 +0100206 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200207 struct buffer *replace, *output;
208 struct ist uri;
209 int len;
210
211 replace = alloc_trash_chunk();
212 output = alloc_trash_chunk();
213 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100214 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200215 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100216
Christopher Faulet2c22a692019-12-18 15:39:56 +0100217 if (rule->action == 1) // replace-path
Jerome Magnin4bbc9492020-02-21 10:37:48 +0100218 uri = iststop(http_get_path(uri), '?');
Willy Tarreau262c3f12019-12-17 06:52:51 +0100219
Christopher Faulet96bff762019-12-17 13:46:18 +0100220 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200221 goto leave;
222
Christopher Faulet96bff762019-12-17 13:46:18 +0100223 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200224
225 /* note: uri.ptr doesn't need to be zero-terminated because it will
226 * only be used to pick pmatch references.
227 */
228 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
229 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100230 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200231
Christopher Faulet2c22a692019-12-18 15:39:56 +0100232 if (http_req_replace_stline(rule->action, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100233 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200234
Christopher Faulete00d06c2019-12-16 17:18:42 +0100235 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200236 free_trash_chunk(output);
237 free_trash_chunk(replace);
238 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100239
240 fail_alloc:
241 if (!(s->flags & SF_ERR_MASK))
242 s->flags |= SF_ERR_RESOURCE;
243 ret = ACT_RET_ERR;
244 goto leave;
245
246 fail_rewrite:
247 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
248 if (s->flags & SF_BE_ASSIGNED)
249 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
250 if (sess->listener->counters)
251 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
252 if (objt_server(s->target))
253 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
254
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100255 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100256 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100257 if (!(s->flags & SF_ERR_MASK))
258 s->flags |= SF_ERR_PRXCOND;
259 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100260 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200261}
262
Willy Tarreau262c3f12019-12-17 06:52:51 +0100263/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200264 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet2c22a692019-12-18 15:39:56 +0100265 * The resulting rule makes use of <.action> to store the action (1/3 for now),
Christopher Faulet96bff762019-12-17 13:46:18 +0100266 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200267 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
268 */
269static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
270 struct act_rule *rule, char **err)
271{
272 int cur_arg = *orig_arg;
273 char *error = NULL;
274
Willy Tarreau262c3f12019-12-17 06:52:51 +0100275 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet2c22a692019-12-18 15:39:56 +0100276 rule->action = 1; // replace-path, same as set-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100277 else
Christopher Faulet2c22a692019-12-18 15:39:56 +0100278 rule->action = 3; // replace-uri, same as set-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100279
Willy Tarreau33810222019-06-12 17:44:02 +0200280 rule->action_ptr = http_action_replace_uri;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100281 rule->release_ptr = release_http_action;
Willy Tarreau33810222019-06-12 17:44:02 +0200282
283 if (!*args[cur_arg] || !*args[cur_arg+1] ||
284 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
285 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
286 return ACT_RET_PRS_ERR;
287 }
288
Christopher Faulet96bff762019-12-17 13:46:18 +0100289 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200290 memprintf(err, "failed to parse the regex : %s", error);
291 free(error);
292 return ACT_RET_PRS_ERR;
293 }
294
Christopher Faulet96bff762019-12-17 13:46:18 +0100295 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200296 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100297 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200298 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
Christopher Faulet1337b322020-01-14 14:50:55 +0100299 regex_free(rule->arg.http.re);
Willy Tarreau33810222019-06-12 17:44:02 +0200300 return ACT_RET_PRS_ERR;
301 }
302
303 (*orig_arg) += 2;
304 return ACT_RET_PRS_OK;
305}
306
Willy Tarreau79e57332018-10-02 16:01:16 +0200307/* This function is just a compliant action wrapper for "set-status". */
308static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
309 struct session *sess, struct stream *s, int flags)
310{
Christopher Faulet96bff762019-12-17 13:46:18 +0100311 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100312 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
313 if (s->flags & SF_BE_ASSIGNED)
314 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
315 if (sess->listener->counters)
316 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
317 if (objt_server(s->target))
318 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
319
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100320 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100321 if (!(s->flags & SF_ERR_MASK))
322 s->flags |= SF_ERR_PRXCOND;
Christopher Faulet692a6c22020-02-07 10:22:31 +0100323 return ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +0100324 }
Christopher Faulete00d06c2019-12-16 17:18:42 +0100325 }
326
Willy Tarreau79e57332018-10-02 16:01:16 +0200327 return ACT_RET_CONT;
328}
329
330/* parse set-status action:
331 * This action accepts a single argument of type int representing
332 * an http status code. It returns ACT_RET_PRS_OK on success,
333 * ACT_RET_PRS_ERR on error.
334 */
335static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
336 struct act_rule *rule, char **err)
337{
338 char *error;
339
340 rule->action = ACT_CUSTOM;
341 rule->action_ptr = action_http_set_status;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100342 rule->release_ptr = release_http_action;
Willy Tarreau79e57332018-10-02 16:01:16 +0200343
344 /* Check if an argument is available */
345 if (!*args[*orig_arg]) {
346 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
347 return ACT_RET_PRS_ERR;
348 }
349
350 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100351 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
352 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200353 memprintf(err, "expects an integer status code between 100 and 999");
354 return ACT_RET_PRS_ERR;
355 }
356
357 (*orig_arg)++;
358
359 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100360 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200361 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
362 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
363 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100364 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
365 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200366 (*orig_arg)++;
367 }
368
Christopher Fauletc20b3712020-01-27 15:51:56 +0100369 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200370 return ACT_RET_PRS_OK;
371}
372
373/* This function executes the "reject" HTTP action. It clears the request and
374 * response buffer without sending any response. It can be useful as an HTTP
375 * alternative to the silent-drop action to defend against DoS attacks, and may
376 * also be used with HTTP/2 to close a connection instead of just a stream.
377 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet90d22a82020-03-06 11:18:39 +0100378 * flags will indicate "PR". It always returns ACT_RET_ABRT.
Willy Tarreau79e57332018-10-02 16:01:16 +0200379 */
380static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
381 struct session *sess, struct stream *s, int flags)
382{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100383 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200384 channel_abort(&s->req);
385 channel_abort(&s->res);
Christopher Fauletd4a824e2020-03-06 15:07:09 +0100386 s->req.analysers &= AN_REQ_FLT_END;
387 s->res.analysers &= AN_RES_FLT_END;
Willy Tarreau79e57332018-10-02 16:01:16 +0200388
Olivier Houcharda798bf52019-03-08 18:52:00 +0100389 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
390 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200391 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100392 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200393
394 if (!(s->flags & SF_ERR_MASK))
395 s->flags |= SF_ERR_PRXCOND;
396 if (!(s->flags & SF_FINST_MASK))
397 s->flags |= SF_FINST_R;
398
Christopher Faulet90d22a82020-03-06 11:18:39 +0100399 return ACT_RET_ABRT;
Willy Tarreau79e57332018-10-02 16:01:16 +0200400}
401
402/* parse the "reject" action:
403 * This action takes no argument and returns ACT_RET_PRS_OK on success,
404 * ACT_RET_PRS_ERR on error.
405 */
406static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
407 struct act_rule *rule, char **err)
408{
409 rule->action = ACT_CUSTOM;
410 rule->action_ptr = http_action_reject;
411 return ACT_RET_PRS_OK;
412}
413
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200414/* This function executes the "disable-l7-retry" HTTP action.
415 * It disables L7 retries (all retry except for a connection failure). This
416 * can be useful for example to avoid retrying on POST requests.
417 * It just removes the L7 retry flag on the stream_interface, and always
418 * return ACT_RET_CONT;
419 */
420static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
421 struct session *sess, struct stream *s, int flags)
422{
423 struct stream_interface *si = &s->si[1];
424
425 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
426 * let's be future-proof and remove it anyway.
427 */
428 si->flags &= ~SI_FL_L7_RETRY;
429 si->flags |= SI_FL_D_L7_RETRY;
430 return ACT_RET_CONT;
431}
432
433/* parse the "disable-l7-retry" action:
434 * This action takes no argument and returns ACT_RET_PRS_OK on success,
435 * ACT_RET_PRS_ERR on error.
436 */
437static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
438 int *orig_args, struct proxy *px,
439 struct act_rule *rule, char **err)
440{
441 rule->action = ACT_CUSTOM;
442 rule->action_ptr = http_req_disable_l7_retry;
443 return ACT_RET_PRS_OK;
444}
445
Willy Tarreau79e57332018-10-02 16:01:16 +0200446/* This function executes the "capture" action. It executes a fetch expression,
447 * turns the result into a string and puts it in a capture slot. It always
448 * returns 1. If an error occurs the action is cancelled, but the rule
449 * processing continues.
450 */
451static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
452 struct session *sess, struct stream *s, int flags)
453{
454 struct sample *key;
455 struct cap_hdr *h = rule->arg.cap.hdr;
456 char **cap = s->req_cap;
457 int len;
458
459 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
460 if (!key)
461 return ACT_RET_CONT;
462
463 if (cap[h->index] == NULL)
464 cap[h->index] = pool_alloc(h->pool);
465
466 if (cap[h->index] == NULL) /* no more capture memory */
467 return ACT_RET_CONT;
468
469 len = key->data.u.str.data;
470 if (len > h->len)
471 len = h->len;
472
473 memcpy(cap[h->index], key->data.u.str.area, len);
474 cap[h->index][len] = 0;
475 return ACT_RET_CONT;
476}
477
478/* This function executes the "capture" action and store the result in a
479 * capture slot if exists. It executes a fetch expression, turns the result
480 * into a string and puts it in a capture slot. It always returns 1. If an
481 * error occurs the action is cancelled, but the rule processing continues.
482 */
483static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
484 struct session *sess, struct stream *s, int flags)
485{
486 struct sample *key;
487 struct cap_hdr *h;
488 char **cap = s->req_cap;
489 struct proxy *fe = strm_fe(s);
490 int len;
491 int i;
492
493 /* Look for the original configuration. */
494 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
495 h != NULL && i != rule->arg.capid.idx ;
496 i--, h = h->next);
497 if (!h)
498 return ACT_RET_CONT;
499
500 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
501 if (!key)
502 return ACT_RET_CONT;
503
504 if (cap[h->index] == NULL)
505 cap[h->index] = pool_alloc(h->pool);
506
507 if (cap[h->index] == NULL) /* no more capture memory */
508 return ACT_RET_CONT;
509
510 len = key->data.u.str.data;
511 if (len > h->len)
512 len = h->len;
513
514 memcpy(cap[h->index], key->data.u.str.area, len);
515 cap[h->index][len] = 0;
516 return ACT_RET_CONT;
517}
518
519/* Check an "http-request capture" action.
520 *
521 * The function returns 1 in success case, otherwise, it returns 0 and err is
522 * filled.
523 */
524static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
525{
526 if (rule->action_ptr != http_action_req_capture_by_id)
527 return 1;
528
Baptiste Assmann19a69b32020-01-16 14:34:22 +0100529 /* capture slots can only be declared in frontends, so we can't check their
530 * existence in backends at configuration parsing step
531 */
532 if (px->cap & PR_CAP_FE && rule->arg.capid.idx >= px->nb_req_cap) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200533 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
534 rule->arg.capid.idx);
535 return 0;
536 }
537
538 return 1;
539}
540
Christopher Faulet2eb53962020-01-14 14:47:34 +0100541/* Release memory allocate by an http capture action */
542static void release_http_capture(struct act_rule *rule)
543{
544 if (rule->action_ptr == http_action_req_capture)
545 release_sample_expr(rule->arg.cap.expr);
546 else
547 release_sample_expr(rule->arg.capid.expr);
548}
549
Willy Tarreau79e57332018-10-02 16:01:16 +0200550/* parse an "http-request capture" action. It takes a single argument which is
551 * a sample fetch expression. It stores the expression into arg->act.p[0] and
552 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
553 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
554 */
555static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
556 struct act_rule *rule, char **err)
557{
558 struct sample_expr *expr;
559 struct cap_hdr *hdr;
560 int cur_arg;
561 int len = 0;
562
563 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
564 if (strcmp(args[cur_arg], "if") == 0 ||
565 strcmp(args[cur_arg], "unless") == 0)
566 break;
567
568 if (cur_arg < *orig_arg + 3) {
569 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
570 return ACT_RET_PRS_ERR;
571 }
572
573 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100574 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 +0200575 if (!expr)
576 return ACT_RET_PRS_ERR;
577
578 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
579 memprintf(err,
580 "fetch method '%s' extracts information from '%s', none of which is available here",
581 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100582 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200583 return ACT_RET_PRS_ERR;
584 }
585
586 if (!args[cur_arg] || !*args[cur_arg]) {
587 memprintf(err, "expects 'len or 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100588 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200589 return ACT_RET_PRS_ERR;
590 }
591
592 if (strcmp(args[cur_arg], "len") == 0) {
593 cur_arg++;
594
595 if (!(px->cap & PR_CAP_FE)) {
596 memprintf(err, "proxy '%s' has no frontend capability", px->id);
Christopher Faulet1337b322020-01-14 14:50:55 +0100597 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200598 return ACT_RET_PRS_ERR;
599 }
600
601 px->conf.args.ctx = ARGC_CAP;
602
603 if (!args[cur_arg]) {
604 memprintf(err, "missing length value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100605 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200606 return ACT_RET_PRS_ERR;
607 }
608 /* we copy the table name for now, it will be resolved later */
609 len = atoi(args[cur_arg]);
610 if (len <= 0) {
611 memprintf(err, "length must be > 0");
Christopher Faulet1337b322020-01-14 14:50:55 +0100612 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200613 return ACT_RET_PRS_ERR;
614 }
615 cur_arg++;
616
Willy Tarreau79e57332018-10-02 16:01:16 +0200617 hdr = calloc(1, sizeof(*hdr));
618 hdr->next = px->req_cap;
619 hdr->name = NULL; /* not a header capture */
620 hdr->namelen = 0;
621 hdr->len = len;
622 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
623 hdr->index = px->nb_req_cap++;
624
625 px->req_cap = hdr;
626 px->to_log |= LW_REQHDR;
627
628 rule->action = ACT_CUSTOM;
629 rule->action_ptr = http_action_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100630 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200631 rule->arg.cap.expr = expr;
632 rule->arg.cap.hdr = hdr;
633 }
634
635 else if (strcmp(args[cur_arg], "id") == 0) {
636 int id;
637 char *error;
638
639 cur_arg++;
640
641 if (!args[cur_arg]) {
642 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100643 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200644 return ACT_RET_PRS_ERR;
645 }
646
647 id = strtol(args[cur_arg], &error, 10);
648 if (*error != '\0') {
649 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100650 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200651 return ACT_RET_PRS_ERR;
652 }
653 cur_arg++;
654
655 px->conf.args.ctx = ARGC_CAP;
656
657 rule->action = ACT_CUSTOM;
658 rule->action_ptr = http_action_req_capture_by_id;
659 rule->check_ptr = check_http_req_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100660 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200661 rule->arg.capid.expr = expr;
662 rule->arg.capid.idx = id;
663 }
664
665 else {
666 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100667 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200668 return ACT_RET_PRS_ERR;
669 }
670
671 *orig_arg = cur_arg;
672 return ACT_RET_PRS_OK;
673}
674
675/* This function executes the "capture" action and store the result in a
676 * capture slot if exists. It executes a fetch expression, turns the result
677 * into a string and puts it in a capture slot. It always returns 1. If an
678 * error occurs the action is cancelled, but the rule processing continues.
679 */
680static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
681 struct session *sess, struct stream *s, int flags)
682{
683 struct sample *key;
684 struct cap_hdr *h;
685 char **cap = s->res_cap;
686 struct proxy *fe = strm_fe(s);
687 int len;
688 int i;
689
690 /* Look for the original configuration. */
691 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
692 h != NULL && i != rule->arg.capid.idx ;
693 i--, h = h->next);
694 if (!h)
695 return ACT_RET_CONT;
696
697 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
698 if (!key)
699 return ACT_RET_CONT;
700
701 if (cap[h->index] == NULL)
702 cap[h->index] = pool_alloc(h->pool);
703
704 if (cap[h->index] == NULL) /* no more capture memory */
705 return ACT_RET_CONT;
706
707 len = key->data.u.str.data;
708 if (len > h->len)
709 len = h->len;
710
711 memcpy(cap[h->index], key->data.u.str.area, len);
712 cap[h->index][len] = 0;
713 return ACT_RET_CONT;
714}
715
716/* Check an "http-response capture" action.
717 *
718 * The function returns 1 in success case, otherwise, it returns 0 and err is
719 * filled.
720 */
721static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
722{
723 if (rule->action_ptr != http_action_res_capture_by_id)
724 return 1;
725
726 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
727 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
728 rule->arg.capid.idx);
729 return 0;
730 }
731
732 return 1;
733}
734
735/* parse an "http-response capture" action. It takes a single argument which is
736 * a sample fetch expression. It stores the expression into arg->act.p[0] and
737 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
738 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
739 */
740static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
741 struct act_rule *rule, char **err)
742{
743 struct sample_expr *expr;
744 int cur_arg;
745 int id;
746 char *error;
747
748 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
749 if (strcmp(args[cur_arg], "if") == 0 ||
750 strcmp(args[cur_arg], "unless") == 0)
751 break;
752
753 if (cur_arg < *orig_arg + 3) {
754 memprintf(err, "expects <expression> id <idx>");
755 return ACT_RET_PRS_ERR;
756 }
757
758 cur_arg = *orig_arg;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100759 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 +0200760 if (!expr)
761 return ACT_RET_PRS_ERR;
762
763 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
764 memprintf(err,
765 "fetch method '%s' extracts information from '%s', none of which is available here",
766 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +0100767 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200768 return ACT_RET_PRS_ERR;
769 }
770
771 if (!args[cur_arg] || !*args[cur_arg]) {
772 memprintf(err, "expects 'id'");
Christopher Faulet1337b322020-01-14 14:50:55 +0100773 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200774 return ACT_RET_PRS_ERR;
775 }
776
777 if (strcmp(args[cur_arg], "id") != 0) {
778 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100779 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200780 return ACT_RET_PRS_ERR;
781 }
782
783 cur_arg++;
784
785 if (!args[cur_arg]) {
786 memprintf(err, "missing id value");
Christopher Faulet1337b322020-01-14 14:50:55 +0100787 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200788 return ACT_RET_PRS_ERR;
789 }
790
791 id = strtol(args[cur_arg], &error, 10);
792 if (*error != '\0') {
793 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
Christopher Faulet1337b322020-01-14 14:50:55 +0100794 release_sample_expr(expr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200795 return ACT_RET_PRS_ERR;
796 }
797 cur_arg++;
798
799 px->conf.args.ctx = ARGC_CAP;
800
801 rule->action = ACT_CUSTOM;
802 rule->action_ptr = http_action_res_capture_by_id;
803 rule->check_ptr = check_http_res_capture;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100804 rule->release_ptr = release_http_capture;
Willy Tarreau79e57332018-10-02 16:01:16 +0200805 rule->arg.capid.expr = expr;
806 rule->arg.capid.idx = id;
807
808 *orig_arg = cur_arg;
809 return ACT_RET_PRS_OK;
810}
811
Christopher Faulet81e20172019-12-12 16:40:30 +0100812/* Parse a "allow" action for a request or a response rule. It takes no argument. It
813 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
814 */
815static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
816 struct act_rule *rule, char **err)
817{
818 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100819 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet81e20172019-12-12 16:40:30 +0100820 return ACT_RET_PRS_OK;
821}
822
Christopher Faulete0fca292020-01-13 21:49:03 +0100823/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200824 * response rule. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on
825 * error. It relies on http_parse_http_reply() to set
826 * <.arg.http_reply>.
Christopher Faulet81e20172019-12-12 16:40:30 +0100827 */
Christopher Faulete0fca292020-01-13 21:49:03 +0100828static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
829 struct act_rule *rule, char **err)
Christopher Faulet81e20172019-12-12 16:40:30 +0100830{
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200831 int default_status;
832 int cur_arg, arg = 0;
Christopher Faulet81e20172019-12-12 16:40:30 +0100833
834 cur_arg = *orig_arg;
Christopher Faulete0fca292020-01-13 21:49:03 +0100835 if (rule->from == ACT_F_HTTP_REQ) {
836 if (!strcmp(args[cur_arg-1], "tarpit")) {
837 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200838 default_status = 500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100839 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100840 else {
841 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200842 default_status = 403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100843 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100844 }
Christopher Faulete0fca292020-01-13 21:49:03 +0100845 else {
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100846 rule->action = ACT_ACTION_DENY;
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200847 default_status = 502;
Christopher Faulete0fca292020-01-13 21:49:03 +0100848 }
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100849
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200850 /* If no args or only a deny_status specified, fallback on the legacy
851 * mode and use default error files despite the fact that
852 * default-errorfiles is not used. Otherwise, parse an http reply.
853 */
Christopher Faulet040c8cd2020-01-13 16:43:45 +0100854
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200855 /* Prepare parsing of log-format strings */
856 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100857
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200858 if (!*(args[cur_arg])) {
859 rule->arg.http_reply = http_parse_http_reply((const char *[]){"default-errorfiles", ""}, &arg, px, default_status, err);
860 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100861 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200862
863 if (strcmp(args[cur_arg], "deny_status") == 0) {
864 if (!*(args[cur_arg+2]) ||
865 (strcmp(args[cur_arg+2], "errorfile") != 0 && strcmp(args[cur_arg+2], "errorfiles") != 0)) {
866 rule->arg.http_reply = http_parse_http_reply((const char *[]){"status", args[cur_arg+1], "default-errorfiles", ""},
867 &arg, px, default_status, err);
868 *orig_arg += 2;
869 goto end;
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100870 }
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200871 args[cur_arg] += 5; /* skip "deny_" for the parsing */
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100872 }
873
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200874 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, default_status, err);
Christopher Faulet554c0eb2020-01-14 12:00:28 +0100875
Christopher Faulet5cb513a2020-05-13 17:56:56 +0200876 end:
877 if (!rule->arg.http_reply)
878 return ACT_RET_PRS_ERR;
879
880 rule->flags |= ACT_FLAG_FINAL;
881 rule->check_ptr = check_act_http_reply;
882 rule->release_ptr = release_act_http_reply;
Christopher Faulet81e20172019-12-12 16:40:30 +0100883 return ACT_RET_PRS_OK;
884}
885
Christopher Fauletb3048832020-05-27 15:26:43 +0200886
887/* This function executes a auth action. It builds an 401/407 HTX message using
888 * the corresponding proxy's error message. On success, it returns
889 * ACT_RET_ABRT. If an error occurs ACT_RET_ERR is returned.
890 */
891static enum act_return http_action_auth(struct act_rule *rule, struct proxy *px,
892 struct session *sess, struct stream *s, int flags)
893{
894 struct channel *req = &s->req;
895 struct channel *res = &s->res;
896 struct htx *htx = htx_from_buf(&res->buf);
897 struct http_reply *reply;
898 const char *auth_realm;
899 struct http_hdr_ctx ctx;
900 struct ist hdr;
901
902 /* Auth might be performed on regular http-req rules as well as on stats */
903 auth_realm = rule->arg.http.str.ptr;
904 if (!auth_realm) {
905 if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules)
906 auth_realm = STATS_DEFAULT_REALM;
907 else
908 auth_realm = px->id;
909 }
910
911 if (!(s->txn->flags & TX_USE_PX_CONN)) {
912 s->txn->status = 401;
913 hdr = ist("WWW-Authenticate");
914 }
915 else {
916 s->txn->status = 407;
917 hdr = ist("Proxy-Authenticate");
918 }
919 reply = http_error_message(s);
920 channel_htx_truncate(res, htx);
921
922 if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
923 goto fail;
924
925 /* Write the generic 40x message */
926 if (http_reply_to_htx(s, htx, reply) == -1)
927 goto fail;
928
929 /* Remove all existing occurrences of the XXX-Authenticate header */
930 ctx.blk = NULL;
931 while (http_find_header(htx, hdr, &ctx, 1))
932 http_remove_header(htx, &ctx);
933
934 /* Now a the right XXX-Authenticate header */
935 if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
936 goto fail;
937
938 /* Finally forward the reply */
939 htx_to_buf(htx, &res->buf);
940 if (!http_forward_proxy_resp(s, 1))
941 goto fail;
942
943 /* Note: Only eval on the request */
944 s->logs.tv_request = now;
945 req->analysers &= AN_REQ_FLT_END;
946
947 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
948 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
949
950 if (!(s->flags & SF_ERR_MASK))
951 s->flags |= SF_ERR_LOCAL;
952 if (!(s->flags & SF_FINST_MASK))
953 s->flags |= SF_FINST_R;
954
955 stream_inc_http_err_ctr(s);
956 return ACT_RET_ABRT;
957
958 fail:
959 /* If an error occurred, remove the incomplete HTTP response from the
960 * buffer */
961 channel_htx_truncate(res, htx);
962 return ACT_RET_ERR;
963}
964
Christopher Faulet81e20172019-12-12 16:40:30 +0100965/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
966 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
967 */
968static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
969 struct act_rule *rule, char **err)
970{
971 int cur_arg;
972
Christopher Fauletb3048832020-05-27 15:26:43 +0200973 rule->action = ACT_CUSTOM;
Christopher Faulet245cf792019-12-18 14:58:12 +0100974 rule->flags |= ACT_FLAG_FINAL;
Christopher Fauletb3048832020-05-27 15:26:43 +0200975 rule->action_ptr = http_action_auth;
Christopher Faulet2eb53962020-01-14 14:47:34 +0100976 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +0100977
978 cur_arg = *orig_arg;
979 if (!strcmp(args[cur_arg], "realm")) {
980 cur_arg++;
981 if (!*args[cur_arg]) {
982 memprintf(err, "missing realm value.\n");
983 return ACT_RET_PRS_ERR;
984 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100985 rule->arg.http.str.ptr = strdup(args[cur_arg]);
986 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100987 cur_arg++;
988 }
989
Christopher Fauletc20b3712020-01-27 15:51:56 +0100990 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100991 *orig_arg = cur_arg;
992 return ACT_RET_PRS_OK;
993}
994
995/* Parse a "set-nice" action. It takes the nice value as argument. It returns
996 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
997 */
998static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
999 struct act_rule *rule, char **err)
1000{
1001 int cur_arg;
1002
1003 rule->action = ACT_HTTP_SET_NICE;
1004
1005 cur_arg = *orig_arg;
1006 if (!*args[cur_arg]) {
1007 memprintf(err, "expects exactly 1 argument (integer value)");
1008 return ACT_RET_PRS_ERR;
1009 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001010 rule->arg.http.i = atoi(args[cur_arg]);
1011 if (rule->arg.http.i < -1024)
1012 rule->arg.http.i = -1024;
1013 else if (rule->arg.http.i > 1024)
1014 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +01001015
Christopher Fauletc20b3712020-01-27 15:51:56 +01001016 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001017 *orig_arg = cur_arg + 1;
1018 return ACT_RET_PRS_OK;
1019}
1020
1021/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
1022 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1023 */
1024static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
1025 struct act_rule *rule, char **err)
1026{
1027#ifdef IP_TOS
1028 char *endp;
1029 int cur_arg;
1030
1031 rule->action = ACT_HTTP_SET_TOS;
1032
1033 cur_arg = *orig_arg;
1034 if (!*args[cur_arg]) {
1035 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1036 return ACT_RET_PRS_ERR;
1037 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001038 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001039 if (endp && *endp != '\0') {
1040 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1041 return ACT_RET_PRS_ERR;
1042 }
1043
Christopher Fauletc20b3712020-01-27 15:51:56 +01001044 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001045 *orig_arg = cur_arg + 1;
1046 return ACT_RET_PRS_OK;
1047#else
1048 memprintf(err, "not supported on this platform (IP_TOS undefined)");
1049 return ACT_RET_PRS_ERR;
1050#endif
1051}
1052
1053/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
1054 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1055 */
1056static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
1057 struct act_rule *rule, char **err)
1058{
1059#ifdef SO_MARK
1060 char *endp;
1061 int cur_arg;
1062
1063 rule->action = ACT_HTTP_SET_MARK;
1064
1065 cur_arg = *orig_arg;
1066 if (!*args[cur_arg]) {
1067 memprintf(err, "expects exactly 1 argument (integer/hex value)");
1068 return ACT_RET_PRS_ERR;
1069 }
Christopher Faulet96bff762019-12-17 13:46:18 +01001070 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +01001071 if (endp && *endp != '\0') {
1072 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
1073 return ACT_RET_PRS_ERR;
1074 }
1075
Christopher Fauletc20b3712020-01-27 15:51:56 +01001076 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001077 *orig_arg = cur_arg + 1;
1078 global.last_checks |= LSTCHK_NETADM;
1079 return ACT_RET_PRS_OK;
1080#else
1081 memprintf(err, "not supported on this platform (SO_MARK undefined)");
1082 return ACT_RET_PRS_ERR;
1083#endif
1084}
1085
1086/* Parse a "set-log-level" action. It takes the level value as argument. It
1087 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1088 */
1089static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
1090 struct act_rule *rule, char **err)
1091{
1092 int cur_arg;
1093
1094 rule->action = ACT_HTTP_SET_LOGL;
1095
1096 cur_arg = *orig_arg;
1097 if (!*args[cur_arg]) {
1098 bad_log_level:
1099 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
1100 return ACT_RET_PRS_ERR;
1101 }
1102 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +01001103 rule->arg.http.i = -1;
1104 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +01001105 goto bad_log_level;
1106
Christopher Fauletc20b3712020-01-27 15:51:56 +01001107 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001108 *orig_arg = cur_arg + 1;
1109 return ACT_RET_PRS_OK;
1110}
1111
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001112/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
1113 * 103 response header with <.arg.http.str> name and with a value built
1114 * according to <.arg.http.fmt> log line format. If it is the first early-hint
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001115 * rule of series, the 103 response start-line is added first. At the end, if
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001116 * the next rule is not an early-hint rule or if it is the last rule, the EOH
1117 * block is added to terminate the response. On success, it returns
1118 * ACT_RET_CONT. If an error occurs while soft rewrites are enabled, the action
1119 * is canceled, but the rule processing continue. Otherwsize ACT_RET_ERR is
1120 * returned.
1121 */
1122static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
1123 struct session *sess, struct stream *s, int flags)
1124{
1125 struct act_rule *prev_rule, *next_rule;
1126 struct channel *res = &s->res;
1127 struct htx *htx = htx_from_buf(&res->buf);
1128 struct buffer *value = alloc_trash_chunk();
1129 enum act_return ret = ACT_RET_CONT;
1130
1131 if (!(s->txn->req.flags & HTTP_MSGF_VER_11))
1132 goto leave;
1133
1134 if (!value) {
1135 if (!(s->flags & SF_ERR_MASK))
1136 s->flags |= SF_ERR_RESOURCE;
1137 goto error;
1138 }
1139
1140 /* get previous and next rules */
1141 prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
1142 next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
1143
1144 /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
1145 * continue to add link to a previously started response */
1146 if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
1147 struct htx_sl *sl;
1148 unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
1149 HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
1150
1151 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
1152 ist("HTTP/1.1"), ist("103"), ist("Early Hints"));
1153 if (!sl)
1154 goto error;
1155 sl->info.res.status = 103;
1156 }
1157
1158 /* Add the HTTP Early Hint HTTP 103 response heade */
1159 value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt);
1160 if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
1161 goto error;
1162
1163 /* if it is the last rule or the next one is not an early-hint, terminate the current
1164 * response. */
1165 if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001166 if (!htx_add_endof(htx, HTX_BLK_EOH)) {
1167 /* If an error occurred during an Early-hint rule,
1168 * remove the incomplete HTTP 103 response from the
1169 * buffer */
1170 goto error;
1171 }
1172
Christopher Fauleta72a7e42020-01-28 09:28:11 +01001173 if (!http_forward_proxy_resp(s, 0))
1174 goto error;
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001175 }
1176
1177 leave:
1178 free_trash_chunk(value);
1179 return ret;
1180
1181 error:
1182 /* If an error occurred during an Early-hint rule, remove the incomplete
1183 * HTTP 103 response from the buffer */
1184 channel_htx_truncate(res, htx);
1185 ret = ACT_RET_ERR;
1186 goto leave;
1187}
1188
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001189/* This function executes a set-header or add-header actions. It builds a string
1190 * in the trash from the specified format string. It finds the action to be
1191 * performed in <.action>, previously filled by function parse_set_header(). The
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001192 * replacement action is executed by the function http_action_set_header(). On
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001193 * success, it returns ACT_RET_CONT. If an error occurs while soft rewrites are
1194 * enabled, the action is canceled, but the rule processing continue. Otherwsize
1195 * ACT_RET_ERR is returned.
1196 */
1197static enum act_return http_action_set_header(struct act_rule *rule, struct proxy *px,
1198 struct session *sess, struct stream *s, int flags)
1199{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001200 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1201 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001202 enum act_return ret = ACT_RET_CONT;
1203 struct buffer *replace;
1204 struct http_hdr_ctx ctx;
1205 struct ist n, v;
1206
1207 replace = alloc_trash_chunk();
1208 if (!replace)
1209 goto fail_alloc;
1210
1211 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1212 n = rule->arg.http.str;
1213 v = ist2(replace->area, replace->data);
1214
1215 if (rule->action == 0) { // set-header
1216 /* remove all occurrences of the header */
1217 ctx.blk = NULL;
1218 while (http_find_header(htx, n, &ctx, 1))
1219 http_remove_header(htx, &ctx);
1220 }
1221
1222 /* Now add header */
1223 if (!http_add_header(htx, n, v))
1224 goto fail_rewrite;
1225
1226 leave:
1227 free_trash_chunk(replace);
1228 return ret;
1229
1230 fail_alloc:
1231 if (!(s->flags & SF_ERR_MASK))
1232 s->flags |= SF_ERR_RESOURCE;
1233 ret = ACT_RET_ERR;
1234 goto leave;
1235
1236 fail_rewrite:
1237 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1238 if (s->flags & SF_BE_ASSIGNED)
1239 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1240 if (sess->listener->counters)
1241 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1242 if (objt_server(s->target))
1243 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1244
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001245 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001246 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001247 if (!(s->flags & SF_ERR_MASK))
1248 s->flags |= SF_ERR_PRXCOND;
1249 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001250 goto leave;
1251}
1252
Christopher Faulet81e20172019-12-12 16:40:30 +01001253/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
1254 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
1255 * on success, ACT_RET_PRS_ERR on error.
1256 *
1257 * Note: same function is used for the request and the response. However
1258 * "early-hint" rules are only supported for request rules.
1259 */
1260static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
1261 struct act_rule *rule, char **err)
1262{
Christopher Faulet81e20172019-12-12 16:40:30 +01001263 int cap, cur_arg;
1264
Christopher Faulet91b3ec12020-01-17 22:30:06 +01001265 if (args[*orig_arg-1][0] == 'e') {
1266 rule->action = ACT_CUSTOM;
1267 rule->action_ptr = http_action_early_hint;
1268 }
Christopher Fauletd1f27e32019-12-17 09:33:38 +01001269 else {
1270 if (args[*orig_arg-1][0] == 's')
1271 rule->action = 0; // set-header
1272 else
1273 rule->action = 1; // add-header
1274 rule->action_ptr = http_action_set_header;
1275 }
Christopher Faulet2eb53962020-01-14 14:47:34 +01001276 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001277
1278 cur_arg = *orig_arg;
1279 if (!*args[cur_arg] || !*args[cur_arg+1]) {
1280 memprintf(err, "expects exactly 2 arguments");
1281 return ACT_RET_PRS_ERR;
1282 }
1283
Christopher Faulet81e20172019-12-12 16:40:30 +01001284
Christopher Faulet96bff762019-12-17 13:46:18 +01001285 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1286 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1287 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001288
1289 if (rule->from == ACT_F_HTTP_REQ) {
1290 px->conf.args.ctx = ARGC_HRQ;
1291 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1292 }
1293 else{
1294 px->conf.args.ctx = ARGC_HRS;
1295 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1296 }
1297
1298 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001299 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001300 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001301 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001302 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001303
1304 free(px->conf.lfs_file);
1305 px->conf.lfs_file = strdup(px->conf.args.file);
1306 px->conf.lfs_line = px->conf.args.line;
1307
1308 *orig_arg = cur_arg + 1;
1309 return ACT_RET_PRS_OK;
1310}
1311
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001312/* This function executes a replace-header or replace-value actions. It
1313 * builds a string in the trash from the specified format string. It finds
1314 * the action to be performed in <.action>, previously filled by function
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001315 * parse_replace_header(). The replacement action is executed by the function
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001316 * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error
1317 * occurs while soft rewrites are enabled, the action is canceled, but the rule
1318 * processing continue. Otherwsize ACT_RET_ERR is returned.
1319 */
1320static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px,
1321 struct session *sess, struct stream *s, int flags)
1322{
Christopher Faulet91e31d82020-01-24 15:37:13 +01001323 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1324 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001325 enum act_return ret = ACT_RET_CONT;
1326 struct buffer *replace;
1327 int r;
1328
1329 replace = alloc_trash_chunk();
1330 if (!replace)
1331 goto fail_alloc;
1332
1333 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
1334
1335 r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0));
1336 if (r == -1)
1337 goto fail_rewrite;
1338
1339 leave:
1340 free_trash_chunk(replace);
1341 return ret;
1342
1343 fail_alloc:
1344 if (!(s->flags & SF_ERR_MASK))
1345 s->flags |= SF_ERR_RESOURCE;
1346 ret = ACT_RET_ERR;
1347 goto leave;
1348
1349 fail_rewrite:
1350 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
1351 if (s->flags & SF_BE_ASSIGNED)
1352 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
1353 if (sess->listener->counters)
1354 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
1355 if (objt_server(s->target))
1356 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
1357
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001358 if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001359 ret = ACT_RET_ERR;
Christopher Faulet333bf8c2020-01-22 14:38:05 +01001360 if (!(s->flags & SF_ERR_MASK))
1361 s->flags |= SF_ERR_PRXCOND;
1362 }
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001363 goto leave;
1364}
1365
Christopher Faulet81e20172019-12-12 16:40:30 +01001366/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
1367 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
1368 * success, ACT_RET_PRS_ERR on error.
1369 */
1370static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
1371 struct act_rule *rule, char **err)
1372{
1373 int cap, cur_arg;
1374
Christopher Faulet92d34fe2019-12-17 09:20:34 +01001375 if (args[*orig_arg-1][8] == 'h')
1376 rule->action = 0; // replace-header
1377 else
1378 rule->action = 1; // replace-value
1379 rule->action_ptr = http_action_replace_header;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001380 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001381
1382 cur_arg = *orig_arg;
1383 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1384 memprintf(err, "expects exactly 3 arguments");
1385 return ACT_RET_PRS_ERR;
1386 }
1387
Christopher Faulet96bff762019-12-17 13:46:18 +01001388 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1389 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1390 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001391
1392 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001393 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err))) {
Tim Duesterhused526372020-03-05 17:56:33 +01001394 istfree(&rule->arg.http.str);
Christopher Faulet81e20172019-12-12 16:40:30 +01001395 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001396 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001397
1398 if (rule->from == ACT_F_HTTP_REQ) {
1399 px->conf.args.ctx = ARGC_HRQ;
1400 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1401 }
1402 else{
1403 px->conf.args.ctx = ARGC_HRS;
1404 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1405 }
1406
1407 cur_arg++;
Christopher Faulet1337b322020-01-14 14:50:55 +01001408 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err)) {
Tim Duesterhused526372020-03-05 17:56:33 +01001409 istfree(&rule->arg.http.str);
Christopher Faulet1337b322020-01-14 14:50:55 +01001410 regex_free(rule->arg.http.re);
Christopher Faulet81e20172019-12-12 16:40:30 +01001411 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001412 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001413
1414 free(px->conf.lfs_file);
1415 px->conf.lfs_file = strdup(px->conf.args.file);
1416 px->conf.lfs_line = px->conf.args.line;
1417
1418 *orig_arg = cur_arg + 1;
1419 return ACT_RET_PRS_OK;
1420}
1421
1422/* Parse a "del-header" action. It takes an header name as argument. It returns
1423 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1424 */
1425static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1426 struct act_rule *rule, char **err)
1427{
1428 int cur_arg;
1429
1430 rule->action = ACT_HTTP_DEL_HDR;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001431 rule->release_ptr = release_http_action;
Christopher Faulet81e20172019-12-12 16:40:30 +01001432
1433 cur_arg = *orig_arg;
1434 if (!*args[cur_arg]) {
1435 memprintf(err, "expects exactly 1 arguments");
1436 return ACT_RET_PRS_ERR;
1437 }
1438
Christopher Faulet96bff762019-12-17 13:46:18 +01001439 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1440 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001441 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1442
Christopher Fauletc20b3712020-01-27 15:51:56 +01001443 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001444 *orig_arg = cur_arg + 1;
1445 return ACT_RET_PRS_OK;
1446}
1447
Christopher Faulet2eb53962020-01-14 14:47:34 +01001448/* Release memory allocated by an http redirect action. */
1449static void release_http_redir(struct act_rule *rule)
1450{
1451 struct logformat_node *lf, *lfb;
1452 struct redirect_rule *redir;
1453
1454 redir = rule->arg.redir;
1455 LIST_DEL(&redir->list);
1456 if (redir->cond) {
1457 prune_acl_cond(redir->cond);
1458 free(redir->cond);
1459 }
1460 free(redir->rdr_str);
1461 free(redir->cookie_str);
1462 list_for_each_entry_safe(lf, lfb, &redir->rdr_fmt, list) {
1463 LIST_DEL(&lf->list);
1464 free(lf);
1465 }
1466 free(redir);
1467}
1468
Christopher Faulet81e20172019-12-12 16:40:30 +01001469/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1470 * ACT_RET_PRS_ERR on error.
1471 */
1472static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1473 struct act_rule *rule, char **err)
1474{
1475 struct redirect_rule *redir;
1476 int dir, cur_arg;
1477
1478 rule->action = ACT_HTTP_REDIR;
Christopher Faulet245cf792019-12-18 14:58:12 +01001479 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001480 rule->release_ptr = release_http_redir;
Christopher Faulet81e20172019-12-12 16:40:30 +01001481
1482 cur_arg = *orig_arg;
1483
1484 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1485 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1486 return ACT_RET_PRS_ERR;
1487
1488 rule->arg.redir = redir;
1489 rule->cond = redir->cond;
1490 redir->cond = NULL;
1491
1492 /* skip all arguments */
1493 while (*args[cur_arg])
1494 cur_arg++;
1495
1496 *orig_arg = cur_arg;
1497 return ACT_RET_PRS_OK;
1498}
1499
Christopher Faulet046cf442019-12-17 15:45:23 +01001500/* This function executes a add-acl, del-acl, set-map or del-map actions. On
1501 * success, it returns ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1502 */
1503static enum act_return http_action_set_map(struct act_rule *rule, struct proxy *px,
1504 struct session *sess, struct stream *s, int flags)
1505{
1506 struct pat_ref *ref;
1507 struct buffer *key = NULL, *value = NULL;
1508 enum act_return ret = ACT_RET_CONT;
1509
1510 /* collect reference */
1511 ref = pat_ref_lookup(rule->arg.map.ref);
1512 if (!ref)
1513 goto leave;
1514
1515 /* allocate key */
1516 key = alloc_trash_chunk();
1517 if (!key)
1518 goto fail_alloc;
1519
1520 /* collect key */
1521 key->data = build_logline(s, key->area, key->size, &rule->arg.map.key);
1522 key->area[key->data] = '\0';
1523
1524 switch (rule->action) {
1525 case 0: // add-acl
1526 /* add entry only if it does not already exist */
1527 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1528 if (pat_ref_find_elt(ref, key->area) == NULL)
1529 pat_ref_add(ref, key->area, NULL, NULL);
1530 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1531 break;
1532
1533 case 1: // set-map
1534 /* allocate value */
1535 value = alloc_trash_chunk();
1536 if (!value)
1537 goto fail_alloc;
1538
1539 /* collect value */
1540 value->data = build_logline(s, value->area, value->size, &rule->arg.map.value);
1541 value->area[value->data] = '\0';
1542
1543 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1544 if (pat_ref_find_elt(ref, key->area) != NULL) {
1545 /* update entry if it exists */
1546 pat_ref_set(ref, key->area, value->area, NULL);
1547 }
1548 else {
1549 /* insert a new entry */
1550 pat_ref_add(ref, key->area, value->area, NULL);
1551 }
1552 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1553 break;
1554
1555 case 2: // del-acl
1556 case 3: // del-map
1557 /* returned code: 1=ok, 0=ko */
1558 HA_SPIN_LOCK(PATREF_LOCK, &ref->lock);
1559 pat_ref_delete(ref, key->area);
1560 HA_SPIN_UNLOCK(PATREF_LOCK, &ref->lock);
1561 break;
1562
1563 default:
1564 ret = ACT_RET_ERR;
1565 }
1566
1567
1568 leave:
1569 free_trash_chunk(key);
1570 free_trash_chunk(value);
1571 return ret;
1572
1573 fail_alloc:
1574 if (!(s->flags & SF_ERR_MASK))
1575 s->flags |= SF_ERR_RESOURCE;
1576 ret = ACT_RET_ERR;
1577 goto leave;
1578}
1579
Christopher Faulet2eb53962020-01-14 14:47:34 +01001580/* Release memory allocated by an http map/acl action. */
1581static void release_http_map(struct act_rule *rule)
1582{
1583 struct logformat_node *lf, *lfb;
1584
1585 free(rule->arg.map.ref);
1586 list_for_each_entry_safe(lf, lfb, &rule->arg.map.key, list) {
1587 LIST_DEL(&lf->list);
1588 release_sample_expr(lf->expr);
1589 free(lf->arg);
1590 free(lf);
1591 }
1592 if (rule->action == 1) {
1593 list_for_each_entry_safe(lf, lfb, &rule->arg.map.value, list) {
1594 LIST_DEL(&lf->list);
1595 release_sample_expr(lf->expr);
1596 free(lf->arg);
1597 free(lf);
1598 }
1599 }
1600}
1601
Christopher Faulet81e20172019-12-12 16:40:30 +01001602/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
Christopher Faulet046cf442019-12-17 15:45:23 +01001603 * two log-format string as argument depending on the action. The action is
1604 * stored in <.action> as an int (0=add-acl, 1=set-map, 2=del-acl,
1605 * 3=del-map). It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
Christopher Faulet81e20172019-12-12 16:40:30 +01001606 */
1607static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1608 struct act_rule *rule, char **err)
1609{
1610 int cap, cur_arg;
1611
Christopher Faulet046cf442019-12-17 15:45:23 +01001612 if (args[*orig_arg-1][0] == 'a') // add-acl
1613 rule->action = 0;
1614 else if (args[*orig_arg-1][0] == 's') // set-map
1615 rule->action = 1;
1616 else if (args[*orig_arg-1][4] == 'a') // del-acl
1617 rule->action = 2;
1618 else if (args[*orig_arg-1][4] == 'm') // del-map
1619 rule->action = 3;
1620 else {
1621 memprintf(err, "internal error: unhandled action '%s'", args[0]);
1622 return ACT_RET_PRS_ERR;
1623 }
1624 rule->action_ptr = http_action_set_map;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001625 rule->release_ptr = release_http_map;
Christopher Faulet81e20172019-12-12 16:40:30 +01001626
1627 cur_arg = *orig_arg;
Christopher Faulet046cf442019-12-17 15:45:23 +01001628 if (rule->action == 1 && (!*args[cur_arg] || !*args[cur_arg+1])) {
1629 /* 2 args for set-map */
Christopher Faulet81e20172019-12-12 16:40:30 +01001630 memprintf(err, "expects exactly 2 arguments");
1631 return ACT_RET_PRS_ERR;
1632 }
1633 else if (!*args[cur_arg]) {
Christopher Faulet046cf442019-12-17 15:45:23 +01001634 /* only one arg for other actions */
Christopher Faulet81e20172019-12-12 16:40:30 +01001635 memprintf(err, "expects exactly 1 arguments");
1636 return ACT_RET_PRS_ERR;
1637 }
1638
1639 /*
1640 * '+ 8' for 'set-map(' (same for del-map)
1641 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1642 */
1643 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1644
1645 if (rule->from == ACT_F_HTTP_REQ) {
1646 px->conf.args.ctx = ARGC_HRQ;
1647 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1648 }
1649 else{
1650 px->conf.args.ctx = ARGC_HRS;
1651 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1652 }
1653
1654 /* key pattern */
1655 LIST_INIT(&rule->arg.map.key);
Christopher Faulet1337b322020-01-14 14:50:55 +01001656 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err)) {
1657 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001658 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001659 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001660
Christopher Faulet046cf442019-12-17 15:45:23 +01001661 if (rule->action == 1) {
Christopher Faulet81e20172019-12-12 16:40:30 +01001662 /* value pattern for set-map only */
1663 cur_arg++;
1664 LIST_INIT(&rule->arg.map.value);
Christopher Faulet1337b322020-01-14 14:50:55 +01001665 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err)) {
1666 free(rule->arg.map.ref);
Christopher Faulet81e20172019-12-12 16:40:30 +01001667 return ACT_RET_PRS_ERR;
Christopher Faulet1337b322020-01-14 14:50:55 +01001668 }
Christopher Faulet81e20172019-12-12 16:40:30 +01001669 }
1670
1671 free(px->conf.lfs_file);
1672 px->conf.lfs_file = strdup(px->conf.args.file);
1673 px->conf.lfs_line = px->conf.args.line;
1674
1675 *orig_arg = cur_arg + 1;
1676 return ACT_RET_PRS_OK;
1677}
1678
Christopher Fauletac98d812019-12-18 09:20:16 +01001679/* This function executes a track-sc* actions. On success, it returns
1680 * ACT_RET_CONT. Otherwsize ACT_RET_ERR is returned.
1681 */
1682static enum act_return http_action_track_sc(struct act_rule *rule, struct proxy *px,
1683 struct session *sess, struct stream *s, int flags)
1684{
1685 struct stktable *t;
1686 struct stksess *ts;
1687 struct stktable_key *key;
1688 void *ptr1, *ptr2, *ptr3, *ptr4;
1689 int opt;
1690
1691 ptr1 = ptr2 = ptr3 = ptr4 = NULL;
1692 opt = ((rule->from == ACT_F_HTTP_REQ) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES) | SMP_OPT_FINAL;
1693
1694 t = rule->arg.trk_ctr.table.t;
1695 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, NULL);
1696
1697 if (!key)
1698 goto end;
1699 ts = stktable_get_entry(t, key);
1700 if (!ts)
1701 goto end;
1702
1703 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
1704
1705 /* let's count a new HTTP request as it's the first time we do it */
1706 ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
1707 ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
1708
1709 /* When the client triggers a 4xx from the server, it's most often due
1710 * to a missing object or permission. These events should be tracked
1711 * because if they happen often, it may indicate a brute force or a
1712 * vulnerability scan. Normally this is done when receiving the response
1713 * but here we're tracking after this ought to have been done so we have
1714 * to do it on purpose.
1715 */
1716 if (rule->from == ACT_F_HTTP_RES && (unsigned)(s->txn->status - 400) < 100) {
1717 ptr3 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_CNT);
1718 ptr4 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_ERR_RATE);
1719 }
1720
1721 if (ptr1 || ptr2 || ptr3 || ptr4) {
1722 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
1723
1724 if (ptr1)
1725 stktable_data_cast(ptr1, http_req_cnt)++;
1726 if (ptr2)
1727 update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
1728 t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
1729 if (ptr3)
1730 stktable_data_cast(ptr3, http_err_cnt)++;
1731 if (ptr4)
1732 update_freq_ctr_period(&stktable_data_cast(ptr4, http_err_rate),
1733 t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
1734
1735 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
1736
1737 /* If data was modified, we need to touch to re-schedule sync */
1738 stktable_touch_local(t, ts, 0);
1739 }
1740
1741 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
1742 if (sess->fe != s->be)
1743 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
1744
1745 end:
1746 return ACT_RET_CONT;
1747}
Christopher Faulet81e20172019-12-12 16:40:30 +01001748
Christopher Faulet2eb53962020-01-14 14:47:34 +01001749static void release_http_track_sc(struct act_rule *rule)
1750{
1751 release_sample_expr(rule->arg.trk_ctr.expr);
1752}
1753
Christopher Faulet81e20172019-12-12 16:40:30 +01001754/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1755 * ACT_RET_PRS_ERR on error.
1756 */
1757static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1758 struct act_rule *rule, char **err)
1759{
1760 struct sample_expr *expr;
1761 unsigned int where;
1762 unsigned int tsc_num;
1763 const char *tsc_num_str;
1764 int cur_arg;
1765
1766 tsc_num_str = &args[*orig_arg-1][8];
1767 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1768 return ACT_RET_PRS_ERR;
1769
1770 cur_arg = *orig_arg;
1771 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01001772 err, &px->conf.args, NULL);
Christopher Faulet81e20172019-12-12 16:40:30 +01001773 if (!expr)
1774 return ACT_RET_PRS_ERR;
1775
1776 where = 0;
1777 if (px->cap & PR_CAP_FE)
1778 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1779 if (px->cap & PR_CAP_BE)
1780 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1781
1782 if (!(expr->fetch->val & where)) {
1783 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1784 args[cur_arg-1], sample_src_names(expr->fetch->use));
Christopher Faulet1337b322020-01-14 14:50:55 +01001785 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001786 return ACT_RET_PRS_ERR;
1787 }
1788
1789 if (strcmp(args[cur_arg], "table") == 0) {
1790 cur_arg++;
1791 if (!*args[cur_arg]) {
1792 memprintf(err, "missing table name");
Christopher Faulet1337b322020-01-14 14:50:55 +01001793 release_sample_expr(expr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001794 return ACT_RET_PRS_ERR;
1795 }
1796
1797 /* we copy the table name for now, it will be resolved later */
1798 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1799 cur_arg++;
1800 }
1801
Christopher Fauletac98d812019-12-18 09:20:16 +01001802 rule->action = tsc_num;
Christopher Faulet81e20172019-12-12 16:40:30 +01001803 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +01001804 rule->action_ptr = http_action_track_sc;
Christopher Faulet2eb53962020-01-14 14:47:34 +01001805 rule->release_ptr = release_http_track_sc;
Christopher Faulet81e20172019-12-12 16:40:30 +01001806 rule->check_ptr = check_trk_action;
1807
1808 *orig_arg = cur_arg;
1809 return ACT_RET_PRS_OK;
1810}
1811
Christopher Faulet46f95542019-12-20 10:07:22 +01001812/* This function executes a strict-mode actions. On success, it always returns
1813 * ACT_RET_CONT
1814 */
1815static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1816 struct session *sess, struct stream *s, int flags)
1817{
1818 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1819
1820 if (rule->action == 0) // strict-mode on
1821 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1822 else // strict-mode off
1823 msg->flags |= HTTP_MSGF_SOFT_RW;
1824 return ACT_RET_CONT;
1825}
1826
1827/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1828 * ACT_RET_PRS_ERR on error.
1829 */
1830static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1831 struct act_rule *rule, char **err)
1832{
1833 int cur_arg;
1834
Christopher Faulet46f95542019-12-20 10:07:22 +01001835 cur_arg = *orig_arg;
1836 if (!*args[cur_arg]) {
1837 memprintf(err, "expects exactly 1 arguments");
1838 return ACT_RET_PRS_ERR;
1839 }
1840
1841 if (strcasecmp(args[cur_arg], "on") == 0)
1842 rule->action = 0; // strict-mode on
1843 else if (strcasecmp(args[cur_arg], "off") == 0)
1844 rule->action = 1; // strict-mode off
1845 else {
1846 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1847 return ACT_RET_PRS_ERR;
1848 }
1849 rule->action_ptr = http_action_strict_mode;
1850
1851 *orig_arg = cur_arg + 1;
1852 return ACT_RET_PRS_OK;
1853}
1854
Christopher Faulet24231ab2020-01-24 17:44:23 +01001855/* This function executes a return action. It builds an HTX message from an
1856 * errorfile, an raw file or a log-format string, depending on <.action>
1857 * value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
1858 * returned.
1859 */
1860static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
1861 struct session *sess, struct stream *s, int flags)
1862{
1863 struct channel *req = &s->req;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001864
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001865 if (http_reply_message(s, rule->arg.http_reply) == -1)
1866 return ACT_RET_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001867
Christopher Faulet24231ab2020-01-24 17:44:23 +01001868 if (rule->from == ACT_F_HTTP_REQ) {
1869 /* let's log the request time */
1870 s->logs.tv_request = now;
1871 req->analysers &= AN_REQ_FLT_END;
1872
1873 if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
1874 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
1875 }
1876
1877 if (!(s->flags & SF_ERR_MASK))
1878 s->flags |= SF_ERR_LOCAL;
1879 if (!(s->flags & SF_FINST_MASK))
1880 s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
1881
Christopher Faulet0e2ad612020-05-13 16:38:37 +02001882 return ACT_RET_ABRT;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001883}
1884
Christopher Faulet24231ab2020-01-24 17:44:23 +01001885/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
Christopher Faulet47e791e2020-05-13 14:36:55 +02001886 * ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
1887 * <.arg.http_reply>.
Christopher Faulet24231ab2020-01-24 17:44:23 +01001888 */
1889static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
1890 struct act_rule *rule, char **err)
1891{
Christopher Faulet47e791e2020-05-13 14:36:55 +02001892 /* Prepare parsing of log-format strings */
1893 px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
1894 rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
1895 if (!rule->arg.http_reply)
1896 return ACT_RET_PRS_ERR;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001897
Christopher Fauletba946bf2020-05-13 08:50:07 +02001898 rule->flags |= ACT_FLAG_FINAL;
Christopher Faulet5ff0c642020-05-12 18:33:37 +02001899 rule->action = ACT_CUSTOM;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001900 rule->check_ptr = check_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001901 rule->action_ptr = http_action_return;
Christopher Faulet5cb513a2020-05-13 17:56:56 +02001902 rule->release_ptr = release_act_http_reply;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001903 return ACT_RET_PRS_OK;
Christopher Faulet24231ab2020-01-24 17:44:23 +01001904}
1905
Willy Tarreau79e57332018-10-02 16:01:16 +02001906/************************************************************************/
1907/* All supported http-request action keywords must be declared here. */
1908/************************************************************************/
1909
1910static struct action_kw_list http_req_actions = {
1911 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001912 { "add-acl", parse_http_set_map, 1 },
1913 { "add-header", parse_http_set_header, 0 },
1914 { "allow", parse_http_allow, 0 },
1915 { "auth", parse_http_auth, 0 },
1916 { "capture", parse_http_req_capture, 0 },
1917 { "del-acl", parse_http_set_map, 1 },
1918 { "del-header", parse_http_del_header, 0 },
1919 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001920 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001921 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1922 { "early-hint", parse_http_set_header, 0 },
1923 { "redirect", parse_http_redirect, 0 },
1924 { "reject", parse_http_action_reject, 0 },
1925 { "replace-header", parse_http_replace_header, 0 },
1926 { "replace-path", parse_replace_uri, 0 },
1927 { "replace-uri", parse_replace_uri, 0 },
1928 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001929 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001930 { "set-header", parse_http_set_header, 0 },
1931 { "set-log-level", parse_http_set_log_level, 0 },
1932 { "set-map", parse_http_set_map, 1 },
1933 { "set-method", parse_set_req_line, 0 },
1934 { "set-mark", parse_http_set_mark, 0 },
1935 { "set-nice", parse_http_set_nice, 0 },
1936 { "set-path", parse_set_req_line, 0 },
1937 { "set-query", parse_set_req_line, 0 },
1938 { "set-tos", parse_http_set_tos, 0 },
1939 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001940 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001941 { "tarpit", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001942 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001943 { NULL, NULL }
1944 }
1945};
1946
Willy Tarreau0108d902018-11-25 19:14:37 +01001947INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1948
Willy Tarreau79e57332018-10-02 16:01:16 +02001949static struct action_kw_list http_res_actions = {
1950 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001951 { "add-acl", parse_http_set_map, 1 },
1952 { "add-header", parse_http_set_header, 0 },
1953 { "allow", parse_http_allow, 0 },
1954 { "capture", parse_http_res_capture, 0 },
1955 { "del-acl", parse_http_set_map, 1 },
1956 { "del-header", parse_http_del_header, 0 },
1957 { "del-map", parse_http_set_map, 1 },
Christopher Faulete0fca292020-01-13 21:49:03 +01001958 { "deny", parse_http_deny, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001959 { "redirect", parse_http_redirect, 0 },
1960 { "replace-header", parse_http_replace_header, 0 },
1961 { "replace-value", parse_http_replace_header, 0 },
Christopher Faulet24231ab2020-01-24 17:44:23 +01001962 { "return", parse_http_return, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001963 { "set-header", parse_http_set_header, 0 },
1964 { "set-log-level", parse_http_set_log_level, 0 },
1965 { "set-map", parse_http_set_map, 1 },
1966 { "set-mark", parse_http_set_mark, 0 },
1967 { "set-nice", parse_http_set_nice, 0 },
1968 { "set-status", parse_http_set_status, 0 },
1969 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001970 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001971 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001972 { NULL, NULL }
1973 }
1974};
1975
Willy Tarreau0108d902018-11-25 19:14:37 +01001976INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001977
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001978static struct action_kw_list http_after_res_actions = {
1979 .kw = {
1980 { "add-header", parse_http_set_header, 0 },
1981 { "allow", parse_http_allow, 0 },
1982 { "del-header", parse_http_del_header, 0 },
1983 { "replace-header", parse_http_replace_header, 0 },
1984 { "replace-value", parse_http_replace_header, 0 },
1985 { "set-header", parse_http_set_header, 0 },
1986 { "set-status", parse_http_set_status, 0 },
1987 { "strict-mode", parse_http_strict_mode, 0 },
1988 { NULL, NULL }
1989 }
1990};
1991
1992INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions);
1993
Willy Tarreau79e57332018-10-02 16:01:16 +02001994/*
1995 * Local variables:
1996 * c-indent-level: 8
1997 * c-basic-offset: 8
1998 * End:
1999 */