The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
regex.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Wrappers around various regular expression libraries
18 *
19 * @file src/lib/util/regex.c
20 *
21 * @copyright 2014 The FreeRADIUS server project
22 * @copyright 2014 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 89efb453464036168dd77e7eacac9485b2452565 $")
25
26#ifdef HAVE_REGEX
27
28#include <freeradius-devel/util/regex.h>
29#include <freeradius-devel/util/atexit.h>
30
31#if defined(HAVE_REGEX_PCRE) || (defined(HAVE_REGEX_PCRE2) && defined(PCRE2_CONFIG_JIT))
32#ifndef FR_PCRE_JIT_STACK_MIN
33# define FR_PCRE_JIT_STACK_MIN (128 * 1024)
34#endif
35#ifndef FR_PCRE_JIT_STACK_MAX
36# define FR_PCRE_JIT_STACK_MAX (512 * 1024)
37#endif
38#endif
39
40const fr_sbuff_escape_rules_t regex_escape_rules = {
41 .name = "regex",
42 .chr = '\\',
43 .subs = {
44 ['$'] = '$',
45 ['('] = '(',
46 ['*'] = '*',
47 ['+'] = '+',
48 ['.'] = '.',
49 ['/'] = '/',
50 ['?'] = '?',
51 ['['] = '[',
52 ['\\'] = '\\',
53 ['^'] = '^',
54 ['`'] = '`',
55 ['|'] = '|',
56 ['\a'] = 'a',
57 ['\b'] = 'b',
58 ['\n'] = 'n',
59 ['\r'] = 'r',
60 ['\t'] = 't',
61 ['\v'] = 'v'
62 },
63 .esc = {
66 },
67 .do_utf8 = true,
68 .do_oct = true
69};
70
71
72/*
73 *######################################
74 *# FUNCTIONS FOR LIBPCRE2 #
75 *######################################
76 */
77#ifdef HAVE_REGEX_PCRE2
78/*
79 * Wrapper functions for libpcre2. Much more powerful, and guaranteed
80 * to be binary safe for both patterns and subjects but require
81 * libpcre2.
82 */
83
84/** Thread local storage for PCRE2
85 *
86 * Not all this storage is thread local, but it simplifies cleanup if
87 * we bind its lifetime to the thread, and lets us get away with not
88 * having specific init/free functions.
89 */
90typedef struct {
91 TALLOC_CTX *alloc_ctx; //!< Context used for any allocations.
92 pcre2_general_context *gcontext; //!< General context.
93 pcre2_compile_context *ccontext; //!< Compile context.
94 pcre2_match_context *mcontext; //!< Match context.
95#ifdef PCRE2_CONFIG_JIT
96 pcre2_jit_stack *jit_stack; //!< Jit stack for executing jit'd patterns.
97 bool do_jit; //!< Whether we have runtime JIT support.
98#endif
99} fr_pcre2_tls_t;
100
101/** Thread local storage for pcre2
102 *
103 */
104static _Thread_local fr_pcre2_tls_t *fr_pcre2_tls;
105
106/** Talloc wrapper for pcre2 memory allocation
107 *
108 * @param[in] to_alloc How many bytes to alloc.
109 * @param[in] uctx UNUSED.
110 */
111static void *_pcre2_talloc(PCRE2_SIZE to_alloc, UNUSED void *uctx)
112{
113 return talloc_array(fr_pcre2_tls->alloc_ctx, uint8_t, to_alloc);
114}
115
116/** Talloc wrapper for pcre2 memory freeing
117 *
118 * @param[in] to_free Memory to free.
119 * @param[in] uctx UNUSED.
120 */
121static void _pcre2_talloc_free(void *to_free, UNUSED void *uctx)
122{
123 talloc_free(to_free);
124}
125
126/** Free thread local data
127 *
128 * @param[in] tls Thread local data to free.
129 */
130static int _pcre2_tls_free(fr_pcre2_tls_t *tls)
131{
132 if (tls->gcontext) pcre2_general_context_free(tls->gcontext);
133 if (tls->ccontext) pcre2_compile_context_free(tls->ccontext);
134 if (tls->mcontext) pcre2_match_context_free(tls->mcontext);
135#ifdef PCRE2_CONFIG_JIT
136 if (tls->jit_stack) pcre2_jit_stack_free(tls->jit_stack);
137#endif
138
139 return 0;
140}
141
142static int _pcre2_tls_free_on_exit(void *arg)
143{
144 return talloc_free(arg);
145}
146
147/** Thread local init for pcre2
148 *
149 */
150static int fr_pcre2_tls_init(void)
151{
152 fr_pcre2_tls_t *tls;
153
154 if (unlikely(fr_pcre2_tls != NULL)) return 0;
155
156 fr_pcre2_tls = tls = talloc_zero(NULL, fr_pcre2_tls_t);
157 if (!tls) return -1;
158 talloc_set_destructor(tls, _pcre2_tls_free);
159
160 tls->gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL);
161 if (!tls->gcontext) {
162 fr_strerror_const("Failed allocating general context");
163 return -1;
164 }
165
166 tls->ccontext = pcre2_compile_context_create(tls->gcontext);
167 if (!tls->ccontext) {
168 fr_strerror_const("Failed allocating compile context");
169 error:
170 fr_pcre2_tls = NULL;
171 _pcre2_tls_free(tls);
172 return -1;
173 }
174
175 tls->mcontext = pcre2_match_context_create(tls->gcontext);
176 if (!tls->mcontext) {
177 fr_strerror_const("Failed allocating match context");
178 goto error;
179 }
180
181#ifdef PCRE2_CONFIG_JIT
182 pcre2_config(PCRE2_CONFIG_JIT, &tls->do_jit);
183 if (tls->do_jit) {
184 tls->jit_stack = pcre2_jit_stack_create(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX, tls->gcontext);
185 if (!tls->jit_stack) {
186 fr_strerror_const("Failed allocating JIT stack");
187 goto error;
188 }
189 pcre2_jit_stack_assign(tls->mcontext, NULL, tls->jit_stack);
190 }
191#endif
192
193 /*
194 * Free on thread exit
195 */
196 fr_atexit_thread_local(fr_pcre2_tls, _pcre2_tls_free_on_exit, tls);
197 fr_pcre2_tls = tls; /* Assign to thread local storage */
198
199 return 0;
200}
201
202/** Free regex_t structure
203 *
204 * Calls libpcre specific free functions for the expression and study.
205 *
206 * @param preg to free.
207 */
208static int _regex_free(regex_t *preg)
209{
210 if (preg->compiled) pcre2_code_free(preg->compiled);
211
212 return 0;
213}
214
215/** Wrapper around pcre2_compile
216 *
217 * Allows the rest of the code to do compilations using one function signature.
218 *
219 * @note Compiled expression must be freed with talloc_free.
220 *
221 * @param[out] out Where to write out a pointer to the structure containing
222 * the compiled expression.
223 * @param[in] pattern to compile.
224 * @param[in] len of pattern.
225 * @param[in] flags controlling matching. May be NULL.
226 * @param[in] subcaptures Whether to compile the regular expression to store subcapture
227 * data.
228 * @param[in] runtime If false run the pattern through the PCRE JIT (if available)
229 * to convert it to machine code. This trades startup time (longer)
230 * for runtime performance (better).
231 * @return
232 * - >= 1 on success.
233 * - <= 0 on error. Negative value is offset of parse error.
234 */
235ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
236 fr_regex_flags_t const *flags, bool subcaptures, bool runtime)
237{
238 int ret;
239 PCRE2_SIZE offset;
240 uint32_t cflags = 0;
241 regex_t *preg;
242
243 /*
244 * Check inputs
245 */
246 *out = NULL;
247
248 /*
249 * Thread local initialisation
250 */
251 if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1;
252
253 if (len == 0) {
254 fr_strerror_const("Empty expression");
255 return 0;
256 }
257
258 /*
259 * Options
260 */
261 if (flags) {
262 /* flags->global implemented by substitution function */
263 if (flags->ignore_case) cflags |= PCRE2_CASELESS;
264 if (flags->multiline) cflags |= PCRE2_MULTILINE;
265 if (flags->dot_all) cflags |= PCRE2_DOTALL;
266 if (flags->unicode) cflags |= PCRE2_UTF;
267 if (flags->extended) cflags |= PCRE2_EXTENDED;
268 }
269
270 if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE;
271
272 preg = talloc_zero(ctx, regex_t);
273 talloc_set_destructor(preg, _regex_free);
274
275 preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len,
276 cflags, &ret, &offset, fr_pcre2_tls->ccontext);
277 if (!preg->compiled) {
278 PCRE2_UCHAR errbuff[128];
279
280 pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
281 fr_strerror_printf("%s", (char *)errbuff);
282 talloc_free(preg);
283
284 return -(ssize_t)offset;
285 }
286
287 if (!runtime) {
288 preg->precompiled = true;
289
290#ifdef PCRE2_CONFIG_JIT
291 /*
292 * This is expensive, so only do it for
293 * expressions that are going to be
294 * evaluated repeatedly.
295 */
296 if (fr_pcre2_tls->do_jit) {
297 ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE);
298 if (ret < 0) {
299 PCRE2_UCHAR errbuff[128];
300
301 pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
302 fr_strerror_printf("Pattern JIT failed: %s", (char *)errbuff);
303 talloc_free(preg);
304
305 return 0;
306 }
307 preg->jitd = true;
308 }
309#endif
310 }
311
312 *out = preg;
313
314 return len;
315}
316
317/** Wrapper around pcre2_exec
318 *
319 * @param[in] preg The compiled expression.
320 * @param[in] subject to match.
321 * @param[in] len Length of subject.
322 * @param[in] regmatch Array of match pointers.
323 * @return
324 * - -1 on failure.
325 * - 0 on no match.
326 * - 1 on match.
327 */
328int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch)
329{
330 int ret;
331 uint32_t options = 0;
332
333 char *our_subject = NULL;
334 bool dup_subject = true;
335 pcre2_match_data *match_data;
336
337 /*
338 * Thread local initialisation
339 */
340 if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1;
341
342 if (regmatch) {
343#ifdef PCRE2_COPY_MATCHED_SUBJECT
344 /*
345 * This is apparently only supported for pcre2_match
346 * NOT pcre2_jit_match.
347 */
348# ifdef PCRE2_CONFIG_JIT
349 if (!preg->jitd) {
350# endif
351 dup_subject = false;
352
353 /*
354 * If PCRE2_COPY_MATCHED_SUBJECT is available
355 * and set as an options flag, pcre2_match will
356 * strdup the subject string if pcre2_match is
357 * successful and store a pointer to it in the
358 * regmatch struct.
359 *
360 * The lifetime of the string memory will be
361 * bound to the regmatch struct. This is more
362 * efficient that doing it ourselves, as the
363 * strdup only occurs if the subject matches.
364 */
365 options |= PCRE2_COPY_MATCHED_SUBJECT;
366# ifdef PCRE2_CONFIG_JIT
367 }
368# endif
369#endif
370 if (dup_subject) {
371 /*
372 * We have to dup and operate on the duplicate
373 * of the subject, because pcre2_jit_match and
374 * pcre2_match store a pointer to the subject
375 * in the regmatch structure.
376 */
377 subject = our_subject = talloc_bstrndup(regmatch, subject, len);
378 if (!subject) {
379 fr_strerror_const("Out of memory");
380 return -1;
381 }
382#ifndef NDEBUG
383 regmatch->subject = subject; /* Stored only for tracking memory issues */
384#endif
385 }
386 }
387
388 /*
389 * If we weren't given match data we
390 * need to alloc it else pcre2_match
391 * fails when passed NULL match data.
392 */
393 if (!regmatch) {
394 match_data = pcre2_match_data_create_from_pattern(preg->compiled, fr_pcre2_tls->gcontext);
395 if (!match_data) {
396 fr_strerror_const("Failed allocating temporary match data");
397 return -1;
398 }
399 } else {
400 match_data = regmatch->match_data;
401 }
402
403#ifdef PCRE2_CONFIG_JIT
404 if (preg->jitd) {
405 ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
406 match_data, fr_pcre2_tls->mcontext);
407 } else
408#endif
409 {
410 ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
411 match_data, fr_pcre2_tls->mcontext);
412 }
413 if (!regmatch) pcre2_match_data_free(match_data);
414 if (ret < 0) {
415 PCRE2_UCHAR errbuff[128];
416
417 if (dup_subject) talloc_free(our_subject);
418
419 if (ret == PCRE2_ERROR_NOMATCH) {
420 if (regmatch) regmatch->used = 0;
421 return 0;
422 }
423
424 pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
425 fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff);
426
427 return -1;
428 }
429
430 if (regmatch) regmatch->used = ret;
431
432 return 1;
433}
434
435/** Wrapper around pcre2_substitute
436 *
437 * @param[in] ctx to allocate output string in.
438 * @param[out] out Output string with replacements performed.
439 * @param[in] max_out Maximum length of output buffer. If this is 0 then
440 * the output length is unlimited.
441 * @param[in] preg The compiled expression.
442 * @param[in] flags that affect matching.
443 * @param[in] subject to perform replacements on.
444 * @param[in] subject_len the length of the subject.
445 * @param[in] replacement replacement string containing substitution
446 * markers.
447 * @param[in] replacement_len Length of the replacement string.
448 * @param[in] regmatch Array of match pointers.
449 * @return
450 * - >= 0 the length of the output string.
451 * - < 0 on error.
452 */
453int regex_substitute(TALLOC_CTX *ctx, char **out, size_t max_out, regex_t *preg, fr_regex_flags_t const *flags,
454 char const *subject, size_t subject_len,
455 char const *replacement, size_t replacement_len,
456 fr_regmatch_t *regmatch)
457{
458 int ret;
459 uint32_t options = 0;
460 size_t buff_len, actual_len;
461 char *buff;
462
463#ifndef PCRE2_COPY_MATCHED_SUBJECT
464 char *our_subject = NULL;
465#endif
466
467 /*
468 * Thread local initialisation
469 */
470 if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return -1;
471
472 /*
473 * Internally pcre2_substitute just calls pcre2_match to
474 * generate the match data, so the same hack as the
475 * regex_exec function above is required.
476 */
477 if (regmatch) {
478#ifndef PCRE2_COPY_MATCHED_SUBJECT
479 /*
480 * We have to dup and operate on the duplicate
481 * of the subject, because pcre2_jit_match and
482 * pcre2_match store a pointer to the subject
483 * in the regmatch structure.
484 */
485 subject = our_subject = talloc_bstrndup(regmatch, subject, subject_len);
486 if (!subject) {
487 fr_strerror_const("Out of memory");
488 return -1;
489 }
490#else
491 /*
492 * If PCRE2_COPY_MATCHED_SUBJECT is available
493 * and set as an options flag, pcre2_match will
494 * strdup the subject string if pcre2_match is
495 * successful and store a pointer to it in the
496 * regmatch struct.
497 *
498 * The lifetime of the string memory will be
499 * bound to the regmatch struct. This is more
500 * efficient that doing it ourselves, as the
501 * strdup only occurs if the subject matches.
502 */
503 options |= PCRE2_COPY_MATCHED_SUBJECT;
504#endif
505 }
506
507 /*
508 * Guess (badly) what the length of the output buffer should be
509 */
510 actual_len = buff_len = subject_len + 1; /* +1 for the \0 */
511 buff = talloc_array(ctx, char, buff_len);
512 if (!buff) {
513#ifndef PCRE2_COPY_MATCHED_SUBJECT
514 talloc_free(our_subject);
515#endif
516 fr_strerror_const("Out of memory");
517 return -1;
518 }
519
520 options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
521 if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL;
522
523again:
524 /*
525 * actual_len input value should be the size of the
526 * buffer including space for '\0'.
527 * If input buffer is too small, then actual_len will be set
528 * to the buffer space needed including space for '\0'.
529 * If input buffer is the correct size, then actual_len
530 * will be set to the size of the string written to buff
531 * without the terminating '\0'.
532 */
533 ret = pcre2_substitute(preg->compiled,
534 (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0,
535 options, NULL, fr_pcre2_tls->mcontext,
536 (PCRE2_UCHAR const *)replacement, replacement_len, (PCRE2_UCHAR *)buff, &actual_len);
537
538 if (ret < 0) {
539 PCRE2_UCHAR errbuff[128];
540
541#ifndef PCRE2_COPY_MATCHED_SUBJECT
542 talloc_free(our_subject);
543#endif
545
546 if (ret == PCRE2_ERROR_NOMEMORY) {
547 if ((max_out > 0) && (actual_len > max_out)) {
548 fr_strerror_printf("String length with substitutions (%zu) "
549 "exceeds max string length (%zu)", actual_len - 1, max_out - 1);
550 return -1;
551 }
552
553 /*
554 * Check that actual_len != buff_len as that'd be
555 * an actual error.
556 */
557 if (actual_len == buff_len) {
558 fr_strerror_const("libpcre2 out of memory");
559 return -1;
560 }
561 buff_len = actual_len; /* The length we get passed back includes the \0 */
562 buff = talloc_array(ctx, char, buff_len);
563 goto again;
564 }
565
566 if (ret == PCRE2_ERROR_NOMATCH) {
567 if (regmatch) regmatch->used = 0;
568 return 0;
569 }
570
571 pcre2_get_error_message(ret, errbuff, sizeof(errbuff));
572 fr_strerror_printf("regex evaluation failed with code (%i): %s", ret, errbuff);
573 return -1;
574 }
575
576 /*
577 * Trim the replacement buffer to the correct length
578 *
579 * buff_len includes \0.
580 * ...and as pcre2_substitute just succeeded actual_len does not include \0.
581 */
582 if (actual_len < (buff_len - 1)) {
583 buff = talloc_bstr_realloc(ctx, buff, actual_len);
584 if (!buff) {
585 fr_strerror_const("reallocing pcre2_substitute result buffer failed");
586 return -1;
587 }
588 }
589
590 if (regmatch) regmatch->used = ret;
591 *out = buff;
592
593 return 1;
594}
595
596
597/** Returns the number of subcapture groups
598 *
599 * @return
600 * - >0 The number of subcaptures contained within the pattern
601 * - 0 if the number of subcaptures can't be determined.
602 */
603uint32_t regex_subcapture_count(regex_t const *preg)
604{
606
607 if (pcre2_pattern_info(preg->compiled, PCRE2_INFO_CAPTURECOUNT, &count) != 0) {
608 fr_strerror_const("Error determining subcapture group count");
609 return 0;
610 }
611
612 return count + 1;
613}
614
615/** Free libpcre2's matchdata
616 *
617 * @note Don't call directly, will be called if talloc_free is called on a #regmatch_t.
618 */
619static int _pcre2_match_data_free(fr_regmatch_t *regmatch)
620{
621 pcre2_match_data_free(regmatch->match_data);
622 return 0;
623}
624
625/** Allocate vectors to fill with match data
626 *
627 * @param[in] ctx to allocate match vectors in.
628 * @param[in] count The number of vectors to allocate.
629 * @return
630 * - NULL on error.
631 * - Array of match vectors.
632 */
633fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count)
634{
635 fr_regmatch_t *regmatch;
636
637 /*
638 * Thread local initialisation
639 */
640 if (unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0)) return NULL;
641
642 regmatch = talloc(ctx, fr_regmatch_t);
643 if (!regmatch) {
644 oom:
645 fr_strerror_const("Out of memory");
646 return NULL;
647 }
648
649 regmatch->match_data = pcre2_match_data_create(count, fr_pcre2_tls->gcontext);
650 if (!regmatch->match_data) {
651 talloc_free(regmatch);
652 goto oom;
653 }
654 talloc_set_type(regmatch->match_data, pcre2_match_data);
655
656 talloc_set_destructor(regmatch, _pcre2_match_data_free);
657
658 return regmatch;
659}
660/*
661 *######################################
662 *# FUNCTIONS FOR LIBPCRE #
663 *######################################
664 */
665#elif defined(HAVE_REGEX_PCRE)
666/*
667 * Wrapper functions for libpcre. Much more powerful, and guaranteed
668 * to be binary safe but require libpcre.
669 */
670#if (PCRE_MAJOR >= 8) && (PCRE_MINOR >= 32) && defined(PCRE_CONFIG_JIT)
671# define HAVE_PCRE_JIT_EXEC 1
672#endif
673
674/** Thread local storage for PCRE
675 *
676 */
677typedef struct {
678 TALLOC_CTX *alloc_ctx; //!< Context used for any allocations.
679#ifdef HAVE_PCRE_JIT_EXEC
680 pcre_jit_stack *jit_stack;
681#endif
682} fr_pcre_tls_t;
683
684static _Thread_local fr_pcre_tls_t *fr_pcre_tls;
685static bool fr_pcre_study_flags;
686
687/*
688 * Replace the libpcre memory allocation and freeing functions
689 * with talloc wrappers. This allows us to use the subcapture copy
690 * functions and just reparent the memory allocated.
691 */
692static void *_pcre_talloc(size_t to_alloc)
693{
694 return talloc_array(fr_pcre_tls->alloc_ctx, uint8_t, to_alloc);
695}
696
697static void _pcre_talloc_free(void *to_free)
698{
699 talloc_free(to_free);
700}
701
702static int _pcre_globals_reset(UNUSED void *uctx)
703{
704 pcre_malloc = NULL;
705 pcre_free = NULL;
706 return 0;
707}
708
709static int _pcre_globals_configure(UNUSED void *uctx)
710{
711#ifdef PCRE_CONFIG_JIT
712 int *do_jit = 0;
713
714 /*
715 * If the headers are from >= 8.20
716 * check at runtime to see if this version
717 * of the libpcre library was compiled with
718 * JIT support.
719 */
720 pcre_config(PCRE_CONFIG_JIT, &do_jit);
721
722 if (do_jit) fr_pcre_study_flags |= PCRE_STUDY_JIT_COMPILE;
723#endif
724 pcre_malloc = _pcre_talloc; /* pcre_malloc is a global provided by libpcre */
725 pcre_free = _pcre_talloc_free; /* pcre_free is a global provided by libpcre */
726
727 return 0;
728}
729
730/** Free thread local data
731 *
732 * @param[in] tls Thread local data to free.
733 */
734static int _pcre_tls_free(fr_pcre_tls_t *tls)
735{
736#ifdef HAVE_PCRE_JIT_EXEC
737 if (tls->jit_stack) pcre_jit_stack_free(tls->jit_stack);
738#endif
739 return 0;
740}
741
742static int _pcre_tls_free_on_exit(void *arg)
743{
744 return talloc_free(arg);
745}
746
747/** Performs thread local storage initialisation for libpcre
748 *
749 */
750static inline CC_HINT(always_inline) int pcre_tls_init(void)
751{
752 fr_pcre_tls_t *tls;
753
754 if (fr_pcre_tls) return 0;
755
756 tls = talloc_zero(NULL, fr_pcre_tls_t);
757 if (unlikely(!tls)) return -1;
758 talloc_set_destructor(tls, _pcre_tls_free);
759
760 /*
761 * Need to set this first so that the alloc
762 * functions can access alloc_ctx.
763 */
764 fr_atexit_thread_local(fr_pcre_tls, _pcre_tls_free_on_exit, tls);
765
766#ifdef HAVE_PCRE_JIT_EXEC
767 /*
768 * Starts at 128K, max is 512K per thread.
769 */
770 tls->jit_stack = pcre_jit_stack_alloc(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX);
771#endif
772
773 return 0;
774}
775
776/** Free regex_t structure
777 *
778 * Calls libpcre specific free functions for the expression and study.
779 *
780 * @param preg to free.
781 */
782static int _regex_free(regex_t *preg)
783{
784 if (preg->compiled) pcre_free(preg->compiled);
785#ifdef PCRE_CONFIG_JIT
786 if (preg->extra) pcre_free_study(preg->extra);
787#else
788 if (preg->extra) pcre_free(preg->extra);
789#endif
790
791 return 0;
792}
793
794/** Wrapper around pcre_compile
795 *
796 * Allows the rest of the code to do compilations using one function signature.
797 *
798 * @note Compiled expression must be freed with talloc_free.
799 *
800 * @param[out] out Where to write out a pointer to the structure containing
801 * the compiled expression.
802 * @param[in] pattern to compile.
803 * @param[in] len of pattern.
804 * @param[in] flags controlling matching. May be NULL.
805 * @param[in] subcaptures Whether to compile the regular expression to store subcapture
806 * data.
807 * @param[in] runtime If false run the pattern through the PCRE JIT to convert it
808 * to machine code. This trades startup time (longer) for
809 * runtime performance (better).
810 * @return
811 * - >= 1 on success.
812 * - <= 0 on error. Negative value is offset of parse error.
813 */
814ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
815 fr_regex_flags_t const *flags, bool subcaptures, bool runtime)
816{
817 char const *error;
818 int offset;
819 int cflags = 0;
820 int ret;
821 regex_t *preg;
822
823 fr_atexit_global_once_ret(&ret, _pcre_globals_configure, _pcre_globals_reset, NULL);
824 if (unlikely(ret < 0)) return -1;
825
826
827 if (unlikely(pcre_tls_init() < 0)) return -1;
828
829 /*
830 * Check inputs
831 */
832 *out = NULL;
833
834 if (len == 0) {
835 fr_strerror_const("Empty expression");
836 return 0;
837 }
838
839 /*
840 * Options
841 */
842 if (flags) {
843 if (flags->global) {
844 fr_strerror_const("g - Global matching/substitution not supported with libpcre");
845 return 0;
846 }
847 if (flags->ignore_case) cflags |= PCRE_CASELESS;
848 if (flags->multiline) cflags |= PCRE_MULTILINE;
849 if (flags->dot_all) cflags |= PCRE_DOTALL;
850 if (flags->unicode) cflags |= PCRE_UTF8;
851 if (flags->extended) cflags |= PCRE_EXTENDED;
852 }
853
854 if (!subcaptures) cflags |= PCRE_NO_AUTO_CAPTURE;
855
856 preg = talloc_zero(ctx, regex_t);
857 if (unlikely(preg == NULL)) {
858 fr_strerror_const("Out of memory");
859 return 0;
860 }
861 talloc_set_destructor(preg, _regex_free);
862
863 preg->compiled = pcre_compile(pattern, cflags, &error, &offset, NULL);
864 if (!preg->compiled) {
865 fr_strerror_printf("%s", error);
866 talloc_free(preg);
867
868 return -(ssize_t)offset;
869 }
870
871 if (!runtime) {
872 preg->precompiled = true;
873 preg->extra = pcre_study(preg->compiled, fr_pcre_study_flags, &error);
874 if (error) {
875 fr_strerror_printf("Pattern study failed: %s", error);
876 talloc_free(preg);
877
878 return 0;
879 }
880
881#ifdef PCRE_INFO_JIT
882 /*
883 * Check to see if the JIT was successful.
884 *
885 * Not all platforms have JIT support, the pattern
886 * may not be jitable, or JIT support may have been
887 * disabled.
888 */
889 if (fr_pcre_study_flags & PCRE_STUDY_JIT_COMPILE) {
890 int jitd = 0;
891
892 pcre_fullinfo(preg->compiled, preg->extra, PCRE_INFO_JIT, &jitd);
893 if (jitd) preg->jitd = true;
894 }
895#endif
896 }
897
898 *out = preg;
899
900 return len;
901}
902
903static fr_table_num_ordered_t const regex_pcre_error_str[] = {
904 { L("PCRE_ERROR_NOMATCH"), PCRE_ERROR_NOMATCH },
905 { L("PCRE_ERROR_NULL"), PCRE_ERROR_NULL },
906 { L("PCRE_ERROR_BADOPTION"), PCRE_ERROR_BADOPTION },
907 { L("PCRE_ERROR_BADMAGIC"), PCRE_ERROR_BADMAGIC },
908 { L("PCRE_ERROR_UNKNOWN_OPCODE"), PCRE_ERROR_UNKNOWN_OPCODE },
909 { L("PCRE_ERROR_NOMEMORY"), PCRE_ERROR_NOMEMORY },
910 { L("PCRE_ERROR_NOSUBSTRING"), PCRE_ERROR_NOSUBSTRING },
911 { L("PCRE_ERROR_MATCHLIMIT"), PCRE_ERROR_MATCHLIMIT },
912 { L("PCRE_ERROR_CALLOUT"), PCRE_ERROR_CALLOUT },
913 { L("PCRE_ERROR_BADUTF8"), PCRE_ERROR_BADUTF8 },
914 { L("PCRE_ERROR_BADUTF8_OFFSET"), PCRE_ERROR_BADUTF8_OFFSET },
915 { L("PCRE_ERROR_PARTIAL"), PCRE_ERROR_PARTIAL },
916 { L("PCRE_ERROR_BADPARTIAL"), PCRE_ERROR_BADPARTIAL },
917 { L("PCRE_ERROR_INTERNAL"), PCRE_ERROR_INTERNAL },
918 { L("PCRE_ERROR_BADCOUNT"), PCRE_ERROR_BADCOUNT },
919 { L("PCRE_ERROR_DFA_UITEM"), PCRE_ERROR_DFA_UITEM },
920 { L("PCRE_ERROR_DFA_UCOND"), PCRE_ERROR_DFA_UCOND },
921 { L("PCRE_ERROR_DFA_UMLIMIT"), PCRE_ERROR_DFA_UMLIMIT },
922 { L("PCRE_ERROR_DFA_WSSIZE"), PCRE_ERROR_DFA_WSSIZE },
923 { L("PCRE_ERROR_DFA_RECURSE"), PCRE_ERROR_DFA_RECURSE },
924 { L("PCRE_ERROR_RECURSIONLIMIT"), PCRE_ERROR_RECURSIONLIMIT },
925 { L("PCRE_ERROR_NULLWSLIMIT"), PCRE_ERROR_NULLWSLIMIT },
926 { L("PCRE_ERROR_BADNEWLINE"), PCRE_ERROR_BADNEWLINE },
927 { L("PCRE_ERROR_BADOFFSET"), PCRE_ERROR_BADOFFSET },
928 { L("PCRE_ERROR_SHORTUTF8"), PCRE_ERROR_SHORTUTF8 },
929 { L("PCRE_ERROR_RECURSELOOP"), PCRE_ERROR_RECURSELOOP },
930 { L("PCRE_ERROR_JIT_STACKLIMIT"), PCRE_ERROR_JIT_STACKLIMIT },
931 { L("PCRE_ERROR_BADMODE"), PCRE_ERROR_BADMODE },
932 { L("PCRE_ERROR_BADENDIANNESS"), PCRE_ERROR_BADENDIANNESS },
933 { L("PCRE_ERROR_DFA_BADRESTART"), PCRE_ERROR_DFA_BADRESTART },
934 { L("PCRE_ERROR_JIT_BADOPTION"), PCRE_ERROR_JIT_BADOPTION },
935 { L("PCRE_ERROR_BADLENGTH"), PCRE_ERROR_BADLENGTH },
936#ifdef PCRE_ERROR_UNSET
937 { L("PCRE_ERROR_UNSET"), PCRE_ERROR_UNSET },
938#endif
939};
940static size_t regex_pcre_error_str_len = NUM_ELEMENTS(regex_pcre_error_str);
941
942/** Wrapper around pcre_exec
943 *
944 * @param[in] preg The compiled expression.
945 * @param[in] subject to match.
946 * @param[in] len Length of subject.
947 * @param[in] regmatch Match result structure.
948 * @return
949 * - -1 on failure.
950 * - 0 on no match.
951 * - 1 on match.
952 */
953int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch)
954{
955 int ret;
956 size_t matches;
957
958 if (unlikely(pcre_tls_init() < 0)) return -1;
959
960 /*
961 * Disable capturing
962 */
963 if (!regmatch) {
964 matches = 0;
965 } else {
966 matches = regmatch->allocd;
967
968 /*
969 * Reset the match result structure
970 */
971 memset(regmatch->match_data, 0, sizeof(regmatch->match_data[0]) * matches);
972 regmatch->used = 0;
973 }
974
975#ifdef HAVE_PCRE_JIT_EXEC
976 if (preg->jitd) {
977 ret = pcre_jit_exec(preg->compiled, preg->extra, subject, len, 0, 0,
978 regmatch ? (int *)regmatch->match_data : NULL, matches * 3, fr_pcre_tls->jit_stack);
979 } else
980#endif
981 {
982 ret = pcre_exec(preg->compiled, preg->extra, subject, len, 0, 0,
983 regmatch ? (int *)regmatch->match_data : NULL, matches * 3);
984 }
985 if (ret < 0) {
986 if (ret == PCRE_ERROR_NOMATCH) return 0;
987
988 fr_strerror_printf("regex evaluation failed with code (%i): %s", ret,
989 fr_table_str_by_value(regex_pcre_error_str, ret, "<INVALID>"));
990 return -1;
991 }
992
993 /*
994 * 0 signifies more offsets than we provided space for,
995 * so don't touch nmatches.
996 */
997 if (regmatch && (ret > 0)) {
998 regmatch->used = ret;
999
1000 if (regmatch->subject) talloc_const_free(regmatch->subject);
1001 regmatch->subject = talloc_bstrndup(regmatch, subject, len);
1002 if (!regmatch->subject) {
1003 fr_strerror_const("Out of memory");
1004 return -1;
1005 }
1006 }
1007
1008 return 1;
1009}
1010
1011/** Returns the number of subcapture groups
1012 *
1013 * @return
1014 * - >0 The number of subcaptures contained within the pattern
1015 * - 0 if the number of subcaptures can't be determined.
1016 */
1017uint32_t regex_subcapture_count(regex_t const *preg)
1018{
1019 int count;
1020
1021 if (pcre_fullinfo(preg->compiled, preg->extra, PCRE_INFO_CAPTURECOUNT, &count) != 0) {
1022 fr_strerror_const("Error determining subcapture group count");
1023 return 0;
1024 }
1025
1026 return (uint32_t)count + 1;
1027}
1028/*
1029 *######################################
1030 *# FUNCTIONS FOR POSIX-REGEX #
1031 *######################################
1032 */
1033# else
1034/*
1035 * Wrapper functions for POSIX like, and extended regular
1036 * expressions. These use the system regex library.
1037 */
1038
1039/** Free heap allocated regex_t structure
1040 *
1041 * Heap allocation of regex_t is needed so regex_compile has the same signature with
1042 * POSIX or libpcre.
1043 *
1044 * @param preg to free.
1045 */
1046static int _regex_free(regex_t *preg)
1047{
1048 regfree(preg);
1049
1050 return 0;
1051}
1052
1053/** Binary safe wrapper around regcomp
1054 *
1055 * If we have the BSD extensions we don't need to do any special work
1056 * if we don't have the BSD extensions we need to check to see if the
1057 * regular expression contains any \0 bytes.
1058 *
1059 * If it does we fail and print the appropriate error message.
1060 *
1061 * @note Compiled expression must be freed with talloc_free.
1062 *
1063 * @param[in] ctx To allocate memory in.
1064 * @param[out] out Where to write out a pointer
1065 * to the structure containing the compiled expression.
1066 * @param[in] pattern to compile.
1067 * @param[in] len of pattern.
1068 * @param[in] flags controlling matching. May be NULL.
1069 * @param[in] subcaptures Whether to compile the regular expression
1070 * to store subcapture data.
1071 * @param[in] runtime Whether the compilation is being done at runtime.
1072 * @return
1073 * - >= 1 on success.
1074 * - <= 0 on error. Negative value is offset of parse error.
1075 * With POSIX regex we only give the correct offset for embedded \0 errors.
1076 */
1077ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
1078 fr_regex_flags_t const *flags, bool subcaptures, UNUSED bool runtime)
1079{
1080 int ret;
1081 int cflags = REG_EXTENDED;
1082 regex_t *preg;
1083
1084 if (len == 0) {
1085 fr_strerror_const("Empty expression");
1086 return 0;
1087 }
1088
1089 /*
1090 * Options
1091 */
1092 if (flags) {
1093 if (flags->global) {
1094 fr_strerror_const("g - Global matching/substitution not supported with posix-regex");
1095 return 0;
1096 }
1097 if (flags->dot_all) {
1098 fr_strerror_const("s - Single line matching is not supported with posix-regex");
1099 return 0;
1100 }
1101 if (flags->unicode) {
1102 fr_strerror_const("u - Unicode matching not supported with posix-regex");
1103 return 0;
1104 }
1105 if (flags->extended) {
1106 fr_strerror_const("x - Whitespace and comments not supported with posix-regex");
1107 return 0;
1108 }
1109
1110 if (flags->ignore_case) cflags |= REG_ICASE;
1111 if (flags->multiline) cflags |= REG_NEWLINE;
1112 }
1113
1114
1115 if (!subcaptures) cflags |= REG_NOSUB;
1116
1117#ifndef HAVE_REGNCOMP
1118 {
1119 char const *p;
1120
1121 p = pattern;
1122 p += strlen(pattern);
1123
1124 if ((size_t)(p - pattern) != len) {
1125 fr_strerror_printf("Found null in pattern at offset %zu. Pattern unsafe for compilation",
1126 (p - pattern));
1127 return -(p - pattern);
1128 }
1129
1130 preg = talloc_zero(ctx, regex_t);
1131 if (!preg) return 0;
1132
1133 ret = regcomp(preg, pattern, cflags);
1134 }
1135#else
1136 preg = talloc_zero(ctx, regex_t);
1137 if (!preg) return 0;
1138 ret = regncomp(preg, pattern, len, cflags);
1139#endif
1140 if (ret != 0) {
1141 char errbuf[128];
1142
1143 regerror(ret, preg, errbuf, sizeof(errbuf));
1144 fr_strerror_printf("%s", errbuf);
1145
1146 talloc_free(preg);
1147
1148 return 0; /* POSIX expressions don't give us the failure offset */
1149 }
1150
1151 talloc_set_destructor(preg, _regex_free);
1152 *out = preg;
1153
1154 return len;
1155}
1156
1157/** Binary safe wrapper around regexec
1158 *
1159 * If we have the BSD extensions we don't need to do any special work
1160 * If we don't have the BSD extensions we need to check to see if the
1161 * value to be compared contains any \0 bytes.
1162 *
1163 * If it does, we fail and print the appropriate error message.
1164 *
1165 * @param[in] preg The compiled expression.
1166 * @param[in] subject to match.
1167 * @param[in] regmatch Match result structure.
1168 * @return
1169 * - -1 on failure.
1170 * - 0 on no match.
1171 * - 1 on match.
1172 */
1173int regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch)
1174{
1175 int ret;
1176 size_t matches;
1177
1178 /*
1179 * Disable capturing
1180 */
1181 if (!regmatch) {
1182 matches = 0;
1183 } else {
1184 matches = regmatch->allocd;
1185
1186 /*
1187 * Reset the match result structure
1188 */
1189 memset(regmatch->match_data, 0, sizeof(regmatch->match_data[0]) * matches);
1190 regmatch->used = 0;
1191 }
1192
1193#ifndef HAVE_REGNEXEC
1194 {
1195 char const *p;
1196
1197 p = subject;
1198 p += strlen(subject);
1199
1200 if ((size_t)(p - subject) != len) {
1201 fr_strerror_printf("Found null in subject at offset %zu. String unsafe for evaluation",
1202 (p - subject));
1203 if (regmatch) regmatch->used = 0;
1204 return -1;
1205 }
1206 ret = regexec(preg, subject, matches, regmatch ? regmatch->match_data : NULL, 0);
1207 }
1208#else
1209 ret = regnexec(preg, subject, len, matches, regmatch ? regmatch->match_data : NULL, 0);
1210#endif
1211 if (ret != 0) {
1212 if (ret != REG_NOMATCH) {
1213 char errbuf[128];
1214
1215 regerror(ret, preg, errbuf, sizeof(errbuf));
1216
1217 fr_strerror_printf("regex evaluation failed: %s", errbuf);
1218 return -1;
1219 }
1220 return 0;
1221 }
1222
1223 /*
1224 * Update regmatch->count to be the maximum number of
1225 * groups that *could* have been populated as we don't
1226 * have the number of matches.
1227 */
1228 if (regmatch) {
1229 regmatch->used = preg->re_nsub + 1;
1230
1231 if (regmatch->subject) talloc_const_free(regmatch->subject);
1232 regmatch->subject = talloc_bstrndup(regmatch, subject, len);
1233 if (!regmatch->subject) {
1234 fr_strerror_const("Out of memory");
1235 return -1;
1236 }
1237 }
1238 return 1;
1239}
1240
1241/** Returns the number of subcapture groups
1242 *
1243 * @return
1244 * - 0 we can't determine this for POSIX regular expressions.
1245 */
1246uint32_t regex_subcapture_count(UNUSED regex_t const *preg)
1247{
1248 return 0;
1249}
1250# endif
1251
1252# if defined(HAVE_REGEX_POSIX) || defined(HAVE_REGEX_PCRE)
1253/** Allocate vectors to fill with match data
1254 *
1255 * @param[in] ctx to allocate match vectors in.
1256 * @param[in] count The number of vectors to allocate.
1257 * @return
1258 * - NULL on error.
1259 * - Array of match vectors.
1260 */
1261fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count)
1262{
1263 fr_regmatch_t *regmatch;
1264
1265 /*
1266 * Pre-allocate space for the match structure
1267 * and for a 128b subject string.
1268 */
1269 regmatch = talloc_zero_pooled_object(ctx, fr_regmatch_t, 2, (sizeof(regmatch_t) * count) + 128);
1270 if (unlikely(!regmatch)) {
1271 error:
1272 fr_strerror_const("Out of memory");
1273 talloc_free(regmatch);
1274 return NULL;
1275 }
1276 regmatch->match_data = talloc_array(regmatch, regmatch_t, count);
1277 if (unlikely(!regmatch->match_data)) goto error;
1278
1279 regmatch->allocd = count;
1280 regmatch->used = 0;
1281 regmatch->subject = NULL;
1282
1283 return regmatch;
1284}
1285# endif
1286
1287/*
1288 *########################################
1289 *# UNIVERSAL FUNCTIONS #
1290 *########################################
1291 */
1292
1293/** Parse a string containing one or more regex flags
1294 *
1295 * @param[out] err May be NULL. If not NULL will be set to:
1296 * - 0 on success.
1297 * - -1 on unknown flag.
1298 * - -2 on duplicate.
1299 * @param[out] out Flag structure to populate. Must be initialised to zero
1300 * if this is the first call to regex_flags_parse.
1301 * @param[in] in Flag string to parse.
1302 * @param[in] terminals Terminal characters. If parsing ends before the buffer
1303 * is exhausted, and is pointing to one of these chars
1304 * it's not considered an error.
1305 * @param[in] err_on_dup Error if the flag is already set.
1306 * @return
1307 * - > 0 on success. The number of flag bytes parsed.
1308 * - <= 0 on failure. Negative offset of first unrecognised flag.
1309 */
1310fr_slen_t regex_flags_parse(int *err, fr_regex_flags_t *out, fr_sbuff_t *in,
1311 fr_sbuff_term_t const *terminals, bool err_on_dup)
1312{
1313 fr_sbuff_t our_in = FR_SBUFF(in);
1314
1315 if (err) *err = 0;
1316
1317 while (fr_sbuff_extend(&our_in)) {
1318 switch (*our_in.p) {
1319#define DO_REGEX_FLAG(_f, _c) \
1320 case _c: \
1321 if (err_on_dup && out->_f) { \
1322 fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \
1323 if (err) *err = -2; \
1324 FR_SBUFF_ERROR_RETURN(&our_in); \
1325 } \
1326 out->_f = 1; \
1327 break
1328
1329 DO_REGEX_FLAG(global, 'g');
1330 DO_REGEX_FLAG(ignore_case, 'i');
1331 DO_REGEX_FLAG(multiline, 'm');
1332 DO_REGEX_FLAG(dot_all, 's');
1333 DO_REGEX_FLAG(unicode, 'u');
1334 DO_REGEX_FLAG(extended, 'x');
1335#undef DO_REGEX_FLAG
1336
1337 default:
1338 if (fr_sbuff_is_terminal(&our_in, terminals)) FR_SBUFF_SET_RETURN(in, &our_in);
1339
1340 fr_strerror_printf("Unsupported regex flag '%c'", *our_in.p);
1341 if (err) *err = -1;
1342 FR_SBUFF_ERROR_RETURN(&our_in);
1343 }
1344 fr_sbuff_advance(&our_in, 1);
1345 }
1346 FR_SBUFF_SET_RETURN(in, &our_in);
1347}
1348
1349/** Print the flags
1350 *
1351 * @param[out] sbuff where to write flags.
1352 * @param[in] flags to print.
1353 * @return
1354 * - The number of bytes written to the out buffer.
1355 * - A number >= outlen if truncation has occurred.
1356 */
1357ssize_t regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const *flags)
1358{
1359 fr_sbuff_t our_sbuff = FR_SBUFF(sbuff);
1360
1361#define DO_REGEX_FLAG(_f, _c) \
1362 if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c)
1363
1364 DO_REGEX_FLAG(global, 'g');
1365 DO_REGEX_FLAG(ignore_case, 'i');
1366 DO_REGEX_FLAG(multiline, 'm');
1367 DO_REGEX_FLAG(dot_all, 's');
1368 DO_REGEX_FLAG(unicode, 'u');
1369 DO_REGEX_FLAG(extended, 'x');
1370#undef DO_REGEX_FLAG
1371
1372 FR_SBUFF_SET_RETURN(sbuff, &our_sbuff);
1373}
1374#endif
1375
1376/** Compare two boxes using an operator
1377 *
1378 * @todo - allow /foo/i on the RHS
1379 *
1380 * However, this involves allocating intermediate sbuffs for the
1381 * unescaped RHS, and all kinds of extra work. It's not overly hard,
1382 * but it's something we wish to avoid for now.
1383 *
1384 * @param[in] op to use in comparison. MUST be T_OP_REG_EQ or T_OP_REG_NE
1385 * @param[in] a Value to compare, MUST be FR_TYPE_STRING
1386 * @param[in] b uncompiled regex as FR_TYPE_STRING
1387 * @return
1388 * - 1 if true
1389 * - 0 if false
1390 * - -1 on failure.
1391 */
1393{
1394 int rcode;
1395 TALLOC_CTX *ctx = NULL;
1396 size_t lhs_len;
1397 char const *lhs;
1398 regex_t *regex = NULL;
1399
1400 if (!((op == T_OP_REG_EQ) || (op == T_OP_REG_NE))) {
1401 fr_strerror_const("Invalid operator for regex comparison");
1402 return -1;
1403 }
1404
1405 if (b->type != FR_TYPE_STRING) {
1406 fr_strerror_const("RHS must be regular expression");
1407 return -1;
1408 }
1409
1410 ctx = talloc_init_const("regex_cmp_op");
1411 if (!ctx) return -1;
1412
1413 if ((a->type != FR_TYPE_STRING) && (a->type != FR_TYPE_OCTETS)) {
1414 fr_slen_t slen;
1415 char *p;
1416
1417 slen = fr_value_box_aprint(ctx, &p, a, NULL); /* no escaping */
1418 if (slen < 0) return slen;
1419
1420 lhs = p;
1421 lhs_len = slen;
1422
1423 } else {
1424 lhs = a->vb_strvalue;
1425 lhs_len = a->vb_length;
1426 }
1427
1428 if (regex_compile(ctx, &regex, b->vb_strvalue, b->vb_length, NULL, false, true) < 0) {
1429 talloc_free(ctx);
1430 return -1;
1431 }
1432
1433#ifdef STATIC_ANALYZER
1434 if (!regex) {
1435 talloc_free(ctx);
1436 return -1;
1437 }
1438#endif
1439
1440 rcode = regex_exec(regex, lhs, lhs_len, NULL);
1441 talloc_free(ctx);
1442 if (rcode < 0) return rcode;
1443
1444 /*
1445 * Invert the sense of the rcode for !~
1446 */
1447 if (op == T_OP_REG_NE) rcode = (rcode == 0);
1448
1449 return rcode;
1450}
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
static fr_slen_t err
Definition dict.h:831
static fr_slen_t in
Definition dict.h:831
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
Definition sbuff.c:2152
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF_SET_RETURN(_dst, _src)
#define SBUFF_CHAR_UNPRINTABLES_EXTENDED
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define SBUFF_CHAR_UNPRINTABLES_LOW
Set of terminal elements.
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
return count
Definition module.c:163
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
char * talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen)
Trim a bstr (char) buffer.
Definition talloc.c:628
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:177
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:112
enum fr_token fr_token_t
@ T_OP_REG_EQ
Definition token.h:102
@ T_OP_REG_NE
Definition token.h:103
int fr_regex_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two boxes using an operator.
Definition regex.c:1392
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules) 1(fr_value_box_print
static size_t char ** out
Definition value.h:1012