]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add fr_regex_cmp_op()
authorAlan T. DeKok <aland@freeradius.org>
Mon, 28 Aug 2023 20:59:05 +0000 (16:59 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 28 Aug 2023 21:02:08 +0000 (17:02 -0400)
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.

src/lib/util/regex.c
src/lib/util/regex.h
src/lib/util/value.c

index f7a211712c4b0b844375ee2355424dbe3db1276b..e06c58644a6c52ddcf0ecb6e001c2a0971c87b22 100644 (file)
@@ -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, &regex, 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;
+}
index 9165f84def427fcb69ccb3dcb7f097f8a06c4394..1b1b83d31ac4cd6935d0753cf041e0d369cabbbe 100644 (file)
@@ -31,6 +31,7 @@ extern "C" {
 #include <freeradius-devel/missing.h>
 #include <freeradius-devel/util/sbuff.h>
 #include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/value.h>
 
 #include <stdbool.h>
 #include <stdint.h>
@@ -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
index 8e850667879791844edff8a5971336be6c432ae7..74a1689e55e9c9a77297303f65b0349c3d302958 100644 (file)
@@ -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 <util/regex.h> 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) {