diff --git a/include/types/acl.h b/include/types/acl.h index db76a2724..ef5be58dc 100644 --- a/include/types/acl.h +++ b/include/types/acl.h @@ -67,6 +67,12 @@ enum { ACL_DIR_RTR, /* ACL evaluated on response */ }; +/* possible flags for expressions or patterns */ +enum { + ACL_PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */ + ACL_PAT_F_FROM_FILE = 1 << 1, /* pattern comes from a file */ +}; + /* How to store a time range and the valid days in 29 bits */ struct acl_time { int dow:7; /* 1 bit per day of week: 0-6 */ @@ -96,6 +102,7 @@ struct acl_pattern { regex_t *reg; /* a compiled regex */ } ptr; /* indirect values, allocated */ int len; /* data length when required */ + int flags; /* expr or pattern flags. */ }; /* The structure exchanged between an acl_fetch_* function responsible for diff --git a/src/acl.c b/src/acl.c index c4860abdf..c266c4a83 100644 --- a/src/acl.c +++ b/src/acl.c @@ -41,9 +41,14 @@ int acl_match_pst(struct acl_test *test, struct acl_pattern *pattern) /* NB: For two strings to be identical, it is required that their lengths match */ int acl_match_str(struct acl_test *test, struct acl_pattern *pattern) { + int icase; + if (pattern->len != test->len) return 0; - if (strncmp(pattern->ptr.str, test->ptr, test->len) == 0) + + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, test->ptr, test->len) == 0) || + (!icase && strncmp(pattern->ptr.str, test->ptr, test->len) == 0)) return 1; return 0; } @@ -89,9 +94,14 @@ int acl_match_reg(struct acl_test *test, struct acl_pattern *pattern) /* Checks that the pattern matches the beginning of the tested string. */ int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern) { + int icase; + if (pattern->len > test->len) return 0; - if (strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0) + + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, test->ptr, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, test->ptr, pattern->len) != 0)) return 0; return 1; } @@ -99,9 +109,13 @@ int acl_match_beg(struct acl_test *test, struct acl_pattern *pattern) /* Checks that the pattern matches the end of the tested string. */ int acl_match_end(struct acl_test *test, struct acl_pattern *pattern) { + int icase; + if (pattern->len > test->len) return 0; - if (strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0) + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, test->ptr + test->len - pattern->len, pattern->len) != 0)) return 0; return 1; } @@ -111,6 +125,7 @@ int acl_match_end(struct acl_test *test, struct acl_pattern *pattern) */ int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern) { + int icase; char *end; char *c; @@ -118,11 +133,21 @@ int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern) return 0; end = test->ptr + test->len - pattern->len; - for (c = test->ptr; c <= end; c++) { - if (*c != *pattern->ptr.str) - continue; - if (strncmp(pattern->ptr.str, c, pattern->len) == 0) - return 1; + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if (icase) { + for (c = test->ptr; c <= end; c++) { + if (tolower(*c) != tolower(*pattern->ptr.str)) + continue; + if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0) + return 1; + } + } else { + for (c = test->ptr; c <= end; c++) { + if (*c != *pattern->ptr.str) + continue; + if (strncmp(pattern->ptr.str, c, pattern->len) == 0) + return 1; + } } return 0; } @@ -130,41 +155,52 @@ int acl_match_sub(struct acl_test *test, struct acl_pattern *pattern) /* This one is used by other real functions. It checks that the pattern is * included inside the tested string, but enclosed between the specified * delimitor, or a '/' or a '?' or at the beginning or end of the string. - * The delimitor is stripped at the beginning or end of the pattern are - * ignored. + * The delimitor is stripped at the beginning or end of the pattern. */ static int match_word(struct acl_test *test, struct acl_pattern *pattern, char delim) { - int may_match; + int may_match, icase; char *c, *end; char *ps; int pl; pl = pattern->len; ps = pattern->ptr.str; - while (pl > 0 && *ps == delim) { + while (pl > 0 && (*ps == delim || *ps == '/' || *ps == '?')) { pl--; ps++; } - while (pl > 0 && *(ps + pl - 1) == delim) + while (pl > 0 && + (ps[pl - 1] == delim || ps[pl - 1] == '/' || ps[pl - 1] == '?')) pl--; if (pl > test->len) return 0; may_match = 1; + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; end = test->ptr + test->len - pl; for (c = test->ptr; c <= end; c++) { if (*c == '/' || *c == delim || *c == '?') { may_match = 1; continue; } - if (may_match && (*c == *ps) && - (strncmp(ps, c, pl) == 0) && - (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?')) - return 1; + if (!may_match) + continue; + + if (icase) { + if ((tolower(*c) == tolower(*ps)) && + (strncasecmp(ps, c, pl) == 0) && + (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?')) + return 1; + } else { + if ((*c == *ps) && + (strncmp(ps, c, pl) == 0) && + (c == end || c[pl] == '/' || c[pl] == delim || c[pl] == '?')) + return 1; + } may_match = 0; } return 0; @@ -227,13 +263,15 @@ int acl_parse_str(const char **text, struct acl_pattern *pattern, int *opaque) int acl_parse_reg(const char **text, struct acl_pattern *pattern, int *opaque) { regex_t *preg; + int icase; preg = calloc(1, sizeof(regex_t)); if (!preg) return 0; - if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB) != 0) { + icase = (pattern->flags & ACL_PAT_F_IGNORE_CASE) ? REG_ICASE : 0; + if (regcomp(preg, *text, REG_EXTENDED | REG_NOSUB | icase) != 0) { free(preg); return 0; } @@ -420,7 +458,7 @@ struct acl_expr *parse_acl_expr(const char **args) struct acl_expr *expr; struct acl_keyword *aclkw; struct acl_pattern *pattern; - int opaque; + int opaque, patflags; const char *arg; aclkw = find_acl_kw(args[0]); @@ -454,14 +492,37 @@ struct acl_expr *parse_acl_expr(const char **args) expr->arg.str = arg2; } - /* now parse all patterns */ args++; + + /* check for options before patterns. Supported options are : + * -i : ignore case for all patterns by default + * -f : read patterns from those files + * -- : everything after this is not an option + */ + patflags = 0; + while (**args == '-') { + if ((*args)[1] == 'i') + patflags |= ACL_PAT_F_IGNORE_CASE; + else if ((*args)[1] == 'f') + patflags |= ACL_PAT_F_FROM_FILE; + else if ((*args)[1] == '-') { + args++; + break; + } + else + break; + args++; + } + + /* now parse all patterns */ opaque = 0; while (**args) { int ret; pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern)); if (!pattern) goto out_free_expr; + pattern->flags = patflags; + ret = aclkw->parse(args, pattern, &opaque); if (!ret) goto out_free_pattern; diff --git a/src/proto_http.c b/src/proto_http.c index 8a13a7184..f92d604c4 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -5283,6 +5283,8 @@ acl_fetch_meth(struct proxy *px, struct session *l4, void *l7, int dir, static int acl_match_meth(struct acl_test *test, struct acl_pattern *pattern) { + int icase; + if (test->i != pattern->val.i) return 0; @@ -5292,7 +5294,10 @@ static int acl_match_meth(struct acl_test *test, struct acl_pattern *pattern) /* Other method, we must compare the strings */ if (pattern->len != test->len) return 0; - if (strncmp(pattern->ptr.str, test->ptr, test->len) != 0) + + icase = pattern->flags & ACL_PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, test->ptr, test->len) != 0) || + (!icase && strncmp(pattern->ptr.str, test->ptr, test->len) != 0)) return 0; return 1; }