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