blob: ffed8263cc8030c8e84d3489eb14a6b69adc2802 [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>
22
Willy Tarreaua9802632008-07-25 19:13:19 +020023/* The capabilities of filtering hooks describe the type of information
24 * available to each of them.
25 */
26const unsigned int filt_cap[] = {
27 [ACL_HOOK_REQ_FE_TCP] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY,
28 [ACL_HOOK_REQ_FE_TCP_CONTENT] = ACL_USE_TCP4_ANY|ACL_USE_TCP6_ANY|ACL_USE_TCP_ANY|ACL_USE_L4REQ_ANY,
29 [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,
30 [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,
31 [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,
32 [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,
33 [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,
34 [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,
35 [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,
36
37 [ACL_HOOK_RTR_BE_TCP_CONTENT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY,
38 [ACL_HOOK_RTR_BE_HTTP_IN] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
39 [ACL_HOOK_RTR_FE_TCP_CONTENT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
40 [ACL_HOOK_RTR_FE_HTTP_IN] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
41 [ACL_HOOK_RTR_BE_HTTP_OUT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
42 [ACL_HOOK_RTR_FE_HTTP_OUT] = ACL_USE_REQ_PERMANENT|ACL_USE_REQ_CACHEABLE|ACL_USE_L4RTR_ANY|ACL_USE_L7RTR_ANY,
43};
44
Willy Tarreaua84d3742007-05-07 00:36:48 +020045/* List head of all known ACL keywords */
46static struct acl_kw_list acl_keywords = {
47 .list = LIST_HEAD_INIT(acl_keywords.list)
48};
49
50
Willy Tarreaua5909832007-06-17 20:40:25 +020051/*
52 * These functions are only used for debugging complex configurations.
Willy Tarreaua84d3742007-05-07 00:36:48 +020053 */
Willy Tarreaua5909832007-06-17 20:40:25 +020054
Willy Tarreau58393e12008-07-20 10:39:22 +020055/* force TRUE to be returned at the fetch level */
Willy Tarreaua5909832007-06-17 20:40:25 +020056static int
Willy Tarreau58393e12008-07-20 10:39:22 +020057acl_fetch_true(struct proxy *px, struct session *l4, void *l7, int dir,
58 struct acl_expr *expr, struct acl_test *test)
Willy Tarreaua5909832007-06-17 20:40:25 +020059{
Willy Tarreau58393e12008-07-20 10:39:22 +020060 test->flags |= ACL_TEST_F_SET_RES_PASS;
Willy Tarreaua5909832007-06-17 20:40:25 +020061 return 1;
62}
63
Willy Tarreaub6fb4202008-07-20 11:18:28 +020064/* wait for more data as long as possible, then return TRUE. This should be
65 * used with content inspection.
66 */
67static int
68acl_fetch_wait_end(struct proxy *px, struct session *l4, void *l7, int dir,
69 struct acl_expr *expr, struct acl_test *test)
70{
71 if (dir & ACL_PARTIAL) {
72 test->flags |= ACL_TEST_F_MAY_CHANGE;
73 return 0;
74 }
75 test->flags |= ACL_TEST_F_SET_RES_PASS;
76 return 1;
77}
78
Willy Tarreau58393e12008-07-20 10:39:22 +020079/* force FALSE to be returned at the fetch level */
Willy Tarreaua5909832007-06-17 20:40:25 +020080static int
Willy Tarreau58393e12008-07-20 10:39:22 +020081acl_fetch_false(struct proxy *px, struct session *l4, void *l7, int dir,
82 struct acl_expr *expr, struct acl_test *test)
Willy Tarreaua84d3742007-05-07 00:36:48 +020083{
Willy Tarreau58393e12008-07-20 10:39:22 +020084 test->flags |= ACL_TEST_F_SET_RES_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +020085 return 1;
86}
87
Willy Tarreau58393e12008-07-20 10:39:22 +020088
89/*
90 * These functions are exported and may be used by any other component.
91 */
92
93/* ignore the current line */
94int acl_parse_nothing(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua5909832007-06-17 20:40:25 +020095{
Willy Tarreau58393e12008-07-20 10:39:22 +020096 return 1;
97}
98
99/* always fake a data retrieval */
100int acl_fetch_nothing(struct proxy *px, struct session *l4, void *l7, int dir,
101 struct acl_expr *expr, struct acl_test *test)
102{
103 return 1;
Willy Tarreaua5909832007-06-17 20:40:25 +0200104}
105
106/* always return false */
Willy Tarreau58393e12008-07-20 10:39:22 +0200107int acl_match_nothing(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua5909832007-06-17 20:40:25 +0200108{
Willy Tarreau11382812008-07-09 16:18:21 +0200109 return ACL_PAT_FAIL;
Willy Tarreaua5909832007-06-17 20:40:25 +0200110}
111
112
Willy Tarreaua84d3742007-05-07 00:36:48 +0200113/* NB: For two strings to be identical, it is required that their lengths match */
114int acl_match_str(struct acl_test *test, struct acl_pattern *pattern)
115{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200116 int icase;
117
Willy Tarreaua84d3742007-05-07 00:36:48 +0200118 if (pattern->len != test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200119 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200120
121 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
122 if ((icase && strncasecmp(pattern->ptr.str, test->ptr, test->len) == 0) ||
123 (!icase && strncmp(pattern->ptr.str, test->ptr, test->len) == 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200124 return ACL_PAT_PASS;
125 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200126}
127
Willy Tarreauf3d25982007-05-08 22:45:09 +0200128/* Executes a regex. It needs to change the data. If it is marked READ_ONLY
129 * then it will be allocated and duplicated in place so that others may use
130 * it later on. Note that this is embarrassing because we always try to avoid
131 * allocating memory at run time.
132 */
133int acl_match_reg(struct acl_test *test, struct acl_pattern *pattern)
134{
135 char old_char;
136 int ret;
137
138 if (unlikely(test->flags & ACL_TEST_F_READ_ONLY)) {
139 char *new_str;
140
141 new_str = calloc(1, test->len + 1);
142 if (!new_str)
Willy Tarreau11382812008-07-09 16:18:21 +0200143 return ACL_PAT_FAIL;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200144
145 memcpy(new_str, test->ptr, test->len);
146 new_str[test->len] = 0;
147 if (test->flags & ACL_TEST_F_MUST_FREE)
148 free(test->ptr);
149 test->ptr = new_str;
150 test->flags |= ACL_TEST_F_MUST_FREE;
151 test->flags &= ~ACL_TEST_F_READ_ONLY;
152 }
153
154 old_char = test->ptr[test->len];
155 test->ptr[test->len] = 0;
156
157 if (regexec(pattern->ptr.reg, test->ptr, 0, NULL, 0) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200158 ret = ACL_PAT_PASS;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200159 else
Willy Tarreau11382812008-07-09 16:18:21 +0200160 ret = ACL_PAT_FAIL;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200161
162 test->ptr[test->len] = old_char;
163 return ret;
164}
165
Willy Tarreaua84d3742007-05-07 00:36:48 +0200166/* Checks that the pattern matches the beginning of the tested string. */
167int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern)
168{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200169 int icase;
170
Willy Tarreaua84d3742007-05-07 00:36:48 +0200171 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200172 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200173
174 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
175 if ((icase && strncasecmp(pattern->ptr.str, test->ptr, pattern->len) != 0) ||
176 (!icase && strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200177 return ACL_PAT_FAIL;
178 return ACL_PAT_PASS;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200179}
180
181/* Checks that the pattern matches the end of the tested string. */
182int acl_match_end(struct acl_test *test, struct acl_pattern *pattern)
183{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200184 int icase;
185
Willy Tarreaua84d3742007-05-07 00:36:48 +0200186 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200187 return ACL_PAT_FAIL;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200188 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
189 if ((icase && strncasecmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0) ||
190 (!icase && strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0))
Willy Tarreau11382812008-07-09 16:18:21 +0200191 return ACL_PAT_FAIL;
192 return ACL_PAT_PASS;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200193}
194
195/* Checks that the pattern is included inside the tested string.
196 * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
197 */
198int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern)
199{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200200 int icase;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200201 char *end;
202 char *c;
203
204 if (pattern->len > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200205 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200206
207 end = test->ptr + test->len - pattern->len;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200208 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
209 if (icase) {
210 for (c = test->ptr; c <= end; c++) {
211 if (tolower(*c) != tolower(*pattern->ptr.str))
212 continue;
213 if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200214 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200215 }
216 } else {
217 for (c = test->ptr; c <= end; c++) {
218 if (*c != *pattern->ptr.str)
219 continue;
220 if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200221 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200222 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200223 }
Willy Tarreau11382812008-07-09 16:18:21 +0200224 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200225}
226
227/* This one is used by other real functions. It checks that the pattern is
228 * included inside the tested string, but enclosed between the specified
229 * delimitor, or a '/' or a '?' or at the beginning or end of the string.
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200230 * The delimitor is stripped at the beginning or end of the pattern.
Willy Tarreaua84d3742007-05-07 00:36:48 +0200231 */
232static int match_word(struct acl_test *test, struct acl_pattern *pattern, char delim)
233{
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200234 int may_match, icase;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200235 char *c, *end;
236 char *ps;
237 int pl;
238
239 pl = pattern->len;
240 ps = pattern->ptr.str;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200241 while (pl > 0 && (*ps == delim || *ps == '/' || *ps == '?')) {
Willy Tarreaua84d3742007-05-07 00:36:48 +0200242 pl--;
243 ps++;
244 }
245
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200246 while (pl > 0 &&
247 (ps[pl - 1] == delim || ps[pl - 1] == '/' || ps[pl - 1] == '?'))
Willy Tarreaua84d3742007-05-07 00:36:48 +0200248 pl--;
249
250 if (pl > test->len)
Willy Tarreau11382812008-07-09 16:18:21 +0200251 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200252
253 may_match = 1;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200254 icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200255 end = test->ptr + test->len - pl;
256 for (c = test->ptr; c <= end; c++) {
257 if (*c == '/' || *c == delim || *c == '?') {
258 may_match = 1;
259 continue;
260 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200261
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200262 if (!may_match)
263 continue;
264
265 if (icase) {
266 if ((tolower(*c) == tolower(*ps)) &&
267 (strncasecmp(ps, c, pl) == 0) &&
268 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
Willy Tarreau11382812008-07-09 16:18:21 +0200269 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200270 } else {
271 if ((*c == *ps) &&
272 (strncmp(ps, c, pl) == 0) &&
273 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
Willy Tarreau11382812008-07-09 16:18:21 +0200274 return ACL_PAT_PASS;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200275 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200276 may_match = 0;
277 }
Willy Tarreau11382812008-07-09 16:18:21 +0200278 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200279}
280
281/* Checks that the pattern is included inside the tested string, but enclosed
282 * between slashes or at the beginning or end of the string. Slashes at the
283 * beginning or end of the pattern are ignored.
284 */
285int acl_match_dir(struct acl_test *test, struct acl_pattern *pattern)
286{
287 return match_word(test, pattern, '/');
288}
289
290/* Checks that the pattern is included inside the tested string, but enclosed
291 * between dots or at the beginning or end of the string. Dots at the beginning
292 * or end of the pattern are ignored.
293 */
294int acl_match_dom(struct acl_test *test, struct acl_pattern *pattern)
295{
296 return match_word(test, pattern, '.');
297}
298
299/* Checks that the integer in <test> is included between min and max */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200300int acl_match_int(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200301{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200302 if ((!pattern->val.range.min_set || pattern->val.range.min <= test->i) &&
303 (!pattern->val.range.max_set || test->i <= pattern->val.range.max))
Willy Tarreau11382812008-07-09 16:18:21 +0200304 return ACL_PAT_PASS;
305 return ACL_PAT_FAIL;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200306}
307
Willy Tarreaua67fad92007-05-08 19:50:09 +0200308int acl_match_ip(struct acl_test *test, struct acl_pattern *pattern)
309{
310 struct in_addr *s;
311
312 if (test->i != AF_INET)
Willy Tarreau11382812008-07-09 16:18:21 +0200313 return ACL_PAT_FAIL;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200314
315 s = (void *)test->ptr;
316 if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
Willy Tarreau11382812008-07-09 16:18:21 +0200317 return ACL_PAT_PASS;
318 return ACL_PAT_FAIL;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200319}
320
Willy Tarreaua84d3742007-05-07 00:36:48 +0200321/* Parse a string. It is allocated and duplicated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200322int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200323{
324 int len;
325
Willy Tarreauae8b7962007-06-09 23:10:04 +0200326 len = strlen(*text);
327 pattern->ptr.str = strdup(*text);
Willy Tarreaua84d3742007-05-07 00:36:48 +0200328 if (!pattern->ptr.str)
329 return 0;
330 pattern->len = len;
331 return 1;
332}
333
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200334/* Free data allocated by acl_parse_reg */
335static void acl_free_reg(void *ptr) {
336
337 regfree((regex_t *)ptr);
338}
339
Willy Tarreauf3d25982007-05-08 22:45:09 +0200340/* Parse a regex. It is allocated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200341int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreauf3d25982007-05-08 22:45:09 +0200342{
343 regex_t *preg;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200344 int icase;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200345
346 preg = calloc(1, sizeof(regex_t));
347
348 if (!preg)
349 return 0;
350
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200351 icase = (pattern->flags & ACL_PAT_F_IGNORE_CASE) ? REG_ICASE : 0;
352 if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB | icase) != 0) {
Willy Tarreauf3d25982007-05-08 22:45:09 +0200353 free(preg);
354 return 0;
355 }
356
357 pattern->ptr.reg = preg;
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200358 pattern->freeptrbuf = &acl_free_reg;
Willy Tarreauf3d25982007-05-08 22:45:09 +0200359 return 1;
360}
361
Willy Tarreauae8b7962007-06-09 23:10:04 +0200362/* Parse a range of positive integers delimited by either ':' or '-'. If only
363 * one integer is read, it is set as both min and max. An operator may be
364 * specified as the prefix, among this list of 5 :
365 *
366 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
367 *
368 * The default operator is "eq". It supports range matching. Ranges are
369 * rejected for other operators. The operator may be changed at any time.
370 * The operator is stored in the 'opaque' argument.
371 *
Willy Tarreaua84d3742007-05-07 00:36:48 +0200372 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200373int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200374{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200375 signed long long i;
376 unsigned int j, last, skip = 0;
377 const char *ptr = *text;
378
379
Willy Tarreau8f8e6452007-06-17 21:51:38 +0200380 while (!isdigit((unsigned char)*ptr)) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200381 if (strcmp(ptr, "eq") == 0) *opaque = 0;
382 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
383 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
384 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
385 else if (strcmp(ptr, "le") == 0) *opaque = 4;
386 else
387 return 0;
388
389 skip++;
390 ptr = text[skip];
391 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200392
393 last = i = 0;
394 while (1) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200395 j = *ptr++;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200396 if ((j == '-' || j == ':') && !last) {
397 last++;
398 pattern->val.range.min = i;
399 i = 0;
400 continue;
401 }
402 j -= '0';
403 if (j > 9)
404 // also catches the terminating zero
405 break;
406 i *= 10;
407 i += j;
408 }
Willy Tarreauae8b7962007-06-09 23:10:04 +0200409
410 if (last && *opaque >= 1 && *opaque <= 4)
411 /* having a range with a min or a max is absurd */
412 return 0;
413
Willy Tarreaua84d3742007-05-07 00:36:48 +0200414 if (!last)
415 pattern->val.range.min = i;
416 pattern->val.range.max = i;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200417
418 switch (*opaque) {
419 case 0: /* eq */
420 pattern->val.range.min_set = 1;
421 pattern->val.range.max_set = 1;
422 break;
423 case 1: /* gt */
424 pattern->val.range.min++; /* gt = ge + 1 */
425 case 2: /* ge */
426 pattern->val.range.min_set = 1;
427 pattern->val.range.max_set = 0;
428 break;
429 case 3: /* lt */
430 pattern->val.range.max--; /* lt = le - 1 */
431 case 4: /* le */
432 pattern->val.range.min_set = 0;
433 pattern->val.range.max_set = 1;
434 break;
435 }
436 return skip + 1;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200437}
438
Willy Tarreau4a26d2f2008-07-15 16:05:33 +0200439/* Parse a range of positive 2-component versions delimited by either ':' or
440 * '-'. The version consists in a major and a minor, both of which must be
441 * smaller than 65536, because internally they will be represented as a 32-bit
442 * integer.
443 * If only one version is read, it is set as both min and max. Just like for
444 * pure integers, an operator may be specified as the prefix, among this list
445 * of 5 :
446 *
447 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
448 *
449 * The default operator is "eq". It supports range matching. Ranges are
450 * rejected for other operators. The operator may be changed at any time.
451 * The operator is stored in the 'opaque' argument. This allows constructs
452 * such as the following one :
453 *
454 * acl obsolete_ssl ssl_req_proto lt 3
455 * acl unsupported_ssl ssl_req_proto gt 3.1
456 * acl valid_ssl ssl_req_proto 3.0-3.1
457 *
458 */
459int acl_parse_dotted_ver(const char **text, struct acl_pattern *pattern, int *opaque)
460{
461 signed long long i;
462 unsigned int j, last, skip = 0;
463 const char *ptr = *text;
464
465
466 while (!isdigit((unsigned char)*ptr)) {
467 if (strcmp(ptr, "eq") == 0) *opaque = 0;
468 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
469 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
470 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
471 else if (strcmp(ptr, "le") == 0) *opaque = 4;
472 else
473 return 0;
474
475 skip++;
476 ptr = text[skip];
477 }
478
479 last = i = 0;
480 while (1) {
481 j = *ptr++;
482 if (j == '.') {
483 /* minor part */
484 if (i >= 65536)
485 return 0;
486 i <<= 16;
487 continue;
488 }
489 if ((j == '-' || j == ':') && !last) {
490 last++;
491 if (i < 65536)
492 i <<= 16;
493 pattern->val.range.min = i;
494 i = 0;
495 continue;
496 }
497 j -= '0';
498 if (j > 9)
499 // also catches the terminating zero
500 break;
501 i = (i & 0xFFFF0000) + (i & 0xFFFF) * 10;
502 i += j;
503 }
504
505 /* if we only got a major version, let's shift it now */
506 if (i < 65536)
507 i <<= 16;
508
509 if (last && *opaque >= 1 && *opaque <= 4)
510 /* having a range with a min or a max is absurd */
511 return 0;
512
513 if (!last)
514 pattern->val.range.min = i;
515 pattern->val.range.max = i;
516
517 switch (*opaque) {
518 case 0: /* eq */
519 pattern->val.range.min_set = 1;
520 pattern->val.range.max_set = 1;
521 break;
522 case 1: /* gt */
523 pattern->val.range.min++; /* gt = ge + 1 */
524 case 2: /* ge */
525 pattern->val.range.min_set = 1;
526 pattern->val.range.max_set = 0;
527 break;
528 case 3: /* lt */
529 pattern->val.range.max--; /* lt = le - 1 */
530 case 4: /* le */
531 pattern->val.range.min_set = 0;
532 pattern->val.range.max_set = 1;
533 break;
534 }
535 return skip + 1;
536}
537
Willy Tarreaua67fad92007-05-08 19:50:09 +0200538/* Parse an IP address and an optional mask in the form addr[/mask].
539 * The addr may either be an IPv4 address or a hostname. The mask
540 * may either be a dotted mask or a number of bits. Returns 1 if OK,
541 * otherwise 0.
542 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200543int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua67fad92007-05-08 19:50:09 +0200544{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200545 if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask))
546 return 1;
547 else
548 return 0;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200549}
550
Willy Tarreaua84d3742007-05-07 00:36:48 +0200551/*
552 * Registers the ACL keyword list <kwl> as a list of valid keywords for next
553 * parsing sessions.
554 */
555void acl_register_keywords(struct acl_kw_list *kwl)
556{
557 LIST_ADDQ(&acl_keywords.list, &kwl->list);
558}
559
560/*
561 * Unregisters the ACL keyword list <kwl> from the list of valid keywords.
562 */
563void acl_unregister_keywords(struct acl_kw_list *kwl)
564{
565 LIST_DEL(&kwl->list);
566 LIST_INIT(&kwl->list);
567}
568
569/* Return a pointer to the ACL <name> within the list starting at <head>, or
570 * NULL if not found.
571 */
572struct acl *find_acl_by_name(const char *name, struct list *head)
573{
574 struct acl *acl;
575 list_for_each_entry(acl, head, list) {
576 if (strcmp(acl->name, name) == 0)
577 return acl;
578 }
579 return NULL;
580}
581
582/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
583 * <kw> contains an opening parenthesis, only the left part of it is checked.
584 */
585struct acl_keyword *find_acl_kw(const char *kw)
586{
587 int index;
588 const char *kwend;
589 struct acl_kw_list *kwl;
590
591 kwend = strchr(kw, '(');
592 if (!kwend)
593 kwend = kw + strlen(kw);
594
595 list_for_each_entry(kwl, &acl_keywords.list, list) {
596 for (index = 0; kwl->kw[index].kw != NULL; index++) {
597 if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
598 kwl->kw[index].kw[kwend-kw] == 0)
599 return &kwl->kw[index];
600 }
601 }
602 return NULL;
603}
604
605static void free_pattern(struct acl_pattern *pat)
606{
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200607
608 if (pat->ptr.ptr) {
609 if (pat->freeptrbuf)
610 pat->freeptrbuf(pat->ptr.ptr);
611
Willy Tarreaua84d3742007-05-07 00:36:48 +0200612 free(pat->ptr.ptr);
Krzysztof Piotr Oledzki8001d612008-05-31 13:53:23 +0200613 }
614
Willy Tarreaua84d3742007-05-07 00:36:48 +0200615 free(pat);
616}
617
618static void free_pattern_list(struct list *head)
619{
620 struct acl_pattern *pat, *tmp;
621 list_for_each_entry_safe(pat, tmp, head, list)
622 free_pattern(pat);
623}
624
625static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
626{
627 free_pattern_list(&expr->patterns);
628 LIST_INIT(&expr->patterns);
629 if (expr->arg.str)
630 free(expr->arg.str);
631 expr->kw->use_cnt--;
632 return expr;
633}
634
635/* Parse an ACL expression starting at <args>[0], and return it.
636 * Right now, the only accepted syntax is :
637 * <subject> [<value>...]
638 */
639struct acl_expr *parse_acl_expr(const char **args)
640{
641 __label__ out_return, out_free_expr, out_free_pattern;
642 struct acl_expr *expr;
643 struct acl_keyword *aclkw;
644 struct acl_pattern *pattern;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200645 int opaque, patflags;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200646 const char *arg;
647
648 aclkw = find_acl_kw(args[0]);
649 if (!aclkw || !aclkw->parse)
650 goto out_return;
651
652 expr = (struct acl_expr *)calloc(1, sizeof(*expr));
653 if (!expr)
654 goto out_return;
655
656 expr->kw = aclkw;
657 aclkw->use_cnt++;
658 LIST_INIT(&expr->patterns);
659 expr->arg.str = NULL;
Willy Tarreaubb768912007-06-10 11:17:01 +0200660 expr->arg_len = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200661
662 arg = strchr(args[0], '(');
663 if (arg != NULL) {
664 char *end, *arg2;
665 /* there is an argument in the form "subject(arg)" */
666 arg++;
667 end = strchr(arg, ')');
668 if (!end)
669 goto out_free_expr;
670 arg2 = (char *)calloc(1, end - arg + 1);
671 if (!arg2)
672 goto out_free_expr;
673 memcpy(arg2, arg, end - arg);
674 arg2[end-arg] = '\0';
Willy Tarreaubb768912007-06-10 11:17:01 +0200675 expr->arg_len = end - arg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200676 expr->arg.str = arg2;
677 }
678
Willy Tarreaua84d3742007-05-07 00:36:48 +0200679 args++;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200680
681 /* check for options before patterns. Supported options are :
682 * -i : ignore case for all patterns by default
683 * -f : read patterns from those files
684 * -- : everything after this is not an option
685 */
686 patflags = 0;
687 while (**args == '-') {
688 if ((*args)[1] == 'i')
689 patflags |= ACL_PAT_F_IGNORE_CASE;
690 else if ((*args)[1] == 'f')
691 patflags |= ACL_PAT_F_FROM_FILE;
692 else if ((*args)[1] == '-') {
693 args++;
694 break;
695 }
696 else
697 break;
698 args++;
699 }
700
701 /* now parse all patterns */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200702 opaque = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200703 while (**args) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200704 int ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200705 pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
706 if (!pattern)
707 goto out_free_expr;
Willy Tarreauc8d7c962007-06-17 08:20:33 +0200708 pattern->flags = patflags;
709
Willy Tarreauae8b7962007-06-09 23:10:04 +0200710 ret = aclkw->parse(args, pattern, &opaque);
711 if (!ret)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200712 goto out_free_pattern;
713 LIST_ADDQ(&expr->patterns, &pattern->list);
Willy Tarreauae8b7962007-06-09 23:10:04 +0200714 args += ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200715 }
716
717 return expr;
718
719 out_free_pattern:
720 free_pattern(pattern);
721 out_free_expr:
722 prune_acl_expr(expr);
723 free(expr);
724 out_return:
725 return NULL;
726}
727
Krzysztof Piotr Oledzkia643baf2008-05-29 23:53:44 +0200728/* Purge everything in the acl <acl>, then return <acl>. */
729struct acl *prune_acl(struct acl *acl) {
730
731 struct acl_expr *expr, *exprb;
732
733 free(acl->name);
734
735 list_for_each_entry_safe(expr, exprb, &acl->expr, list) {
736 LIST_DEL(&expr->list);
737 prune_acl_expr(expr);
738 free(expr);
739 }
740
741 return acl;
742}
743
Willy Tarreaua84d3742007-05-07 00:36:48 +0200744/* Parse an ACL with the name starting at <args>[0], and with a list of already
745 * known ACLs in <acl>. If the ACL was not in the list, it will be added.
746 * A pointer to that ACL is returned.
747 *
748 * args syntax: <aclname> <acl_expr>
749 */
750struct acl *parse_acl(const char **args, struct list *known_acl)
751{
752 __label__ out_return, out_free_acl_expr, out_free_name;
753 struct acl *cur_acl;
754 struct acl_expr *acl_expr;
755 char *name;
756
Willy Tarreau2e74c3f2007-12-02 18:45:09 +0100757 if (invalid_char(*args))
758 goto out_return;
759
Willy Tarreaua84d3742007-05-07 00:36:48 +0200760 acl_expr = parse_acl_expr(args + 1);
761 if (!acl_expr)
762 goto out_return;
763
764 cur_acl = find_acl_by_name(args[0], known_acl);
765 if (!cur_acl) {
766 name = strdup(args[0]);
767 if (!name)
768 goto out_free_acl_expr;
769 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
770 if (cur_acl == NULL)
771 goto out_free_name;
772
773 LIST_INIT(&cur_acl->expr);
774 LIST_ADDQ(known_acl, &cur_acl->list);
775 cur_acl->name = name;
776 }
777
Willy Tarreaua9802632008-07-25 19:13:19 +0200778 cur_acl->requires |= acl_expr->kw->requires;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200779 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
780 return cur_acl;
781
782 out_free_name:
783 free(name);
784 out_free_acl_expr:
785 prune_acl_expr(acl_expr);
786 free(acl_expr);
787 out_return:
788 return NULL;
789}
790
Willy Tarreau16fbe822007-06-17 11:54:31 +0200791/* Some useful ACLs provided by default. Only those used are allocated. */
792
793const struct {
794 const char *name;
795 const char *expr[4]; /* put enough for longest expression */
796} default_acl_list[] = {
Willy Tarreau58393e12008-07-20 10:39:22 +0200797 { .name = "TRUE", .expr = {"always_true",""}},
798 { .name = "FALSE", .expr = {"always_false",""}},
Willy Tarreau16fbe822007-06-17 11:54:31 +0200799 { .name = "LOCALHOST", .expr = {"src","127.0.0.1/8",""}},
800 { .name = "HTTP_1.0", .expr = {"req_ver","1.0",""}},
801 { .name = "HTTP_1.1", .expr = {"req_ver","1.1",""}},
802 { .name = "METH_CONNECT", .expr = {"method","CONNECT",""}},
803 { .name = "METH_GET", .expr = {"method","GET","HEAD",""}},
804 { .name = "METH_HEAD", .expr = {"method","HEAD",""}},
805 { .name = "METH_OPTIONS", .expr = {"method","OPTIONS",""}},
806 { .name = "METH_POST", .expr = {"method","POST",""}},
807 { .name = "METH_TRACE", .expr = {"method","TRACE",""}},
808 { .name = "HTTP_URL_ABS", .expr = {"url_reg","^[^/:]*://",""}},
809 { .name = "HTTP_URL_SLASH", .expr = {"url_beg","/",""}},
810 { .name = "HTTP_URL_STAR", .expr = {"url","*",""}},
811 { .name = "HTTP_CONTENT", .expr = {"hdr_val(content-length)","gt","0",""}},
Willy Tarreauc6317702008-07-20 09:29:50 +0200812 { .name = "REQ_CONTENT", .expr = {"req_len","gt","0",""}},
Willy Tarreaub6fb4202008-07-20 11:18:28 +0200813 { .name = "WAIT_END", .expr = {"wait_end",""}},
Willy Tarreau16fbe822007-06-17 11:54:31 +0200814 { .name = NULL, .expr = {""}}
815};
816
817/* Find a default ACL from the default_acl list, compile it and return it.
818 * If the ACL is not found, NULL is returned. In theory, it cannot fail,
819 * except when default ACLs are broken, in which case it will return NULL.
820 * If <known_acl> is not NULL, the ACL will be queued at its tail.
821 */
822struct acl *find_acl_default(const char *acl_name, struct list *known_acl)
823{
824 __label__ out_return, out_free_acl_expr, out_free_name;
825 struct acl *cur_acl;
826 struct acl_expr *acl_expr;
827 char *name;
828 int index;
829
830 for (index = 0; default_acl_list[index].name != NULL; index++) {
831 if (strcmp(acl_name, default_acl_list[index].name) == 0)
832 break;
833 }
834
835 if (default_acl_list[index].name == NULL)
836 return NULL;
837
838 acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr);
839 if (!acl_expr)
840 goto out_return;
841
842 name = strdup(acl_name);
843 if (!name)
844 goto out_free_acl_expr;
845 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
846 if (cur_acl == NULL)
847 goto out_free_name;
848
849 cur_acl->name = name;
850 LIST_INIT(&cur_acl->expr);
851 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
852 if (known_acl)
853 LIST_ADDQ(known_acl, &cur_acl->list);
854
855 return cur_acl;
856
857 out_free_name:
858 free(name);
859 out_free_acl_expr:
860 prune_acl_expr(acl_expr);
861 free(acl_expr);
862 out_return:
863 return NULL;
864}
Willy Tarreaua84d3742007-05-07 00:36:48 +0200865
866/* Purge everything in the acl_cond <cond>, then return <cond>. */
867struct acl_cond *prune_acl_cond(struct acl_cond *cond)
868{
869 struct acl_term_suite *suite, *tmp_suite;
870 struct acl_term *term, *tmp_term;
871
872 /* iterate through all term suites and free all terms and all suites */
873 list_for_each_entry_safe(suite, tmp_suite, &cond->suites, list) {
874 list_for_each_entry_safe(term, tmp_term, &suite->terms, list)
875 free(term);
876 free(suite);
877 }
878 return cond;
879}
880
881/* Parse an ACL condition starting at <args>[0], relying on a list of already
882 * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
883 * case of low memory). Supports multiple conditions separated by "or".
884 */
885struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol)
886{
887 __label__ out_return, out_free_suite, out_free_term;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200888 int arg, neg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200889 const char *word;
890 struct acl *cur_acl;
891 struct acl_term *cur_term;
892 struct acl_term_suite *cur_suite;
893 struct acl_cond *cond;
894
895 cond = (struct acl_cond *)calloc(1, sizeof(*cond));
896 if (cond == NULL)
897 goto out_return;
898
899 LIST_INIT(&cond->list);
900 LIST_INIT(&cond->suites);
901 cond->pol = pol;
902
903 cur_suite = NULL;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200904 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200905 for (arg = 0; *args[arg]; arg++) {
906 word = args[arg];
907
908 /* remove as many exclamation marks as we can */
909 while (*word == '!') {
910 neg = !neg;
911 word++;
912 }
913
914 /* an empty word is allowed because we cannot force the user to
915 * always think about not leaving exclamation marks alone.
916 */
917 if (!*word)
918 continue;
919
Willy Tarreau16fbe822007-06-17 11:54:31 +0200920 if (strcasecmp(word, "or") == 0 || strcmp(word, "||") == 0) {
Willy Tarreaua84d3742007-05-07 00:36:48 +0200921 /* new term suite */
922 cur_suite = NULL;
923 neg = 0;
924 continue;
925 }
926
Willy Tarreau16fbe822007-06-17 11:54:31 +0200927 /* search for <word> in the known ACL names. If we do not find
928 * it, let's look for it in the default ACLs, and if found, add
929 * it to the list of ACLs of this proxy. This makes it possible
930 * to override them.
931 */
Willy Tarreaua84d3742007-05-07 00:36:48 +0200932 cur_acl = find_acl_by_name(word, known_acl);
Willy Tarreau16fbe822007-06-17 11:54:31 +0200933 if (cur_acl == NULL) {
934 cur_acl = find_acl_default(word, known_acl);
935 if (cur_acl == NULL)
936 goto out_free_suite;
937 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200938
939 cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term));
940 if (cur_term == NULL)
941 goto out_free_suite;
942
943 cur_term->acl = cur_acl;
944 cur_term->neg = neg;
Willy Tarreaua9802632008-07-25 19:13:19 +0200945 cond->requires |= cur_acl->requires;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200946
947 if (!cur_suite) {
948 cur_suite = (struct acl_term_suite *)calloc(1, sizeof(*cur_suite));
949 if (cur_term == NULL)
950 goto out_free_term;
951 LIST_INIT(&cur_suite->terms);
952 LIST_ADDQ(&cond->suites, &cur_suite->list);
953 }
954 LIST_ADDQ(&cur_suite->terms, &cur_term->list);
Willy Tarreau74b98a82007-06-16 19:35:18 +0200955 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200956 }
957
958 return cond;
959
960 out_free_term:
961 free(cur_term);
962 out_free_suite:
963 prune_acl_cond(cond);
964 free(cond);
965 out_return:
966 return NULL;
967}
968
Willy Tarreau11382812008-07-09 16:18:21 +0200969/* Execute condition <cond> and return either ACL_PAT_FAIL, ACL_PAT_MISS or
Willy Tarreaub6866442008-07-14 23:54:42 +0200970 * ACL_PAT_PASS depending on the test results. ACL_PAT_MISS may only be
971 * returned if <dir> contains ACL_PARTIAL, indicating that incomplete data
972 * is being examined.
973 * This function only computes the condition, it does not apply the polarity
974 * required by IF/UNLESS, it's up to the caller to do this using something like
975 * this :
Willy Tarreau11382812008-07-09 16:18:21 +0200976 *
977 * res = acl_pass(res);
Willy Tarreaub6866442008-07-14 23:54:42 +0200978 * if (res == ACL_PAT_MISS)
979 * return 0;
Willy Tarreau11382812008-07-09 16:18:21 +0200980 * if (cond->pol == ACL_COND_UNLESS)
981 * res = !res;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200982 */
Willy Tarreaud41f8d82007-06-10 10:06:18 +0200983int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, void *l7, int dir)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200984{
985 __label__ fetch_next;
986 struct acl_term_suite *suite;
987 struct acl_term *term;
988 struct acl_expr *expr;
989 struct acl *acl;
990 struct acl_pattern *pattern;
991 struct acl_test test;
Willy Tarreau11382812008-07-09 16:18:21 +0200992 int acl_res, suite_res, cond_res;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200993
Willy Tarreau11382812008-07-09 16:18:21 +0200994 /* We're doing a logical OR between conditions so we initialize to FAIL.
995 * The MISS status is propagated down from the suites.
996 */
Willy Tarreaua84d3742007-05-07 00:36:48 +0200997 cond_res = ACL_PAT_FAIL;
998 list_for_each_entry(suite, &cond->suites, list) {
Willy Tarreau11382812008-07-09 16:18:21 +0200999 /* Evaluate condition suite <suite>. We stop at the first term
1000 * which returns ACL_PAT_FAIL. The MISS status is still propagated
1001 * in case of uncertainty in the result.
Willy Tarreaua84d3742007-05-07 00:36:48 +02001002 */
1003
1004 /* we're doing a logical AND between terms, so we must set the
1005 * initial value to PASS.
1006 */
1007 suite_res = ACL_PAT_PASS;
1008 list_for_each_entry(term, &suite->terms, list) {
1009 acl = term->acl;
1010
1011 /* FIXME: use cache !
1012 * check acl->cache_idx for this.
1013 */
1014
1015 /* ACL result not cached. Let's scan all the expressions
1016 * and use the first one to match.
1017 */
1018 acl_res = ACL_PAT_FAIL;
1019 list_for_each_entry(expr, &acl->expr, list) {
Willy Tarreaud41f8d82007-06-10 10:06:18 +02001020 /* we need to reset context and flags */
1021 memset(&test, 0, sizeof(test));
Willy Tarreaua84d3742007-05-07 00:36:48 +02001022 fetch_next:
Willy Tarreaub6866442008-07-14 23:54:42 +02001023 if (!expr->kw->fetch(px, l4, l7, dir, expr, &test)) {
1024 /* maybe we could not fetch because of missing data */
1025 if (test.flags & ACL_TEST_F_MAY_CHANGE && dir & ACL_PARTIAL)
1026 acl_res |= ACL_PAT_MISS;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001027 continue;
Willy Tarreaub6866442008-07-14 23:54:42 +02001028 }
Willy Tarreaua84d3742007-05-07 00:36:48 +02001029
Willy Tarreaua79534f2008-07-20 10:13:37 +02001030 if (test.flags & ACL_TEST_F_RES_SET) {
1031 if (test.flags & ACL_TEST_F_RES_PASS)
1032 acl_res |= ACL_PAT_PASS;
1033 else
1034 acl_res |= ACL_PAT_FAIL;
1035 }
1036 else {
1037 /* call the match() function for all tests on this value */
1038 list_for_each_entry(pattern, &expr->patterns, list) {
1039 acl_res |= expr->kw->match(&test, pattern);
1040 if (acl_res == ACL_PAT_PASS)
1041 break;
1042 }
Willy Tarreaua84d3742007-05-07 00:36:48 +02001043 }
1044 /*
Willy Tarreau11382812008-07-09 16:18:21 +02001045 * OK now acl_res holds the result of this expression
1046 * as one of ACL_PAT_FAIL, ACL_PAT_MISS or ACL_PAT_PASS.
Willy Tarreaua84d3742007-05-07 00:36:48 +02001047 *
Willy Tarreau11382812008-07-09 16:18:21 +02001048 * Then if (!MISS) we can cache the result, and put
Willy Tarreaua84d3742007-05-07 00:36:48 +02001049 * (test.flags & ACL_TEST_F_VOLATILE) in the cache flags.
1050 *
1051 * FIXME: implement cache.
1052 *
1053 */
1054
1055 /* now we may have some cleanup to do */
1056 if (test.flags & ACL_TEST_F_MUST_FREE) {
1057 free(test.ptr);
1058 test.len = 0;
1059 }
1060
Willy Tarreau11382812008-07-09 16:18:21 +02001061 /* we're ORing these terms, so a single PASS is enough */
1062 if (acl_res == ACL_PAT_PASS)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001063 break;
1064
Willy Tarreaua84d3742007-05-07 00:36:48 +02001065 if (test.flags & ACL_TEST_F_FETCH_MORE)
1066 goto fetch_next;
Willy Tarreaub6866442008-07-14 23:54:42 +02001067
1068 /* sometimes we know the fetched data is subject to change
1069 * later and give another chance for a new match (eg: request
1070 * size, time, ...)
1071 */
1072 if (test.flags & ACL_TEST_F_MAY_CHANGE && dir & ACL_PARTIAL)
1073 acl_res |= ACL_PAT_MISS;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001074 }
1075 /*
1076 * Here we have the result of an ACL (cached or not).
1077 * ACLs are combined, negated or not, to form conditions.
1078 */
1079
Willy Tarreaua84d3742007-05-07 00:36:48 +02001080 if (term->neg)
Willy Tarreau11382812008-07-09 16:18:21 +02001081 acl_res = acl_neg(acl_res);
Willy Tarreaua84d3742007-05-07 00:36:48 +02001082
1083 suite_res &= acl_res;
Willy Tarreau11382812008-07-09 16:18:21 +02001084
1085 /* we're ANDing these terms, so a single FAIL is enough */
1086 if (suite_res == ACL_PAT_FAIL)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001087 break;
1088 }
1089 cond_res |= suite_res;
Willy Tarreau11382812008-07-09 16:18:21 +02001090
1091 /* we're ORing these terms, so a single PASS is enough */
1092 if (cond_res == ACL_PAT_PASS)
Willy Tarreaua84d3742007-05-07 00:36:48 +02001093 break;
1094 }
Willy Tarreau11382812008-07-09 16:18:21 +02001095 return cond_res;
Willy Tarreaua84d3742007-05-07 00:36:48 +02001096}
1097
1098
1099/************************************************************************/
1100/* All supported keywords must be declared here. */
1101/************************************************************************/
1102
1103/* Note: must not be declared <const> as its list will be overwritten */
1104static struct acl_kw_list acl_kws = {{ },{
Willy Tarreau58393e12008-07-20 10:39:22 +02001105 { "always_true", acl_parse_nothing, acl_fetch_true, acl_match_nothing },
1106 { "always_false", acl_parse_nothing, acl_fetch_false, acl_match_nothing },
Willy Tarreaub6fb4202008-07-20 11:18:28 +02001107 { "wait_end", acl_parse_nothing, acl_fetch_wait_end, acl_match_nothing },
Willy Tarreaua84d3742007-05-07 00:36:48 +02001108#if 0
1109 { "time", acl_parse_time, acl_fetch_time, acl_match_time },
1110#endif
1111 { NULL, NULL, NULL, NULL }
1112}};
1113
1114
1115__attribute__((constructor))
1116static void __acl_init(void)
1117{
1118 acl_register_keywords(&acl_kws);
1119}
1120
1121
1122/*
1123 * Local variables:
1124 * c-indent-level: 8
1125 * c-basic-offset: 8
1126 * End:
1127 */