]> granicus.if.org Git - apache/commitdiff
- add -ipmatch, -str(c)match, -fnmatch, -R operators to ap_expr
authorStefan Fritsch <sf@apache.org>
Sun, 28 Nov 2010 16:35:14 +0000 (16:35 +0000)
committerStefan Fritsch <sf@apache.org>
Sun, 28 Nov 2010 16:35:14 +0000 (16:35 +0000)
- 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

docs/manual/expr.xml
docs/manual/mod/mod_setenvif.xml
include/ap_expr.h
server/util_expr_eval.c
server/util_expr_parse.y
server/util_expr_scan.l

index a5e046b7adff61afcd1f9dee15cea4b975364735..fa2fe19167bf746b5b4aa20482fb41e39a4f0bab 100644 (file)
@@ -218,6 +218,9 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
     minus and at least two characters. The name is not case sensitive.
     Modules may register additional binary operators.</p>
 
+    <section id="comp">
+    <title>Comparison operators</title>
+
     <table border="1" style="zebra">
     <columnspec><column width=".2"/><column width=".2"/><column width=".6"/></columnspec>
 
@@ -259,6 +262,27 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
         <td><code>ge</code></td>
         <td>Integer greater than or equal</td></tr>
     </table>
+    </section>
+
+    <section id="binaryother">
+    <title>Other binary operators</title>
+
+    <table border="1" style="zebra">
+    <columnspec><column width=".2"/><column width=".8"/></columnspec>
+
+    <tr><th>Name</th><th>Description</th></tr>
+    <tr><td><code>-ipmatch</code></td>
+        <td>IP address matches address/netmask</td></tr>
+    <tr><td><code>-strmatch</code></td>
+        <td>left string matches pattern given by right string (containing
+            wildcards *, ?, [])</td></tr>
+    <tr><td><code>-strcmatch</code></td>
+        <td>same as <code>-strmatch</code>, but case insensitive</td></tr>
+    <tr><td><code>-fnmatch</code></td>
+        <td>same as <code>-strmatch</code>, but slashes are not matched by
+            wildcards</td></tr>
+    </table>
+    </section>
 
 </section>
 
@@ -277,6 +301,9 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
         <td>String is not empty</td></tr>
     <tr><td><code>-z</code></td>
         <td>String is empty</td></tr>
+    <tr><td><code>-R</code></td>
+        <td>Same as "<code>%{REMOTE_ADDR} -ipmatch ...</code>", but more efficient
+        </td></tr>
     </table>
 
 </section>
index b0062c27ee21ec096e42bfa62d84c78f14123b08..c970500312f09c080ffa09480329b0c6bb33a907 100644 (file)
@@ -258,7 +258,7 @@ for additional examples.
 <directivesynopsis>
 
 <name>SetEnvIfExpr</name>
-<description>Sets environment variables based on an expression</description>
+<description>Sets environment variables based on an ap_expr expression</description>
 <syntax>SetEnvIfExpr <em>expr
     [!]env-variable</em>[=<em>value</em>]
     [[!]<em>env-variable</em>[=<em>value</em>]] ...</syntax>
@@ -281,11 +281,11 @@ for additional examples.
     <p>This would set the environment variable <code>iso_delivered</code>
     every time our application attempts to send it via <code>X-Sendfile</code></p>
 
-    <p>For a more useful example, consider the <code>Referer</code>
-    example from above for a site with more than one domain:</p>
+    <p>A more useful example would be to set the variable rfc1918 if the
+    remote IP address is a private address according to RFC 1918:</p>
 
 <example>
-    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
 </example>
 </usage>
 
index dbfff1302e63be31bbeefa6fb1f71ce4b9d62d7d..a23d9ba1338fb07259fba618591b869c6340e25a 100644 (file)
@@ -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
index ddd94c1a63bd870464224979ce773a312197e8fd..9e7dc272f8edf7ee5a16942b5e08fdd9f0b4ec70 100644 (file)
@@ -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;
     }
index 381025871843af668af2347147750fbe13c5ab14..d125fa69bbb2452c0f7356607cf66fc7d7f78154 100644 (file)
@@ -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);
 }
 
index 6ca4afe9ed8aa3d12ca0cb568a5fbe07addac62c..fca0c82f989b8f1fe6a49f614811f3dabb408808 100644 (file)
     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;
 }