From: Stefan Fritsch Date: Sun, 28 Nov 2010 16:35:14 +0000 (+0000) Subject: - add -ipmatch, -str(c)match, -fnmatch, -R operators to ap_expr X-Git-Tag: 2.3.10~155 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e27a3c21e32ec39dea1bd77944c81bf1d0a5a860;p=apache - add -ipmatch, -str(c)match, -fnmatch, -R operators to ap_expr - allow lookup function to pre-parse string constant arguments (used for subnet masks so far) - various bug fixes for binary operators - do strdup() for error messages created on the stack to avoid corruption git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1039900 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/docs/manual/expr.xml b/docs/manual/expr.xml index a5e046b7ad..fa2fe19167 100644 --- a/docs/manual/expr.xml +++ b/docs/manual/expr.xml @@ -218,6 +218,9 @@ listfunction ::= listfuncname "(" word ")" minus and at least two characters. The name is not case sensitive. Modules may register additional binary operators.

+
+ Comparison operators + @@ -259,6 +262,27 @@ listfunction ::= listfuncname "(" word ")"
ge Integer greater than or equal
+
+ +
+ Other binary operators + + + + + + + + + + + + + +
NameDescription
-ipmatchIP address matches address/netmask
-strmatchleft string matches pattern given by right string (containing + wildcards *, ?, [])
-strcmatchsame as -strmatch, but case insensitive
-fnmatchsame as -strmatch, but slashes are not matched by + wildcards
+
@@ -277,6 +301,9 @@ listfunction ::= listfuncname "(" word ")" String is not empty -z String is empty + -R + Same as "%{REMOTE_ADDR} -ipmatch ...", but more efficient + diff --git a/docs/manual/mod/mod_setenvif.xml b/docs/manual/mod/mod_setenvif.xml index b0062c27ee..c970500312 100644 --- a/docs/manual/mod/mod_setenvif.xml +++ b/docs/manual/mod/mod_setenvif.xml @@ -258,7 +258,7 @@ for additional examples. SetEnvIfExpr -Sets environment variables based on an expression +Sets environment variables based on an ap_expr expression SetEnvIfExpr expr [!]env-variable[=value] [[!]env-variable[=value]] ... @@ -281,11 +281,11 @@ for additional examples.

This would set the environment variable iso_delivered every time our application attempts to send it via X-Sendfile

-

For a more useful example, consider the Referer - example from above for a site with more than one domain:

+

A more useful example would be to set the variable rfc1918 if the + remote IP address is a private address according to RFC 1918:

- SetEnvIfExpr "${HTTP_REFERER} in { 'www.example.com','example.com','w2.example3.org' }" intra_site_referral + SetEnvIfExpr "-R '10.0.0.0/8' || -R '172.16.0.0/12' || -R '192.168.0.0/16'" rfc1918 diff --git a/include/ap_expr.h b/include/ap_expr.h index dbfff1302e..a23d9ba133 100644 --- a/include/ap_expr.h +++ b/include/ap_expr.h @@ -53,9 +53,7 @@ typedef struct { * operators) */ #define AP_EXPR_FLAGS_SSL_EXPR_COMPAT 1 -/** If using the simple ap_expr_exec(), don't add siginificant request headers - * to the Vary response header - */ +/** Don't add siginificant request headers to the Vary response header */ #define AP_EXPR_FLAGS_DONT_VARY 2 @@ -126,6 +124,8 @@ typedef struct { * * During parsing, the parser calls the lookup function to resolve a * name into a function pointer and an opaque context for the function. + * If the argument to a function or operator is constant, the lookup function + * may also parse that argument and store the parsed data in the context. * * The default lookup function is the hook 'ap_expr_lookup_default' which just * calls ap_expr_lookup_default. Modules can use it to make functions and @@ -204,8 +204,12 @@ typedef struct { const void **func; /** where to store the function's context */ const void **data; - /** Where to store the error message (if any) */ + /** where to store the error message (if any) */ const char **err; + + /** arg for pre-parsing (only if a simple string). + * For binary ops, this is the right argument. */ + const char *arg; } ap_expr_lookup_parms; /** Function for looking up the provider function for a variable, operator diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c index ddd94c1a63..9e7dc272f8 100644 --- a/server/util_expr_eval.c +++ b/server/util_expr_eval.c @@ -26,6 +26,7 @@ #include "util_expr_private.h" #include "apr_lib.h" +#include "apr_fnmatch.h" APLOG_USE_MODULE(core); @@ -53,8 +54,6 @@ static const char *ap_expr_eval_word(ap_expr_eval_ctx *ctx, const ap_expr *node) const char *result = ""; switch (node->node_op) { case op_Digit: - result = node->node_arg1; - break; case op_String: result = node->node_arg1; break; @@ -415,8 +414,9 @@ ap_expr *ap_expr_make(ap_expr_node_op op, const void *a1, const void *a2, return node; } - -static ap_expr *ap_expr_info_make(int type, const char *name, ap_expr_parse_ctx *ctx) +static ap_expr *ap_expr_info_make(int type, const char *name, + ap_expr_parse_ctx *ctx, + const ap_expr *arg) { ap_expr *info = apr_palloc(ctx->pool, sizeof(ap_expr)); ap_expr_lookup_parms parms; @@ -428,6 +428,7 @@ static ap_expr *ap_expr_info_make(int type, const char *name, ap_expr_parse_ctx parms.func = &info->node_arg1; parms.data = &info->node_arg2; parms.err = &ctx->error2; + parms.arg = (arg && arg->node_op == op_String) ? arg->node_arg1 : NULL; if (ctx->lookup_fn(&parms) != OK) return NULL; return info; @@ -436,7 +437,7 @@ static ap_expr *ap_expr_info_make(int type, const char *name, ap_expr_parse_ctx ap_expr *ap_expr_str_func_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { - ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx); + ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg); if (!info) return NULL; @@ -447,7 +448,7 @@ ap_expr *ap_expr_str_func_make(const char *name, const ap_expr *arg, ap_expr *ap_expr_list_func_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { - ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx); + ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx, arg); if (!info) return NULL; @@ -458,7 +459,7 @@ ap_expr *ap_expr_list_func_make(const char *name, const ap_expr *arg, ap_expr *ap_expr_unary_op_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { - ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx); + ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx, arg); if (!info) return NULL; @@ -470,7 +471,7 @@ ap_expr *ap_expr_binary_op_make(const char *name, const ap_expr *arg1, const ap_expr *arg2, ap_expr_parse_ctx *ctx) { ap_expr *args; - ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx); + ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_BINARY, name, ctx, arg2); if (!info) return NULL; @@ -482,7 +483,7 @@ ap_expr *ap_expr_binary_op_make(const char *name, const ap_expr *arg1, ap_expr *ap_expr_var_make(const char *name, ap_expr_parse_ctx *ctx) { - ap_expr *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx); + ap_expr *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx, NULL); if (!node) return NULL; @@ -1155,10 +1156,87 @@ static const char *misc_var_fn(ap_expr_eval_ctx *ctx, const void *data) return NULL; } +static int subnet_parse_arg(ap_expr_lookup_parms *parms) +{ + apr_ipsubnet_t *subnet; + const char *addr = parms->arg; + const char *mask; + apr_status_t ret; + + if (!parms->arg) { + *parms->err = apr_psprintf(parms->ptemp, + "-%s requires subnet/netmask as constant argument", + parms->name); + return !OK; + } + + mask = ap_strchr_c(addr, '/'); + if (mask) { + addr = apr_pstrmemdup(parms->ptemp, addr, mask - addr); + mask++; + } + + ret = apr_ipsubnet_create(&subnet, addr, mask, parms->pool); + if (ret != APR_SUCCESS) { + *parms->err = "parsing of subnet/netmask failed"; + return !OK; + } + + *parms->data = subnet; + return OK; +} + +static int op_ipmatch(ap_expr_eval_ctx *ctx, const void *data, const char *arg1, + const char *arg2) +{ + apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data; + apr_sockaddr_t *saddr; + + AP_DEBUG_ASSERT(subnet != NULL); + + /* maybe log an error if this goes wrong? */ + if (apr_sockaddr_info_get(&saddr, arg1, APR_UNSPEC, 0, 0, ctx->p) != APR_SUCCESS) + return FALSE; + + return apr_ipsubnet_test(subnet, saddr); +} + +static int op_R(ap_expr_eval_ctx *ctx, const void *data, const char *arg1) +{ + apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data; + + AP_DEBUG_ASSERT(subnet != NULL); + + if (!ctx->c) + return FALSE; + + return apr_ipsubnet_test(subnet, ctx->c->remote_addr); +} + +static int op_fnmatch(ap_expr_eval_ctx *ctx, const void *data, const char *arg1, + const char *arg2) +{ + return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_PATHNAME)); +} + +static int op_strmatch(ap_expr_eval_ctx *ctx, const void *data, const char *arg1, + const char *arg2) +{ + return (APR_SUCCESS == apr_fnmatch(arg2, arg1, 0)); +} + +static int op_strcmatch(ap_expr_eval_ctx *ctx, const void *data, const char *arg1, + const char *arg2) +{ + return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_CASE_BLIND)); +} + struct expr_provider_single { const void *func; const char *name; + ap_expr_lookup_fn *arg_parsing_func; }; + struct expr_provider_multi { const void *func; const char **names; @@ -1173,71 +1251,89 @@ static const struct expr_provider_multi var_providers[] = { }; static const struct expr_provider_single string_func_providers[] = { - { osenv_func, "osenv" }, - { env_func, "env" }, - { req_table_func, "resp" }, - { req_table_func, "req" }, + { osenv_func, "osenv", NULL }, + { env_func, "env", NULL }, + { req_table_func, "resp", NULL }, + { req_table_func, "req", NULL }, /* 'http' as alias for 'req' for compatibility with ssl_expr */ - { req_table_func, "http" }, - { req_table_func, "note" }, - { req_table_func, "reqenv" }, - { tolower_func, "tolower" }, - { toupper_func, "toupper" }, - { escape_func, "escape" }, - { unescape_func, "unescape" }, - { file_func, "file" }, - { NULL, NULL} + { req_table_func, "http", NULL }, + { req_table_func, "note", NULL }, + { req_table_func, "reqenv", NULL }, + { tolower_func, "tolower", NULL }, + { toupper_func, "toupper", NULL }, + { escape_func, "escape", NULL }, + { unescape_func, "unescape", NULL }, + { file_func, "file", NULL }, + { NULL, NULL, NULL} }; /* XXX: base64 encode/decode ? */ static const struct expr_provider_single unary_op_providers[] = { - { op_nz, "n" }, - { op_nz, "z" }, - { NULL, NULL} + { op_nz, "n", NULL }, + { op_nz, "z", NULL }, + { op_R, "R", subnet_parse_arg }, + { NULL, NULL, NULL } +}; + +static const struct expr_provider_single binary_op_providers[] = { + { op_ipmatch, "ipmatch", subnet_parse_arg }, + { op_fnmatch, "fnmatch", NULL }, + { op_strmatch, "strmatch", NULL }, + { op_strcmatch, "strcmatch", NULL }, + { NULL, NULL, NULL } }; + static int core_expr_lookup(ap_expr_lookup_parms *parms) { switch (parms->type) { case AP_EXPR_FUNC_VAR: { - const struct expr_provider_multi *prov = var_providers; - while (prov->func) { - const char **name = prov->names; - while (*name) { - if (strcasecmp(*name, parms->name) == 0) { - *parms->func = prov->func; - *parms->data = name; - return OK; + const struct expr_provider_multi *prov = var_providers; + while (prov->func) { + const char **name = prov->names; + while (*name) { + if (strcasecmp(*name, parms->name) == 0) { + *parms->func = prov->func; + *parms->data = name; + return OK; + } + name++; } - name++; + prov++; } - prov++; } break; - } - case AP_EXPR_FUNC_STRING: { - const struct expr_provider_single *prov = string_func_providers; - while (prov->func) { - if (strcasecmp(prov->name, parms->name) == 0) { - *parms->func = prov->func; - *parms->data = prov->name; - return OK; + case AP_EXPR_FUNC_STRING: + case AP_EXPR_FUNC_OP_UNARY: + case AP_EXPR_FUNC_OP_BINARY: { + const struct expr_provider_single *prov; + switch (parms->type) { + case AP_EXPR_FUNC_STRING: + prov = string_func_providers; + break; + case AP_EXPR_FUNC_OP_UNARY: + prov = unary_op_providers; + break; + case AP_EXPR_FUNC_OP_BINARY: + prov = binary_op_providers; + break; + default: + ap_assert(0); } - prov++; - } - break; - } - case AP_EXPR_FUNC_OP_UNARY: { - const struct expr_provider_single *prov = unary_op_providers; - while (prov->func) { - if (strcasecmp(prov->name, parms->name) == 0) { - *parms->func = prov->func; - *parms->data = prov->name; - return OK; + while (prov->func) { + if (strcasecmp(prov->name, parms->name) == 0) { + *parms->func = prov->func; + if (prov->arg_parsing_func) { + return prov->arg_parsing_func(parms); + } + else { + *parms->data = prov->name; + return OK; + } + } + prov++; } - prov++; } break; - } default: break; } diff --git a/server/util_expr_parse.y b/server/util_expr_parse.y index 3810258718..d125fa69bb 100644 --- a/server/util_expr_parse.y +++ b/server/util_expr_parse.y @@ -117,6 +117,7 @@ expr : T_TRUE { $$ = ap_expr_make(op_True, NUL | expr T_OP_AND expr { $$ = ap_expr_make(op_And, $1, $3, ctx); } | comparison { $$ = ap_expr_make(op_Comp, $1, NULL, ctx); } | T_OP_UNARY word { $$ = ap_expr_unary_op_make( $1, $2, ctx); } + | word T_OP_BINARY word { $$ = ap_expr_binary_op_make($2, $1, $3, ctx); } | '(' expr ')' { $$ = $2; } ; @@ -135,7 +136,6 @@ comparison: word T_OP_EQ word { $$ = ap_expr_make(op_EQ, $1, $3, | word T_OP_IN wordlist { $$ = ap_expr_make(op_IN, $1, $3, ctx); } | word T_OP_REG regex { $$ = ap_expr_make(op_REG, $1, $3, ctx); } | word T_OP_NRE regex { $$ = ap_expr_make(op_NRE, $1, $3, ctx); } - | word T_OP_BINARY word { $$ = ap_expr_binary_op_make($2, $1, $3, ctx); } ; wordlist : lstfunccall { $$ = $1; } @@ -205,6 +205,7 @@ strfunccall : T_ID '(' word ')' { $$ = ap_expr_str_func_make($1, $3, ctx); } void yyerror(ap_expr_parse_ctx *ctx, char *s) { - ctx->error = s; + /* s is allocated on the stack */ + ctx->error = apr_pstrdup(ctx->ptemp, s); } diff --git a/server/util_expr_scan.l b/server/util_expr_scan.l index 6ca4afe9ed..fca0c82f98 100644 --- a/server/util_expr_scan.l +++ b/server/util_expr_scan.l @@ -316,7 +316,7 @@ return T_OP_UNARY; } -"-"[a-zA-Z_][a-zA-Z_0-9] { +"-"[a-zA-Z_][a-zA-Z_0-9]+ { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1); return T_OP_BINARY; }