The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
unit_test_attribute.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/**
18 * $Id: 95d8dcf0d069fb27426e9257c2d67e9f495afe89 $
19 *
20 * @file unit_test_attribute.c
21 * @brief Provides a test harness for various internal libraries and functions.
22 *
23 * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2010 Alan DeKok (aland@freeradius.org)
25 */
26RCSID("$Id: 95d8dcf0d069fb27426e9257c2d67e9f495afe89 $")
27
28typedef struct request_s request_t;
29
30#include <freeradius-devel/io/test_point.h>
31#include <freeradius-devel/server/cf_parse.h>
32#include <freeradius-devel/server/cf_util.h>
33#include <freeradius-devel/server/command.h>
34#include <freeradius-devel/server/dependency.h>
35#include <freeradius-devel/server/dl_module.h>
36#include <freeradius-devel/server/log.h>
37#include <freeradius-devel/server/map.h>
38#include <freeradius-devel/server/tmpl.h>
39#ifdef WITH_TLS
40# include <freeradius-devel/tls/base.h>
41#endif
42#include <freeradius-devel/unlang/base.h>
43#include <freeradius-devel/unlang/xlat.h>
44#include <freeradius-devel/unlang/xlat_func.h>
45#include <freeradius-devel/util/atexit.h>
46#include <freeradius-devel/util/base64.h>
47#include <freeradius-devel/util/calc.h>
48#include <freeradius-devel/util/conf.h>
49#include <freeradius-devel/util/dict.h>
50#include <freeradius-devel/util/dns.h>
51#include <freeradius-devel/util/file.h>
52#include <freeradius-devel/util/log.h>
53#include <freeradius-devel/util/skip.h>
54#include <freeradius-devel/util/pair_legacy.h>
55#include <freeradius-devel/util/sha1.h>
56#include <freeradius-devel/util/syserror.h>
57
58#include <ctype.h>
59
60#ifdef __clangd__
61# undef HAVE_SANITIZER_LSAN_INTERFACE_H
62#endif
63#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
64# include <sanitizer/asan_interface.h>
65#endif
66
67#ifdef HAVE_GETOPT_H
68# include <getopt.h>
69#endif
70
71#include <assert.h>
72#include <fcntl.h>
73#include <libgen.h>
74#include <limits.h>
75#include <sys/file.h>
76#include <sys/stat.h>
77#include <sys/wait.h>
78
79#ifndef HAVE_SANITIZER_LSAN_INTERFACE_H
80# define ASAN_POISON_MEMORY_REGION(_start, _end)
81# define ASAN_UNPOISON_MEMORY_REGION(_start, _end)
82#endif
83
84#define EXIT_WITH_FAILURE \
85do { \
86 ret = EXIT_FAILURE; \
87 goto cleanup; \
88} while (0)
89
90#define COMMAND_OUTPUT_MAX 8192
91
92#define RETURN_OK(_len) \
93 do { \
94 result->rcode = RESULT_OK; \
95 result->file = __FILE__; \
96 result->line = __LINE__; \
97 return (_len); \
98 } while (0)
99
100#define RETURN_OK_WITH_ERROR() \
101 do { \
102 result->rcode = RESULT_OK; \
103 result->file = __FILE__; \
104 result->line = __LINE__; \
105 result->error_to_data = true; \
106 return 0; \
107 } while (0)
108
109#define RETURN_NOOP(_len) \
110 do { \
111 result->rcode = RESULT_NOOP; \
112 result->file = __FILE__; \
113 result->line = __LINE__; \
114 return (_len); \
115 } while (0)
116
117#define RETURN_SKIP_FILE() \
118 do { \
119 result->rcode = RESULT_SKIP_FILE; \
120 result->file = __FILE__; \
121 result->line = __LINE__; \
122 return 0; \
123 } while (0)
124
125#define RETURN_PARSE_ERROR(_offset) \
126 do { \
127 result->rcode = RESULT_PARSE_ERROR; \
128 result->offset = _offset; \
129 result->file = __FILE__; \
130 result->line = __LINE__; \
131 return 0; \
132 } while (0)
133
134#define RETURN_COMMAND_ERROR() \
135 do { \
136 result->rcode = RESULT_COMMAND_ERROR; \
137 result->file = __FILE__; \
138 result->line = __LINE__; \
139 return 0; \
140 } while (0)
141
142#define RETURN_MISMATCH(_len) \
143 do { \
144 result->rcode = RESULT_MISMATCH; \
145 result->file = __FILE__; \
146 result->line = __LINE__; \
147 return (_len); \
148 } while (0)
149
150#define RETURN_EXIT(_ret) \
151 do { \
152 result->rcode = RESULT_EXIT; \
153 result->ret = _ret; \
154 result->file = __FILE__; \
155 result->line = __LINE__; \
156 return 0; \
157 } while (0)
158
159/** Default buffer size for a command_file_ctx_t
160 *
161 */
162#define DEFAULT_BUFFER_SIZE 1024
163
164typedef enum {
165 RESULT_OK = 0, //!< Not an error - Result as expected.
166 RESULT_NOOP, //!< Not an error - Did nothing...
167 RESULT_SKIP_FILE, //!< Not an error - Skip the rest of this file, or until we
168 ///< reach an "eof" command.
169 RESULT_PARSE_ERROR, //!< Fatal error - Command syntax error.
170 RESULT_COMMAND_ERROR, //!< Fatal error - Command operation error.
171 RESULT_MISMATCH, //!< Fatal error - Result didn't match what we expected.
172 RESULT_EXIT, //!< Stop processing files and exit.
174
176 { L("command-error"), RESULT_COMMAND_ERROR },
177 { L("exit"), RESULT_EXIT },
178 { L("ok"), RESULT_OK },
179 { L("parse-error"), RESULT_PARSE_ERROR },
180 { L("result-mismatch"), RESULT_MISMATCH },
181 { L("skip-file"), RESULT_SKIP_FILE },
182};
184
185typedef struct {
186 TALLOC_CTX *tmp_ctx; //!< Temporary context to hold buffers
187 ///< in this
188 union {
189 size_t offset; //!< Where we failed parsing the command.
190 int ret; //!< What code we should exit with.
191 };
192 char const *file;
193 int line;
197
198/** Configuration parameters passed to command functions
199 *
200 */
201typedef struct {
202 fr_dict_t *dict; //!< Dictionary to "reset" to.
203 fr_dict_gctx_t const *dict_gctx; //!< Dictionary gctx to "reset" to.
204 char const *raddb_dir;
205 char const *dict_dir;
206 char const *fuzzer_dir; //!< Where to write fuzzer files.
207 CONF_SECTION *features; //!< Enabled features.
209
210typedef struct {
211 TALLOC_CTX *tmp_ctx; //!< Talloc context for test points.
212
213 char *path; //!< Current path we're operating in.
214 char const *filename; //!< Current file we're operating on.
215 uint32_t lineno; //!< Current line number.
216
217 uint32_t test_count; //!< How many tests we've executed in this file.
218 ssize_t last_ret; //!< Last return value.
219
220 uint8_t *buffer; //!< Temporary resizable buffer we use for
221 ///< holding non-string data.
222 uint8_t *buffer_start; //!< Where the non-poisoned region of the buffer starts.
223 uint8_t *buffer_end; //!< Where the non-poisoned region of the buffer ends.
224
225 tmpl_rules_t tmpl_rules; //!< To pass to parsing functions.
226 fr_dict_t *test_internal_dict; //!< Internal dictionary of test_gctx.
227 fr_dict_gctx_t const *test_gctx; //!< Dictionary context for test dictionaries.
228
229 int fuzzer_dir; //!< File descriptor pointing to a a directory to
230 ///< write fuzzer output.
233
234
235typedef struct {
236 fr_dlist_t entry; //!< Entry in the dlist.
237 uint32_t start; //!< Start of line range.
238 uint32_t end; //!< End of line range.
240
241/** Command to execute
242 *
243 * @param[out] result Of executing the command.
244 * @param[in] cc Information about the file being processed.
245 * @param[in,out] data Output of this command, or the previous command.
246 * @param[in] data_used Length of data in the data buffer.
247 * @param[in] in Command text to process.
248 * @param[in] inlen Length of the remainder of the command to process.
249 */
251 size_t data_used, char *in, size_t inlen);
252
253typedef struct {
255 char const *usage;
256 char const *description;
258
260 { .required = true, .single = true, .type = FR_TYPE_STRING },
262};
263
267
269 UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request,
270 UNUSED fr_value_box_list_t *in)
271{
272 return XLAT_ACTION_DONE;
273}
274
275static char proto_name_prev[128];
276static dl_t *dl;
277static dl_loader_t *dl_loader = NULL;
278
279static fr_event_list_t *el = NULL;
280
281static char const *write_filename = NULL;
282static FILE *write_fp = NULL;
283
284size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen);
285static int process_file(bool *exit_now, TALLOC_CTX *ctx,
286 command_config_t const *config, const char *root_dir, char const *filename, fr_dlist_head_t *lines);
287
288#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
289# define BUFF_POISON_START 1024
290# define BUFF_POISON_END 1024
291
292/** Unpoison the start and end regions of the buffer
293 *
294 */
295static int _free_buffer(uint8_t *buff)
296{
297 size_t size = talloc_array_length(buff) - (BUFF_POISON_START + BUFF_POISON_END);
298
301
302 return 0;
303}
304#else
305# define BUFF_POISON_START 0
306# define BUFF_POISON_END 0
307#endif
308
309/** Allocate a special buffer with poisoned memory regions at the start and end
310 *
311 */
312static int poisoned_buffer_allocate(TALLOC_CTX *ctx, uint8_t **buff, size_t size)
313{
314 uint8_t *our_buff = *buff;
315
316 if (our_buff) {
317 /*
318 * If it's already the correct length
319 * don't bother re-allocing the buffer,
320 * just memset it to zero.
321 */
322 if ((size + BUFF_POISON_START + BUFF_POISON_END) == talloc_array_length(our_buff)) {
323 memset(our_buff + BUFF_POISON_START, 0, size);
324 return 0;
325 }
326
327 talloc_free(our_buff); /* Destructor de-poisons */
328 *buff = NULL;
329 }
330
331 our_buff = talloc_array(ctx, uint8_t, size + BUFF_POISON_START + BUFF_POISON_END);
332 if (!our_buff) return -1;
333
334#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
335 talloc_set_destructor(our_buff, _free_buffer);
336
337 /*
338 * Poison regions before and after the buffer
339 */
342#endif
343
344 *buff = our_buff;
345
346 return 0;
347}
348#define POISONED_BUFFER_START(_p) ((_p) + BUFF_POISON_START)
349#define POISONED_BUFFER_END(_p) ((_p) + BUFF_POISON_START + (talloc_array_length(_p) - (BUFF_POISON_START + BUFF_POISON_END)))
350
351static void mismatch_print(command_file_ctx_t *cc, char const *command,
352 char *expected, size_t expected_len, char *got, size_t got_len,
353 bool print_diff)
354{
355 char *g, *e;
356
357 ERROR("%s failed %s/%s:%d", command, cc->path, cc->filename, cc->lineno);
358
359 if (!print_diff) {
360 ERROR(" got : %.*s", (int) got_len, got);
361 ERROR(" expected : %.*s", (int) expected_len, expected);
362 } else {
363 g = got;
364 e = expected;
365
366 while (*g && *e && (*g == *e)) {
367 g++;
368 e++;
369 }
370
371 if (expected_len < 100) {
372 char const *spaces = " ";
373
374 ERROR(" got : %.*s", (int) got_len, got);
375 ERROR(" expected : %.*s", (int) expected_len, expected);
376 ERROR(" %.*s^ differs here (%zu)", (int) (e - expected), spaces, e - expected);
377 } else if (fr_debug_lvl > 1) {
378 ERROR(" got : %.*s", (int) got_len, got);
379 ERROR("Differs at : %zu", e - expected);
380
381 } else {
382 size_t glen, elen;
383
384 elen = strlen(e);
385 if (elen > 40) elen = 40;
386 glen = strlen(g);
387 if (glen > 40) glen = 40;
388
389 ERROR("(%zu) ... %.*s ... ", e - expected, (int) elen, e);
390 ERROR("(%zu) ... %.*s ... ", e - expected, (int) glen, g);
391 }
392 }
393}
394
395/** Print hex string to buffer
396 *
397 */
398static inline CC_HINT(nonnull) size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
399{
400 char *p = out;
401 char *end = p + outlen;
402 size_t i;
403
404 if (inlen == 0) {
405 *p = '\0';
406 return 0;
407 }
408
409 for (i = 0; i < inlen; i++) {
410 size_t len;
411
412 len = snprintf(p, end - p, "%02x ", in[i]);
413 if (is_truncated(len, end - p)) return 0;
414
415 p += len;
416 }
417
418 *(--p) = '\0';
419
420 return p - out;
421}
422
423/** Concatenate error stack
424 */
425static inline size_t strerror_concat(char *out, size_t outlen)
426{
427 char *end = out + outlen;
428 char *p = out;
429 char const *err;
430
431 while ((p < end) && (err = fr_strerror_pop())) {
432 if (*fr_strerror_peek()) {
433 p += snprintf(p, end - p, "%s: ", err);
434 } else {
435 p += strlcpy(p, err, end - p);
436 }
437 }
438
439 return p - out;
440}
441
442static inline CC_HINT(nonnull) int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
443{
444 fr_sha1_ctx ctx;
446 char digest_str[(SHA1_DIGEST_LENGTH * 2) + 1];
447 int file_fd;
448
449 fr_sha1_init(&ctx);
450 fr_sha1_update(&ctx, (uint8_t const *)text, strlen(text));
451 fr_sha1_final(digest, &ctx);
452
453 /*
454 * We need to use the url alphabet as the standard
455 * one contains forwarded slashes which openat
456 * doesn't like.
457 */
458 fr_base64_encode_nstd(&FR_SBUFF_OUT(digest_str, sizeof(digest_str)), &FR_DBUFF_TMP(digest, sizeof(digest)),
460
461 file_fd = openat(fd_dir, digest_str, O_RDWR | O_CREAT | O_TRUNC,
462 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
463 if (file_fd < 0) {
464 fr_strerror_printf("Failed opening or creating corpus seed file \"%s\": %s",
465 digest_str, fr_syserror(errno));
466 return -1;
467 }
468
469 if (flock(file_fd, LOCK_EX) < 0) {
470 fr_strerror_printf("Failed locking corpus seed file \"%s\": %s",
471 digest_str, fr_syserror(errno));
472 return -1;
473 }
474
475 while (data_len) {
476 ssize_t ret;
477
478 ret = write(file_fd, data, data_len);
479 if (ret < 0) {
480 fr_strerror_printf("Failed writing to corpus seed file \"%s\": %s",
481 digest_str, fr_syserror(errno));
482 (void)flock(file_fd, LOCK_UN);
483 unlinkat(fd_dir, digest_str, 0);
484 return -1;
485 }
486 data_len -= ret;
487 data += ret;
488 }
489 (void)flock(file_fd, LOCK_UN);
490
491 return 0;
492}
493
494/*
495 * End of hacks for xlat
496 *
497 **********************************************************************/
498
499static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen);
500
501static char const hextab[] = "0123456789abcdef";
502
503static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
504{
505 ssize_t slen = 0;
506 char *p;
507
508 p = buffer + 1;
509
510 while (*p && (outlen > 0)) {
511 if (*p == '"') {
512 return slen;
513 }
514
515 if (*p != '\\') {
516 *(output++) = *(p++);
517 outlen--;
518 slen++;
519 continue;
520 }
521
522 switch (p[1]) {
523 default:
524 *(output++) = p[1];
525 break;
526
527 case 'n':
528 *(output++) = '\n';
529 break;
530
531 case 'r':
532 *(output++) = '\r';
533 break;
534
535 case 't':
536 *(output++) = '\t';
537 break;
538 }
539
540 outlen--;
541 slen++;
542 }
543
544 ERROR("String is not terminated");
545 return 0;
546}
547
548static ssize_t encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen)
549{
550 int depth = 0;
551 ssize_t slen;
552 char *p;
553
554 for (p = buffer; *p != '\0'; p++) {
555 if (*p == '{') depth++;
556 if (*p == '}') {
557 depth--;
558 if (depth == 0) break;
559 }
560 }
561
562 if (*p != '}') {
563 ERROR("No trailing '}' in string starting with \"%s\"", buffer);
564 return 0;
565 }
566
567 *endptr = p + 1;
568 *p = '\0';
569
570 p = buffer + 1;
572
573 slen = encode_tlv(p, output, outlen);
574 if (slen <= 0) return 0;
575
576 return slen;
577}
578
579static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
580{
581 char *p = in;
582 char *end = in + inlen;
583 uint8_t *out_p = out, *out_end = out_p + outlen;
584
585 while (p < end) {
586 char *c1, *c2;
587
588 if (out_p >= out_end) {
589 fr_strerror_const("Would overflow output buffer");
590 return -(p - in);
591 }
592
594
595 if (!*p) break;
596
597 c1 = memchr(hextab, tolower((uint8_t) *p++), sizeof(hextab));
598 if (!c1) {
599 bad_input:
600 fr_strerror_printf("Invalid hex data starting at \"%s\"", p);
601 return -(p - in);
602 }
603
604 c2 = memchr(hextab, tolower((uint8_t)*p++), sizeof(hextab));
605 if (!c2) goto bad_input;
606
607 *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
608 }
609
610 return out_p - out;
611}
612
613
614static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
615{
616 ssize_t slen;
617
618 if (!isspace((uint8_t) *p)) {
619 ERROR("Invalid character following attribute definition");
620 return 0;
621 }
622
624
625 if (*p == '{') {
626 size_t sublen;
627 char *q;
628
629 slen = 0;
630
631 do {
633 if (!*p) {
634 if (slen == 0) {
635 ERROR("No data");
636 return 0;
637 }
638
639 break;
640 }
641
642 sublen = encode_data_tlv(p, &q, output, outlen);
643 if (sublen <= 0) return 0;
644
645 slen += sublen;
646 output += sublen;
647 outlen -= sublen;
648 p = q;
649 } while (*q);
650
651 return slen;
652 }
653
654 if (*p == '"') {
655 slen = encode_data_string(p, output, outlen);
656 return slen;
657 }
658
659 slen = hex_to_bin(output, outlen, p, strlen(p));
660 if (slen <= 0) {
661 fr_strerror_const_push("Empty hex string");
662 return slen;
663 }
664
665 return slen;
666}
667
668static int decode_attr(char *buffer, char **endptr)
669{
670 long attr;
671
672 attr = strtol(buffer, endptr, 10);
673 if (*endptr == buffer) {
674 ERROR("No valid number found in string starting with \"%s\"", buffer);
675 return 0;
676 }
677
678 if (!**endptr) {
679 ERROR("Nothing follows attribute number");
680 return 0;
681 }
682
683 if ((attr <= 0) || (attr > 256)) {
684 ERROR("Attribute number is out of valid range");
685 return 0;
686 }
687
688 return (int) attr;
689}
690
691static int decode_vendor(char *buffer, char **endptr)
692{
693 long vendor;
694
695 if (*buffer != '.') {
696 ERROR("Invalid separator before vendor id");
697 return 0;
698 }
699
700 vendor = strtol(buffer + 1, endptr, 10);
701 if (*endptr == (buffer + 1)) {
702 ERROR("No valid vendor number found");
703 return 0;
704 }
705
706 if (!**endptr) {
707 ERROR("Nothing follows vendor number");
708 return 0;
709 }
710
711 if ((vendor <= 0) || (vendor > (1 << 24))) {
712 ERROR("Vendor number is out of valid range");
713 return 0;
714 }
715
716 if (**endptr != '.') {
717 ERROR("Invalid data following vendor number");
718 return 0;
719 }
720 (*endptr)++;
721
722 return (int) vendor;
723}
724
725static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen)
726{
727 int attr;
728 ssize_t slen;
729 char *p;
730
731 attr = decode_attr(buffer, &p);
732 if (attr == 0) return 0;
733
734 output[0] = attr;
735 output[1] = 2;
736
737 if (*p == '.') {
738 p++;
739 slen = encode_tlv(p, output + 2, outlen - 2);
740
741 } else {
742 slen = encode_data(p, output + 2, outlen - 2);
743 }
744
745 if (slen <= 0) return slen;
746 if (slen > (255 - 2)) {
747 ERROR("TLV data is too long");
748 return 0;
749 }
750
751 output[1] += slen;
752
753 return slen + 2;
754}
755
756static ssize_t encode_vsa(char *buffer, uint8_t *output, size_t outlen)
757{
758 int vendor;
759 ssize_t slen;
760 char *p;
761
762 vendor = decode_vendor(buffer, &p);
763 if (vendor == 0) return 0;
764
765 output[0] = 0;
766 output[1] = (vendor >> 16) & 0xff;
767 output[2] = (vendor >> 8) & 0xff;
768 output[3] = vendor & 0xff;
769
770 slen = encode_tlv(p, output + 4, outlen - 4);
771 if (slen <= 0) return slen;
772 if (slen > (255 - 6)) {
773 ERROR("VSA data is too long");
774 return 0;
775 }
776
777 return slen + 4;
778}
779
780static ssize_t encode_evs(char *buffer, uint8_t *output, size_t outlen)
781{
782 int vendor;
783 int attr;
784 ssize_t slen;
785 char *p;
786
787 vendor = decode_vendor(buffer, &p);
788 if (vendor == 0) return 0;
789
790 attr = decode_attr(p, &p);
791 if (attr == 0) return 0;
792
793 output[0] = 0;
794 output[1] = (vendor >> 16) & 0xff;
795 output[2] = (vendor >> 8) & 0xff;
796 output[3] = vendor & 0xff;
797 output[4] = attr;
798
799 slen = encode_data(p, output + 5, outlen - 5);
800 if (slen <= 0) return slen;
801
802 return slen + 5;
803}
804
805static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
806{
807 int attr;
808 ssize_t slen;
809 char *p;
810
811 attr = decode_attr(buffer, &p);
812 if (attr == 0) return 0;
813
814 output[0] = attr;
815
816 if (attr == 26) {
817 slen = encode_evs(p, output + 1, outlen - 1);
818 } else {
819 slen = encode_data(p, output + 1, outlen - 1);
820 }
821 if (slen <= 0) return slen;
822 if (slen > (255 - 3)) {
823 ERROR("Extended Attr data is too long");
824 return 0;
825 }
826
827 return slen + 1;
828}
829
830static ssize_t encode_long_extended(char *buffer, uint8_t *output, size_t outlen)
831{
832 int attr;
833 ssize_t slen, total;
834 char *p;
835
836 attr = decode_attr(buffer, &p);
837 if (attr == 0) return 0;
838
839 /* output[0] is the extended attribute */
840 output[1] = 4;
841 output[2] = attr;
842 output[3] = 0;
843
844 if (attr == 26) {
845 slen = encode_evs(p, output + 4, outlen - 4);
846 if (slen <= 0) return slen;
847
848 output[1] += 5;
849 slen -= 5;
850 } else {
851 slen = encode_data(p, output + 4, outlen - 4);
852 }
853 if (slen <= 0) return slen;
854
855 total = 0;
856 while (1) {
857 int sublen = 255 - output[1];
858
859 if (slen <= sublen) {
860 output[1] += slen;
861 total += output[1];
862 break;
863 }
864
865 slen -= sublen;
866
867 memmove(output + 255 + 4, output + 255, slen);
868 memcpy(output + 255, output, 4);
869
870 output[1] = 255;
871 output[3] |= 0x80;
872
873 output += 255;
874 output[1] = 4;
875 total += 255;
876 }
877
878 return total;
879}
880
881static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
882{
883 int attr;
884 ssize_t slen, sublen;
885 char *p;
886
887 attr = decode_attr(buffer, &p);
888 if (attr == 0) return 0;
889
890 slen = 2;
891 output[0] = attr;
892 output[1] = 2;
893
894 if (attr == 26) {
895 sublen = encode_vsa(p, output + 2, outlen - 2);
896
897 } else if ((attr < 241) || (attr > 246)) {
898 sublen = encode_data(p, output + 2, outlen - 2);
899
900 } else {
901 if (*p != '.') {
902 ERROR("Invalid data following attribute number");
903 return 0;
904 }
905
906 if (attr < 245) {
907 sublen = encode_extended(p + 1, output + 2, outlen - 2);
908 } else {
909 /*
910 * Not like the others!
911 */
912 return encode_long_extended(p + 1, output, outlen);
913 }
914 }
915 if (sublen <= 0) return sublen;
916 if (sublen > (255 -2)) {
917 ERROR("RFC Data is too long");
918 return 0;
919 }
920
921 output[1] += sublen;
922 return slen + sublen;
923}
924
925
926static void unload_proto_library(void)
927{
928 TALLOC_FREE(dl);
929}
930
931static ssize_t load_proto_library(char const *proto_name)
932{
933 char dl_name[128];
934
935 if (strcmp(proto_name_prev, proto_name) != 0) {
936 /*
937 * Ensure the old proto library is unloaded
938 */
940
941 snprintf(dl_name, sizeof(dl_name), "libfreeradius-%s", proto_name);
942 if (dl) TALLOC_FREE(dl);
943
944 dl = dl_by_name(dl_loader, dl_name, NULL, false);
945 if (!dl) {
946 fr_perror("Failed to link to library \"%s\"", dl_name);
948 return 0;
949 }
950
951 strlcpy(proto_name_prev, proto_name, sizeof(proto_name_prev));
952 }
953
954 return strlen(proto_name);
955}
956
957static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
958{
959 char buffer[256];
960 char const *p, *q;
961 void *dl_symbol;
962
963 if (!dl) {
964 fr_strerror_printf("No protocol library loaded. Specify library with \"load <proto name>\"");
965 return 0;
966 }
967
968 p = command;
969
970 /*
971 * Use the dflt_symbol name as the test point
972 */
973 if ((*p == '.') && (q = strchr(p, ' ')) && (q != (p + 1)) && ((size_t)(q - p) < sizeof(buffer))) {
974 p++;
975 strlcpy(buffer, p, (q - p) + 1);
976 p = q + 1;
977 } else {
978 snprintf(buffer, sizeof(buffer), "%s_%s", proto_name_prev, dflt_symbol);
979 }
980
981 dl_symbol = dlsym(dl->handle, buffer);
982 if (!dl_symbol) {
983 fr_strerror_printf("Test point (symbol \"%s\") not exported by library", buffer);
985 return 0;
986 }
987 *symbol = dl_symbol;
988
989 return p - command;
990}
991
993{
994 if (cc->tmpl_rules.attr.dict_def) {
996 }
997
998 return cc->config->dict;
999}
1000
1001/** Common dictionary load function
1002 *
1003 * Callers call fr_dict_global_ctx_set to set the context
1004 * the dictionaries will be loaded into.
1005 */
1006static int dictionary_load_common(command_result_t *result, command_file_ctx_t *cc, char const *in, char const *default_subdir)
1007{
1008 char const *dir;
1009 char *q;
1010 char const *name;
1011 char *tmp = NULL;
1012 int ret;
1013 fr_dict_t *dict;
1014
1015 if (in[0] == '\0') {
1016 fr_strerror_const("Missing dictionary name");
1018 }
1019
1020 /*
1021 * Decrease ref count if we're loading in a new dictionary
1022 */
1023 if (cc->tmpl_rules.attr.dict_def) {
1025 }
1026
1027 q = strchr(in, ' ');
1028 if (q) {
1029 name = tmp = talloc_bstrndup(NULL, in, q - in);
1030 q++;
1031 dir = q;
1032 } else {
1033 name = in;
1034 dir = default_subdir;
1035 }
1036
1037 ret = fr_dict_protocol_afrom_file(&dict, name, dir, __FILE__);
1038 talloc_free(tmp);
1039 if (ret < 0) RETURN_COMMAND_ERROR();
1040
1041 cc->tmpl_rules.attr.dict_def = dict;
1042 cc->tmpl_rules.attr.namespace = fr_dict_root(dict);
1043
1044 /*
1045 * Dump the dictionary if we're in super debug mode
1046 */
1048
1049
1050 RETURN_OK(0);
1051}
1052
1053static size_t parse_typed_value(command_result_t *result, command_file_ctx_t *cc, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
1054{
1056 size_t match_len;
1057 ssize_t slen;
1058 char const *p;
1059 fr_sbuff_t sbuff;
1060 fr_dict_attr_t const *enumv = NULL;
1061
1062 /*
1063 * Parse data types
1064 */
1066 if (fr_type_is_null(type)) {
1068 }
1069 fr_assert(match_len < inlen);
1070
1071 p = in + match_len;
1073 *out = p;
1074
1075 /*
1076 * As a hack, allow most things to be inside
1077 * double-quoted strings. This is really only for dates,
1078 * which are space-delimited.
1079 */
1080 if (*p == '"'){
1081 p++;
1082 sbuff = FR_SBUFF_IN(p, strlen(p));
1083 slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, enumv,
1084 &sbuff,
1086 if (slen < 0) {
1088 }
1089
1090 p += fr_sbuff_used(&sbuff);
1091 if (*p != '"') {
1093 }
1094 p++;
1095
1096 if (type != FR_TYPE_STRING) {
1097 if (fr_value_box_cast_in_place(box, box, type, NULL) < 0) {
1099 }
1100 }
1101
1102 } else {
1103 sbuff = FR_SBUFF_IN(p, strlen(p));
1104
1105 /*
1106 * We have no other way to pass the dict to the value-box parse function.
1107 */
1108 if (type == FR_TYPE_ATTR) {
1109 fr_dict_t const *dict = dictionary_current(cc);
1110
1111 if (!dict) {
1112 fr_strerror_const("proto-dictionary must be defined");
1114 }
1115
1116 enumv = fr_dict_root(dict);
1117 }
1118
1119 slen = fr_value_box_from_substr(box, box, type, enumv,
1120 &sbuff,
1122 if (slen < 0) {
1124 }
1125 p += fr_sbuff_used(&sbuff);
1126 }
1128
1129 RETURN_OK(p - in);
1130}
1131
1132static fr_cmd_t *command_head = NULL;
1133
1134static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
1135{
1136 return 0;
1137}
1138
1139static int command_walk(UNUSED void *ctx, fr_cmd_walk_info_t *info)
1140{
1141 int i;
1142
1143 for (i = 0; i < info->num_parents; i++) {
1144 printf("%s ", info->parents[i]);
1145 }
1146
1147 printf(":%s ", info->name);
1148 if (info->syntax) printf("%s", info->syntax);
1149 printf("%s", "");
1150
1151 return 1;
1152}
1153
1154static void command_print(void)
1155{
1156 void *walk_ctx = NULL;
1157
1158 printf("Command hierarchy --------");
1159 fr_cmd_debug(stdout, command_head);
1160
1161 printf("Command list --------");
1162 while (fr_command_walk(command_head, &walk_ctx, NULL, command_walk) == 1) {
1163 // do nothing
1164 }
1165}
1166
1167#define CLEAR_TEST_POINT(_cc) \
1168do { \
1169 talloc_free_children((_cc)->tmp_ctx); \
1170 tp = NULL; \
1171} while (0)
1172
1173/** Placeholder function for comments
1174 *
1175 */
1177 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1178{
1179 return 0;
1180}
1181
1182/** Execute another test file
1183 *
1184 */
1186 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1187{
1188 char *q;
1189 bool exit_now = false;
1190 int ret;
1191
1192 if (write_fp) {
1193 fprintf(stderr, "Can't do $INCLUDE with -w %s\n", write_filename);
1194 RETURN_EXIT(1);
1195 }
1196
1197 q = strrchr(cc->path, '/');
1198 if (q) {
1199 *q = '\0';
1200 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, cc->path, in, NULL);
1201 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1202 *q = '/';
1203 RETURN_OK(0);
1204 }
1205
1206 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, NULL, in, NULL);
1207 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1208
1209 RETURN_OK(0);
1210}
1211
1212/** Determine if unresolved attributes are allowed
1213 *
1214 */
1216 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1217{
1218 fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen);
1219 bool res;
1220
1221 if (fr_sbuff_out_bool(&res, &our_in) == 0) {
1222 fr_strerror_printf("Invalid boolean value, must be \"yes\" or \"no\"");
1224 }
1226
1227 RETURN_OK(0);
1228}
1229
1230static const fr_token_t token2op[UINT8_MAX + 1] = {
1231 [ '+' ] = T_ADD,
1232 [ '-' ] = T_SUB,
1233 [ '*' ] = T_MUL,
1234 [ '/' ] = T_DIV,
1235 [ '^' ] = T_XOR,
1236 [ '.' ] = T_ADD,
1237 [ '&' ] = T_AND,
1238 [ '|' ] = T_OR,
1239 [ '%' ] = T_MOD,
1240};
1241
1242/** Perform calculations
1243 *
1244 */
1246 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1247{
1248 fr_value_box_t *a, *b, *out;
1249 size_t match_len;
1251 fr_token_t op;
1252 char const *p, *value, *end;
1253 size_t slen;
1254 bool assignment;
1255
1256 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1257 b = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1258
1259 p = in;
1260 end = in + inlen;
1261
1262 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1263 if (match_len == 0) return 0; /* errors have already been updated */
1264
1265 p += match_len;
1267
1268 op = fr_table_value_by_longest_prefix(&match_len, fr_tokens_table, p, end - p, T_INVALID);
1269 if (op != T_INVALID) {
1270 p += match_len;
1271 assignment = fr_assignment_op[op];
1272
1273 } else {
1274 op = token2op[(uint8_t) p[0]];
1275 if (op == T_INVALID) {
1276 fr_strerror_printf("Unknown operator '%c'", p[0]);
1278 }
1279 p++;
1280
1281 assignment = false;
1282 }
1284
1285 match_len = parse_typed_value(result, cc, b, &value, p, end - p);
1286 if (match_len == 0) return 0;
1287
1288 p += match_len;
1290
1291 if (assignment) {
1292 if (fr_value_calc_assignment_op(cc->tmp_ctx, a, op, b) < 0) {
1294 }
1295 out = a;
1296
1297 } else {
1298 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1299
1300 /*
1301 * If there's no output data type, then the code tries to
1302 * figure one out automatically.
1303 */
1304 if (!*p) {
1306 } else {
1307 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1308 p += 2;
1310
1313 fr_value_box_init(out, type, NULL, false);
1314 }
1315
1316 if (fr_value_calc_binary_op(cc->tmp_ctx, out, type, a, op, b) < 0) {
1318 }
1319 }
1320
1322 if (slen <= 0) RETURN_OK_WITH_ERROR();
1323
1324 RETURN_OK(slen);
1325}
1326
1327/** Perform calculations on multi-valued ops
1328 *
1329 */
1331 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1332{
1333 fr_value_box_t *group, *a, *out;
1334 size_t match_len;
1336 fr_token_t op;
1337 char const *p, *value, *end;
1338 size_t slen;
1339
1340 group = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1341 fr_value_box_init(group, FR_TYPE_GROUP, NULL, false);
1342
1343 p = in;
1344 end = in + inlen;
1345
1346 /*
1347 * Multi-valued operations
1348 */
1349 op = token2op[(uint8_t) p[0]];
1350 if (op == T_INVALID) {
1351 fr_strerror_printf("Unknown operator '%c'", p[0]);
1353 }
1354 p++;
1355
1356 while (p < end) {
1358
1359 a = talloc_zero(group, fr_value_box_t);
1360
1361 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1362 if (match_len == 0) return 0; /* errors have already been updated */
1363
1364 fr_value_box_list_insert_tail(&group->vb_group, a);
1365
1366 p += match_len;
1367
1368 if (strncmp(p, "->", 2) == 0) break;
1369 }
1370
1371 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1373
1374 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1375 p += 2;
1377
1380
1381
1382 if (fr_value_calc_nary_op(cc->tmp_ctx, out, type, op, group) < 0) {
1384 }
1385
1387 if (slen <= 0) RETURN_OK_WITH_ERROR();
1388
1389 RETURN_OK(slen);
1390}
1391
1392/** Perform casting
1393 *
1394 */
1396 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1397{
1398 fr_value_box_t *a, *out;
1399 size_t match_len;
1401 char const *p, *value, *end;
1402 size_t slen;
1403
1404 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1405
1406 p = in;
1407 end = in + inlen;
1408
1409 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1410 if (match_len == 0) return 0; /* errors have already been updated */
1411
1412 p += match_len;
1414
1415 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1416
1417 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1418 p += 2;
1420
1423 fr_value_box_init(out, type, NULL, false);
1424
1425 if (fr_value_box_cast(out, out, type, NULL, a) < 0) {
1427 }
1428
1430 if (slen <= 0) RETURN_OK_WITH_ERROR();
1431
1432 RETURN_OK(slen);
1433}
1434
1435/** Change the working directory
1436 *
1437 */
1439 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1440{
1441 TALLOC_FREE(cc->path); /* Free old directories */
1442
1443 cc->path = fr_realpath(cc->tmp_ctx, in, inlen);
1444 if (!cc->path) RETURN_COMMAND_ERROR();
1445
1447
1448 RETURN_OK(talloc_array_length(cc->path) - 1);
1449}
1450
1451/*
1452 * Clear the data buffer
1453 */
1455 char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
1456{
1457 memset(data, 0, COMMAND_OUTPUT_MAX);
1458 RETURN_NOOP(0);
1459}
1460
1461/*
1462 * Add a command by talloc'ing a table for it.
1463 */
1465 char *data, size_t UNUSED data_used, char *in, UNUSED size_t inlen)
1466{
1467 char *p, *name;
1468 char *parent = NULL;
1469 fr_cmd_table_t *table;
1470 char buffer[8192];
1471
1472 table = talloc_zero(cc->tmp_ctx, fr_cmd_table_t);
1473
1474 strlcpy(buffer, in, sizeof(buffer));
1475
1476 p = strchr(buffer, ':');
1477 if (!p) {
1478 fr_strerror_const("no ':name' specified");
1480 }
1481
1482 *p = '\0';
1483 p++;
1484
1485 parent = talloc_strdup(cc->tmp_ctx, in);
1486
1487 /*
1488 * Set the name and try to find the syntax.
1489 */
1490 name = p;
1492
1493 if (isspace((uint8_t) *p)) {
1494 *p = '\0';
1495 p++;
1496 }
1497
1499
1500 if (*p) {
1501 table->syntax = talloc_strdup(table, p);
1502 }
1503 table->parent = parent;
1504 table->name = name;
1505 table->help = NULL;
1506 table->func = command_func;
1507 table->tab_expand = NULL;
1508 table->read_only = true;
1509
1510 if (fr_command_add(table, &command_head, NULL, NULL, table) < 0) {
1511 fr_strerror_const_push("ERROR: Failed adding command");
1513 }
1514
1516
1518}
1519
1520/*
1521 * Do tab completion on a command
1522 */
1524 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1525{
1526 int i;
1527 int num_expansions;
1528 char const *expansions[CMD_MAX_ARGV];
1529 char *p = data, *end = p + COMMAND_OUTPUT_MAX, **argv;
1530 fr_cmd_info_t info;
1531 size_t len;
1532
1533 info.argc = 0;
1534 info.max_argc = CMD_MAX_ARGV;
1535 info.argv = talloc_zero_array(cc->tmp_ctx, char const *, CMD_MAX_ARGV);
1536 info.box = talloc_zero_array(cc->tmp_ctx, fr_value_box_t *, CMD_MAX_ARGV);
1537
1538 memcpy(&argv, &info.argv, sizeof(argv)); /* const issues */
1539 info.argc = fr_dict_str_to_argv(in, argv, CMD_MAX_ARGV);
1540 if (info.argc <= 0) {
1541 fr_strerror_const("Failed splitting input");
1542 RETURN_PARSE_ERROR(-(info.argc));
1543 }
1544
1545 num_expansions = fr_command_tab_expand(cc->tmp_ctx, command_head, &info, CMD_MAX_ARGV, expansions);
1546
1547 len = snprintf(p, end - p, "%d - ", num_expansions);
1548 if (is_truncated(len, end - p)) {
1549 oob:
1550 fr_strerror_const("Out of output buffer space for radmin command");
1552 }
1553 p += len;
1554
1555 for (i = 0; i < num_expansions; i++) {
1556 len = snprintf(p, end - p, "'%s', ", expansions[i]);
1557 if (is_truncated(len, end - p)) goto oob;
1558 p += len;
1559 }
1560
1561 /*
1562 * Remove the trailing ", "
1563 */
1564 if (num_expansions > 0) {
1565 p -= 2;
1566 *p = '\0';
1567 }
1568
1569 return p - data;
1570}
1571
1572/** Parse and reprint a condition
1573 *
1574 */
1576 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1577{
1578 ssize_t slen;
1579 CONF_SECTION *cs;
1580 size_t len;
1581 xlat_exp_head_t *head = NULL;
1582
1583 cs = cf_section_alloc(NULL, NULL, "if", "condition");
1584 if (!cs) {
1585 fr_strerror_const("Out of memory");
1587 }
1588 cf_filename_set(cs, cc->filename);
1589 cf_lineno_set(cs, cc->lineno);
1590
1592
1593 slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, inlen), NULL, &cc->tmpl_rules);
1594 if (slen == 0) {
1595 fr_strerror_printf_push_head("ERROR failed to parse any input");
1596 talloc_free(cs);
1598 }
1599
1600 if (slen < 0) {
1601 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
1602 talloc_free(cs);
1604 }
1605
1606 if ((size_t) slen < inlen) {
1607 len = snprintf(data, COMMAND_OUTPUT_MAX, "ERROR passed in %zu, returned %zd", inlen, slen);
1608
1609 } else {
1611 }
1612
1614 talloc_free(cs);
1615
1616 RETURN_OK(len);
1617}
1618
1620 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1621{
1622 size_t len;
1623
1624 len = snprintf(data, COMMAND_OUTPUT_MAX, "%u", cc->test_count);
1625 if (is_truncated(len, COMMAND_OUTPUT_MAX)) {
1626 fr_strerror_const("Command count would overflow data buffer (shouldn't happen)");
1628 }
1629
1630 RETURN_OK(len);
1631}
1632
1634 char *data, size_t data_used, char *in, size_t inlen)
1635{
1636 fr_test_point_pair_decode_t *tp = NULL;
1637 void *decode_ctx = NULL;
1638 char *p;
1639 uint8_t *to_dec;
1640 uint8_t *to_dec_end;
1641 ssize_t slen;
1642
1643 fr_dict_attr_t const *da;
1644 fr_pair_t *head;
1645
1646 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1647 fr_assert(da != NULL);
1648 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1649 if (!head) {
1650 fr_strerror_const_push("Failed allocating memory");
1652 }
1653
1654 p = in;
1655
1656 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_pair");
1657 if (!tp) {
1658 fr_strerror_const_push("Failed locating decoder testpoint");
1660 }
1661
1662 p += slen;
1664
1665 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc)) < 0)) {
1666 fr_strerror_const_push("Failed initialising decoder testpoint");
1668 }
1669
1670 /*
1671 * Hack because we consume more of the command string
1672 * so we need to check this again.
1673 */
1674 if (*p == '-') {
1675 p = data;
1676 inlen = data_used;
1677 }
1678
1679 /*
1680 * Decode hex from input text
1681 */
1683 if (slen <= 0) {
1684 CLEAR_TEST_POINT(cc);
1685 RETURN_PARSE_ERROR(-(slen));
1686 }
1687
1688 to_dec = (uint8_t *)data;
1689 to_dec_end = to_dec + slen;
1690
1692
1693 /*
1694 * Run the input data through the test
1695 * point to produce fr_pair_ts.
1696 */
1697 while (to_dec < to_dec_end) {
1698 slen = tp->func(head, &head->vp_group, cc->tmpl_rules.attr.namespace,
1699 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1700 cc->last_ret = slen;
1701 if (slen <= 0) {
1703 CLEAR_TEST_POINT(cc);
1705 }
1706 if ((size_t)slen > (size_t)(to_dec_end - to_dec)) {
1707 fr_perror("%s: Internal sanity check failed at %d", __FUNCTION__, __LINE__);
1709 CLEAR_TEST_POINT(cc);
1711 }
1712 to_dec += slen;
1713 }
1714
1715 /*
1716 * Clear any spurious errors
1717 */
1720
1721 /*
1722 * Output may be an error, and we ignore
1723 * it if so.
1724 */
1725 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1726 if (slen <= 0) {
1728 }
1729
1730 CLEAR_TEST_POINT(cc);
1731 RETURN_OK(slen);
1732}
1733
1735 char *data, size_t data_used, char *in, size_t inlen)
1736{
1738 void *decode_ctx = NULL;
1739 char *p;
1740 uint8_t *to_dec;
1741 uint8_t *to_dec_end;
1742 ssize_t slen;
1743
1744 fr_dict_attr_t const *da;
1745 fr_pair_t *head;
1746
1747 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1748 fr_assert(da != NULL);
1749 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1750 if (!head) {
1751 fr_strerror_const_push("Failed allocating memory");
1753 }
1754
1755 p = in;
1756
1757 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_proto");
1758 if (!tp) {
1759 fr_strerror_const_push("Failed locating decoder testpoint");
1761 }
1762
1763 p += slen;
1765
1766 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc)) < 0)) {
1767 fr_strerror_const_push("Failed initialising decoder testpoint");
1769 }
1770
1771 /*
1772 * Hack because we consume more of the command string
1773 * so we need to check this again.
1774 */
1775 if (*p == '-') {
1776 p = data;
1777 inlen = data_used;
1778 }
1779
1780 /*
1781 * Decode hex from input text
1782 */
1784 if (slen <= 0) {
1785 CLEAR_TEST_POINT(cc);
1786 RETURN_PARSE_ERROR(-(slen));
1787 }
1788
1789 to_dec = (uint8_t *)data;
1790 to_dec_end = to_dec + slen;
1791
1793
1794 slen = tp->func(head, &head->vp_group,
1795 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1796 cc->last_ret = slen;
1797 if (slen <= 0) {
1799 CLEAR_TEST_POINT(cc);
1801 }
1802
1803 /*
1804 * Clear any spurious errors
1805 */
1808
1809 /*
1810 * Output may be an error, and we ignore
1811 * it if so.
1812 */
1813
1814 /*
1815 * Print the pairs.
1816 */
1817 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1818 if (slen <= 0) {
1819 fr_assert(0);
1821 }
1822
1823 CLEAR_TEST_POINT(cc);
1824 RETURN_OK(slen);
1825}
1826
1827/** Parse a dictionary attribute, writing "ok" to the data buffer is everything was ok
1828 *
1829 */
1831 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1832{
1834
1836}
1837
1838/** Print the currently loaded dictionary
1839 *
1840 */
1842 UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1843{
1845
1846 /*
1847 * Don't modify the contents of the data buffer
1848 */
1849 RETURN_OK(data_used);
1850}
1851
1852static CC_HINT(nonnull)
1854 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1855{
1856 size_t need;
1857 ssize_t ret;
1858 char *p, *next;
1859 uint8_t *enc_p;
1860 char buffer[8192];
1861
1862 strlcpy(buffer, in, sizeof(buffer));
1863
1864 p = buffer;
1865 next = strchr(p, ',');
1866 if (next) *next = 0;
1867
1868 enc_p = cc->buffer_start;
1869
1870 while (true) {
1871 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
1872
1874
1875 if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL,
1876 p, strlen(p),
1878 talloc_free(box);
1880 }
1881
1882 ret = fr_dns_label_from_value_box(&need,
1883 cc->buffer_start, cc->buffer_end - cc->buffer_start, enc_p, true, box, NULL);
1884 talloc_free(box);
1885
1886 if (ret < 0) RETURN_OK_WITH_ERROR();
1887
1888 if (ret == 0) RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "need=%zd", need));
1889
1890 enc_p += ret;
1891
1892 /*
1893 * Go to the next input string
1894 */
1895 if (!next) break;
1896
1897 p = next + 1;
1898 next = strchr(p, ',');
1899 if (next) *next = 0;
1900 }
1901
1902 if ((cc->fuzzer_dir >= 0) &&
1903 (dump_fuzzer_data(cc->fuzzer_dir, in, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
1905 }
1906
1907 RETURN_OK(hex_print(data, COMMAND_OUTPUT_MAX, cc->buffer_start, enc_p - cc->buffer_start));
1908}
1909
1911 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1912{
1913 ssize_t slen, total, i, outlen;
1914 char *out, *end;
1915 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
1916
1917 /*
1918 * Decode hex from input text
1919 */
1920 total = hex_to_bin(cc->buffer_start, cc->buffer_end - cc->buffer_start, in, inlen);
1921 if (total <= 0) RETURN_PARSE_ERROR(-total);
1922
1923 out = data;
1924 end = data + COMMAND_OUTPUT_MAX;
1925
1926 for (i = 0; i < total; i += slen) {
1927 slen = fr_dns_label_to_value_box(box, box, cc->buffer_start, total, cc->buffer_start + i, false, NULL);
1928 if (slen <= 0) {
1929 error:
1930 talloc_free(box);
1932 }
1933
1934 /*
1935 * Separate names by commas
1936 */
1937 if (i > 0) *(out++) = ',';
1938
1939 /*
1940 * We don't print it with quotes.
1941 */
1942 outlen = fr_value_box_print(&FR_SBUFF_OUT(out, end - out), box, NULL);
1943 if (outlen <= 0) goto error;
1944 out += outlen;
1945
1946 fr_value_box_clear(box);
1947 }
1948
1949 talloc_free(box);
1950 RETURN_OK(out - data);
1951}
1952
1954 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1955{
1956 fr_test_point_pair_encode_t *tp = NULL;
1957
1958 fr_dcursor_t cursor;
1959 void *encode_ctx = NULL;
1960 ssize_t slen;
1961 char *p = in;
1962
1963 uint8_t *enc_p, *enc_end;
1965 fr_pair_t *vp;
1966 bool truncate = false;
1967
1968 size_t iterations = 0;
1969 fr_pair_parse_t root, relative;
1970
1972
1973 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_pair");
1974 if (!tp) {
1975 fr_strerror_const_push("Failed locating encode testpoint");
1976 CLEAR_TEST_POINT(cc);
1978 }
1979
1980 p += ((size_t)slen);
1982
1983 /*
1984 * The truncate torture test.
1985 *
1986 * Increase the buffer one byte at a time until all items in the cursor
1987 * have been encoded.
1988 *
1989 * The poisoned region at the end of the buffer will detect overruns
1990 * if we're running with asan.
1991 *
1992 */
1993 if (strncmp(p, "truncate", sizeof("truncate") - 1) == 0) {
1994 truncate = true;
1995 p += sizeof("truncate") - 1;
1997 }
1998
1999 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc)) < 0)) {
2000 fr_strerror_const_push("Failed initialising encoder testpoint");
2001 CLEAR_TEST_POINT(cc);
2003 }
2004
2005 root = (fr_pair_parse_t) {
2006 .ctx = cc->tmp_ctx,
2007 .da = cc->tmpl_rules.attr.namespace,
2008 .list = &head,
2009 };
2010 relative = (fr_pair_parse_t) { };
2011
2012 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2013 if (slen <= 0) {
2014 CLEAR_TEST_POINT(cc);
2016 }
2017
2019
2020 /*
2021 * Outer loop implements truncate test
2022 */
2023 do {
2024 enc_p = cc->buffer_start;
2025 enc_end = truncate ? cc->buffer_start + iterations++ : cc->buffer_end;
2026
2027 if (truncate) {
2028#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2029 /*
2030 * Poison the region between the subset of the buffer
2031 * we're using and the end of the buffer.
2032 */
2033 ASAN_POISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2034
2035 DEBUG("%s[%d]: Iteration %zu - Safe region %p-%p (%zu bytes), "
2036 "poisoned region %p-%p (%zu bytes)", cc->filename, cc->lineno, iterations - 1,
2037 enc_p, enc_end, enc_end - enc_p, enc_end, cc->buffer_end, cc->buffer_end - enc_end);
2038#else
2039 DEBUG("%s[%d]: Iteration %zu - Allowed region %p-%p (%zu bytes)",
2040 cc->filename, cc->lineno, iterations - 1, enc_p, enc_end, enc_end - enc_p);
2041#endif
2042 }
2043
2044 for (vp = fr_pair_dcursor_iter_init(&cursor, &head,
2046 dictionary_current(cc));
2047 vp;
2048 vp = fr_dcursor_current(&cursor)) {
2049 slen = tp->func(&FR_DBUFF_TMP(enc_p, enc_end), &cursor, encode_ctx);
2050 cc->last_ret = slen;
2051
2052 if (truncate) DEBUG("%s[%d]: Iteration %zu - Result %zd%s%s",
2053 cc->filename, cc->lineno, iterations - 1, slen,
2054 *fr_strerror_peek() != '\0' ? " - " : "",
2055 *fr_strerror_peek() != '\0' ? fr_strerror_peek() : "");
2056 if (slen < 0) break;
2057
2058 /*
2059 * Encoder indicated it encoded too much data
2060 */
2061 if (slen > (enc_end - enc_p)) {
2062 fr_strerror_printf("Expected returned encoded length <= %zu bytes, got %zu bytes",
2063 (enc_end - enc_p), (size_t)slen);
2064#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2065 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2066#endif
2068 CLEAR_TEST_POINT(cc);
2070 }
2071
2072 enc_p += slen;
2073
2074 if (slen == 0) break;
2075
2076 }
2077
2078#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2079 /*
2080 * un-poison the region between the subset of the buffer
2081 * we're using and the end of the buffer.
2082 */
2083 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2084#endif
2085 /*
2086 * We consumed all the VPs, so presumably encoded the
2087 * complete pair list.
2088 */
2089 if (!vp) break;
2090 } while (truncate && (enc_end < cc->buffer_end));
2091
2092 /*
2093 * Last iteration result in an error
2094 */
2095 if (slen < 0) {
2097 CLEAR_TEST_POINT(cc);
2099 }
2100
2101 /*
2102 * Clear any spurious errors
2103 */
2105
2107
2108 CLEAR_TEST_POINT(cc);
2109
2110 if ((cc->fuzzer_dir >= 0) &&
2111 (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2113 }
2114
2116}
2117
2118/** Encode a RADIUS attribute writing the result to the data buffer as space separated hexits
2119 *
2120 */
2122 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2123{
2124 size_t len;
2125 char buffer[8192];
2126
2127 strlcpy(buffer, in, sizeof(buffer));
2128
2129 len = encode_rfc(buffer, cc->buffer_start, cc->buffer_end - cc->buffer_start);
2130 if (len <= 0) RETURN_PARSE_ERROR(0);
2131
2132 if (len >= (size_t)(cc->buffer_end - cc->buffer_start)) {
2133 fr_strerror_const("Encoder output would overflow output buffer");
2135 }
2136
2138}
2139
2140/** Parse a list of pairs
2141 *
2142 */
2144 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2145{
2146 ssize_t slen;
2148 bool done = false;
2149 char *filename;
2150 FILE *fp;
2151
2152 filename = talloc_asprintf(cc->tmp_ctx, "%s/%s", cc->path, in);
2153
2154 fp = fopen(filename, "r");
2155 talloc_free(filename);
2156
2157 if (!fp) {
2158 fr_strerror_printf("Failed opening %s - %s", in, fr_syserror(errno));
2160 }
2161
2164 fclose(fp);
2165 if (slen < 0) {
2167 }
2168
2169 /*
2170 * Print the pairs.
2171 */
2173 if (slen <= 0) {
2174 fr_assert(0);
2176 }
2177
2178 if (!done) {
2179 strlcpy(data + slen, "!DONE", COMMAND_OUTPUT_MAX - slen);
2180 slen += 5;
2181 }
2182
2184
2185 RETURN_OK(slen);
2186}
2187
2188
2190 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2191{
2193}
2194
2196 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2197{
2199
2200 void *encode_ctx = NULL;
2201 ssize_t slen;
2202 char *p = in;
2203
2205 fr_pair_parse_t root, relative;
2206
2208
2209 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_proto");
2210 if (!tp) {
2211 fr_strerror_const_push("Failed locating encode testpoint");
2212 CLEAR_TEST_POINT(cc);
2214 }
2215
2216 p += ((size_t)slen);
2218 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc)) < 0)) {
2219 fr_strerror_const_push("Failed initialising encoder testpoint");
2220 CLEAR_TEST_POINT(cc);
2222 }
2223
2224 root = (fr_pair_parse_t) {
2225 .ctx = cc->tmp_ctx,
2226 .da = cc->tmpl_rules.attr.namespace,
2227 .list = &head,
2228 };
2229 relative = (fr_pair_parse_t) { };
2230
2231 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2232 if (slen <= 0) {
2233 CLEAR_TEST_POINT(cc);
2235 }
2236
2237 slen = tp->func(cc->tmp_ctx, &head, cc->buffer_start, cc->buffer_end - cc->buffer_start, encode_ctx);
2239 cc->last_ret = slen;
2240 if (slen < 0) {
2241 CLEAR_TEST_POINT(cc);
2243 }
2244 /*
2245 * Clear any spurious errors
2246 */
2248
2249 CLEAR_TEST_POINT(cc);
2250
2251 if ((cc->fuzzer_dir >= 0) &&
2252 (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, slen) < 0)) {
2254 }
2255
2257}
2258
2259/** Command eof
2260 *
2261 * Mark the end of a test file if we're reading from stdin.
2262 *
2263 * Doesn't actually do anything, is just a placeholder for the command processing loop.
2264 */
2266 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2267{
2268 return 0;
2269}
2270
2271/** Enable fuzzer output
2272 *
2273 * Any commands that produce potentially useful corpus seed data will write that out data
2274 * to files in the specified directory, using the md5 of the text input at as the file name.
2275 *
2276 */
2278 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2279{
2280 int fd;
2281 struct stat sdir;
2282 char *fuzzer_dir;
2283 bool retry_dir = true;
2284
2285 /*
2286 * Close any open fuzzer output dirs
2287 */
2288 if (cc->fuzzer_dir >= 0) {
2289 close(cc->fuzzer_dir);
2290 cc->fuzzer_dir = -1;
2291 }
2292
2293 if (in[0] == '\0') {
2294 fr_strerror_const("Missing directory name");
2296 }
2297
2298 fuzzer_dir = talloc_asprintf(cc->tmp_ctx, "%s/%s",
2299 cc->config->fuzzer_dir ? cc->config->fuzzer_dir : cc->path, in);
2300
2301again:
2302 fd = open(fuzzer_dir, O_RDONLY);
2303 if (fd < 0) {
2304 if (mkdir(fuzzer_dir, 0777) == 0) {
2305 fd = open(fuzzer_dir, O_RDONLY);
2306 if (fd >= 0) goto stat;
2307 /*
2308 * Prevent race if multiple unit_test_attribute instances
2309 * attempt to create the same output dir.
2310 */
2311 } else if ((errno == EEXIST) && retry_dir) {
2312 retry_dir = false; /* Only allow this once */
2313 goto again;
2314 }
2315
2316 fr_strerror_printf("fuzzer-out \"%s\" doesn't exist: %s", fuzzer_dir, fr_syserror(errno));
2318 }
2319
2320stat:
2321 if (fstat(fd, &sdir) < 0) {
2322 close(fd);
2323 fr_strerror_printf("failed statting fuzzer-out \"%s\": %s", fuzzer_dir, fr_syserror(errno));
2325 }
2326
2327 if (!(sdir.st_mode & S_IFDIR)) {
2328 close(fd);
2329 fr_strerror_printf("fuzzer-out \"%s\" is not a directory", fuzzer_dir);
2331 }
2332 cc->fuzzer_dir = fd;
2333 talloc_free(fuzzer_dir);
2334
2335 return 0;
2336}
2337
2338/** Exit gracefully with the specified code
2339 *
2340 */
2342 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2343{
2344 if (!*in) RETURN_EXIT(0);
2345
2346 RETURN_EXIT(atoi(in));
2347}
2348
2350 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2351{
2352 char *name, *tmp = NULL;
2353 char const *dir;
2354 char *q;
2355 int ret;
2356
2358
2359 if (in[0] == '\0') {
2360 fr_strerror_const("Missing dictionary name");
2362 }
2363
2364 q = strchr(in, ' ');
2365 if (q) {
2366 name = tmp = talloc_bstrndup(NULL, in, q - in);
2367 q++;
2368 dir = q;
2369 } else {
2370 name = in;
2371 dir = cc->path;
2372 }
2373
2375 talloc_free(tmp);
2376 if (ret < 0) RETURN_COMMAND_ERROR();
2377
2378 RETURN_OK(0);
2379}
2380
2381
2382/** Compare the data buffer to an expected value
2383 *
2384 */
2386 char *data, size_t data_used, char *in, size_t inlen)
2387{
2388 if (strcmp(in, data) != 0) {
2389 if (write_fp) {
2390 strcpy(in, data);
2391 RETURN_OK(data_used);
2392 }
2393
2394 mismatch_print(cc, "match", in, inlen, data, data_used, true);
2395 RETURN_MISMATCH(data_used);
2396 }
2397
2398 /*
2399 * We didn't actually write anything, but this
2400 * keeps the contents of the data buffer around
2401 * for the next command to operate on.
2402 */
2403 RETURN_OK(data_used);
2404}
2405
2406/** Compare the data buffer against an expected expression
2407 *
2408 */
2410 char *data, size_t data_used, char *in, size_t inlen)
2411{
2412 ssize_t slen;
2413 regex_t *regex;
2414 int ret;
2415
2416 slen = regex_compile(cc->tmp_ctx, &regex, in, inlen, NULL, false, true);
2417 if (slen <= 0) RETURN_COMMAND_ERROR();
2418
2419 ret = regex_exec(regex, data, data_used, NULL);
2420 talloc_free(regex);
2421
2422 switch (ret) {
2423 case -1:
2424 default:
2426
2427 case 0:
2428 mismatch_print(cc, "match-regex", in, inlen, data, data_used, false);
2429 RETURN_MISMATCH(data_used);
2430
2431 case 1:
2432 RETURN_OK(data_used);
2433 }
2434}
2435
2436/** Artificially limit the maximum packet size.
2437 *
2438 */
2440 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2441{
2442 unsigned long size;
2443 char *end;
2444
2446
2447 if (*in != '\0') {
2448 size = strtoul(in, &end, 10);
2449 if ((size == ULONG_MAX) || *end || (size >= 65536)) {
2450 fr_strerror_const_push("Invalid integer");
2452 }
2453 } else {
2454 size = DEFAULT_BUFFER_SIZE;
2455 }
2456
2457 if (poisoned_buffer_allocate(cc, &cc->buffer, size) < 0) RETURN_EXIT(1);
2460
2461 RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "%ld", size));
2462}
2463
2464/** Set or clear migration flags.
2465 *
2466 */
2468 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2469{
2470 char *p;
2471 bool *out;
2472
2474 p = in;
2475
2476 if (strncmp(p, "xlat_new_functions", sizeof("xlat_new_functions") - 1) == 0) {
2477 p += sizeof("xlat_new_functions") - 1;
2479
2480 } else {
2481 fr_strerror_const("Unknown migration flag");
2483 }
2484
2486 if (*p != '=') {
2487 fr_strerror_const("Missing '=' after flag");
2489 }
2490 p++;
2491
2493 if ((strcmp(p, "yes") == 0) || (strcmp(p, "true") == 0) || (strcmp(p, "1") == 0)) {
2494 *out = true;
2495
2496 } else if ((strcmp(p, "no") == 0) || (strcmp(p, "false") == 0) || (strcmp(p, "0") == 0)) {
2497 *out = false;
2498
2499 } else {
2500 fr_strerror_const("Invalid value for flag");
2502 }
2503
2504 RETURN_OK(0);
2505}
2506
2507/** Skip the test file if we're missing a particular feature
2508 *
2509 */
2511 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2512{
2513 CONF_PAIR *cp;
2514
2515 if (in[0] == '\0') {
2516 fr_strerror_printf("Prerequisite syntax is \"need-feature <feature>\". "
2517 "Use -f to print features");
2519 }
2520
2521 cp = cf_pair_find(cc->config->features, in);
2522 if (!cp || (strcmp(cf_pair_value(cp), "yes") != 0)) {
2523 DEBUG("Skipping, missing feature \"%s\"", in);
2525 }
2526
2527 RETURN_NOOP(0);
2528}
2529
2530/** Negate the result of a match command or any command which returns "OK"
2531 *
2532 */
2534 char *data, size_t data_used, char *in, size_t inlen)
2535{
2536 data_used = process_line(result, cc, data, data_used, in, inlen);
2537 switch (result->rcode) {
2538 /*
2539 * OK becomes a command error
2540 */
2541 case RESULT_OK:
2542 ERROR("%s[%d]: %.*s: returned 'ok', where we expected 'result-mismatch'",
2543 cc->filename, cc->lineno, (int) inlen, in);
2544 RETURN_MISMATCH(data_used);
2545
2546 /*
2547 * Mismatch becomes OK
2548 */
2549 case RESULT_MISMATCH:
2550 RETURN_OK(data_used);
2551
2552 /*
2553 * The rest are unchanged...
2554 */
2555 default:
2556 break;
2557 }
2558
2559 return data_used;
2560}
2561
2562/** Parse an print an attribute pair or pair list.
2563 *
2564 */
2566 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2567{
2569 ssize_t slen;
2570 fr_dict_t const *dict = dictionary_current(cc);
2571 fr_pair_parse_t root, relative;
2572
2574
2575 root = (fr_pair_parse_t) {
2576 .ctx = cc->tmp_ctx,
2577 .da = fr_dict_root(dict),
2578 .list = &head,
2579 };
2580 relative = (fr_pair_parse_t) { };
2581
2582 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(in, inlen));
2583 if (slen <= 0) {
2584// fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
2587 }
2588
2589 /*
2590 * Output may be an error, and we ignore
2591 * it if so.
2592 */
2593
2595 if (slen <= 0) {
2598 }
2599
2601 RETURN_OK(slen);
2602}
2603
2604/** Dynamically load a protocol library
2605 *
2606 */
2608 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2609{
2610 ssize_t slen;
2611
2612 if (*in == '\0') {
2613 fr_strerror_printf("Load syntax is \"proto <lib_name>\"");
2615 }
2616
2618 slen = load_proto_library(in);
2619 if (slen <= 0) RETURN_PARSE_ERROR(-(slen));
2620
2621 RETURN_OK(0);
2622}
2623
2625 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2626{
2628 return dictionary_load_common(result, cc, in, NULL);
2629}
2630
2632 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2633{
2634 fr_dict_t const *dict = dictionary_current(cc);
2635 fr_dict_attr_t const *root_da = fr_dict_root(dict);
2636 fr_dict_attr_t const *new_root;
2637
2638 if (is_whitespace(in) || (*in == '\0')) {
2639 new_root = fr_dict_root(dict);
2640 } else {
2641 new_root = fr_dict_attr_by_name(NULL, fr_dict_root(dict), in);
2642 if (!new_root) {
2643 fr_strerror_printf("dictionary attribute \"%s\" not found in %s", in, root_da->name);
2645 }
2646 }
2647
2648 cc->tmpl_rules.attr.namespace = new_root;
2649
2650 RETURN_OK(0);
2651}
2652
2653/** Parse an reprint a tmpl expansion
2654 *
2655 */
2657 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2658{
2659 ssize_t slen;
2660 tmpl_t *vpt;
2661 size_t input_len = strlen(in), escaped_len;
2662
2663 slen = tmpl_afrom_substr(cc->tmp_ctx, &vpt, &FR_SBUFF_IN(in, input_len), T_BARE_WORD,
2665 &(tmpl_rules_t) {
2666 .attr = {
2667 .dict_def = dictionary_current(cc),
2668 .list_def = request_attr_request,
2669 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2670 },
2671 .xlat = cc->tmpl_rules.xlat,
2672 });
2673 if (slen == 0) {
2674 fr_strerror_printf_push_head("ERROR failed to parse any input");
2676 }
2677
2678 if (slen < 0) {
2679 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
2680
2681 return_error:
2683 }
2684
2685 if (((size_t) slen != input_len)) {
2686 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
2687 goto return_error;
2688 }
2689
2690 escaped_len = tmpl_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), vpt, NULL);
2691 RETURN_OK(escaped_len);
2692}
2693
2694/** Touch a file to indicate a test completed
2695 *
2696 */
2698 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2699{
2700 if (fr_unlink(in) < 0) RETURN_COMMAND_ERROR();
2701 if (fr_touch(NULL, in, 0644, true, 0755) <= 0) RETURN_COMMAND_ERROR();
2702
2703 RETURN_OK(0);
2704}
2705
2706/** Callback for a tmpl rule parser
2707 *
2708 */
2709typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value);
2710
2712{
2713 bool res;
2714 ssize_t slen;
2715
2716 slen = fr_sbuff_out_bool(&res, value);
2717 rules->attr.allow_foreign = res;
2718 return slen;
2719}
2720
2722{
2723 bool res;
2724 ssize_t slen;
2725
2726 slen = fr_sbuff_out_bool(&res, value);
2727 rules->attr.allow_unknown = res;
2728 return slen;
2729}
2730
2732{
2733 bool res;
2734 ssize_t slen;
2735
2736 slen = fr_sbuff_out_bool(&res, value);
2737 rules->attr.allow_unresolved = res;
2738 return slen;
2739}
2740
2742{
2744 fr_slen_t slen;
2745
2747 &rules->attr.namespace,
2748 rules->attr.dict_def ? fr_dict_root(rules->attr.dict_def) :
2750 value, NULL);
2752 return slen;
2753}
2754
2756{
2757 ssize_t slen;
2758
2760
2761 if (slen == 0) {
2762 fr_strerror_printf("Invalid list specifier \"%pV\"",
2764 }
2765
2766 return slen;
2767}
2768
2770{
2771 fr_slen_t slen;
2772
2773 slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
2774 &rules->attr.request_def,
2775 value);
2776 if (slen < 0) {
2777 fr_strerror_printf("Invalid request specifier \"%pV\"",
2779 }
2780
2781 return slen;
2782}
2783
2785 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
2786{
2787 fr_sbuff_t sbuff = FR_SBUFF_IN(in, inlen);
2788 ssize_t slen;
2790 void *res;
2791
2792 static fr_table_ptr_sorted_t tmpl_rule_func_table[] = {
2793 { L("allow_foreign"), (void *)command_tmpl_rule_allow_foreign },
2794 { L("allow_unknown"), (void *)command_tmpl_rule_allow_unknown },
2795 { L("allow_unresolved"), (void *)command_tmpl_rule_allow_unresolved },
2796 { L("attr_parent"), (void *)command_tmpl_rule_attr_parent },
2797 { L("list_def"), (void *)command_tmpl_rule_list_def },
2798 { L("request_def"), (void *)command_tmpl_rule_request_def }
2799 };
2800 static size_t tmpl_rule_func_table_len = NUM_ELEMENTS(tmpl_rule_func_table);
2801
2802 while (fr_sbuff_extend(&sbuff)) {
2803 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2804
2805 fr_sbuff_out_by_longest_prefix(&slen, &res, tmpl_rule_func_table, &sbuff, NULL);
2806 if (res == NULL) {
2807 fr_strerror_printf("Specified rule \"%pV\" is invalid",
2810 }
2811 func = (command_tmpl_rule_func)res; /* -Wpedantic */
2812
2813 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2814
2815 if (!fr_sbuff_next_if_char(&sbuff, '=')) {
2816 fr_strerror_printf("Expected '=' after rule identifier, got \"%pV\"",
2819 }
2820
2821 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2822
2823 if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
2824 }
2825
2826 return fr_sbuff_used(&sbuff);
2827}
2828
2830 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2831{
2832 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2833 fr_value_box_t *box2;
2834 char const *value;
2835 size_t match_len;
2836 ssize_t slen;
2838
2839 match_len = parse_typed_value(result, cc, box, &value, in, strlen(in));
2840 if (match_len == 0) {
2841 talloc_free(box);
2842 return 0; /* errors have already been updated */
2843 }
2844
2845 type = box->type;
2846
2847 /*
2848 * Don't print dates with enclosing quotation marks.
2849 */
2850 if (type != FR_TYPE_DATE) {
2853 } else {
2855 }
2856 if (slen <= 0) {
2857 talloc_free(box);
2859 }
2860
2861 /*
2862 * Behind the scenes, parse the data
2863 * string. We should get the same value
2864 * box as last time.
2865 */
2866 box2 = talloc_zero(NULL, fr_value_box_t);
2867 if (fr_value_box_from_str(box2, box2, type, box->enumv,
2868 data, slen,
2870 talloc_free(box2);
2871 talloc_free(box);
2873 }
2874
2875 /*
2876 * They MUST be identical
2877 */
2878 if (fr_value_box_cmp(box, box2) != 0) {
2879 fr_strerror_const("ERROR value box reparsing failed. Results not identical");
2880 fr_strerror_printf_push("out: %pV (as string %.*s)", box2, (int) slen, data);
2881 fr_strerror_printf_push("in: %pV (from string %s)", box, value);
2882 talloc_free(box2);
2883 talloc_free(box);
2885 }
2886
2887 /*
2888 * Store <type><value str...>
2889 */
2890 if (cc->fuzzer_dir >= 0) {
2891 char fuzzer_buffer[1024];
2892 char *fuzzer_p = fuzzer_buffer, *fuzzer_end = fuzzer_p + sizeof(fuzzer_buffer);
2893
2894 *fuzzer_p++ = (uint8_t)type; /* Fuzzer uses first byte for type */
2895
2896 strlcpy(fuzzer_p, data, slen > fuzzer_end - fuzzer_p ? fuzzer_end - fuzzer_p : slen);
2897
2898 if (dump_fuzzer_data(cc->fuzzer_dir, fuzzer_buffer,
2899 (uint8_t *)fuzzer_buffer, strlen(fuzzer_buffer)) < 0) {
2901 }
2902 }
2903
2904 talloc_free(box2);
2905 talloc_free(box);
2906 RETURN_OK(slen);
2907}
2908
2910 char *data, size_t data_used, char *in, size_t inlen)
2911{
2912 int fd;
2913 char *path;
2914 bool locked = false;
2915
2916 path = talloc_bstrndup(cc->tmp_ctx, in, inlen);
2917
2918 fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
2919 if (fd < 0) {
2920 fr_strerror_printf("Failed opening \"%s\": %s", path, fr_syserror(errno));
2921 error:
2922 talloc_free(path);
2923 if (fd >= 0) {
2924 if (locked) (void)flock(fd, LOCK_UN);
2925 close(fd);
2926 }
2928 }
2929
2930 if (flock(fd, LOCK_EX) < 0) {
2931 fr_strerror_printf("Failed locking \"%s\": %s", path, fr_syserror(errno));
2932 goto error;
2933 }
2934 locked = true;
2935
2936 while (data_used) {
2937 ssize_t ret;
2938 ret = write(fd, data, data_used);
2939 if (ret < 0) {
2940 fr_strerror_printf("Failed writing to \"%s\": %s", path, fr_syserror(errno));
2941 goto error;
2942 }
2943 data_used -= ret;
2944 data += ret;
2945 }
2946 (void)flock(fd, LOCK_UN);
2947 talloc_free(path);
2948 close(fd);
2949
2950 RETURN_OK(data_used);
2951}
2952
2953/** Parse an reprint and xlat expansion
2954 *
2955 */
2957 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2958{
2959 ssize_t slen;
2960 xlat_exp_head_t *head = NULL;
2961 size_t input_len = strlen(in), escaped_len;
2962 fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
2963
2964 slen = xlat_tokenize(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), &p_rules,
2965 &(tmpl_rules_t) {
2966 .attr = {
2967 .dict_def = dictionary_current(cc),
2968 .list_def = request_attr_request,
2969 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2970 },
2971 .xlat = cc->tmpl_rules.xlat,
2972 });
2973 if (slen == 0) {
2974 fr_strerror_printf_push_head("ERROR failed to parse any input");
2976 }
2977
2978 if (slen < 0) {
2979 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
2980
2981 return_error:
2983 }
2984
2985 if (((size_t) slen != input_len)) {
2986 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
2987 goto return_error;
2988 }
2989
2991 RETURN_OK(escaped_len);
2992}
2993
2994/** Parse and reprint an xlat expression expansion
2995 *
2996 */
2998 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2999{
3000 ssize_t dec_len;
3001 xlat_exp_head_t *head = NULL;
3002 size_t input_len = strlen(in), escaped_len;
3003// fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
3004
3005 dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL,
3006 &(tmpl_rules_t) {
3007 .attr = {
3008 .dict_def = dictionary_current(cc),
3009 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3010 .list_def = request_attr_request,
3011 }
3012 });
3013 if (dec_len <= 0) {
3014 fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
3015
3016 return_error:
3018 }
3019
3020 if (((size_t) dec_len != input_len)) {
3021 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, dec_len);
3022 goto return_error;
3023 }
3024
3026 RETURN_OK(escaped_len);
3027}
3028
3029/** Parse, purify, and reprint an xlat expression expansion
3030 *
3031 */
3033 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3034{
3035 ssize_t slen;
3036 xlat_exp_head_t *head = NULL;
3037 size_t input_len = strlen(in), escaped_len;
3038 tmpl_rules_t t_rules = (tmpl_rules_t) {
3039 .attr = {
3041 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3042 .list_def = request_attr_request,
3043 },
3044 .xlat = cc->tmpl_rules.xlat,
3045 .at_runtime = true,
3046 };
3047
3048 if (!el) {
3049 fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
3050 goto return_error;
3051 }
3052 t_rules.xlat.runtime_el = el;
3053
3054 slen = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
3055 if (slen == 0) {
3056 fr_strerror_printf_push_head("ERROR failed to parse any input");
3058 }
3059
3060 if (slen < 0) {
3061 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3062 return_error:
3064 }
3065
3066 if (((size_t) slen != input_len)) {
3067 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, slen);
3068 goto return_error;
3069 }
3070
3071 if (fr_debug_lvl > 2) {
3072 DEBUG("Before purify --------------------------------------------------");
3074 }
3075
3076 if (xlat_purify(head, NULL) < 0) {
3077 fr_strerror_printf_push_head("ERROR purifying node - %s", fr_strerror());
3078 goto return_error;
3079 }
3080
3081 if (fr_debug_lvl > 2) {
3082 DEBUG("After purify --------------------------------------------------");
3084 }
3085
3087 RETURN_OK(escaped_len);
3088}
3089
3090
3091/** Parse an reprint and xlat argv expansion
3092 *
3093 */
3095 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3096{
3097 int i, argc;
3098 char *p;
3099 ssize_t slen;
3100 xlat_exp_head_t *head = NULL;
3101 xlat_exp_head_t **argv;
3102 size_t len;
3103 size_t input_len = strlen(in);
3104 char buff[1024];
3105
3106 slen = xlat_tokenize_argv(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len),
3107 NULL, NULL,
3108 &(tmpl_rules_t) {
3109 .attr = {
3110 .dict_def = dictionary_current(cc),
3111 .list_def = request_attr_request,
3112 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
3113 },
3114 }, true);
3115 if (slen <= 0) {
3116 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
3118 }
3119
3120 argc = xlat_flatten_to_argv(cc->tmp_ctx, &argv, head);
3121 if (argc <= 0) {
3122 fr_strerror_printf_push("ERROR in argument %d", (int) -argc);
3124 }
3125
3126 for (i = 0, p = data; i < argc; i++) {
3127 (void) xlat_print(&FR_SBUFF_OUT(buff, sizeof(buff)), argv[i], NULL);
3128
3129 len = snprintf(p, data + COMMAND_OUTPUT_MAX - p, "[%d]{ %s }, ", i, buff);
3130 p += len;
3131 }
3132
3133 p -= 2;
3134 *p = '\0';
3135
3136 RETURN_OK(p - data);
3137}
3138
3140 { L("#"), &(command_entry_t){
3141 .func = command_comment,
3142 .usage = "#<string>",
3143 .description = "A comment - not processed"
3144 }},
3145 { L("$INCLUDE "), &(command_entry_t){
3146 .func = command_include,
3147 .usage = "$INCLUDE <relative_path>",
3148 .description = "Execute a test file"
3149 }},
3150 { L("allow-unresolved "), &(command_entry_t){
3152 .usage = "allow-unresolved yes|no",
3153 .description = "Allow or disallow unresolved attributes in xlats and references"
3154 }},
3155 { L("calc "), &(command_entry_t){
3156 .func = command_calc,
3157 .usage = "calc <type1> <value1> <operator> <type2> <value2> -> <output-type>",
3158 .description = "Perform calculations on value boxes",
3159 }},
3160 { L("calc_nary "), &(command_entry_t){
3161 .func = command_calc_nary,
3162 .usage = "calc_nary op <type1> <value1> <type2> <value2> ... -> <output-type>",
3163 .description = "Perform calculations on value boxes",
3164 }},
3165 { L("cast "), &(command_entry_t){
3166 .func = command_cast,
3167 .usage = "cast (type) <value> -> <output-type>",
3168 .description = "Perform calculations on value boxes",
3169 }},
3170 { L("cd "), &(command_entry_t){
3171 .func = command_cd,
3172 .usage = "cd <path>",
3173 .description = "Change the directory for loading dictionaries and $INCLUDEs, writing the full path into the data buffer on success"
3174 }},
3175 { L("clear"), &(command_entry_t){
3176 .func = command_clear,
3177 .usage = "clear",
3178 .description = "Explicitly zero out the contents of the data buffer"
3179 }},
3180 { L("command add "), &(command_entry_t){
3181 .func = command_radmin_add,
3182 .usage = "command add <string>",
3183 .description = "Add a command to a radmin command tree"
3184 }},
3185 { L("command tab "), &(command_entry_t){
3186 .func = command_radmin_tab,
3187 .usage = "command tab <string>",
3188 .description = "Test a tab completion against a radmin command tree"
3189 }},
3190 { L("condition "), &(command_entry_t){
3192 .usage = "condition <string>",
3193 .description = "Parse and reprint a condition, writing the normalised condition to the data buffer on success"
3194 }},
3195 { L("count"), &(command_entry_t){
3196 .func = command_count,
3197 .usage = "count",
3198 .description = "Write the number of executed tests to the data buffer. A test is any command that should return 'ok'"
3199 }},
3200 { L("decode-dns-label "), &(command_entry_t){
3202 .usage = "decode-dns-label (-|<hex_string>)",
3203 .description = "Decode one or more DNS labels, writing the decoded strings to the data buffer.",
3204 }},
3205 { L("decode-pair"), &(command_entry_t){
3206 .func = command_decode_pair,
3207 .usage = "decode-pair[.<testpoint_symbol>] (-|<hex_string>)",
3208 .description = "Produce an attribute value pair from a binary value using a specified protocol decoder. Protocol must be loaded with \"load <protocol>\" first",
3209 }},
3210 { L("decode-proto"), &(command_entry_t){
3211 .func = command_decode_proto,
3212 .usage = "decode-proto[.<testpoint_symbol>] (-|<hex string>)",
3213 .description = "Decode a packet as attribute value pairs from a binary value using a specified protocol decoder. Protocol must be loaded with \"load <protocol>\" first",
3214 }},
3215 { L("dictionary "), &(command_entry_t){
3217 .usage = "dictionary <string>",
3218 .description = "Parse dictionary attribute definition, writing \"ok\" to the data buffer if successful",
3219 }},
3220 { L("dictionary-dump"), &(command_entry_t){
3222 .usage = "dictionary-dump",
3223 .description = "Print the contents of the currently active dictionary to stdout",
3224 }},
3225 { L("encode-dns-label "), &(command_entry_t){
3227 .usage = "encode-dns-label (-|string[,string])",
3228 .description = "Encode one or more DNS labels, writing a hex string to the data buffer.",
3229 }},
3230 { L("encode-pair"), &(command_entry_t){
3231 .func = command_encode_pair,
3232 .usage = "encode-pair[.<testpoint_symbol>] [truncate] (-|<attribute> = <value>[,<attribute = <value>])",
3233 .description = "Encode one or more attribute value pairs, writing a hex string to the data buffer. Protocol must be loaded with \"load <protocol>\" first",
3234 }},
3235 { L("encode-proto"), &(command_entry_t){
3236 .func = command_encode_proto,
3237 .usage = "encode-proto[.<testpoint_symbol>] (-|<attribute> = <value>[,<attribute = <value>])",
3238 .description = "Encode one or more attributes as a packet, writing a hex string to the data buffer. Protocol must be loaded with \"proto <protocol>\" first"
3239 }},
3240 { L("eof"), &(command_entry_t){
3241 .func = command_eof,
3242 .usage = "eof",
3243 .description = "Mark the end of a 'virtual' file. Used to prevent 'need-feature' skipping all the content of a command stream or file",
3244 }},
3245 { L("exit"), &(command_entry_t){
3246 .func = command_exit,
3247 .usage = "exit[ <num>]",
3248 .description = "Exit with the specified error number. If no <num> is provided, process will exit with 0"
3249 }},
3250 { L("fuzzer-out"), &(command_entry_t){
3251 .func = command_fuzzer_out,
3252 .usage = "fuzzer-out <dir>",
3253 .description = "Write encode-pair, encode-proto, and encode-dns-label output, and value input as separate files in the specified directory. Text input will be sha1 hashed and base64 encoded to create the filename",
3254 }},
3255 { L("load-dictionary "),&(command_entry_t){
3257 .usage = "load-dictionary <name> [<dir>]",
3258 .description = "Load an additional dictionary from the same directory as the input file. "
3259 "Optionally you can specify a full path via <dir>. ",
3260 }},
3261 { L("match"), &(command_entry_t){
3262 .func = command_match,
3263 .usage = "match <string>",
3264 .description = "Compare the contents of the data buffer with an expected value"
3265 }},
3266 { L("match-regex "), &(command_entry_t){
3267 .func = command_match_regex,
3268 .usage = "match-regex <regex>",
3269 .description = "Compare the contents of the data buffer with a regular expression"
3270 }},
3271 { L("max-buffer-size"), &(command_entry_t){
3273 .usage = "max-buffer-size[ <integer>]",
3274 .description = "Limit the maximum temporary buffer space available for any command which uses it"
3275 }},
3276 { L("migrate "), &(command_entry_t){
3277 .func = command_migrate,
3278 .usage = "migrate <flag>=<value>",
3279 .description = "Set migration flag"
3280 }},
3281 { L("need-feature "), &(command_entry_t){
3282 .func = command_need_feature,
3283 .usage = "need-feature <feature>",
3284 .description = "Skip the contents of the current file, or up to the next \"eof\" command if a particular feature is not available"
3285 }},
3286 { L("no "), &(command_entry_t){
3287 .func = command_no,
3288 .usage = "no ...",
3289 .description = "Negate the result of a command returning 'ok'"
3290 }},
3291 { L("pair "), &(command_entry_t){
3292 .func = command_pair,
3293 .usage = "pair ... data ...",
3294 .description = "Parse a list of pairs",
3295 }},
3296 { L("proto "), &(command_entry_t){
3297 .func = command_proto,
3298 .usage = "proto <protocol>",
3299 .description = "Switch the active protocol to the one specified, unloading the previous protocol",
3300 }},
3301 { L("proto-dictionary "),&(command_entry_t){
3303 .usage = "proto-dictionary <proto_name> [<proto_dir>]",
3304 .description = "Switch the active dictionary. Root is set to the default dictionary path, or the one specified with -d. <proto_dir> is relative to the root.",
3305 }},
3306
3307
3308 { L("proto-dictionary-root "), &(command_entry_t){
3310 .usage = "proto-dictionary-root[ <root_attribute>]",
3311 .description = "Set the root attribute for the current protocol dictionary. "
3312 "If no attribute name is provided, the root will be reset to the root of the current dictionary",
3313 }},
3314 { L("raw "), &(command_entry_t){
3315 .func = command_encode_raw,
3316 .usage = "raw <string>",
3317 .description = "Create nested attributes from OID strings and values"
3318 }},
3319 { L("read_file "), &(command_entry_t){
3320 .func = command_read_file,
3321 .usage = "read_file <filename>",
3322 .description = "Read a list of pairs from a file",
3323 }},
3324 { L("returned"), &(command_entry_t){
3325 .func = command_returned,
3326 .usage = "returned",
3327 .description = "Print the returned value to the data buffer"
3328 }},
3329
3330 { L("tmpl "), &(command_entry_t){
3331 .func = command_tmpl,
3332 .usage = "parse <string>",
3333 .description = "Parse then print a tmpl expansion, writing the normalised tmpl expansion to the data buffer"
3334 }},
3335
3336 { L("tmpl-rules "), &(command_entry_t){
3337 .func = command_tmpl_rules,
3338 .usage = "tmpl-rule [allow_foreign=yes] [allow_unknown=yes|no] [allow_unresolved=yes|no] [attr_parent=<oid>] [list_def=request|reply|control|session-state] [request_def=current|outer|parent]",
3339 .description = "Alter the tmpl parsing rules for subsequent tmpl parsing commands in the same command context"
3340 }},
3341 { L("touch "), &(command_entry_t){
3342 .func = command_touch,
3343 .usage = "touch <file>",
3344 .description = "Touch a file, updating its created timestamp. Useful for marking the completion of a series of tests"
3345 }},
3346 { L("value "), &(command_entry_t){
3348 .usage = "value <type> <string>",
3349 .description = "Parse a value of a given type from its presentation form, print it, then parse it again (checking printed/parsed versions match), writing printed form to the data buffer"
3350 }},
3351 { L("write "), &(command_entry_t){
3352 .func = command_write,
3353 .usage = "write <file>",
3354 .description = "Write the contents of the data buffer (as a raw binary string) to the specified file"
3355 }},
3356 { L("xlat "), &(command_entry_t){
3357 .func = command_xlat_normalise,
3358 .usage = "xlat <string>",
3359 .description = "Parse then print an xlat expansion, writing the normalised xlat expansion to the data buffer"
3360 }},
3361
3362 { L("xlat_argv "), &(command_entry_t){
3363 .func = command_xlat_argv,
3364 .usage = "xlat_argv <string>",
3365 .description = "Parse then print an xlat expansion argv, writing the normalised xlat expansion arguments to the data buffer"
3366 }},
3367
3368 { L("xlat_expr "), &(command_entry_t){
3369 .func = command_xlat_expr,
3370 .usage = "xlat_expr <string>",
3371 .description = "Parse then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3372 }},
3373
3374 { L("xlat_purify "), &(command_entry_t){
3375 .func = command_xlat_purify,
3376 .usage = "xlat_purify <string>",
3377 .description = "Parse, purify, then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3378 }},
3379
3380};
3382
3383size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used,
3384 char *in, UNUSED size_t inlen)
3385{
3386
3387 command_entry_t *command;
3388 size_t match_len;
3389 char *p;
3390
3391 p = in;
3393
3394 /*
3395 * Skip empty lines and comments.
3396 */
3397 if (!*p || (*p == '#')) {
3398 /*
3399 * Dump the input to the output.
3400 */
3401 if (write_fp) {
3402 fputs(in, write_fp);
3403 fputs("\n", write_fp);
3404 }
3405
3406 RETURN_NOOP(data_used);
3407 }
3408
3409 DEBUG2("%s[%d]: %s", cc->filename, cc->lineno, p);
3410
3411 /*
3412 * Look up the command by longest prefix
3413 */
3414 command = fr_table_value_by_longest_prefix(&match_len, commands, p, -1, NULL);
3415 if (!command) {
3416 fr_strerror_printf("Unknown command: %s", p);
3418 }
3419
3420 p += match_len; /* Jump to after the command */
3421 fr_skip_whitespace(p); /* Skip any whitespace */
3422
3423 /*
3424 * Feed the data buffer in as the command
3425 */
3426 if ((p[0] == '-') && ((p[1] == ' ') || (p[1] == '\0'))) {
3427 data_used = command->func(result, cc, data, data_used, data, data_used);
3428 }
3429 else {
3430 data_used = command->func(result, cc, data, data_used, p, strlen(p));
3431 }
3432
3433 /*
3434 * Dump the contents of the error stack
3435 * to the data buffer.
3436 *
3437 * This is then what's checked in
3438 * subsequent match commands.
3439 */
3440 if (result->error_to_data) data_used = strerror_concat(data, COMMAND_OUTPUT_MAX);
3441
3442 fr_assert((size_t)data_used < COMMAND_OUTPUT_MAX);
3443 data[data_used] = '\0'; /* Ensure the data buffer is \0 terminated */
3444
3445 if (data_used) {
3446 DEBUG2("%s[%d]: --> %s (%zu bytes in buffer)", cc->filename, cc->lineno,
3447 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"), data_used);
3448 } else {
3449 DEBUG2("%s[%d]: --> %s", cc->filename, cc->lineno,
3450 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"));
3451 }
3452
3453 /*
3454 * Dump the input to the output.
3455 */
3456 if (write_fp) {
3457 fputs(in, write_fp);
3458 fputs("\n", write_fp);
3459 };
3460
3461 talloc_free_children(cc->tmp_ctx);
3462
3463 return data_used;
3464}
3465
3467{
3468 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3469 fr_perror("unit_test_attribute");
3470 return -1;
3471 }
3472 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) {
3473 fr_perror("unit_test_attribute");
3474 return -1;
3475 }
3476 if (cc->fuzzer_dir >= 0) {
3477 close(cc->fuzzer_dir);
3478 cc->fuzzer_dir = -1;
3479 }
3480 return 0;
3481}
3482
3484 command_config_t const *config, char const *path, char const *filename)
3485{
3487
3488 cc = talloc_zero(ctx, command_file_ctx_t);
3489 talloc_set_destructor(cc, _command_ctx_free);
3490
3491 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3492 cc->path = talloc_strdup(cc, path);
3493 cc->filename = filename;
3494 cc->config = config;
3495
3496 /*
3497 * Allocate a special buffer with poisoned regions
3498 * at either end.
3499 */
3501 talloc_free(cc);
3502 return NULL;
3503 }
3506
3507 /*
3508 * Initialise a special temporary dictionary context
3509 *
3510 * Any protocol dictionaries loaded by "test-dictionary"
3511 * go in this context, and don't affect the main
3512 * dictionary context.
3513 */
3514 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3515 if (!cc->test_gctx) {
3516 fr_perror("Failed allocating test dict_gctx");
3517 return NULL;
3518 }
3519
3522 fr_perror("Failed loading test dict_gctx internal dictionary");
3523 return NULL;
3524 }
3525
3526 fr_dict_global_ctx_dir_set(cc->path); /* Load new dictionaries relative to the test file */
3528
3529 cc->fuzzer_dir = -1;
3530
3532 cc->tmpl_rules.attr.namespace = fr_dict_root(cc->config->dict);
3533 cc->tmpl_rules.attr.allow_unresolved = false; /* tests have to use real attributes */
3534
3535 return cc;
3536}
3537
3538static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
3539{
3540 talloc_free(cc->tmp_ctx);
3541 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3542 cc->test_count = 0;
3543
3544 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3545 fr_perror("unit_test_attribute");
3546 }
3547
3548 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) fr_perror("unit_test_attribute");
3549
3550 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3552 fr_perror("Failed loading test dict_gctx internal dictionary");
3553 }
3554
3555 if (cc->fuzzer_dir >= 0) {
3556 close(cc->fuzzer_dir);
3557 cc->fuzzer_dir = -1;
3558 }
3559}
3560
3561static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config,
3562 const char *root_dir, char const *filename, fr_dlist_head_t *lines)
3563{
3564 int ret = 0;
3565 FILE *fp; /* File we're reading from */
3566 char buffer[8192]; /* Command buffer */
3567 char data[COMMAND_OUTPUT_MAX + 1]; /* Data written by previous command */
3568 ssize_t data_used = 0; /* How much data the last command wrote */
3569 static char path[PATH_MAX] = "";
3570 command_line_range_t *lr = NULL;
3571 bool opened_fp = false;
3572
3574
3575 cc = command_ctx_alloc(ctx, config, root_dir, filename);
3576
3577 /*
3578 * Open the file, or stdin
3579 */
3580 if (strcmp(filename, "-") == 0) {
3581 fp = stdin;
3582 filename = "<stdin>";
3583 } else {
3584 if (root_dir && *root_dir) {
3585 snprintf(path, sizeof(path), "%s/%s", root_dir, filename);
3586 } else {
3587 strlcpy(path, filename, sizeof(path));
3588 }
3589
3590 fp = fopen(path, "r");
3591 if (!fp) {
3592 ERROR("Error opening test file \"%s\": %s", path, fr_syserror(errno));
3593 ret = -1;
3594 goto finish;
3595 }
3596
3597 filename = path;
3598 opened_fp = true;
3599 }
3600
3601 if (lines && !fr_dlist_empty(lines)) lr = fr_dlist_head(lines);
3602
3603 /*
3604 * Loop over lines in the file or stdin
3605 */
3606 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3607 command_result_t result = { .rcode = RESULT_OK }; /* Reset to OK */
3608 char *p = strchr(buffer, '\n');
3609
3611 cc->lineno++; /* The first line of the file becomes line 1 */
3612
3613 if (lr) {
3614 if (cc->lineno > lr->end) {
3615 lr = fr_dlist_next(lines, lr);
3616 if (!lr) goto finish;
3617 }
3618
3619 if (cc->lineno < lr->start) continue;
3620 }
3621
3622 if (!p) {
3623 if (!feof(fp)) {
3624 ERROR("Line %d too long in %s/%s", cc->lineno, cc->path, cc->filename);
3625 ret = -1;
3626 goto finish;
3627 }
3628 } else {
3629 *p = '\0';
3630 }
3631
3632 data_used = process_line(&result, cc, data, data_used, buffer, strlen(buffer));
3633 switch (result.rcode) {
3634 /*
3635 * Command completed successfully
3636 */
3637 case RESULT_OK:
3638 cc->test_count++;
3639 continue;
3640
3641 /*
3642 * Did nothing (not a test)
3643 */
3644 case RESULT_NOOP:
3645 continue;
3646
3647 /*
3648 * If this is a file, then break out of the loop
3649 * and cleanup, otherwise we need to find the
3650 * EOF marker in the input stream.
3651 */
3652 case RESULT_SKIP_FILE:
3653 if (fp != stdin) goto finish;
3654
3655 /*
3656 * Skip over the input stream until we
3657 * find an eof command, or the stream
3658 * is closed.
3659 */
3660 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3661 command_entry_t *command;
3662 size_t match_len;
3663
3664 command = fr_table_value_by_longest_prefix(&match_len, commands, buffer, -1, NULL);
3665 if (!command) {
3666 ERROR("%s[%d]: Unknown command: %s", cc->path, cc->lineno, p);
3667 ret = -1;
3668 goto finish;
3669 }
3670
3671 if (command->func == command_eof) {
3672 command_ctx_reset(cc, ctx);
3673 break;
3674 }
3675 }
3676 goto finish;
3677
3678 /*
3679 * Fatal error parsing a command
3680 */
3681 case RESULT_PARSE_ERROR:
3683 fr_perror("%s[%d]", filename, cc->lineno);
3684 ret = -1;
3685 goto finish;
3686
3687 /*
3688 * Result didn't match what we expected
3689 */
3690 case RESULT_MISMATCH:
3691 {
3692 ret = EXIT_FAILURE;
3693 goto finish;
3694 }
3695
3696 case RESULT_EXIT:
3697 ret = result.ret;
3698 *exit_now = true;
3699 goto finish;
3700
3701 default:
3702 /*
3703 * If this happens, fix the damn command.
3704 */
3705 fr_assert_msg(false, "Command exited with invalid return code (%i)", result.rcode);
3706 ret = -1;
3707 goto finish;
3708 }
3709 }
3710
3711finish:
3712 /* The explicit check is to quiet clang_analyzer */
3713 if (opened_fp) fclose(fp);
3714
3715 /*
3716 * Free any residual resources we loaded.
3717 */
3718 if (cc && (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0)) {
3719 fr_perror("unit_test_attribute");
3720 ret = -1;
3721 }
3722
3723 fr_dict_global_ctx_set(config->dict_gctx); /* Switch back to the main dict ctx */
3725 talloc_free(cc);
3726
3727 return ret;
3728}
3729
3730static void usage(char const *name)
3731{
3732 INFO("usage: %s [options] (-|<filename>[:<lines>] [ <filename>[:<lines>]])", name);
3733 INFO("options:");
3734 INFO(" -d <raddb> Set user dictionary path (defaults to " RADDBDIR ").");
3735 INFO(" -D <dictdir> Set main dictionary path (defaults to " DICTDIR ").");
3736 INFO(" -x Debugging mode.");
3737 INFO(" -f Print features.");
3738 INFO(" -c Print commands.");
3739 INFO(" -h Print help text.");
3740 INFO(" -M Show talloc memory report.");
3741 INFO(" -p Allow xlat_purify");
3742 INFO(" -r <receipt_file> Create the <receipt_file> as a 'success' exit.");
3743 INFO(" -w <output_file> Write 'corrected' output to <output_file>.");
3744 INFO("Where <filename> is a file containing one or more commands and '-' indicates commands should be read from stdin.");
3745 INFO("Ranges of <lines> may be specified in the format <start>[-[<end>]][,]");
3746}
3747
3748static void features_print(CONF_SECTION *features)
3749{
3750 CONF_PAIR *cp;
3751
3752 INFO("features:");
3753 for (cp = cf_pair_find(features, CF_IDENT_ANY);
3754 cp;
3755 cp = cf_pair_find_next(features, cp, CF_IDENT_ANY)) {
3756 INFO(" %s %s", cf_pair_attr(cp), cf_pair_value(cp));
3757 }
3758}
3759
3760static void commands_print(void)
3761{
3762 size_t i;
3763
3764 INFO("commands:");
3765 for (i = 0; i < commands_len; i++) {
3766 INFO(" %s:", ((command_entry_t const *)commands[i].value)->usage);
3767 INFO(" %s.", ((command_entry_t const *)commands[i].value)->description);
3768 INFO("%s", "");
3769 }
3770}
3771
3772static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *in)
3773{
3774 static bool tokens[UINT8_MAX + 1] = { [','] = true , ['-'] = true };
3775 uint32_t max = 0;
3778
3779 while (fr_sbuff_extend(in)) {
3780 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3781
3782 MEM(lr = talloc_zero(ctx, command_line_range_t));
3784
3785 fr_sbuff_out(&err, &lr->start, in);
3786 if (err != FR_SBUFF_PARSE_OK) {
3787 ERROR("Invalid line start number");
3788 error:
3790 return -1;
3791 }
3792 if (max > lr->start) {
3793 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->start);
3794 goto error;
3795 } else {
3796 max = lr->start;
3797 }
3798 lr->end = lr->start; /* Default to a single line */
3799 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3800
3801 again:
3802 if (!fr_sbuff_extend(in)) break;
3803 if (!fr_sbuff_is_in_charset(in, tokens)) {
3804 ERROR("Unexpected text \"%pV\"",
3806 goto error;
3807 }
3808
3809 fr_sbuff_switch(in, '\0') {
3810 /*
3811 * More ranges...
3812 */
3813 case ',':
3814 fr_sbuff_next(in);
3815 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3816 continue;
3817
3818 /*
3819 * <start>-<end>
3820 */
3821 case '-':
3822 {
3823 fr_sbuff_next(in);
3824 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3825
3826 /*
3827 * A bare '-' with no number means
3828 * run all remaining lines.
3829 */
3830 if (fr_sbuff_extend(in) == 0) {
3831 lr->end = UINT32_MAX;
3832 return 0;
3833 }
3834
3835 fr_sbuff_out(&err, &lr->end, in);
3836 if (err != FR_SBUFF_PARSE_OK) {
3837 ERROR("Invalid line end number");
3838 goto error;
3839 }
3840 if (lr->end < lr->start) {
3841 ERROR("Line end must be >= line start (%u < %u)", lr->end, lr->start);
3842 goto error;
3843 }
3844 if (max > lr->end) {
3845 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->end);
3846 goto error;
3847 } else {
3848 max = lr->end;
3849 }
3850 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3851 }
3852 goto again;
3853 }
3854 }
3855
3856 return 0;
3857}
3858
3859/**
3860 *
3861 * @hidecallgraph
3862 */
3863int main(int argc, char *argv[])
3864{
3865 int c;
3866 char const *receipt_file = NULL;
3867 CONF_SECTION *cs;
3868 int ret = EXIT_SUCCESS;
3869 TALLOC_CTX *autofree;
3870 TALLOC_CTX *thread_ctx;
3871 bool exit_now = false;
3872
3874 .raddb_dir = RADDBDIR,
3875 .dict_dir = DICTDIR
3876 };
3877
3878 char const *name;
3879 bool do_features = false;
3880 bool do_commands = false;
3881 bool do_usage = false;
3882 bool allow_purify = false;
3883 xlat_t *xlat;
3884
3885 /*
3886 * Must be called first, so the handler is called last
3887 */
3889
3891 thread_ctx = talloc_new(autofree);
3892
3893#ifndef NDEBUG
3894 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
3895 fr_perror("unit_test_attribute");
3896 goto cleanup;
3897 }
3898#else
3900#endif
3901
3902 /*
3903 * Sync wallclock and cpu time so that we can find
3904 * uses of fr_time_[to|from]_* where
3905 * fr_unix_time_[to|from]_* should be used.
3906 *
3907 * If the wallclock/cpu offset is 0, then both sets
3908 * of macros produce the same result.
3909 */
3910 fr_time_start();
3911
3912 /*
3913 * Allocate a root config section so we can write
3914 * out features and versions.
3915 */
3916 MEM(cs = cf_section_alloc(autofree, NULL, "unit_test_attribute", NULL));
3917 MEM(config.features = cf_section_alloc(cs, cs, "feature", NULL));
3918 dependency_features_init(config.features); /* Add build time features to the config section */
3919
3920 name = argv[0];
3921
3923 default_log.fd = STDOUT_FILENO;
3924 default_log.print_level = false;
3925
3926 while ((c = getopt(argc, argv, "cd:D:F:fxMhpr:S:w:")) != -1) switch (c) {
3927 case 'c':
3928 do_commands = true;
3929 break;
3930
3931 case 'd':
3932 config.raddb_dir = optarg;
3933 break;
3934
3935 case 'D':
3936 config.dict_dir = optarg;
3937 break;
3938
3939 case 'F':
3940 config.fuzzer_dir = optarg;
3941 break;
3942
3943 case 'f':
3944 do_features = true;
3945 break;
3946
3947 case 'x':
3948 fr_debug_lvl++;
3949 if (fr_debug_lvl > 2) default_log.print_level = true;
3950 break;
3951
3952 case 'M':
3953 talloc_enable_leak_report();
3954 break;
3955
3956 case 'r':
3957 receipt_file = optarg;
3958 break;
3959
3960 case 'p':
3961 allow_purify = true;
3962 break;
3963
3964 case 'S':
3965 fprintf(stderr, "Invalid option to -S\n");
3967
3968 case 'w':
3969 write_filename = optarg;
3970 break;
3971
3972 case 'h':
3973 default:
3974 do_usage = true; /* Just set a flag, so we can process extra -x args */
3975 break;
3976 }
3977 argc -= (optind - 1);
3978 argv += (optind - 1);
3979
3980 if (do_usage) usage(name);
3981 if (do_features) features_print(config.features);
3982 if (do_commands) commands_print();
3983 if (do_usage || do_features || do_commands) {
3984 ret = EXIT_SUCCESS;
3985 goto cleanup;
3986 }
3987
3988 if (receipt_file && (fr_unlink(receipt_file) < 0)) {
3989 fr_perror("unit_test_attribute");
3991 }
3992
3993 /*
3994 * Mismatch between the binary and the libraries it depends on
3995 */
3997 fr_perror("unit_test_attribute");
3999 }
4000
4001#ifdef WITH_TLS
4002 /*
4003 * OpenSSL can only be initialised once during the lifetime
4004 * of a process. Initialise it here so that we don't attempt
4005 * to unload and load it multiple times.
4006 */
4007 if (fr_openssl_init() < 0) {
4008 fr_perror("unit_test_attribute");
4010 }
4011#endif
4012
4013 modules_init(NULL);
4014
4015 dl_loader = dl_loader_init(autofree, NULL, false, false);
4016 if (!dl_loader) {
4017 fr_perror("unit_test_attribute");
4019 }
4020
4021 config.dict_gctx = fr_dict_global_ctx_init(NULL, true, config.dict_dir);
4022 if (!config.dict_gctx) {
4023 fr_perror("unit_test_attribute");
4025 }
4026
4028 fr_perror("unit_test_attribute");
4030 }
4031
4032 /*
4033 * Always needed so we can load the list attributes
4034 * otherwise the tmpl_tokenize code fails.
4035 */
4036 if (request_global_init() < 0) {
4037 fr_perror("unit_test_attribute");
4039 }
4040
4041 /*
4042 * Initialise the interpreter, registering operations.
4043 * Needed because some keywords also register xlats.
4044 */
4045 if (unlang_global_init() < 0) {
4046 fr_perror("unit_test_attribute");
4048 }
4049
4050 /*
4051 * Create a dummy event list
4052 */
4053 if (allow_purify) {
4054 el = fr_event_list_alloc(autofree, NULL, NULL);
4055 fr_assert(el != NULL);
4056
4057 /*
4058 * Simulate thread specific instantiation
4059 */
4061 if (xlat_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
4062 }
4063
4064 unlang_thread_instantiate(thread_ctx);
4065
4066 xlat = xlat_func_register(NULL, "test", xlat_test, FR_TYPE_NULL);
4067 if (!xlat) {
4068 ERROR("Failed registering xlat");
4070 }
4072
4073 /*
4074 * And again WITHOUT arguments.
4075 */
4076 xlat = xlat_func_register(NULL, "test_no_args", xlat_test, FR_TYPE_NULL);
4077 if (!xlat) {
4078 ERROR("Failed registering xlat");
4080 }
4082
4083 /*
4084 * Disable hostname lookups, so we don't produce spurious DNS
4085 * queries, and there's no chance of spurious failures if
4086 * it takes a long time to get a response.
4087 */
4089
4090 /*
4091 * Read tests from stdin
4092 */
4093 if (argc < 2) {
4094 if (write_filename) {
4095 ERROR("Can't use '-w' with stdin");
4097 }
4098
4099 ret = process_file(&exit_now, autofree, &config, name, "-", NULL);
4100
4101 /*
4102 * ...or process each file in turn.
4103 */
4104 } else {
4105 int i;
4106
4107 if (write_filename) {
4108 if (argc != 2) { /* program name and file to write */
4109 ERROR("Can't use '-w' with multiple filenames");
4111 }
4112
4113 write_fp = fopen(write_filename, "w");
4114 if (!write_fp) {
4115 ERROR("Failed opening %s: %s", write_filename, strerror(errno));
4117 }
4118 }
4119
4120 /*
4121 * Loop over all input files.
4122 */
4123 for (i = 1; i < argc; i++) {
4124 char *dir = NULL, *file;
4125 fr_sbuff_t in = FR_SBUFF_IN(argv[i], strlen(argv[i]));
4127 L("/"),
4128 L(":")
4129 );
4130 fr_sbuff_marker_t file_start, file_end, dir_end;
4131 fr_dlist_head_t lines;
4132
4133 fr_sbuff_marker(&file_start, &in);
4134 fr_sbuff_marker(&file_end, &in);
4135 fr_sbuff_marker(&dir_end, &in);
4136 fr_sbuff_set(&file_end, fr_sbuff_end(&in));
4137
4138 fr_dlist_init(&lines, command_line_range_t, entry);
4139
4140 while (fr_sbuff_extend(&in)) {
4141 fr_sbuff_adv_until(&in, SIZE_MAX, &dir_sep, '\0');
4142
4143 fr_sbuff_switch(&in, '\0') {
4144 case '/':
4145 fr_sbuff_set(&dir_end, &in);
4146 fr_sbuff_advance(&in, 1);
4147 fr_sbuff_set(&file_start, &in);
4148 break;
4149
4150 case ':':
4151 fr_sbuff_set(&file_end, &in);
4152 fr_sbuff_advance(&in, 1);
4153 if (line_ranges_parse(autofree, &lines, &in) < 0) EXIT_WITH_FAILURE;
4154 break;
4155
4156 default:
4157 fr_sbuff_set(&file_end, &in);
4158 break;
4159 }
4160 }
4161
4163 fr_sbuff_current(&file_start), fr_sbuff_diff(&file_end, &file_start));
4164 if (fr_sbuff_used(&dir_end)) dir = talloc_bstrndup(autofree,
4166 fr_sbuff_used(&dir_end));
4167
4168 ret = process_file(&exit_now, autofree, &config, dir, file, &lines);
4169 talloc_free(dir);
4171 fr_dlist_talloc_free(&lines);
4172
4173 if ((ret != 0) || exit_now) break;
4174 }
4175
4176 if (write_fp) {
4177 fclose(write_fp);
4178 if (rename(write_filename, argv[1]) < 0) {
4179 ERROR("Failed renaming %s: %s", write_filename, strerror(errno));
4181 }
4182 }
4183 }
4184
4185 /*
4186 * Try really hard to free any allocated
4187 * memory, so we get clean talloc reports.
4188 */
4189cleanup:
4190 /*
4191 * Ensure all thread local memory is cleaned up
4192 * at the appropriate time. This emulates what's
4193 * done with worker/network threads in the
4194 * scheduler.
4195 */
4197
4198#ifdef WITH_TLS
4199 fr_openssl_free();
4200#endif
4201
4202 /*
4203 * dl_loader check needed as talloc_free
4204 * returns -1 on failure.
4205 */
4206 if (dl_loader && (talloc_free(dl_loader) < 0)) {
4207 fr_perror("unit_test_attribute - dl_loader - "); /* Print free order issues */
4209 }
4210 if (fr_dict_free(&config.dict, __FILE__) < 0) {
4211 fr_perror("unit_test_attribute");
4213 }
4214
4215 if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
4216 fr_perror("unit_test_attribute");
4218 }
4219
4220 /*
4221 * Explicitly free the autofree context
4222 * to make errors less confusing.
4223 */
4224 if (talloc_free(autofree) < 0) {
4225 fr_perror("unit_test_attribute");
4227 }
4228
4229 /*
4230 * Ensure our atexit handlers run before any other
4231 * atexit handlers registered by third party libraries.
4232 */
4234
4235 return ret;
4236}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
strcpy(log_entry->msg, buffer)
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:160
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:286
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:233
ssize_t fr_base64_encode_nstd(fr_sbuff_t *out, fr_dbuff_t *in, bool add_padding, char const alphabet[static UINT8_MAX])
Base 64 encode binary data.
Definition base64.c:326
char const fr_base64_url_alphabet_encode[UINT8_MAX]
Definition base64.c:173
static TALLOC_CTX * autofree
Definition fuzzer.c:45
#define CMD_MAX_ARGV
Definition radmin.c:150
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
int fr_value_calc_nary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_token_t op, fr_value_box_t const *group)
Calculate DST = OP { A, B, C, ... }.
Definition calc.c:2269
int fr_value_calc_assignment_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_token_t op, fr_value_box_t const *src)
Calculate DST OP SRC.
Definition calc.c:2420
int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
Calculate DST = A OP B.
Definition calc.c:1924
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *cs, CONF_PAIR const *prev, char const *attr)
Find a pair with a name matching attr, after specified pair.
Definition cf_util.c:1440
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1426
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1581
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1565
#define cf_lineno_set(_ci, _lineno)
Definition cf_util.h:131
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
#define cf_filename_set(_ci, _filename)
Definition cf_util.h:128
#define CF_IDENT_ANY
Definition cf_util.h:78
int fr_command_walk(fr_cmd_t *head, void **walk_ctx, void *ctx, fr_cmd_walk_t callback)
Walk over a command hierarchy.
Definition command.c:1027
void fr_cmd_debug(FILE *fp, fr_cmd_t *head)
Definition command.c:1610
int fr_command_add(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add one command to the global command tree.
Definition command.c:733
int fr_command_tab_expand(TALLOC_CTX *ctx, fr_cmd_t *head, fr_cmd_info_t *info, int max_expansions, char const **expansions)
Get the commands && help at a particular level.
Definition command.c:1298
char const ** parents
Definition command.h:66
char const * help
help text
Definition command.h:55
int argc
current argument count
Definition command.h:39
fr_cmd_func_t func
function to process this command
Definition command.h:56
char const * syntax
Definition command.h:68
char const * syntax
e.g. "STRING"
Definition command.h:54
fr_value_box_t ** box
value_box version of commands.
Definition command.h:43
bool read_only
Definition command.h:58
char const * parent
e.g. "show module"
Definition command.h:52
fr_cmd_tab_t tab_expand
tab expand things in the syntax string
Definition command.h:57
int max_argc
maximum number of arguments
Definition command.h:40
char const * name
e.g. "stats"
Definition command.h:53
char const ** argv
text version of commands
Definition command.h:42
char const * name
Definition command.h:67
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
Definition debug.c:1020
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1055
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
#define MEM(x)
Definition debug.h:36
void dependency_features_init(CONF_SECTION *cs)
Initialise core feature flags.
Definition dependency.c:183
static char const * spaces
Definition dependency.c:360
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:114
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir)
Initialise the global protocol hashes.
Definition dict_util.c:4525
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name, char const *dependent)
(Re-)Initialize the special internal dictionary
int fr_dict_global_ctx_dir_set(char const *dict_dir)
Allow the default dict dir to be changed after initialisation.
Definition dict_util.c:4616
static fr_slen_t err
Definition dict.h:849
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3355
void fr_dict_debug(FILE *fp, fr_dict_t const *dict)
Definition dict_print.c:276
fr_slen_t fr_dict_attr_by_oid_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_sbuff_term_t const *tt))
Resolve an attribute using an OID string.
Definition dict_util.c:2416
int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent)
(Re)-initialize a protocol dictionary
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2492
void fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx)
Set a new, active, global dictionary context.
Definition dict_util.c:4586
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4157
int fr_dict_const_free(fr_dict_t const **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4141
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4745
int fr_dict_global_ctx_free(fr_dict_gctx_t const *gctx)
Explicitly free all data associated with a global dictionary context.
Definition dict_util.c:4602
int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent)
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename)
Read supplementary attribute definitions into an existing dictionary.
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:297
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:298
static fr_slen_t in
Definition dict.h:849
Test enumeration values.
Definition dict_test.h:92
dl_loader_t * dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
Initialise structures needed by the dynamic linker.
Definition dl.c:885
dl_t * dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
Search for a dl's shared object in various locations.
Definition dl.c:470
A dynamic loader.
Definition dl.c:81
void * handle
Handle returned by dlopen.
Definition dl.h:62
Module handle.
Definition dl.h:58
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:486
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:908
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:501
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
ssize_t fr_dns_label_from_value_box(size_t *need, uint8_t *buf, size_t buf_len, uint8_t *where, bool compression, fr_value_box_t const *value, fr_dns_labels_t *lb)
Encode a single value box of type string, serializing its contents to a dns label.
Definition dns.c:639
ssize_t fr_dns_label_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *dst, uint8_t const *src, size_t len, uint8_t const *label, bool tainted, fr_dns_labels_t *lb)
Decode a fr_value_box_t from one DNS label.
Definition dns.c:1225
int fr_unlink(char const *filename)
Remove a regular file from the filesystem.
Definition file.c:367
ssize_t fr_touch(int *fd_out, char const *filename, mode_t mode, bool mkdir, mode_t dir_mode)
Create an empty file.
Definition file.c:323
char * fr_realpath(TALLOC_CTX *ctx, char const *path, ssize_t len)
Convenience wrapper around realpath.
Definition file.c:284
bool fr_hostname_lookups
hostname -> IP lookups?
Definition inet.c:53
bool fr_reverse_lookups
IP -> hostname lookups?
Definition inet.c:52
int unlang_global_init(void)
Definition base.c:158
talloc_free(reap)
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2523
Stores all information relating to an event list.
Definition event.c:377
int fr_debug_lvl
Definition log.c:40
FILE * fr_log_fp
Definition log.c:39
fr_log_t default_log
Definition log.c:288
@ L_DST_STDOUT
Log to stdout.
Definition log.h:78
fr_type_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
fr_slen_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt, fr_sbuff_escape_rules_t const *e_rules)
unsigned long int size_t
#define UINT8_MAX
fr_sbuff_parse_error_t
@ FR_SBUFF_PARSE_OK
No error.
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
static bool is_whitespace(char const *value)
Check whether the string is all whitespace.
Definition misc.h:67
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:287
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
Read valuepairs from the fp up to End-Of-File.
struct fr_pair_parse_s fr_pair_parse_t
TALLOC_CTX * ctx
Definition pair_legacy.h:43
#define is_truncated(_ret, _max)
Definition print.h:48
static const conf_parser_t config[]
Definition base.c:186
void * fr_proto_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
Implements the default iterator to encode pairs belonging to a specific dictionary that are not inter...
Definition proto.c:100
static fr_internal_encode_ctx_t encode_ctx
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:81
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define INFO(fmt,...)
Definition radict.c:54
static bool cleanup
Definition radsniff.c:60
fr_dict_attr_t const * request_attr_request
Definition request.c:43
int request_global_init(void)
Definition request.c:596
static char const * name
fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in)
See if the string contains a truth value.
Definition sbuff.c:1114
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
Wind position until we hit a character in the terminal set.
Definition sbuff.c:1880
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2116
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_diff(_a, _b)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define fr_sbuff_extend(_sbuff_or_marker)
#define FR_SBUFF_ERROR_RETURN(_sbuff_or_marker)
#define fr_sbuff_end(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_switch(_sbuff_or_marker, _eob)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
Set of terminal elements.
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:336
bool new_functions
new function syntax
Definition tmpl.h:326
static fr_slen_t vpt
Definition tmpl.h:1269
fr_slen_t tmpl_request_ref_list_afrom_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, FR_DLIST_HEAD(tmpl_request_list) _CONST **out, fr_sbuff_t *in)
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
Parse one a single list reference.
fr_event_list_t * runtime_el
The eventlist to use for runtime instantiation of xlats.
Definition tmpl.h:324
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
void fr_sha1_init(fr_sha1_ctx *context)
Definition sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
void modules_init(char const *lib_dir)
Perform global initialisation for modules.
Definition module.c:1904
fr_aka_sim_id_type_t type
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
fr_log_dst_t dst
Log destination.
Definition log.h:97
int fd
File descriptor to write messages to.
Definition log.h:112
bool print_level
sometimes we don't want log levels printed
Definition log.h:106
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:304
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:312
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:301
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_longest_prefix(_match_len, _table, _name, _name_len, _def)
Find the longest string match using a sorted or ordered table.
Definition table.h:732
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
An element in a lexicographically sorted array of name to ptr mappings.
Definition table.h:65
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:85
fr_tp_proto_decode_t func
Decoder for proto layer.
Definition test_point.h:68
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:93
fr_dcursor_iter_t next_encodable
Iterator to use to select attributes to encode.
Definition test_point.h:95
fr_tp_proto_encode_t func
Encoder for proto layer.
Definition test_point.h:76
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:75
fr_pair_decode_t func
Decoder for pairs.
Definition test_point.h:86
fr_pair_encode_t func
Encoder for pairs.
Definition test_point.h:94
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:67
Entry point for pair decoders.
Definition test_point.h:84
Entry point for pair encoders.
Definition test_point.h:92
Entry point for protocol decoders.
Definition test_point.h:66
Entry point for protocol encoders.
Definition test_point.h:74
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:169
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:34
enum fr_token fr_token_t
@ T_AND
Definition token.h:55
@ T_INVALID
Definition token.h:39
@ T_SUB
Definition token.h:52
@ T_XOR
Definition token.h:58
@ T_DIV
Definition token.h:54
@ T_MOD
Definition token.h:60
@ T_BARE_WORD
Definition token.h:120
@ T_ADD
Definition token.h:51
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
@ T_MUL
Definition token.h:53
@ T_OR
Definition token.h:56
close(uq->fd)
static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
#define RETURN_OK(_len)
int fuzzer_dir
File descriptor pointing to a a directory to write fuzzer output.
#define POISONED_BUFFER_START(_p)
static size_t commands_len
static int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
static size_t command_touch(command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Touch a file to indicate a test completed.
uint32_t start
Start of line range.
static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
static dl_loader_t * dl_loader
#define BUFF_POISON_START
static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
int main(int argc, char *argv[])
static size_t command_decode_dns_label(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
static size_t command_decode_proto(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
static void unload_proto_library(void)
static size_t command_xlat_normalise(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse an reprint and xlat expansion.
static size_t parse_typed_value(command_result_t *result, command_file_ctx_t *cc, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
uint32_t test_count
How many tests we've executed in this file.
static int decode_vendor(char *buffer, char **endptr)
static size_t command_proto_dictionary(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static void commands_print(void)
#define POISONED_BUFFER_END(_p)
static char proto_name_prev[128]
TALLOC_CTX * tmp_ctx
Temporary context to hold buffers in this.
static size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
Print hex string to buffer.
static size_t command_exit(command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Exit gracefully with the specified code.
static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
#define RETURN_OK_WITH_ERROR()
static size_t command_xlat_purify(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse, purify, and reprint an xlat expression expansion.
char const * filename
Current file we're operating on.
static void mismatch_print(command_file_ctx_t *cc, char const *command, char *expected, size_t expected_len, char *got, size_t got_len, bool print_diff)
static size_t command_tmpl(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse an reprint a tmpl expansion.
static size_t command_tmpl_rules(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
static size_t command_fuzzer_out(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Enable fuzzer output.
static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
static dl_t * dl
static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
static int dictionary_load_common(command_result_t *result, command_file_ctx_t *cc, char const *in, char const *default_subdir)
Common dictionary load function.
static size_t command_include(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Execute another test file.
static size_t command_proto_dictionary_root(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static ssize_t command_tmpl_rule_request_def(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
static ssize_t command_tmpl_rule_allow_unresolved(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
uint32_t lineno
Current line number.
ssize_t last_ret
Last return value.
#define ASAN_POISON_MEMORY_REGION(_start, _end)
static void features_print(CONF_SECTION *features)
static size_t command_returned(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
static xlat_arg_parser_t const xlat_test_args[]
static int decode_attr(char *buffer, char **endptr)
static fr_dict_t * dictionary_current(command_file_ctx_t *cc)
static size_t command_encode_pair(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
fr_dlist_t entry
Entry in the dlist.
uint8_t * buffer_end
Where the non-poisoned region of the buffer ends.
static fr_table_ptr_sorted_t commands[]
#define RETURN_NOOP(_len)
TALLOC_CTX * tmp_ctx
Talloc context for test points.
static size_t command_radmin_tab(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static size_t command_calc_nary(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Perform calculations on multi-valued ops.
static size_t command_dictionary_dump(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
Print the currently loaded dictionary.
fr_dict_gctx_t const * test_gctx
Dictionary context for test dictionaries.
static size_t command_proto(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Dynamically load a protocol library.
static fr_cmd_t * command_head
static size_t command_write(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
char * path
Current path we're operating in.
static int poisoned_buffer_allocate(TALLOC_CTX *ctx, uint8_t **buff, size_t size)
Allocate a special buffer with poisoned memory regions at the start and end.
#define DEFAULT_BUFFER_SIZE
Default buffer size for a command_file_ctx_t.
static FILE * write_fp
static size_t strerror_concat(char *out, size_t outlen)
Concatenate error stack.
static size_t command_match_regex(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Compare the data buffer against an expected expression.
static size_t command_encode_raw(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Encode a RADIUS attribute writing the result to the data buffer as space separated hexits.
#define RETURN_PARSE_ERROR(_offset)
static ssize_t command_tmpl_rule_allow_unknown(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
command_config_t const * config
static int _command_ctx_free(command_file_ctx_t *cc)
#define COMMAND_OUTPUT_MAX
static size_t command_allow_unresolved(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Determine if unresolved attributes are allowed.
static size_t command_read_file(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse a list of pairs.
static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config, const char *root_dir, char const *filename, fr_dlist_head_t *lines)
static size_t command_cast(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Perform casting.
static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen)
#define BUFF_POISON_END
static int command_walk(UNUSED void *ctx, fr_cmd_walk_info_t *info)
static size_t command_condition_normalise(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Parse and reprint a condition.
static size_t command_count(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
static fr_table_num_sorted_t command_rcode_table[]
static size_t command_max_buffer_size(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Artificially limit the maximum packet size.
static const fr_token_t token2op[UINT8_MAX+1]
tmpl_rules_t tmpl_rules
To pass to parsing functions.
size_t(* command_func_t)(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Command to execute.
#define RETURN_MISMATCH(_len)
uint32_t end
End of line range.
static char const hextab[]
static command_file_ctx_t * command_ctx_alloc(TALLOC_CTX *ctx, command_config_t const *config, char const *path, char const *filename)
static size_t command_encode_proto(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
static size_t command_load_dictionary(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
static void command_print(void)
#define RETURN_EXIT(_ret)
static fr_event_list_t * el
static size_t command_decode_pair(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
static size_t command_xlat_expr(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse and reprint an xlat expression expansion.
char const * fuzzer_dir
Where to write fuzzer files.
#define EXIT_WITH_FAILURE
static size_t command_pair(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Parse an print an attribute pair or pair list.
CONF_SECTION * features
Enabled features.
static size_t command_calc(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Perform calculations.
static size_t command_migrate(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Set or clear migration flags.
static size_t command_dictionary_attribute_parse(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse a dictionary attribute, writing "ok" to the data buffer is everything was ok.
static size_t command_xlat_argv(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse an reprint and xlat argv expansion.
#define CLEAR_TEST_POINT(_cc)
static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
#define RETURN_COMMAND_ERROR()
static size_t command_eof(UNUSED command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
Command eof.
static size_t command_match(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Compare the data buffer to an expected value.
#define ASAN_UNPOISON_MEMORY_REGION(_start, _end)
#define RETURN_SKIP_FILE()
static ssize_t command_tmpl_rule_attr_parent(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
command_rcode_t rcode
fr_dict_gctx_t const * dict_gctx
Dictionary gctx to "reset" to.
static ssize_t command_tmpl_rule_list_def(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
static ssize_t command_tmpl_rule_allow_foreign(UNUSED TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
static ssize_t encode_vsa(char *buffer, uint8_t *output, size_t outlen)
size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
static ssize_t load_proto_library(char const *proto_name)
@ RESULT_MISMATCH
Fatal error - Result didn't match what we expected.
@ RESULT_COMMAND_ERROR
Fatal error - Command operation error.
@ RESULT_NOOP
Not an error - Did nothing...
@ RESULT_OK
Not an error - Result as expected.
@ RESULT_EXIT
Stop processing files and exit.
@ RESULT_SKIP_FILE
Not an error - Skip the rest of this file, or until we reach an "eof" command.
@ RESULT_PARSE_ERROR
Fatal error - Command syntax error.
static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *in)
static size_t command_cd(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Change the working directory.
static size_t command_comment(UNUSED command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
Placeholder function for comments.
uint8_t * buffer_start
Where the non-poisoned region of the buffer starts.
static ssize_t encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen)
static size_t command_radmin_add(command_result_t *result, command_file_ctx_t *cc, char *data, size_t UNUSED data_used, char *in, UNUSED size_t inlen)
static size_t command_need_feature(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Skip the test file if we're missing a particular feature.
static ssize_t encode_long_extended(char *buffer, uint8_t *output, size_t outlen)
ssize_t(* command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value)
Callback for a tmpl rule parser.
static size_t command_clear(command_result_t *result, UNUSED command_file_ctx_t *cc, char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
fr_dict_t * test_internal_dict
Internal dictionary of test_gctx.
static ssize_t encode_evs(char *buffer, uint8_t *output, size_t outlen)
static size_t command_value_box_normalise(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static xlat_action_t xlat_test(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, UNUSED fr_value_box_list_t *in)
fr_dict_t * dict
Dictionary to "reset" to.
static char const * write_filename
static xlat_arg_parser_t const xlat_test_no_args[]
uint8_t * buffer
Temporary resizable buffer we use for holding non-string data.
static size_t command_no(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Negate the result of a match command or any command which returns "OK".
static size_t command_rcode_table_len
static size_t command_encode_dns_label(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Configuration parameters passed to command functions.
int unlang_thread_instantiate(TALLOC_CTX *ctx)
Create thread-specific data structures for unlang.
Definition compile.c:2293
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3163
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules)
Reconstitute an xlat expression from its constituent nodes.
void xlat_debug_head(xlat_exp_head_t const *head)
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Tokenize an xlat expansion.
static fr_slen_t head
Definition xlat.h:420
int xlat_instantiate(void)
Call instantiation functions for all registered, "permanent" xlats.
Definition xlat_inst.c:511
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:146
int xlat_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Create thread specific instance tree and create thread instances.
Definition xlat_inst.c:442
int xlat_purify(xlat_exp_head_t *head, unlang_interpret_t *intp)
Purify an xlat.
int xlat_flatten_to_argv(TALLOC_CTX *ctx, xlat_exp_head_t ***argv, xlat_exp_head_t *head)
Turn am xlat list into an argv[] array, and nuke the input list.
Definition xlat_eval.c:1837
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_arg_parser_t const *xlat_args, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool spaces))
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3135
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:8
#define fr_pair_dcursor_iter_init(_cursor, _list, _iter, _uctx)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:569
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define PAIR_LIST_VERIFY(_x)
Definition pair.h:196
ssize_t fr_pair_list_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pair_list_t const *list)
Print a pair list.
Definition pair_print.c:242
static fr_slen_t parent
Definition pair.h:841
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
char const * fr_strerror_peek(void)
Get the last library error.
Definition strerror.c:626
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:680
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_printf_push_head(_fmt,...)
Add a message to an existing stack of messages at the head.
Definition strerror.h:104
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
#define fr_type_is_null(_x)
Definition types.h:348
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
fr_sbuff_escape_rules_t fr_value_escape_double
Definition value.c:356
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
Definition value.c:5798
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3744
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:743
fr_sbuff_parse_rules_t const value_parse_rules_bareword_unquoted
Default formatting rules.
Definition value.c:486
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3990
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:5761
ssize_t fr_value_box_print_quoted(fr_sbuff_t *out, fr_value_box_t const *data, fr_token_t quote)
Print one boxed value to a string with quotes (where needed)
Definition value.c:6022
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition value.c:553
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4145
fr_sbuff_unescape_rules_t fr_value_unescape_double
Definition value.c:272
ssize_t fr_value_box_from_substr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *rules)
Convert string value to a fr_value_box_t type.
Definition value.c:5124
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1074
static fr_slen_t data
Definition value.h:1291
#define fr_box_strvalue_len(_val, _len)
Definition value.h:308
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1023
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:609
static size_t char ** out
Definition value.h:1023
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:173
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:216