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