Switch to new subcapture handling code
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 12 Dec 2014 20:54:19 +0000 (15:54 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 12 Dec 2014 20:54:59 +0000 (15:54 -0500)
src/include/radiusd.h
src/include/regex.h
src/lib/regex.c
src/main/evaluate.c
src/main/pair.c
src/main/regex.c
src/main/xlat.c

index 1db49e8..335b2b0 100644 (file)
@@ -537,7 +537,7 @@ void                rdebug_proto_pair_list(log_lvl_t level, REQUEST *, VALUE_PAIR *);
 int            log_err (char *);
 
 /* util.c */
-#define MEM(x) if (!(x)) { ERROR("Out of memory"); _fr_exit_now(__FILE__, __LINE__, 1); }
+#define MEM(x) if (!(x)) { ERROR("%s[%u] OUT OF MEMORY", __FILE__, __LINE__); _fr_exit_now(__FILE__, __LINE__, 1); }
 void (*reset_signal(int signo, void (*func)(int)))(int);
 int            rad_mkdir(char *directory, mode_t mode);
 size_t         rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen,
@@ -569,10 +569,25 @@ bool              fr_getgid(char const *name, gid_t *gid);
 #endif
 
 /* regex.c */
-#define REQUEST_DATA_REGEX (0xadbeef00)
-#define REQUEST_MAX_REGEX (8)
 
-void regex_sub_to_request(REQUEST *request, char const *value, regmatch_t rxmatch[], size_t nmatch);
+#ifdef HAVE_REGEX
+/*
+ *     Increasing this is essentially free
+ *     It just increases memory usage. 12 bytes for each additional subcapture.
+ */
+#  define REQUEST_MAX_REGEX (32)
+
+void   regex_sub_to_request(REQUEST *request, char const *value, size_t len, regmatch_t rxmatch[], size_t nmatch);
+
+int    regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num);
+
+/*
+ *     Named capture groups only supported by PCRE.
+ */
+#  ifdef HAVE_PCRE
+int    regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name);
+#  endif
+#endif
 
 /* client.c */
 RADCLIENT_LIST *clients_init(CONF_SECTION *cs);
index 5fc0baa..1be4996 100644 (file)
@@ -29,7 +29,6 @@ RCSIDH(regex_h, "$Id$")
 #  ifdef __cplusplus
 extern "C" {
 #  endif
-#undef HAVE_PCRE
 #  ifdef HAVE_PCRE
 #    include <pcre.h>
 /*
index 349a019..4413af2 100644 (file)
@@ -49,6 +49,22 @@ static int _regex_free(regex_t *preg)
        return 0;
 }
 
+/*
+ *     Replace the libpcre malloc and free functions with
+ *     talloc wrappers. This allows us to use the subcapture copy
+ *     functions and just reparent the memory allocated.
+ */
+static void *_pcre_malloc(size_t to_alloc) {
+       return talloc_array(NULL, uint8_t, to_alloc);
+}
+
+static void _pcre_free(void *to_free) {
+       talloc_free(to_free);
+}
+
+extern
+
+
 /** Wrapper around pcre_compile
  *
  * Allows the rest of the code to do compilations using one function signature.
@@ -70,6 +86,16 @@ ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_
        int cflags = 0;
        regex_t *preg;
 
+       static bool setup;
+
+       /*
+        *      Lets us use subcapture copy
+        */
+       if (!setup) {
+               pcre_malloc = _pcre_malloc;
+               pcre_free = _pcre_free;
+       }
+
        *out = NULL;
 
        if (len == 0) {
@@ -107,13 +133,13 @@ ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_
 /** Wrapper around pcre_exec
  *
  * @param preg The compiled expression.
- * @param string to match.
- * @param len Length of string.
+ * @param subject to match.
+ * @param len Length of subject.
  * @param pmatch Array of match pointers.
  * @param nmatch How big the match array is. Updated to number of matches.
  * @return -1 on error, 0 on no match, 1 on match.
  */
-int regex_exec(regex_t *preg, char const *string, size_t len, regmatch_t pmatch[], size_t *nmatch)
+int regex_exec(regex_t *preg, char const *subject, size_t len, regmatch_t pmatch[], size_t *nmatch)
 {
        int     ret;
        size_t  matches;
@@ -131,7 +157,7 @@ int regex_exec(regex_t *preg, char const *string, size_t len, regmatch_t pmatch[
                matches = *nmatch;
        }
 
-       ret = pcre_exec(preg->compiled, preg->extra, string, len, 0, eflags, (int *)pmatch, matches * 3);
+       ret = pcre_exec(preg->compiled, preg->extra, subject, len, 0, eflags, (int *)pmatch, matches * 3);
        if (ret < 0) {
                if (ret == PCRE_ERROR_NOMATCH) return 0;
 
@@ -205,15 +231,17 @@ ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_
        {
                char const *p;
 
-               p = strlen(pattern);
-               if ((p - pattern) != len) {
+               p = pattern;
+               p += strlen(pattern);
+
+               if ((size_t)(p - pattern) != len) {
                        fr_strerror_printf("Found null in pattern at offset %zu.  Pattern unsafe for compilation",
                                           (p - pattern));
                        return -(p - pattern);
                }
 
                preg = talloc_zero(ctx, regex_t);
-               if (!reg) return 0;
+               if (!preg) return 0;
 
                ret = regcomp(preg, pattern, cflags);
        }
@@ -248,12 +276,12 @@ ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_
  *  If it does, we fail and print the appropriate error message.
  *
  * @param preg The compiled expression.
- * @param string to match.
+ * @param subject to match.
  * @param pmatch Array of match pointers.
  * @param nmatch How big the match array is. Updated to number of matches.
  * @return -1 on error, 0 on no match, 1 on match.
  */
-int regex_exec(regex_t *preg, char const *string, size_t len, regmatch_t pmatch[], size_t *nmatch)
+int regex_exec(regex_t *preg, char const *subject, size_t len, regmatch_t pmatch[], size_t *nmatch)
 {
        int     ret;
        int     eflags = 0;
@@ -274,19 +302,21 @@ int regex_exec(regex_t *preg, char const *string, size_t len, regmatch_t pmatch[
 
 #ifndef HAVE_REGNEXEC
        {
-               char const *p
+               char const *p;
 
-               p = strlen(string);
-               if ((p - pattern) != len) {
-                       fr_strerror_printf("Found null in string at offset %zu.  String unsafe for evaluation",
-                                          (p - pattern));
+               p = subject;
+               p += strlen(subject);
+
+               if ((size_t)(p - subject) != len) {
+                       fr_strerror_printf("Found null in subject at offset %zu.  String unsafe for evaluation",
+                                          (p - subject));
                        return -1;
                }
                /* regexec does not seem to initialise unused elements */
-               ret = regexec(preg, string, matches, pmatch, eflags);
+               ret = regexec(preg, subject, matches, pmatch, eflags);
        }
 #else
-       ret = regnexec(preg, string, len, matches, pmatch, eflags);
+       ret = regnexec(preg, subject, len, matches, pmatch, eflags);
 #endif
        if (ret != 0) {
                if (ret != REG_NOMATCH) {
index 5a80390..775685c 100644 (file)
@@ -302,12 +302,12 @@ static int cond_do_regex(REQUEST *request, fr_cond_t const *c,
        switch (ret) {
        case 0:
                EVAL_DEBUG("CLEARING SUBCAPTURES");
-               regex_sub_to_request(request, NULL, NULL, 0);   /* clear out old entries */
+               regex_sub_to_request(request, NULL, 0, NULL, 0);        /* clear out old entries */
                break;
 
        case 1:
                EVAL_DEBUG("SETTING SUBCAPTURES");
-               regex_sub_to_request(request, lhs->strvalue, rxmatch, nmatch);
+               regex_sub_to_request(request, lhs->strvalue, lhs_len, rxmatch, nmatch);
                break;
 
        case -1:
index fa87639..7d40167 100644 (file)
@@ -118,7 +118,7 @@ int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *v
                }
 
                if (check->op == T_OP_REG_EQ) {
-                       regex_sub_to_request(request, value_p, rxmatch, nmatch);
+                       regex_sub_to_request(request, value_p, talloc_array_length(value_p) - 1, rxmatch, nmatch);
                        ret = (slen == 1) ? 0 : -1;
                } else {
                        ret = (slen != 1) ? 0 : -1;
index 08d3d96..287052a 100644 (file)
@@ -29,7 +29,16 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #ifdef HAVE_REGEX
-#  ifdef HAVE_PCRE
+
+#define REQUEST_DATA_REGEX (0xadbeef00)
+
+typedef struct regcapture {
+       regex_t         *preg;          //!< Compiled pattern.
+       char const      *value;         //!< Original string.
+       regmatch_t      *rxmatch;       //!< Match vectors.
+       size_t          nmatch;         //!< Number of match vectors.
+} regcapture_t;
+
 /** Adds subcapture values to request data
  *
  * Allows use of %{n} expansions.
@@ -39,129 +48,153 @@ RCSID("$Id$")
  * @param rxmatch Pointers into value.
  * @param nmatch Sizeof rxmatch.
  */
-void regex_sub_to_request(REQUEST *request, char const *value, regmatch_t rxmatch[], size_t nmatch)
+void regex_sub_to_request(REQUEST *request, char const *value, size_t len, regmatch_t rxmatch[], size_t nmatch)
 {
-       int i, old;
+       regcapture_t *old, *new;
        char *p;
-       int *ovector = (int *)rxmatch;
 
        /*
         *      Clear out old matches
         */
-       old = (int)request_data_get(request, request, REQUEST_DATA_REGEX);
-       RDEBUG4("Clearing %i previous subcapture values", old);
-       for (i = 0; i < old; i++) {
-               p = request_data_get(request, request, REQUEST_DATA_REGEX | (i + 1));
-               if (!p) {
-                       RDEBUG4("%%{%i}: Empty", i);
-                       continue;
-               }
-
-               RDEBUG4("%%{%i}: Clearing old value \"%s\"", i, p);
-               talloc_free(p);
+       old = request_data_get(request, request, REQUEST_DATA_REGEX);
+       if (old) {
+               RDEBUG4("Clearing %zu old matches", old->nmatch);
+               talloc_free(old);
+       } else {
+               RDEBUG4("No old matches");
        }
 
+       if (nmatch == 0) return;
+
+       rad_assert(rxmatch);
+
+       RDEBUG4("Adding %zu new matches", nmatch);
        /*
-        *      Add new %{0}, %{1}, etc.
+        *      Add new matches
         */
-       RDEBUG4("Storing %zu new subcapture values", nmatch);
-       for (i = 0; i < (int)nmatch; i++) {
-               char    const *start;
-               size_t  len;
+       MEM(new = talloc(request, regcapture_t));
 
-               len = ovector[(2 * i) + 1] - ovector[2 * i];
-               start = value + ovector[i * 2];
+       MEM(new->rxmatch = talloc_memdup(new, rxmatch, sizeof(rxmatch[0]) * nmatch));
+       talloc_set_type(new->rxmatch, regmatch_t *);
 
-               /*
-                *      Using talloc for the buffers gives
-                *      consumers the length too.
-                */
-               MEM(p = talloc_array(request, char, len + 1));
-               memcpy(p, start, len);
-               p[len] = '\0';
+       MEM(p = talloc_array(new, char, len + 1));
+       memcpy(p, value, len);
+       p[len] = '\0';
+       new->value = p;
+
+       new->nmatch = nmatch;
+
+       request_data_add(request, request, REQUEST_DATA_REGEX, new, true);
+}
+
+#  ifdef HAVE_PCRE
+/** Extract a subcapture value from the request
+ *
+ * @note This is the PCRE variant of the function.
+ *
+ * @param ctx To allocate subcapture buffer in.
+ * @param out Where to write the subcapture string.
+ * @param request to extract.
+ * @param num Subcapture index (0 for entire match).
+ * @return 0 on success, -1 on notfound.
+ */
+int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
+{
+       regcapture_t *cap;
+       char const *p;
+       int ret;
+
+       cap = request_data_reference(request, request, REQUEST_DATA_REGEX);
+       if (!cap) {
+               RDEBUG4("No subcapture data found");
+               *out = NULL;
+               return 1;
+       }
+
+       ret = pcre_get_substring(cap->value, (int *)cap->rxmatch, (int)cap->nmatch, num, &p);
+       switch (ret) {
+       case PCRE_ERROR_NOMEMORY:
+               MEM(NULL);
+
+       /*
+        *      Not finding a substring is fine
+        */
+       case PCRE_ERROR_NOSUBSTRING:
+               RDEBUG4("%i/%zu Not found", num, cap->nmatch);
+               *out = NULL;
+               return -1;
+
+       default:
+               if (ret < 0) {
+                       *out = NULL;
+                       return -1;
+               }
 
-               RDEBUG4("%%{%i}: Inserting new value \"%s\"", i, p);
                /*
-                *      Copy substring, and add it to the request.
+                *      Check libpcre really is using our overloaded
+                *      malloc/free talloc wrappers.
                 */
-               request_data_add(request, request, REQUEST_DATA_REGEX | (i + 1), p, true);
-       }
+               p = (char *)talloc_get_type_abort(p, uint8_t);
+               talloc_set_type(p, char *);
+               talloc_steal(ctx, p);
+               memcpy(out, &p, sizeof(*out));
+
+               RDEBUG4("%i/%zu Found: %s (%zu)", num, cap->nmatch, p, talloc_array_length(p));
 
-       if (nmatch > 0) request_data_add(request, request, REQUEST_DATA_REGEX, (void *)nmatch, false);
+               return 0;
+       }
 }
-/*
- *     Wrapper functions for POSIX like, and extended regular
- *     expressions.  These use the system regex library.
- */
 #  else
-/** Adds subcapture values to request data
+/** Extract a subcapture value from the request
  *
- * Allows use of %{n} expansions.
+ * @note This is the POSIX variant of the function.
  *
- * @param request Current request.
- * @param value The original value.
- * @param rxmatch Pointers into value.
- * @param nmatch Sizeof rxmatch.
+ * @param ctx To allocate subcapture buffer in.
+ * @param out Where to write the subcapture string.
+ * @param request to extract.
+ * @param num Subcapture index (0 for entire match).
+ * @return 0 on success, -1 on notfound.
  */
-void regex_sub_to_request(REQUEST *request, char const *value, regmatch_t rxmatch[], size_t nmatch)
+int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
 {
-       int     i, old;
-       char    *p;
-       size_t  len;
+       regcapture_t    *cap;
+       char            *p;
+       char const      *start;
+       size_t          len;
+
+       cap = request_data_reference(request, request, REQUEST_DATA_REGEX);
+       if (!cap) {
+               RDEBUG4("No subcapture data found", num);
+               *out = NULL;
+               return -1;
+       }
 
        /*
-        *      Clear out old matches
+        *      Greater than our capture array
         */
-       old = (int)request_data_get(request, request, REQUEST_DATA_REGEX);
-       RDEBUG4("Clearing %i previous subcapture values", old);
-       for (i = 0; i < old; i++) {
-               p = request_data_get(request, request, REQUEST_DATA_REGEX | (i + 1));
-               if (!p) {
-                       RDEBUG4("%%{%i}: Empty", i);
-                       continue;
-               }
-
-               RDEBUG4("%%{%i}: Clearing old value \"%s\"", i, p);
-               talloc_free(p);
+       if ((num >= cap->nmatch) || (cap->rxmatch[num].rm_eo == -1) || (cap->rxmatch[num].rm_so == -1)) {
+               RDEBUG4("%i/%zu Not found", num, cap->nmatch);
+               *out = NULL;
+               return -1;
        }
 
        /*
-        *      Add new %{0}, %{1}, etc.
+        *      Sanity checks on the offsets
         */
-       RDEBUG4("Storing %zu new subcapture values", nmatch);
-       for (i = 0; i < (int)nmatch; i++) {
-               /*
-                *      Empty capture
-                */
-               if (rxmatch[i].rm_eo == -1) continue;
+       rad_assert(cap->rxmatch[num].rm_eo <= (regoff_t)talloc_array_length(cap->value));
+       rad_assert(cap->rxmatch[num].rm_so <= (regoff_t)talloc_array_length(cap->value));
 
-               /*
-                *      Using talloc for the buffers gives
-                *      consumers the length too.
-                */
-               len = rxmatch[i].rm_eo - rxmatch[i].rm_so;
-               p = talloc_array(request, char, len + 1);
-               if (!p) {
-                       ERROR("Out of memory");
-                       return;
-               }
+       start = cap->value + cap->rxmatch[num].rm_so;
+       len = cap->rxmatch[num].rm_eo - cap->rxmatch[num].rm_so;
 
-               memcpy(p, value + rxmatch[i].rm_so, len);
-               p[len] = '\0';
+       RDEBUG4("%i/%zu Found: %.*s (%zu)", num, cap->nmatch, (int)len, start, len);
+       MEM(p = talloc_array(ctx, char, len + 1));
+       memcpy(p, start, len);
+       p[len] = '\0';
 
-               RDEBUG4("%%{%i}: Inserting new value \"%s\"", i, p);
-               /*
-                *      Copy substring, and add it to
-                *      the request.
-                *
-                *      Note that we don't check
-                *      for out of memory, which is
-                *      the only error we can get...
-                */
-               request_data_add(request, request, REQUEST_DATA_REGEX | (i + 1), p, true);
-       }
+       *out = p;
 
-       if (nmatch > 0) request_data_add(request, request, REQUEST_DATA_REGEX, (void *)nmatch, false);
+       return 0;
 }
 #  endif
 #endif
index 9a43be6..db59e3f 100644 (file)
@@ -89,10 +89,10 @@ static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
                                                  NULL};
 #endif
 
-#if REQUEST_MAX_REGEX > 8
+#if REQUEST_MAX_REGEX > 32
 #  error Please fix the following line
 #endif
-static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };        /* up to 8 for regex */
+static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 ,29, 30, 31, 32 }; /* up to 32 for regex */
 
 char const *radiusd_short_version = RADIUSD_VERSION_STRING;
 
@@ -2145,11 +2145,8 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
 #ifdef HAVE_REGEX
        case XLAT_REGEX:
                XLAT_DEBUG("xlat_aprint REGEX");
-               child = request_data_reference(request, request,
-                                              REQUEST_DATA_REGEX | (node->attr.tmpl_num + 1));
-               if (!child) return NULL;
+               if (regex_request_to_sub(ctx, &str, request, node->attr.tmpl_num) < 0) return NULL;
 
-               str = talloc_typed_strdup(ctx, child);
                break;
 #endif