blob: 0ded6bf82b7ca06f1521ef9c8ea5ca8a5f911a9c [file] [log] [blame]
Willy Tarreaua84d3742007-05-07 00:36:48 +02001/*
2 * ACL management functions.
3 *
Willy Tarreau11382812008-07-09 16:18:21 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaua84d3742007-05-07 00:36:48 +02005 *
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
Willy Tarreauae8b7962007-06-09 23:10:04 +020013#include <ctype.h>
Willy Tarreaua84d3742007-05-07 00:36:48 +020014#include <stdio.h>
15#include <string.h>
16
17#include <common/config.h>
18#include <common/mini-clist.h>
19#include <common/standard.h>
20
21#include <proto/acl.h>
Willy Tarreau404e8ab2009-07-26 19:40:40 +020022#include <proto/log.h>
Willy Tarreaua84d3742007-05-07 00:36:48 +020023
Willy Tarreaua9802632008-07-25 19:13:19 +020024/* The capabilities of filtering hooks describe the type of information
25 * available to each of them.
26 */
27const unsigned int filt_cap[] = {
28 [ACL_HOOK_REQ_FE_TCP] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY,
29 [ACL_HOOK_REQ_FE_TCP_CONTENT] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY,
30 [ACL_HOOK_REQ_FE_HTTP_IN] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
31 [ACL_HOOK_REQ_FE_SWITCH] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
32 [ACL_HOOK_REQ_BE_TCP_CONTENT] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
33 [ACL_HOOK_REQ_BE_HTTP_IN] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
34 [ACL_HOOK_REQ_BE_SWITCH] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
35 [ACL_HOOK_REQ_FE_HTTP_OUT] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
36 [ACL_HOOK_REQ_BE_HTTP_OUT] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY|ACL_USE_L7REQ_ANY|ACL_USE_HDR_ANY,
37
38 [ACL_HOOK_RTR_BE_TCP_CONTENT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY,
39 [ACL_HOOK_RTR_BE_HTTP_IN] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
40 [ACL_HOOK_RTR_FE_TCP_CONTENT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
41 [ACL_HOOK_RTR_FE_HTTP_IN] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
42 [ACL_HOOK_RTR_BE_HTTP_OUT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
43 [ACL_HOOK_RTR_FE_HTTP_OUT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
44};
45
Willy Tarreaua84d3742007-05-07 00:36:48 +020046/* List head of all known ACL keywords */
47static struct acl_kw_list acl_keywords = {
48 .list = LIST_HEAD_INIT(acl_keywords.list)
49};
50
51
Willy Tarreaua5909832007-06-17 20:40:25 +020052/*
53 * These functions are only used for debugging complex configurations.
Willy Tarreaua84d3742007-05-07 00:36:48 +020054 */
Willy Tarreaua5909832007-06-17 20:40:25 +020055
Willy Tarreau58393e12008-07-20 10:39:22 +020056/* force TRUE to be returned at the fetch level */
Willy Tarreaua5909832007-06-17 20:40:25 +020057static int
Willy Tarreau58393e12008-07-20 10:39:22 +020058acl_fetch_true(struct proxy *px, struct session *l4, void *l7, int dir,
59 struct acl_expr *expr, struct acl_test *test)
Willy Tarreaua5909832007-06-17 20:40:25 +020060{
Willy Tarreau58393e12008-07-20 10:39:22 +020061 test->flags |= ACL_TEST_F_SET_RES_PASS;
Willy Tarreaua5909832007-06-17 20:40:25 +020062 return 1;
63}
64
Willy Tarreaub6fb4202008-07-20 11:18:28 +020065/* wait for more data as long as possible, then return TRUE. This should be
66 * used with content inspection.
67 */
68static int
69acl_fetch_wait_end(struct proxy *px, struct session *l4, void *l7, int dir,
70 struct acl_expr *expr, struct acl_test *test)
71{
72 if (dir & ACL_PARTIAL) {
73 test->flags |= ACL_TEST_F_MAY_CHANGE;
74 return 0;
75 }
76 test->flags |= ACL_TEST_F_SET_RES_PASS;
77 return 1;
78}
79
Willy Tarreau58393e12008-07-20 10:39:22 +020080/* force FALSE to be returned at the fetch level */
Willy Tarreaua5909832007-06-17 20:40:25 +020081static int
Willy Tarreau58393e12008-07-20 10:39:22 +020082acl_fetch_false(struct proxy *px, struct session *l4, void *l7, int dir,
83 struct acl_expr *expr, struct acl_test *test)
Willy Tarreaua84d3742007-05-07 00:36:48 +020084{
Willy Tarreau58393e12008-07-20 10:39:22 +020085 test->flags |= ACL_TEST_F_SET_RES_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +020086 return 1;
87}
88
Willy Tarreau58393e12008-07-20 10:39:22 +020089
90/*
91 * These functions are exported and may be used by any other component.
92 */
93
94/* ignore the current line */
95int acl_parse_nothing(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua5909832007-06-17 20:40:25 +020096{
Willy Tarreau58393e12008-07-20 10:39:22 +020097 return 1;
98}
99
100/* always fake a data retrieval */
101int acl_fetch_nothing(struct proxy *px, struct session *l4, void *l7, int dir,
102 struct acl_expr *expr, struct acl_test *test)
103{
104 return 1;
Willy Tarreaua5909832007-06-17 20:40:25 +0200105}
106
107/* always return false */
Willy Tarreau58393e12008-07-20 10:39:22 +0200108int acl_match_nothing(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua5909832007-06-17 20:40:25 +0200109{
Willy Tarreau11382812008-07-09 16:18:21 +0200110 return ACL_PAT_FAIL;
Willy Tarreaua5909832007-06-17 20:40:25 +0200111}
112
113
Willy Tarreaua84d3742007-05-07 00:36:48 +0200114/* NB: For two strings to be identical, it is required that their lengths match */
115int acl_match_str(struct acl_test *test, struct acl_pattern *pattern)
116{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200117 int icase;
118
Willy Tarreaua84d3742007-05-07 00:36:48 +0200119 if (pattern->len != test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200120 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200121
122 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
123 if ((icase && strncasecmp(pattern->ptr.str, test->ptr, test->len) == 0) ||
124 (!icase && strncmp(pattern->ptr.str, test->ptr, test->len) == 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200125 return ACL_PAT_PASS;
126 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200127}
128
Willy Tarreauf3d25982007-05-08 22:45:09 +0200129/* Executes a regex. It needs to change the data. If it is marked READ_ONLY
130 * then it will be allocated and duplicated in place so that others may use
131 * it later on. Note that this is embarrassing because we always try to avoid
132 * allocating memory at run time.
133 */
134int acl_match_reg(struct acl_test *test, struct acl_pattern *pattern)
135{
136 char old_char;
137 int ret;
138
139 if (unlikely(test->flags & ACL_TEST_F_READ_ONLY)) {
140 char *new_str;
141
142 new_str = calloc(1, test->len + 1);
143 if (!new_str)
Willy Tarreau11382812008-07-09 16:18:21 +0200144 return ACL_PAT_FAIL;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200145
146 memcpy(new_str, test->ptr, test->len);
147 new_str[test->len] = 0;
148 if (test->flags & ACL_TEST_F_MUST_FREE)
149 free(test->ptr);
150 test->ptr = new_str;
151 test->flags |= ACL_TEST_F_MUST_FREE;
152 test->flags &= ~ACL_TEST_F_READ_ONLY;
153 }
154
155 old_char = test->ptr[test->len];
156 test->ptr[test->len] = 0;
157
158 if (regexec(pattern->ptr.reg, test->ptr, 0, NULL, 0) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200159 ret = ACL_PAT_PASS;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200160 else
Willy Tarreau11382812008-07-09 16:18:21 +0200161 ret = ACL_PAT_FAIL;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200162
163 test->ptr[test->len] = old_char;
164 return ret;
165}
166
Willy Tarreaua84d3742007-05-07 00:36:48 +0200167/* Checks that the pattern matches the beginning of the tested string. */
168int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern)
169{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200170 int icase;
171
Willy Tarreaua84d3742007-05-07 00:36:48 +0200172 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200173 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200174
175 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
176 if ((icase && strncasecmp(pattern->ptr.str, test->ptr, pattern->len) != 0) ||
177 (!icase && strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200178 return ACL_PAT_FAIL;
179 return ACL_PAT_PASS;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200180}
181
182/* Checks that the pattern matches the end of the tested string. */
183int acl_match_end(struct acl_test *test, struct acl_pattern *pattern)
184{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200185 int icase;
186
Willy Tarreaua84d3742007-05-07 00:36:48 +0200187 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200188 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200189 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
190 if ((icase && strncasecmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0) ||
191 (!icase && strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200192 return ACL_PAT_FAIL;
193 return ACL_PAT_PASS;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200194}
195
196/* Checks that the pattern is included inside the tested string.
197 * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
198 */
199int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern)
200{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200201 int icase;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200202 char *end;
203 char *c;
204
205 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200206 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200207
208 end = test->ptr + test->len - pattern->len;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200209 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
210 if (icase) {
211 for (c = test->ptr; c <= end; c++) {
212 if (tolower(*c) != tolower(*pattern->ptr.str))
213 continue;
214 if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200215 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200216 }
217 } else {
218 for (c = test->ptr; c <= end; c++) {
219 if (*c != *pattern->ptr.str)
220 continue;
221 if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200222 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200223 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200224 }
Willy Tarreau11382812008-07-09 16:18:21 +0200225 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200226}
227
228/* This one is used by other real functions. It checks that the pattern is
229 * included inside the tested string, but enclosed between the specified
230 * delimitor, or a '/' or a '?' or at the beginning or end of the string.
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200231 * The delimitor is stripped at the beginning or end of the pattern.
Willy Tarreaua84d3742007-05-07 00:36:48 +0200232 */
233static int match_word(struct acl_test *test, struct acl_pattern *pattern, char delim)
234{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200235 int may_match, icase;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200236 char *c, *end;
237 char *ps;
238 int pl;
239
240 pl = pattern->len;
241 ps = pattern->ptr.str;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200242 while (pl > 0 && (*ps == delim || *ps == '/' || *ps == '?')) {
Willy Tarreaua84d3742007-05-07 00:36:48 +0200243 pl--;
244 ps++;
245 }
246
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200247 while (pl > 0 &&
248 (ps[pl - 1] == delim || ps[pl - 1] == '/' || ps[pl - 1] == '?'))
Willy Tarreaua84d3742007-05-07 00:36:48 +0200249 pl--;
250
251 if (pl > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200252 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200253
254 may_match = 1;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200255 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200256 end = test->ptr + test->len - pl;
257 for (c = test->ptr; c <= end; c++) {
258 if (*c == '/' || *c == delim || *c == '?') {
259 may_match = 1;
260 continue;
261 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200262
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200263 if (!may_match)
264 continue;
265
266 if (icase) {
267 if ((tolower(*c) == tolower(*ps)) &&
268 (strncasecmp(ps, c, pl) == 0) &&
269 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
Willy Tarreau11382812008-07-09 16:18:21 +0200270 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200271 } else {
272 if ((*c == *ps) &&
273 (strncmp(ps, c, pl) == 0) &&
274 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
Willy Tarreau11382812008-07-09 16:18:21 +0200275 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200276 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200277 may_match = 0;
278 }
Willy Tarreau11382812008-07-09 16:18:21 +0200279 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200280}
281
282/* Checks that the pattern is included inside the tested string, but enclosed
283 * between slashes or at the beginning or end of the string. Slashes at the
284 * beginning or end of the pattern are ignored.
285 */
286int acl_match_dir(struct acl_test *test, struct acl_pattern *pattern)
287{
288 return match_word(test, pattern, '/');
289}
290
291/* Checks that the pattern is included inside the tested string, but enclosed
292 * between dots or at the beginning or end of the string. Dots at the beginning
293 * or end of the pattern are ignored.
294 */
295int acl_match_dom(struct acl_test *test, struct acl_pattern *pattern)
296{
297 return match_word(test, pattern, '.');
298}
299
300/* Checks that the integer in <test> is included between min and max */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200301int acl_match_int(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200302{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200303 if ((!pattern->val.range.min_set || pattern->val.range.min <= test->i) &&
304 (!pattern->val.range.max_set || test->i <= pattern->val.range.max))
Willy Tarreau11382812008-07-09 16:18:21 +0200305 return ACL_PAT_PASS;
306 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200307}
308
Willy Tarreaua67fad92007-05-08 19:50:09 +0200309int acl_match_ip(struct acl_test *test, struct acl_pattern *pattern)
310{
311 struct in_addr *s;
312
313 if (test->i != AF_INET)
Willy Tarreau11382812008-07-09 16:18:21 +0200314 return ACL_PAT_FAIL;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200315
316 s = (void *)test->ptr;
317 if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200318 return ACL_PAT_PASS;
319 return ACL_PAT_FAIL;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200320}
321
Willy Tarreaua84d3742007-05-07 00:36:48 +0200322/* Parse a string. It is allocated and duplicated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200323int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200324{
325 int len;
326
Willy Tarreauae8b7962007-06-09 23:10:04 +0200327 len = strlen(*text);
328 pattern->ptr.str = strdup(*text);
Willy Tarreaua84d3742007-05-07 00:36:48 +0200329 if (!pattern->ptr.str)
330 return 0;
331 pattern->len = len;
332 return 1;
333}
334
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200335/* Free data allocated by acl_parse_reg */
336static void acl_free_reg(void *ptr) {
337
338 regfree((regex_t *)ptr);
339}
340
Willy Tarreauf3d25982007-05-08 22:45:09 +0200341/* Parse a regex. It is allocated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200342int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreauf3d25982007-05-08 22:45:09 +0200343{
344 regex_t *preg;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200345 int icase;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200346
347 preg = calloc(1, sizeof(regex_t));
348
349 if (!preg)
350 return 0;
351
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200352 icase = (pattern->flags & ACL_PAT_F_IGNORE_CASE) ? REG_ICASE : 0;
353 if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB | icase) != 0) {
Willy Tarreauf3d25982007-05-08 22:45:09 +0200354 free(preg);
355 return 0;
356 }
357
358 pattern->ptr.reg = preg;
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200359 pattern->freeptrbuf = &acl_free_reg;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200360 return 1;
361}
362
Willy Tarreauae8b7962007-06-09 23:10:04 +0200363/* Parse a range of positive integers delimited by either ':' or '-'. If only
364 * one integer is read, it is set as both min and max. An operator may be
365 * specified as the prefix, among this list of 5 :
366 *
367 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
368 *
369 * The default operator is "eq". It supports range matching. Ranges are
370 * rejected for other operators. The operator may be changed at any time.
371 * The operator is stored in the 'opaque' argument.
372 *
Willy Tarreaua84d3742007-05-07 00:36:48 +0200373 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200374int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200375{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200376 signed long long i;
377 unsigned int j, last, skip = 0;
378 const char *ptr = *text;
379
380
Willy Tarreau8f8e6452007-06-17 21:51:38 +0200381 while (!isdigit((unsigned char)*ptr)) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200382 if (strcmp(ptr, "eq") == 0) *opaque = 0;
383 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
384 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
385 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
386 else if (strcmp(ptr, "le") == 0) *opaque = 4;
387 else
388 return 0;
389
390 skip++;
391 ptr = text[skip];
392 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200393
394 last = i = 0;
395 while (1) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200396 j = *ptr++;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200397 if ((j == '-' || j == ':') && !last) {
398 last++;
399 pattern->val.range.min = i;
400 i = 0;
401 continue;
402 }
403 j -= '0';
404 if (j > 9)
405 // also catches the terminating zero
406 break;
407 i *= 10;
408 i += j;
409 }
Willy Tarreauae8b7962007-06-09 23:10:04 +0200410
411 if (last && *opaque >= 1 && *opaque <= 4)
412 /* having a range with a min or a max is absurd */
413 return 0;
414
Willy Tarreaua84d3742007-05-07 00:36:48 +0200415 if (!last)
416 pattern->val.range.min = i;
417 pattern->val.range.max = i;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200418
419 switch (*opaque) {
420 case 0: /* eq */
421 pattern->val.range.min_set = 1;
422 pattern->val.range.max_set = 1;
423 break;
424 case 1: /* gt */
425 pattern->val.range.min++; /* gt = ge + 1 */
426 case 2: /* ge */
427 pattern->val.range.min_set = 1;
428 pattern->val.range.max_set = 0;
429 break;
430 case 3: /* lt */
431 pattern->val.range.max--; /* lt = le - 1 */
432 case 4: /* le */
433 pattern->val.range.min_set = 0;
434 pattern->val.range.max_set = 1;
435 break;
436 }
437 return skip + 1;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200438}
439
Willy Tarreau4a26d2f2008-07-15 16:05:33 +0200440/* Parse a range of positive 2-component versions delimited by either ':' or
441 * '-'. The version consists in a major and a minor, both of which must be
442 * smaller than 65536, because internally they will be represented as a 32-bit
443 * integer.
444 * If only one version is read, it is set as both min and max. Just like for
445 * pure integers, an operator may be specified as the prefix, among this list
446 * of 5 :
447 *
448 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
449 *
450 * The default operator is "eq". It supports range matching. Ranges are
451 * rejected for other operators. The operator may be changed at any time.
452 * The operator is stored in the 'opaque' argument. This allows constructs
453 * such as the following one :
454 *
455 * acl obsolete_ssl ssl_req_proto lt 3
456 * acl unsupported_ssl ssl_req_proto gt 3.1
457 * acl valid_ssl ssl_req_proto 3.0-3.1
458 *
459 */
460int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, int *opaque)
461{
462 signed long long i;
463 unsigned int j, last, skip = 0;
464 const char *ptr = *text;
465
466
467 while (!isdigit((unsigned char)*ptr)) {
468 if (strcmp(ptr, "eq") == 0) *opaque = 0;
469 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
470 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
471 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
472 else if (strcmp(ptr, "le") == 0) *opaque = 4;
473 else
474 return 0;
475
476 skip++;
477 ptr = text[skip];
478 }
479
480 last = i = 0;
481 while (1) {
482 j = *ptr++;
483 if (j == '.') {
484 /* minor part */
485 if (i >= 65536)
486 return 0;
487 i <<= 16;
488 continue;
489 }
490 if ((j == '-' || j == ':') && !last) {
491 last++;
492 if (i < 65536)
493 i <<= 16;
494 pattern->val.range.min = i;
495 i = 0;
496 continue;
497 }
498 j -= '0';
499 if (j > 9)
500 // also catches the terminating zero
501 break;
502 i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10;
503 i += j;
504 }
505
506 /* if we only got a major version, let's shift it now */
507 if (i < 65536)
508 i <<= 16;
509
510 if (last && *opaque >= 1 && *opaque <= 4)
511 /* having a range with a min or a max is absurd */
512 return 0;
513
514 if (!last)
515 pattern->val.range.min = i;
516 pattern->val.range.max = i;
517
518 switch (*opaque) {
519 case 0: /* eq */
520 pattern->val.range.min_set = 1;
521 pattern->val.range.max_set = 1;
522 break;
523 case 1: /* gt */
524 pattern->val.range.min++; /* gt = ge + 1 */
525 case 2: /* ge */
526 pattern->val.range.min_set = 1;
527 pattern->val.range.max_set = 0;
528 break;
529 case 3: /* lt */
530 pattern->val.range.max--; /* lt = le - 1 */
531 case 4: /* le */
532 pattern->val.range.min_set = 0;
533 pattern->val.range.max_set = 1;
534 break;
535 }
536 return skip + 1;
537}
538
Willy Tarreaua67fad92007-05-08 19:50:09 +0200539/* Parse an IP address and an optional mask in the form addr[/mask].
540 * The addr may either be an IPv4 address or a hostname. The mask
541 * may either be a dotted mask or a number of bits. Returns 1 if OK,
542 * otherwise 0.
543 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200544int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua67fad92007-05-08 19:50:09 +0200545{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200546 if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask))
547 return 1;
548 else
549 return 0;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200550}
551
Willy Tarreaua84d3742007-05-07 00:36:48 +0200552/*
553 * Registers the ACL keyword list <kwl> as a list of valid keywords for next
554 * parsing sessions.
555 */
556void acl_register_keywords(struct acl_kw_list *kwl)
557{
558 LIST_ADDQ(&acl_keywords.list, &kwl->list);
559}
560
561/*
562 * Unregisters the ACL keyword list <kwl> from the list of valid keywords.
563 */
564void acl_unregister_keywords(struct acl_kw_list *kwl)
565{
566 LIST_DEL(&kwl->list);
567 LIST_INIT(&kwl->list);
568}
569
570/* Return a pointer to the ACL <name> within the list starting at <head>, or
571 * NULL if not found.
572 */
573struct acl *find_acl_by_name(const char *name, struct list *head)
574{
575 struct acl *acl;
576 list_for_each_entry(acl, head, list) {
577 if (strcmp(acl->name, name) == 0)
578 return acl;
579 }
580 return NULL;
581}
582
583/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
584 * <kw> contains an opening parenthesis, only the left part of it is checked.
585 */
586struct acl_keyword *find_acl_kw(const char *kw)
587{
588 int index;
589 const char *kwend;
590 struct acl_kw_list *kwl;
591
592 kwend = strchr(kw, '(');
593 if (!kwend)
594 kwend = kw + strlen(kw);
595
596 list_for_each_entry(kwl, &acl_keywords.list, list) {
597 for (index = 0; kwl->kw[index].kw != NULL; index++) {
598 if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
599 kwl->kw[index].kw[kwend-kw] == 0)
600 return &kwl->kw[index];
601 }
602 }
603 return NULL;
604}
605
606static void free_pattern(struct acl_pattern *pat)
607{
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200608
609 if (pat->ptr.ptr) {
610 if (pat->freeptrbuf)
611 pat->freeptrbuf(pat->ptr.ptr);
612
Willy Tarreaua84d3742007-05-07 00:36:48 +0200613 free(pat->ptr.ptr);
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200614 }
615
Willy Tarreaua84d3742007-05-07 00:36:48 +0200616 free(pat);
617}
618
619static void free_pattern_list(struct list *head)
620{
621 struct acl_pattern *pat, *tmp;
622 list_for_each_entry_safe(pat, tmp, head, list)
623 free_pattern(pat);
624}
625
626static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
627{
628 free_pattern_list(&expr->patterns);
629 LIST_INIT(&expr->patterns);
630 if (expr->arg.str)
631 free(expr->arg.str);
632 expr->kw->use_cnt--;
633 return expr;
634}
635
636/* Parse an ACL expression starting at <args>[0], and return it.
637 * Right now, the only accepted syntax is :
638 * <subject> [<value>...]
639 */
640struct acl_expr *parse_acl_expr(const char **args)
641{
642 __label__ out_return, out_free_expr, out_free_pattern;
643 struct acl_expr *expr;
644 struct acl_keyword *aclkw;
645 struct acl_pattern *pattern;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200646 int opaque, patflags;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200647 const char *arg;
648
649 aclkw = find_acl_kw(args[0]);
650 if (!aclkw || !aclkw->parse)
651 goto out_return;
652
653 expr = (struct acl_expr *)calloc(1, sizeof(*expr));
654 if (!expr)
655 goto out_return;
656
657 expr->kw = aclkw;
658 aclkw->use_cnt++;
659 LIST_INIT(&expr->patterns);
660 expr->arg.str = NULL;
Willy Tarreaubb768912007-06-10 11:17:01 +0200661 expr->arg_len = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200662
663 arg = strchr(args[0], '(');
664 if (arg != NULL) {
665 char *end, *arg2;
666 /* there is an argument in the form "subject(arg)" */
667 arg++;
668 end = strchr(arg, ')');
669 if (!end)
670 goto out_free_expr;
671 arg2 = (char *)calloc(1, end - arg + 1);
672 if (!arg2)
673 goto out_free_expr;
674 memcpy(arg2, arg, end - arg);
675 arg2[end-arg] = '\0';
Willy Tarreaubb768912007-06-10 11:17:01 +0200676 expr->arg_len = end - arg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200677 expr->arg.str = arg2;
678 }
679
Willy Tarreaua84d3742007-05-07 00:36:48 +0200680 args++;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200681
682 /* check for options before patterns. Supported options are :
683 * -i : ignore case for all patterns by default
684 * -f : read patterns from those files
685 * -- : everything after this is not an option
686 */
687 patflags = 0;
688 while (**args == '-') {
689 if ((*args)[1] == 'i')
690 patflags |= ACL_PAT_F_IGNORE_CASE;
691 else if ((*args)[1] == 'f')
692 patflags |= ACL_PAT_F_FROM_FILE;
693 else if ((*args)[1] == '-') {
694 args++;
695 break;
696 }
697 else
698 break;
699 args++;
700 }
701
702 /* now parse all patterns */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200703 opaque = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200704 while (**args) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200705 int ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200706 pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
707 if (!pattern)
708 goto out_free_expr;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200709 pattern->flags = patflags;
710
Willy Tarreauae8b7962007-06-09 23:10:04 +0200711 ret = aclkw->parse(args, pattern, &opaque);
712 if (!ret)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200713 goto out_free_pattern;
714 LIST_ADDQ(&expr->patterns, &pattern->list);
Willy Tarreauae8b7962007-06-09 23:10:04 +0200715 args += ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200716 }
717
718 return expr;
719
720 out_free_pattern:
721 free_pattern(pattern);
722 out_free_expr:
723 prune_acl_expr(expr);
724 free(expr);
725 out_return:
726 return NULL;
727}
728
Krzysztof Piotr Oledzkia643baf2008-05-29 23:53:44 +0200729/* Purge everything in the acl <acl>, then return <acl>. */
730struct acl *prune_acl(struct acl *acl) {
731
732 struct acl_expr *expr, *exprb;
733
734 free(acl->name);
735
736 list_for_each_entry_safe(expr, exprb, &acl->expr, list) {
737 LIST_DEL(&expr->list);
738 prune_acl_expr(expr);
739 free(expr);
740 }
741
742 return acl;
743}
744
Willy Tarreaua84d3742007-05-07 00:36:48 +0200745/* Parse an ACL with the name starting at <args>[0], and with a list of already
746 * known ACLs in <acl>. If the ACL was not in the list, it will be added.
747 * A pointer to that ACL is returned.
748 *
749 * args syntax: <aclname> <acl_expr>
750 */
751struct acl *parse_acl(const char **args, struct list *known_acl)
752{
753 __label__ out_return, out_free_acl_expr, out_free_name;
754 struct acl *cur_acl;
755 struct acl_expr *acl_expr;
756 char *name;
757
Willy Tarreau2e74c3f2007-12-02 18:45:09 +0100758 if (invalid_char(*args))
759 goto out_return;
760
Willy Tarreaua84d3742007-05-07 00:36:48 +0200761 acl_expr = parse_acl_expr(args + 1);
762 if (!acl_expr)
763 goto out_return;
764
Willy Tarreau404e8ab2009-07-26 19:40:40 +0200765 /* Check for args beginning with an opening parenthesis just after the
766 * subject, as this is almost certainly a typo. Right now we can only
767 * emit a warning, so let's do so.
768 */
Krzysztof Piotr Oledzki4cdd8312009-10-05 00:23:35 +0200769 if (!strchr(args[1], '(') && *args[2] == '(')
Willy Tarreau404e8ab2009-07-26 19:40:40 +0200770 Warning("parsing acl '%s' :\n"
771 " matching '%s' for pattern '%s' is likely a mistake and probably\n"
772 " not what you want. Maybe you need to remove the extraneous space before '('.\n"
773 " If you are really sure this is not an error, please insert '--' between the\n"
774 " match and the pattern to make this warning message disappear.\n",
775 args[0], args[1], args[2]);
776
Willy Tarreaua84d3742007-05-07 00:36:48 +0200777 cur_acl = find_acl_by_name(args[0], known_acl);
778 if (!cur_acl) {
779 name = strdup(args[0]);
780 if (!name)
781 goto out_free_acl_expr;
782 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
783 if (cur_acl == NULL)
784 goto out_free_name;
785
786 LIST_INIT(&cur_acl->expr);
787 LIST_ADDQ(known_acl, &cur_acl->list);
788 cur_acl->name = name;
789 }
790
Willy Tarreaua9802632008-07-25 19:13:19 +0200791 cur_acl->requires |= acl_expr->kw->requires;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200792 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
793 return cur_acl;
794
795 out_free_name:
796 free(name);
797 out_free_acl_expr:
798 prune_acl_expr(acl_expr);
799 free(acl_expr);
800 out_return:
801 return NULL;
802}
803
Willy Tarreau16fbe822007-06-17 11:54:31 +0200804/* Some useful ACLs provided by default. Only those used are allocated. */
805
806const struct {
807 const char *name;
808 const char *expr[4]; /* put enough for longest expression */
809} default_acl_list[] = {
Willy Tarreau58393e12008-07-20 10:39:22 +0200810 { .name = "TRUE", .expr = {"always_true",""}},
811 { .name = "FALSE", .expr = {"always_false",""}},
Willy Tarreau16fbe822007-06-17 11:54:31 +0200812 { .name = "LOCALHOST", .expr = {"src","127.0.0.1/8",""}},
Willy Tarreau2492d5b2009-07-11 00:06:00 +0200813 { .name = "HTTP", .expr = {"req_proto_http",""}},
Willy Tarreau16fbe822007-06-17 11:54:31 +0200814 { .name = "HTTP_1.0", .expr = {"req_ver","1.0",""}},
815 { .name = "HTTP_1.1", .expr = {"req_ver","1.1",""}},
816 { .name = "METH_CONNECT", .expr = {"method","CONNECT",""}},
817 { .name = "METH_GET", .expr = {"method","GET","HEAD",""}},
818 { .name = "METH_HEAD", .expr = {"method","HEAD",""}},
819 { .name = "METH_OPTIONS", .expr = {"method","OPTIONS",""}},
820 { .name = "METH_POST", .expr = {"method","POST",""}},
821 { .name = "METH_TRACE", .expr = {"method","TRACE",""}},
822 { .name = "HTTP_URL_ABS", .expr = {"url_reg","^[^/:]*://",""}},
823 { .name = "HTTP_URL_SLASH", .expr = {"url_beg","/",""}},
824 { .name = "HTTP_URL_STAR", .expr = {"url","*",""}},
825 { .name = "HTTP_CONTENT", .expr = {"hdr_val(content-length)","gt","0",""}},
Emeric Brunbede3d02009-06-30 17:54:00 +0200826 { .name = "RDP_COOKIE", .expr = {"req_rdp_cookie_cnt","gt","0",""}},
Willy Tarreauc6317702008-07-20 09:29:50 +0200827 { .name = "REQ_CONTENT", .expr = {"req_len","gt","0",""}},
Willy Tarreaub6fb4202008-07-20 11:18:28 +0200828 { .name = "WAIT_END", .expr = {"wait_end",""}},
Willy Tarreau16fbe822007-06-17 11:54:31 +0200829 { .name = NULL, .expr = {""}}
830};
831
832/* Find a default ACL from the default_acl list, compile it and return it.
833 * If the ACL is not found, NULL is returned. In theory, it cannot fail,
834 * except when default ACLs are broken, in which case it will return NULL.
835 * If <known_acl> is not NULL, the ACL will be queued at its tail.
836 */
837struct acl *find_acl_default(const char *acl_name, struct list *known_acl)
838{
839 __label__ out_return, out_free_acl_expr, out_free_name;
840 struct acl *cur_acl;
841 struct acl_expr *acl_expr;
842 char *name;
843 int index;
844
845 for (index = 0; default_acl_list[index].name != NULL; index++) {
846 if (strcmp(acl_name, default_acl_list[index].name) == 0)
847 break;
848 }
849
850 if (default_acl_list[index].name == NULL)
851 return NULL;
852
853 acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr);
854 if (!acl_expr)
855 goto out_return;
856
857 name = strdup(acl_name);
858 if (!name)
859 goto out_free_acl_expr;
860 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
861 if (cur_acl == NULL)
862 goto out_free_name;
863
864 cur_acl->name = name;
Willy Tarreaua55b7dc2009-07-12 09:21:30 +0200865 cur_acl->requires |= acl_expr->kw->requires;
Willy Tarreau16fbe822007-06-17 11:54:31 +0200866 LIST_INIT(&cur_acl->expr);
867 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
868 if (known_acl)
869 LIST_ADDQ(known_acl, &cur_acl->list);
870
871 return cur_acl;
872
873 out_free_name:
874 free(name);
875 out_free_acl_expr:
876 prune_acl_expr(acl_expr);
877 free(acl_expr);
878 out_return:
879 return NULL;
880}
Willy Tarreaua84d3742007-05-07 00:36:48 +0200881
882/* Purge everything in the acl_cond <cond>, then return <cond>. */
883struct acl_cond *prune_acl_cond(struct acl_cond *cond)
884{
885 struct acl_term_suite *suite, *tmp_suite;
886 struct acl_term *term, *tmp_term;
887
888 /* iterate through all term suites and free all terms and all suites */
889 list_for_each_entry_safe(suite, tmp_suite, &cond->suites, list) {
890 list_for_each_entry_safe(term, tmp_term, &suite->terms, list)
891 free(term);
892 free(suite);
893 }
894 return cond;
895}
896
897/* Parse an ACL condition starting at <args>[0], relying on a list of already
898 * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
899 * case of low memory). Supports multiple conditions separated by "or".
900 */
901struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol)
902{
903 __label__ out_return, out_free_suite, out_free_term;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200904 int arg, neg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200905 const char *word;
906 struct acl *cur_acl;
907 struct acl_term *cur_term;
908 struct acl_term_suite *cur_suite;
909 struct acl_cond *cond;
910
911 cond = (struct acl_cond *)calloc(1, sizeof(*cond));
912 if (cond == NULL)
913 goto out_return;
914
915 LIST_INIT(&cond->list);
916 LIST_INIT(&cond->suites);
917 cond->pol = pol;
918
919 cur_suite = NULL;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200920 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200921 for (arg = 0; *args[arg]; arg++) {
922 word = args[arg];
923
924 /* remove as many exclamation marks as we can */
925 while (*word == '!') {
926 neg = !neg;
927 word++;
928 }
929
930 /* an empty word is allowed because we cannot force the user to
931 * always think about not leaving exclamation marks alone.
932 */
933 if (!*word)
934 continue;
935
Willy Tarreau16fbe822007-06-17 11:54:31 +0200936 if (strcasecmp(word, "or") == 0 || strcmp(word, "||") == 0) {
Willy Tarreaua84d3742007-05-07 00:36:48 +0200937 /* new term suite */
938 cur_suite = NULL;
939 neg = 0;
940 continue;
941 }
942
Willy Tarreau16fbe822007-06-17 11:54:31 +0200943 /* search for <word> in the known ACL names. If we do not find
944 * it, let's look for it in the default ACLs, and if found, add
945 * it to the list of ACLs of this proxy. This makes it possible
946 * to override them.
947 */
Willy Tarreaua84d3742007-05-07 00:36:48 +0200948 cur_acl = find_acl_by_name(word, known_acl);
Willy Tarreau16fbe822007-06-17 11:54:31 +0200949 if (cur_acl == NULL) {
950 cur_acl = find_acl_default(word, known_acl);
951 if (cur_acl == NULL)
952 goto out_free_suite;
953 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200954
955 cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term));
956 if (cur_term == NULL)
957 goto out_free_suite;
958
959 cur_term->acl = cur_acl;
960 cur_term->neg = neg;
Willy Tarreaua9802632008-07-25 19:13:19 +0200961 cond->requires |= cur_acl->requires;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200962
963 if (!cur_suite) {
964 cur_suite = (struct acl_term_suite *)calloc(1, sizeof(*cur_suite));
965 if (cur_term == NULL)
966 goto out_free_term;
967 LIST_INIT(&cur_suite->terms);
968 LIST_ADDQ(&cond->suites, &cur_suite->list);
969 }
970 LIST_ADDQ(&cur_suite->terms, &cur_term->list);
Willy Tarreau74b98a82007-06-16 19:35:18 +0200971 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200972 }
973
974 return cond;
975
976 out_free_term:
977 free(cur_term);
978 out_free_suite:
979 prune_acl_cond(cond);
980 free(cond);
981 out_return:
982 return NULL;
983}
984
Willy Tarreau11382812008-07-09 16:18:21 +0200985/* Execute condition <cond> and return either ACL_PAT_FAIL, ACL_PAT_MISS or
Willy Tarreaub6866442008-07-14 23:54:42 +0200986 * ACL_PAT_PASS depending on the test results. ACL_PAT_MISS may only be
987 * returned if <dir> contains ACL_PARTIAL, indicating that incomplete data
988 * is being examined.
989 * This function only computes the condition, it does not apply the polarity
990 * required by IF/UNLESS, it's up to the caller to do this using something like
991 * this :
Willy Tarreau11382812008-07-09 16:18:21 +0200992 *
993 * res = acl_pass(res);
Willy Tarreaub6866442008-07-14 23:54:42 +0200994 * if (res == ACL_PAT_MISS)
995 * return 0;
Willy Tarreau11382812008-07-09 16:18:21 +0200996 * if (cond->pol == ACL_COND_UNLESS)
997 * res = !res;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200998 */
Willy Tarreaud41f8d82007-06-10 10:06:18 +0200999int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, void *l7, int dir)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001000{
1001 __label__ fetch_next;
1002 struct acl_term_suite *suite;
1003 struct acl_term *term;
1004 struct acl_expr *expr;
1005 struct acl *acl;
1006 struct acl_pattern *pattern;
1007 struct acl_test test;
Willy Tarreau11382812008-07-09 16:18:21 +02001008 int acl_res, suite_res, cond_res;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001009
Willy Tarreau11382812008-07-09 16:18:21 +02001010 /* We're doing a logical OR between conditions so we initialize to FAIL.
1011 * The MISS status is propagated down from the suites.
1012 */
Willy Tarreaua84d3742007-05-07 00:36:48 +02001013 cond_res = ACL_PAT_FAIL;
1014 list_for_each_entry(suite, &cond->suites, list) {
Willy Tarreau11382812008-07-09 16:18:21 +02001015 /* Evaluate condition suite <suite>. We stop at the first term
1016 * which returns ACL_PAT_FAIL. The MISS status is still propagated
1017 * in case of uncertainty in the result.
Willy Tarreaua84d3742007-05-07 00:36:48 +02001018 */
1019
1020 /* we're doing a logical AND between terms, so we must set the
1021 * initial value to PASS.
1022 */
1023 suite_res = ACL_PAT_PASS;
1024 list_for_each_entry(term, &suite->terms, list) {
1025 acl = term->acl;
1026
1027 /* FIXME: use cache !
1028 * check acl->cache_idx for this.
1029 */
1030
1031 /* ACL result not cached. Let's scan all the expressions
1032 * and use the first one to match.
1033 */
1034 acl_res = ACL_PAT_FAIL;
1035 list_for_each_entry(expr, &acl->expr, list) {
Willy Tarreaud41f8d82007-06-10 10:06:18 +02001036 /* we need to reset context and flags */
1037 memset(&test, 0, sizeof(test));
Willy Tarreaua84d3742007-05-07 00:36:48 +02001038 fetch_next:
Willy Tarreaub6866442008-07-14 23:54:42 +02001039 if (!expr->kw->fetch(px, l4, l7, dir, expr, &test)) {
1040 /* maybe we could not fetch because of missing data */
1041 if (test.flags & ACL_TEST_F_MAY_CHANGE && dir & ACL_PARTIAL)
1042 acl_res |= ACL_PAT_MISS;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001043 continue;
Willy Tarreaub6866442008-07-14 23:54:42 +02001044 }
Willy Tarreaua84d3742007-05-07 00:36:48 +02001045
Willy Tarreaua79534f2008-07-20 10:13:37 +02001046 if (test.flags & ACL_TEST_F_RES_SET) {
1047 if (test.flags & ACL_TEST_F_RES_PASS)
1048 acl_res |= ACL_PAT_PASS;
1049 else
1050 acl_res |= ACL_PAT_FAIL;
1051 }
1052 else {
1053 /* call the match() function for all tests on this value */
1054 list_for_each_entry(pattern, &expr->patterns, list) {
1055 acl_res |= expr->kw->match(&test, pattern);
1056 if (acl_res == ACL_PAT_PASS)
1057 break;
1058 }
Willy Tarreaua84d3742007-05-07 00:36:48 +02001059 }
1060 /*
Willy Tarreau11382812008-07-09 16:18:21 +02001061 * OK now acl_res holds the result of this expression
1062 * as one of ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS.
Willy Tarreaua84d3742007-05-07 00:36:48 +02001063 *
Willy Tarreau11382812008-07-09 16:18:21 +02001064 * Then if (!MISS) we can cache the result, and put
Willy Tarreaua84d3742007-05-07 00:36:48 +02001065 * (test.flags & ACL_TEST_F_VOLATILE) in the cache flags.
1066 *
1067 * FIXME: implement cache.
1068 *
1069 */
1070
1071 /* now we may have some cleanup to do */
1072 if (test.flags & ACL_TEST_F_MUST_FREE) {
1073 free(test.ptr);
1074 test.len = 0;
1075 }
1076
Willy Tarreau11382812008-07-09 16:18:21 +02001077 /* we're ORing these terms, so a single PASS is enough */
1078 if (acl_res == ACL_PAT_PASS)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001079 break;
1080
Willy Tarreaua84d3742007-05-07 00:36:48 +02001081 if (test.flags & ACL_TEST_F_FETCH_MORE)
1082 goto fetch_next;
Willy Tarreaub6866442008-07-14 23:54:42 +02001083
1084 /* sometimes we know the fetched data is subject to change
1085 * later and give another chance for a new match (eg: request
1086 * size, time, ...)
1087 */
1088 if (test.flags & ACL_TEST_F_MAY_CHANGE && dir & ACL_PARTIAL)
1089 acl_res |= ACL_PAT_MISS;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001090 }
1091 /*
1092 * Here we have the result of an ACL (cached or not).
1093 * ACLs are combined, negated or not, to form conditions.
1094 */
1095
Willy Tarreaua84d3742007-05-07 00:36:48 +02001096 if (term->neg)
Willy Tarreau11382812008-07-09 16:18:21 +02001097 acl_res = acl_neg(acl_res);
Willy Tarreaua84d3742007-05-07 00:36:48 +02001098
1099 suite_res &= acl_res;
Willy Tarreau11382812008-07-09 16:18:21 +02001100
1101 /* we're ANDing these terms, so a single FAIL is enough */
1102 if (suite_res == ACL_PAT_FAIL)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001103 break;
1104 }
1105 cond_res |= suite_res;
Willy Tarreau11382812008-07-09 16:18:21 +02001106
1107 /* we're ORing these terms, so a single PASS is enough */
1108 if (cond_res == ACL_PAT_PASS)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001109 break;
1110 }
Willy Tarreau11382812008-07-09 16:18:21 +02001111 return cond_res;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001112}
1113
1114
Willy Tarreaudd64f8d2008-07-27 22:02:32 +02001115/* Reports a pointer to the first ACL used in condition <cond> which requires
1116 * at least one of the USE_FLAGS in <require>. Returns NULL if none matches.
1117 * The construct is almost the same as for acl_exec_cond() since we're walking
1118 * down the ACL tree as well. It is important that the tree is really walked
1119 * through and never cached, because that way, this function can be used as a
1120 * late check.
1121 */
1122struct acl *cond_find_require(struct acl_cond *cond, unsigned int require)
1123{
1124 struct acl_term_suite *suite;
1125 struct acl_term *term;
1126 struct acl *acl;
1127
1128 list_for_each_entry(suite, &cond->suites, list) {
1129 list_for_each_entry(term, &suite->terms, list) {
1130 acl = term->acl;
1131 if (acl->requires & require)
1132 return acl;
1133 }
1134 }
1135 return NULL;
1136}
1137
1138
Willy Tarreaua84d3742007-05-07 00:36:48 +02001139/************************************************************************/
1140/* All supported keywords must be declared here. */
1141/************************************************************************/
1142
1143/* Note: must not be declared <const> as its list will be overwritten */
1144static struct acl_kw_list acl_kws = {{ },{
Willy Tarreau0ceba5a2008-07-25 19:31:03 +02001145 { "always_true", acl_parse_nothing, acl_fetch_true, acl_match_nothing, ACL_USE_NOTHING },
1146 { "always_false", acl_parse_nothing, acl_fetch_false, acl_match_nothing, ACL_USE_NOTHING },
1147 { "wait_end", acl_parse_nothing, acl_fetch_wait_end, acl_match_nothing, ACL_USE_NOTHING },
Willy Tarreaua84d3742007-05-07 00:36:48 +02001148#if 0
1149 { "time", acl_parse_time, acl_fetch_time, acl_match_time },
1150#endif
1151 { NULL, NULL, NULL, NULL }
1152}};
1153
1154
1155__attribute__((constructor))
1156static void __acl_init(void)
1157{
1158 acl_register_keywords(&acl_kws);
1159}
1160
1161
1162/*
1163 * Local variables:
1164 * c-indent-level: 8
1165 * c-basic-offset: 8
1166 * End:
1167 */