The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 98af58cc7c34fd3f78aa431ea863930a0f453706 $
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: 98af58cc7c34fd3f78aa431ea863930a0f453706 $")
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/command.h>
33#include <freeradius-devel/server/dependency.h>
34#include <freeradius-devel/server/dl_module.h>
35#include <freeradius-devel/server/log.h>
36#include <freeradius-devel/server/map.h>
37#ifdef WITH_TLS
38# include <freeradius-devel/tls/base.h>
39#endif
40#include <freeradius-devel/unlang/base.h>
41#include <freeradius-devel/unlang/xlat_func.h>
42#include <freeradius-devel/util/lsan.h>
43#include <freeradius-devel/util/atexit.h>
44#include <freeradius-devel/util/base64.h>
45#include <freeradius-devel/util/calc.h>
46#include <freeradius-devel/util/conf.h>
47#include <freeradius-devel/util/dns.h>
48#include <freeradius-devel/util/file.h>
49#include <freeradius-devel/util/skip.h>
50#include <freeradius-devel/util/pair_legacy.h>
51#include <freeradius-devel/util/sha1.h>
52#include <freeradius-devel/util/syserror.h>
53
54#include <freeradius-devel/util/dict_priv.h>
55
56#ifdef HAVE_GETOPT_H
57# include <getopt.h>
58#endif
59
60#include <assert.h>
61#include <fcntl.h>
62#include <libgen.h>
63#include <sys/file.h>
64#include <sys/stat.h>
65#include <sys/wait.h>
66
67#define EXIT_WITH_FAILURE \
68do { \
69 ret = EXIT_FAILURE; \
70 goto cleanup; \
71} while (0)
72
73#define COMMAND_OUTPUT_MAX 8192
74
75#define RETURN_OK(_len) \
76 do { \
77 result->rcode = RESULT_OK; \
78 result->file = __FILE__; \
79 result->line = __LINE__; \
80 return (_len); \
81 } while (0)
82
83#define RETURN_OK_WITH_ERROR() \
84 do { \
85 result->rcode = RESULT_OK; \
86 result->file = __FILE__; \
87 result->line = __LINE__; \
88 result->error_to_data = true; \
89 return 0; \
90 } while (0)
91
92#define RETURN_NOOP(_len) \
93 do { \
94 result->rcode = RESULT_NOOP; \
95 result->file = __FILE__; \
96 result->line = __LINE__; \
97 return (_len); \
98 } while (0)
99
100#define RETURN_SKIP_FILE() \
101 do { \
102 result->rcode = RESULT_SKIP_FILE; \
103 result->file = __FILE__; \
104 result->line = __LINE__; \
105 return 0; \
106 } while (0)
107
108#define RETURN_PARSE_ERROR(_offset) \
109 do { \
110 result->rcode = RESULT_PARSE_ERROR; \
111 result->offset = _offset; \
112 result->file = __FILE__; \
113 result->line = __LINE__; \
114 return 0; \
115 } while (0)
116
117#define RETURN_COMMAND_ERROR() \
118 do { \
119 result->rcode = RESULT_COMMAND_ERROR; \
120 result->file = __FILE__; \
121 result->line = __LINE__; \
122 return 0; \
123 } while (0)
124
125#define RETURN_MISMATCH(_len) \
126 do { \
127 result->rcode = RESULT_MISMATCH; \
128 result->file = __FILE__; \
129 result->line = __LINE__; \
130 return (_len); \
131 } while (0)
132
133#define RETURN_EXIT(_ret) \
134 do { \
135 result->rcode = RESULT_EXIT; \
136 result->ret = _ret; \
137 result->file = __FILE__; \
138 result->line = __LINE__; \
139 return 0; \
140 } while (0)
141
142#define SBUFF_RETURN_OK_WITH_ERROR(_function, ...) \
143 do { \
144 slen = _function(__VA_ARGS__); \
145 if (slen <= 0) RETURN_OK_WITH_ERROR(); \
146 } while (0)
147
148/** Default buffer size for a command_file_ctx_t
149 *
150 */
151#define DEFAULT_BUFFER_SIZE 1024
152
153typedef enum {
154 RESULT_OK = 0, //!< Not an error - Result as expected.
155 RESULT_NOOP, //!< Not an error - Did nothing...
156 RESULT_SKIP_FILE, //!< Not an error - Skip the rest of this file, or until we
157 ///< reach an "eof" command.
158 RESULT_PARSE_ERROR, //!< Fatal error - Command syntax error.
159 RESULT_COMMAND_ERROR, //!< Fatal error - Command operation error.
160 RESULT_MISMATCH, //!< Fatal error - Result didn't match what we expected.
161 RESULT_EXIT, //!< Stop processing files and exit.
163
165 { L("command-error"), RESULT_COMMAND_ERROR },
166 { L("exit"), RESULT_EXIT },
167 { L("ok"), RESULT_OK },
168 { L("parse-error"), RESULT_PARSE_ERROR },
169 { L("result-mismatch"), RESULT_MISMATCH },
170 { L("skip-file"), RESULT_SKIP_FILE },
171};
173
174typedef struct {
175 TALLOC_CTX *tmp_ctx; //!< Temporary context to hold buffers
176 ///< in this
177 union {
178 size_t offset; //!< Where we failed parsing the command.
179 int ret; //!< What code we should exit with.
180 };
181 char const *file;
182 int line;
186
187/** Configuration parameters passed to command functions
188 *
189 */
190typedef struct {
191 fr_dict_t *dict; //!< Dictionary to "reset" to.
192 fr_dict_gctx_t const *dict_gctx; //!< Dictionary gctx to "reset" to.
193 char const *confdir;
194 char const *dict_dir;
195 char const *fuzzer_base_dir; //!< Base directory of where to write fuzzer files, from '-F'
196 CONF_SECTION *features; //!< Enabled features.
198
199typedef struct {
200 TALLOC_CTX *tmp_ctx; //!< Talloc context for test points.
201
202 char *path; //!< Current path we're operating in.
203 char const *filename; //!< Current file we're operating on.
204 uint32_t lineno; //!< Current line number.
205
206 uint32_t test_count; //!< How many tests we've executed in this file.
207 ssize_t last_ret; //!< Last return value.
208
209 uint8_t *buffer; //!< Temporary resizable buffer we use for
210 ///< holding non-string data.
211 uint8_t *buffer_start; //!< Where the non-poisoned region of the buffer starts.
212 uint8_t *buffer_end; //!< Where the non-poisoned region of the buffer ends.
213
214 tmpl_rules_t tmpl_rules; //!< To pass to parsing functions.
215 fr_dict_t *test_internal_dict; //!< Internal dictionary of test_gctx.
216 fr_dict_gctx_t const *test_gctx; //!< Dictionary context for test dictionaries.
217
218 char *fuzzer_proto_dir; //!< Subdirectory of where to write fuzzer files,
219 //!< from 'fuzzer-out dir'.
220 int fuzzer_fd; //!< File descriptor pointing to a a directory to
221 ///< write fuzzer output.
224
225
226typedef struct {
227 fr_dlist_t entry; //!< Entry in the dlist.
228 uint32_t start; //!< Start of line range.
229 uint32_t end; //!< End of line range.
231
232/** Command to execute
233 *
234 * @param[out] result Of executing the command.
235 * @param[in] cc Information about the file being processed.
236 * @param[in,out] data Output of this command, or the previous command.
237 * @param[in] data_used Length of data in the data buffer.
238 * @param[in] in Command text to process.
239 * @param[in] inlen Length of the remainder of the command to process.
240 */
242 size_t data_used, char *in, size_t inlen);
243
244typedef struct {
246 char const *usage;
247 char const *description;
249
251 { .required = true, .single = true, .type = FR_TYPE_STRING },
253};
254
258
260 UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request,
261 UNUSED fr_value_box_list_t *in)
262{
263 return XLAT_ACTION_DONE;
264}
265
266static char proto_name_prev[128] = {};
267static dl_t *dl = NULL;
268static dl_loader_t *dl_loader = NULL;
269
270static fr_event_list_t *el = NULL;
271
272static bool allow_purify = false;
273
274static char const *write_filename = NULL;
275static FILE *write_fp = NULL;
276
277static char const *receipt_file = NULL;
278static char const *receipt_dir = NULL;
279static char const *fail_file = "";
280
281size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen);
282static int process_file(bool *exit_now, TALLOC_CTX *ctx,
283 command_config_t const *config, const char *root_dir, char const *filename, fr_dlist_head_t *lines);
284
285#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
286# define BUFF_POISON_START 1024
287# define BUFF_POISON_END 1024
288
289/** Unpoison the start and end regions of the buffer
290 *
291 */
292static int _free_buffer(uint8_t *buff)
293{
294 size_t size = talloc_array_length(buff) - (BUFF_POISON_START + BUFF_POISON_END);
295
298
299 return 0;
300}
301#else
302# define BUFF_POISON_START 0
303# define BUFF_POISON_END 0
304#endif
305
306/** Allocate a special buffer with poisoned memory regions at the start and end
307 *
308 */
309static int poisoned_buffer_allocate(TALLOC_CTX *ctx, uint8_t **buff, size_t size)
310{
311 uint8_t *our_buff = *buff;
312
313 if (our_buff) {
314 /*
315 * If it's already the correct length
316 * don't bother re-allocing the buffer,
317 * just memset it to zero.
318 */
319 if ((size + BUFF_POISON_START + BUFF_POISON_END) == talloc_array_length(our_buff)) {
320 memset(our_buff + BUFF_POISON_START, 0, size);
321 return 0;
322 }
323
324 talloc_free(our_buff); /* Destructor de-poisons */
325 *buff = NULL;
326 }
327
328 our_buff = talloc_array(ctx, uint8_t, size + BUFF_POISON_START + BUFF_POISON_END);
329 if (!our_buff) return -1;
330
331#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
332 talloc_set_destructor(our_buff, _free_buffer);
333
334 /*
335 * Poison regions before and after the buffer
336 */
339#endif
340
341 *buff = our_buff;
342
343 return 0;
344}
345#define POISONED_BUFFER_START(_p) ((_p) + BUFF_POISON_START)
346#define POISONED_BUFFER_END(_p) ((_p) + BUFF_POISON_START + (talloc_array_length(_p) - (BUFF_POISON_START + BUFF_POISON_END)))
347
348static void mismatch_print(command_file_ctx_t *cc, char const *command,
349 char *expected, size_t expected_len, char *got, size_t got_len,
350 bool print_diff)
351{
352 char *g, *e;
353
354 ERROR("%s failed %s/%s:%d", command, cc->path, cc->filename, cc->lineno);
355
356 if (!print_diff) {
357 ERROR(" got : %.*s", (int) got_len, got);
358 ERROR(" expected : %.*s", (int) expected_len, expected);
359 } else {
360 g = got;
361 e = expected;
362
363 while (*g && *e && (*g == *e)) {
364 g++;
365 e++;
366 }
367
368 if (expected_len < 100) {
369 char const *spaces = " ";
370
371 ERROR(" EXPECTED : %.*s", (int) expected_len, expected);
372 ERROR(" GOT : %.*s", (int) got_len, got);
373 ERROR(" %.*s^ differs here (%zu)", (int) (e - expected), spaces, e - expected);
374 } else if (fr_debug_lvl > 1) {
375 ERROR(" EXPECTED : %.*s", (int) expected_len, expected);
376 ERROR(" GOT : %.*s", (int) got_len, got);
377 ERROR("Differs at : %zu", e - expected);
378
379 } else {
380 size_t glen, elen;
381
382 elen = strlen(e);
383 if (elen > 70) elen = 70;
384 glen = strlen(g);
385 if (glen > 70) glen = 70;
386
387 ERROR("(%zu) ... %.*s ... ", e - expected, (int) elen, e);
388 ERROR("(%zu) ... %.*s ... ", e - expected, (int) glen, g);
389 }
390 }
391}
392
393/** Print hex string to buffer
394 *
395 */
396static inline CC_HINT(nonnull) size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
397{
398 char *p = out;
399 char *end = p + outlen;
400 size_t i;
401
402 if (inlen == 0) {
403 *p = '\0';
404 return 0;
405 }
406
407 for (i = 0; i < inlen; i++) {
408 size_t len;
409
410 len = snprintf(p, end - p, "%02x ", in[i]);
411 if (is_truncated(len, end - p)) return 0;
412
413 p += len;
414 }
415
416 *(--p) = '\0';
417
418 return p - out;
419}
420
421/** Concatenate error stack
422 */
423static inline size_t strerror_concat(char *out, size_t outlen)
424{
425 char *end = out + outlen;
426 char *p = out;
427 char const *err;
428
429 strcpy(out, "ERROR: ");
430 p += 7;
431
432 while ((p < end) && (err = fr_strerror_pop())) {
433 if (*fr_strerror_peek()) {
434 p += snprintf(p, end - p, "%s: ", err);
435 } else {
436 p += strlcpy(p, err, end - p);
437 }
438 }
439
440 return p - out;
441}
442
443static inline CC_HINT(nonnull) int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
444{
445 fr_sha1_ctx ctx;
447 char digest_str[(SHA1_DIGEST_LENGTH * 2) + 1];
448 int file_fd;
449
450 fr_assert(data_len <= COMMAND_OUTPUT_MAX);
451
452 fr_sha1_init(&ctx);
453 fr_sha1_update(&ctx, (uint8_t const *)text, strlen(text));
454 fr_sha1_final(digest, &ctx);
455
456 /*
457 * We need to use the url alphabet as the standard
458 * one contains forwarded slashes which openat
459 * doesn't like.
460 */
461 fr_base64_encode_nstd(&FR_SBUFF_OUT(digest_str, sizeof(digest_str)), &FR_DBUFF_TMP(digest, sizeof(digest)),
463
464 file_fd = openat(fd_dir, digest_str, O_RDWR | O_CREAT | O_TRUNC,
465 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
466 if (file_fd < 0) {
467 fr_strerror_printf("Failed opening or creating corpus seed file \"%s\": %s",
468 digest_str, fr_syserror(errno));
469 return -1;
470 }
471
472 if (flock(file_fd, LOCK_EX) < 0) {
473 close(file_fd);
474 fr_strerror_printf("Failed locking corpus seed file \"%s\": %s",
475 digest_str, fr_syserror(errno));
476 return -1;
477 }
478
479 while (data_len) {
480 ssize_t ret;
481
482 ret = write(file_fd, data, data_len);
483 if (ret < 0) {
484 fr_strerror_printf("Failed writing to corpus seed file \"%s\": %s",
485 digest_str, fr_syserror(errno));
486 (void)flock(file_fd, LOCK_UN);
487 unlinkat(fd_dir, digest_str, 0);
488 close(file_fd);
489 return -1;
490 }
491 data_len -= ret;
492 data += ret;
493 }
494 (void)flock(file_fd, LOCK_UN);
495 close(file_fd);
496
497 return 0;
498}
499
500/*
501 * End of hacks for xlat
502 *
503 **********************************************************************/
504
505static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen);
506
507static char const hextab[] = "0123456789abcdef";
508
509static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
510{
511 ssize_t slen = 0;
512 char *p;
513
514 p = buffer + 1;
515
516 while (*p && (outlen > 0)) {
517 if (*p == '"') {
518 return slen;
519 }
520
521 if (*p != '\\') {
522 *(output++) = *(p++);
523 outlen--;
524 slen++;
525 continue;
526 }
527
528 switch (p[1]) {
529 default:
530 *(output++) = p[1];
531 break;
532
533 case 'n':
534 *(output++) = '\n';
535 break;
536
537 case 'r':
538 *(output++) = '\r';
539 break;
540
541 case 't':
542 *(output++) = '\t';
543 break;
544 }
545
546 p += 2;
547 outlen--;
548 slen++;
549 }
550
551 ERROR("String is not terminated");
552 return 0;
553}
554
555static ssize_t encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen)
556{
557 int depth = 0;
558 ssize_t slen;
559 char *p;
560
561 for (p = buffer; *p != '\0'; p++) {
562 if (*p == '{') depth++;
563 if (*p == '}') {
564 depth--;
565 if (depth == 0) break;
566 }
567 }
568
569 if (*p != '}') {
570 ERROR("No trailing '}' in string starting with \"%s\"", buffer);
571 return 0;
572 }
573
574 *endptr = p + 1;
575 *p = '\0';
576
577 p = buffer + 1;
579
580 slen = encode_tlv(p, output, outlen);
581 if (slen <= 0) return 0;
582
583 return slen;
584}
585
586static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
587{
588 char *p = in;
589 char *end = in + inlen;
590 uint8_t *out_p = out, *out_end = out_p + outlen;
591
592 while (p < end) {
593 char const *c1, *c2;
594
595 if (out_p >= out_end) {
596 fr_strerror_const("Would overflow output buffer");
597 return -(p - in);
598 }
599
601
602 if (!*p) break;
603
604 c1 = memchr(hextab, tolower((uint8_t) *p), sizeof(hextab) - 1);
605 if (!c1) {
606 bad_input:
607 fr_strerror_printf("Invalid hex data starting at \"%s\"", p);
608 return -(p - in);
609 }
610 p++;
611
612 c2 = memchr(hextab, tolower((uint8_t) *p), sizeof(hextab) - 1);
613 if (!c2) goto bad_input;
614 p++;
615
616 *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
617 }
618
619 return out_p - out;
620}
621
622
623static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
624{
625 ssize_t slen;
626
627 if (!isspace((uint8_t) *p)) {
628 ERROR("Invalid character following attribute definition");
629 return 0;
630 }
631
633
634 if (*p == '{') {
635 size_t sublen;
636 char *q;
637
638 slen = 0;
639
640 do {
642 if (!*p) {
643 if (slen == 0) {
644 ERROR("No data");
645 return 0;
646 }
647
648 break;
649 }
650
651 sublen = encode_data_tlv(p, &q, output, outlen);
652 if (sublen <= 0) return 0;
653
654 slen += sublen;
655 output += sublen;
656 outlen -= sublen;
657 p = q;
658 } while (*q);
659
660 return slen;
661 }
662
663 if (*p == '"') {
664 slen = encode_data_string(p, output, outlen);
665 return slen;
666 }
667
668 slen = hex_to_bin(output, outlen, p, strlen(p));
669 if (slen <= 0) {
670 fr_strerror_const_push("Empty hex string");
671 return slen;
672 }
673
674 return slen;
675}
676
677static int decode_attr(char *buffer, char **endptr)
678{
679 long attr;
680
681 attr = strtol(buffer, endptr, 10);
682 if (*endptr == buffer) {
683 ERROR("No valid number found in string starting with \"%s\"", buffer);
684 return 0;
685 }
686
687 if (!**endptr) {
688 ERROR("Nothing follows attribute number");
689 return 0;
690 }
691
692 if ((attr <= 0) || (attr > 256)) {
693 ERROR("Attribute number is out of valid range");
694 return 0;
695 }
696
697 return (int) attr;
698}
699
700static int decode_vendor(char *buffer, char **endptr)
701{
702 long vendor;
703
704 if (*buffer != '.') {
705 ERROR("Invalid separator before vendor id");
706 return 0;
707 }
708
709 vendor = strtol(buffer + 1, endptr, 10);
710 if (*endptr == (buffer + 1)) {
711 ERROR("No valid vendor number found");
712 return 0;
713 }
714
715 if (!**endptr) {
716 ERROR("Nothing follows vendor number");
717 return 0;
718 }
719
720 if ((vendor <= 0) || (vendor > (1 << 24))) {
721 ERROR("Vendor number is out of valid range");
722 return 0;
723 }
724
725 if (**endptr != '.') {
726 ERROR("Invalid data following vendor number");
727 return 0;
728 }
729 (*endptr)++;
730
731 return (int) vendor;
732}
733
734static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen)
735{
736 int attr;
737 ssize_t slen;
738 char *p;
739
740 attr = decode_attr(buffer, &p);
741 if (attr == 0) return 0;
742
743 output[0] = attr;
744 output[1] = 2;
745
746 if (*p == '.') {
747 p++;
748 slen = encode_tlv(p, output + 2, outlen - 2);
749
750 } else {
751 slen = encode_data(p, output + 2, outlen - 2);
752 }
753
754 if (slen <= 0) return slen;
755 if (slen > (255 - 2)) {
756 ERROR("TLV data is too long");
757 return 0;
758 }
759
760 output[1] += slen;
761
762 return slen + 2;
763}
764
765static ssize_t encode_vsa(char *buffer, uint8_t *output, size_t outlen)
766{
767 int vendor;
768 ssize_t slen;
769 char *p;
770
771 vendor = decode_vendor(buffer, &p);
772 if (vendor == 0) return 0;
773
774 output[0] = 0;
775 output[1] = (vendor >> 16) & 0xff;
776 output[2] = (vendor >> 8) & 0xff;
777 output[3] = vendor & 0xff;
778
779 slen = encode_tlv(p, output + 4, outlen - 4);
780 if (slen <= 0) return slen;
781 if (slen > (255 - 6)) {
782 ERROR("VSA data is too long");
783 return 0;
784 }
785
786 return slen + 4;
787}
788
789static ssize_t encode_evs(char *buffer, uint8_t *output, size_t outlen)
790{
791 int vendor;
792 int attr;
793 ssize_t slen;
794 char *p;
795
796 vendor = decode_vendor(buffer, &p);
797 if (vendor == 0) return 0;
798
799 attr = decode_attr(p, &p);
800 if (attr == 0) return 0;
801
802 output[0] = 0;
803 output[1] = (vendor >> 16) & 0xff;
804 output[2] = (vendor >> 8) & 0xff;
805 output[3] = vendor & 0xff;
806 output[4] = attr;
807
808 slen = encode_data(p, output + 5, outlen - 5);
809 if (slen <= 0) return slen;
810
811 return slen + 5;
812}
813
814static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
815{
816 int attr;
817 ssize_t slen;
818 char *p;
819
820 attr = decode_attr(buffer, &p);
821 if (attr == 0) return 0;
822
823 output[0] = attr;
824
825 if (attr == 26) {
826 slen = encode_evs(p, output + 1, outlen - 1);
827 } else {
828 slen = encode_data(p, output + 1, outlen - 1);
829 }
830 if (slen <= 0) return slen;
831 if (slen > (255 - 3)) {
832 ERROR("Extended Attr data is too long");
833 return 0;
834 }
835
836 return slen + 1;
837}
838
839static ssize_t encode_long_extended(char *buffer, uint8_t *output, size_t outlen)
840{
841 int attr;
842 ssize_t slen, total;
843 char *p;
844
845 attr = decode_attr(buffer, &p);
846 if (attr == 0) return 0;
847
848 /* output[0] is the extended attribute */
849 output[1] = 4;
850 output[2] = attr;
851 output[3] = 0;
852
853 if (attr == 26) {
854 slen = encode_evs(p, output + 4, outlen - 4);
855 if (slen <= 0) return slen;
856
857 output[1] += 5;
858 slen -= 5;
859 } else {
860 slen = encode_data(p, output + 4, outlen - 4);
861 }
862 if (slen <= 0) return slen;
863
864 total = 0;
865 while (1) {
866 int sublen = 255 - output[1];
867
868 if (slen <= sublen) {
869 output[1] += slen;
870 total += output[1];
871 break;
872 }
873
874 slen -= sublen;
875
876 memmove(output + 255 + 4, output + 255, slen);
877 memcpy(output + 255, output, 4);
878
879 output[1] = 255;
880 output[3] |= 0x80;
881
882 output += 255;
883 output[1] = 4;
884 total += 255;
885 }
886
887 return total;
888}
889
890static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
891{
892 int attr;
893 ssize_t slen, sublen;
894 char *p;
895
896 attr = decode_attr(buffer, &p);
897 if (attr == 0) return 0;
898
899 slen = 2;
900 output[0] = attr;
901 output[1] = 2;
902
903 if (attr == 26) {
904 sublen = encode_vsa(p, output + 2, outlen - 2);
905
906 } else if ((attr < 241) || (attr > 246)) {
907 sublen = encode_data(p, output + 2, outlen - 2);
908
909 } else {
910 if (*p != '.') {
911 ERROR("Invalid data following attribute number");
912 return 0;
913 }
914
915 if (attr < 245) {
916 sublen = encode_extended(p + 1, output + 2, outlen - 2);
917 } else {
918 /*
919 * Not like the others!
920 */
921 return encode_long_extended(p + 1, output, outlen);
922 }
923 }
924 if (sublen <= 0) return sublen;
925 if (sublen > (255 -2)) {
926 ERROR("RFC Data is too long");
927 return 0;
928 }
929
930 output[1] += sublen;
931 return slen + sublen;
932}
933
934
935static void unload_proto_library(void)
936{
937 proto_name_prev[0] = '\0';
938 TALLOC_FREE(dl);
939}
940
941static ssize_t load_proto_library(char const *proto_name)
942{
943 char dl_name[128];
944
945 if (strcmp(proto_name_prev, proto_name) != 0) {
946 /*
947 * Ensure the old proto library is unloaded
948 */
950
951 snprintf(dl_name, sizeof(dl_name), "libfreeradius-%s", proto_name);
952 if (dl) TALLOC_FREE(dl);
953
954 dl = dl_by_name(dl_loader, dl_name, NULL, false);
955 if (!dl) {
956 fr_perror("Failed to link to library \"%s\"", dl_name);
958 return 0;
959 }
960
961 strlcpy(proto_name_prev, proto_name, sizeof(proto_name_prev));
962 }
963
964 fr_assert(dl != NULL);
965 return strlen(proto_name);
966}
967
968static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
969{
970 char buffer[256];
971 char const *p, *q;
972 void *dl_symbol;
973
974 if (!dl) {
975 fr_strerror_printf("No protocol library loaded. Specify library with \"load <proto name>\"");
976 return 0;
977 }
978
979 p = command;
980
981 /*
982 * Use the dflt_symbol name as the test point
983 */
984 if ((*p == '.') && (q = strchr(p, ' ')) && (q != (p + 1)) && ((size_t)(q - p) < sizeof(buffer))) {
985 p++;
986 strlcpy(buffer, p, (q - p) + 1);
987 p = q + 1;
988 } else {
989 snprintf(buffer, sizeof(buffer), "%s_%s", proto_name_prev, dflt_symbol);
990 }
991
992 dl_symbol = dlsym(dl->handle, buffer);
993 if (!dl_symbol) {
994 fr_strerror_printf("Test point (symbol \"%s\") not exported by library", buffer);
996 return 0;
997 }
998 *symbol = dl_symbol;
999
1000 return p - command;
1001}
1002
1004{
1005 if (cc->tmpl_rules.attr.dict_def) {
1006 return UNCONST(fr_dict_t *, cc->tmpl_rules.attr.dict_def);
1007 }
1008
1009 return cc->config->dict;
1010}
1011
1012/** Common dictionary load function
1013 *
1014 * Callers call fr_dict_global_ctx_set to set the context
1015 * the dictionaries will be loaded into.
1016 */
1017static int dictionary_load_common(command_result_t *result, command_file_ctx_t *cc, char const *in, char const *default_subdir)
1018{
1019 char const *dir;
1020 char const *q;
1021 char const *name;
1022 char *tmp = NULL;
1023 int ret;
1024 fr_dict_t *dict;
1025
1026 if (in[0] == '\0') {
1027 fr_strerror_const("Missing dictionary name");
1029 }
1030
1031 /*
1032 * Decrease ref count if we're loading in a new dictionary
1033 */
1034 if (cc->tmpl_rules.attr.dict_def) {
1036 }
1037
1038 q = strchr(in, ' ');
1039 if (q) {
1040 name = tmp = talloc_bstrndup(NULL, in, q - in);
1041 q++;
1042 dir = q;
1043 } else {
1044 name = in;
1045 dir = default_subdir;
1046 }
1047
1048 ret = fr_dict_protocol_afrom_file(&dict, name, dir, __FILE__);
1049 talloc_free(tmp);
1050 if (ret < 0) RETURN_COMMAND_ERROR();
1051
1053 cc->tmpl_rules.attr.namespace = fr_dict_root(dict);
1054
1055 /*
1056 * Dump the dictionary if we're in super debug mode
1057 */
1059
1060
1061 RETURN_OK(0);
1062}
1063
1064static 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)
1065{
1067 size_t match_len;
1068 ssize_t slen;
1069 char const *p;
1070 fr_sbuff_t sbuff;
1071 fr_dict_attr_t const *enumv = NULL;
1072
1073 /*
1074 * Parse data types
1075 */
1077 if (fr_type_is_null(type)) {
1079 }
1080 fr_assert(match_len <= inlen);
1081
1082 p = in + match_len;
1084 *out = p;
1085
1086 if (type == FR_TYPE_ATTR) {
1087 enumv = cc->tmpl_rules.attr.dict_def ?
1090 }
1091
1092 /*
1093 * As a hack, allow most things to be inside
1094 * double-quoted strings. This is really only for dates,
1095 * which are space-delimited.
1096 */
1097 if (*p == '"'){
1098 p++;
1099 sbuff = FR_SBUFF_IN(p, strlen(p));
1100 slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, enumv,
1101 &sbuff,
1103 if (slen < 0) {
1105 }
1106
1107 p += fr_sbuff_used(&sbuff);
1108 if (*p != '"') {
1110 }
1111 p++;
1112
1113 if (type != FR_TYPE_STRING) {
1114 if (fr_value_box_cast_in_place(box, box, type, NULL) < 0) {
1116 }
1117 }
1118
1119 } else {
1120 sbuff = FR_SBUFF_IN(p, strlen(p));
1121
1122 /*
1123 * We have no other way to pass the dict to the value-box parse function.
1124 */
1125 if (type == FR_TYPE_ATTR) {
1126 fr_dict_t const *dict = dictionary_current(cc);
1127
1128 if (!dict) {
1129 fr_strerror_const("proto-dictionary must be defined");
1131 }
1132
1133 enumv = fr_dict_root(dict);
1134 }
1135
1136 slen = fr_value_box_from_substr(box, box, type, enumv,
1137 &sbuff,
1139 if (slen < 0) {
1141 }
1142 p += fr_sbuff_used(&sbuff);
1143 }
1145
1146 RETURN_OK(p - in);
1147}
1148
1149static fr_cmd_t *command_head = NULL;
1150
1151static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
1152{
1153 return 0;
1154}
1155
1157{
1158 int i;
1159
1160 for (i = 0; i < info->num_parents; i++) {
1161 printf("%s ", info->parents[i]);
1162 }
1163
1164 printf(":%s ", info->name);
1165 if (info->syntax) printf("%s", info->syntax);
1166 printf("%s", "");
1167
1168 return 1;
1169}
1170
1171static void command_print(void)
1172{
1173 void *walk_ctx = NULL;
1174
1175 printf("Command hierarchy --------");
1176 fr_cmd_debug(stdout, command_head);
1177
1178 printf("Command list --------");
1179 while (fr_command_walk(command_head, &walk_ctx, NULL, command_walk) == 1) {
1180 // do nothing
1181 }
1182}
1183
1184#define CLEAR_TEST_POINT(_cc) \
1185do { \
1186 talloc_free_children((_cc)->tmp_ctx); \
1187 tp = NULL; \
1188} while (0)
1189
1190/** Placeholder function for comments
1191 *
1192 */
1194 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1195{
1196 return 0;
1197}
1198
1199/** Execute another test file
1200 *
1201 */
1203 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1204{
1205 char *q;
1206 bool exit_now = false;
1207 int ret;
1208
1209 if (write_fp) {
1210 fprintf(stderr, "Can't do $INCLUDE with -w %s\n", write_filename);
1211 RETURN_EXIT(1);
1212 }
1213
1214 q = strrchr(cc->path, '/');
1215 if (q) {
1216 *q = '\0';
1217 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, cc->path, in, NULL);
1218 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1219 *q = '/';
1220 RETURN_OK(0);
1221 }
1222
1223 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, NULL, in, NULL);
1224 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1225
1226 RETURN_OK(0);
1227}
1228
1229/** Determine if unresolved attributes are allowed
1230 *
1231 */
1233 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1234{
1235 fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen);
1236 bool res;
1237
1238 if (fr_sbuff_out_bool(&res, &our_in) == 0) {
1239 fr_strerror_printf("Invalid boolean value, must be \"yes\" or \"no\"");
1241 }
1243
1244 RETURN_OK(0);
1245}
1246
1247#define ATTR_COMMON \
1248 fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen); \
1249 fr_dict_attr_err_t err; \
1250 fr_slen_t slen; \
1251 fr_dict_attr_t const *root; \
1252 fr_dict_attr_t const *da; \
1253 root = cc->tmpl_rules.attr.dict_def ? \
1254 fr_dict_root(cc->tmpl_rules.attr.dict_def) : \
1255 fr_dict_root(fr_dict_internal()); \
1256 slen = fr_dict_attr_by_oid_substr(&err, \
1257 &da, \
1258 root, \
1259 &our_in, NULL); \
1260 if (err != FR_DICT_ATTR_OK) FR_SBUFF_ERROR_RETURN(&our_in)
1261
1262
1263/** Print attribute information
1264 *
1265 */
1267 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1268{
1269 fr_hash_table_t *namespace;
1270 fr_hash_iter_t iter;
1271 fr_dict_attr_t const *ref;
1274
1275 namespace = dict_attr_namespace(da);
1276 fr_assert(namespace != NULL);
1277
1278 for (da = fr_hash_table_iter_init(namespace, &iter);
1279 da != NULL;
1280 da = fr_hash_table_iter_next(namespace, &iter)) {
1281 if (da->flags.is_alias) {
1282 ref = fr_dict_attr_ref(da);
1283 fr_assert(ref != NULL);
1284
1285 SBUFF_RETURN_OK_WITH_ERROR(fr_sbuff_in_sprintf, &out, "%s (ALIAS ref=", da->name);
1286
1287 slen = fr_dict_attr_oid_print(&out, fr_dict_root(da->dict), ref, false);
1288 if (slen <= 0) RETURN_OK_WITH_ERROR();
1289
1291 continue;
1292 }
1293
1294 SBUFF_RETURN_OK_WITH_ERROR(fr_sbuff_in_sprintf, &out, "%s (%s), ", da->name, fr_type_to_str(da->type));
1295 }
1296
1297 fr_sbuff_trim(&out, (bool[SBUFF_CHAR_CLASS]){ [' '] = true, [','] = true });
1298
1300}
1301
1302
1303/** Print attribute information
1304 *
1305 */
1307 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1308{
1310
1311 slen = fr_dict_attr_flags_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), da->dict, da->type, &da->flags);
1312 if (slen <= 0) RETURN_OK_WITH_ERROR();
1313
1314 RETURN_OK(slen);
1315}
1316
1317/** Print attribute information
1318 *
1319 */
1321 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1322{
1324
1325 slen = fr_dict_attr_oid_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), root, da, false);
1326 if (slen <= 0) RETURN_OK_WITH_ERROR();
1327
1328 RETURN_OK(slen);
1329}
1330
1331/** Print attribute information
1332 *
1333 */
1335 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1336{
1338
1340 if (slen <= 0) RETURN_OK_WITH_ERROR();
1341
1342 RETURN_OK(slen);
1343}
1344
1345/** Print attribute information
1346 *
1347 */
1349 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1350{
1352
1354
1355 RETURN_OK(slen);
1356}
1357
1359 [ '+' ] = T_ADD,
1360 [ '-' ] = T_SUB,
1361 [ '*' ] = T_MUL,
1362 [ '/' ] = T_DIV,
1363 [ '^' ] = T_XOR,
1364 [ '.' ] = T_ADD,
1365 [ '&' ] = T_AND,
1366 [ '|' ] = T_OR,
1367 [ '%' ] = T_MOD,
1368};
1369
1370/** Perform calculations
1371 *
1372 */
1374 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1375{
1376 fr_value_box_t *a, *b, *out;
1377 size_t match_len;
1379 fr_token_t op;
1380 char const *p, *value, *end;
1381 size_t slen;
1382 bool assignment;
1383
1384 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1385 b = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1386
1387 p = in;
1388 end = in + inlen;
1389
1390 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1391 if (match_len == 0) return 0; /* errors have already been updated */
1392
1393 p += match_len;
1395
1396 op = fr_table_value_by_longest_prefix(&match_len, fr_tokens_table, p, end - p, T_INVALID);
1397 if (op != T_INVALID) {
1398 p += match_len;
1399 assignment = fr_assignment_op[op];
1400
1401 } else {
1402 op = token2op[(uint8_t) p[0]];
1403 if (op == T_INVALID) {
1404 fr_strerror_printf("Unknown operator '%c'", p[0]);
1406 }
1407 p++;
1408
1409 assignment = false;
1410 }
1412
1413 match_len = parse_typed_value(result, cc, b, &value, p, end - p);
1414 if (match_len == 0) return 0;
1415
1416 p += match_len;
1418
1419 if (assignment) {
1420 if (fr_value_calc_assignment_op(cc->tmp_ctx, a, op, b) < 0) {
1422 }
1423 out = a;
1424
1425 } else {
1426 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1427
1428 /*
1429 * If there's no output data type, then the code tries to
1430 * figure one out automatically.
1431 */
1432 if (!*p) {
1434 } else {
1435 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1436 p += 2;
1438
1441 fr_value_box_init(out, type, NULL, false);
1442 }
1443
1444 if (fr_value_calc_binary_op(cc->tmp_ctx, out, type, a, op, b) < 0) {
1446 }
1447 }
1448
1450 if (slen <= 0) RETURN_OK_WITH_ERROR();
1451
1452 RETURN_OK(slen);
1453}
1454
1455/** Perform calculations on multi-valued ops
1456 *
1457 */
1459 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1460{
1461 fr_value_box_t *group, *a, *out;
1462 size_t match_len;
1464 fr_token_t op;
1465 char const *p, *value, *end;
1466 size_t slen;
1467
1468 group = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1469 fr_value_box_init(group, FR_TYPE_GROUP, NULL, false);
1470
1471 p = in;
1472 end = in + inlen;
1473
1474 /*
1475 * Multi-valued operations
1476 */
1477 op = token2op[(uint8_t) p[0]];
1478 if (op == T_INVALID) {
1479 fr_strerror_printf("Unknown operator '%c'", p[0]);
1481 }
1482 p++;
1483
1484 while (p < end) {
1486
1487 a = talloc_zero(group, fr_value_box_t);
1488
1489 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1490 if (match_len == 0) return 0; /* errors have already been updated */
1491
1492 fr_value_box_list_insert_tail(&group->vb_group, a);
1493
1494 p += match_len;
1495
1496 if (strncmp(p, "->", 2) == 0) break;
1497 }
1498
1499 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1501
1502 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1503 p += 2;
1505
1508
1509
1510 if (fr_value_calc_nary_op(cc->tmp_ctx, out, type, op, group) < 0) {
1512 }
1513
1515 if (slen <= 0) RETURN_OK_WITH_ERROR();
1516
1517 RETURN_OK(slen);
1518}
1519
1520/** Perform casting
1521 *
1522 */
1524 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1525{
1526 fr_value_box_t *a, *out;
1527 size_t match_len;
1529 char const *p, *value, *end;
1530 size_t slen;
1531 fr_dict_attr_t const *enumv = NULL;
1532
1533 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1534
1535 p = in;
1536 end = in + inlen;
1537
1538 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1539 if (match_len == 0) return 0; /* errors have already been updated */
1540
1541 p += match_len;
1543
1544 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1545
1546 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1547 p += 2;
1549
1552 fr_value_box_init(out, type, NULL, false);
1553
1554 if (type == FR_TYPE_ATTR) {
1555 enumv = cc->tmpl_rules.attr.dict_def ?
1558 }
1559
1560 if (fr_value_box_cast(out, out, type, enumv, a) < 0) {
1562 }
1563
1565 if (slen <= 0) RETURN_OK_WITH_ERROR();
1566
1567 RETURN_OK(slen);
1568}
1569
1570/** Change the working directory
1571 *
1572 */
1574 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1575{
1576 TALLOC_FREE(cc->path); /* Free old directories */
1577
1578 cc->path = fr_realpath(cc->tmp_ctx, in, inlen);
1579 if (!cc->path) RETURN_COMMAND_ERROR();
1580
1582
1584}
1585
1586/*
1587 * Clear the data buffer
1588 */
1590 char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
1591{
1592 memset(data, 0, COMMAND_OUTPUT_MAX);
1593 RETURN_NOOP(0);
1594}
1595
1596/*
1597 * Add a command by talloc'ing a table for it.
1598 */
1600 char *data, size_t UNUSED data_used, char *in, UNUSED size_t inlen)
1601{
1602 char *p, *name;
1603 char *parent = NULL;
1604 fr_cmd_table_t *table;
1605 char buffer[8192];
1606
1607 table = talloc_zero(cc->tmp_ctx, fr_cmd_table_t);
1608
1609 strlcpy(buffer, in, sizeof(buffer));
1610
1611 p = strchr(buffer, ':');
1612 if (!p) {
1613 fr_strerror_const("no ':name' specified");
1615 }
1616
1617 *p = '\0';
1618 p++;
1619
1621
1622 /*
1623 * Set the name and try to find the syntax.
1624 */
1625 name = p;
1627
1628 if (isspace((uint8_t) *p)) {
1629 *p = '\0';
1630 p++;
1631 }
1632
1634
1635 if (*p) {
1636 table->syntax = talloc_strdup(table, p);
1637 }
1638 table->parent = parent;
1639 table->name = name;
1640 table->help = NULL;
1641 table->func = command_func;
1642 table->tab_expand = NULL;
1643 table->read_only = true;
1644
1645 if (fr_command_add(table, &command_head, NULL, NULL, table) < 0) {
1646 fr_strerror_const_push("ERROR: Failed adding command");
1648 }
1649
1651
1653}
1654
1655/*
1656 * Do tab completion on a command
1657 */
1659 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1660{
1661 int i;
1662 int num_expansions;
1663 char const *expansions[CMD_MAX_ARGV];
1664 char *p = data, *end = p + COMMAND_OUTPUT_MAX, **argv;
1666 size_t len;
1667
1668 info.argc = 0;
1669 info.max_argc = CMD_MAX_ARGV;
1670 info.argv = talloc_zero_array(cc->tmp_ctx, char const *, CMD_MAX_ARGV);
1671 info.box = talloc_zero_array(cc->tmp_ctx, fr_value_box_t *, CMD_MAX_ARGV);
1672
1673 memcpy(&argv, &info.argv, sizeof(argv)); /* const issues */
1674 info.argc = fr_dict_str_to_argv(in, argv, CMD_MAX_ARGV);
1675 if (info.argc <= 0) {
1676 fr_strerror_const("Failed splitting input");
1677 RETURN_PARSE_ERROR(-(info.argc));
1678 }
1679
1680 num_expansions = fr_command_tab_expand(cc->tmp_ctx, command_head, &info, CMD_MAX_ARGV, expansions);
1681
1682 len = snprintf(p, end - p, "%d - ", num_expansions);
1683 if (is_truncated(len, end - p)) {
1684 oob:
1685 fr_strerror_const("Out of output buffer space for radmin command");
1687 }
1688 p += len;
1689
1690 for (i = 0; i < num_expansions; i++) {
1691 len = snprintf(p, end - p, "'%s', ", expansions[i]);
1692 if (is_truncated(len, end - p)) goto oob;
1693 p += len;
1694 }
1695
1696 /*
1697 * Remove the trailing ", "
1698 */
1699 if (num_expansions > 0) {
1700 p -= 2;
1701 *p = '\0';
1702 }
1703
1704 return p - data;
1705}
1706
1707/** Parse and reprint a condition
1708 *
1709 */
1711 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1712{
1713 ssize_t slen;
1714 CONF_SECTION *cs;
1715 size_t len;
1716 xlat_exp_head_t *head = NULL;
1717
1718 cs = cf_section_alloc(NULL, NULL, "if", "condition");
1719 if (!cs) {
1720 fr_strerror_const("Out of memory");
1722 }
1723 cf_filename_set(cs, cc->filename);
1724 cf_lineno_set(cs, cc->lineno);
1725
1727
1728 slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, inlen), NULL, &cc->tmpl_rules);
1729 if (slen == 0) {
1730 fr_strerror_printf_push_head("ERROR failed to parse any input");
1731 talloc_free(cs);
1733 }
1734
1735 if (slen < 0) {
1736 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
1737 talloc_free(cs);
1739 }
1740
1741 if ((size_t) slen < inlen) {
1742 len = snprintf(data, COMMAND_OUTPUT_MAX, "ERROR passed in %zu, returned %zd", inlen, slen);
1743
1744 } else {
1746 }
1747
1749 talloc_free(cs);
1750
1751 RETURN_OK(len);
1752}
1753
1755 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1756{
1757 size_t len;
1758
1759 len = snprintf(data, COMMAND_OUTPUT_MAX, "%u", cc->test_count);
1760 if (is_truncated(len, COMMAND_OUTPUT_MAX)) {
1761 fr_strerror_const("Command count would overflow data buffer (shouldn't happen)");
1763 }
1764
1765 RETURN_OK(len);
1766}
1767
1769 char *data, size_t data_used, char *in, size_t inlen)
1770{
1771 fr_test_point_pair_decode_t *tp = NULL;
1772 void *decode_ctx = NULL;
1773 char *p;
1774 uint8_t *to_dec, *to_dec_start;
1775 uint8_t *to_dec_end;
1776 ssize_t slen;
1777#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1778 size_t poison_size;
1779#endif
1780
1781 fr_dict_attr_t const *da;
1782 fr_pair_t *head;
1783
1784 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1785 fr_assert(da != NULL);
1786 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1787 if (!head) {
1788 fr_strerror_const_push("Failed allocating memory");
1790 }
1791
1792 p = in;
1793
1794 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_pair");
1795 if (!tp) {
1796 fr_strerror_const_push("Failed locating decoder testpoint");
1798 }
1799
1800 p += slen;
1802
1803 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
1804 fr_strerror_const_push("Failed initialising decoder testpoint");
1806 }
1807
1808 /*
1809 * Hack because we consume more of the command string
1810 * so we need to check this again.
1811 */
1812 if (*p == '-') {
1813 p = data;
1814 inlen = data_used;
1815 }
1816
1817 /*
1818 * Decode hex from input text
1819 */
1821 if (slen <= 0) {
1822 CLEAR_TEST_POINT(cc);
1823 RETURN_PARSE_ERROR(-(slen));
1824 }
1825
1826 to_dec = to_dec_start = (uint8_t *)data;
1827 to_dec_end = to_dec + slen;
1828
1829#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1830 poison_size = COMMAND_OUTPUT_MAX - slen;
1831#endif
1832 ASAN_POISON_MEMORY_REGION(to_dec_end, poison_size);
1833
1834 /*
1835 * Run the input data through the test
1836 * point to produce fr_pair_ts.
1837 */
1838 while (to_dec < to_dec_end) {
1839 slen = tp->func(head, &head->vp_group, cc->tmpl_rules.attr.namespace,
1840 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1841
1842 cc->last_ret = slen;
1843 if (slen <= 0) {
1844 ASAN_UNPOISON_MEMORY_REGION(to_dec_end, poison_size);
1845 CLEAR_TEST_POINT(cc);
1847 }
1848 if ((size_t)slen > (size_t)(to_dec_end - to_dec)) {
1849 fr_perror("%s: Internal sanity check failed at %d", __FUNCTION__, __LINE__);
1850 ASAN_UNPOISON_MEMORY_REGION(to_dec_end, poison_size);
1851 CLEAR_TEST_POINT(cc);
1853 }
1854 to_dec += slen;
1855 }
1856
1857 /*
1858 * Clear any spurious errors
1859 */
1861 ASAN_UNPOISON_MEMORY_REGION(to_dec_end, poison_size);
1862
1863 if ((cc->fuzzer_fd >= 0) &&
1864 (dump_fuzzer_data(cc->fuzzer_fd, in, to_dec_start, (to_dec - to_dec_start)) < 0)) {
1866 }
1867
1868 /*
1869 * Output may be an error, and we ignore
1870 * it if so.
1871 */
1872 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1873 if (slen <= 0) {
1875 }
1876
1877 CLEAR_TEST_POINT(cc);
1878 RETURN_OK(slen);
1879}
1880
1882 char *data, size_t data_used, char *in, size_t inlen)
1883{
1885 void *decode_ctx = NULL;
1886 char *p;
1887 uint8_t *to_dec, *to_dec_start;
1888 uint8_t *to_dec_end;
1889 ssize_t slen;
1890#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1891 size_t poison_size;
1892#endif
1893 fr_dict_attr_t const *da;
1894 fr_pair_t *head;
1895
1896 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1897 fr_assert(da != NULL);
1898 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1899 if (!head) {
1900 fr_strerror_const_push("Failed allocating memory");
1902 }
1903
1904 p = in;
1905
1906 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_proto");
1907 if (!tp) {
1908 fr_strerror_const_push("Failed locating decoder testpoint");
1910 }
1911
1912 p += slen;
1914
1915 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
1916 fr_strerror_const_push("Failed initialising decoder testpoint");
1918 }
1919
1920 /*
1921 * Hack because we consume more of the command string
1922 * so we need to check this again.
1923 */
1924 if (*p == '-') {
1925 p = data;
1926 inlen = data_used;
1927 }
1928
1929 /*
1930 * Decode hex from input text
1931 */
1933 if (slen <= 0) {
1934 CLEAR_TEST_POINT(cc);
1935 RETURN_PARSE_ERROR(-(slen));
1936 }
1937
1938 to_dec = to_dec_start = (uint8_t *)data;
1939 to_dec_end = to_dec + slen;
1940
1941#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1942 poison_size = COMMAND_OUTPUT_MAX - slen;
1943#endif
1944 ASAN_POISON_MEMORY_REGION(to_dec_end, poison_size);
1945
1946 slen = tp->func(head, &head->vp_group,
1947 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1948 cc->last_ret = slen;
1949 if (slen <= 0) {
1950 ASAN_UNPOISON_MEMORY_REGION(to_dec_end, poison_size);
1951 CLEAR_TEST_POINT(cc);
1953 }
1954
1955 /*
1956 * Clear any spurious errors
1957 */
1959 ASAN_UNPOISON_MEMORY_REGION(to_dec_end, poison_size);
1960
1961 if ((cc->fuzzer_fd >= 0) &&
1962 (dump_fuzzer_data(cc->fuzzer_fd, in, to_dec_start, slen) < 0)) {
1964 }
1965
1966 /*
1967 * Output may be an error, and we ignore
1968 * it if so.
1969 */
1970
1971 /*
1972 * Print the pairs.
1973 */
1974 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1975 if (slen <= 0) {
1976 fr_assert(0);
1978 }
1979
1980 CLEAR_TEST_POINT(cc);
1981 RETURN_OK(slen);
1982}
1983
1984/** Parse a dictionary attribute, writing "ok" to the data buffer is everything was ok
1985 *
1986 */
1988 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1989{
1991
1993}
1994
1995/** Read a dictionary from a file, and then free it.
1996 *
1997 */
1999 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2000{
2001 int ret;
2002 char *filename, *dir = NULL;
2003 char *p;
2004 fr_dict_t *dict;
2005
2007
2008 if (in[0] == '\0') {
2009 fr_strerror_const("Missing dictionary name");
2011 }
2012
2013 p = strchr(in, ' ');
2014 if (p) *p = '\0';
2015
2016 filename = in;
2017 if (*filename != '/') {
2018 dir = cc->path;
2019
2020 }
2021
2022 /*
2023 * Load the dictionary, and then immediately free it. This lets each run proceed independently.
2024 */
2025 ret = fr_dict_afrom_file(&dict, dir, filename);
2026 if (ret < 0) RETURN_OK_WITH_ERROR();
2027
2029
2031}
2032
2033/** Print the currently loaded dictionary
2034 *
2035 */
2037 UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2038{
2040
2041 /*
2042 * Don't modify the contents of the data buffer
2043 */
2044 RETURN_OK(data_used);
2045}
2046
2047static CC_HINT(nonnull)
2049 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2050{
2051 size_t need;
2052 ssize_t ret;
2053 char *p, *next;
2054 uint8_t *enc_p;
2055 char buffer[8192];
2056
2057 strlcpy(buffer, in, sizeof(buffer));
2058
2059 p = buffer;
2060 next = strchr(p, ',');
2061 if (next) *next = 0;
2062
2063 enc_p = cc->buffer_start;
2064
2065 while (true) {
2066 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2067
2069
2070 if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL,
2071 p, strlen(p),
2073 talloc_free(box);
2075 }
2076
2077 ret = fr_dns_label_from_value_box(&need,
2078 cc->buffer_start, cc->buffer_end - cc->buffer_start, enc_p, true, box, NULL);
2079 talloc_free(box);
2080
2081 if (ret < 0) RETURN_OK_WITH_ERROR();
2082
2083 if (ret == 0) RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "need=%zd", need));
2084
2085 enc_p += ret;
2086
2087 /*
2088 * Go to the next input string
2089 */
2090 if (!next) break;
2091
2092 p = next + 1;
2093 next = strchr(p, ',');
2094 if (next) *next = 0;
2095 }
2096
2097 if ((cc->fuzzer_fd >= 0) &&
2098 (dump_fuzzer_data(cc->fuzzer_fd, in, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2100 }
2101
2102 RETURN_OK(hex_print(data, COMMAND_OUTPUT_MAX, cc->buffer_start, enc_p - cc->buffer_start));
2103}
2104
2106 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2107{
2108 ssize_t slen, total, i, outlen;
2109 char *out, *end;
2110 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2111
2112 /*
2113 * Decode hex from input text
2114 */
2115 total = hex_to_bin(cc->buffer_start, cc->buffer_end - cc->buffer_start, in, inlen);
2116 if (total <= 0) RETURN_PARSE_ERROR(-total);
2117
2118 out = data;
2119 end = data + COMMAND_OUTPUT_MAX;
2120
2121 for (i = 0; i < total; i += slen) {
2122 slen = fr_dns_label_to_value_box(box, box, cc->buffer_start, total, cc->buffer_start + i, false, NULL);
2123 if (slen <= 0) {
2124 error:
2125 talloc_free(box);
2127 }
2128
2129 /*
2130 * Separate names by commas
2131 */
2132 if (i > 0) *(out++) = ',';
2133
2134 /*
2135 * We don't print it with quotes.
2136 */
2137 outlen = fr_value_box_print(&FR_SBUFF_OUT(out, end - out), box, NULL);
2138 if (outlen <= 0) goto error;
2139 out += outlen;
2140
2141 fr_value_box_clear(box);
2142 }
2143
2144 talloc_free(box);
2145 RETURN_OK(out - data);
2146}
2147
2149 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2150{
2151 fr_test_point_pair_encode_t *tp = NULL;
2152
2153 fr_dcursor_t cursor;
2154 void *encode_ctx = NULL;
2155 ssize_t slen;
2156 char *p = in;
2157
2158 uint8_t *enc_p, *enc_end;
2160 fr_pair_t *vp;
2161 bool truncate = false;
2162
2163 size_t iterations = 0;
2164 fr_pair_parse_t root, relative;
2165
2167
2168 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_pair");
2169 if (!tp) {
2170 fr_strerror_const_push("Failed locating encode testpoint");
2171 CLEAR_TEST_POINT(cc);
2173 }
2174
2175 p += ((size_t)slen);
2177
2178 /*
2179 * The truncate torture test.
2180 *
2181 * Increase the buffer one byte at a time until all items in the cursor
2182 * have been encoded.
2183 *
2184 * The poisoned region at the end of the buffer will detect overruns
2185 * if we're running with asan.
2186 *
2187 */
2188 if (strncmp(p, "truncate", sizeof("truncate") - 1) == 0) {
2189 truncate = true;
2190 p += sizeof("truncate") - 1;
2192 }
2193
2194 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
2195 fr_strerror_const_push("Failed initialising encoder testpoint");
2196 CLEAR_TEST_POINT(cc);
2198 }
2199
2200 root = (fr_pair_parse_t) {
2201 .ctx = cc->tmp_ctx,
2202 .da = cc->tmpl_rules.attr.namespace,
2203 .list = &head,
2204 .dict = cc->tmpl_rules.attr.namespace->dict,
2205 .internal = fr_dict_internal(),
2206 .allow_exec = true
2207 };
2208 relative = (fr_pair_parse_t) { };
2209
2210 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2211 if (slen <= 0) {
2212 CLEAR_TEST_POINT(cc);
2214 }
2215
2217
2218 /*
2219 * Outer loop implements truncate test
2220 */
2221 do {
2222 enc_p = cc->buffer_start;
2223 enc_end = truncate ? cc->buffer_start + iterations++ : cc->buffer_end;
2224
2225 if (truncate) {
2226#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2227 /*
2228 * Poison the region between the subset of the buffer
2229 * we're using and the end of the buffer.
2230 */
2231 ASAN_POISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2232
2233 DEBUG("%s[%d]: Iteration %zu - Safe region %p-%p (%zu bytes), "
2234 "poisoned region %p-%p (%zu bytes)", cc->filename, cc->lineno, iterations - 1,
2235 enc_p, enc_end, enc_end - enc_p, enc_end, cc->buffer_end, cc->buffer_end - enc_end);
2236#else
2237 DEBUG("%s[%d]: Iteration %zu - Allowed region %p-%p (%zu bytes)",
2238 cc->filename, cc->lineno, iterations - 1, enc_p, enc_end, enc_end - enc_p);
2239#endif
2240 }
2241
2242 for (vp = fr_pair_dcursor_iter_init(&cursor, &head,
2244 dictionary_current(cc));
2245 vp;
2246 vp = fr_dcursor_current(&cursor)) {
2247 slen = tp->func(&FR_DBUFF_TMP(enc_p, enc_end), &cursor, encode_ctx);
2248 cc->last_ret = slen;
2249
2250 if (truncate) DEBUG("%s[%d]: Iteration %zu - Result %zd%s%s",
2251 cc->filename, cc->lineno, iterations - 1, slen,
2252 *fr_strerror_peek() != '\0' ? " - " : "",
2253 *fr_strerror_peek() != '\0' ? fr_strerror_peek() : "");
2254 if (slen < 0) break;
2255
2256 /*
2257 * Encoder indicated it encoded too much data
2258 */
2259 if (slen > (enc_end - enc_p)) {
2260 fr_strerror_printf("Expected returned encoded length <= %zu bytes, got %zu bytes",
2261 (enc_end - enc_p), (size_t)slen);
2262#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2263 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2264#endif
2266 CLEAR_TEST_POINT(cc);
2268 }
2269
2270 enc_p += slen;
2271
2272 if (slen == 0) break;
2273
2274 }
2275
2276#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2277 /*
2278 * un-poison the region between the subset of the buffer
2279 * we're using and the end of the buffer.
2280 */
2281 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2282#endif
2283 /*
2284 * We consumed all the VPs, so presumably encoded the
2285 * complete pair list.
2286 */
2287 if (!vp) break;
2288 } while (truncate && (enc_end < cc->buffer_end));
2289
2290 /*
2291 * Last iteration result in an error
2292 */
2293 if (slen < 0) {
2295 CLEAR_TEST_POINT(cc);
2297 }
2298
2299 /*
2300 * Clear any spurious errors
2301 */
2303
2305
2306 CLEAR_TEST_POINT(cc);
2307
2308 if ((cc->fuzzer_fd >= 0) &&
2309 (dump_fuzzer_data(cc->fuzzer_fd, p, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2311 }
2312
2314}
2315
2316/** Encode a RADIUS attribute writing the result to the data buffer as space separated hexits
2317 *
2318 */
2320 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2321{
2322 size_t len;
2323 char buffer[8192];
2324
2325 strlcpy(buffer, in, sizeof(buffer));
2326
2327 len = encode_rfc(buffer, cc->buffer_start, cc->buffer_end - cc->buffer_start);
2328 if (len <= 0) RETURN_PARSE_ERROR(0);
2329
2330 if (len >= (size_t)(cc->buffer_end - cc->buffer_start)) {
2331 fr_strerror_const("Encoder output would overflow output buffer");
2333 }
2334
2336}
2337
2338/** Parse a list of pairs
2339 *
2340 */
2342 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2343{
2344 ssize_t slen;
2346 bool done = false;
2347 char *filename;
2348 FILE *fp;
2349
2350 filename = talloc_asprintf(cc->tmp_ctx, "%s/%s", cc->path, in);
2351
2352 fp = fopen(filename, "r");
2353 talloc_free(filename);
2354
2355 if (!fp) {
2356 fr_strerror_printf("Failed opening %s - %s", in, fr_syserror(errno));
2358 }
2359
2361 slen = fr_pair_list_afrom_file(cc->tmp_ctx, cc->tmpl_rules.attr.dict_def, &head, fp, &done, true);
2362 fclose(fp);
2363 if (slen < 0) {
2365 }
2366
2367 /*
2368 * Print the pairs.
2369 */
2371 if (slen <= 0) {
2372 fr_assert(0);
2374 }
2375
2376 if (!done) {
2377 strlcpy(data + slen, "!DONE", COMMAND_OUTPUT_MAX - slen);
2378 slen += 5;
2379 }
2380
2382
2383 RETURN_OK(slen);
2384}
2385
2386
2388 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2389{
2391}
2392
2394 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2395{
2397
2398 void *encode_ctx = NULL;
2399 ssize_t slen;
2400 char *p = in;
2401
2403 fr_pair_parse_t root, relative;
2404
2406
2407 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_proto");
2408 if (!tp) {
2409 fr_strerror_const_push("Failed locating encode testpoint");
2410 CLEAR_TEST_POINT(cc);
2412 }
2413
2414 p += ((size_t)slen);
2416 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
2417 fr_strerror_const_push("Failed initialising encoder testpoint");
2418 CLEAR_TEST_POINT(cc);
2420 }
2421
2422 root = (fr_pair_parse_t) {
2423 .ctx = cc->tmp_ctx,
2424 .da = cc->tmpl_rules.attr.namespace,
2425 .list = &head,
2426 .dict = cc->tmpl_rules.attr.namespace->dict,
2427 .internal = fr_dict_internal(),
2428 .allow_exec = true
2429 };
2430 relative = (fr_pair_parse_t) { };
2431
2432 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2433 if (slen <= 0) {
2434 CLEAR_TEST_POINT(cc);
2436 }
2437
2438 slen = tp->func(cc->tmp_ctx, &head, cc->buffer_start, cc->buffer_end - cc->buffer_start, encode_ctx);
2440 cc->last_ret = slen;
2441 if (slen < 0) {
2442 CLEAR_TEST_POINT(cc);
2444 }
2445 /*
2446 * Clear any spurious errors
2447 */
2449
2450 CLEAR_TEST_POINT(cc);
2451
2452 if ((cc->fuzzer_fd >= 0) &&
2453 (dump_fuzzer_data(cc->fuzzer_fd, p, cc->buffer_start, slen) < 0)) {
2455 }
2456
2458}
2459
2460/** Command eof
2461 *
2462 * Mark the end of a test file if we're reading from stdin.
2463 *
2464 * Doesn't actually do anything, is just a placeholder for the command processing loop.
2465 */
2467 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2468{
2469 return 0;
2470}
2471
2472/** Helper function to open a fuzzer path, and update the name / FD.
2473 *
2474 */
2475static int fuzzer_open_fd(command_file_ctx_t *cc, char **out, char const *base, char const *dir)
2476{
2477 int fd;
2478 struct stat sdir;
2479 char *fuzzer_dir = NULL;
2480 bool retry_dir = true;
2481
2482 /*
2483 * Close any open fuzzer output dirs
2484 */
2485 if (cc->fuzzer_fd >= 0) {
2486 close(cc->fuzzer_fd);
2487 cc->fuzzer_fd = -1;
2488 }
2489
2490 fuzzer_dir = talloc_asprintf(cc, "%s/%s", base, dir);
2491
2492again:
2493 fd = open(fuzzer_dir, O_RDONLY);
2494 if (fd < 0) {
2495 if (mkdir(fuzzer_dir, 0777) == 0) {
2496 fd = open(fuzzer_dir, O_RDONLY);
2497 if (fd >= 0) goto stat;
2498 /*
2499 * Prevent race if multiple unit_test_attribute instances
2500 * attempt to create the same output dir.
2501 */
2502 } else if ((errno == EEXIST) && retry_dir) {
2503 retry_dir = false; /* Only allow this once */
2504 goto again;
2505 }
2506
2507 fr_strerror_printf("fuzzer-out \"%s\" doesn't exist: %s", fuzzer_dir, fr_syserror(errno));
2508 return -1;
2509 }
2510
2511stat:
2512 if (fstat(fd, &sdir) < 0) {
2513 close(fd);
2514 fr_strerror_printf("failed statting fuzzer-out \"%s\": %s", fuzzer_dir, fr_syserror(errno));
2515 return -1;
2516 }
2517
2518 if (!(sdir.st_mode & S_IFDIR)) {
2519 close(fd);
2520 fr_strerror_printf("fuzzer-out \"%s\" is not a directory", fuzzer_dir);
2521 return -1;
2522 }
2523
2524 cc->fuzzer_fd = fd;
2525
2526 talloc_free(*out);
2527 *out = fuzzer_dir;
2528
2529 return 0;
2530}
2531
2532/** Enable fuzzer output
2533 *
2534 * Any commands that produce potentially useful corpus seed data will write that out data
2535 * to files in the specified directory, using the md5 of the text input at as the file name.
2536 *
2537 * The output directory given here is appended to the path given by '-F path'.
2538 *
2539 * If there's no '-F path' command-line option set, then the path of the current file is used as the base
2540 * directory.
2541 */
2543 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2544{
2545 if (in[0] == '\0') {
2546 fr_strerror_const("Missing directory name");
2548 }
2549
2550 if (fuzzer_open_fd(cc, &cc->fuzzer_proto_dir,
2552 in) < 0) {
2554
2555 }
2556
2557 return 0;
2558}
2559
2560/** Exit gracefully with the specified code
2561 *
2562 */
2564 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2565{
2566 if (!*in) RETURN_EXIT(0);
2567
2568 RETURN_EXIT(atoi(in));
2569}
2570
2572 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2573{
2574 char *name, *tmp = NULL;
2575 char const *dir;
2576 char *q;
2577 int ret;
2578
2580
2581 if (in[0] == '\0') {
2582 fr_strerror_const("Missing dictionary name");
2584 }
2585
2586 q = strchr(in, ' ');
2587 if (q) {
2588 name = tmp = talloc_bstrndup(NULL, in, q - in);
2589 q++;
2590 dir = q;
2591 } else {
2592 name = in;
2593 dir = cc->path;
2594 }
2595
2596 /*
2597 * When we're reading multiple files at the same time, they might all have a 'load-dictionary foo'
2598 * command. In which case we don't complain.
2599 */
2601 RETURN_OK(0);
2602 }
2603
2605 talloc_free(tmp);
2606 if (ret < 0) RETURN_COMMAND_ERROR();
2607
2608 RETURN_OK(0);
2609}
2610
2611
2612/** Compare the data buffer to an expected value
2613 *
2614 */
2616 char *data, size_t data_used, char *in, size_t inlen)
2617{
2618 if (strcmp(in, data) != 0) {
2619 if (write_fp) {
2620 strcpy(in, data);
2621 RETURN_OK(data_used);
2622 }
2623
2624 mismatch_print(cc, "match", in, inlen, data, data_used, true);
2625 RETURN_MISMATCH(data_used);
2626 }
2627
2628 /*
2629 * We didn't actually write anything, but this
2630 * keeps the contents of the data buffer around
2631 * for the next command to operate on.
2632 */
2633 RETURN_OK(data_used);
2634}
2635
2636/** Compare the data buffer against an expected expression
2637 *
2638 */
2640 char *data, size_t data_used, char *in, size_t inlen)
2641{
2642 ssize_t slen;
2643 regex_t *regex;
2644 int ret;
2645
2646 slen = regex_compile(cc->tmp_ctx, &regex, in, inlen, NULL, false, true);
2647 if (slen <= 0) RETURN_COMMAND_ERROR();
2648
2649 ret = regex_exec(regex, data, data_used, NULL);
2650 talloc_free(regex);
2651
2652 switch (ret) {
2653 case -1:
2654 default:
2656
2657 case 0:
2658 mismatch_print(cc, "match-regex", in, inlen, data, data_used, false);
2659 RETURN_MISMATCH(data_used);
2660
2661 case 1:
2662 RETURN_OK(data_used);
2663 }
2664}
2665
2666/** Artificially limit the maximum packet size.
2667 *
2668 */
2670 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2671{
2672 unsigned long size;
2673 char *end;
2674
2676
2677 if (*in != '\0') {
2678 size = strtoul(in, &end, 10);
2679 if ((size == ULONG_MAX) || *end || (size >= 65536)) {
2680 fr_strerror_const_push("Invalid integer");
2682 }
2683 } else {
2684 size = DEFAULT_BUFFER_SIZE;
2685 }
2686
2687 if (poisoned_buffer_allocate(cc, &cc->buffer, size) < 0) RETURN_EXIT(1);
2690
2691 RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "%ld", size));
2692}
2693
2694/** Set or clear migration flags.
2695 *
2696 */
2698 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2699{
2700 char *p;
2701 bool *out;
2702
2704 p = in;
2705
2706 if (strncmp(p, "xlat_new_functions", sizeof("xlat_new_functions") - 1) == 0) {
2707 p += sizeof("xlat_new_functions") - 1;
2709
2710 } else {
2711 fr_strerror_const("Unknown migration flag");
2713 }
2714
2716 if (*p != '=') {
2717 fr_strerror_const("Missing '=' after flag");
2719 }
2720 p++;
2721
2723 if ((strcmp(p, "yes") == 0) || (strcmp(p, "true") == 0) || (strcmp(p, "1") == 0)) {
2724 *out = true;
2725
2726 } else if ((strcmp(p, "no") == 0) || (strcmp(p, "false") == 0) || (strcmp(p, "0") == 0)) {
2727 *out = false;
2728
2729 } else {
2730 fr_strerror_const("Invalid value for flag");
2732 }
2733
2734 RETURN_OK(0);
2735}
2736
2737/** Skip the test file if we're missing a particular feature
2738 *
2739 */
2741 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2742{
2743 CONF_PAIR *cp;
2744
2745 if (in[0] == '\0') {
2746 fr_strerror_printf("Prerequisite syntax is \"need-feature <feature>\". "
2747 "Use -f to print features");
2749 }
2750
2751 cp = cf_pair_find(cc->config->features, in);
2752 if (!cp || (strcmp(cf_pair_value(cp), "yes") != 0)) {
2753 DEBUG("Skipping, missing feature \"%s\"", in);
2755 }
2756
2757 RETURN_NOOP(0);
2758}
2759
2760/** Negate the result of a match command or any command which returns "OK"
2761 *
2762 */
2764 char *data, size_t data_used, char *in, size_t inlen)
2765{
2766 data_used = process_line(result, cc, data, data_used, in, inlen);
2767 switch (result->rcode) {
2768 /*
2769 * OK becomes a command error
2770 */
2771 case RESULT_OK:
2772 ERROR("%s[%d]: %.*s: returned 'ok', where we expected 'result-mismatch'",
2773 cc->filename, cc->lineno, (int) inlen, in);
2774 RETURN_MISMATCH(data_used);
2775
2776 /*
2777 * Mismatch becomes OK
2778 */
2779 case RESULT_MISMATCH:
2780 RETURN_OK(data_used);
2781
2782 /*
2783 * The rest are unchanged...
2784 */
2785 default:
2786 break;
2787 }
2788
2789 return data_used;
2790}
2791
2792/** Parse an print an attribute pair or pair list.
2793 *
2794 */
2796 char *data, UNUSED size_t data_used, char *in, size_t inlen,
2797 bool allow_compare)
2798{
2800 ssize_t slen;
2801 fr_dict_t const *dict = dictionary_current(cc);
2802 fr_pair_parse_t root, relative;
2803
2805
2806 root = (fr_pair_parse_t) {
2807 .ctx = cc->tmp_ctx,
2808 .da = fr_dict_root(dict),
2809 .list = &head,
2810 .dict = dict,
2811 .internal = fr_dict_internal(),
2812 .allow_compare = allow_compare,
2813 .allow_exec = true
2814 };
2815 relative = (fr_pair_parse_t) { };
2816
2817 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(in, inlen));
2818 if (slen <= 0) {
2819// fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
2822 }
2823
2824 /*
2825 * Output may be an error, and we ignore
2826 * it if so.
2827 */
2828
2830 if (slen <= 0) {
2833 }
2834
2836 RETURN_OK(slen);
2837}
2838
2840 char *data, size_t data_used, char *in, size_t inlen)
2841{
2842 return command_pair_common(result, cc, data, data_used, in, inlen, false);
2843}
2844
2846 char *data, size_t data_used, char *in, size_t inlen)
2847{
2848 return command_pair_common(result, cc, data, data_used, in, inlen, true);
2849}
2850
2851
2852/** Dynamically load a protocol library
2853 *
2854 */
2856 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2857{
2858 ssize_t slen;
2859
2860 if (*in == '\0') {
2861 fr_strerror_printf("Load syntax is \"proto <lib_name>\"");
2863 }
2864
2866 slen = load_proto_library(in);
2867 if (slen <= 0) RETURN_PARSE_ERROR(-(slen));
2868
2869 RETURN_OK(0);
2870}
2871
2873 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2874{
2876 return dictionary_load_common(result, cc, in, NULL);
2877}
2878
2880 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2881{
2882 fr_dict_t const *dict = dictionary_current(cc);
2884 fr_dict_attr_t const *new_root;
2885 char *p, buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
2886 char *fuzzer_dir = NULL;
2887 char const *q;
2888
2889 if (is_whitespace(in) || (*in == '\0')) {
2890 new_root = fr_dict_root(dict);
2891 } else {
2892 new_root = fr_dict_attr_by_name(NULL, fr_dict_root(dict), in);
2893 if (!new_root) {
2894 fr_strerror_printf("dictionary attribute \"%s\" not found in %s", in, root_da->name);
2896 }
2897 }
2898
2899 cc->tmpl_rules.attr.namespace = new_root;
2900
2901 if (cc->fuzzer_fd < 0) RETURN_OK(0);
2902
2903 for (p = buffer, q = new_root->name; *q != '\0'; p++, q++) {
2904 if (isalnum((uint8_t) *q)) {
2905 *p = *q;
2906 } else {
2907 *p = '_';
2908 }
2909 }
2910 *p = '\0';
2911
2912 if (fuzzer_open_fd(cc, &fuzzer_dir, cc->fuzzer_proto_dir, buffer) < 0) {
2914 }
2915
2916 RETURN_OK(0);
2917}
2918
2919/** Parse an reprint a tmpl expansion
2920 *
2921 */
2923 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2924{
2925 ssize_t slen;
2926 tmpl_t *vpt;
2927 size_t input_len = strlen(in), escaped_len;
2928
2929 slen = tmpl_afrom_substr(cc->tmp_ctx, &vpt, &FR_SBUFF_IN(in, input_len), T_BARE_WORD,
2931 &(tmpl_rules_t) {
2932 .attr = {
2933 .dict_def = dictionary_current(cc),
2934 .list_def = request_attr_request,
2935 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2936 },
2937 .xlat = cc->tmpl_rules.xlat,
2938 });
2939 if (slen == 0) {
2940 fr_strerror_printf_push_head("ERROR failed to parse any input");
2942 }
2943
2944 if (slen < 0) {
2945 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
2946
2947 return_error:
2949 }
2950
2951 if (((size_t) slen != input_len)) {
2952 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
2953 goto return_error;
2954 }
2955
2956 escaped_len = tmpl_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), vpt, NULL);
2957 RETURN_OK(escaped_len);
2958}
2959
2960/** Touch a file to indicate a test completed
2961 *
2962 */
2964 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2965{
2966 if (fr_unlink(in) < 0) RETURN_COMMAND_ERROR();
2967 if (fr_touch(NULL, in, 0644, true, 0755) <= 0) RETURN_COMMAND_ERROR();
2968
2969 RETURN_OK(0);
2970}
2971
2972/** Callback for a tmpl rule parser
2973 *
2974 */
2975typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value);
2976
2978{
2979 bool res;
2980 ssize_t slen;
2981
2982 slen = fr_sbuff_out_bool(&res, value);
2983 rules->attr.allow_foreign = res;
2984 return slen;
2985}
2986
2988{
2989 bool res;
2990 ssize_t slen;
2991
2992 slen = fr_sbuff_out_bool(&res, value);
2993 rules->attr.allow_unknown = res;
2994 return slen;
2995}
2996
2998{
2999 bool res;
3000 ssize_t slen;
3001
3002 slen = fr_sbuff_out_bool(&res, value);
3003 rules->attr.allow_unresolved = res;
3004 return slen;
3005}
3006
3008{
3010 fr_slen_t slen;
3011
3013 &rules->attr.namespace,
3014 rules->attr.dict_def ? fr_dict_root(rules->attr.dict_def) :
3016 value, NULL);
3018 return slen;
3019}
3020
3022{
3023 ssize_t slen;
3024
3026
3027 if (slen == 0) {
3028 fr_strerror_printf("Invalid list specifier \"%pV\"",
3030 }
3031
3032 return slen;
3033}
3034
3036{
3037 fr_slen_t slen;
3038
3039 slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
3040 &rules->attr.request_def,
3041 value);
3042 if (slen < 0) {
3043 fr_strerror_printf("Invalid request specifier \"%pV\"",
3045 }
3046
3047 return slen;
3048}
3049
3051 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
3052{
3053 fr_sbuff_t sbuff = FR_SBUFF_IN(in, inlen);
3054 ssize_t slen;
3056 void *res;
3057
3058 static fr_table_ptr_sorted_t tmpl_rule_func_table[] = {
3059 { L("allow_foreign"), (void *)command_tmpl_rule_allow_foreign },
3060 { L("allow_unknown"), (void *)command_tmpl_rule_allow_unknown },
3061 { L("allow_unresolved"), (void *)command_tmpl_rule_allow_unresolved },
3062 { L("attr_parent"), (void *)command_tmpl_rule_attr_parent },
3063 { L("list_def"), (void *)command_tmpl_rule_list_def },
3064 { L("request_def"), (void *)command_tmpl_rule_request_def }
3065 };
3066 static size_t tmpl_rule_func_table_len = NUM_ELEMENTS(tmpl_rule_func_table);
3067
3068 while (fr_sbuff_extend(&sbuff)) {
3069 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
3070
3071 fr_sbuff_out_by_longest_prefix(&slen, &res, tmpl_rule_func_table, &sbuff, NULL);
3072 if (res == NULL) {
3073 fr_strerror_printf("Specified rule \"%pV\" is invalid",
3076 }
3077 func = (command_tmpl_rule_func)res; /* -Wpedantic */
3078
3079 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
3080
3081 if (!fr_sbuff_next_if_char(&sbuff, '=')) {
3082 fr_strerror_printf("Expected '=' after rule identifier, got \"%pV\"",
3085 }
3086
3087 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
3088
3089 if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
3090 }
3091
3092 return fr_sbuff_used(&sbuff);
3093}
3094
3096 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3097{
3098 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
3099 fr_value_box_t *box2;
3100 char const *value;
3101 size_t match_len;
3102 ssize_t slen;
3104
3105 match_len = parse_typed_value(result, cc, box, &value, in, strlen(in));
3106 if (match_len == 0) {
3107 talloc_free(box);
3108 return 0; /* errors have already been updated */
3109 }
3110
3111 type = box->type;
3112
3113 /*
3114 * Don't print dates with enclosing quotation marks.
3115 */
3116 if (type != FR_TYPE_DATE) {
3119 } else {
3121 }
3122 if (slen <= 0) {
3123 talloc_free(box);
3125 }
3126
3127 /*
3128 * Behind the scenes, parse the data
3129 * string. We should get the same value
3130 * box as last time.
3131 */
3132 box2 = talloc_zero(NULL, fr_value_box_t);
3133 if (fr_value_box_from_str(box2, box2, type, box->enumv,
3134 data, slen,
3136 talloc_free(box2);
3137 talloc_free(box);
3139 }
3140
3141 /*
3142 * They MUST be identical
3143 */
3144 if (fr_value_box_cmp(box, box2) != 0) {
3145 fr_strerror_const("ERROR value box reparsing failed. Results not identical");
3146 fr_strerror_printf_push("out: %pV (as string %.*s)", box2, (int) slen, data);
3147 fr_strerror_printf_push("in: %pV (from string %s)", box, value);
3148 talloc_free(box2);
3149 talloc_free(box);
3151 }
3152
3153 /*
3154 * Store <type><value str...>
3155 */
3156 if (cc->fuzzer_fd >= 0) {
3157 char fuzzer_buffer[1024];
3158 char *fuzzer_p = fuzzer_buffer, *fuzzer_end = fuzzer_p + sizeof(fuzzer_buffer);
3159
3160 *fuzzer_p++ = (uint8_t)type; /* Fuzzer uses first byte for type */
3161
3162 strlcpy(fuzzer_p, data, slen > fuzzer_end - fuzzer_p ? fuzzer_end - fuzzer_p : slen);
3163
3164 if (dump_fuzzer_data(cc->fuzzer_fd, fuzzer_buffer,
3165 (uint8_t *)fuzzer_buffer, strlen(fuzzer_buffer)) < 0) {
3167 }
3168 }
3169
3170 talloc_free(box2);
3171 talloc_free(box);
3172 RETURN_OK(slen);
3173}
3174
3176 char *data, size_t data_used, char *in, size_t inlen)
3177{
3178 int fd;
3179 char *path;
3180 bool locked = false;
3181
3182 path = talloc_bstrndup(cc->tmp_ctx, in, inlen);
3183
3184 fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
3185 if (fd < 0) {
3186 fr_strerror_printf("Failed opening \"%s\": %s", path, fr_syserror(errno));
3187 error:
3188 talloc_free(path);
3189 if (fd >= 0) {
3190 if (locked) (void)flock(fd, LOCK_UN);
3191 close(fd);
3192 }
3194 }
3195
3196 if (flock(fd, LOCK_EX) < 0) {
3197 fr_strerror_printf("Failed locking \"%s\": %s", path, fr_syserror(errno));
3198 goto error;
3199 }
3200 locked = true;
3201
3202 while (data_used) {
3203 ssize_t ret;
3204 ret = write(fd, data, data_used);
3205 if (ret < 0) {
3206 fr_strerror_printf("Failed writing to \"%s\": %s", path, fr_syserror(errno));
3207 goto error;
3208 }
3209 data_used -= ret;
3210 data += ret;
3211 }
3212 (void)flock(fd, LOCK_UN);
3213 talloc_free(path);
3214 close(fd);
3215
3216 RETURN_OK(data_used);
3217}
3218
3219/** Parse an reprint and xlat expansion
3220 *
3221 */
3223 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3224{
3225 ssize_t slen;
3226 xlat_exp_head_t *head = NULL;
3227 size_t input_len = strlen(in), escaped_len;
3228 fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
3229
3230 if (allow_purify) {
3231 fr_strerror_printf_push_head("ERROR cannot run 'xlat' when running with command-line argument '-p'");
3233 }
3234
3235 slen = xlat_tokenize(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), &p_rules,
3236 &(tmpl_rules_t) {
3237 .attr = {
3238 .dict_def = dictionary_current(cc),
3239 .list_def = request_attr_request,
3240 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
3241 },
3242 .xlat = cc->tmpl_rules.xlat,
3243 });
3244 if (slen == 0) {
3245 fr_strerror_printf_push_head("ERROR failed to parse any input");
3247 }
3248
3249 if (slen < 0) {
3250 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3251
3252 return_error:
3254 }
3255
3256 if (((size_t) slen != input_len)) {
3257 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
3258 goto return_error;
3259 }
3260
3262 RETURN_OK(escaped_len);
3263}
3264
3265/** Parse and reprint an xlat expression expansion
3266 *
3267 */
3269 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3270{
3271 ssize_t dec_len;
3272 xlat_exp_head_t *head = NULL;
3273 size_t input_len = strlen(in), escaped_len;
3274// fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
3275
3276 dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL,
3277 &(tmpl_rules_t) {
3278 .attr = {
3279 .dict_def = dictionary_current(cc),
3280 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3281 .list_def = request_attr_request,
3282 }
3283 });
3284 if (dec_len <= 0) {
3285 fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
3286
3287 return_error:
3289 }
3290
3291 if (((size_t) dec_len != input_len)) {
3292 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, dec_len);
3293 goto return_error;
3294 }
3295
3297 RETURN_OK(escaped_len);
3298}
3299
3300/** Parse, purify, and reprint an xlat expression expansion
3301 *
3302 */
3304 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3305{
3306 ssize_t slen;
3307 xlat_exp_head_t *head = NULL;
3308 size_t input_len = strlen(in), escaped_len;
3309 tmpl_rules_t t_rules = (tmpl_rules_t) {
3310 .attr = {
3312 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3313 .list_def = request_attr_request,
3314 },
3315 .xlat = cc->tmpl_rules.xlat,
3316 .at_runtime = true,
3317 };
3318
3319 if (!el) {
3320 fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
3321 goto return_error;
3322 }
3323 t_rules.xlat.runtime_el = el;
3324
3325 slen = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
3326 if (slen == 0) {
3327 fr_strerror_printf_push_head("ERROR failed to parse any input");
3329 }
3330
3331 if (slen < 0) {
3332 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3333 return_error:
3335 }
3336
3337 if (((size_t) slen != input_len)) {
3338 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, slen);
3339 goto return_error;
3340 }
3341
3342 if (fr_debug_lvl > 2) {
3343 DEBUG("Before purify --------------------------------------------------");
3345 }
3346
3347 if (xlat_purify(head, NULL) < 0) {
3348 fr_strerror_printf_push_head("ERROR purifying node - %s", fr_strerror());
3349 goto return_error;
3350 }
3351
3352 if (fr_debug_lvl > 2) {
3353 DEBUG("After purify --------------------------------------------------");
3355 }
3356
3358 RETURN_OK(escaped_len);
3359}
3360
3361
3362/** Parse, purify, and reprint an xlat expression expansion
3363 *
3364 */
3366 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3367{
3368 ssize_t slen;
3369 xlat_exp_head_t *head = NULL;
3370 size_t input_len = strlen(in), escaped_len;
3371 tmpl_rules_t t_rules = (tmpl_rules_t) {
3372 .attr = {
3374 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3375 .list_def = request_attr_request,
3376 },
3377 .xlat = cc->tmpl_rules.xlat,
3378 .at_runtime = true,
3379 };
3380
3381 if (!el) {
3382 fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
3383 goto return_error;
3384 }
3385 t_rules.xlat.runtime_el = el;
3386
3387 slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
3388 if (slen == 0) {
3389 fr_strerror_printf_push_head("ERROR failed to parse any input");
3391 }
3392
3393 if (slen < 0) {
3394 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3395 return_error:
3397 }
3398
3399 if (((size_t) slen != input_len)) {
3400 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, slen);
3401 goto return_error;
3402 }
3403
3404 if (fr_debug_lvl > 2) {
3405 DEBUG("Before purify --------------------------------------------------");
3407 }
3408
3409 if (xlat_purify(head, NULL) < 0) {
3410 fr_strerror_printf_push_head("ERROR purifying node - %s", fr_strerror());
3411 goto return_error;
3412 }
3413
3414 if (fr_debug_lvl > 2) {
3415 DEBUG("After purify --------------------------------------------------");
3417 }
3418
3420 RETURN_OK(escaped_len);
3421}
3422
3423
3424/** Parse an reprint and xlat argv expansion
3425 *
3426 */
3428 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3429{
3430 int i, argc;
3431 char *p;
3432 ssize_t slen;
3433 xlat_exp_head_t *head = NULL;
3434 xlat_exp_head_t **argv;
3435 size_t len;
3436 size_t input_len = strlen(in);
3437 char buff[1024];
3438
3439 if (allow_purify) {
3440 fr_strerror_printf_push_head("ERROR cannot run 'xlat_argv' when running with command-line argument '-p'");
3442 }
3443
3444 slen = xlat_tokenize_argv(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len),
3445 NULL, NULL,
3446 &(tmpl_rules_t) {
3447 .attr = {
3448 .dict_def = dictionary_current(cc),
3449 .list_def = request_attr_request,
3450 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
3451 },
3452 }, true);
3453 if (slen <= 0) {
3454 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
3456 }
3457
3458 argc = xlat_flatten_to_argv(cc->tmp_ctx, &argv, head);
3459 if (argc <= 0) {
3460 fr_strerror_printf_push("ERROR in argument %d", (int) -argc);
3462 }
3463
3464 for (i = 0, p = data; i < argc; i++) {
3465 (void) xlat_print(&FR_SBUFF_OUT(buff, sizeof(buff)), argv[i], NULL);
3466
3467 len = snprintf(p, data + COMMAND_OUTPUT_MAX - p, "[%d]{ %s }, ", i, buff);
3468 p += len;
3469 }
3470
3471 p -= 2;
3472 *p = '\0';
3473
3474 RETURN_OK(p - data);
3475}
3476
3478 { L("#"), &(command_entry_t){
3479 .func = command_comment,
3480 .usage = "#<string>",
3481 .description = "A comment - not processed"
3482 }},
3483 { L("$INCLUDE "), &(command_entry_t){
3484 .func = command_include,
3485 .usage = "$INCLUDE <relative_path>",
3486 .description = "Execute a test file"
3487 }},
3488 { L("allow-unresolved "), &(command_entry_t){
3490 .usage = "allow-unresolved yes|no",
3491 .description = "Allow or disallow unresolved attributes in xlats and references"
3492 }},
3493 { L("attr.children"), &(command_entry_t){
3494 .func = command_attr_children,
3495 .usage = "attr.children",
3496 .description = "Return the children of the named attribute",
3497 }},
3498 { L("attr.flags"), &(command_entry_t){
3499 .func = command_attr_flags,
3500 .usage = "attr.flags",
3501 .description = "Return the flags of the named attribute",
3502 }},
3503 { L("attr.name"), &(command_entry_t){
3504 .func = command_attr_name,
3505 .usage = "attr.name",
3506 .description = "Return the number of the named attribute",
3507 }},
3508#if 0
3509 { L("attr.number"), &(command_entry_t){
3510 .func = command_attr_number,
3511 .usage = "attr.number",
3512 .description = "Return the number of the named attribute",
3513 }},
3514#endif
3515 { L("attr.oid"), &(command_entry_t){
3516 .func = command_attr_oid,
3517 .usage = "attr.oid",
3518 .description = "Return the OID of the named attribute",
3519 }},
3520#if 0
3521 { L("attr.ref"), &(command_entry_t){
3522 .func = command_attr_ref,
3523 .usage = "attr.ref",
3524 .description = "Return the reference (if any) of the named attribute",
3525 }},
3526#endif
3527 { L("attr.type"), &(command_entry_t){
3528 .func = command_attr_type,
3529 .usage = "attr.type",
3530 .description = "Return the data type of the named attribute",
3531 }},
3532 { L("calc "), &(command_entry_t){
3533 .func = command_calc,
3534 .usage = "calc <type1> <value1> <operator> <type2> <value2> -> <output-type>",
3535 .description = "Perform calculations on value boxes",
3536 }},
3537 { L("calc_nary "), &(command_entry_t){
3538 .func = command_calc_nary,
3539 .usage = "calc_nary op <type1> <value1> <type2> <value2> ... -> <output-type>",
3540 .description = "Perform calculations on value boxes",
3541 }},
3542 { L("cast "), &(command_entry_t){
3543 .func = command_cast,
3544 .usage = "cast (type) <value> -> <output-type>",
3545 .description = "Perform calculations on value boxes",
3546 }},
3547 { L("cd "), &(command_entry_t){
3548 .func = command_cd,
3549 .usage = "cd <path>",
3550 .description = "Change the directory for loading dictionaries and $INCLUDEs, writing the full path into the data buffer on success"
3551 }},
3552 { L("clear"), &(command_entry_t){
3553 .func = command_clear,
3554 .usage = "clear",
3555 .description = "Explicitly zero out the contents of the data buffer"
3556 }},
3557 { L("command add "), &(command_entry_t){
3558 .func = command_radmin_add,
3559 .usage = "command add <string>",
3560 .description = "Add a command to a radmin command tree"
3561 }},
3562 { L("command tab "), &(command_entry_t){
3563 .func = command_radmin_tab,
3564 .usage = "command tab <string>",
3565 .description = "Test a tab completion against a radmin command tree"
3566 }},
3567 { L("condition "), &(command_entry_t){
3569 .usage = "condition <string>",
3570 .description = "Parse and reprint a condition, writing the normalised condition to the data buffer on success"
3571 }},
3572 { L("count"), &(command_entry_t){
3573 .func = command_count,
3574 .usage = "count",
3575 .description = "Write the number of executed tests to the data buffer. A test is any command that should return 'ok'"
3576 }},
3577 { L("decode-dns-label "), &(command_entry_t){
3579 .usage = "decode-dns-label (-|<hex_string>)",
3580 .description = "Decode one or more DNS labels, writing the decoded strings to the data buffer.",
3581 }},
3582 { L("decode-pair"), &(command_entry_t){
3583 .func = command_decode_pair,
3584 .usage = "decode-pair[.<testpoint_symbol>] (-|<hex_string>)",
3585 .description = "Produce an attribute value pair from a binary value using a specified protocol decoder. Protocol must be loaded with \"load <protocol>\" first",
3586 }},
3587 { L("decode-proto"), &(command_entry_t){
3588 .func = command_decode_proto,
3589 .usage = "decode-proto[.<testpoint_symbol>] (-|<hex string>)",
3590 .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",
3591 }},
3592 { L("dictionary "), &(command_entry_t){
3594 .usage = "dictionary <string>",
3595 .description = "Parse dictionary attribute definition, writing \"ok\" to the data buffer if successful",
3596 }},
3597 { L("dictionary-dump"), &(command_entry_t){
3599 .usage = "dictionary-dump",
3600 .description = "Print the contents of the currently active dictionary to stdout",
3601 }},
3602 { L("dictionary-read "), &(command_entry_t){
3604 .usage = "dictionary-read <filename>",
3605 .description = "Load the named dictionary file, writing \"ok\" to the data buffer if successful",
3606 }},
3607 { L("encode-dns-label "), &(command_entry_t){
3609 .usage = "encode-dns-label (-|string[,string])",
3610 .description = "Encode one or more DNS labels, writing a hex string to the data buffer.",
3611 }},
3612 { L("encode-pair"), &(command_entry_t){
3613 .func = command_encode_pair,
3614 .usage = "encode-pair[.<testpoint_symbol>] [truncate] (-|<attribute> = <value>[,<attribute = <value>])",
3615 .description = "Encode one or more attribute value pairs, writing a hex string to the data buffer. Protocol must be loaded with \"load <protocol>\" first",
3616 }},
3617 { L("encode-proto"), &(command_entry_t){
3618 .func = command_encode_proto,
3619 .usage = "encode-proto[.<testpoint_symbol>] (-|<attribute> = <value>[,<attribute = <value>])",
3620 .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"
3621 }},
3622 { L("eof"), &(command_entry_t){
3623 .func = command_eof,
3624 .usage = "eof",
3625 .description = "Mark the end of a 'virtual' file. Used to prevent 'need-feature' skipping all the content of a command stream or file",
3626 }},
3627 { L("exit"), &(command_entry_t){
3628 .func = command_exit,
3629 .usage = "exit[ <num>]",
3630 .description = "Exit with the specified error number. If no <num> is provided, process will exit with 0"
3631 }},
3632 { L("fuzzer-out"), &(command_entry_t){
3633 .func = command_fuzzer_out,
3634 .usage = "fuzzer-out <dir>",
3635 .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",
3636 }},
3637 { L("load-dictionary "),&(command_entry_t){
3639 .usage = "load-dictionary <name> [<dir>]",
3640 .description = "Load an additional dictionary from the same directory as the input file. "
3641 "Optionally you can specify a full path via <dir>. ",
3642 }},
3643 { L("match"), &(command_entry_t){
3644 .func = command_match,
3645 .usage = "match <string>",
3646 .description = "Compare the contents of the data buffer with an expected value"
3647 }},
3648 { L("match-regex "), &(command_entry_t){
3649 .func = command_match_regex,
3650 .usage = "match-regex <regex>",
3651 .description = "Compare the contents of the data buffer with a regular expression"
3652 }},
3653 { L("max-buffer-size"), &(command_entry_t){
3655 .usage = "max-buffer-size[ <integer>]",
3656 .description = "Limit the maximum temporary buffer space available for any command which uses it"
3657 }},
3658 { L("migrate "), &(command_entry_t){
3659 .func = command_migrate,
3660 .usage = "migrate <flag>=<value>",
3661 .description = "Set migration flag"
3662 }},
3663 { L("need-feature "), &(command_entry_t){
3664 .func = command_need_feature,
3665 .usage = "need-feature <feature>",
3666 .description = "Skip the contents of the current file, or up to the next \"eof\" command if a particular feature is not available"
3667 }},
3668 { L("no "), &(command_entry_t){
3669 .func = command_no,
3670 .usage = "no ...",
3671 .description = "Negate the result of a command returning 'ok'"
3672 }},
3673 { L("pair "), &(command_entry_t){
3674 .func = command_pair,
3675 .usage = "pair ... data ...",
3676 .description = "Parse a list of pairs",
3677 }},
3678 { L("pair-compare "), &(command_entry_t){
3679 .func = command_pair_compare,
3680 .usage = "pair-compare ... data ...",
3681 .description = "Parse a list of pairs, allowing comparison operators",
3682 }},
3683 { L("proto "), &(command_entry_t){
3684 .func = command_proto,
3685 .usage = "proto <protocol>",
3686 .description = "Switch the active protocol to the one specified, unloading the previous protocol",
3687 }},
3688 { L("proto-dictionary "),&(command_entry_t){
3690 .usage = "proto-dictionary <proto_name> [<proto_dir>]",
3691 .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.",
3692 }},
3693
3694
3695 { L("proto-dictionary-root "), &(command_entry_t){
3697 .usage = "proto-dictionary-root[ <root_attribute>]",
3698 .description = "Set the root attribute for the current protocol dictionary. "
3699 "If no attribute name is provided, the root will be reset to the root of the current dictionary",
3700 }},
3701 { L("raw "), &(command_entry_t){
3702 .func = command_encode_raw,
3703 .usage = "raw <string>",
3704 .description = "Create nested attributes from OID strings and values"
3705 }},
3706 { L("read_file "), &(command_entry_t){
3707 .func = command_read_file,
3708 .usage = "read_file <filename>",
3709 .description = "Read a list of pairs from a file",
3710 }},
3711 { L("returned"), &(command_entry_t){
3712 .func = command_returned,
3713 .usage = "returned",
3714 .description = "Print the returned value to the data buffer"
3715 }},
3716
3717 { L("tmpl "), &(command_entry_t){
3718 .func = command_tmpl,
3719 .usage = "parse <string>",
3720 .description = "Parse then print a tmpl expansion, writing the normalised tmpl expansion to the data buffer"
3721 }},
3722
3723 { L("tmpl-rules "), &(command_entry_t){
3724 .func = command_tmpl_rules,
3725 .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]",
3726 .description = "Alter the tmpl parsing rules for subsequent tmpl parsing commands in the same command context"
3727 }},
3728 { L("touch "), &(command_entry_t){
3729 .func = command_touch,
3730 .usage = "touch <file>",
3731 .description = "Touch a file, updating its created timestamp. Useful for marking the completion of a series of tests"
3732 }},
3733 { L("value "), &(command_entry_t){
3735 .usage = "value <type> <string>",
3736 .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"
3737 }},
3738 { L("write "), &(command_entry_t){
3739 .func = command_write,
3740 .usage = "write <file>",
3741 .description = "Write the contents of the data buffer (as a raw binary string) to the specified file"
3742 }},
3743 { L("xlat "), &(command_entry_t){
3744 .func = command_xlat_normalise,
3745 .usage = "xlat <string>",
3746 .description = "Parse then print an xlat expansion, writing the normalised xlat expansion to the data buffer"
3747 }},
3748
3749 { L("xlat_argv "), &(command_entry_t){
3750 .func = command_xlat_argv,
3751 .usage = "xlat_argv <string>",
3752 .description = "Parse then print an xlat expansion argv, writing the normalised xlat expansion arguments to the data buffer"
3753 }},
3754
3755 { L("xlat_expr "), &(command_entry_t){
3756 .func = command_xlat_expr,
3757 .usage = "xlat_expr <string>",
3758 .description = "Parse then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3759 }},
3760
3761 { L("xlat_purify "), &(command_entry_t){
3762 .func = command_xlat_purify,
3763 .usage = "xlat_purify <string>",
3764 .description = "Parse, purify, then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3765 }},
3766
3767 { L("xlat_purify_cond "), &(command_entry_t){
3769 .usage = "xlat_purify_cond <string>",
3770 .description = "Parse, purify, then print an xlat condition, writing the normalised xlat expansion to the data buffer"
3771 }},
3772
3773};
3775
3776size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used,
3777 char *in, UNUSED size_t inlen)
3778{
3779
3780 command_entry_t *command;
3781 size_t match_len;
3782 char *p;
3783
3784 p = in;
3786
3787 /*
3788 * Skip empty lines and comments.
3789 */
3790 if (!*p || (*p == '#')) {
3791 /*
3792 * Dump the input to the output.
3793 */
3794 if (write_fp) {
3795 fputs(in, write_fp);
3796 fputs("\n", write_fp);
3797 }
3798
3799 RETURN_NOOP(data_used);
3800 }
3801
3802 DEBUG2("%s[%d]: %s", cc->filename, cc->lineno, p);
3803
3804 /*
3805 * Look up the command by longest prefix
3806 */
3807 command = fr_table_value_by_longest_prefix(&match_len, commands, p, -1, NULL);
3808 if (!command) {
3809 fr_strerror_printf("Unknown command: %s", p);
3811 }
3812
3813 p += match_len; /* Jump to after the command */
3814 fr_skip_whitespace(p); /* Skip any whitespace */
3815
3816 /*
3817 * Feed the data buffer in as the command
3818 */
3819 if ((p[0] == '-') && ((p[1] == ' ') || (p[1] == '\0'))) {
3820 data_used = command->func(result, cc, data, data_used, data, data_used);
3821 }
3822 else {
3823 data_used = command->func(result, cc, data, data_used, p, strlen(p));
3824 }
3825
3826 /*
3827 * Dump the contents of the error stack
3828 * to the data buffer.
3829 *
3830 * This is then what's checked in
3831 * subsequent match commands.
3832 */
3833 if (result->error_to_data) data_used = strerror_concat(data, COMMAND_OUTPUT_MAX);
3834
3835 fr_assert((size_t)data_used < COMMAND_OUTPUT_MAX);
3836 data[data_used] = '\0'; /* Ensure the data buffer is \0 terminated */
3837
3838 if (data_used) {
3839 DEBUG2("%s[%d]: --> %s (%zu bytes in buffer)", cc->filename, cc->lineno,
3840 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"), data_used);
3841 } else {
3842 DEBUG2("%s[%d]: --> %s", cc->filename, cc->lineno,
3843 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"));
3844 }
3845
3846 /*
3847 * Dump the input to the output.
3848 */
3849 if (write_fp) {
3850 fputs(in, write_fp);
3851 fputs("\n", write_fp);
3852 };
3853
3854 talloc_free_children(cc->tmp_ctx);
3855
3856 return data_used;
3857}
3858
3860{
3861 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3862 fr_perror("unit_test_attribute");
3863 return -1;
3864 }
3865 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) {
3866 fr_perror("unit_test_attribute");
3867 return -1;
3868 }
3869 if (cc->fuzzer_fd >= 0) {
3870 close(cc->fuzzer_fd);
3871 cc->fuzzer_fd = -1;
3872 }
3873 return 0;
3874}
3875
3877 command_config_t const *config, char const *path, char const *filename)
3878{
3880
3881 cc = talloc_zero(ctx, command_file_ctx_t);
3882 talloc_set_destructor(cc, _command_ctx_free);
3883
3884 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3885 cc->path = talloc_strdup(cc, path);
3886 cc->filename = filename;
3887 cc->config = config;
3888
3889 /*
3890 * Allocate a special buffer with poisoned regions
3891 * at either end.
3892 */
3894 talloc_free(cc);
3895 return NULL;
3896 }
3899
3900 /*
3901 * Initialise a special temporary dictionary context
3902 *
3903 * Any protocol dictionaries loaded by "test-dictionary"
3904 * go in this context, and don't affect the main
3905 * dictionary context.
3906 */
3907 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3908 if (!cc->test_gctx) {
3909 fr_perror("Failed allocating test dict_gctx");
3910 return NULL;
3911 }
3912
3915 fr_perror("Failed loading test dict_gctx internal dictionary");
3916 return NULL;
3917 }
3918
3919 fr_dict_global_ctx_dir_set(cc->path); /* Load new dictionaries relative to the test file */
3921
3922 cc->fuzzer_fd = -1;
3923
3925 cc->tmpl_rules.attr.namespace = fr_dict_root(cc->config->dict);
3926 cc->tmpl_rules.attr.allow_unresolved = false; /* tests have to use real attributes */
3927
3928 return cc;
3929}
3930
3931static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
3932{
3933 talloc_free(cc->tmp_ctx);
3934 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3935 cc->test_count = 0;
3936
3937 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3938 fr_perror("unit_test_attribute");
3939 }
3940
3941 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) fr_perror("unit_test_attribute");
3942
3943 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3945 fr_perror("Failed loading test dict_gctx internal dictionary");
3946 }
3947
3948 if (cc->fuzzer_fd >= 0) {
3949 close(cc->fuzzer_fd);
3950 cc->fuzzer_fd = -1;
3951 }
3952}
3953
3954static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config,
3955 const char *root_dir, char const *filename, fr_dlist_head_t *lines)
3956{
3957 int ret = 0;
3958 FILE *fp; /* File we're reading from */
3959 char buffer[8192]; /* Command buffer */
3960 char data[COMMAND_OUTPUT_MAX + 1]; /* Data written by previous command */
3961 ssize_t data_used = 0; /* How much data the last command wrote */
3962 static char path[PATH_MAX] = "";
3963 command_line_range_t *lr = NULL;
3964 bool opened_fp = false;
3965
3967
3968 cc = command_ctx_alloc(ctx, config, root_dir, filename);
3969
3970 /*
3971 * Open the file, or stdin
3972 */
3973 if (strcmp(filename, "-") == 0) {
3974 fp = stdin;
3975 filename = "<stdin>";
3976 fr_assert(!root_dir);
3977
3978 } else {
3979 if (root_dir && *root_dir) {
3980 snprintf(path, sizeof(path), "%s/%s", root_dir, filename);
3981 } else {
3982 strlcpy(path, filename, sizeof(path));
3983 }
3984
3985 fp = fopen(path, "r");
3986 if (!fp) {
3987 ERROR("Error opening test file \"%s\": %s", path, fr_syserror(errno));
3988 ret = -1;
3989 goto finish;
3990 }
3991
3992 filename = path;
3993 opened_fp = true;
3994 }
3995
3996 if (lines && !fr_dlist_empty(lines)) lr = fr_dlist_head(lines);
3997
3998 /*
3999 * Loop over lines in the file or stdin
4000 */
4001 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
4002 command_result_t result = { .rcode = RESULT_OK }; /* Reset to OK */
4003 char *p = strchr(buffer, '\n');
4004
4006 cc->lineno++; /* The first line of the file becomes line 1 */
4007
4008 if (lr) {
4009 if (cc->lineno > lr->end) {
4010 lr = fr_dlist_next(lines, lr);
4011 if (!lr) goto finish;
4012 }
4013
4014 if (cc->lineno < lr->start) continue;
4015 }
4016
4017 if (!p) {
4018 if (!feof(fp)) {
4019 ERROR("Line %d too long in %s/%s", cc->lineno, cc->path, cc->filename);
4020 ret = -1;
4021 goto finish;
4022 }
4023 } else {
4024 *p = '\0';
4025 }
4026
4027 data_used = process_line(&result, cc, data, data_used, buffer, strlen(buffer));
4028 switch (result.rcode) {
4029 /*
4030 * Command completed successfully
4031 */
4032 case RESULT_OK:
4033 cc->test_count++;
4034 continue;
4035
4036 /*
4037 * Did nothing (not a test)
4038 */
4039 case RESULT_NOOP:
4040 continue;
4041
4042 /*
4043 * If this is a file, then break out of the loop
4044 * and cleanup, otherwise we need to find the
4045 * EOF marker in the input stream.
4046 */
4047 case RESULT_SKIP_FILE:
4048 if (fp != stdin) goto finish;
4049
4050 /*
4051 * Skip over the input stream until we
4052 * find an eof command, or the stream
4053 * is closed.
4054 */
4055 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
4056 command_entry_t *command;
4057 size_t match_len;
4058
4059 command = fr_table_value_by_longest_prefix(&match_len, commands, buffer, -1, NULL);
4060 if (!command) {
4061 ERROR("%s[%d]: Unknown command: %s", cc->path, cc->lineno, p);
4062 ret = -1;
4063 goto finish;
4064 }
4065
4066 if (command->func == command_eof) {
4067 command_ctx_reset(cc, ctx);
4068 break;
4069 }
4070 }
4071 goto finish;
4072
4073 /*
4074 * Fatal error parsing a command
4075 */
4076 case RESULT_PARSE_ERROR:
4078 fr_perror("%s[%d]", filename, cc->lineno);
4079 ret = -1;
4080 goto finish;
4081
4082 /*
4083 * Result didn't match what we expected
4084 */
4085 case RESULT_MISMATCH:
4086 {
4087 ret = EXIT_FAILURE;
4088 goto finish;
4089 }
4090
4091 case RESULT_EXIT:
4092 ret = result.ret;
4093 *exit_now = true;
4094 goto finish;
4095
4096 default:
4097 /*
4098 * If this happens, fix the damn command.
4099 */
4100 fr_assert_msg(false, "Command exited with invalid return code (%i)", result.rcode);
4101 ret = -1;
4102 goto finish;
4103 }
4104 }
4105
4106finish:
4107 /* The explicit check is to quiet clang_analyzer */
4108 if (opened_fp) fclose(fp);
4109
4110 /*
4111 * Free any residual resources we loaded.
4112 */
4113 if (cc && (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0)) {
4114 fr_perror("unit_test_attribute");
4115 ret = -1;
4116 }
4117
4118 if ((ret == 0) && !cc->test_count) {
4119 ERROR("Empty input file is invalid");
4120 ret = -1;
4121 }
4122
4123 fr_dict_global_ctx_set(config->dict_gctx); /* Switch back to the main dict ctx */
4125 talloc_free(cc);
4126
4127 return ret;
4128}
4129
4130static void usage(char const *name)
4131{
4132 INFO("usage: %s [options] (-|<filename>[:<lines>] [ <filename>[:<lines>]])", name);
4133 INFO("options:");
4134 INFO(" -d <confdir> Set user dictionary path (defaults to " CONFDIR ").");
4135 INFO(" -D <dictdir> Set main dictionary path (defaults to " DICTDIR ").");
4136 INFO(" -x Debugging mode.");
4137 INFO(" -f Print features.");
4138 INFO(" -c Print commands.");
4139 INFO(" -h Print help text.");
4140 INFO(" -M Show talloc memory report.");
4141 INFO(" -p Allow xlat_purify");
4142 INFO(" -o <receipt_file> Create the <receipt_file> as a 'success' exit.");
4143 INFO(" -w <output_file> Write 'corrected' output to <output_file>.");
4144 INFO("Where <filename> is a file containing one or more commands and '-' indicates commands should be read from stdin.");
4145 INFO("Ranges of <lines> may be specified in the format <start>[-[<end>]][,]");
4146}
4147
4148static void features_print(CONF_SECTION *features)
4149{
4150 CONF_PAIR *cp;
4151
4152 INFO("features:");
4153 for (cp = cf_pair_find(features, CF_IDENT_ANY);
4154 cp;
4155 cp = cf_pair_find_next(features, cp, CF_IDENT_ANY)) {
4156 INFO(" %s %s", cf_pair_attr(cp), cf_pair_value(cp));
4157 }
4158}
4159
4160static void commands_print(void)
4161{
4162 size_t i;
4163
4164 INFO("commands:");
4165 for (i = 0; i < commands_len; i++) {
4166 INFO(" %s:", ((command_entry_t const *)commands[i].value)->usage);
4167 INFO(" %s.", ((command_entry_t const *)commands[i].value)->description);
4168 INFO("%s", "");
4169 }
4170}
4171
4172static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *in)
4173{
4174 static bool tokens[SBUFF_CHAR_CLASS] = { [','] = true , ['-'] = true };
4175 uint32_t max = 0;
4178
4179 while (fr_sbuff_extend(in)) {
4180 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4181
4182 MEM(lr = talloc_zero(ctx, command_line_range_t));
4184
4185 fr_sbuff_out(&err, &lr->start, in);
4186 if (err != FR_SBUFF_PARSE_OK) {
4187 ERROR("Invalid line start number");
4188 error:
4190 return -1;
4191 }
4192 if (max > lr->start) {
4193 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->start);
4194 goto error;
4195 } else {
4196 max = lr->start;
4197 }
4198 lr->end = lr->start; /* Default to a single line */
4199 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4200
4201 again:
4202 if (!fr_sbuff_extend(in)) break;
4203 if (!fr_sbuff_is_in_charset(in, tokens)) {
4204 ERROR("Unexpected text \"%pV\"",
4206 goto error;
4207 }
4208
4209 fr_sbuff_switch(in, '\0') {
4210 /*
4211 * More ranges...
4212 */
4213 case ',':
4214 fr_sbuff_next(in);
4215 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4216 continue;
4217
4218 /*
4219 * <start>-<end>
4220 */
4221 case '-':
4222 {
4223 fr_sbuff_next(in);
4224 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4225
4226 /*
4227 * A bare '-' with no number means
4228 * run all remaining lines.
4229 */
4230 if (fr_sbuff_extend(in) == 0) {
4231 lr->end = UINT32_MAX;
4232 return 0;
4233 }
4234
4235 fr_sbuff_out(&err, &lr->end, in);
4236 if (err != FR_SBUFF_PARSE_OK) {
4237 ERROR("Invalid line end number");
4238 goto error;
4239 }
4240 if (lr->end < lr->start) {
4241 ERROR("Line end must be >= line start (%u < %u)", lr->end, lr->start);
4242 goto error;
4243 }
4244 if (max > lr->end) {
4245 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->end);
4246 goto error;
4247 } else {
4248 max = lr->end;
4249 }
4250 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4251 }
4252 goto again;
4253 }
4254 }
4255
4256 return 0;
4257}
4258
4259static int process_path(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config, const char *path)
4260{
4261 char *p, *dir = NULL, *file;
4262 int ret = EXIT_SUCCESS;
4263 fr_sbuff_t in = FR_SBUFF_IN(path, strlen(path));
4265 L("/"),
4266 L(":")
4267 );
4268 fr_sbuff_marker_t file_start, file_end, dir_end;
4269 fr_dlist_head_t lines;
4270
4271 fr_sbuff_marker(&file_start, &in);
4272 fr_sbuff_marker(&file_end, &in);
4273 fr_sbuff_marker(&dir_end, &in);
4274 fr_sbuff_set(&file_end, fr_sbuff_end(&in));
4275
4276 fr_dlist_init(&lines, command_line_range_t, entry);
4277
4278 while (fr_sbuff_extend(&in)) {
4279 fr_sbuff_adv_until(&in, SIZE_MAX, &dir_sep, '\0');
4280
4281 fr_sbuff_switch(&in, '\0') {
4282 case '/':
4283 fr_sbuff_set(&dir_end, &in);
4284 fr_sbuff_advance(&in, 1);
4285 fr_sbuff_set(&file_start, &in);
4286 break;
4287
4288 case ':':
4289 fr_sbuff_set(&file_end, &in);
4290 fr_sbuff_advance(&in, 1);
4291 if (line_ranges_parse(ctx, &lines, &in) < 0) {
4292 return EXIT_FAILURE;
4293 }
4294 break;
4295
4296 default:
4297 fr_sbuff_set(&file_end, &in);
4298 break;
4299 }
4300 }
4301
4302 file = talloc_bstrndup(ctx,
4303 fr_sbuff_current(&file_start), fr_sbuff_diff(&file_end, &file_start));
4304 if (fr_sbuff_used(&dir_end)) dir = talloc_bstrndup(ctx,
4306 fr_sbuff_used(&dir_end));
4307
4308 /*
4309 * Do things so that GNU Make does less work.
4310 */
4311 if ((receipt_dir || receipt_file) &&
4312 (strncmp(path, "src/tests/unit/", 15) == 0)) {
4313 p = UNCONST(char *, strchr(path + 15, '/'));
4314 if (!p) {
4315 printf("UNIT-TEST %s\n", path + 15);
4316 } else {
4317 char *q = UNCONST(char *, strchr(p + 1, '/'));
4318
4319 *p = '\0';
4320
4321 if (!q) {
4322 printf("UNIT-TEST %s - %s\n", path + 15, p + 1);
4323 } else {
4324 *q = '\0';
4325
4326 printf("UNIT-TEST %s - %s\n", p + 1, q + 1);
4327 *q = '/';
4328 }
4329
4330 *p = '/';
4331 }
4332 }
4333
4334 /*
4335 * Rewrite this file if requested.
4336 */
4337 if (write_filename) {
4338 write_fp = fopen(write_filename, "w");
4339 if (!write_fp) {
4340 ERROR("Failed opening %s: %s", write_filename, strerror(errno));
4341 return EXIT_FAILURE;
4342 }
4343 }
4344
4345 ret = process_file(exit_now, ctx, config, dir, file, &lines);
4346
4347 if ((ret == EXIT_SUCCESS) && receipt_dir && dir) {
4348 char *touch_file, *subdir;
4349
4350 if (strncmp(dir, "src/", 4) == 0) {
4351 subdir = dir + 4;
4352 } else {
4353 subdir = dir;
4354 }
4355
4356 touch_file = talloc_asprintf(ctx, "build/%s/%s", subdir, file);
4357 fr_assert(touch_file);
4358
4359 p = strchr(touch_file, '/');
4360 fr_assert(p);
4361
4362 if (fr_mkdir(NULL, touch_file, (size_t) (p - touch_file), S_IRWXU, NULL, NULL) < 0) {
4363 fr_perror("unit_test_attribute - failed to make directory %.*s - ",
4364 (int) (p - touch_file), touch_file);
4365fail:
4366 if (write_fp) fclose(write_fp);
4367 return EXIT_FAILURE;
4368 }
4369
4370 if (fr_touch(NULL, touch_file, 0644, true, 0755) <= 0) {
4371 fr_perror("unit_test_attribute - failed to create receipt file %s - ",
4372 touch_file);
4373 goto fail;
4374 }
4375
4376 talloc_free(touch_file);
4377 }
4378
4379 talloc_free(dir);
4381 fr_dlist_talloc_free(&lines);
4382
4383 if (ret != EXIT_SUCCESS) {
4384 if (write_fp) {
4385 fclose(write_fp);
4386 write_fp = NULL;
4387 }
4388 fail_file = path;
4389 }
4390
4391 if (write_fp) {
4392 fclose(write_fp);
4393 if (rename(write_filename, path) < 0) {
4394 ERROR("Failed renaming %s: %s", write_filename, strerror(errno));
4395 return EXIT_FAILURE;
4396 }
4397 }
4398
4399 return ret;
4400}
4401
4402/**
4403 *
4404 * @hidecallgraph
4405 */
4406int main(int argc, char *argv[])
4407{
4408 int c;
4409 CONF_SECTION *cs;
4410 int ret = EXIT_SUCCESS;
4411 TALLOC_CTX *autofree;
4412 TALLOC_CTX *thread_ctx;
4413 bool exit_now = false;
4414
4416 .confdir = CONFDIR,
4417 .dict_dir = DICTDIR
4418 };
4419
4420 char const *name;
4421 bool do_features = false;
4422 bool do_commands = false;
4423 bool do_usage = false;
4424 xlat_t *xlat;
4425 char *p;
4426 char const *error_str = NULL, *fail_str = NULL;
4427
4428 /*
4429 * Must be called first, so the handler is called last
4430 */
4432
4434 thread_ctx = talloc_new(autofree);
4435
4436 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0], PANIC_ACTION_SIGNALS) < 0) {
4437 fr_perror("unit_test_attribute");
4438 goto cleanup;
4439 }
4440#ifdef NDEBUG
4442#endif
4443
4444 /*
4445 * Sync wallclock and cpu time so that we can find
4446 * uses of fr_time_[to|from]_* where
4447 * fr_unix_time_[to|from]_* should be used.
4448 *
4449 * If the wallclock/cpu offset is 0, then both sets
4450 * of macros produce the same result.
4451 */
4452 fr_time_start();
4453
4454 /*
4455 * Allocate a root config section so we can write
4456 * out features and versions.
4457 */
4458 MEM(cs = cf_section_alloc(autofree, NULL, "unit_test_attribute", NULL));
4459 MEM(config.features = cf_section_alloc(cs, cs, "feature", NULL));
4460 dependency_features_init(config.features); /* Add build time features to the config section */
4461
4462 name = argv[0];
4463
4465 default_log.fd = STDOUT_FILENO;
4466 default_log.print_level = false;
4467
4468 while ((c = getopt(argc, argv, "cd:D:F:fxMhpo:S:w:")) != -1) switch (c) {
4469 case 'c':
4470 do_commands = true;
4471 break;
4472
4473 case 'd':
4474 config.confdir = optarg;
4475 break;
4476
4477 case 'D':
4478 config.dict_dir = optarg;
4479 break;
4480
4481 case 'F':
4482 config.fuzzer_base_dir = optarg;
4483 break;
4484
4485 case 'f':
4486 do_features = true;
4487 break;
4488
4489 case 'x':
4490 fr_debug_lvl++;
4491 if (fr_debug_lvl > 2) default_log.print_level = true;
4492 break;
4493
4494 case 'M':
4495 talloc_enable_leak_report();
4496 break;
4497
4498 case 'o':
4499 p = strrchr(optarg, '/');
4500 if (!p || p[1]) {
4501 receipt_file = optarg;
4502
4503 if ((fr_unlink(receipt_file) < 0)) {
4504 fr_perror("unit_test_attribute");
4506 }
4507
4508 } else {
4509 receipt_dir = optarg;
4510 }
4511 break;
4512
4513 case 'p':
4514 allow_purify = true;
4515 break;
4516
4517 case 'S':
4518 fprintf(stderr, "Invalid option to -S\n");
4520
4521 case 'w':
4522 write_filename = optarg;
4523 break;
4524
4525 case 'h':
4526 default:
4527 do_usage = true; /* Just set a flag, so we can process extra -x args */
4528 break;
4529 }
4530 argc -= (optind - 1);
4531 argv += (optind - 1);
4532
4533 if (do_usage) usage(name);
4534 if (do_features) features_print(config.features);
4535 if (do_commands) commands_print();
4536 if (do_usage || do_features || do_commands) {
4537 ret = EXIT_SUCCESS;
4538 goto cleanup;
4539 }
4540
4541 /*
4542 * Mismatch between the binary and the libraries it depends on
4543 */
4545 fr_perror("unit_test_attribute");
4547 }
4548
4549#ifdef WITH_TLS
4550 /*
4551 * OpenSSL can only be initialised once during the lifetime
4552 * of a process. Initialise it here so that we don't attempt
4553 * to unload and load it multiple times.
4554 */
4555 if (fr_openssl_init() < 0) {
4556 fr_perror("unit_test_attribute");
4558 }
4559#endif
4560
4561 modules_init(NULL);
4562
4563 dl_loader = dl_loader_init(autofree, NULL, false, false);
4564 if (!dl_loader) {
4565 fr_perror("unit_test_attribute");
4567 }
4568
4569 config.dict_gctx = fr_dict_global_ctx_init(NULL, true, config.dict_dir);
4570 if (!config.dict_gctx) {
4571 fr_perror("unit_test_attribute");
4573 }
4574
4576 fr_perror("unit_test_attribute");
4578 }
4579
4580 /*
4581 * Initialize the internal attributes needed by the tmpls.
4582 */
4583 if (tmpl_global_init() < 0) {
4584 fr_perror("unit_test_attribute");
4586 }
4587
4588 /*
4589 * Always needed so we can load the list attributes
4590 * otherwise the tmpl_tokenize code fails.
4591 */
4592 if (request_global_init() < 0) {
4593 fr_perror("unit_test_attribute");
4595 }
4596
4597 /*
4598 * Initialise the interpreter, registering operations.
4599 * Needed because some keywords also register xlats.
4600 */
4601 if (unlang_global_init() < 0) {
4602 fr_perror("unit_test_attribute");
4604 }
4605
4606 /*
4607 * Create a dummy event list
4608 */
4609 if (allow_purify) {
4610 el = fr_event_list_alloc(autofree, NULL, NULL);
4611 fr_assert(el != NULL);
4612
4613 /*
4614 * Simulate thread specific instantiation
4615 */
4617 if (xlat_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
4618 }
4619
4620 unlang_thread_instantiate(thread_ctx);
4621
4622 xlat = xlat_func_register(NULL, "test", xlat_test, FR_TYPE_NULL);
4623 if (!xlat) {
4624 ERROR("Failed registering xlat");
4626 }
4628
4629 /*
4630 * And again WITHOUT arguments.
4631 */
4632 xlat = xlat_func_register(NULL, "test_no_args", xlat_test, FR_TYPE_NULL);
4633 if (!xlat) {
4634 ERROR("Failed registering xlat");
4636 }
4638
4639 /*
4640 * Disable hostname lookups, so we don't produce spurious DNS
4641 * queries, and there's no chance of spurious failures if
4642 * it takes a long time to get a response.
4643 */
4645
4646 /*
4647 * Read test commands from stdin
4648 */
4649 if ((argc < 2) && !receipt_dir) {
4650 if (write_filename) {
4651 ERROR("Can only use '-w' with input files");
4653 }
4654
4655 ret = process_file(&exit_now, autofree, &config, NULL, "-", NULL);
4656
4657 } else if ((argc == 2) && (strcmp(argv[1], "-") == 0)) {
4658 char buffer[1024];
4659
4660 /*
4661 * Read the list of filenames from stdin.
4662 */
4663 while (fgets(buffer, sizeof(buffer) - 1, stdin) != NULL) {
4664 buffer[sizeof(buffer) - 1] = '\0';
4665
4666 p = buffer;
4667 while (isspace((unsigned int) *p)) p++;
4668
4669 if (!*p || (*p == '#')) continue;
4670
4671 name = p;
4672
4673 /*
4674 * Smash CR/LF.
4675 *
4676 * Note that we don't care about truncated filenames. The code below
4677 * will complain that it can't open the file.
4678 */
4679 while (*p) {
4680 if (*p < ' ') {
4681 *p = '\0';
4682 break;
4683 }
4684
4685 p++;
4686 }
4687
4688 ret = process_path(&exit_now, autofree, &config, name);
4689 if ((ret != EXIT_SUCCESS) || exit_now) break;
4690 }
4691
4692 } else if (argc > 1) {
4693 int i;
4694
4695 if (receipt_file) for (i = 1; i < argc; i++) {
4696 if (strcmp(receipt_file, argv[i]) == 0) {
4697 ERROR("Receipt file cannot be one of the input files");
4699 }
4700 }
4701
4702 /*
4703 * Read test commands from a list of files in argv[].
4704 */
4705 for (i = 1; i < argc; i++) {
4706 ret = process_path(&exit_now, autofree, &config, argv[i]);
4707 if ((ret != EXIT_SUCCESS) || exit_now) break;
4708 }
4709 } /* nothing to do */
4710
4711 /*
4712 * Try really hard to free any allocated
4713 * memory, so we get clean talloc reports.
4714 */
4715cleanup:
4716#undef EXIT_WITH_FAILURE
4717#define EXIT_WITH_FAILURE \
4718do { \
4719 ret = EXIT_FAILURE; \
4720 error_str = fr_strerror(); \
4721 if (error_str) error_str = talloc_strdup(NULL, error_str); \
4722 goto fail; \
4723} while (0)
4724
4725 /*
4726 * Ensure all thread local memory is cleaned up
4727 * at the appropriate time. This emulates what's
4728 * done with worker/network threads in the
4729 * scheduler.
4730 */
4732
4733#ifdef WITH_TLS
4734 fr_openssl_free();
4735#endif
4736
4737 /*
4738 * dl_loader check needed as talloc_free
4739 * returns -1 on failure.
4740 */
4741 if (dl_loader && (talloc_free(dl_loader) < 0)) {
4742 fail_str = "cleaning up dynamically loaded libraries";
4744 }
4745
4746 if (fr_dict_free(&config.dict, __FILE__) < 0) {
4747 fail_str = "cleaning up dictionaries";
4749 }
4750
4751 if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
4752 fail_str = "creating receipt file";
4754 }
4755
4756 /*
4757 * Explicitly free the autofree context
4758 * to make errors less confusing.
4759 */
4760 if (talloc_free(autofree) < 0) {
4761 fail_str = "cleaning up all memory";
4763 }
4764
4765 if (ret != EXIT_SUCCESS) {
4766 fail:
4767 if (!fail_str) fail_str = "in an input file";
4768 if (!error_str) error_str = "";
4769
4770 fprintf(stderr, "unit_test_attribute failed %s - %s\n", fail_str, error_str);
4771
4772 /*
4773 * Print any command needed to run the test from the command line.
4774 */
4775 p = getenv("UNIT_TEST_ATTRIBUTE");
4776 if (p) printf("%s %s\n", p, fail_file);
4777 }
4778
4779
4780 /*
4781 * Ensure our atexit handlers run before any other
4782 * atexit handlers registered by third party libraries.
4783 */
4785
4786 return ret;
4787}
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:179
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:310
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:236
char const fr_base64_url_alphabet_encode[SBUFF_CHAR_CLASS]
Definition base64.c:173
ssize_t fr_base64_encode_nstd(fr_sbuff_t *out, fr_dbuff_t *in, bool add_padding, char const alphabet[static SBUFF_CHAR_CLASS])
Base 64 encode binary data.
Definition base64.c:326
#define CMD_MAX_ARGV
Definition radmin.c:151
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
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:2351
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:2502
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:1991
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
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:1611
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:1597
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1756
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1740
#define cf_lineno_set(_ci, _lineno)
Definition cf_util.h:186
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:201
#define cf_filename_set(_ci, _filename)
Definition cf_util.h:183
#define CF_IDENT_ANY
Definition cf_util.h:80
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 * help
help text
Definition command.h:55
fr_cmd_func_t func
function to process this command
Definition command.h:56
char const * syntax
e.g. "STRING"
Definition command.h:54
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
char const * name
e.g. "stats"
Definition command.h:53
TALLOC_CTX * autofree
Definition common.c:29
fr_dict_t * dict
Definition common.c:31
fr_dict_attr_t const * root_da
Definition common.c:32
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
Definition debug.c:1034
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1074
#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:243
#define PANIC_ACTION_SIGNALS
Definition debug.h:128
#define MEM(x)
Definition debug.h:38
void dependency_features_init(CONF_SECTION *cs)
Initialise core feature flags.
Definition dependency.c:182
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
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:4797
static fr_slen_t err
Definition dict.h:882
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:3518
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent))
(Re-)Initialize the special internal dictionary
void fr_dict_debug(FILE *fp, fr_dict_t const *dict)
Definition dict_print.c:278
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:2561
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:2637
void fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx)
Set a new, active, global dictionary context.
Definition dict_util.c:4767
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename))
Read supplementary attribute definitions into an existing dictionary.
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4327
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:4311
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4926
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
bool fr_dict_filename_loaded(fr_dict_t const *dict, char const *dict_dir, char const *filename))
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:4783
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:317
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:318
int fr_dict_parse_str(fr_dict_t *dict, char const *str, fr_dict_attr_t const *parent))
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:4711
static fr_slen_t in
Definition dict.h:882
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:501
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:148
int fr_dict_afrom_file(fr_dict_t **out, char const *dir, char const *filename))
Load one dictionary file.
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:907
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:61
Module handle.
Definition dl.h:57
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
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:468
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:892
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:483
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:360
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:537
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:636
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:1224
Definition dwarf.c:424
void * fr_hash_table_iter_next(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Iterate over entries in a hash table.
Definition hash.c:668
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:723
Stores the state of the current iteration operation.
Definition hash.h:41
talloc_free(hp)
bool fr_hostname_lookups
hostname -> IP lookups?
Definition inet.c:52
bool fr_reverse_lookups
IP -> hostname lookups?
Definition inet.c:51
int unlang_global_init(void)
Definition base.c:158
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:2533
Stores all information relating to an event list.
Definition event.c:377
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:218
int fr_unlink(char const *filename)
Remove a regular file from the filesystem.
Definition file.c:366
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:322
char * fr_realpath(TALLOC_CTX *ctx, char const *path, ssize_t len)
Convenience wrapper around realpath.
Definition file.c:283
int fr_debug_lvl
Definition log.c:41
FILE * fr_log_fp
Definition log.c:40
fr_log_t default_log
Definition log.c:306
@ L_DST_STDOUT
Log to stdout.
Definition log.h:75
#define ASAN_POISON_MEMORY_REGION(_start, _size)
Definition lsan.h:62
#define ASAN_UNPOISON_MEMORY_REGION(_start, _size)
Definition lsan.h:63
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
ssize_t fr_dict_attr_flags_print(fr_sbuff_t *out, fr_dict_t const *dict, fr_type_t type, fr_dict_attr_flags_t const *flags)
long int ssize_t
ssize_t fr_dict_attr_oid_print(fr_sbuff_t *out, fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da, bool numeric)
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
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:86
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:290
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, bool allow_exec)
Read valuepairs from the fp up to End-Of-File.
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:162
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:37
#define DEBUG2(fmt,...)
static bool done
Definition radclient.c:80
#define INFO(fmt,...)
Definition radict.c:63
static const char * spaces
Definition radict.c:177
static bool cleanup
Definition radsniff.c:59
fr_dict_attr_t const * request_attr_request
Definition request.c:43
int request_global_init(void)
Definition request.c:598
static char const * name
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1471
size_t fr_sbuff_trim(fr_sbuff_t *sbuff, bool const to_trim[static SBUFF_CHAR_CLASS])
Trim trailing characters from a string we're composing.
Definition sbuff.c:2218
fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in)
See if the string contains a truth value.
Definition sbuff.c:1126
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:1942
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1611
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:2178
#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 SBUFF_CHAR_CLASS
Definition sbuff.h:203
#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:190
#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.
int tmpl_global_init(void)
Definition tmpl_eval.c:1383
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition tmpl.h:340
bool new_functions
new function syntax
Definition tmpl.h:330
static fr_slen_t vpt
Definition tmpl.h:1267
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:339
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:328
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
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:37
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:36
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:1952
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:94
int fd
File descriptor to write messages to.
Definition log.h:109
bool print_level
sometimes we don't want log levels printed
Definition log.h:103
unsigned int allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:303
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
unsigned int allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:314
unsigned int allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:306
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:764
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
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:618
#define talloc_asprintf
Definition talloc.h:151
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:55
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:86
fr_tp_proto_decode_t func
Decoder for proto layer.
Definition test_point.h:69
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:94
fr_dcursor_iter_t next_encodable
Iterator to use to select attributes to encode.
Definition test_point.h:96
fr_tp_proto_encode_t func
Encoder for proto layer.
Definition test_point.h:77
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:76
fr_pair_decode_t func
Decoder for pairs.
Definition test_point.h:87
fr_pair_encode_t func
Encoder for pairs.
Definition test_point.h:95
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:68
Entry point for pair decoders.
Definition test_point.h:85
Entry point for pair encoders.
Definition test_point.h:93
Entry point for protocol decoders.
Definition test_point.h:67
Entry point for protocol encoders.
Definition test_point.h:75
const char * base(const char *p)
Definition testlib.c:55
Definition testlib.h:54
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:236
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
enum fr_token fr_token_t
@ T_AND
Definition token.h:53
@ T_INVALID
Definition token.h:37
@ T_SUB
Definition token.h:50
@ T_XOR
Definition token.h:56
@ T_DIV
Definition token.h:52
@ T_MOD
Definition token.h:58
@ T_BARE_WORD
Definition token.h:118
@ T_ADD
Definition token.h:49
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
@ T_MUL
Definition token.h:51
@ T_OR
Definition token.h:54
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)
#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_xlat_purify_condition(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.
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 char const * fail_file
static void commands_print(void)
#define POISONED_BUFFER_END(_p)
static bool allow_purify
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.
#define SBUFF_RETURN_OK_WITH_ERROR(_function,...)
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 size_t command_attr_name(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Print attribute information.
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.
static size_t command_attr_children(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Print attribute information.
static size_t command_pair_compare(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
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)
int fuzzer_fd
File descriptor pointing to a a directory to write fuzzer output.
fr_dlist_t entry
Entry in the dlist.
uint8_t * buffer_end
Where the non-poisoned region of the buffer ends.
static size_t command_attr_oid(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Print attribute information.
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.
char * fuzzer_proto_dir
Subdirectory of where to write fuzzer files, from 'fuzzer-out dir'.
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 char const * receipt_file
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 int fuzzer_open_fd(command_file_ctx_t *cc, char **out, char const *base, char const *dir)
Helper function to open a fuzzer path, and update the name / FD.
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.
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 ATTR_COMMON
static size_t command_pair_common(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen, bool allow_compare)
Parse an print an attribute pair or pair list.
#define RETURN_EXIT(_ret)
static fr_event_list_t * el
static char const * receipt_dir
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.
#define EXIT_WITH_FAILURE
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 int process_path(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config, const char *path)
static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
static size_t command_dictionary_read(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Read a dictionary from a file, and then free it.
#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.
static size_t command_attr_type(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Print attribute information.
#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 size_t command_attr_flags(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
Print attribute information.
char const * fuzzer_base_dir
Base directory of where to write fuzzer files, from '-F'.
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_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_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 const fr_token_t token2op[SBUFF_CHAR_CLASS]
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 interpret.c:69
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:3206
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:421
int xlat_instantiate(void)
Call instantiation functions for all registered, "permanent" xlats.
Definition xlat_inst.c:510
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:441
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:1934
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:147
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:171
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:3178
Definition for a single argument consumed by an xlat function.
Definition xlat.h:146
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:7
#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:584
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define PAIR_LIST_VERIFY_WITH_CTX(_c, _x)
Definition pair.h:208
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:320
static fr_slen_t parent
Definition pair.h:858
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
char const * fr_strerror_peek(void)
Get the last library error.
Definition strerror.c:631
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:685
#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:83
#define fr_type_is_null(_x)
Definition types.h:347
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
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_cmp_ret_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:759
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:6131
fr_sbuff_escape_rules_t const fr_value_escape_double
Definition value.c:355
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:3968
fr_sbuff_parse_rules_t const value_parse_rules_bareword_unquoted
Default formatting rules.
Definition value.c:485
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:4218
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:6094
fr_sbuff_unescape_rules_t const fr_value_unescape_double
Definition value.c:271
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:6371
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition value.c:552
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4399
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:5432
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1093
static fr_slen_t data
Definition value.h:1340
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1030
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
static size_t char ** out
Definition value.h:1030
#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:374
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:225