From: Alan T. DeKok Date: Mon, 28 Aug 2023 20:59:05 +0000 (-0400) Subject: add fr_regex_cmp_op() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f27e0a96a2974d3c67e1cdbe0522efbf0c984dd;p=thirdparty%2Ffreeradius-server.git add fr_regex_cmp_op() as a mirror to fr_value_box_cmp_op(), and which is called from that function. If the LHS isn't a string / octets, the LHS is printed to an intermediate buffer, and that is used for the regex. --- diff --git a/src/lib/util/regex.c b/src/lib/util/regex.c index f7a211712c4..e06c58644a6 100644 --- a/src/lib/util/regex.c +++ b/src/lib/util/regex.c @@ -1340,3 +1340,72 @@ ssize_t regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const *flags) FR_SBUFF_SET_RETURN(sbuff, &our_sbuff); } #endif + +/** Compare two boxes using an operator + * + * @todo - allow /foo/i on the RHS + * + * However, this involves allocating intermediate sbuffs for the + * unescaped RHS, and all kinds of extra work. It's not overly hard, + * but it's something we wish to avoid for now. + * + * @param[in] op to use in comparison. MUST be T_OP_REG_EQ or T_OP_REG_NE + * @param[in] a Value to compare, MUST be FR_TYPE_STRING + * @param[in] b uncompiled regex as FR_TYPE_STRING + * @return + * - 1 if true + * - 0 if false + * - -1 on failure. + */ +int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b) +{ + int rcode; + TALLOC_CTX *ctx = NULL; + size_t lhs_len; + char const *lhs; + regex_t *regex; + + if (!((op == T_OP_REG_EQ) || (op == T_OP_REG_NE))) { + fr_strerror_const("Invalid operator for regex comparison"); + return -1; + } + + if (b->type != FR_TYPE_STRING) { + fr_strerror_const("RHS must be regular expression"); + return -1; + } + + ctx = talloc_init_const("regex_cmp_op"); + if (!ctx) return -1; + + if ((a->type != FR_TYPE_STRING) && (a->type != FR_TYPE_OCTETS)) { + fr_slen_t slen; + char *p; + + slen = fr_value_box_aprint(ctx, &p, a, NULL); /* no escaping */ + if (slen < 0) return slen; + + lhs = p; + lhs_len = slen; + + } else { + lhs = a->vb_strvalue; + lhs_len = b->vb_length; + } + + if (regex_compile(ctx, ®ex, b->vb_strvalue, b->vb_length, NULL, false, true) < 0) { + talloc_free(ctx); + return -1; + } + + rcode = regex_exec(regex, lhs, lhs_len, NULL); + talloc_free(ctx); + if (rcode < 0) return rcode; + + /* + * Invert the sense of the rcode for !~ + */ + if (op == T_OP_REG_NE) rcode = (rcode == 0); + + return rcode; +} diff --git a/src/lib/util/regex.h b/src/lib/util/regex.h index 9165f84def4..1b1b83d31ac 100644 --- a/src/lib/util/regex.h +++ b/src/lib/util/regex.h @@ -31,6 +31,7 @@ extern "C" { #include #include #include +#include #include #include @@ -173,7 +174,7 @@ ssize_t regex_flags_parse(int *err, fr_regex_flags_t *out, fr_sbuff_t *in, ssize_t regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const *flags); -ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len, + ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len, fr_regex_flags_t const *flags, bool subcaptures, bool runtime); int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch); #ifdef HAVE_REGEX_PCRE2 @@ -184,6 +185,9 @@ int regex_substitute(TALLOC_CTX *ctx, char **out, size_t max_out, regex_t *preg #endif uint32_t regex_subcapture_count(regex_t const *preg); fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count); + +int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b) CC_HINT(nonnull); + # ifdef __cplusplus } # endif diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 8e850667879..74a1689e55e 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -861,6 +861,11 @@ static int fr_value_box_cidr_cmp_op(fr_token_t op, int bytes, return false; } +/* + * So we don't have to include in a recursive fashion. + */ +extern int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b); + /** Compare two attributes using an operator * * @param[in] op to use in comparison. @@ -878,6 +883,8 @@ int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t c if (!fr_cond_assert(a->type != FR_TYPE_NULL)) return -1; if (!fr_cond_assert(b->type != FR_TYPE_NULL)) return -1; + if (unlikely((op == T_OP_REG_EQ) || (op == T_OP_REG_NE))) return fr_regex_cmp_op(op, a, b); + switch (a->type) { case FR_TYPE_IPV4_ADDR: switch (b->type) {