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