blob: c4860abdfe5f8176d7684c6e963e5615c7856b2f [file] [log] [blame]
Willy Tarreaua84d3742007-05-07 00:36:48 +02001/*
2 * ACL management functions.
3 *
4 * Copyright 2000-2007 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
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
23#include <types/acl.h>
24#include <types/proxy.h>
25#include <types/session.h>
26
27/* List head of all known ACL keywords */
28static struct acl_kw_list acl_keywords = {
29 .list = LIST_HEAD_INIT(acl_keywords.list)
30};
31
32
33/* This one always returns 1 because its only purpose is to check that the
34 * value is present, which is already checked by getval().
35 */
36int acl_match_pst(struct acl_test *test, struct acl_pattern *pattern)
37{
38 return 1;
39}
40
41/* NB: For two strings to be identical, it is required that their lengths match */
42int acl_match_str(struct acl_test *test, struct acl_pattern *pattern)
43{
44 if (pattern->len != test->len)
45 return 0;
46 if (strncmp(pattern->ptr.str, test->ptr, test->len) == 0)
47 return 1;
48 return 0;
49}
50
Willy Tarreauf3d25982007-05-08 22:45:09 +020051/* Executes a regex. It needs to change the data. If it is marked READ_ONLY
52 * then it will be allocated and duplicated in place so that others may use
53 * it later on. Note that this is embarrassing because we always try to avoid
54 * allocating memory at run time.
55 */
56int acl_match_reg(struct acl_test *test, struct acl_pattern *pattern)
57{
58 char old_char;
59 int ret;
60
61 if (unlikely(test->flags & ACL_TEST_F_READ_ONLY)) {
62 char *new_str;
63
64 new_str = calloc(1, test->len + 1);
65 if (!new_str)
66 return 0;
67
68 memcpy(new_str, test->ptr, test->len);
69 new_str[test->len] = 0;
70 if (test->flags & ACL_TEST_F_MUST_FREE)
71 free(test->ptr);
72 test->ptr = new_str;
73 test->flags |= ACL_TEST_F_MUST_FREE;
74 test->flags &= ~ACL_TEST_F_READ_ONLY;
75 }
76
77 old_char = test->ptr[test->len];
78 test->ptr[test->len] = 0;
79
80 if (regexec(pattern->ptr.reg, test->ptr, 0, NULL, 0) == 0)
81 ret = 1;
82 else
83 ret = 0;
84
85 test->ptr[test->len] = old_char;
86 return ret;
87}
88
Willy Tarreaua84d3742007-05-07 00:36:48 +020089/* Checks that the pattern matches the beginning of the tested string. */
90int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern)
91{
92 if (pattern->len > test->len)
93 return 0;
94 if (strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0)
95 return 0;
96 return 1;
97}
98
99/* Checks that the pattern matches the end of the tested string. */
100int acl_match_end(struct acl_test *test, struct acl_pattern *pattern)
101{
102 if (pattern->len > test->len)
103 return 0;
104 if (strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0)
105 return 0;
106 return 1;
107}
108
109/* Checks that the pattern is included inside the tested string.
110 * NB: Suboptimal, should be rewritten using a Boyer-Moore method.
111 */
112int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern)
113{
114 char *end;
115 char *c;
116
117 if (pattern->len > test->len)
118 return 0;
119
120 end = test->ptr + test->len - pattern->len;
121 for (c = test->ptr; c <= end; c++) {
122 if (*c != *pattern->ptr.str)
123 continue;
124 if (strncmp(pattern->ptr.str, c, pattern->len) == 0)
125 return 1;
126 }
127 return 0;
128}
129
130/* This one is used by other real functions. It checks that the pattern is
131 * included inside the tested string, but enclosed between the specified
132 * delimitor, or a '/' or a '?' or at the beginning or end of the string.
133 * The delimitor is stripped at the beginning or end of the pattern are
134 * ignored.
135 */
136static int match_word(struct acl_test *test, struct acl_pattern *pattern, char delim)
137{
138 int may_match;
139 char *c, *end;
140 char *ps;
141 int pl;
142
143 pl = pattern->len;
144 ps = pattern->ptr.str;
145 while (pl > 0 && *ps == delim) {
146 pl--;
147 ps++;
148 }
149
150 while (pl > 0 && *(ps + pl - 1) == delim)
151 pl--;
152
153 if (pl > test->len)
154 return 0;
155
156 may_match = 1;
157 end = test->ptr + test->len - pl;
158 for (c = test->ptr; c <= end; c++) {
159 if (*c == '/' || *c == delim || *c == '?') {
160 may_match = 1;
161 continue;
162 }
163 if (may_match && (*c == *ps) &&
164 (strncmp(ps, c, pl) == 0) &&
165 (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?'))
166 return 1;
167
168 may_match = 0;
169 }
170 return 0;
171}
172
173/* Checks that the pattern is included inside the tested string, but enclosed
174 * between slashes or at the beginning or end of the string. Slashes at the
175 * beginning or end of the pattern are ignored.
176 */
177int acl_match_dir(struct acl_test *test, struct acl_pattern *pattern)
178{
179 return match_word(test, pattern, '/');
180}
181
182/* Checks that the pattern is included inside the tested string, but enclosed
183 * between dots or at the beginning or end of the string. Dots at the beginning
184 * or end of the pattern are ignored.
185 */
186int acl_match_dom(struct acl_test *test, struct acl_pattern *pattern)
187{
188 return match_word(test, pattern, '.');
189}
190
191/* Checks that the integer in <test> is included between min and max */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200192int acl_match_int(struct acl_test *test, struct acl_pattern *pattern)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200193{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200194 if ((!pattern->val.range.min_set || pattern->val.range.min <= test->i) &&
195 (!pattern->val.range.max_set || test->i <= pattern->val.range.max))
Willy Tarreaua84d3742007-05-07 00:36:48 +0200196 return 1;
197 return 0;
198}
199
Willy Tarreaua67fad92007-05-08 19:50:09 +0200200int acl_match_ip(struct acl_test *test, struct acl_pattern *pattern)
201{
202 struct in_addr *s;
203
204 if (test->i != AF_INET)
205 return 0;
206
207 s = (void *)test->ptr;
208 if (((s->s_addr ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0)
209 return 1;
210 return 0;
211}
212
Willy Tarreaua84d3742007-05-07 00:36:48 +0200213/* Parse a string. It is allocated and duplicated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200214int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200215{
216 int len;
217
Willy Tarreauae8b7962007-06-09 23:10:04 +0200218 len = strlen(*text);
219 pattern->ptr.str = strdup(*text);
Willy Tarreaua84d3742007-05-07 00:36:48 +0200220 if (!pattern->ptr.str)
221 return 0;
222 pattern->len = len;
223 return 1;
224}
225
Willy Tarreauf3d25982007-05-08 22:45:09 +0200226/* Parse a regex. It is allocated. */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200227int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreauf3d25982007-05-08 22:45:09 +0200228{
229 regex_t *preg;
230
231 preg = calloc(1, sizeof(regex_t));
232
233 if (!preg)
234 return 0;
235
Willy Tarreauae8b7962007-06-09 23:10:04 +0200236 if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB) != 0) {
Willy Tarreauf3d25982007-05-08 22:45:09 +0200237 free(preg);
238 return 0;
239 }
240
241 pattern->ptr.reg = preg;
242 return 1;
243}
244
Willy Tarreauae8b7962007-06-09 23:10:04 +0200245/* Parse a range of positive integers delimited by either ':' or '-'. If only
246 * one integer is read, it is set as both min and max. An operator may be
247 * specified as the prefix, among this list of 5 :
248 *
249 * 0:eq, 1:gt, 2:ge, 3:lt, 4:le
250 *
251 * The default operator is "eq". It supports range matching. Ranges are
252 * rejected for other operators. The operator may be changed at any time.
253 * The operator is stored in the 'opaque' argument.
254 *
Willy Tarreaua84d3742007-05-07 00:36:48 +0200255 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200256int acl_parse_int(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200257{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200258 signed long long i;
259 unsigned int j, last, skip = 0;
260 const char *ptr = *text;
261
262
263 while (!isdigit(*ptr)) {
264 if (strcmp(ptr, "eq") == 0) *opaque = 0;
265 else if (strcmp(ptr, "gt") == 0) *opaque = 1;
266 else if (strcmp(ptr, "ge") == 0) *opaque = 2;
267 else if (strcmp(ptr, "lt") == 0) *opaque = 3;
268 else if (strcmp(ptr, "le") == 0) *opaque = 4;
269 else
270 return 0;
271
272 skip++;
273 ptr = text[skip];
274 }
Willy Tarreaua84d3742007-05-07 00:36:48 +0200275
276 last = i = 0;
277 while (1) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200278 j = *ptr++;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200279 if ((j == '-' || j == ':') && !last) {
280 last++;
281 pattern->val.range.min = i;
282 i = 0;
283 continue;
284 }
285 j -= '0';
286 if (j > 9)
287 // also catches the terminating zero
288 break;
289 i *= 10;
290 i += j;
291 }
Willy Tarreauae8b7962007-06-09 23:10:04 +0200292
293 if (last && *opaque >= 1 && *opaque <= 4)
294 /* having a range with a min or a max is absurd */
295 return 0;
296
Willy Tarreaua84d3742007-05-07 00:36:48 +0200297 if (!last)
298 pattern->val.range.min = i;
299 pattern->val.range.max = i;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200300
301 switch (*opaque) {
302 case 0: /* eq */
303 pattern->val.range.min_set = 1;
304 pattern->val.range.max_set = 1;
305 break;
306 case 1: /* gt */
307 pattern->val.range.min++; /* gt = ge + 1 */
308 case 2: /* ge */
309 pattern->val.range.min_set = 1;
310 pattern->val.range.max_set = 0;
311 break;
312 case 3: /* lt */
313 pattern->val.range.max--; /* lt = le - 1 */
314 case 4: /* le */
315 pattern->val.range.min_set = 0;
316 pattern->val.range.max_set = 1;
317 break;
318 }
319 return skip + 1;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200320}
321
Willy Tarreaua67fad92007-05-08 19:50:09 +0200322/* Parse an IP address and an optional mask in the form addr[/mask].
323 * The addr may either be an IPv4 address or a hostname. The mask
324 * may either be a dotted mask or a number of bits. Returns 1 if OK,
325 * otherwise 0.
326 */
Willy Tarreauae8b7962007-06-09 23:10:04 +0200327int acl_parse_ip(const char **text, struct acl_pattern *pattern, int *opaque)
Willy Tarreaua67fad92007-05-08 19:50:09 +0200328{
Willy Tarreauae8b7962007-06-09 23:10:04 +0200329 if (str2net(*text, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask))
330 return 1;
331 else
332 return 0;
Willy Tarreaua67fad92007-05-08 19:50:09 +0200333}
334
Willy Tarreaua84d3742007-05-07 00:36:48 +0200335/*
336 * Registers the ACL keyword list <kwl> as a list of valid keywords for next
337 * parsing sessions.
338 */
339void acl_register_keywords(struct acl_kw_list *kwl)
340{
341 LIST_ADDQ(&acl_keywords.list, &kwl->list);
342}
343
344/*
345 * Unregisters the ACL keyword list <kwl> from the list of valid keywords.
346 */
347void acl_unregister_keywords(struct acl_kw_list *kwl)
348{
349 LIST_DEL(&kwl->list);
350 LIST_INIT(&kwl->list);
351}
352
353/* Return a pointer to the ACL <name> within the list starting at <head>, or
354 * NULL if not found.
355 */
356struct acl *find_acl_by_name(const char *name, struct list *head)
357{
358 struct acl *acl;
359 list_for_each_entry(acl, head, list) {
360 if (strcmp(acl->name, name) == 0)
361 return acl;
362 }
363 return NULL;
364}
365
366/* Return a pointer to the ACL keyword <kw>, or NULL if not found. Note that if
367 * <kw> contains an opening parenthesis, only the left part of it is checked.
368 */
369struct acl_keyword *find_acl_kw(const char *kw)
370{
371 int index;
372 const char *kwend;
373 struct acl_kw_list *kwl;
374
375 kwend = strchr(kw, '(');
376 if (!kwend)
377 kwend = kw + strlen(kw);
378
379 list_for_each_entry(kwl, &acl_keywords.list, list) {
380 for (index = 0; kwl->kw[index].kw != NULL; index++) {
381 if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
382 kwl->kw[index].kw[kwend-kw] == 0)
383 return &kwl->kw[index];
384 }
385 }
386 return NULL;
387}
388
389static void free_pattern(struct acl_pattern *pat)
390{
391 if (pat->ptr.ptr)
392 free(pat->ptr.ptr);
393 free(pat);
394}
395
396static void free_pattern_list(struct list *head)
397{
398 struct acl_pattern *pat, *tmp;
399 list_for_each_entry_safe(pat, tmp, head, list)
400 free_pattern(pat);
401}
402
403static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
404{
405 free_pattern_list(&expr->patterns);
406 LIST_INIT(&expr->patterns);
407 if (expr->arg.str)
408 free(expr->arg.str);
409 expr->kw->use_cnt--;
410 return expr;
411}
412
413/* Parse an ACL expression starting at <args>[0], and return it.
414 * Right now, the only accepted syntax is :
415 * <subject> [<value>...]
416 */
417struct acl_expr *parse_acl_expr(const char **args)
418{
419 __label__ out_return, out_free_expr, out_free_pattern;
420 struct acl_expr *expr;
421 struct acl_keyword *aclkw;
422 struct acl_pattern *pattern;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200423 int opaque;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200424 const char *arg;
425
426 aclkw = find_acl_kw(args[0]);
427 if (!aclkw || !aclkw->parse)
428 goto out_return;
429
430 expr = (struct acl_expr *)calloc(1, sizeof(*expr));
431 if (!expr)
432 goto out_return;
433
434 expr->kw = aclkw;
435 aclkw->use_cnt++;
436 LIST_INIT(&expr->patterns);
437 expr->arg.str = NULL;
Willy Tarreaubb768912007-06-10 11:17:01 +0200438 expr->arg_len = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200439
440 arg = strchr(args[0], '(');
441 if (arg != NULL) {
442 char *end, *arg2;
443 /* there is an argument in the form "subject(arg)" */
444 arg++;
445 end = strchr(arg, ')');
446 if (!end)
447 goto out_free_expr;
448 arg2 = (char *)calloc(1, end - arg + 1);
449 if (!arg2)
450 goto out_free_expr;
451 memcpy(arg2, arg, end - arg);
452 arg2[end-arg] = '\0';
Willy Tarreaubb768912007-06-10 11:17:01 +0200453 expr->arg_len = end - arg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200454 expr->arg.str = arg2;
455 }
456
457 /* now parse all patterns */
458 args++;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200459 opaque = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200460 while (**args) {
Willy Tarreauae8b7962007-06-09 23:10:04 +0200461 int ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200462 pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
463 if (!pattern)
464 goto out_free_expr;
Willy Tarreauae8b7962007-06-09 23:10:04 +0200465 ret = aclkw->parse(args, pattern, &opaque);
466 if (!ret)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200467 goto out_free_pattern;
468 LIST_ADDQ(&expr->patterns, &pattern->list);
Willy Tarreauae8b7962007-06-09 23:10:04 +0200469 args += ret;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200470 }
471
472 return expr;
473
474 out_free_pattern:
475 free_pattern(pattern);
476 out_free_expr:
477 prune_acl_expr(expr);
478 free(expr);
479 out_return:
480 return NULL;
481}
482
483/* Parse an ACL with the name starting at <args>[0], and with a list of already
484 * known ACLs in <acl>. If the ACL was not in the list, it will be added.
485 * A pointer to that ACL is returned.
486 *
487 * args syntax: <aclname> <acl_expr>
488 */
489struct acl *parse_acl(const char **args, struct list *known_acl)
490{
491 __label__ out_return, out_free_acl_expr, out_free_name;
492 struct acl *cur_acl;
493 struct acl_expr *acl_expr;
494 char *name;
495
496 acl_expr = parse_acl_expr(args + 1);
497 if (!acl_expr)
498 goto out_return;
499
500 cur_acl = find_acl_by_name(args[0], known_acl);
501 if (!cur_acl) {
502 name = strdup(args[0]);
503 if (!name)
504 goto out_free_acl_expr;
505 cur_acl = (struct acl *)calloc(1, sizeof(*cur_acl));
506 if (cur_acl == NULL)
507 goto out_free_name;
508
509 LIST_INIT(&cur_acl->expr);
510 LIST_ADDQ(known_acl, &cur_acl->list);
511 cur_acl->name = name;
512 }
513
514 LIST_ADDQ(&cur_acl->expr, &acl_expr->list);
515 return cur_acl;
516
517 out_free_name:
518 free(name);
519 out_free_acl_expr:
520 prune_acl_expr(acl_expr);
521 free(acl_expr);
522 out_return:
523 return NULL;
524}
525
526
527/* Purge everything in the acl_cond <cond>, then return <cond>. */
528struct acl_cond *prune_acl_cond(struct acl_cond *cond)
529{
530 struct acl_term_suite *suite, *tmp_suite;
531 struct acl_term *term, *tmp_term;
532
533 /* iterate through all term suites and free all terms and all suites */
534 list_for_each_entry_safe(suite, tmp_suite, &cond->suites, list) {
535 list_for_each_entry_safe(term, tmp_term, &suite->terms, list)
536 free(term);
537 free(suite);
538 }
539 return cond;
540}
541
542/* Parse an ACL condition starting at <args>[0], relying on a list of already
543 * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
544 * case of low memory). Supports multiple conditions separated by "or".
545 */
546struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol)
547{
548 __label__ out_return, out_free_suite, out_free_term;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200549 int arg, neg;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200550 const char *word;
551 struct acl *cur_acl;
552 struct acl_term *cur_term;
553 struct acl_term_suite *cur_suite;
554 struct acl_cond *cond;
555
556 cond = (struct acl_cond *)calloc(1, sizeof(*cond));
557 if (cond == NULL)
558 goto out_return;
559
560 LIST_INIT(&cond->list);
561 LIST_INIT(&cond->suites);
562 cond->pol = pol;
563
564 cur_suite = NULL;
Willy Tarreau74b98a82007-06-16 19:35:18 +0200565 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200566 for (arg = 0; *args[arg]; arg++) {
567 word = args[arg];
568
569 /* remove as many exclamation marks as we can */
570 while (*word == '!') {
571 neg = !neg;
572 word++;
573 }
574
575 /* an empty word is allowed because we cannot force the user to
576 * always think about not leaving exclamation marks alone.
577 */
578 if (!*word)
579 continue;
580
581 if (strcasecmp(word, "or") == 0) {
582 /* new term suite */
583 cur_suite = NULL;
584 neg = 0;
585 continue;
586 }
587
588 /* search for <word> in the known ACL names */
589 cur_acl = find_acl_by_name(word, known_acl);
590 if (cur_acl == NULL)
591 goto out_free_suite;
592
593 cur_term = (struct acl_term *)calloc(1, sizeof(*cur_term));
594 if (cur_term == NULL)
595 goto out_free_suite;
596
597 cur_term->acl = cur_acl;
598 cur_term->neg = neg;
599
600 if (!cur_suite) {
601 cur_suite = (struct acl_term_suite *)calloc(1, sizeof(*cur_suite));
602 if (cur_term == NULL)
603 goto out_free_term;
604 LIST_INIT(&cur_suite->terms);
605 LIST_ADDQ(&cond->suites, &cur_suite->list);
606 }
607 LIST_ADDQ(&cur_suite->terms, &cur_term->list);
Willy Tarreau74b98a82007-06-16 19:35:18 +0200608 neg = 0;
Willy Tarreaua84d3742007-05-07 00:36:48 +0200609 }
610
611 return cond;
612
613 out_free_term:
614 free(cur_term);
615 out_free_suite:
616 prune_acl_cond(cond);
617 free(cond);
618 out_return:
619 return NULL;
620}
621
622/* Execute condition <cond> and return 0 if test fails or 1 if test succeeds.
623 * This function only computes the condition, it does not apply the polarity
624 * required by IF/UNLESS, it's up to the caller to do this.
625 */
Willy Tarreaud41f8d82007-06-10 10:06:18 +0200626int acl_exec_cond(struct acl_cond *cond, struct proxy *px, struct session *l4, void *l7, int dir)
Willy Tarreaua84d3742007-05-07 00:36:48 +0200627{
628 __label__ fetch_next;
629 struct acl_term_suite *suite;
630 struct acl_term *term;
631 struct acl_expr *expr;
632 struct acl *acl;
633 struct acl_pattern *pattern;
634 struct acl_test test;
635 int acl_res, pat_res, suite_res, cond_res;
636
637 /* we're doing a logical OR between conditions so we initialize to FAIL */
638 cond_res = ACL_PAT_FAIL;
639 list_for_each_entry(suite, &cond->suites, list) {
640 /* evaluate condition suite <suite>. We stop at the first term
641 * which does not return ACL_PAT_PASS.
642 */
643
644 /* we're doing a logical AND between terms, so we must set the
645 * initial value to PASS.
646 */
647 suite_res = ACL_PAT_PASS;
648 list_for_each_entry(term, &suite->terms, list) {
649 acl = term->acl;
650
651 /* FIXME: use cache !
652 * check acl->cache_idx for this.
653 */
654
655 /* ACL result not cached. Let's scan all the expressions
656 * and use the first one to match.
657 */
658 acl_res = ACL_PAT_FAIL;
659 list_for_each_entry(expr, &acl->expr, list) {
Willy Tarreaud41f8d82007-06-10 10:06:18 +0200660 /* we need to reset context and flags */
661 memset(&test, 0, sizeof(test));
Willy Tarreaua84d3742007-05-07 00:36:48 +0200662 fetch_next:
Willy Tarreau97be1452007-06-10 11:47:14 +0200663 if (!expr->kw->fetch(px, l4, l7, dir, expr, &test))
Willy Tarreaua84d3742007-05-07 00:36:48 +0200664 continue;
665
666 /* apply all tests to this value */
667 list_for_each_entry(pattern, &expr->patterns, list) {
668 pat_res = expr->kw->match(&test, pattern);
669
670 if (pat_res & ACL_PAT_MISS) {
671 /* there is at least one test which might be worth retrying later. */
672 acl_res |= ACL_PAT_MISS;
673 continue;
674 } else if (pat_res & ACL_PAT_PASS) {
675 /* we found one ! */
676 acl_res |= ACL_PAT_PASS;
677 break;
678 }
679 }
680 /*
Willy Tarreau74b98a82007-06-16 19:35:18 +0200681 * OK now we have the result of this expression in acl_res.
Willy Tarreaua84d3742007-05-07 00:36:48 +0200682 * - we have the PASS bit set if at least one pattern matched ;
683 * - we have the MISS bit set if at least one pattern may match
684 * later so that we should not cache a failure ;
685 *
686 * Then if (PASS || !MISS) we can cache the result, and put
687 * (test.flags & ACL_TEST_F_VOLATILE) in the cache flags.
688 *
689 * FIXME: implement cache.
690 *
691 */
692
693 /* now we may have some cleanup to do */
694 if (test.flags & ACL_TEST_F_MUST_FREE) {
695 free(test.ptr);
696 test.len = 0;
697 }
698
699 if (acl_res & ACL_PAT_PASS)
700 break;
701
702 /* prepare to test another expression */
703 acl_res = ACL_PAT_FAIL;
704
705 if (test.flags & ACL_TEST_F_FETCH_MORE)
706 goto fetch_next;
707 }
708 /*
709 * Here we have the result of an ACL (cached or not).
710 * ACLs are combined, negated or not, to form conditions.
711 */
712
713 acl_res &= ACL_PAT_PASS;
714 if (term->neg)
715 acl_res ^= ACL_PAT_PASS;
716
717 suite_res &= acl_res;
718 if (!(suite_res & ACL_PAT_PASS))
719 break;
720 }
721 cond_res |= suite_res;
722 if (cond_res & ACL_PAT_PASS)
723 break;
724 }
725
726 return (cond_res & ACL_PAT_PASS) ? 1 : 0;
727}
728
729
730/************************************************************************/
731/* All supported keywords must be declared here. */
732/************************************************************************/
733
734/* Note: must not be declared <const> as its list will be overwritten */
735static struct acl_kw_list acl_kws = {{ },{
736#if 0
737 { "time", acl_parse_time, acl_fetch_time, acl_match_time },
738#endif
739 { NULL, NULL, NULL, NULL }
740}};
741
742
743__attribute__((constructor))
744static void __acl_init(void)
745{
746 acl_register_keywords(&acl_kws);
747}
748
749
750/*
751 * Local variables:
752 * c-indent-level: 8
753 * c-basic-offset: 8
754 * End:
755 */