blob: 2e70e6bf0f03262c18dededb1bc794a9e05c0c73 [file] [log] [blame]
Willy Tarreau61c112a2018-10-02 16:43:32 +02001/*
2 * HTTP rules parsing and registration
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
19#include <common/cfgparse.h>
20#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
24#include <common/http.h>
25#include <common/memory.h>
26#include <common/standard.h>
27#include <common/version.h>
28
29#include <types/capture.h>
30#include <types/global.h>
31
32#include <proto/acl.h>
33#include <proto/action.h>
34#include <proto/arg.h>
35#include <proto/http_rules.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020036#include <proto/http_ana.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020037#include <proto/sample.h>
38
39
40/* List head of all known action keywords for "http-request" */
41struct action_kw_list http_req_keywords = {
42 .list = LIST_HEAD_INIT(http_req_keywords.list)
43};
44
45/* List head of all known action keywords for "http-response" */
46struct action_kw_list http_res_keywords = {
47 .list = LIST_HEAD_INIT(http_res_keywords.list)
48};
49
50/*
51 * Return the struct http_req_action_kw associated to a keyword.
52 */
53static struct action_kw *action_http_req_custom(const char *kw)
54{
55 return action_lookup(&http_req_keywords.list, kw);
56}
57
58/*
59 * Return the struct http_res_action_kw associated to a keyword.
60 */
61static struct action_kw *action_http_res_custom(const char *kw)
62{
63 return action_lookup(&http_res_keywords.list, kw);
64}
65
66/* parse an "http-request" rule */
67struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
68{
69 struct act_rule *rule;
70 struct action_kw *custom = NULL;
71 int cur_arg;
Willy Tarreau61c112a2018-10-02 16:43:32 +020072
73 rule = calloc(1, sizeof(*rule));
74 if (!rule) {
75 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
76 goto out_err;
77 }
Christopher Faulet81e20172019-12-12 16:40:30 +010078 rule->from = ACT_F_HTTP_REQ;
Willy Tarreau61c112a2018-10-02 16:43:32 +020079
Christopher Faulet81e20172019-12-12 16:40:30 +010080 if (((custom = action_http_req_custom(args[0])) != NULL)) {
Willy Tarreau61c112a2018-10-02 16:43:32 +020081 char *errmsg = NULL;
82
Willy Tarreau61c112a2018-10-02 16:43:32 +020083 cur_arg = 1;
84 /* try in the module list */
Willy Tarreau61c112a2018-10-02 16:43:32 +020085 rule->kw = custom;
86 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
87 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
88 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
89 free(errmsg);
90 goto out_err;
91 }
Christopher Faulet81e20172019-12-12 16:40:30 +010092 else if (errmsg) {
93 ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg);
94 free(errmsg);
95 }
96 }
97 else {
Willy Tarreau61c112a2018-10-02 16:43:32 +020098 action_build_list(&http_req_keywords.list, &trash);
Christopher Faulet81e20172019-12-12 16:40:30 +010099 ha_alert("parsing [%s:%d]: 'http-request' expects %s%s, but got '%s'%s.\n",
Willy Tarreau61c112a2018-10-02 16:43:32 +0200100 file, linenum, *trash.area ? ", " : "", trash.area,
101 args[0], *args[0] ? "" : " (missing argument)");
102 goto out_err;
103 }
104
105 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
106 struct acl_cond *cond;
107 char *errmsg = NULL;
108
109 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
110 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n",
111 file, linenum, args[0], errmsg);
112 free(errmsg);
113 goto out_err;
114 }
115 rule->cond = cond;
116 }
117 else if (*args[cur_arg]) {
Christopher Faulet81e20172019-12-12 16:40:30 +0100118 ha_alert("parsing [%s:%d]: 'http-request %s' expects"
Willy Tarreau61c112a2018-10-02 16:43:32 +0200119 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
120 file, linenum, args[0], args[cur_arg]);
121 goto out_err;
122 }
123
124 return rule;
125 out_err:
126 free(rule);
127 return NULL;
128}
129
130/* parse an "http-respose" rule */
131struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy)
132{
133 struct act_rule *rule;
134 struct action_kw *custom = NULL;
135 int cur_arg;
Willy Tarreau61c112a2018-10-02 16:43:32 +0200136
137 rule = calloc(1, sizeof(*rule));
138 if (!rule) {
139 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
140 goto out_err;
141 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100142 rule->from = ACT_F_HTTP_RES;
Willy Tarreau61c112a2018-10-02 16:43:32 +0200143
Christopher Faulet81e20172019-12-12 16:40:30 +0100144 if (((custom = action_http_res_custom(args[0])) != NULL)) {
Willy Tarreau61c112a2018-10-02 16:43:32 +0200145 char *errmsg = NULL;
146
Willy Tarreau61c112a2018-10-02 16:43:32 +0200147 cur_arg = 1;
148 /* try in the module list */
Willy Tarreau61c112a2018-10-02 16:43:32 +0200149 rule->kw = custom;
150 if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
151 ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
152 file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
153 free(errmsg);
154 goto out_err;
155 }
Christopher Faulet81e20172019-12-12 16:40:30 +0100156 else if (errmsg) {
157 ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg);
158 free(errmsg);
159 }
160 }
161 else {
Willy Tarreau61c112a2018-10-02 16:43:32 +0200162 action_build_list(&http_res_keywords.list, &trash);
Christopher Faulet81e20172019-12-12 16:40:30 +0100163 ha_alert("parsing [%s:%d]: 'http-response' expects %s%s, but got '%s'%s.\n",
Willy Tarreau61c112a2018-10-02 16:43:32 +0200164 file, linenum, *trash.area ? ", " : "", trash.area,
165 args[0], *args[0] ? "" : " (missing argument)");
166 goto out_err;
167 }
168
169 if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) {
170 struct acl_cond *cond;
171 char *errmsg = NULL;
172
173 if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) {
174 ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n",
175 file, linenum, args[0], errmsg);
176 free(errmsg);
177 goto out_err;
178 }
179 rule->cond = cond;
180 }
181 else if (*args[cur_arg]) {
182 ha_alert("parsing [%s:%d]: 'http-response %s' expects"
183 " either 'if' or 'unless' followed by a condition but found '%s'.\n",
184 file, linenum, args[0], args[cur_arg]);
185 goto out_err;
186 }
187
188 return rule;
189 out_err:
190 free(rule);
191 return NULL;
192}
193
194/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
195 * with <err> filled with the error message. If <use_fmt> is not null, builds a
196 * dynamic log-format rule instead of a static string. Parameter <dir> indicates
197 * the direction of the rule, and equals 0 for request, non-zero for responses.
198 */
199struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
200 const char **args, char **errmsg, int use_fmt, int dir)
201{
202 struct redirect_rule *rule;
203 int cur_arg;
204 int type = REDIRECT_TYPE_NONE;
205 int code = 302;
206 const char *destination = NULL;
207 const char *cookie = NULL;
208 int cookie_set = 0;
209 unsigned int flags = REDIRECT_FLAG_NONE;
210 struct acl_cond *cond = NULL;
211
212 cur_arg = 0;
213 while (*(args[cur_arg])) {
214 if (strcmp(args[cur_arg], "location") == 0) {
215 if (!*args[cur_arg + 1])
216 goto missing_arg;
217
218 type = REDIRECT_TYPE_LOCATION;
219 cur_arg++;
220 destination = args[cur_arg];
221 }
222 else if (strcmp(args[cur_arg], "prefix") == 0) {
223 if (!*args[cur_arg + 1])
224 goto missing_arg;
225 type = REDIRECT_TYPE_PREFIX;
226 cur_arg++;
227 destination = args[cur_arg];
228 }
229 else if (strcmp(args[cur_arg], "scheme") == 0) {
230 if (!*args[cur_arg + 1])
231 goto missing_arg;
232
233 type = REDIRECT_TYPE_SCHEME;
234 cur_arg++;
235 destination = args[cur_arg];
236 }
237 else if (strcmp(args[cur_arg], "set-cookie") == 0) {
238 if (!*args[cur_arg + 1])
239 goto missing_arg;
240
241 cur_arg++;
242 cookie = args[cur_arg];
243 cookie_set = 1;
244 }
245 else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
246 if (!*args[cur_arg + 1])
247 goto missing_arg;
248
249 cur_arg++;
250 cookie = args[cur_arg];
251 cookie_set = 0;
252 }
253 else if (strcmp(args[cur_arg], "code") == 0) {
254 if (!*args[cur_arg + 1])
255 goto missing_arg;
256
257 cur_arg++;
258 code = atol(args[cur_arg]);
259 if (code < 301 || code > 308 || (code > 303 && code < 307)) {
260 memprintf(errmsg,
261 "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)",
262 args[cur_arg - 1], args[cur_arg]);
263 return NULL;
264 }
265 }
266 else if (!strcmp(args[cur_arg],"drop-query")) {
267 flags |= REDIRECT_FLAG_DROP_QS;
268 }
269 else if (!strcmp(args[cur_arg],"append-slash")) {
270 flags |= REDIRECT_FLAG_APPEND_SLASH;
271 }
272 else if (strcmp(args[cur_arg], "if") == 0 ||
273 strcmp(args[cur_arg], "unless") == 0) {
274 cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg);
275 if (!cond) {
276 memprintf(errmsg, "error in condition: %s", *errmsg);
277 return NULL;
278 }
279 break;
280 }
281 else {
282 memprintf(errmsg,
283 "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
284 args[cur_arg]);
285 return NULL;
286 }
287 cur_arg++;
288 }
289
290 if (type == REDIRECT_TYPE_NONE) {
291 memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
292 return NULL;
293 }
294
295 if (dir && type != REDIRECT_TYPE_LOCATION) {
296 memprintf(errmsg, "response only supports redirect type 'location'");
297 return NULL;
298 }
299
300 rule = calloc(1, sizeof(*rule));
301 rule->cond = cond;
302 LIST_INIT(&rule->rdr_fmt);
303
304 if (!use_fmt) {
305 /* old-style static redirect rule */
306 rule->rdr_str = strdup(destination);
307 rule->rdr_len = strlen(destination);
308 }
309 else {
310 /* log-format based redirect rule */
311
312 /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
313 * if prefix == "/", we don't want to add anything, otherwise it
314 * makes it hard for the user to configure a self-redirection.
315 */
316 curproxy->conf.args.ctx = ARGC_RDR;
317 if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
318 if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
319 dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR
320 : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
321 errmsg)) {
322 return NULL;
323 }
324 free(curproxy->conf.lfs_file);
325 curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);
326 curproxy->conf.lfs_line = curproxy->conf.args.line;
327 }
328 }
329
330 if (cookie) {
331 /* depending on cookie_set, either we want to set the cookie, or to clear it.
332 * a clear consists in appending "; path=/; Max-Age=0;" at the end.
333 */
334 rule->cookie_len = strlen(cookie);
335 if (cookie_set) {
336 rule->cookie_str = malloc(rule->cookie_len + 10);
337 memcpy(rule->cookie_str, cookie, rule->cookie_len);
338 memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
339 rule->cookie_len += 9;
340 } else {
341 rule->cookie_str = malloc(rule->cookie_len + 21);
342 memcpy(rule->cookie_str, cookie, rule->cookie_len);
343 memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
344 rule->cookie_len += 20;
345 }
346 }
347 rule->type = type;
348 rule->code = code;
349 rule->flags = flags;
350 LIST_INIT(&rule->list);
351 return rule;
352
353 missing_arg:
354 memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
355 return NULL;
356}
357
Willy Tarreau61c112a2018-10-02 16:43:32 +0200358__attribute__((constructor))
359static void __http_rules_init(void)
360{
361}
362
363/*
364 * Local variables:
365 * c-indent-level: 8
366 * c-basic-offset: 8
367 * End:
368 */