blob: 994e3b47c0524ca7983625e8d735493b5736ae98 [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP actions
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
Christopher Faulet81e20172019-12-12 16:40:30 +010019#include <common/cfgparse.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020020#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
24#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010025#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020026#include <common/memory.h>
27#include <common/standard.h>
28#include <common/version.h>
29
30#include <types/capture.h>
31#include <types/global.h>
32
33#include <proto/acl.h>
34#include <proto/arg.h>
Christopher Faulet81e20172019-12-12 16:40:30 +010035#include <proto/action.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020036#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020037#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020038#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020039#include <proto/http_ana.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010040#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020041
42
43/* This function executes one of the set-{method,path,query,uri} actions. It
44 * builds a string in the trash from the specified format string. It finds
Christopher Faulet96bff762019-12-17 13:46:18 +010045 * the action to be performed in <http.i>, previously filled by function
Willy Tarreau79e57332018-10-02 16:01:16 +020046 * parse_set_req_line(). The replacement action is excuted by the function
Christopher Faulete00d06c2019-12-16 17:18:42 +010047 * http_action_set_req_line(). On success, it returns ACT_RET_CONT. If an error
48 * occurs while soft rewrites are enabled, the action is canceled, but the rule
49 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau79e57332018-10-02 16:01:16 +020050 */
51static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
52 struct session *sess, struct stream *s, int flags)
53{
54 struct buffer *replace;
Christopher Faulet13403762019-12-13 09:01:57 +010055 enum act_return ret = ACT_RET_CONT;
Willy Tarreau79e57332018-10-02 16:01:16 +020056
57 replace = alloc_trash_chunk();
58 if (!replace)
Christopher Faulete00d06c2019-12-16 17:18:42 +010059 goto fail_alloc;
Willy Tarreau79e57332018-10-02 16:01:16 +020060
61 /* If we have to create a query string, prepare a '?'. */
Christopher Faulet96bff762019-12-17 13:46:18 +010062 if (rule->arg.http.i == 2) // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +020063 replace->area[replace->data++] = '?';
64 replace->data += build_logline(s, replace->area + replace->data,
65 replace->size - replace->data,
Christopher Faulet96bff762019-12-17 13:46:18 +010066 &rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +020067
Christopher Faulet96bff762019-12-17 13:46:18 +010068 if (http_req_replace_stline(rule->arg.http.i, replace->area,
Christopher Faulete00d06c2019-12-16 17:18:42 +010069 replace->data, px, s) == -1)
70 goto fail_rewrite;
Willy Tarreau79e57332018-10-02 16:01:16 +020071
Christopher Faulete00d06c2019-12-16 17:18:42 +010072 leave:
Willy Tarreau79e57332018-10-02 16:01:16 +020073 free_trash_chunk(replace);
74 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +010075
76 fail_alloc:
77 if (!(s->flags & SF_ERR_MASK))
78 s->flags |= SF_ERR_RESOURCE;
79 ret = ACT_RET_ERR;
80 goto leave;
81
82 fail_rewrite:
83 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
84 if (s->flags & SF_BE_ASSIGNED)
85 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
86 if (sess->listener->counters)
87 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
88 if (objt_server(s->target))
89 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
90
91 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
92 ret = ACT_RET_ERR;
93 goto leave;
Willy Tarreau79e57332018-10-02 16:01:16 +020094}
95
96/* parse an http-request action among :
97 * set-method
98 * set-path
99 * set-query
100 * set-uri
101 *
102 * All of them accept a single argument of type string representing a log-format.
Christopher Faulet96bff762019-12-17 13:46:18 +0100103 * The resulting rule makes use of <http.fmt> to store the log-format list head,
104 * and <http.i> to store the action as an int (0=method, 1=path, 2=query, 3=uri).
Willy Tarreau79e57332018-10-02 16:01:16 +0200105 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
106 */
107static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
108 struct act_rule *rule, char **err)
109{
110 int cur_arg = *orig_arg;
111
112 rule->action = ACT_CUSTOM;
113
114 switch (args[0][4]) {
115 case 'm' :
Christopher Faulet96bff762019-12-17 13:46:18 +0100116 rule->arg.http.i = 0; // set-method
Willy Tarreau79e57332018-10-02 16:01:16 +0200117 break;
118 case 'p' :
Christopher Faulet96bff762019-12-17 13:46:18 +0100119 rule->arg.http.i = 1; // set-path
Willy Tarreau79e57332018-10-02 16:01:16 +0200120 break;
121 case 'q' :
Christopher Faulet96bff762019-12-17 13:46:18 +0100122 rule->arg.http.i = 2; // set-query
Willy Tarreau79e57332018-10-02 16:01:16 +0200123 break;
124 case 'u' :
Christopher Faulet96bff762019-12-17 13:46:18 +0100125 rule->arg.http.i = 3; // set-uri
Willy Tarreau79e57332018-10-02 16:01:16 +0200126 break;
127 default:
128 memprintf(err, "internal error: unhandled action '%s'", args[0]);
129 return ACT_RET_PRS_ERR;
130 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100131 rule->action_ptr = http_action_set_req_line;
Willy Tarreau79e57332018-10-02 16:01:16 +0200132
133 if (!*args[cur_arg] ||
134 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
135 memprintf(err, "expects exactly 1 argument <format>");
136 return ACT_RET_PRS_ERR;
137 }
138
Christopher Faulet96bff762019-12-17 13:46:18 +0100139 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau79e57332018-10-02 16:01:16 +0200140 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100141 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau79e57332018-10-02 16:01:16 +0200142 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
143 return ACT_RET_PRS_ERR;
144 }
145
146 (*orig_arg)++;
147 return ACT_RET_PRS_OK;
148}
149
Willy Tarreau33810222019-06-12 17:44:02 +0200150/* This function executes a replace-uri action. It finds its arguments in
Christopher Faulet96bff762019-12-17 13:46:18 +0100151 * <rule>.arg.http. It builds a string in the trash from the format string
Willy Tarreau33810222019-06-12 17:44:02 +0200152 * previously filled by function parse_replace_uri() and will execute the regex
Christopher Faulet96bff762019-12-17 13:46:18 +0100153 * in <http.re> to replace the URI. It uses the format string present in
154 * <http.fmt>. The component to act on (path/uri) is taken from <http.i> which
155 * contains 1 for the path or 3 for the URI (values used by
156 * http_req_replace_stline()). On success, it returns ACT_RET_CONT. If an error
157 * occurs while soft rewrites are enabled, the action is canceled, but the rule
158 * processing continue. Otherwsize ACT_RET_ERR is returned.
Willy Tarreau33810222019-06-12 17:44:02 +0200159 */
160static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
161 struct session *sess, struct stream *s, int flags)
162{
Christopher Faulet13403762019-12-13 09:01:57 +0100163 enum act_return ret = ACT_RET_CONT;
Willy Tarreau33810222019-06-12 17:44:02 +0200164 struct buffer *replace, *output;
165 struct ist uri;
166 int len;
167
168 replace = alloc_trash_chunk();
169 output = alloc_trash_chunk();
170 if (!replace || !output)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100171 goto fail_alloc;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200172 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau262c3f12019-12-17 06:52:51 +0100173
Christopher Faulet96bff762019-12-17 13:46:18 +0100174 if (rule->arg.http.i == 1) // replace-path
175 uri = http_get_path(uri);
Willy Tarreau262c3f12019-12-17 06:52:51 +0100176
Christopher Faulet96bff762019-12-17 13:46:18 +0100177 if (!regex_exec_match2(rule->arg.http.re, uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
Willy Tarreau33810222019-06-12 17:44:02 +0200178 goto leave;
179
Christopher Faulet96bff762019-12-17 13:46:18 +0100180 replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200181
182 /* note: uri.ptr doesn't need to be zero-terminated because it will
183 * only be used to pick pmatch references.
184 */
185 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
186 if (len == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100187 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200188
Christopher Faulet96bff762019-12-17 13:46:18 +0100189 if (http_req_replace_stline(rule->arg.http.i, output->area, len, px, s) == -1)
Christopher Faulete00d06c2019-12-16 17:18:42 +0100190 goto fail_rewrite;
Willy Tarreau33810222019-06-12 17:44:02 +0200191
Christopher Faulete00d06c2019-12-16 17:18:42 +0100192 leave:
Willy Tarreau33810222019-06-12 17:44:02 +0200193 free_trash_chunk(output);
194 free_trash_chunk(replace);
195 return ret;
Christopher Faulete00d06c2019-12-16 17:18:42 +0100196
197 fail_alloc:
198 if (!(s->flags & SF_ERR_MASK))
199 s->flags |= SF_ERR_RESOURCE;
200 ret = ACT_RET_ERR;
201 goto leave;
202
203 fail_rewrite:
204 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
205 if (s->flags & SF_BE_ASSIGNED)
206 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
207 if (sess->listener->counters)
208 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
209 if (objt_server(s->target))
210 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
211
212 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
213 ret = ACT_RET_ERR;
214 goto leave;
Willy Tarreau33810222019-06-12 17:44:02 +0200215}
216
Willy Tarreau262c3f12019-12-17 06:52:51 +0100217/* parse a "replace-uri" or "replace-path" http-request action.
Willy Tarreau33810222019-06-12 17:44:02 +0200218 * This action takes 2 arguments (a regex and a replacement format string).
Christopher Faulet96bff762019-12-17 13:46:18 +0100219 * The resulting rule makes use of <http.i> to store the action (1/3 for now),
220 * <http.re> to store the compiled regex, and <http.fmt> to store the log-format
Willy Tarreau33810222019-06-12 17:44:02 +0200221 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
222 */
223static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
224 struct act_rule *rule, char **err)
225{
226 int cur_arg = *orig_arg;
227 char *error = NULL;
228
229 rule->action = ACT_CUSTOM;
Willy Tarreau262c3f12019-12-17 06:52:51 +0100230 if (strcmp(args[cur_arg-1], "replace-path") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +0100231 rule->arg.http.i = 1; // replace-path
Willy Tarreau262c3f12019-12-17 06:52:51 +0100232 else
Christopher Faulet96bff762019-12-17 13:46:18 +0100233 rule->arg.http.i = 3; // replace-uri
Willy Tarreau262c3f12019-12-17 06:52:51 +0100234
Willy Tarreau33810222019-06-12 17:44:02 +0200235 rule->action_ptr = http_action_replace_uri;
236
237 if (!*args[cur_arg] || !*args[cur_arg+1] ||
238 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
239 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
240 return ACT_RET_PRS_ERR;
241 }
242
Christopher Faulet96bff762019-12-17 13:46:18 +0100243 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, &error))) {
Willy Tarreau33810222019-06-12 17:44:02 +0200244 memprintf(err, "failed to parse the regex : %s", error);
245 free(error);
246 return ACT_RET_PRS_ERR;
247 }
248
Christopher Faulet96bff762019-12-17 13:46:18 +0100249 LIST_INIT(&rule->arg.http.fmt);
Willy Tarreau33810222019-06-12 17:44:02 +0200250 px->conf.args.ctx = ARGC_HRQ;
Christopher Faulet96bff762019-12-17 13:46:18 +0100251 if (!parse_logformat_string(args[cur_arg + 1], px, &rule->arg.http.fmt, LOG_OPT_HTTP,
Willy Tarreau33810222019-06-12 17:44:02 +0200252 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
253 return ACT_RET_PRS_ERR;
254 }
255
256 (*orig_arg) += 2;
257 return ACT_RET_PRS_OK;
258}
259
Willy Tarreau79e57332018-10-02 16:01:16 +0200260/* This function is just a compliant action wrapper for "set-status". */
261static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
262 struct session *sess, struct stream *s, int flags)
263{
Christopher Faulet96bff762019-12-17 13:46:18 +0100264 if (http_res_set_status(rule->arg.http.i, rule->arg.http.str, s) == -1) {
Christopher Faulete00d06c2019-12-16 17:18:42 +0100265 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
266 if (s->flags & SF_BE_ASSIGNED)
267 _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
268 if (sess->listener->counters)
269 _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
270 if (objt_server(s->target))
271 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);
272
273 if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW))
274 return ACT_RET_ERR;
275 }
276
Willy Tarreau79e57332018-10-02 16:01:16 +0200277 return ACT_RET_CONT;
278}
279
280/* parse set-status action:
281 * This action accepts a single argument of type int representing
282 * an http status code. It returns ACT_RET_PRS_OK on success,
283 * ACT_RET_PRS_ERR on error.
284 */
285static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
286 struct act_rule *rule, char **err)
287{
288 char *error;
289
290 rule->action = ACT_CUSTOM;
291 rule->action_ptr = action_http_set_status;
292
293 /* Check if an argument is available */
294 if (!*args[*orig_arg]) {
295 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
296 return ACT_RET_PRS_ERR;
297 }
298
299 /* convert status code as integer */
Christopher Faulet96bff762019-12-17 13:46:18 +0100300 rule->arg.http.i = strtol(args[*orig_arg], &error, 10);
301 if (*error != '\0' || rule->arg.http.i < 100 || rule->arg.http.i > 999) {
Willy Tarreau79e57332018-10-02 16:01:16 +0200302 memprintf(err, "expects an integer status code between 100 and 999");
303 return ACT_RET_PRS_ERR;
304 }
305
306 (*orig_arg)++;
307
308 /* set custom reason string */
Christopher Faulet96bff762019-12-17 13:46:18 +0100309 rule->arg.http.str = ist(NULL); // If null, we use the default reason for the status code.
Willy Tarreau79e57332018-10-02 16:01:16 +0200310 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
311 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
312 (*orig_arg)++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100313 rule->arg.http.str.ptr = strdup(args[*orig_arg]);
314 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200315 (*orig_arg)++;
316 }
317
318 return ACT_RET_PRS_OK;
319}
320
321/* This function executes the "reject" HTTP action. It clears the request and
322 * response buffer without sending any response. It can be useful as an HTTP
323 * alternative to the silent-drop action to defend against DoS attacks, and may
324 * also be used with HTTP/2 to close a connection instead of just a stream.
325 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200326 * flags will indicate "PR". It always returns ACT_RET_DONE.
Willy Tarreau79e57332018-10-02 16:01:16 +0200327 */
328static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
329 struct session *sess, struct stream *s, int flags)
330{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100331 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200332 channel_abort(&s->req);
333 channel_abort(&s->res);
334 s->req.analysers = 0;
335 s->res.analysers = 0;
336
Olivier Houcharda798bf52019-03-08 18:52:00 +0100337 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
338 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200339 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100340 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200341
342 if (!(s->flags & SF_ERR_MASK))
343 s->flags |= SF_ERR_PRXCOND;
344 if (!(s->flags & SF_FINST_MASK))
345 s->flags |= SF_FINST_R;
346
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200347 return ACT_RET_DONE;
Willy Tarreau79e57332018-10-02 16:01:16 +0200348}
349
350/* parse the "reject" action:
351 * This action takes no argument and returns ACT_RET_PRS_OK on success,
352 * ACT_RET_PRS_ERR on error.
353 */
354static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
355 struct act_rule *rule, char **err)
356{
357 rule->action = ACT_CUSTOM;
358 rule->action_ptr = http_action_reject;
359 return ACT_RET_PRS_OK;
360}
361
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200362/* This function executes the "disable-l7-retry" HTTP action.
363 * It disables L7 retries (all retry except for a connection failure). This
364 * can be useful for example to avoid retrying on POST requests.
365 * It just removes the L7 retry flag on the stream_interface, and always
366 * return ACT_RET_CONT;
367 */
368static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
369 struct session *sess, struct stream *s, int flags)
370{
371 struct stream_interface *si = &s->si[1];
372
373 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
374 * let's be future-proof and remove it anyway.
375 */
376 si->flags &= ~SI_FL_L7_RETRY;
377 si->flags |= SI_FL_D_L7_RETRY;
378 return ACT_RET_CONT;
379}
380
381/* parse the "disable-l7-retry" action:
382 * This action takes no argument and returns ACT_RET_PRS_OK on success,
383 * ACT_RET_PRS_ERR on error.
384 */
385static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
386 int *orig_args, struct proxy *px,
387 struct act_rule *rule, char **err)
388{
389 rule->action = ACT_CUSTOM;
390 rule->action_ptr = http_req_disable_l7_retry;
391 return ACT_RET_PRS_OK;
392}
393
Willy Tarreau79e57332018-10-02 16:01:16 +0200394/* This function executes the "capture" action. It executes a fetch expression,
395 * turns the result into a string and puts it in a capture slot. It always
396 * returns 1. If an error occurs the action is cancelled, but the rule
397 * processing continues.
398 */
399static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
400 struct session *sess, struct stream *s, int flags)
401{
402 struct sample *key;
403 struct cap_hdr *h = rule->arg.cap.hdr;
404 char **cap = s->req_cap;
405 int len;
406
407 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
408 if (!key)
409 return ACT_RET_CONT;
410
411 if (cap[h->index] == NULL)
412 cap[h->index] = pool_alloc(h->pool);
413
414 if (cap[h->index] == NULL) /* no more capture memory */
415 return ACT_RET_CONT;
416
417 len = key->data.u.str.data;
418 if (len > h->len)
419 len = h->len;
420
421 memcpy(cap[h->index], key->data.u.str.area, len);
422 cap[h->index][len] = 0;
423 return ACT_RET_CONT;
424}
425
426/* This function executes the "capture" action and store the result in a
427 * capture slot if exists. It executes a fetch expression, turns the result
428 * into a string and puts it in a capture slot. It always returns 1. If an
429 * error occurs the action is cancelled, but the rule processing continues.
430 */
431static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
432 struct session *sess, struct stream *s, int flags)
433{
434 struct sample *key;
435 struct cap_hdr *h;
436 char **cap = s->req_cap;
437 struct proxy *fe = strm_fe(s);
438 int len;
439 int i;
440
441 /* Look for the original configuration. */
442 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
443 h != NULL && i != rule->arg.capid.idx ;
444 i--, h = h->next);
445 if (!h)
446 return ACT_RET_CONT;
447
448 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
449 if (!key)
450 return ACT_RET_CONT;
451
452 if (cap[h->index] == NULL)
453 cap[h->index] = pool_alloc(h->pool);
454
455 if (cap[h->index] == NULL) /* no more capture memory */
456 return ACT_RET_CONT;
457
458 len = key->data.u.str.data;
459 if (len > h->len)
460 len = h->len;
461
462 memcpy(cap[h->index], key->data.u.str.area, len);
463 cap[h->index][len] = 0;
464 return ACT_RET_CONT;
465}
466
467/* Check an "http-request capture" action.
468 *
469 * The function returns 1 in success case, otherwise, it returns 0 and err is
470 * filled.
471 */
472static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
473{
474 if (rule->action_ptr != http_action_req_capture_by_id)
475 return 1;
476
477 if (rule->arg.capid.idx >= px->nb_req_cap) {
478 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
479 rule->arg.capid.idx);
480 return 0;
481 }
482
483 return 1;
484}
485
486/* parse an "http-request capture" action. It takes a single argument which is
487 * a sample fetch expression. It stores the expression into arg->act.p[0] and
488 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
489 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
490 */
491static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
492 struct act_rule *rule, char **err)
493{
494 struct sample_expr *expr;
495 struct cap_hdr *hdr;
496 int cur_arg;
497 int len = 0;
498
499 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
500 if (strcmp(args[cur_arg], "if") == 0 ||
501 strcmp(args[cur_arg], "unless") == 0)
502 break;
503
504 if (cur_arg < *orig_arg + 3) {
505 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
506 return ACT_RET_PRS_ERR;
507 }
508
509 cur_arg = *orig_arg;
510 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
511 if (!expr)
512 return ACT_RET_PRS_ERR;
513
514 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
515 memprintf(err,
516 "fetch method '%s' extracts information from '%s', none of which is available here",
517 args[cur_arg-1], sample_src_names(expr->fetch->use));
518 free(expr);
519 return ACT_RET_PRS_ERR;
520 }
521
522 if (!args[cur_arg] || !*args[cur_arg]) {
523 memprintf(err, "expects 'len or 'id'");
524 free(expr);
525 return ACT_RET_PRS_ERR;
526 }
527
528 if (strcmp(args[cur_arg], "len") == 0) {
529 cur_arg++;
530
531 if (!(px->cap & PR_CAP_FE)) {
532 memprintf(err, "proxy '%s' has no frontend capability", px->id);
533 return ACT_RET_PRS_ERR;
534 }
535
536 px->conf.args.ctx = ARGC_CAP;
537
538 if (!args[cur_arg]) {
539 memprintf(err, "missing length value");
540 free(expr);
541 return ACT_RET_PRS_ERR;
542 }
543 /* we copy the table name for now, it will be resolved later */
544 len = atoi(args[cur_arg]);
545 if (len <= 0) {
546 memprintf(err, "length must be > 0");
547 free(expr);
548 return ACT_RET_PRS_ERR;
549 }
550 cur_arg++;
551
Willy Tarreau79e57332018-10-02 16:01:16 +0200552 hdr = calloc(1, sizeof(*hdr));
553 hdr->next = px->req_cap;
554 hdr->name = NULL; /* not a header capture */
555 hdr->namelen = 0;
556 hdr->len = len;
557 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
558 hdr->index = px->nb_req_cap++;
559
560 px->req_cap = hdr;
561 px->to_log |= LW_REQHDR;
562
563 rule->action = ACT_CUSTOM;
564 rule->action_ptr = http_action_req_capture;
565 rule->arg.cap.expr = expr;
566 rule->arg.cap.hdr = hdr;
567 }
568
569 else if (strcmp(args[cur_arg], "id") == 0) {
570 int id;
571 char *error;
572
573 cur_arg++;
574
575 if (!args[cur_arg]) {
576 memprintf(err, "missing id value");
577 free(expr);
578 return ACT_RET_PRS_ERR;
579 }
580
581 id = strtol(args[cur_arg], &error, 10);
582 if (*error != '\0') {
583 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
584 free(expr);
585 return ACT_RET_PRS_ERR;
586 }
587 cur_arg++;
588
589 px->conf.args.ctx = ARGC_CAP;
590
591 rule->action = ACT_CUSTOM;
592 rule->action_ptr = http_action_req_capture_by_id;
593 rule->check_ptr = check_http_req_capture;
594 rule->arg.capid.expr = expr;
595 rule->arg.capid.idx = id;
596 }
597
598 else {
599 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
600 free(expr);
601 return ACT_RET_PRS_ERR;
602 }
603
604 *orig_arg = cur_arg;
605 return ACT_RET_PRS_OK;
606}
607
608/* This function executes the "capture" action and store the result in a
609 * capture slot if exists. It executes a fetch expression, turns the result
610 * into a string and puts it in a capture slot. It always returns 1. If an
611 * error occurs the action is cancelled, but the rule processing continues.
612 */
613static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
614 struct session *sess, struct stream *s, int flags)
615{
616 struct sample *key;
617 struct cap_hdr *h;
618 char **cap = s->res_cap;
619 struct proxy *fe = strm_fe(s);
620 int len;
621 int i;
622
623 /* Look for the original configuration. */
624 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
625 h != NULL && i != rule->arg.capid.idx ;
626 i--, h = h->next);
627 if (!h)
628 return ACT_RET_CONT;
629
630 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
631 if (!key)
632 return ACT_RET_CONT;
633
634 if (cap[h->index] == NULL)
635 cap[h->index] = pool_alloc(h->pool);
636
637 if (cap[h->index] == NULL) /* no more capture memory */
638 return ACT_RET_CONT;
639
640 len = key->data.u.str.data;
641 if (len > h->len)
642 len = h->len;
643
644 memcpy(cap[h->index], key->data.u.str.area, len);
645 cap[h->index][len] = 0;
646 return ACT_RET_CONT;
647}
648
649/* Check an "http-response capture" action.
650 *
651 * The function returns 1 in success case, otherwise, it returns 0 and err is
652 * filled.
653 */
654static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
655{
656 if (rule->action_ptr != http_action_res_capture_by_id)
657 return 1;
658
659 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
660 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
661 rule->arg.capid.idx);
662 return 0;
663 }
664
665 return 1;
666}
667
668/* parse an "http-response capture" action. It takes a single argument which is
669 * a sample fetch expression. It stores the expression into arg->act.p[0] and
670 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
671 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
672 */
673static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
674 struct act_rule *rule, char **err)
675{
676 struct sample_expr *expr;
677 int cur_arg;
678 int id;
679 char *error;
680
681 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
682 if (strcmp(args[cur_arg], "if") == 0 ||
683 strcmp(args[cur_arg], "unless") == 0)
684 break;
685
686 if (cur_arg < *orig_arg + 3) {
687 memprintf(err, "expects <expression> id <idx>");
688 return ACT_RET_PRS_ERR;
689 }
690
691 cur_arg = *orig_arg;
692 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
693 if (!expr)
694 return ACT_RET_PRS_ERR;
695
696 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
697 memprintf(err,
698 "fetch method '%s' extracts information from '%s', none of which is available here",
699 args[cur_arg-1], sample_src_names(expr->fetch->use));
700 free(expr);
701 return ACT_RET_PRS_ERR;
702 }
703
704 if (!args[cur_arg] || !*args[cur_arg]) {
705 memprintf(err, "expects 'id'");
706 free(expr);
707 return ACT_RET_PRS_ERR;
708 }
709
710 if (strcmp(args[cur_arg], "id") != 0) {
711 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
712 free(expr);
713 return ACT_RET_PRS_ERR;
714 }
715
716 cur_arg++;
717
718 if (!args[cur_arg]) {
719 memprintf(err, "missing id value");
720 free(expr);
721 return ACT_RET_PRS_ERR;
722 }
723
724 id = strtol(args[cur_arg], &error, 10);
725 if (*error != '\0') {
726 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
727 free(expr);
728 return ACT_RET_PRS_ERR;
729 }
730 cur_arg++;
731
732 px->conf.args.ctx = ARGC_CAP;
733
734 rule->action = ACT_CUSTOM;
735 rule->action_ptr = http_action_res_capture_by_id;
736 rule->check_ptr = check_http_res_capture;
737 rule->arg.capid.expr = expr;
738 rule->arg.capid.idx = id;
739
740 *orig_arg = cur_arg;
741 return ACT_RET_PRS_OK;
742}
743
Christopher Faulet81e20172019-12-12 16:40:30 +0100744/* Parse a "allow" action for a request or a response rule. It takes no argument. It
745 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
746 */
747static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
748 struct act_rule *rule, char **err)
749{
750 rule->action = ACT_ACTION_ALLOW;
751 return ACT_RET_PRS_OK;
752}
753
754/* Parse "deny" or "tarpit" actions for a request rule. It may take 2 optional arguments
755 * to define the status code. It returns ACT_RET_PRS_OK on success,
756 * ACT_RET_PRS_ERR on error.
757 */
758static enum act_parse_ret parse_http_req_deny(const char **args, int *orig_arg, struct proxy *px,
759 struct act_rule *rule, char **err)
760{
761 int code, hc, cur_arg;
762
763 cur_arg = *orig_arg;
764 if (!strcmp(args[cur_arg-1], "tarpit")) {
765 rule->action = ACT_HTTP_REQ_TARPIT;
Christopher Faulet96bff762019-12-17 13:46:18 +0100766 rule->arg.http.i = HTTP_ERR_500;
Christopher Faulet81e20172019-12-12 16:40:30 +0100767 }
768 else {
769 rule->action = ACT_ACTION_DENY;
Christopher Faulet96bff762019-12-17 13:46:18 +0100770 rule->arg.http.i = HTTP_ERR_403;
Christopher Faulet81e20172019-12-12 16:40:30 +0100771 }
772
773 if (strcmp(args[cur_arg], "deny_status") == 0) {
774 cur_arg++;
775 if (!*args[cur_arg]) {
776 memprintf(err, "missing status code.\n");
777 return ACT_RET_PRS_ERR;
778 }
779
780 code = atol(args[cur_arg]);
781 cur_arg++;
782 for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
783 if (http_err_codes[hc] == code) {
Christopher Faulet96bff762019-12-17 13:46:18 +0100784 rule->arg.http.i = hc;
Christopher Faulet81e20172019-12-12 16:40:30 +0100785 break;
786 }
787 }
788 if (hc >= HTTP_ERR_SIZE)
789 memprintf(err, "status code %d not handled, using default code %d",
Christopher Faulet96bff762019-12-17 13:46:18 +0100790 code, http_err_codes[rule->arg.http.i]);
Christopher Faulet81e20172019-12-12 16:40:30 +0100791 }
792
793 *orig_arg = cur_arg;
794 return ACT_RET_PRS_OK;
795}
796
797/* Parse a "deny" action for a response rule. It takes no argument. It returns
798 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
799 */
800static enum act_parse_ret parse_http_res_deny(const char **args, int *orig_arg, struct proxy *px,
801 struct act_rule *rule, char **err)
802{
803 rule->action = ACT_ACTION_DENY;
804 return ACT_RET_PRS_OK;
805}
806
807/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
808 * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
809 */
810static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
811 struct act_rule *rule, char **err)
812{
813 int cur_arg;
814
815 rule->action = ACT_HTTP_REQ_AUTH;
816
817 cur_arg = *orig_arg;
818 if (!strcmp(args[cur_arg], "realm")) {
819 cur_arg++;
820 if (!*args[cur_arg]) {
821 memprintf(err, "missing realm value.\n");
822 return ACT_RET_PRS_ERR;
823 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100824 rule->arg.http.str.ptr = strdup(args[cur_arg]);
825 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +0100826 cur_arg++;
827 }
828
829 *orig_arg = cur_arg;
830 return ACT_RET_PRS_OK;
831}
832
833/* Parse a "set-nice" action. It takes the nice value as argument. It returns
834 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
835 */
836static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
837 struct act_rule *rule, char **err)
838{
839 int cur_arg;
840
841 rule->action = ACT_HTTP_SET_NICE;
842
843 cur_arg = *orig_arg;
844 if (!*args[cur_arg]) {
845 memprintf(err, "expects exactly 1 argument (integer value)");
846 return ACT_RET_PRS_ERR;
847 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100848 rule->arg.http.i = atoi(args[cur_arg]);
849 if (rule->arg.http.i < -1024)
850 rule->arg.http.i = -1024;
851 else if (rule->arg.http.i > 1024)
852 rule->arg.http.i = 1024;
Christopher Faulet81e20172019-12-12 16:40:30 +0100853
854 *orig_arg = cur_arg + 1;
855 return ACT_RET_PRS_OK;
856}
857
858/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
859 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
860 */
861static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
862 struct act_rule *rule, char **err)
863{
864#ifdef IP_TOS
865 char *endp;
866 int cur_arg;
867
868 rule->action = ACT_HTTP_SET_TOS;
869
870 cur_arg = *orig_arg;
871 if (!*args[cur_arg]) {
872 memprintf(err, "expects exactly 1 argument (integer/hex value)");
873 return ACT_RET_PRS_ERR;
874 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100875 rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100876 if (endp && *endp != '\0') {
877 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
878 return ACT_RET_PRS_ERR;
879 }
880
881 *orig_arg = cur_arg + 1;
882 return ACT_RET_PRS_OK;
883#else
884 memprintf(err, "not supported on this platform (IP_TOS undefined)");
885 return ACT_RET_PRS_ERR;
886#endif
887}
888
889/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
890 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
891 */
892static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
893 struct act_rule *rule, char **err)
894{
895#ifdef SO_MARK
896 char *endp;
897 int cur_arg;
898
899 rule->action = ACT_HTTP_SET_MARK;
900
901 cur_arg = *orig_arg;
902 if (!*args[cur_arg]) {
903 memprintf(err, "expects exactly 1 argument (integer/hex value)");
904 return ACT_RET_PRS_ERR;
905 }
Christopher Faulet96bff762019-12-17 13:46:18 +0100906 rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
Christopher Faulet81e20172019-12-12 16:40:30 +0100907 if (endp && *endp != '\0') {
908 memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
909 return ACT_RET_PRS_ERR;
910 }
911
912 *orig_arg = cur_arg + 1;
913 global.last_checks |= LSTCHK_NETADM;
914 return ACT_RET_PRS_OK;
915#else
916 memprintf(err, "not supported on this platform (SO_MARK undefined)");
917 return ACT_RET_PRS_ERR;
918#endif
919}
920
921/* Parse a "set-log-level" action. It takes the level value as argument. It
922 * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
923 */
924static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
925 struct act_rule *rule, char **err)
926{
927 int cur_arg;
928
929 rule->action = ACT_HTTP_SET_LOGL;
930
931 cur_arg = *orig_arg;
932 if (!*args[cur_arg]) {
933 bad_log_level:
934 memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
935 return ACT_RET_PRS_ERR;
936 }
937 if (strcmp(args[cur_arg], "silent") == 0)
Christopher Faulet96bff762019-12-17 13:46:18 +0100938 rule->arg.http.i = -1;
939 else if ((rule->arg.http.i = get_log_level(args[cur_arg]) + 1) == 0)
Christopher Faulet81e20172019-12-12 16:40:30 +0100940 goto bad_log_level;
941
942 *orig_arg = cur_arg + 1;
943 return ACT_RET_PRS_OK;
944}
945
946/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
947 * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
948 * on success, ACT_RET_PRS_ERR on error.
949 *
950 * Note: same function is used for the request and the response. However
951 * "early-hint" rules are only supported for request rules.
952 */
953static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
954 struct act_rule *rule, char **err)
955{
Christopher Faulet81e20172019-12-12 16:40:30 +0100956 int cap, cur_arg;
957
958 rule->action = (*args[*orig_arg-1] == 'a' ? ACT_HTTP_ADD_HDR :
959 *args[*orig_arg-1] == 's' ? ACT_HTTP_SET_HDR : ACT_HTTP_EARLY_HINT);
960
961 cur_arg = *orig_arg;
962 if (!*args[cur_arg] || !*args[cur_arg+1]) {
963 memprintf(err, "expects exactly 2 arguments");
964 return ACT_RET_PRS_ERR;
965 }
966
Christopher Faulet81e20172019-12-12 16:40:30 +0100967
Christopher Faulet96bff762019-12-17 13:46:18 +0100968 rule->arg.http.str.ptr = strdup(args[cur_arg]);
969 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
970 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +0100971
972 if (rule->from == ACT_F_HTTP_REQ) {
973 px->conf.args.ctx = ARGC_HRQ;
974 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
975 }
976 else{
977 px->conf.args.ctx = ARGC_HRS;
978 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
979 }
980
981 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +0100982 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err))
Christopher Faulet81e20172019-12-12 16:40:30 +0100983 return ACT_RET_PRS_ERR;
984
985 free(px->conf.lfs_file);
986 px->conf.lfs_file = strdup(px->conf.args.file);
987 px->conf.lfs_line = px->conf.args.line;
988
989 *orig_arg = cur_arg + 1;
990 return ACT_RET_PRS_OK;
991}
992
993/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
994 * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
995 * success, ACT_RET_PRS_ERR on error.
996 */
997static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
998 struct act_rule *rule, char **err)
999{
1000 int cap, cur_arg;
1001
1002 rule->action = args[*orig_arg-1][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
1003
1004 cur_arg = *orig_arg;
1005 if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
1006 memprintf(err, "expects exactly 3 arguments");
1007 return ACT_RET_PRS_ERR;
1008 }
1009
Christopher Faulet96bff762019-12-17 13:46:18 +01001010 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1011 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
1012 LIST_INIT(&rule->arg.http.fmt);
Christopher Faulet81e20172019-12-12 16:40:30 +01001013
1014 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +01001015 if (!(rule->arg.http.re = regex_comp(args[cur_arg], 1, 1, err)))
Christopher Faulet81e20172019-12-12 16:40:30 +01001016 return ACT_RET_PRS_ERR;
1017
1018 if (rule->from == ACT_F_HTTP_REQ) {
1019 px->conf.args.ctx = ARGC_HRQ;
1020 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1021 }
1022 else{
1023 px->conf.args.ctx = ARGC_HRS;
1024 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1025 }
1026
1027 cur_arg++;
Christopher Faulet96bff762019-12-17 13:46:18 +01001028 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.fmt, LOG_OPT_HTTP, cap, err))
Christopher Faulet81e20172019-12-12 16:40:30 +01001029 return ACT_RET_PRS_ERR;
1030
1031 free(px->conf.lfs_file);
1032 px->conf.lfs_file = strdup(px->conf.args.file);
1033 px->conf.lfs_line = px->conf.args.line;
1034
1035 *orig_arg = cur_arg + 1;
1036 return ACT_RET_PRS_OK;
1037}
1038
1039/* Parse a "del-header" action. It takes an header name as argument. It returns
1040 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1041 */
1042static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
1043 struct act_rule *rule, char **err)
1044{
1045 int cur_arg;
1046
1047 rule->action = ACT_HTTP_DEL_HDR;
1048
1049 cur_arg = *orig_arg;
1050 if (!*args[cur_arg]) {
1051 memprintf(err, "expects exactly 1 arguments");
1052 return ACT_RET_PRS_ERR;
1053 }
1054
Christopher Faulet96bff762019-12-17 13:46:18 +01001055 rule->arg.http.str.ptr = strdup(args[cur_arg]);
1056 rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
Christopher Faulet81e20172019-12-12 16:40:30 +01001057
1058 px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
1059
1060 *orig_arg = cur_arg + 1;
1061 return ACT_RET_PRS_OK;
1062}
1063
1064/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
1065 * ACT_RET_PRS_ERR on error.
1066 */
1067static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
1068 struct act_rule *rule, char **err)
1069{
1070 struct redirect_rule *redir;
1071 int dir, cur_arg;
1072
1073 rule->action = ACT_HTTP_REDIR;
1074
1075 cur_arg = *orig_arg;
1076
1077 dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
1078 if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
1079 return ACT_RET_PRS_ERR;
1080
1081 rule->arg.redir = redir;
1082 rule->cond = redir->cond;
1083 redir->cond = NULL;
1084
1085 /* skip all arguments */
1086 while (*args[cur_arg])
1087 cur_arg++;
1088
1089 *orig_arg = cur_arg;
1090 return ACT_RET_PRS_OK;
1091}
1092
1093/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
1094 * two log-format string as argument depending on the action. It returns
1095 * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
1096 */
1097static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
1098 struct act_rule *rule, char **err)
1099{
1100 int cap, cur_arg;
1101
1102 rule->action = (args[*orig_arg-1][0] == 'a' ? ACT_HTTP_ADD_ACL :
1103 (args[*orig_arg-1][0] == 's' ? ACT_HTTP_SET_MAP :
1104 (args[*orig_arg-1][4] == 'a' ? ACT_HTTP_DEL_ACL : ACT_HTTP_DEL_MAP)));
1105
1106 cur_arg = *orig_arg;
1107 if (rule->action == ACT_HTTP_SET_MAP && (!*args[cur_arg] || !*args[cur_arg+1])) {
1108 memprintf(err, "expects exactly 2 arguments");
1109 return ACT_RET_PRS_ERR;
1110 }
1111 else if (!*args[cur_arg]) {
1112 memprintf(err, "expects exactly 1 arguments");
1113 return ACT_RET_PRS_ERR;
1114 }
1115
1116 /*
1117 * '+ 8' for 'set-map(' (same for del-map)
1118 * '- 9' for 'set-map(' + trailing ')' (same for del-map)
1119 */
1120 rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
1121
1122 if (rule->from == ACT_F_HTTP_REQ) {
1123 px->conf.args.ctx = ARGC_HRQ;
1124 cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
1125 }
1126 else{
1127 px->conf.args.ctx = ARGC_HRS;
1128 cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
1129 }
1130
1131 /* key pattern */
1132 LIST_INIT(&rule->arg.map.key);
1133 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err))
1134 return ACT_RET_PRS_ERR;
1135
1136 if (rule->action == ACT_HTTP_SET_MAP) {
1137 /* value pattern for set-map only */
1138 cur_arg++;
1139 LIST_INIT(&rule->arg.map.value);
1140 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err))
1141 return ACT_RET_PRS_ERR;
1142 }
1143
1144 free(px->conf.lfs_file);
1145 px->conf.lfs_file = strdup(px->conf.args.file);
1146 px->conf.lfs_line = px->conf.args.line;
1147
1148 *orig_arg = cur_arg + 1;
1149 return ACT_RET_PRS_OK;
1150}
1151
1152
1153/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
1154 * ACT_RET_PRS_ERR on error.
1155 */
1156static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
1157 struct act_rule *rule, char **err)
1158{
1159 struct sample_expr *expr;
1160 unsigned int where;
1161 unsigned int tsc_num;
1162 const char *tsc_num_str;
1163 int cur_arg;
1164
1165 tsc_num_str = &args[*orig_arg-1][8];
1166 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
1167 return ACT_RET_PRS_ERR;
1168
1169 cur_arg = *orig_arg;
1170 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
1171 err, &px->conf.args);
1172 if (!expr)
1173 return ACT_RET_PRS_ERR;
1174
1175 where = 0;
1176 if (px->cap & PR_CAP_FE)
1177 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
1178 if (px->cap & PR_CAP_BE)
1179 where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
1180
1181 if (!(expr->fetch->val & where)) {
1182 memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
1183 args[cur_arg-1], sample_src_names(expr->fetch->use));
1184 return ACT_RET_PRS_ERR;
1185 }
1186
1187 if (strcmp(args[cur_arg], "table") == 0) {
1188 cur_arg++;
1189 if (!*args[cur_arg]) {
1190 memprintf(err, "missing table name");
1191 return ACT_RET_PRS_ERR;
1192 }
1193
1194 /* we copy the table name for now, it will be resolved later */
1195 rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
1196 cur_arg++;
1197 }
1198
1199 rule->arg.trk_ctr.expr = expr;
1200 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
1201 rule->check_ptr = check_trk_action;
1202
1203 *orig_arg = cur_arg;
1204 return ACT_RET_PRS_OK;
1205}
1206
Christopher Faulet46f95542019-12-20 10:07:22 +01001207/* This function executes a strict-mode actions. On success, it always returns
1208 * ACT_RET_CONT
1209 */
1210static enum act_return http_action_strict_mode(struct act_rule *rule, struct proxy *px,
1211 struct session *sess, struct stream *s, int flags)
1212{
1213 struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
1214
1215 if (rule->action == 0) // strict-mode on
1216 msg->flags &= ~HTTP_MSGF_SOFT_RW;
1217 else // strict-mode off
1218 msg->flags |= HTTP_MSGF_SOFT_RW;
1219 return ACT_RET_CONT;
1220}
1221
1222/* Parse a "strict-mode" action. It returns ACT_RET_PRS_OK on success,
1223 * ACT_RET_PRS_ERR on error.
1224 */
1225static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_arg, struct proxy *px,
1226 struct act_rule *rule, char **err)
1227{
1228 int cur_arg;
1229
1230
1231 cur_arg = *orig_arg;
1232 if (!*args[cur_arg]) {
1233 memprintf(err, "expects exactly 1 arguments");
1234 return ACT_RET_PRS_ERR;
1235 }
1236
1237 if (strcasecmp(args[cur_arg], "on") == 0)
1238 rule->action = 0; // strict-mode on
1239 else if (strcasecmp(args[cur_arg], "off") == 0)
1240 rule->action = 1; // strict-mode off
1241 else {
1242 memprintf(err, "Unexpected value '%s'. Only 'on' and 'off' are supported", args[cur_arg]);
1243 return ACT_RET_PRS_ERR;
1244 }
1245 rule->action_ptr = http_action_strict_mode;
1246
1247 *orig_arg = cur_arg + 1;
1248 return ACT_RET_PRS_OK;
1249}
1250
Willy Tarreau79e57332018-10-02 16:01:16 +02001251/************************************************************************/
1252/* All supported http-request action keywords must be declared here. */
1253/************************************************************************/
1254
1255static struct action_kw_list http_req_actions = {
1256 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001257 { "add-acl", parse_http_set_map, 1 },
1258 { "add-header", parse_http_set_header, 0 },
1259 { "allow", parse_http_allow, 0 },
1260 { "auth", parse_http_auth, 0 },
1261 { "capture", parse_http_req_capture, 0 },
1262 { "del-acl", parse_http_set_map, 1 },
1263 { "del-header", parse_http_del_header, 0 },
1264 { "del-map", parse_http_set_map, 1 },
1265 { "deny", parse_http_req_deny, 0 },
1266 { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
1267 { "early-hint", parse_http_set_header, 0 },
1268 { "redirect", parse_http_redirect, 0 },
1269 { "reject", parse_http_action_reject, 0 },
1270 { "replace-header", parse_http_replace_header, 0 },
1271 { "replace-path", parse_replace_uri, 0 },
1272 { "replace-uri", parse_replace_uri, 0 },
1273 { "replace-value", parse_http_replace_header, 0 },
1274 { "set-header", parse_http_set_header, 0 },
1275 { "set-log-level", parse_http_set_log_level, 0 },
1276 { "set-map", parse_http_set_map, 1 },
1277 { "set-method", parse_set_req_line, 0 },
1278 { "set-mark", parse_http_set_mark, 0 },
1279 { "set-nice", parse_http_set_nice, 0 },
1280 { "set-path", parse_set_req_line, 0 },
1281 { "set-query", parse_set_req_line, 0 },
1282 { "set-tos", parse_http_set_tos, 0 },
1283 { "set-uri", parse_set_req_line, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001284 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001285 { "tarpit", parse_http_req_deny, 0 },
1286 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001287 { NULL, NULL }
1288 }
1289};
1290
Willy Tarreau0108d902018-11-25 19:14:37 +01001291INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
1292
Willy Tarreau79e57332018-10-02 16:01:16 +02001293static struct action_kw_list http_res_actions = {
1294 .kw = {
Christopher Faulet81e20172019-12-12 16:40:30 +01001295 { "add-acl", parse_http_set_map, 1 },
1296 { "add-header", parse_http_set_header, 0 },
1297 { "allow", parse_http_allow, 0 },
1298 { "capture", parse_http_res_capture, 0 },
1299 { "del-acl", parse_http_set_map, 1 },
1300 { "del-header", parse_http_del_header, 0 },
1301 { "del-map", parse_http_set_map, 1 },
1302 { "deny", parse_http_res_deny, 0 },
1303 { "redirect", parse_http_redirect, 0 },
1304 { "replace-header", parse_http_replace_header, 0 },
1305 { "replace-value", parse_http_replace_header, 0 },
1306 { "set-header", parse_http_set_header, 0 },
1307 { "set-log-level", parse_http_set_log_level, 0 },
1308 { "set-map", parse_http_set_map, 1 },
1309 { "set-mark", parse_http_set_mark, 0 },
1310 { "set-nice", parse_http_set_nice, 0 },
1311 { "set-status", parse_http_set_status, 0 },
1312 { "set-tos", parse_http_set_tos, 0 },
Christopher Faulet46f95542019-12-20 10:07:22 +01001313 { "strict-mode", parse_http_strict_mode, 0 },
Christopher Faulet81e20172019-12-12 16:40:30 +01001314 { "track-sc", parse_http_track_sc, 1 },
Willy Tarreau79e57332018-10-02 16:01:16 +02001315 { NULL, NULL }
1316 }
1317};
1318
Willy Tarreau0108d902018-11-25 19:14:37 +01001319INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +02001320
1321/*
1322 * Local variables:
1323 * c-indent-level: 8
1324 * c-basic-offset: 8
1325 * End:
1326 */