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
+
+
+
+
+ Name | Description |
+ -ipmatch |
+ IP address matches address/netmask |
+ -strmatch |
+ left string matches pattern given by right string (containing
+ wildcards *, ?, []) |
+ -strcmatch |
+ same as -strmatch , but case insensitive |
+ -fnmatch |
+ same 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;
}