diff options
Diffstat (limited to 'src/main/regex.c')
-rw-r--r-- | src/main/regex.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/main/regex.c b/src/main/regex.c new file mode 100644 index 0000000..f66414c --- /dev/null +++ b/src/main/regex.c @@ -0,0 +1,279 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * $Id$ + * + * @file main/regex.c + * @brief Regular expression functions used by the server library. + * + * @copyright 2014 The FreeRADIUS server project + */ + +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#ifdef HAVE_REGEX + +#define REQUEST_DATA_REGEX (0xadbeef00) + +typedef struct regcapture { +#ifdef HAVE_PCRE + regex_t *preg; //!< Compiled pattern. +#endif + 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. + * + * @note After calling regex_sub_to_request *preg may no longer be valid and + * should be passed to talloc_free. + * + * @param request Current request. + * @param preg Compiled pattern. May be set to NULL if reparented to the regcapture struct. + * @param value The original value. + * @param rxmatch Pointers into value. + * @param nmatch Sizeof rxmatch. + */ +void regex_sub_to_request(REQUEST *request, regex_t **preg, char const *value, size_t len, + regmatch_t rxmatch[], size_t nmatch) +{ + regcapture_t *old_sc, *new_sc; /* lldb doesn't like new *sigh* */ + char *p; + + /* + * Clear out old_sc matches + */ + old_sc = request_data_get(request, request, REQUEST_DATA_REGEX); + if (old_sc) { + DEBUG4("Clearing %zu old matches", old_sc->nmatch); + talloc_free(old_sc); + } else { + DEBUG4("No old matches"); + } + + if (nmatch == 0) return; + + rad_assert(preg && *preg); + rad_assert(rxmatch); + + DEBUG4("Adding %zu matches", nmatch); + + /* + * Add new_sc matches + */ + MEM(new_sc = talloc(request, regcapture_t)); + + MEM(new_sc->rxmatch = talloc_memdup(new_sc, rxmatch, sizeof(rxmatch[0]) * nmatch)); + talloc_set_type(new_sc->rxmatch, regmatch_t[]); + + MEM(p = talloc_array(new_sc, char, len + 1)); + memcpy(p, value, len); + p[len] = '\0'; + new_sc->value = p; + new_sc->nmatch = nmatch; + +#ifdef HAVE_PCRE + if (!(*preg)->precompiled) { + new_sc->preg = talloc_steal(new_sc, *preg); + *preg = NULL; + } else { + new_sc->preg = *preg; + } +#endif + + request_data_add(request, request, REQUEST_DATA_REGEX, new_sc, 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); + /* FALL-THROUGH */ + + /* + * 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; + } + + /* + * Check libpcre really is using our overloaded + * malloc/free talloc wrappers. + */ + 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)); + + return 0; + } +} + +/** Extract a named 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 name of subcapture. + * @return 0 on success, -1 on notfound. + */ +int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, char const *name) +{ + 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_named_substring(cap->preg->compiled, cap->value, + (int *)cap->rxmatch, (int)cap->nmatch, name, &p); + switch (ret) { + case PCRE_ERROR_NOMEMORY: + MEM(NULL); + /* FALL-THROUGH */ + + /* + * Not finding a substring is fine + */ + case PCRE_ERROR_NOSUBSTRING: + RDEBUG4("No named capture group \"%s\"", name); + *out = NULL; + return -1; + + default: + if (ret < 0) { + *out = NULL; + return -1; + } + + /* + * Check libpcre really is using our overloaded + * malloc/free talloc wrappers. + */ + p = (char *)talloc_get_type_abort(p, uint8_t); + talloc_set_type(p, char *); + talloc_steal(ctx, p); + memcpy(out, &p, sizeof(*out)); + + RDEBUG4("Found \"%s\": %s (%zu)", name, p, talloc_array_length(p)); + + return 0; + } +} +# else +/** Extract a subcapture value from the request + * + * @note This is the POSIX 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 *p; + char const *start; + size_t len; + + cap = request_data_reference(request, request, REQUEST_DATA_REGEX); + if (!cap) { + RDEBUG4("No subcapture data found"); + *out = NULL; + return -1; + } + + /* + * Greater than our capture array + * + * -1 means no value in this capture group. + */ + 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; + } + + /* + * Sanity checks on the offsets + */ + 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)); + + start = cap->value + cap->rxmatch[num].rm_so; + len = cap->rxmatch[num].rm_eo - cap->rxmatch[num].rm_so; + + 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'; + + *out = p; + + return 0; +} +# endif +#endif |