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