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