24 RCSID(
"$Id: 21091671974cdbd11e5882a48d64b813fb310662 $")
28 #include <freeradius-devel/util/regex.h>
29 #include <freeradius-devel/util/atexit.h>
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)
35 #ifndef FR_PCRE_JIT_STACK_MAX
36 # define FR_PCRE_JIT_STACK_MAX (512 * 1024)
45 #ifdef HAVE_REGEX_PCRE2
59 TALLOC_CTX *alloc_ctx;
60 pcre2_general_context *gcontext;
61 pcre2_compile_context *ccontext;
62 pcre2_match_context *mcontext;
63 #ifdef PCRE2_CONFIG_JIT
64 pcre2_jit_stack *jit_stack;
72 static _Thread_local fr_pcre2_tls_t *fr_pcre2_tls;
79 static void *_pcre2_talloc(PCRE2_SIZE to_alloc,
UNUSED void *
uctx)
81 return talloc_array(fr_pcre2_tls->alloc_ctx,
uint8_t, to_alloc);
89 static void _pcre2_talloc_free(
void *to_free,
UNUSED void *
uctx)
98 static int _pcre2_tls_free(fr_pcre2_tls_t *tls)
100 if (tls->gcontext) pcre2_general_context_free(tls->gcontext);
101 if (tls->ccontext) pcre2_compile_context_free(tls->ccontext);
102 if (tls->mcontext) pcre2_match_context_free(tls->mcontext);
103 #ifdef PCRE2_CONFIG_JIT
104 if (tls->jit_stack) pcre2_jit_stack_free(tls->jit_stack);
110 static int _pcre2_tls_free_on_exit(
void *arg)
118 static int fr_pcre2_tls_init(
void)
122 if (
unlikely(fr_pcre2_tls != NULL))
return 0;
124 fr_pcre2_tls = tls = talloc_zero(NULL, fr_pcre2_tls_t);
126 talloc_set_destructor(tls, _pcre2_tls_free);
128 tls->gcontext = pcre2_general_context_create(_pcre2_talloc, _pcre2_talloc_free, NULL);
129 if (!tls->gcontext) {
134 tls->ccontext = pcre2_compile_context_create(tls->gcontext);
135 if (!tls->ccontext) {
139 _pcre2_tls_free(tls);
143 tls->mcontext = pcre2_match_context_create(tls->gcontext);
144 if (!tls->mcontext) {
149 #ifdef PCRE2_CONFIG_JIT
150 pcre2_config(PCRE2_CONFIG_JIT, &tls->do_jit);
152 tls->jit_stack = pcre2_jit_stack_create(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX, tls->gcontext);
153 if (!tls->jit_stack) {
157 pcre2_jit_stack_assign(tls->mcontext, NULL, tls->jit_stack);
176 static int _regex_free(regex_t *preg)
178 if (preg->compiled) pcre2_code_free(preg->compiled);
203 ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
204 fr_regex_flags_t
const *flags,
bool subcaptures,
bool runtime)
219 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
231 if (flags->ignore_case) cflags |= PCRE2_CASELESS;
232 if (flags->multiline) cflags |= PCRE2_MULTILINE;
233 if (flags->dot_all) cflags |= PCRE2_DOTALL;
234 if (flags->unicode) cflags |= PCRE2_UTF;
235 if (flags->extended) cflags |= PCRE2_EXTENDED;
238 if (!subcaptures) cflags |= PCRE2_NO_AUTO_CAPTURE;
240 preg = talloc_zero(ctx, regex_t);
241 talloc_set_destructor(preg, _regex_free);
243 preg->compiled = pcre2_compile((PCRE2_SPTR8)pattern, len,
244 cflags, &ret, &offset, fr_pcre2_tls->ccontext);
245 if (!preg->compiled) {
246 PCRE2_UCHAR errbuff[128];
248 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
256 preg->precompiled =
true;
258 #ifdef PCRE2_CONFIG_JIT
264 if (fr_pcre2_tls->do_jit) {
265 ret = pcre2_jit_compile(preg->compiled, PCRE2_JIT_COMPLETE);
267 PCRE2_UCHAR errbuff[128];
269 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
296 int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
301 char *our_subject = NULL;
302 bool dup_subject =
true;
303 pcre2_match_data *match_data;
308 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
311 #ifdef PCRE2_COPY_MATCHED_SUBJECT
316 # ifdef PCRE2_CONFIG_JIT
333 options |= PCRE2_COPY_MATCHED_SUBJECT;
334 # ifdef PCRE2_CONFIG_JIT
351 regmatch->subject = subject;
362 match_data = pcre2_match_data_create_from_pattern(preg->compiled, fr_pcre2_tls->gcontext);
368 match_data = regmatch->match_data;
371 #ifdef PCRE2_CONFIG_JIT
373 ret = pcre2_jit_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
374 match_data, fr_pcre2_tls->mcontext);
378 ret = pcre2_match(preg->compiled, (PCRE2_SPTR8)subject, len, 0, options,
379 match_data, fr_pcre2_tls->mcontext);
381 if (!regmatch) pcre2_match_data_free(match_data);
383 PCRE2_UCHAR errbuff[128];
387 if (ret == PCRE2_ERROR_NOMATCH) {
388 if (regmatch) regmatch->used = 0;
392 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
398 if (regmatch) regmatch->used = ret;
421 int regex_substitute(TALLOC_CTX *ctx,
char **
out,
size_t max_out, regex_t *preg, fr_regex_flags_t
const *flags,
422 char const *subject,
size_t subject_len,
423 char const *replacement,
size_t replacement_len,
424 fr_regmatch_t *regmatch)
428 size_t buff_len, actual_len;
431 #ifndef PCRE2_COPY_MATCHED_SUBJECT
432 char *our_subject = NULL;
438 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return -1;
446 #ifndef PCRE2_COPY_MATCHED_SUBJECT
453 subject = our_subject =
talloc_bstrndup(regmatch, subject, subject_len);
471 options |= PCRE2_COPY_MATCHED_SUBJECT;
478 actual_len = buff_len = subject_len + 1;
479 buff = talloc_array(ctx,
char, buff_len);
481 #ifndef PCRE2_COPY_MATCHED_SUBJECT
488 options |= PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
489 if (flags->global) options |= PCRE2_SUBSTITUTE_GLOBAL;
501 ret = pcre2_substitute(preg->compiled,
502 (PCRE2_SPTR8)subject, (PCRE2_SIZE)subject_len, 0,
503 options, NULL, fr_pcre2_tls->mcontext,
504 (PCRE2_UCHAR
const *)replacement, replacement_len, (PCRE2_UCHAR *)
buff, &actual_len);
507 PCRE2_UCHAR errbuff[128];
509 #ifndef PCRE2_COPY_MATCHED_SUBJECT
514 if (ret == PCRE2_ERROR_NOMEMORY) {
515 if ((max_out > 0) && (actual_len > max_out)) {
517 "exceeds max string length (%zu)", actual_len - 1, max_out - 1);
525 if (actual_len == buff_len) {
529 buff_len = actual_len;
530 buff = talloc_array(ctx,
char, buff_len);
534 if (ret == PCRE2_ERROR_NOMATCH) {
535 if (regmatch) regmatch->used = 0;
539 pcre2_get_error_message(ret, errbuff,
sizeof(errbuff));
550 if (actual_len < (buff_len - 1)) {
558 if (regmatch) regmatch->used = ret;
571 uint32_t regex_subcapture_count(regex_t
const *preg)
575 if (pcre2_pattern_info(preg->compiled, PCRE2_INFO_CAPTURECOUNT, &
count) != 0) {
587 static int _pcre2_match_data_free(fr_regmatch_t *regmatch)
589 pcre2_match_data_free(regmatch->match_data);
601 fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
603 fr_regmatch_t *regmatch;
608 if (
unlikely(!fr_pcre2_tls) && (fr_pcre2_tls_init() < 0))
return NULL;
610 regmatch = talloc(ctx, fr_regmatch_t);
617 regmatch->match_data = pcre2_match_data_create(
count, fr_pcre2_tls->gcontext);
618 if (!regmatch->match_data) {
622 talloc_set_type(regmatch->match_data, pcre2_match_data);
624 talloc_set_destructor(regmatch, _pcre2_match_data_free);
633 #elif defined(HAVE_REGEX_PCRE)
638 #if (PCRE_MAJOR >= 8) && (PCRE_MINOR >= 32) && defined(PCRE_CONFIG_JIT)
639 # define HAVE_PCRE_JIT_EXEC 1
646 TALLOC_CTX *alloc_ctx;
647 #ifdef HAVE_PCRE_JIT_EXEC
648 pcre_jit_stack *jit_stack;
652 static _Thread_local fr_pcre_tls_t *fr_pcre_tls;
653 static bool fr_pcre_study_flags;
660 static void *_pcre_talloc(
size_t to_alloc)
662 return talloc_array(fr_pcre_tls->alloc_ctx,
uint8_t, to_alloc);
665 static void _pcre_talloc_free(
void *to_free)
670 static int _pcre_globals_reset(
UNUSED void *
uctx)
677 static int _pcre_globals_configure(
UNUSED void *
uctx)
679 #ifdef PCRE_CONFIG_JIT
688 pcre_config(PCRE_CONFIG_JIT, &do_jit);
690 if (do_jit) fr_pcre_study_flags |= PCRE_STUDY_JIT_COMPILE;
692 pcre_malloc = _pcre_talloc;
693 pcre_free = _pcre_talloc_free;
702 static int _pcre_tls_free(fr_pcre_tls_t *tls)
704 #ifdef HAVE_PCRE_JIT_EXEC
705 if (tls->jit_stack) pcre_jit_stack_free(tls->jit_stack);
710 static int _pcre_tls_free_on_exit(
void *arg)
718 static inline CC_HINT(always_inline)
int pcre_tls_init(
void)
722 if (fr_pcre_tls)
return 0;
724 tls = talloc_zero(NULL, fr_pcre_tls_t);
726 talloc_set_destructor(tls, _pcre_tls_free);
734 #ifdef HAVE_PCRE_JIT_EXEC
738 tls->jit_stack = pcre_jit_stack_alloc(FR_PCRE_JIT_STACK_MIN, FR_PCRE_JIT_STACK_MAX);
750 static int _regex_free(regex_t *preg)
752 if (preg->compiled) pcre_free(preg->compiled);
753 #ifdef PCRE_CONFIG_JIT
754 if (preg->extra) pcre_free_study(preg->extra);
756 if (preg->extra) pcre_free(preg->extra);
782 ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
783 fr_regex_flags_t
const *flags,
bool subcaptures,
bool runtime)
791 fr_atexit_global_once_ret(&ret, _pcre_globals_configure, _pcre_globals_reset, NULL);
795 if (
unlikely(pcre_tls_init() < 0))
return -1;
812 fr_strerror_const(
"g - Global matching/substitution not supported with libpcre");
815 if (flags->ignore_case) cflags |= PCRE_CASELESS;
816 if (flags->multiline) cflags |= PCRE_MULTILINE;
817 if (flags->dot_all) cflags |= PCRE_DOTALL;
818 if (flags->unicode) cflags |= PCRE_UTF8;
819 if (flags->extended) cflags |= PCRE_EXTENDED;
822 if (!subcaptures) cflags |= PCRE_NO_AUTO_CAPTURE;
824 preg = talloc_zero(ctx, regex_t);
829 talloc_set_destructor(preg, _regex_free);
831 preg->compiled = pcre_compile(pattern, cflags, &error, &offset, NULL);
832 if (!preg->compiled) {
840 preg->precompiled =
true;
841 preg->extra = pcre_study(preg->compiled, fr_pcre_study_flags, &error);
857 if (fr_pcre_study_flags & PCRE_STUDY_JIT_COMPILE) {
860 pcre_fullinfo(preg->compiled, preg->extra, PCRE_INFO_JIT, &jitd);
861 if (jitd) preg->jitd =
true;
872 {
L(
"PCRE_ERROR_NOMATCH"), PCRE_ERROR_NOMATCH },
873 {
L(
"PCRE_ERROR_NULL"), PCRE_ERROR_NULL },
874 {
L(
"PCRE_ERROR_BADOPTION"), PCRE_ERROR_BADOPTION },
875 {
L(
"PCRE_ERROR_BADMAGIC"), PCRE_ERROR_BADMAGIC },
876 {
L(
"PCRE_ERROR_UNKNOWN_OPCODE"), PCRE_ERROR_UNKNOWN_OPCODE },
877 {
L(
"PCRE_ERROR_NOMEMORY"), PCRE_ERROR_NOMEMORY },
878 {
L(
"PCRE_ERROR_NOSUBSTRING"), PCRE_ERROR_NOSUBSTRING },
879 {
L(
"PCRE_ERROR_MATCHLIMIT"), PCRE_ERROR_MATCHLIMIT },
880 {
L(
"PCRE_ERROR_CALLOUT"), PCRE_ERROR_CALLOUT },
881 {
L(
"PCRE_ERROR_BADUTF8"), PCRE_ERROR_BADUTF8 },
882 {
L(
"PCRE_ERROR_BADUTF8_OFFSET"), PCRE_ERROR_BADUTF8_OFFSET },
883 {
L(
"PCRE_ERROR_PARTIAL"), PCRE_ERROR_PARTIAL },
884 {
L(
"PCRE_ERROR_BADPARTIAL"), PCRE_ERROR_BADPARTIAL },
885 {
L(
"PCRE_ERROR_INTERNAL"), PCRE_ERROR_INTERNAL },
886 {
L(
"PCRE_ERROR_BADCOUNT"), PCRE_ERROR_BADCOUNT },
887 {
L(
"PCRE_ERROR_DFA_UITEM"), PCRE_ERROR_DFA_UITEM },
888 {
L(
"PCRE_ERROR_DFA_UCOND"), PCRE_ERROR_DFA_UCOND },
889 {
L(
"PCRE_ERROR_DFA_UMLIMIT"), PCRE_ERROR_DFA_UMLIMIT },
890 {
L(
"PCRE_ERROR_DFA_WSSIZE"), PCRE_ERROR_DFA_WSSIZE },
891 {
L(
"PCRE_ERROR_DFA_RECURSE"), PCRE_ERROR_DFA_RECURSE },
892 {
L(
"PCRE_ERROR_RECURSIONLIMIT"), PCRE_ERROR_RECURSIONLIMIT },
893 {
L(
"PCRE_ERROR_NULLWSLIMIT"), PCRE_ERROR_NULLWSLIMIT },
894 {
L(
"PCRE_ERROR_BADNEWLINE"), PCRE_ERROR_BADNEWLINE },
895 {
L(
"PCRE_ERROR_BADOFFSET"), PCRE_ERROR_BADOFFSET },
896 {
L(
"PCRE_ERROR_SHORTUTF8"), PCRE_ERROR_SHORTUTF8 },
897 {
L(
"PCRE_ERROR_RECURSELOOP"), PCRE_ERROR_RECURSELOOP },
898 {
L(
"PCRE_ERROR_JIT_STACKLIMIT"), PCRE_ERROR_JIT_STACKLIMIT },
899 {
L(
"PCRE_ERROR_BADMODE"), PCRE_ERROR_BADMODE },
900 {
L(
"PCRE_ERROR_BADENDIANNESS"), PCRE_ERROR_BADENDIANNESS },
901 {
L(
"PCRE_ERROR_DFA_BADRESTART"), PCRE_ERROR_DFA_BADRESTART },
902 {
L(
"PCRE_ERROR_JIT_BADOPTION"), PCRE_ERROR_JIT_BADOPTION },
903 {
L(
"PCRE_ERROR_BADLENGTH"), PCRE_ERROR_BADLENGTH },
904 #ifdef PCRE_ERROR_UNSET
905 {
L(
"PCRE_ERROR_UNSET"), PCRE_ERROR_UNSET },
908 static size_t regex_pcre_error_str_len =
NUM_ELEMENTS(regex_pcre_error_str);
921 int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
926 if (
unlikely(pcre_tls_init() < 0))
return -1;
934 matches = regmatch->allocd;
939 memset(regmatch->match_data, 0,
sizeof(regmatch->match_data[0]) * matches);
943 #ifdef HAVE_PCRE_JIT_EXEC
945 ret = pcre_jit_exec(preg->compiled, preg->extra, subject, len, 0, 0,
946 regmatch ? (
int *)regmatch->match_data : NULL, matches * 3, fr_pcre_tls->jit_stack);
950 ret = pcre_exec(preg->compiled, preg->extra, subject, len, 0, 0,
951 regmatch ? (
int *)regmatch->match_data : NULL, matches * 3);
954 if (ret == PCRE_ERROR_NOMATCH)
return 0;
965 if (regmatch && (ret > 0)) {
966 regmatch->used = ret;
970 if (!regmatch->subject) {
985 uint32_t regex_subcapture_count(regex_t
const *preg)
989 if (pcre_fullinfo(preg->compiled, preg->extra, PCRE_INFO_CAPTURECOUNT, &
count) != 0) {
1014 static int _regex_free(regex_t *preg)
1045 ssize_t regex_compile(TALLOC_CTX *ctx, regex_t **
out,
char const *pattern,
size_t len,
1046 fr_regex_flags_t
const *flags,
bool subcaptures,
UNUSED bool runtime)
1049 int cflags = REG_EXTENDED;
1061 if (flags->global) {
1062 fr_strerror_const(
"g - Global matching/substitution not supported with posix-regex");
1065 if (flags->dot_all) {
1066 fr_strerror_const(
"s - Single line matching is not supported with posix-regex");
1069 if (flags->unicode) {
1073 if (flags->extended) {
1074 fr_strerror_const(
"x - Whitespace and comments not supported with posix-regex");
1078 if (flags->ignore_case) cflags |= REG_ICASE;
1079 if (flags->multiline) cflags |= REG_NEWLINE;
1083 if (!subcaptures) cflags |= REG_NOSUB;
1085 #ifndef HAVE_REGNCOMP
1090 p += strlen(pattern);
1092 if ((
size_t)(p - pattern) != len) {
1093 fr_strerror_printf(
"Found null in pattern at offset %zu. Pattern unsafe for compilation",
1095 return -(p - pattern);
1098 preg = talloc_zero(ctx, regex_t);
1099 if (!preg)
return 0;
1101 ret = regcomp(preg, pattern, cflags);
1104 preg = talloc_zero(ctx, regex_t);
1105 if (!preg)
return 0;
1106 ret = regncomp(preg, pattern, len, cflags);
1111 regerror(ret, preg, errbuf,
sizeof(errbuf));
1119 talloc_set_destructor(preg, _regex_free);
1141 int regex_exec(regex_t *preg,
char const *subject,
size_t len, fr_regmatch_t *regmatch)
1152 matches = regmatch->allocd;
1157 memset(regmatch->match_data, 0,
sizeof(regmatch->match_data[0]) * matches);
1161 #ifndef HAVE_REGNEXEC
1166 p += strlen(subject);
1168 if ((
size_t)(p - subject) != len) {
1169 fr_strerror_printf(
"Found null in subject at offset %zu. String unsafe for evaluation",
1171 if (regmatch) regmatch->used = 0;
1174 ret = regexec(preg, subject, matches, regmatch ? regmatch->match_data : NULL, 0);
1177 ret = regnexec(preg, subject, len, matches, regmatch ? regmatch->match_data : NULL, 0);
1180 if (ret != REG_NOMATCH) {
1183 regerror(ret, preg, errbuf,
sizeof(errbuf));
1197 regmatch->used = preg->re_nsub + 1;
1201 if (!regmatch->subject) {
1220 # if defined(HAVE_REGEX_POSIX) || defined(HAVE_REGEX_PCRE)
1229 fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx,
uint32_t count)
1231 fr_regmatch_t *regmatch;
1244 regmatch->match_data = talloc_array(regmatch, regmatch_t,
count);
1245 if (
unlikely(!regmatch->match_data))
goto error;
1247 regmatch->allocd =
count;
1249 regmatch->subject = NULL;
1286 switch (*our_in.
p) {
1287 #define DO_REGEX_FLAG(_f, _c) \
1289 if (err_on_dup && out->_f) { \
1290 fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \
1291 if (err) *err = -2; \
1292 FR_SBUFF_ERROR_RETURN(&our_in); \
1297 DO_REGEX_FLAG(global,
'g');
1298 DO_REGEX_FLAG(ignore_case,
'i');
1299 DO_REGEX_FLAG(multiline,
'm');
1300 DO_REGEX_FLAG(dot_all,
's');
1301 DO_REGEX_FLAG(unicode,
'u');
1302 DO_REGEX_FLAG(extended,
'x');
1303 #undef DO_REGEX_FLAG
1329 #define DO_REGEX_FLAG(_f, _c) \
1330 if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c)
1332 DO_REGEX_FLAG(global,
'g');
1333 DO_REGEX_FLAG(ignore_case,
'i');
1334 DO_REGEX_FLAG(multiline,
'm');
1335 DO_REGEX_FLAG(dot_all,
's');
1336 DO_REGEX_FLAG(unicode,
'u');
1337 DO_REGEX_FLAG(extended,
'x');
1338 #undef DO_REGEX_FLAG
1363 TALLOC_CTX *ctx = NULL;
1366 regex_t *regex = NULL;
1379 if (!ctx)
return -1;
1386 if (slen < 0)
return slen;
1392 lhs = a->vb_strvalue;
1393 lhs_len = a->vb_length;
1396 if (regex_compile(ctx, ®ex, b->vb_strvalue, b->vb_length, NULL,
false,
true) < 0) {
1401 #ifdef STATIC_ANALYZER
1408 rcode = regex_exec(regex, lhs, lhs_len, NULL);
1410 if (rcode < 0)
return rcode;
#define fr_atexit_thread_local(_name, _free, _uctx)
#define L(_str)
Helper for initialising arrays of string literals.
fr_dcursor_eval_t void const * uctx
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
Efficient terminal string search.
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define FR_SBUFF(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
Set of terminal elements.
static char buff[sizeof("18446744073709551615")+3]
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
An element in an arbitrarily ordered array of name to num mappings.
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
char * talloc_bstr_realloc(TALLOC_CTX *ctx, char *in, size_t inlen)
Trim a bstr (char) buffer.
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
static int talloc_const_free(void const *ptr)
Free const'd memory.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
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.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const(_msg)
FR_SBUFF_SET_RETURN(sbuff, &our_sbuff)
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