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: 64952dc060cdde59a5e75412c129e28a8a8ed7a0 $
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: 64952dc060cdde59a5e75412c129e28a8a8ed7a0 $")
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 *raddb_dir;
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;
279static dl_loader_t *dl_loader = NULL;
280
281static fr_event_list_t *el = NULL;
282
283static char const *write_filename = NULL;
284static FILE *write_fp = NULL;
285
286size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen);
287static int process_file(bool *exit_now, TALLOC_CTX *ctx,
288 command_config_t const *config, const char *root_dir, char const *filename, fr_dlist_head_t *lines);
289
290#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
291# define BUFF_POISON_START 1024
292# define BUFF_POISON_END 1024
293
294/** Unpoison the start and end regions of the buffer
295 *
296 */
297static int _free_buffer(uint8_t *buff)
298{
299 size_t size = talloc_array_length(buff) - (BUFF_POISON_START + BUFF_POISON_END);
300
303
304 return 0;
305}
306#else
307# define BUFF_POISON_START 0
308# define BUFF_POISON_END 0
309#endif
310
311/** Allocate a special buffer with poisoned memory regions at the start and end
312 *
313 */
314static int poisoned_buffer_allocate(TALLOC_CTX *ctx, uint8_t **buff, size_t size)
315{
316 uint8_t *our_buff = *buff;
317
318 if (our_buff) {
319 /*
320 * If it's already the correct length
321 * don't bother re-allocing the buffer,
322 * just memset it to zero.
323 */
324 if ((size + BUFF_POISON_START + BUFF_POISON_END) == talloc_array_length(our_buff)) {
325 memset(our_buff + BUFF_POISON_START, 0, size);
326 return 0;
327 }
328
329 talloc_free(our_buff); /* Destructor de-poisons */
330 *buff = NULL;
331 }
332
333 our_buff = talloc_array(ctx, uint8_t, size + BUFF_POISON_START + BUFF_POISON_END);
334 if (!our_buff) return -1;
335
336#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
337 talloc_set_destructor(our_buff, _free_buffer);
338
339 /*
340 * Poison regions before and after the buffer
341 */
344#endif
345
346 *buff = our_buff;
347
348 return 0;
349}
350#define POISONED_BUFFER_START(_p) ((_p) + BUFF_POISON_START)
351#define POISONED_BUFFER_END(_p) ((_p) + BUFF_POISON_START + (talloc_array_length(_p) - (BUFF_POISON_START + BUFF_POISON_END)))
352
353static void mismatch_print(command_file_ctx_t *cc, char const *command,
354 char *expected, size_t expected_len, char *got, size_t got_len,
355 bool print_diff)
356{
357 char *g, *e;
358
359 ERROR("%s failed %s/%s:%d", command, cc->path, cc->filename, cc->lineno);
360
361 if (!print_diff) {
362 ERROR(" got : %.*s", (int) got_len, got);
363 ERROR(" expected : %.*s", (int) expected_len, expected);
364 } else {
365 g = got;
366 e = expected;
367
368 while (*g && *e && (*g == *e)) {
369 g++;
370 e++;
371 }
372
373 if (expected_len < 100) {
374 char const *spaces = " ";
375
376 ERROR(" EXPECTED : %.*s", (int) expected_len, expected);
377 ERROR(" GOT : %.*s", (int) got_len, got);
378 ERROR(" %.*s^ differs here (%zu)", (int) (e - expected), spaces, e - expected);
379 } else if (fr_debug_lvl > 1) {
380 ERROR(" EXPECTED : %.*s", (int) expected_len, expected);
381 ERROR(" GOT : %.*s", (int) got_len, got);
382 ERROR("Differs at : %zu", e - expected);
383
384 } else {
385 size_t glen, elen;
386
387 elen = strlen(e);
388 if (elen > 70) elen = 70;
389 glen = strlen(g);
390 if (glen > 70) glen = 70;
391
392 ERROR("(%zu) ... %.*s ... ", e - expected, (int) elen, e);
393 ERROR("(%zu) ... %.*s ... ", e - expected, (int) glen, g);
394 }
395 }
396}
397
398/** Print hex string to buffer
399 *
400 */
401static inline CC_HINT(nonnull) size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
402{
403 char *p = out;
404 char *end = p + outlen;
405 size_t i;
406
407 if (inlen == 0) {
408 *p = '\0';
409 return 0;
410 }
411
412 for (i = 0; i < inlen; i++) {
413 size_t len;
414
415 len = snprintf(p, end - p, "%02x ", in[i]);
416 if (is_truncated(len, end - p)) return 0;
417
418 p += len;
419 }
420
421 *(--p) = '\0';
422
423 return p - out;
424}
425
426/** Concatenate error stack
427 */
428static inline size_t strerror_concat(char *out, size_t outlen)
429{
430 char *end = out + outlen;
431 char *p = out;
432 char const *err;
433
434 while ((p < end) && (err = fr_strerror_pop())) {
435 if (*fr_strerror_peek()) {
436 p += snprintf(p, end - p, "%s: ", err);
437 } else {
438 p += strlcpy(p, err, end - p);
439 }
440 }
441
442 return p - out;
443}
444
445static inline CC_HINT(nonnull) int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
446{
447 fr_sha1_ctx ctx;
449 char digest_str[(SHA1_DIGEST_LENGTH * 2) + 1];
450 int file_fd;
451
452 fr_assert(data_len <= COMMAND_OUTPUT_MAX);
453
454 fr_sha1_init(&ctx);
455 fr_sha1_update(&ctx, (uint8_t const *)text, strlen(text));
456 fr_sha1_final(digest, &ctx);
457
458 /*
459 * We need to use the url alphabet as the standard
460 * one contains forwarded slashes which openat
461 * doesn't like.
462 */
463 fr_base64_encode_nstd(&FR_SBUFF_OUT(digest_str, sizeof(digest_str)), &FR_DBUFF_TMP(digest, sizeof(digest)),
465
466 file_fd = openat(fd_dir, digest_str, O_RDWR | O_CREAT | O_TRUNC,
467 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
468 if (file_fd < 0) {
469 fr_strerror_printf("Failed opening or creating corpus seed file \"%s\": %s",
470 digest_str, fr_syserror(errno));
471 return -1;
472 }
473
474 if (flock(file_fd, LOCK_EX) < 0) {
475 fr_strerror_printf("Failed locking corpus seed file \"%s\": %s",
476 digest_str, fr_syserror(errno));
477 return -1;
478 }
479
480 while (data_len) {
481 ssize_t ret;
482
483 ret = write(file_fd, data, data_len);
484 if (ret < 0) {
485 fr_strerror_printf("Failed writing to corpus seed file \"%s\": %s",
486 digest_str, fr_syserror(errno));
487 (void)flock(file_fd, LOCK_UN);
488 unlinkat(fd_dir, digest_str, 0);
489 return -1;
490 }
491 data_len -= ret;
492 data += ret;
493 }
494 (void)flock(file_fd, LOCK_UN);
495
496 return 0;
497}
498
499/*
500 * End of hacks for xlat
501 *
502 **********************************************************************/
503
504static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen);
505
506static char const hextab[] = "0123456789abcdef";
507
508static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
509{
510 ssize_t slen = 0;
511 char *p;
512
513 p = buffer + 1;
514
515 while (*p && (outlen > 0)) {
516 if (*p == '"') {
517 return slen;
518 }
519
520 if (*p != '\\') {
521 *(output++) = *(p++);
522 outlen--;
523 slen++;
524 continue;
525 }
526
527 switch (p[1]) {
528 default:
529 *(output++) = p[1];
530 break;
531
532 case 'n':
533 *(output++) = '\n';
534 break;
535
536 case 'r':
537 *(output++) = '\r';
538 break;
539
540 case 't':
541 *(output++) = '\t';
542 break;
543 }
544
545 outlen--;
546 slen++;
547 }
548
549 ERROR("String is not terminated");
550 return 0;
551}
552
553static ssize_t encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen)
554{
555 int depth = 0;
556 ssize_t slen;
557 char *p;
558
559 for (p = buffer; *p != '\0'; p++) {
560 if (*p == '{') depth++;
561 if (*p == '}') {
562 depth--;
563 if (depth == 0) break;
564 }
565 }
566
567 if (*p != '}') {
568 ERROR("No trailing '}' in string starting with \"%s\"", buffer);
569 return 0;
570 }
571
572 *endptr = p + 1;
573 *p = '\0';
574
575 p = buffer + 1;
577
578 slen = encode_tlv(p, output, outlen);
579 if (slen <= 0) return 0;
580
581 return slen;
582}
583
584static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
585{
586 char *p = in;
587 char *end = in + inlen;
588 uint8_t *out_p = out, *out_end = out_p + outlen;
589
590 while (p < end) {
591 char *c1, *c2;
592
593 if (out_p >= out_end) {
594 fr_strerror_const("Would overflow output buffer");
595 return -(p - in);
596 }
597
599
600 if (!*p) break;
601
602 c1 = memchr(hextab, tolower((uint8_t) *p++), sizeof(hextab));
603 if (!c1) {
604 bad_input:
605 fr_strerror_printf("Invalid hex data starting at \"%s\"", p);
606 return -(p - in);
607 }
608
609 c2 = memchr(hextab, tolower((uint8_t)*p++), sizeof(hextab));
610 if (!c2) goto bad_input;
611
612 *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
613 }
614
615 return out_p - out;
616}
617
618
619static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
620{
621 ssize_t slen;
622
623 if (!isspace((uint8_t) *p)) {
624 ERROR("Invalid character following attribute definition");
625 return 0;
626 }
627
629
630 if (*p == '{') {
631 size_t sublen;
632 char *q;
633
634 slen = 0;
635
636 do {
638 if (!*p) {
639 if (slen == 0) {
640 ERROR("No data");
641 return 0;
642 }
643
644 break;
645 }
646
647 sublen = encode_data_tlv(p, &q, output, outlen);
648 if (sublen <= 0) return 0;
649
650 slen += sublen;
651 output += sublen;
652 outlen -= sublen;
653 p = q;
654 } while (*q);
655
656 return slen;
657 }
658
659 if (*p == '"') {
660 slen = encode_data_string(p, output, outlen);
661 return slen;
662 }
663
664 slen = hex_to_bin(output, outlen, p, strlen(p));
665 if (slen <= 0) {
666 fr_strerror_const_push("Empty hex string");
667 return slen;
668 }
669
670 return slen;
671}
672
673static int decode_attr(char *buffer, char **endptr)
674{
675 long attr;
676
677 attr = strtol(buffer, endptr, 10);
678 if (*endptr == buffer) {
679 ERROR("No valid number found in string starting with \"%s\"", buffer);
680 return 0;
681 }
682
683 if (!**endptr) {
684 ERROR("Nothing follows attribute number");
685 return 0;
686 }
687
688 if ((attr <= 0) || (attr > 256)) {
689 ERROR("Attribute number is out of valid range");
690 return 0;
691 }
692
693 return (int) attr;
694}
695
696static int decode_vendor(char *buffer, char **endptr)
697{
698 long vendor;
699
700 if (*buffer != '.') {
701 ERROR("Invalid separator before vendor id");
702 return 0;
703 }
704
705 vendor = strtol(buffer + 1, endptr, 10);
706 if (*endptr == (buffer + 1)) {
707 ERROR("No valid vendor number found");
708 return 0;
709 }
710
711 if (!**endptr) {
712 ERROR("Nothing follows vendor number");
713 return 0;
714 }
715
716 if ((vendor <= 0) || (vendor > (1 << 24))) {
717 ERROR("Vendor number is out of valid range");
718 return 0;
719 }
720
721 if (**endptr != '.') {
722 ERROR("Invalid data following vendor number");
723 return 0;
724 }
725 (*endptr)++;
726
727 return (int) vendor;
728}
729
730static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen)
731{
732 int attr;
733 ssize_t slen;
734 char *p;
735
736 attr = decode_attr(buffer, &p);
737 if (attr == 0) return 0;
738
739 output[0] = attr;
740 output[1] = 2;
741
742 if (*p == '.') {
743 p++;
744 slen = encode_tlv(p, output + 2, outlen - 2);
745
746 } else {
747 slen = encode_data(p, output + 2, outlen - 2);
748 }
749
750 if (slen <= 0) return slen;
751 if (slen > (255 - 2)) {
752 ERROR("TLV data is too long");
753 return 0;
754 }
755
756 output[1] += slen;
757
758 return slen + 2;
759}
760
761static ssize_t encode_vsa(char *buffer, uint8_t *output, size_t outlen)
762{
763 int vendor;
764 ssize_t slen;
765 char *p;
766
767 vendor = decode_vendor(buffer, &p);
768 if (vendor == 0) return 0;
769
770 output[0] = 0;
771 output[1] = (vendor >> 16) & 0xff;
772 output[2] = (vendor >> 8) & 0xff;
773 output[3] = vendor & 0xff;
774
775 slen = encode_tlv(p, output + 4, outlen - 4);
776 if (slen <= 0) return slen;
777 if (slen > (255 - 6)) {
778 ERROR("VSA data is too long");
779 return 0;
780 }
781
782 return slen + 4;
783}
784
785static ssize_t encode_evs(char *buffer, uint8_t *output, size_t outlen)
786{
787 int vendor;
788 int attr;
789 ssize_t slen;
790 char *p;
791
792 vendor = decode_vendor(buffer, &p);
793 if (vendor == 0) return 0;
794
795 attr = decode_attr(p, &p);
796 if (attr == 0) return 0;
797
798 output[0] = 0;
799 output[1] = (vendor >> 16) & 0xff;
800 output[2] = (vendor >> 8) & 0xff;
801 output[3] = vendor & 0xff;
802 output[4] = attr;
803
804 slen = encode_data(p, output + 5, outlen - 5);
805 if (slen <= 0) return slen;
806
807 return slen + 5;
808}
809
810static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
811{
812 int attr;
813 ssize_t slen;
814 char *p;
815
816 attr = decode_attr(buffer, &p);
817 if (attr == 0) return 0;
818
819 output[0] = attr;
820
821 if (attr == 26) {
822 slen = encode_evs(p, output + 1, outlen - 1);
823 } else {
824 slen = encode_data(p, output + 1, outlen - 1);
825 }
826 if (slen <= 0) return slen;
827 if (slen > (255 - 3)) {
828 ERROR("Extended Attr data is too long");
829 return 0;
830 }
831
832 return slen + 1;
833}
834
835static ssize_t encode_long_extended(char *buffer, uint8_t *output, size_t outlen)
836{
837 int attr;
838 ssize_t slen, total;
839 char *p;
840
841 attr = decode_attr(buffer, &p);
842 if (attr == 0) return 0;
843
844 /* output[0] is the extended attribute */
845 output[1] = 4;
846 output[2] = attr;
847 output[3] = 0;
848
849 if (attr == 26) {
850 slen = encode_evs(p, output + 4, outlen - 4);
851 if (slen <= 0) return slen;
852
853 output[1] += 5;
854 slen -= 5;
855 } else {
856 slen = encode_data(p, output + 4, outlen - 4);
857 }
858 if (slen <= 0) return slen;
859
860 total = 0;
861 while (1) {
862 int sublen = 255 - output[1];
863
864 if (slen <= sublen) {
865 output[1] += slen;
866 total += output[1];
867 break;
868 }
869
870 slen -= sublen;
871
872 memmove(output + 255 + 4, output + 255, slen);
873 memcpy(output + 255, output, 4);
874
875 output[1] = 255;
876 output[3] |= 0x80;
877
878 output += 255;
879 output[1] = 4;
880 total += 255;
881 }
882
883 return total;
884}
885
886static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
887{
888 int attr;
889 ssize_t slen, sublen;
890 char *p;
891
892 attr = decode_attr(buffer, &p);
893 if (attr == 0) return 0;
894
895 slen = 2;
896 output[0] = attr;
897 output[1] = 2;
898
899 if (attr == 26) {
900 sublen = encode_vsa(p, output + 2, outlen - 2);
901
902 } else if ((attr < 241) || (attr > 246)) {
903 sublen = encode_data(p, output + 2, outlen - 2);
904
905 } else {
906 if (*p != '.') {
907 ERROR("Invalid data following attribute number");
908 return 0;
909 }
910
911 if (attr < 245) {
912 sublen = encode_extended(p + 1, output + 2, outlen - 2);
913 } else {
914 /*
915 * Not like the others!
916 */
917 return encode_long_extended(p + 1, output, outlen);
918 }
919 }
920 if (sublen <= 0) return sublen;
921 if (sublen > (255 -2)) {
922 ERROR("RFC Data is too long");
923 return 0;
924 }
925
926 output[1] += sublen;
927 return slen + sublen;
928}
929
930
931static void unload_proto_library(void)
932{
933 TALLOC_FREE(dl);
934}
935
936static ssize_t load_proto_library(char const *proto_name)
937{
938 char dl_name[128];
939
940 if (strcmp(proto_name_prev, proto_name) != 0) {
941 /*
942 * Ensure the old proto library is unloaded
943 */
945
946 snprintf(dl_name, sizeof(dl_name), "libfreeradius-%s", proto_name);
947 if (dl) TALLOC_FREE(dl);
948
949 dl = dl_by_name(dl_loader, dl_name, NULL, false);
950 if (!dl) {
951 fr_perror("Failed to link to library \"%s\"", dl_name);
953 return 0;
954 }
955
956 strlcpy(proto_name_prev, proto_name, sizeof(proto_name_prev));
957 }
958
959 return strlen(proto_name);
960}
961
962static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
963{
964 char buffer[256];
965 char const *p, *q;
966 void *dl_symbol;
967
968 if (!dl) {
969 fr_strerror_printf("No protocol library loaded. Specify library with \"load <proto name>\"");
970 return 0;
971 }
972
973 p = command;
974
975 /*
976 * Use the dflt_symbol name as the test point
977 */
978 if ((*p == '.') && (q = strchr(p, ' ')) && (q != (p + 1)) && ((size_t)(q - p) < sizeof(buffer))) {
979 p++;
980 strlcpy(buffer, p, (q - p) + 1);
981 p = q + 1;
982 } else {
983 snprintf(buffer, sizeof(buffer), "%s_%s", proto_name_prev, dflt_symbol);
984 }
985
986 dl_symbol = dlsym(dl->handle, buffer);
987 if (!dl_symbol) {
988 fr_strerror_printf("Test point (symbol \"%s\") not exported by library", buffer);
990 return 0;
991 }
992 *symbol = dl_symbol;
993
994 return p - command;
995}
996
998{
999 if (cc->tmpl_rules.attr.dict_def) {
1000 return UNCONST(fr_dict_t *, cc->tmpl_rules.attr.dict_def);
1001 }
1002
1003 return cc->config->dict;
1004}
1005
1006/** Common dictionary load function
1007 *
1008 * Callers call fr_dict_global_ctx_set to set the context
1009 * the dictionaries will be loaded into.
1010 */
1011static int dictionary_load_common(command_result_t *result, command_file_ctx_t *cc, char const *in, char const *default_subdir)
1012{
1013 char const *dir;
1014 char *q;
1015 char const *name;
1016 char *tmp = NULL;
1017 int ret;
1018 fr_dict_t *dict;
1019
1020 if (in[0] == '\0') {
1021 fr_strerror_const("Missing dictionary name");
1023 }
1024
1025 /*
1026 * Decrease ref count if we're loading in a new dictionary
1027 */
1028 if (cc->tmpl_rules.attr.dict_def) {
1030 }
1031
1032 q = strchr(in, ' ');
1033 if (q) {
1034 name = tmp = talloc_bstrndup(NULL, in, q - in);
1035 q++;
1036 dir = q;
1037 } else {
1038 name = in;
1039 dir = default_subdir;
1040 }
1041
1042 ret = fr_dict_protocol_afrom_file(&dict, name, dir, __FILE__);
1043 talloc_free(tmp);
1044 if (ret < 0) RETURN_COMMAND_ERROR();
1045
1046 cc->tmpl_rules.attr.dict_def = dict;
1047 cc->tmpl_rules.attr.namespace = fr_dict_root(dict);
1048
1049 /*
1050 * Dump the dictionary if we're in super debug mode
1051 */
1053
1054
1055 RETURN_OK(0);
1056}
1057
1058static 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)
1059{
1061 size_t match_len;
1062 ssize_t slen;
1063 char const *p;
1064 fr_sbuff_t sbuff;
1065 fr_dict_attr_t const *enumv = NULL;
1066
1067 /*
1068 * Parse data types
1069 */
1071 if (fr_type_is_null(type)) {
1073 }
1074 fr_assert(match_len < inlen);
1075
1076 p = in + match_len;
1078 *out = p;
1079
1080 /*
1081 * As a hack, allow most things to be inside
1082 * double-quoted strings. This is really only for dates,
1083 * which are space-delimited.
1084 */
1085 if (*p == '"'){
1086 p++;
1087 sbuff = FR_SBUFF_IN(p, strlen(p));
1088 slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, enumv,
1089 &sbuff,
1091 if (slen < 0) {
1093 }
1094
1095 p += fr_sbuff_used(&sbuff);
1096 if (*p != '"') {
1098 }
1099 p++;
1100
1101 if (type != FR_TYPE_STRING) {
1102 if (fr_value_box_cast_in_place(box, box, type, NULL) < 0) {
1104 }
1105 }
1106
1107 } else {
1108 sbuff = FR_SBUFF_IN(p, strlen(p));
1109
1110 /*
1111 * We have no other way to pass the dict to the value-box parse function.
1112 */
1113 if (type == FR_TYPE_ATTR) {
1114 fr_dict_t const *dict = dictionary_current(cc);
1115
1116 if (!dict) {
1117 fr_strerror_const("proto-dictionary must be defined");
1119 }
1120
1121 enumv = fr_dict_root(dict);
1122 }
1123
1124 slen = fr_value_box_from_substr(box, box, type, enumv,
1125 &sbuff,
1127 if (slen < 0) {
1129 }
1130 p += fr_sbuff_used(&sbuff);
1131 }
1133
1134 RETURN_OK(p - in);
1135}
1136
1137static fr_cmd_t *command_head = NULL;
1138
1139static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
1140{
1141 return 0;
1142}
1143
1144static int command_walk(UNUSED void *ctx, fr_cmd_walk_info_t *info)
1145{
1146 int i;
1147
1148 for (i = 0; i < info->num_parents; i++) {
1149 printf("%s ", info->parents[i]);
1150 }
1151
1152 printf(":%s ", info->name);
1153 if (info->syntax) printf("%s", info->syntax);
1154 printf("%s", "");
1155
1156 return 1;
1157}
1158
1159static void command_print(void)
1160{
1161 void *walk_ctx = NULL;
1162
1163 printf("Command hierarchy --------");
1164 fr_cmd_debug(stdout, command_head);
1165
1166 printf("Command list --------");
1167 while (fr_command_walk(command_head, &walk_ctx, NULL, command_walk) == 1) {
1168 // do nothing
1169 }
1170}
1171
1172#define CLEAR_TEST_POINT(_cc) \
1173do { \
1174 talloc_free_children((_cc)->tmp_ctx); \
1175 tp = NULL; \
1176} while (0)
1177
1178/** Placeholder function for comments
1179 *
1180 */
1182 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1183{
1184 return 0;
1185}
1186
1187/** Execute another test file
1188 *
1189 */
1191 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1192{
1193 char *q;
1194 bool exit_now = false;
1195 int ret;
1196
1197 if (write_fp) {
1198 fprintf(stderr, "Can't do $INCLUDE with -w %s\n", write_filename);
1199 RETURN_EXIT(1);
1200 }
1201
1202 q = strrchr(cc->path, '/');
1203 if (q) {
1204 *q = '\0';
1205 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, cc->path, in, NULL);
1206 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1207 *q = '/';
1208 RETURN_OK(0);
1209 }
1210
1211 ret = process_file(&exit_now, cc->tmp_ctx, cc->config, NULL, in, NULL);
1212 if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1213
1214 RETURN_OK(0);
1215}
1216
1217/** Determine if unresolved attributes are allowed
1218 *
1219 */
1221 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1222{
1223 fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen);
1224 bool res;
1225
1226 if (fr_sbuff_out_bool(&res, &our_in) == 0) {
1227 fr_strerror_printf("Invalid boolean value, must be \"yes\" or \"no\"");
1229 }
1231
1232 RETURN_OK(0);
1233}
1234
1235#define ATTR_COMMON \
1236 fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen); \
1237 fr_dict_attr_err_t err; \
1238 fr_slen_t slen; \
1239 fr_dict_attr_t const *root; \
1240 fr_dict_attr_t const *da; \
1241 root = cc->tmpl_rules.attr.dict_def ? \
1242 fr_dict_root(cc->tmpl_rules.attr.dict_def) : \
1243 fr_dict_root(fr_dict_internal()); \
1244 slen = fr_dict_attr_by_oid_substr(&err, \
1245 &da, \
1246 root, \
1247 &our_in, NULL); \
1248 if (err != FR_DICT_ATTR_OK) FR_SBUFF_ERROR_RETURN(&our_in)
1249
1250
1251/** Print attribute information
1252 *
1253 */
1255 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1256{
1257 fr_hash_table_t *namespace;
1258 fr_hash_iter_t iter;
1259 fr_dict_attr_t const *ref;
1262
1263 namespace = dict_attr_namespace(da);
1264 fr_assert(namespace != NULL);
1265
1266 for (da = fr_hash_table_iter_init(namespace, &iter);
1267 da != NULL;
1268 da = fr_hash_table_iter_next(namespace, &iter)) {
1269 if (da->flags.is_alias) {
1270 ref = fr_dict_attr_ref(da);
1271 fr_assert(ref != NULL);
1272
1273 slen = fr_sbuff_in_sprintf(&out, "%s (ALIAS ref=", da->name);
1274 if (slen <= 0) RETURN_OK_WITH_ERROR();
1275
1276 slen = fr_dict_attr_oid_print(&out, fr_dict_root(da->dict), ref, false);
1277 if (slen <= 0) RETURN_OK_WITH_ERROR();
1278
1279 slen = fr_sbuff_in_strcpy(&out, "), ");
1280 if (slen <= 0) RETURN_OK_WITH_ERROR();
1281 continue;
1282 }
1283
1284 slen = fr_sbuff_in_sprintf(&out, "%s (%s), ", da->name, fr_type_to_str(da->type));
1285 if (slen <= 0) RETURN_OK_WITH_ERROR();
1286 }
1287
1288 fr_sbuff_trim(&out, (bool[UINT8_MAX + 1]){ [' '] = true, [','] = true });
1289
1291}
1292
1293
1294/** Print attribute information
1295 *
1296 */
1298 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1299{
1301
1302 slen = fr_dict_attr_flags_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), da->dict, da->type, &da->flags);
1303 if (slen <= 0) RETURN_OK_WITH_ERROR();
1304
1305 RETURN_OK(slen);
1306}
1307
1308/** Print attribute information
1309 *
1310 */
1312 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1313{
1315
1316 slen = fr_dict_attr_oid_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), root, da, false);
1317 if (slen <= 0) RETURN_OK_WITH_ERROR();
1318
1319 RETURN_OK(slen);
1320}
1321
1322/** Print attribute information
1323 *
1324 */
1326 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1327{
1329
1331 if (slen <= 0) RETURN_OK_WITH_ERROR();
1332
1333 RETURN_OK(slen);
1334}
1335
1336/** Print attribute information
1337 *
1338 */
1340 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1341{
1343
1345 if (slen <= 0) RETURN_OK_WITH_ERROR();
1346
1347 RETURN_OK(slen);
1348}
1349
1350static const fr_token_t token2op[UINT8_MAX + 1] = {
1351 [ '+' ] = T_ADD,
1352 [ '-' ] = T_SUB,
1353 [ '*' ] = T_MUL,
1354 [ '/' ] = T_DIV,
1355 [ '^' ] = T_XOR,
1356 [ '.' ] = T_ADD,
1357 [ '&' ] = T_AND,
1358 [ '|' ] = T_OR,
1359 [ '%' ] = T_MOD,
1360};
1361
1362/** Perform calculations
1363 *
1364 */
1366 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1367{
1368 fr_value_box_t *a, *b, *out;
1369 size_t match_len;
1371 fr_token_t op;
1372 char const *p, *value, *end;
1373 size_t slen;
1374 bool assignment;
1375
1376 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1377 b = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1378
1379 p = in;
1380 end = in + inlen;
1381
1382 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1383 if (match_len == 0) return 0; /* errors have already been updated */
1384
1385 p += match_len;
1387
1388 op = fr_table_value_by_longest_prefix(&match_len, fr_tokens_table, p, end - p, T_INVALID);
1389 if (op != T_INVALID) {
1390 p += match_len;
1391 assignment = fr_assignment_op[op];
1392
1393 } else {
1394 op = token2op[(uint8_t) p[0]];
1395 if (op == T_INVALID) {
1396 fr_strerror_printf("Unknown operator '%c'", p[0]);
1398 }
1399 p++;
1400
1401 assignment = false;
1402 }
1404
1405 match_len = parse_typed_value(result, cc, b, &value, p, end - p);
1406 if (match_len == 0) return 0;
1407
1408 p += match_len;
1410
1411 if (assignment) {
1412 if (fr_value_calc_assignment_op(cc->tmp_ctx, a, op, b) < 0) {
1414 }
1415 out = a;
1416
1417 } else {
1418 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1419
1420 /*
1421 * If there's no output data type, then the code tries to
1422 * figure one out automatically.
1423 */
1424 if (!*p) {
1426 } else {
1427 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1428 p += 2;
1430
1433 fr_value_box_init(out, type, NULL, false);
1434 }
1435
1436 if (fr_value_calc_binary_op(cc->tmp_ctx, out, type, a, op, b) < 0) {
1438 }
1439 }
1440
1442 if (slen <= 0) RETURN_OK_WITH_ERROR();
1443
1444 RETURN_OK(slen);
1445}
1446
1447/** Perform calculations on multi-valued ops
1448 *
1449 */
1451 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1452{
1453 fr_value_box_t *group, *a, *out;
1454 size_t match_len;
1456 fr_token_t op;
1457 char const *p, *value, *end;
1458 size_t slen;
1459
1460 group = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1461 fr_value_box_init(group, FR_TYPE_GROUP, NULL, false);
1462
1463 p = in;
1464 end = in + inlen;
1465
1466 /*
1467 * Multi-valued operations
1468 */
1469 op = token2op[(uint8_t) p[0]];
1470 if (op == T_INVALID) {
1471 fr_strerror_printf("Unknown operator '%c'", p[0]);
1473 }
1474 p++;
1475
1476 while (p < end) {
1478
1479 a = talloc_zero(group, fr_value_box_t);
1480
1481 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1482 if (match_len == 0) return 0; /* errors have already been updated */
1483
1484 fr_value_box_list_insert_tail(&group->vb_group, a);
1485
1486 p += match_len;
1487
1488 if (strncmp(p, "->", 2) == 0) break;
1489 }
1490
1491 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1493
1494 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1495 p += 2;
1497
1500
1501
1502 if (fr_value_calc_nary_op(cc->tmp_ctx, out, type, op, group) < 0) {
1504 }
1505
1507 if (slen <= 0) RETURN_OK_WITH_ERROR();
1508
1509 RETURN_OK(slen);
1510}
1511
1512/** Perform casting
1513 *
1514 */
1516 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1517{
1518 fr_value_box_t *a, *out;
1519 size_t match_len;
1521 char const *p, *value, *end;
1522 size_t slen;
1523
1524 a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1525
1526 p = in;
1527 end = in + inlen;
1528
1529 match_len = parse_typed_value(result, cc, a, &value, p, end - p);
1530 if (match_len == 0) return 0; /* errors have already been updated */
1531
1532 p += match_len;
1534
1535 out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1536
1537 if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1538 p += 2;
1540
1543 fr_value_box_init(out, type, NULL, false);
1544
1545 if (fr_value_box_cast(out, out, type, NULL, a) < 0) {
1547 }
1548
1550 if (slen <= 0) RETURN_OK_WITH_ERROR();
1551
1552 RETURN_OK(slen);
1553}
1554
1555/** Change the working directory
1556 *
1557 */
1559 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1560{
1561 TALLOC_FREE(cc->path); /* Free old directories */
1562
1563 cc->path = fr_realpath(cc->tmp_ctx, in, inlen);
1564 if (!cc->path) RETURN_COMMAND_ERROR();
1565
1567
1568 RETURN_OK(talloc_array_length(cc->path) - 1);
1569}
1570
1571/*
1572 * Clear the data buffer
1573 */
1575 char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
1576{
1577 memset(data, 0, COMMAND_OUTPUT_MAX);
1578 RETURN_NOOP(0);
1579}
1580
1581/*
1582 * Add a command by talloc'ing a table for it.
1583 */
1585 char *data, size_t UNUSED data_used, char *in, UNUSED size_t inlen)
1586{
1587 char *p, *name;
1588 char *parent = NULL;
1589 fr_cmd_table_t *table;
1590 char buffer[8192];
1591
1592 table = talloc_zero(cc->tmp_ctx, fr_cmd_table_t);
1593
1594 strlcpy(buffer, in, sizeof(buffer));
1595
1596 p = strchr(buffer, ':');
1597 if (!p) {
1598 fr_strerror_const("no ':name' specified");
1600 }
1601
1602 *p = '\0';
1603 p++;
1604
1605 parent = talloc_strdup(cc->tmp_ctx, in);
1606
1607 /*
1608 * Set the name and try to find the syntax.
1609 */
1610 name = p;
1612
1613 if (isspace((uint8_t) *p)) {
1614 *p = '\0';
1615 p++;
1616 }
1617
1619
1620 if (*p) {
1621 table->syntax = talloc_strdup(table, p);
1622 }
1623 table->parent = parent;
1624 table->name = name;
1625 table->help = NULL;
1626 table->func = command_func;
1627 table->tab_expand = NULL;
1628 table->read_only = true;
1629
1630 if (fr_command_add(table, &command_head, NULL, NULL, table) < 0) {
1631 fr_strerror_const_push("ERROR: Failed adding command");
1633 }
1634
1636
1638}
1639
1640/*
1641 * Do tab completion on a command
1642 */
1644 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1645{
1646 int i;
1647 int num_expansions;
1648 char const *expansions[CMD_MAX_ARGV];
1649 char *p = data, *end = p + COMMAND_OUTPUT_MAX, **argv;
1650 fr_cmd_info_t info;
1651 size_t len;
1652
1653 info.argc = 0;
1654 info.max_argc = CMD_MAX_ARGV;
1655 info.argv = talloc_zero_array(cc->tmp_ctx, char const *, CMD_MAX_ARGV);
1656 info.box = talloc_zero_array(cc->tmp_ctx, fr_value_box_t *, CMD_MAX_ARGV);
1657
1658 memcpy(&argv, &info.argv, sizeof(argv)); /* const issues */
1659 info.argc = fr_dict_str_to_argv(in, argv, CMD_MAX_ARGV);
1660 if (info.argc <= 0) {
1661 fr_strerror_const("Failed splitting input");
1662 RETURN_PARSE_ERROR(-(info.argc));
1663 }
1664
1665 num_expansions = fr_command_tab_expand(cc->tmp_ctx, command_head, &info, CMD_MAX_ARGV, expansions);
1666
1667 len = snprintf(p, end - p, "%d - ", num_expansions);
1668 if (is_truncated(len, end - p)) {
1669 oob:
1670 fr_strerror_const("Out of output buffer space for radmin command");
1672 }
1673 p += len;
1674
1675 for (i = 0; i < num_expansions; i++) {
1676 len = snprintf(p, end - p, "'%s', ", expansions[i]);
1677 if (is_truncated(len, end - p)) goto oob;
1678 p += len;
1679 }
1680
1681 /*
1682 * Remove the trailing ", "
1683 */
1684 if (num_expansions > 0) {
1685 p -= 2;
1686 *p = '\0';
1687 }
1688
1689 return p - data;
1690}
1691
1692/** Parse and reprint a condition
1693 *
1694 */
1696 char *data, UNUSED size_t data_used, char *in, size_t inlen)
1697{
1698 ssize_t slen;
1699 CONF_SECTION *cs;
1700 size_t len;
1701 xlat_exp_head_t *head = NULL;
1702
1703 cs = cf_section_alloc(NULL, NULL, "if", "condition");
1704 if (!cs) {
1705 fr_strerror_const("Out of memory");
1707 }
1708 cf_filename_set(cs, cc->filename);
1709 cf_lineno_set(cs, cc->lineno);
1710
1712
1713 slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, inlen), NULL, &cc->tmpl_rules);
1714 if (slen == 0) {
1715 fr_strerror_printf_push_head("ERROR failed to parse any input");
1716 talloc_free(cs);
1718 }
1719
1720 if (slen < 0) {
1721 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
1722 talloc_free(cs);
1724 }
1725
1726 if ((size_t) slen < inlen) {
1727 len = snprintf(data, COMMAND_OUTPUT_MAX, "ERROR passed in %zu, returned %zd", inlen, slen);
1728
1729 } else {
1731 }
1732
1734 talloc_free(cs);
1735
1736 RETURN_OK(len);
1737}
1738
1740 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1741{
1742 size_t len;
1743
1744 len = snprintf(data, COMMAND_OUTPUT_MAX, "%u", cc->test_count);
1745 if (is_truncated(len, COMMAND_OUTPUT_MAX)) {
1746 fr_strerror_const("Command count would overflow data buffer (shouldn't happen)");
1748 }
1749
1750 RETURN_OK(len);
1751}
1752
1754 char *data, size_t data_used, char *in, size_t inlen)
1755{
1756 fr_test_point_pair_decode_t *tp = NULL;
1757 void *decode_ctx = NULL;
1758 char *p;
1759 uint8_t *to_dec;
1760 uint8_t *to_dec_end;
1761 ssize_t slen;
1762
1763 fr_dict_attr_t const *da;
1764 fr_pair_t *head;
1765
1766 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1767 fr_assert(da != NULL);
1768 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1769 if (!head) {
1770 fr_strerror_const_push("Failed allocating memory");
1772 }
1773
1774 p = in;
1775
1776 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_pair");
1777 if (!tp) {
1778 fr_strerror_const_push("Failed locating decoder testpoint");
1780 }
1781
1782 p += slen;
1784
1785 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
1786 fr_strerror_const_push("Failed initialising decoder testpoint");
1788 }
1789
1790 /*
1791 * Hack because we consume more of the command string
1792 * so we need to check this again.
1793 */
1794 if (*p == '-') {
1795 p = data;
1796 inlen = data_used;
1797 }
1798
1799 /*
1800 * Decode hex from input text
1801 */
1803 if (slen <= 0) {
1804 CLEAR_TEST_POINT(cc);
1805 RETURN_PARSE_ERROR(-(slen));
1806 }
1807
1808 to_dec = (uint8_t *)data;
1809 to_dec_end = to_dec + slen;
1810
1812
1813 /*
1814 * Run the input data through the test
1815 * point to produce fr_pair_ts.
1816 */
1817 while (to_dec < to_dec_end) {
1818 slen = tp->func(head, &head->vp_group, cc->tmpl_rules.attr.namespace,
1819 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1820 cc->last_ret = slen;
1821 if (slen <= 0) {
1823 CLEAR_TEST_POINT(cc);
1825 }
1826 if ((size_t)slen > (size_t)(to_dec_end - to_dec)) {
1827 fr_perror("%s: Internal sanity check failed at %d", __FUNCTION__, __LINE__);
1829 CLEAR_TEST_POINT(cc);
1831 }
1832 to_dec += slen;
1833 }
1834
1835 /*
1836 * Clear any spurious errors
1837 */
1840
1841 /*
1842 * Output may be an error, and we ignore
1843 * it if so.
1844 */
1845 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1846 if (slen <= 0) {
1848 }
1849
1850 CLEAR_TEST_POINT(cc);
1851 RETURN_OK(slen);
1852}
1853
1855 char *data, size_t data_used, char *in, size_t inlen)
1856{
1858 void *decode_ctx = NULL;
1859 char *p;
1860 uint8_t *to_dec;
1861 uint8_t *to_dec_end;
1862 ssize_t slen;
1863
1864 fr_dict_attr_t const *da;
1865 fr_pair_t *head;
1866
1867 da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1868 fr_assert(da != NULL);
1869 head = fr_pair_afrom_da(cc->tmp_ctx, da);
1870 if (!head) {
1871 fr_strerror_const_push("Failed allocating memory");
1873 }
1874
1875 p = in;
1876
1877 slen = load_test_point_by_command((void **)&tp, in, "tp_decode_proto");
1878 if (!tp) {
1879 fr_strerror_const_push("Failed locating decoder testpoint");
1881 }
1882
1883 p += slen;
1885
1886 if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
1887 fr_strerror_const_push("Failed initialising decoder testpoint");
1889 }
1890
1891 /*
1892 * Hack because we consume more of the command string
1893 * so we need to check this again.
1894 */
1895 if (*p == '-') {
1896 p = data;
1897 inlen = data_used;
1898 }
1899
1900 /*
1901 * Decode hex from input text
1902 */
1904 if (slen <= 0) {
1905 CLEAR_TEST_POINT(cc);
1906 RETURN_PARSE_ERROR(-(slen));
1907 }
1908
1909 to_dec = (uint8_t *)data;
1910 to_dec_end = to_dec + slen;
1911
1913
1914 slen = tp->func(head, &head->vp_group,
1915 (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1916 cc->last_ret = slen;
1917 if (slen <= 0) {
1919 CLEAR_TEST_POINT(cc);
1921 }
1922
1923 /*
1924 * Clear any spurious errors
1925 */
1928
1929 /*
1930 * Output may be an error, and we ignore
1931 * it if so.
1932 */
1933
1934 /*
1935 * Print the pairs.
1936 */
1937 slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1938 if (slen <= 0) {
1939 fr_assert(0);
1941 }
1942
1943 CLEAR_TEST_POINT(cc);
1944 RETURN_OK(slen);
1945}
1946
1947/** Parse a dictionary attribute, writing "ok" to the data buffer is everything was ok
1948 *
1949 */
1951 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1952{
1954
1956}
1957
1958/** Print the currently loaded dictionary
1959 *
1960 */
1962 UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1963{
1965
1966 /*
1967 * Don't modify the contents of the data buffer
1968 */
1969 RETURN_OK(data_used);
1970}
1971
1972static CC_HINT(nonnull)
1974 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1975{
1976 size_t need;
1977 ssize_t ret;
1978 char *p, *next;
1979 uint8_t *enc_p;
1980 char buffer[8192];
1981
1982 strlcpy(buffer, in, sizeof(buffer));
1983
1984 p = buffer;
1985 next = strchr(p, ',');
1986 if (next) *next = 0;
1987
1988 enc_p = cc->buffer_start;
1989
1990 while (true) {
1991 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
1992
1994
1995 if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL,
1996 p, strlen(p),
1998 talloc_free(box);
2000 }
2001
2002 ret = fr_dns_label_from_value_box(&need,
2003 cc->buffer_start, cc->buffer_end - cc->buffer_start, enc_p, true, box, NULL);
2004 talloc_free(box);
2005
2006 if (ret < 0) RETURN_OK_WITH_ERROR();
2007
2008 if (ret == 0) RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "need=%zd", need));
2009
2010 enc_p += ret;
2011
2012 /*
2013 * Go to the next input string
2014 */
2015 if (!next) break;
2016
2017 p = next + 1;
2018 next = strchr(p, ',');
2019 if (next) *next = 0;
2020 }
2021
2022 if ((cc->fuzzer_dir >= 0) &&
2023 (dump_fuzzer_data(cc->fuzzer_dir, in, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2025 }
2026
2027 RETURN_OK(hex_print(data, COMMAND_OUTPUT_MAX, cc->buffer_start, enc_p - cc->buffer_start));
2028}
2029
2031 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2032{
2033 ssize_t slen, total, i, outlen;
2034 char *out, *end;
2035 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2036
2037 /*
2038 * Decode hex from input text
2039 */
2040 total = hex_to_bin(cc->buffer_start, cc->buffer_end - cc->buffer_start, in, inlen);
2041 if (total <= 0) RETURN_PARSE_ERROR(-total);
2042
2043 out = data;
2044 end = data + COMMAND_OUTPUT_MAX;
2045
2046 for (i = 0; i < total; i += slen) {
2047 slen = fr_dns_label_to_value_box(box, box, cc->buffer_start, total, cc->buffer_start + i, false, NULL);
2048 if (slen <= 0) {
2049 error:
2050 talloc_free(box);
2052 }
2053
2054 /*
2055 * Separate names by commas
2056 */
2057 if (i > 0) *(out++) = ',';
2058
2059 /*
2060 * We don't print it with quotes.
2061 */
2062 outlen = fr_value_box_print(&FR_SBUFF_OUT(out, end - out), box, NULL);
2063 if (outlen <= 0) goto error;
2064 out += outlen;
2065
2066 fr_value_box_clear(box);
2067 }
2068
2069 talloc_free(box);
2070 RETURN_OK(out - data);
2071}
2072
2074 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2075{
2076 fr_test_point_pair_encode_t *tp = NULL;
2077
2078 fr_dcursor_t cursor;
2079 void *encode_ctx = NULL;
2080 ssize_t slen;
2081 char *p = in;
2082
2083 uint8_t *enc_p, *enc_end;
2085 fr_pair_t *vp;
2086 bool truncate = false;
2087
2088 size_t iterations = 0;
2089 fr_pair_parse_t root, relative;
2090
2092
2093 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_pair");
2094 if (!tp) {
2095 fr_strerror_const_push("Failed locating encode testpoint");
2096 CLEAR_TEST_POINT(cc);
2098 }
2099
2100 p += ((size_t)slen);
2102
2103 /*
2104 * The truncate torture test.
2105 *
2106 * Increase the buffer one byte at a time until all items in the cursor
2107 * have been encoded.
2108 *
2109 * The poisoned region at the end of the buffer will detect overruns
2110 * if we're running with asan.
2111 *
2112 */
2113 if (strncmp(p, "truncate", sizeof("truncate") - 1) == 0) {
2114 truncate = true;
2115 p += sizeof("truncate") - 1;
2117 }
2118
2119 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
2120 fr_strerror_const_push("Failed initialising encoder testpoint");
2121 CLEAR_TEST_POINT(cc);
2123 }
2124
2125 root = (fr_pair_parse_t) {
2126 .ctx = cc->tmp_ctx,
2127 .da = cc->tmpl_rules.attr.namespace,
2128 .list = &head,
2129 .dict = cc->tmpl_rules.attr.namespace->dict,
2130 .internal = fr_dict_internal(),
2131 };
2132 relative = (fr_pair_parse_t) { };
2133
2134 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2135 if (slen <= 0) {
2136 CLEAR_TEST_POINT(cc);
2138 }
2139
2141
2142 /*
2143 * Outer loop implements truncate test
2144 */
2145 do {
2146 enc_p = cc->buffer_start;
2147 enc_end = truncate ? cc->buffer_start + iterations++ : cc->buffer_end;
2148
2149 if (truncate) {
2150#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2151 /*
2152 * Poison the region between the subset of the buffer
2153 * we're using and the end of the buffer.
2154 */
2155 ASAN_POISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2156
2157 DEBUG("%s[%d]: Iteration %zu - Safe region %p-%p (%zu bytes), "
2158 "poisoned region %p-%p (%zu bytes)", cc->filename, cc->lineno, iterations - 1,
2159 enc_p, enc_end, enc_end - enc_p, enc_end, cc->buffer_end, cc->buffer_end - enc_end);
2160#else
2161 DEBUG("%s[%d]: Iteration %zu - Allowed region %p-%p (%zu bytes)",
2162 cc->filename, cc->lineno, iterations - 1, enc_p, enc_end, enc_end - enc_p);
2163#endif
2164 }
2165
2166 for (vp = fr_pair_dcursor_iter_init(&cursor, &head,
2168 dictionary_current(cc));
2169 vp;
2170 vp = fr_dcursor_current(&cursor)) {
2171 slen = tp->func(&FR_DBUFF_TMP(enc_p, enc_end), &cursor, encode_ctx);
2172 cc->last_ret = slen;
2173
2174 if (truncate) DEBUG("%s[%d]: Iteration %zu - Result %zd%s%s",
2175 cc->filename, cc->lineno, iterations - 1, slen,
2176 *fr_strerror_peek() != '\0' ? " - " : "",
2177 *fr_strerror_peek() != '\0' ? fr_strerror_peek() : "");
2178 if (slen < 0) break;
2179
2180 /*
2181 * Encoder indicated it encoded too much data
2182 */
2183 if (slen > (enc_end - enc_p)) {
2184 fr_strerror_printf("Expected returned encoded length <= %zu bytes, got %zu bytes",
2185 (enc_end - enc_p), (size_t)slen);
2186#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2187 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2188#endif
2190 CLEAR_TEST_POINT(cc);
2192 }
2193
2194 enc_p += slen;
2195
2196 if (slen == 0) break;
2197
2198 }
2199
2200#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
2201 /*
2202 * un-poison the region between the subset of the buffer
2203 * we're using and the end of the buffer.
2204 */
2205 if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
2206#endif
2207 /*
2208 * We consumed all the VPs, so presumably encoded the
2209 * complete pair list.
2210 */
2211 if (!vp) break;
2212 } while (truncate && (enc_end < cc->buffer_end));
2213
2214 /*
2215 * Last iteration result in an error
2216 */
2217 if (slen < 0) {
2219 CLEAR_TEST_POINT(cc);
2221 }
2222
2223 /*
2224 * Clear any spurious errors
2225 */
2227
2229
2230 CLEAR_TEST_POINT(cc);
2231
2232 if ((cc->fuzzer_dir >= 0) &&
2233 (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2235 }
2236
2238}
2239
2240/** Encode a RADIUS attribute writing the result to the data buffer as space separated hexits
2241 *
2242 */
2244 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2245{
2246 size_t len;
2247 char buffer[8192];
2248
2249 strlcpy(buffer, in, sizeof(buffer));
2250
2251 len = encode_rfc(buffer, cc->buffer_start, cc->buffer_end - cc->buffer_start);
2252 if (len <= 0) RETURN_PARSE_ERROR(0);
2253
2254 if (len >= (size_t)(cc->buffer_end - cc->buffer_start)) {
2255 fr_strerror_const("Encoder output would overflow output buffer");
2257 }
2258
2260}
2261
2262/** Parse a list of pairs
2263 *
2264 */
2266 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2267{
2268 ssize_t slen;
2270 bool done = false;
2271 char *filename;
2272 FILE *fp;
2273
2274 filename = talloc_asprintf(cc->tmp_ctx, "%s/%s", cc->path, in);
2275
2276 fp = fopen(filename, "r");
2277 talloc_free(filename);
2278
2279 if (!fp) {
2280 fr_strerror_printf("Failed opening %s - %s", in, fr_syserror(errno));
2282 }
2283
2286 fclose(fp);
2287 if (slen < 0) {
2289 }
2290
2291 /*
2292 * Print the pairs.
2293 */
2295 if (slen <= 0) {
2296 fr_assert(0);
2298 }
2299
2300 if (!done) {
2301 strlcpy(data + slen, "!DONE", COMMAND_OUTPUT_MAX - slen);
2302 slen += 5;
2303 }
2304
2306
2307 RETURN_OK(slen);
2308}
2309
2310
2312 char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2313{
2315}
2316
2318 char *data, UNUSED size_t data_used, char *in, size_t inlen)
2319{
2321
2322 void *encode_ctx = NULL;
2323 ssize_t slen;
2324 char *p = in;
2325
2327 fr_pair_parse_t root, relative;
2328
2330
2331 slen = load_test_point_by_command((void **)&tp, p, "tp_encode_proto");
2332 if (!tp) {
2333 fr_strerror_const_push("Failed locating encode testpoint");
2334 CLEAR_TEST_POINT(cc);
2336 }
2337
2338 p += ((size_t)slen);
2340 if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx, dictionary_current(cc), NULL) < 0)) {
2341 fr_strerror_const_push("Failed initialising encoder testpoint");
2342 CLEAR_TEST_POINT(cc);
2344 }
2345
2346 root = (fr_pair_parse_t) {
2347 .ctx = cc->tmp_ctx,
2348 .da = cc->tmpl_rules.attr.namespace,
2349 .list = &head,
2350 .dict = cc->tmpl_rules.attr.namespace->dict,
2351 .internal = fr_dict_internal(),
2352 };
2353 relative = (fr_pair_parse_t) { };
2354
2355 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2356 if (slen <= 0) {
2357 CLEAR_TEST_POINT(cc);
2359 }
2360
2361 slen = tp->func(cc->tmp_ctx, &head, cc->buffer_start, cc->buffer_end - cc->buffer_start, encode_ctx);
2363 cc->last_ret = slen;
2364 if (slen < 0) {
2365 CLEAR_TEST_POINT(cc);
2367 }
2368 /*
2369 * Clear any spurious errors
2370 */
2372
2373 CLEAR_TEST_POINT(cc);
2374
2375 if ((cc->fuzzer_dir >= 0) &&
2376 (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, slen) < 0)) {
2378 }
2379
2381}
2382
2383/** Command eof
2384 *
2385 * Mark the end of a test file if we're reading from stdin.
2386 *
2387 * Doesn't actually do anything, is just a placeholder for the command processing loop.
2388 */
2390 UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2391{
2392 return 0;
2393}
2394
2395/** Enable fuzzer output
2396 *
2397 * Any commands that produce potentially useful corpus seed data will write that out data
2398 * to files in the specified directory, using the md5 of the text input at as the file name.
2399 *
2400 */
2402 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2403{
2404 int fd;
2405 struct stat sdir;
2406 char *fuzzer_dir;
2407 bool retry_dir = true;
2408
2409 /*
2410 * Close any open fuzzer output dirs
2411 */
2412 if (cc->fuzzer_dir >= 0) {
2413 close(cc->fuzzer_dir);
2414 cc->fuzzer_dir = -1;
2415 }
2416
2417 if (in[0] == '\0') {
2418 fr_strerror_const("Missing directory name");
2420 }
2421
2422 fuzzer_dir = talloc_asprintf(cc->tmp_ctx, "%s/%s",
2423 cc->config->fuzzer_dir ? cc->config->fuzzer_dir : cc->path, in);
2424
2425again:
2426 fd = open(fuzzer_dir, O_RDONLY);
2427 if (fd < 0) {
2428 if (mkdir(fuzzer_dir, 0777) == 0) {
2429 fd = open(fuzzer_dir, O_RDONLY);
2430 if (fd >= 0) goto stat;
2431 /*
2432 * Prevent race if multiple unit_test_attribute instances
2433 * attempt to create the same output dir.
2434 */
2435 } else if ((errno == EEXIST) && retry_dir) {
2436 retry_dir = false; /* Only allow this once */
2437 goto again;
2438 }
2439
2440 fr_strerror_printf("fuzzer-out \"%s\" doesn't exist: %s", fuzzer_dir, fr_syserror(errno));
2442 }
2443
2444stat:
2445 if (fstat(fd, &sdir) < 0) {
2446 close(fd);
2447 fr_strerror_printf("failed statting fuzzer-out \"%s\": %s", fuzzer_dir, fr_syserror(errno));
2449 }
2450
2451 if (!(sdir.st_mode & S_IFDIR)) {
2452 close(fd);
2453 fr_strerror_printf("fuzzer-out \"%s\" is not a directory", fuzzer_dir);
2455 }
2456 cc->fuzzer_dir = fd;
2457 talloc_free(fuzzer_dir);
2458
2459 return 0;
2460}
2461
2462/** Exit gracefully with the specified code
2463 *
2464 */
2466 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2467{
2468 if (!*in) RETURN_EXIT(0);
2469
2470 RETURN_EXIT(atoi(in));
2471}
2472
2474 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2475{
2476 char *name, *tmp = NULL;
2477 char const *dir;
2478 char *q;
2479 int ret;
2480
2482
2483 if (in[0] == '\0') {
2484 fr_strerror_const("Missing dictionary name");
2486 }
2487
2488 q = strchr(in, ' ');
2489 if (q) {
2490 name = tmp = talloc_bstrndup(NULL, in, q - in);
2491 q++;
2492 dir = q;
2493 } else {
2494 name = in;
2495 dir = cc->path;
2496 }
2497
2499 talloc_free(tmp);
2500 if (ret < 0) RETURN_COMMAND_ERROR();
2501
2502 RETURN_OK(0);
2503}
2504
2505
2506/** Compare the data buffer to an expected value
2507 *
2508 */
2510 char *data, size_t data_used, char *in, size_t inlen)
2511{
2512 if (strcmp(in, data) != 0) {
2513 if (write_fp) {
2514 strcpy(in, data);
2515 RETURN_OK(data_used);
2516 }
2517
2518 mismatch_print(cc, "match", in, inlen, data, data_used, true);
2519 RETURN_MISMATCH(data_used);
2520 }
2521
2522 /*
2523 * We didn't actually write anything, but this
2524 * keeps the contents of the data buffer around
2525 * for the next command to operate on.
2526 */
2527 RETURN_OK(data_used);
2528}
2529
2530/** Compare the data buffer against an expected expression
2531 *
2532 */
2534 char *data, size_t data_used, char *in, size_t inlen)
2535{
2536 ssize_t slen;
2537 regex_t *regex;
2538 int ret;
2539
2540 slen = regex_compile(cc->tmp_ctx, &regex, in, inlen, NULL, false, true);
2541 if (slen <= 0) RETURN_COMMAND_ERROR();
2542
2543 ret = regex_exec(regex, data, data_used, NULL);
2544 talloc_free(regex);
2545
2546 switch (ret) {
2547 case -1:
2548 default:
2550
2551 case 0:
2552 mismatch_print(cc, "match-regex", in, inlen, data, data_used, false);
2553 RETURN_MISMATCH(data_used);
2554
2555 case 1:
2556 RETURN_OK(data_used);
2557 }
2558}
2559
2560/** Artificially limit the maximum packet size.
2561 *
2562 */
2564 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2565{
2566 unsigned long size;
2567 char *end;
2568
2570
2571 if (*in != '\0') {
2572 size = strtoul(in, &end, 10);
2573 if ((size == ULONG_MAX) || *end || (size >= 65536)) {
2574 fr_strerror_const_push("Invalid integer");
2576 }
2577 } else {
2578 size = DEFAULT_BUFFER_SIZE;
2579 }
2580
2581 if (poisoned_buffer_allocate(cc, &cc->buffer, size) < 0) RETURN_EXIT(1);
2584
2585 RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "%ld", size));
2586}
2587
2588/** Set or clear migration flags.
2589 *
2590 */
2592 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2593{
2594 char *p;
2595 bool *out;
2596
2598 p = in;
2599
2600 if (strncmp(p, "xlat_new_functions", sizeof("xlat_new_functions") - 1) == 0) {
2601 p += sizeof("xlat_new_functions") - 1;
2603
2604 } else {
2605 fr_strerror_const("Unknown migration flag");
2607 }
2608
2610 if (*p != '=') {
2611 fr_strerror_const("Missing '=' after flag");
2613 }
2614 p++;
2615
2617 if ((strcmp(p, "yes") == 0) || (strcmp(p, "true") == 0) || (strcmp(p, "1") == 0)) {
2618 *out = true;
2619
2620 } else if ((strcmp(p, "no") == 0) || (strcmp(p, "false") == 0) || (strcmp(p, "0") == 0)) {
2621 *out = false;
2622
2623 } else {
2624 fr_strerror_const("Invalid value for flag");
2626 }
2627
2628 RETURN_OK(0);
2629}
2630
2631/** Skip the test file if we're missing a particular feature
2632 *
2633 */
2635 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2636{
2637 CONF_PAIR *cp;
2638
2639 if (in[0] == '\0') {
2640 fr_strerror_printf("Prerequisite syntax is \"need-feature <feature>\". "
2641 "Use -f to print features");
2643 }
2644
2645 cp = cf_pair_find(cc->config->features, in);
2646 if (!cp || (strcmp(cf_pair_value(cp), "yes") != 0)) {
2647 DEBUG("Skipping, missing feature \"%s\"", in);
2649 }
2650
2651 RETURN_NOOP(0);
2652}
2653
2654/** Negate the result of a match command or any command which returns "OK"
2655 *
2656 */
2658 char *data, size_t data_used, char *in, size_t inlen)
2659{
2660 data_used = process_line(result, cc, data, data_used, in, inlen);
2661 switch (result->rcode) {
2662 /*
2663 * OK becomes a command error
2664 */
2665 case RESULT_OK:
2666 ERROR("%s[%d]: %.*s: returned 'ok', where we expected 'result-mismatch'",
2667 cc->filename, cc->lineno, (int) inlen, in);
2668 RETURN_MISMATCH(data_used);
2669
2670 /*
2671 * Mismatch becomes OK
2672 */
2673 case RESULT_MISMATCH:
2674 RETURN_OK(data_used);
2675
2676 /*
2677 * The rest are unchanged...
2678 */
2679 default:
2680 break;
2681 }
2682
2683 return data_used;
2684}
2685
2686/** Parse an print an attribute pair or pair list.
2687 *
2688 */
2690 char *data, UNUSED size_t data_used, char *in, size_t inlen,
2691 bool allow_compare)
2692{
2694 ssize_t slen;
2695 fr_dict_t const *dict = dictionary_current(cc);
2696 fr_pair_parse_t root, relative;
2697
2699
2700 root = (fr_pair_parse_t) {
2701 .ctx = cc->tmp_ctx,
2702 .da = fr_dict_root(dict),
2703 .list = &head,
2704 .dict = dict,
2705 .internal = fr_dict_internal(),
2706 .allow_compare = allow_compare,
2707 };
2708 relative = (fr_pair_parse_t) { };
2709
2710 slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(in, inlen));
2711 if (slen <= 0) {
2712// fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
2715 }
2716
2717 /*
2718 * Output may be an error, and we ignore
2719 * it if so.
2720 */
2721
2723 if (slen <= 0) {
2726 }
2727
2729 RETURN_OK(slen);
2730}
2731
2733 char *data, size_t data_used, char *in, size_t inlen)
2734{
2735 return command_pair_common(result, cc, data, data_used, in, inlen, false);
2736}
2737
2739 char *data, size_t data_used, char *in, size_t inlen)
2740{
2741 return command_pair_common(result, cc, data, data_used, in, inlen, true);
2742}
2743
2744
2745/** Dynamically load a protocol library
2746 *
2747 */
2749 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2750{
2751 ssize_t slen;
2752
2753 if (*in == '\0') {
2754 fr_strerror_printf("Load syntax is \"proto <lib_name>\"");
2756 }
2757
2759 slen = load_proto_library(in);
2760 if (slen <= 0) RETURN_PARSE_ERROR(-(slen));
2761
2762 RETURN_OK(0);
2763}
2764
2766 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2767{
2769 return dictionary_load_common(result, cc, in, NULL);
2770}
2771
2773 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2774{
2775 fr_dict_t const *dict = dictionary_current(cc);
2776 fr_dict_attr_t const *root_da = fr_dict_root(dict);
2777 fr_dict_attr_t const *new_root;
2778
2779 if (is_whitespace(in) || (*in == '\0')) {
2780 new_root = fr_dict_root(dict);
2781 } else {
2782 new_root = fr_dict_attr_by_name(NULL, fr_dict_root(dict), in);
2783 if (!new_root) {
2784 fr_strerror_printf("dictionary attribute \"%s\" not found in %s", in, root_da->name);
2786 }
2787 }
2788
2789 cc->tmpl_rules.attr.namespace = new_root;
2790
2791 RETURN_OK(0);
2792}
2793
2794/** Parse an reprint a tmpl expansion
2795 *
2796 */
2798 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2799{
2800 ssize_t slen;
2801 tmpl_t *vpt;
2802 size_t input_len = strlen(in), escaped_len;
2803
2804 slen = tmpl_afrom_substr(cc->tmp_ctx, &vpt, &FR_SBUFF_IN(in, input_len), T_BARE_WORD,
2806 &(tmpl_rules_t) {
2807 .attr = {
2808 .dict_def = dictionary_current(cc),
2809 .list_def = request_attr_request,
2810 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2811 },
2812 .xlat = cc->tmpl_rules.xlat,
2813 });
2814 if (slen == 0) {
2815 fr_strerror_printf_push_head("ERROR failed to parse any input");
2817 }
2818
2819 if (slen < 0) {
2820 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
2821
2822 return_error:
2824 }
2825
2826 if (((size_t) slen != input_len)) {
2827 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
2828 goto return_error;
2829 }
2830
2831 escaped_len = tmpl_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), vpt, NULL);
2832 RETURN_OK(escaped_len);
2833}
2834
2835/** Touch a file to indicate a test completed
2836 *
2837 */
2839 UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2840{
2841 if (fr_unlink(in) < 0) RETURN_COMMAND_ERROR();
2842 if (fr_touch(NULL, in, 0644, true, 0755) <= 0) RETURN_COMMAND_ERROR();
2843
2844 RETURN_OK(0);
2845}
2846
2847/** Callback for a tmpl rule parser
2848 *
2849 */
2850typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value);
2851
2853{
2854 bool res;
2855 ssize_t slen;
2856
2857 slen = fr_sbuff_out_bool(&res, value);
2858 rules->attr.allow_foreign = res;
2859 return slen;
2860}
2861
2863{
2864 bool res;
2865 ssize_t slen;
2866
2867 slen = fr_sbuff_out_bool(&res, value);
2868 rules->attr.allow_unknown = res;
2869 return slen;
2870}
2871
2873{
2874 bool res;
2875 ssize_t slen;
2876
2877 slen = fr_sbuff_out_bool(&res, value);
2878 rules->attr.allow_unresolved = res;
2879 return slen;
2880}
2881
2883{
2885 fr_slen_t slen;
2886
2888 &rules->attr.namespace,
2889 rules->attr.dict_def ? fr_dict_root(rules->attr.dict_def) :
2891 value, NULL);
2893 return slen;
2894}
2895
2897{
2898 ssize_t slen;
2899
2901
2902 if (slen == 0) {
2903 fr_strerror_printf("Invalid list specifier \"%pV\"",
2905 }
2906
2907 return slen;
2908}
2909
2911{
2912 fr_slen_t slen;
2913
2914 slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
2915 &rules->attr.request_def,
2916 value);
2917 if (slen < 0) {
2918 fr_strerror_printf("Invalid request specifier \"%pV\"",
2920 }
2921
2922 return slen;
2923}
2924
2926 UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
2927{
2928 fr_sbuff_t sbuff = FR_SBUFF_IN(in, inlen);
2929 ssize_t slen;
2931 void *res;
2932
2933 static fr_table_ptr_sorted_t tmpl_rule_func_table[] = {
2934 { L("allow_foreign"), (void *)command_tmpl_rule_allow_foreign },
2935 { L("allow_unknown"), (void *)command_tmpl_rule_allow_unknown },
2936 { L("allow_unresolved"), (void *)command_tmpl_rule_allow_unresolved },
2937 { L("attr_parent"), (void *)command_tmpl_rule_attr_parent },
2938 { L("list_def"), (void *)command_tmpl_rule_list_def },
2939 { L("request_def"), (void *)command_tmpl_rule_request_def }
2940 };
2941 static size_t tmpl_rule_func_table_len = NUM_ELEMENTS(tmpl_rule_func_table);
2942
2943 while (fr_sbuff_extend(&sbuff)) {
2944 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2945
2946 fr_sbuff_out_by_longest_prefix(&slen, &res, tmpl_rule_func_table, &sbuff, NULL);
2947 if (res == NULL) {
2948 fr_strerror_printf("Specified rule \"%pV\" is invalid",
2951 }
2952 func = (command_tmpl_rule_func)res; /* -Wpedantic */
2953
2954 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2955
2956 if (!fr_sbuff_next_if_char(&sbuff, '=')) {
2957 fr_strerror_printf("Expected '=' after rule identifier, got \"%pV\"",
2960 }
2961
2962 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2963
2964 if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
2965 }
2966
2967 return fr_sbuff_used(&sbuff);
2968}
2969
2971 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2972{
2973 fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2974 fr_value_box_t *box2;
2975 char const *value;
2976 size_t match_len;
2977 ssize_t slen;
2979
2980 match_len = parse_typed_value(result, cc, box, &value, in, strlen(in));
2981 if (match_len == 0) {
2982 talloc_free(box);
2983 return 0; /* errors have already been updated */
2984 }
2985
2986 type = box->type;
2987
2988 /*
2989 * Don't print dates with enclosing quotation marks.
2990 */
2991 if (type != FR_TYPE_DATE) {
2994 } else {
2996 }
2997 if (slen <= 0) {
2998 talloc_free(box);
3000 }
3001
3002 /*
3003 * Behind the scenes, parse the data
3004 * string. We should get the same value
3005 * box as last time.
3006 */
3007 box2 = talloc_zero(NULL, fr_value_box_t);
3008 if (fr_value_box_from_str(box2, box2, type, box->enumv,
3009 data, slen,
3011 talloc_free(box2);
3012 talloc_free(box);
3014 }
3015
3016 /*
3017 * They MUST be identical
3018 */
3019 if (fr_value_box_cmp(box, box2) != 0) {
3020 fr_strerror_const("ERROR value box reparsing failed. Results not identical");
3021 fr_strerror_printf_push("out: %pV (as string %.*s)", box2, (int) slen, data);
3022 fr_strerror_printf_push("in: %pV (from string %s)", box, value);
3023 talloc_free(box2);
3024 talloc_free(box);
3026 }
3027
3028 /*
3029 * Store <type><value str...>
3030 */
3031 if (cc->fuzzer_dir >= 0) {
3032 char fuzzer_buffer[1024];
3033 char *fuzzer_p = fuzzer_buffer, *fuzzer_end = fuzzer_p + sizeof(fuzzer_buffer);
3034
3035 *fuzzer_p++ = (uint8_t)type; /* Fuzzer uses first byte for type */
3036
3037 strlcpy(fuzzer_p, data, slen > fuzzer_end - fuzzer_p ? fuzzer_end - fuzzer_p : slen);
3038
3039 if (dump_fuzzer_data(cc->fuzzer_dir, fuzzer_buffer,
3040 (uint8_t *)fuzzer_buffer, strlen(fuzzer_buffer)) < 0) {
3042 }
3043 }
3044
3045 talloc_free(box2);
3046 talloc_free(box);
3047 RETURN_OK(slen);
3048}
3049
3051 char *data, size_t data_used, char *in, size_t inlen)
3052{
3053 int fd;
3054 char *path;
3055 bool locked = false;
3056
3057 path = talloc_bstrndup(cc->tmp_ctx, in, inlen);
3058
3059 fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
3060 if (fd < 0) {
3061 fr_strerror_printf("Failed opening \"%s\": %s", path, fr_syserror(errno));
3062 error:
3063 talloc_free(path);
3064 if (fd >= 0) {
3065 if (locked) (void)flock(fd, LOCK_UN);
3066 close(fd);
3067 }
3069 }
3070
3071 if (flock(fd, LOCK_EX) < 0) {
3072 fr_strerror_printf("Failed locking \"%s\": %s", path, fr_syserror(errno));
3073 goto error;
3074 }
3075 locked = true;
3076
3077 while (data_used) {
3078 ssize_t ret;
3079 ret = write(fd, data, data_used);
3080 if (ret < 0) {
3081 fr_strerror_printf("Failed writing to \"%s\": %s", path, fr_syserror(errno));
3082 goto error;
3083 }
3084 data_used -= ret;
3085 data += ret;
3086 }
3087 (void)flock(fd, LOCK_UN);
3088 talloc_free(path);
3089 close(fd);
3090
3091 RETURN_OK(data_used);
3092}
3093
3094/** Parse an reprint and xlat expansion
3095 *
3096 */
3098 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3099{
3100 ssize_t slen;
3101 xlat_exp_head_t *head = NULL;
3102 size_t input_len = strlen(in), escaped_len;
3103 fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
3104
3105 slen = xlat_tokenize(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), &p_rules,
3106 &(tmpl_rules_t) {
3107 .attr = {
3108 .dict_def = dictionary_current(cc),
3109 .list_def = request_attr_request,
3110 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
3111 },
3112 .xlat = cc->tmpl_rules.xlat,
3113 });
3114 if (slen == 0) {
3115 fr_strerror_printf_push_head("ERROR failed to parse any input");
3117 }
3118
3119 if (slen < 0) {
3120 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3121
3122 return_error:
3124 }
3125
3126 if (((size_t) slen != input_len)) {
3127 fr_strerror_printf_push_head("offset %d 'Too much text'", (int) slen);
3128 goto return_error;
3129 }
3130
3132 RETURN_OK(escaped_len);
3133}
3134
3135/** Parse and reprint an xlat expression expansion
3136 *
3137 */
3139 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3140{
3141 ssize_t dec_len;
3142 xlat_exp_head_t *head = NULL;
3143 size_t input_len = strlen(in), escaped_len;
3144// fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
3145
3146 dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL,
3147 &(tmpl_rules_t) {
3148 .attr = {
3149 .dict_def = dictionary_current(cc),
3150 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3151 .list_def = request_attr_request,
3152 }
3153 });
3154 if (dec_len <= 0) {
3155 fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
3156
3157 return_error:
3159 }
3160
3161 if (((size_t) dec_len != input_len)) {
3162 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, dec_len);
3163 goto return_error;
3164 }
3165
3167 RETURN_OK(escaped_len);
3168}
3169
3170/** Parse, purify, and reprint an xlat expression expansion
3171 *
3172 */
3174 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3175{
3176 ssize_t slen;
3177 xlat_exp_head_t *head = NULL;
3178 size_t input_len = strlen(in), escaped_len;
3179 tmpl_rules_t t_rules = (tmpl_rules_t) {
3180 .attr = {
3182 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3183 .list_def = request_attr_request,
3184 },
3185 .xlat = cc->tmpl_rules.xlat,
3186 .at_runtime = true,
3187 };
3188
3189 if (!el) {
3190 fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
3191 goto return_error;
3192 }
3193 t_rules.xlat.runtime_el = el;
3194
3195 slen = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
3196 if (slen == 0) {
3197 fr_strerror_printf_push_head("ERROR failed to parse any input");
3199 }
3200
3201 if (slen < 0) {
3202 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3203 return_error:
3205 }
3206
3207 if (((size_t) slen != input_len)) {
3208 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, slen);
3209 goto return_error;
3210 }
3211
3212 if (fr_debug_lvl > 2) {
3213 DEBUG("Before purify --------------------------------------------------");
3215 }
3216
3217 if (xlat_purify(head, NULL) < 0) {
3218 fr_strerror_printf_push_head("ERROR purifying node - %s", fr_strerror());
3219 goto return_error;
3220 }
3221
3222 if (fr_debug_lvl > 2) {
3223 DEBUG("After purify --------------------------------------------------");
3225 }
3226
3228 RETURN_OK(escaped_len);
3229}
3230
3231
3232/** Parse, purify, and reprint an xlat expression expansion
3233 *
3234 */
3236 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3237{
3238 ssize_t slen;
3239 xlat_exp_head_t *head = NULL;
3240 size_t input_len = strlen(in), escaped_len;
3241 tmpl_rules_t t_rules = (tmpl_rules_t) {
3242 .attr = {
3244 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
3245 .list_def = request_attr_request,
3246 },
3247 .xlat = cc->tmpl_rules.xlat,
3248 .at_runtime = true,
3249 };
3250
3251 if (!el) {
3252 fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
3253 goto return_error;
3254 }
3255 t_rules.xlat.runtime_el = el;
3256
3257 slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
3258 if (slen == 0) {
3259 fr_strerror_printf_push_head("ERROR failed to parse any input");
3261 }
3262
3263 if (slen < 0) {
3264 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen - 1);
3265 return_error:
3267 }
3268
3269 if (((size_t) slen != input_len)) {
3270 fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, slen);
3271 goto return_error;
3272 }
3273
3274 if (fr_debug_lvl > 2) {
3275 DEBUG("Before purify --------------------------------------------------");
3277 }
3278
3279 if (xlat_purify(head, NULL) < 0) {
3280 fr_strerror_printf_push_head("ERROR purifying node - %s", fr_strerror());
3281 goto return_error;
3282 }
3283
3284 if (fr_debug_lvl > 2) {
3285 DEBUG("After purify --------------------------------------------------");
3287 }
3288
3290 RETURN_OK(escaped_len);
3291}
3292
3293
3294/** Parse an reprint and xlat argv expansion
3295 *
3296 */
3298 char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
3299{
3300 int i, argc;
3301 char *p;
3302 ssize_t slen;
3303 xlat_exp_head_t *head = NULL;
3304 xlat_exp_head_t **argv;
3305 size_t len;
3306 size_t input_len = strlen(in);
3307 char buff[1024];
3308
3309 slen = xlat_tokenize_argv(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len),
3310 NULL, NULL,
3311 &(tmpl_rules_t) {
3312 .attr = {
3313 .dict_def = dictionary_current(cc),
3314 .list_def = request_attr_request,
3315 .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
3316 },
3317 }, true);
3318 if (slen <= 0) {
3319 fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
3321 }
3322
3323 argc = xlat_flatten_to_argv(cc->tmp_ctx, &argv, head);
3324 if (argc <= 0) {
3325 fr_strerror_printf_push("ERROR in argument %d", (int) -argc);
3327 }
3328
3329 for (i = 0, p = data; i < argc; i++) {
3330 (void) xlat_print(&FR_SBUFF_OUT(buff, sizeof(buff)), argv[i], NULL);
3331
3332 len = snprintf(p, data + COMMAND_OUTPUT_MAX - p, "[%d]{ %s }, ", i, buff);
3333 p += len;
3334 }
3335
3336 p -= 2;
3337 *p = '\0';
3338
3339 RETURN_OK(p - data);
3340}
3341
3343 { L("#"), &(command_entry_t){
3344 .func = command_comment,
3345 .usage = "#<string>",
3346 .description = "A comment - not processed"
3347 }},
3348 { L("$INCLUDE "), &(command_entry_t){
3349 .func = command_include,
3350 .usage = "$INCLUDE <relative_path>",
3351 .description = "Execute a test file"
3352 }},
3353 { L("allow-unresolved "), &(command_entry_t){
3355 .usage = "allow-unresolved yes|no",
3356 .description = "Allow or disallow unresolved attributes in xlats and references"
3357 }},
3358 { L("attr.children"), &(command_entry_t){
3359 .func = command_attr_children,
3360 .usage = "attr.children",
3361 .description = "Return the children of the named attribute",
3362 }},
3363 { L("attr.flags"), &(command_entry_t){
3364 .func = command_attr_flags,
3365 .usage = "attr.flags",
3366 .description = "Return the flags of the named attribute",
3367 }},
3368 { L("attr.name"), &(command_entry_t){
3369 .func = command_attr_name,
3370 .usage = "attr.name",
3371 .description = "Return the number of the named attribute",
3372 }},
3373#if 0
3374 { L("attr.number"), &(command_entry_t){
3375 .func = command_attr_number,
3376 .usage = "attr.number",
3377 .description = "Return the number of the named attribute",
3378 }},
3379#endif
3380 { L("attr.oid"), &(command_entry_t){
3381 .func = command_attr_oid,
3382 .usage = "attr.oid",
3383 .description = "Return the OID of the named attribute",
3384 }},
3385#if 0
3386 { L("attr.ref"), &(command_entry_t){
3387 .func = command_attr_ref,
3388 .usage = "attr.ref",
3389 .description = "Return the reference (if any) of the named attribute",
3390 }},
3391#endif
3392 { L("attr.type"), &(command_entry_t){
3393 .func = command_attr_type,
3394 .usage = "attr.type",
3395 .description = "Return the data type of the named attribute",
3396 }},
3397 { L("calc "), &(command_entry_t){
3398 .func = command_calc,
3399 .usage = "calc <type1> <value1> <operator> <type2> <value2> -> <output-type>",
3400 .description = "Perform calculations on value boxes",
3401 }},
3402 { L("calc_nary "), &(command_entry_t){
3403 .func = command_calc_nary,
3404 .usage = "calc_nary op <type1> <value1> <type2> <value2> ... -> <output-type>",
3405 .description = "Perform calculations on value boxes",
3406 }},
3407 { L("cast "), &(command_entry_t){
3408 .func = command_cast,
3409 .usage = "cast (type) <value> -> <output-type>",
3410 .description = "Perform calculations on value boxes",
3411 }},
3412 { L("cd "), &(command_entry_t){
3413 .func = command_cd,
3414 .usage = "cd <path>",
3415 .description = "Change the directory for loading dictionaries and $INCLUDEs, writing the full path into the data buffer on success"
3416 }},
3417 { L("clear"), &(command_entry_t){
3418 .func = command_clear,
3419 .usage = "clear",
3420 .description = "Explicitly zero out the contents of the data buffer"
3421 }},
3422 { L("command add "), &(command_entry_t){
3423 .func = command_radmin_add,
3424 .usage = "command add <string>",
3425 .description = "Add a command to a radmin command tree"
3426 }},
3427 { L("command tab "), &(command_entry_t){
3428 .func = command_radmin_tab,
3429 .usage = "command tab <string>",
3430 .description = "Test a tab completion against a radmin command tree"
3431 }},
3432 { L("condition "), &(command_entry_t){
3434 .usage = "condition <string>",
3435 .description = "Parse and reprint a condition, writing the normalised condition to the data buffer on success"
3436 }},
3437 { L("count"), &(command_entry_t){
3438 .func = command_count,
3439 .usage = "count",
3440 .description = "Write the number of executed tests to the data buffer. A test is any command that should return 'ok'"
3441 }},
3442 { L("decode-dns-label "), &(command_entry_t){
3444 .usage = "decode-dns-label (-|<hex_string>)",
3445 .description = "Decode one or more DNS labels, writing the decoded strings to the data buffer.",
3446 }},
3447 { L("decode-pair"), &(command_entry_t){
3448 .func = command_decode_pair,
3449 .usage = "decode-pair[.<testpoint_symbol>] (-|<hex_string>)",
3450 .description = "Produce an attribute value pair from a binary value using a specified protocol decoder. Protocol must be loaded with \"load <protocol>\" first",
3451 }},
3452 { L("decode-proto"), &(command_entry_t){
3453 .func = command_decode_proto,
3454 .usage = "decode-proto[.<testpoint_symbol>] (-|<hex string>)",
3455 .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",
3456 }},
3457 { L("dictionary "), &(command_entry_t){
3459 .usage = "dictionary <string>",
3460 .description = "Parse dictionary attribute definition, writing \"ok\" to the data buffer if successful",
3461 }},
3462 { L("dictionary-dump"), &(command_entry_t){
3464 .usage = "dictionary-dump",
3465 .description = "Print the contents of the currently active dictionary to stdout",
3466 }},
3467 { L("encode-dns-label "), &(command_entry_t){
3469 .usage = "encode-dns-label (-|string[,string])",
3470 .description = "Encode one or more DNS labels, writing a hex string to the data buffer.",
3471 }},
3472 { L("encode-pair"), &(command_entry_t){
3473 .func = command_encode_pair,
3474 .usage = "encode-pair[.<testpoint_symbol>] [truncate] (-|<attribute> = <value>[,<attribute = <value>])",
3475 .description = "Encode one or more attribute value pairs, writing a hex string to the data buffer. Protocol must be loaded with \"load <protocol>\" first",
3476 }},
3477 { L("encode-proto"), &(command_entry_t){
3478 .func = command_encode_proto,
3479 .usage = "encode-proto[.<testpoint_symbol>] (-|<attribute> = <value>[,<attribute = <value>])",
3480 .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"
3481 }},
3482 { L("eof"), &(command_entry_t){
3483 .func = command_eof,
3484 .usage = "eof",
3485 .description = "Mark the end of a 'virtual' file. Used to prevent 'need-feature' skipping all the content of a command stream or file",
3486 }},
3487 { L("exit"), &(command_entry_t){
3488 .func = command_exit,
3489 .usage = "exit[ <num>]",
3490 .description = "Exit with the specified error number. If no <num> is provided, process will exit with 0"
3491 }},
3492 { L("fuzzer-out"), &(command_entry_t){
3493 .func = command_fuzzer_out,
3494 .usage = "fuzzer-out <dir>",
3495 .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",
3496 }},
3497 { L("load-dictionary "),&(command_entry_t){
3499 .usage = "load-dictionary <name> [<dir>]",
3500 .description = "Load an additional dictionary from the same directory as the input file. "
3501 "Optionally you can specify a full path via <dir>. ",
3502 }},
3503 { L("match"), &(command_entry_t){
3504 .func = command_match,
3505 .usage = "match <string>",
3506 .description = "Compare the contents of the data buffer with an expected value"
3507 }},
3508 { L("match-regex "), &(command_entry_t){
3509 .func = command_match_regex,
3510 .usage = "match-regex <regex>",
3511 .description = "Compare the contents of the data buffer with a regular expression"
3512 }},
3513 { L("max-buffer-size"), &(command_entry_t){
3515 .usage = "max-buffer-size[ <integer>]",
3516 .description = "Limit the maximum temporary buffer space available for any command which uses it"
3517 }},
3518 { L("migrate "), &(command_entry_t){
3519 .func = command_migrate,
3520 .usage = "migrate <flag>=<value>",
3521 .description = "Set migration flag"
3522 }},
3523 { L("need-feature "), &(command_entry_t){
3524 .func = command_need_feature,
3525 .usage = "need-feature <feature>",
3526 .description = "Skip the contents of the current file, or up to the next \"eof\" command if a particular feature is not available"
3527 }},
3528 { L("no "), &(command_entry_t){
3529 .func = command_no,
3530 .usage = "no ...",
3531 .description = "Negate the result of a command returning 'ok'"
3532 }},
3533 { L("pair "), &(command_entry_t){
3534 .func = command_pair,
3535 .usage = "pair ... data ...",
3536 .description = "Parse a list of pairs",
3537 }},
3538 { L("pair-compare "), &(command_entry_t){
3539 .func = command_pair_compare,
3540 .usage = "pair-compare ... data ...",
3541 .description = "Parse a list of pairs, allowing comparison operators",
3542 }},
3543 { L("proto "), &(command_entry_t){
3544 .func = command_proto,
3545 .usage = "proto <protocol>",
3546 .description = "Switch the active protocol to the one specified, unloading the previous protocol",
3547 }},
3548 { L("proto-dictionary "),&(command_entry_t){
3550 .usage = "proto-dictionary <proto_name> [<proto_dir>]",
3551 .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.",
3552 }},
3553
3554
3555 { L("proto-dictionary-root "), &(command_entry_t){
3557 .usage = "proto-dictionary-root[ <root_attribute>]",
3558 .description = "Set the root attribute for the current protocol dictionary. "
3559 "If no attribute name is provided, the root will be reset to the root of the current dictionary",
3560 }},
3561 { L("raw "), &(command_entry_t){
3562 .func = command_encode_raw,
3563 .usage = "raw <string>",
3564 .description = "Create nested attributes from OID strings and values"
3565 }},
3566 { L("read_file "), &(command_entry_t){
3567 .func = command_read_file,
3568 .usage = "read_file <filename>",
3569 .description = "Read a list of pairs from a file",
3570 }},
3571 { L("returned"), &(command_entry_t){
3572 .func = command_returned,
3573 .usage = "returned",
3574 .description = "Print the returned value to the data buffer"
3575 }},
3576
3577 { L("tmpl "), &(command_entry_t){
3578 .func = command_tmpl,
3579 .usage = "parse <string>",
3580 .description = "Parse then print a tmpl expansion, writing the normalised tmpl expansion to the data buffer"
3581 }},
3582
3583 { L("tmpl-rules "), &(command_entry_t){
3584 .func = command_tmpl_rules,
3585 .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]",
3586 .description = "Alter the tmpl parsing rules for subsequent tmpl parsing commands in the same command context"
3587 }},
3588 { L("touch "), &(command_entry_t){
3589 .func = command_touch,
3590 .usage = "touch <file>",
3591 .description = "Touch a file, updating its created timestamp. Useful for marking the completion of a series of tests"
3592 }},
3593 { L("value "), &(command_entry_t){
3595 .usage = "value <type> <string>",
3596 .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"
3597 }},
3598 { L("write "), &(command_entry_t){
3599 .func = command_write,
3600 .usage = "write <file>",
3601 .description = "Write the contents of the data buffer (as a raw binary string) to the specified file"
3602 }},
3603 { L("xlat "), &(command_entry_t){
3604 .func = command_xlat_normalise,
3605 .usage = "xlat <string>",
3606 .description = "Parse then print an xlat expansion, writing the normalised xlat expansion to the data buffer"
3607 }},
3608
3609 { L("xlat_argv "), &(command_entry_t){
3610 .func = command_xlat_argv,
3611 .usage = "xlat_argv <string>",
3612 .description = "Parse then print an xlat expansion argv, writing the normalised xlat expansion arguments to the data buffer"
3613 }},
3614
3615 { L("xlat_expr "), &(command_entry_t){
3616 .func = command_xlat_expr,
3617 .usage = "xlat_expr <string>",
3618 .description = "Parse then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3619 }},
3620
3621 { L("xlat_purify "), &(command_entry_t){
3622 .func = command_xlat_purify,
3623 .usage = "xlat_purify <string>",
3624 .description = "Parse, purify, then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3625 }},
3626
3627 { L("xlat_purify_cond "), &(command_entry_t){
3629 .usage = "xlat_purify_cond <string>",
3630 .description = "Parse, purify, then print an xlat condition, writing the normalised xlat expansion to the data buffer"
3631 }},
3632
3633};
3635
3636size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used,
3637 char *in, UNUSED size_t inlen)
3638{
3639
3640 command_entry_t *command;
3641 size_t match_len;
3642 char *p;
3643
3644 p = in;
3646
3647 /*
3648 * Skip empty lines and comments.
3649 */
3650 if (!*p || (*p == '#')) {
3651 /*
3652 * Dump the input to the output.
3653 */
3654 if (write_fp) {
3655 fputs(in, write_fp);
3656 fputs("\n", write_fp);
3657 }
3658
3659 RETURN_NOOP(data_used);
3660 }
3661
3662 DEBUG2("%s[%d]: %s", cc->filename, cc->lineno, p);
3663
3664 /*
3665 * Look up the command by longest prefix
3666 */
3667 command = fr_table_value_by_longest_prefix(&match_len, commands, p, -1, NULL);
3668 if (!command) {
3669 fr_strerror_printf("Unknown command: %s", p);
3671 }
3672
3673 p += match_len; /* Jump to after the command */
3674 fr_skip_whitespace(p); /* Skip any whitespace */
3675
3676 /*
3677 * Feed the data buffer in as the command
3678 */
3679 if ((p[0] == '-') && ((p[1] == ' ') || (p[1] == '\0'))) {
3680 data_used = command->func(result, cc, data, data_used, data, data_used);
3681 }
3682 else {
3683 data_used = command->func(result, cc, data, data_used, p, strlen(p));
3684 }
3685
3686 /*
3687 * Dump the contents of the error stack
3688 * to the data buffer.
3689 *
3690 * This is then what's checked in
3691 * subsequent match commands.
3692 */
3693 if (result->error_to_data) data_used = strerror_concat(data, COMMAND_OUTPUT_MAX);
3694
3695 fr_assert((size_t)data_used < COMMAND_OUTPUT_MAX);
3696 data[data_used] = '\0'; /* Ensure the data buffer is \0 terminated */
3697
3698 if (data_used) {
3699 DEBUG2("%s[%d]: --> %s (%zu bytes in buffer)", cc->filename, cc->lineno,
3700 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"), data_used);
3701 } else {
3702 DEBUG2("%s[%d]: --> %s", cc->filename, cc->lineno,
3703 fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"));
3704 }
3705
3706 /*
3707 * Dump the input to the output.
3708 */
3709 if (write_fp) {
3710 fputs(in, write_fp);
3711 fputs("\n", write_fp);
3712 };
3713
3714 talloc_free_children(cc->tmp_ctx);
3715
3716 return data_used;
3717}
3718
3720{
3721 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3722 fr_perror("unit_test_attribute");
3723 return -1;
3724 }
3725 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) {
3726 fr_perror("unit_test_attribute");
3727 return -1;
3728 }
3729 if (cc->fuzzer_dir >= 0) {
3730 close(cc->fuzzer_dir);
3731 cc->fuzzer_dir = -1;
3732 }
3733 return 0;
3734}
3735
3737 command_config_t const *config, char const *path, char const *filename)
3738{
3740
3741 cc = talloc_zero(ctx, command_file_ctx_t);
3742 talloc_set_destructor(cc, _command_ctx_free);
3743
3744 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3745 cc->path = talloc_strdup(cc, path);
3746 cc->filename = filename;
3747 cc->config = config;
3748
3749 /*
3750 * Allocate a special buffer with poisoned regions
3751 * at either end.
3752 */
3754 talloc_free(cc);
3755 return NULL;
3756 }
3759
3760 /*
3761 * Initialise a special temporary dictionary context
3762 *
3763 * Any protocol dictionaries loaded by "test-dictionary"
3764 * go in this context, and don't affect the main
3765 * dictionary context.
3766 */
3767 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3768 if (!cc->test_gctx) {
3769 fr_perror("Failed allocating test dict_gctx");
3770 return NULL;
3771 }
3772
3775 fr_perror("Failed loading test dict_gctx internal dictionary");
3776 return NULL;
3777 }
3778
3779 fr_dict_global_ctx_dir_set(cc->path); /* Load new dictionaries relative to the test file */
3781
3782 cc->fuzzer_dir = -1;
3783
3785 cc->tmpl_rules.attr.namespace = fr_dict_root(cc->config->dict);
3786 cc->tmpl_rules.attr.allow_unresolved = false; /* tests have to use real attributes */
3787
3788 return cc;
3789}
3790
3791static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
3792{
3793 talloc_free(cc->tmp_ctx);
3794 cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3795 cc->test_count = 0;
3796
3797 if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3798 fr_perror("unit_test_attribute");
3799 }
3800
3801 if (fr_dict_global_ctx_free(cc->test_gctx) < 0) fr_perror("unit_test_attribute");
3802
3803 cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3805 fr_perror("Failed loading test dict_gctx internal dictionary");
3806 }
3807
3808 if (cc->fuzzer_dir >= 0) {
3809 close(cc->fuzzer_dir);
3810 cc->fuzzer_dir = -1;
3811 }
3812}
3813
3814static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config,
3815 const char *root_dir, char const *filename, fr_dlist_head_t *lines)
3816{
3817 int ret = 0;
3818 FILE *fp; /* File we're reading from */
3819 char buffer[8192]; /* Command buffer */
3820 char data[COMMAND_OUTPUT_MAX + 1]; /* Data written by previous command */
3821 ssize_t data_used = 0; /* How much data the last command wrote */
3822 static char path[PATH_MAX] = "";
3823 command_line_range_t *lr = NULL;
3824 bool opened_fp = false;
3825
3827
3828 cc = command_ctx_alloc(ctx, config, root_dir, filename);
3829
3830 /*
3831 * Open the file, or stdin
3832 */
3833 if (strcmp(filename, "-") == 0) {
3834 fp = stdin;
3835 filename = "<stdin>";
3836 } else {
3837 if (root_dir && *root_dir) {
3838 snprintf(path, sizeof(path), "%s/%s", root_dir, filename);
3839 } else {
3840 strlcpy(path, filename, sizeof(path));
3841 }
3842
3843 fp = fopen(path, "r");
3844 if (!fp) {
3845 ERROR("Error opening test file \"%s\": %s", path, fr_syserror(errno));
3846 ret = -1;
3847 goto finish;
3848 }
3849
3850 filename = path;
3851 opened_fp = true;
3852 }
3853
3854 if (lines && !fr_dlist_empty(lines)) lr = fr_dlist_head(lines);
3855
3856 /*
3857 * Loop over lines in the file or stdin
3858 */
3859 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3860 command_result_t result = { .rcode = RESULT_OK }; /* Reset to OK */
3861 char *p = strchr(buffer, '\n');
3862
3864 cc->lineno++; /* The first line of the file becomes line 1 */
3865
3866 if (lr) {
3867 if (cc->lineno > lr->end) {
3868 lr = fr_dlist_next(lines, lr);
3869 if (!lr) goto finish;
3870 }
3871
3872 if (cc->lineno < lr->start) continue;
3873 }
3874
3875 if (!p) {
3876 if (!feof(fp)) {
3877 ERROR("Line %d too long in %s/%s", cc->lineno, cc->path, cc->filename);
3878 ret = -1;
3879 goto finish;
3880 }
3881 } else {
3882 *p = '\0';
3883 }
3884
3885 data_used = process_line(&result, cc, data, data_used, buffer, strlen(buffer));
3886 switch (result.rcode) {
3887 /*
3888 * Command completed successfully
3889 */
3890 case RESULT_OK:
3891 cc->test_count++;
3892 continue;
3893
3894 /*
3895 * Did nothing (not a test)
3896 */
3897 case RESULT_NOOP:
3898 continue;
3899
3900 /*
3901 * If this is a file, then break out of the loop
3902 * and cleanup, otherwise we need to find the
3903 * EOF marker in the input stream.
3904 */
3905 case RESULT_SKIP_FILE:
3906 if (fp != stdin) goto finish;
3907
3908 /*
3909 * Skip over the input stream until we
3910 * find an eof command, or the stream
3911 * is closed.
3912 */
3913 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3914 command_entry_t *command;
3915 size_t match_len;
3916
3917 command = fr_table_value_by_longest_prefix(&match_len, commands, buffer, -1, NULL);
3918 if (!command) {
3919 ERROR("%s[%d]: Unknown command: %s", cc->path, cc->lineno, p);
3920 ret = -1;
3921 goto finish;
3922 }
3923
3924 if (command->func == command_eof) {
3925 command_ctx_reset(cc, ctx);
3926 break;
3927 }
3928 }
3929 goto finish;
3930
3931 /*
3932 * Fatal error parsing a command
3933 */
3934 case RESULT_PARSE_ERROR:
3936 fr_perror("%s[%d]", filename, cc->lineno);
3937 ret = -1;
3938 goto finish;
3939
3940 /*
3941 * Result didn't match what we expected
3942 */
3943 case RESULT_MISMATCH:
3944 {
3945 ret = EXIT_FAILURE;
3946 goto finish;
3947 }
3948
3949 case RESULT_EXIT:
3950 ret = result.ret;
3951 *exit_now = true;
3952 goto finish;
3953
3954 default:
3955 /*
3956 * If this happens, fix the damn command.
3957 */
3958 fr_assert_msg(false, "Command exited with invalid return code (%i)", result.rcode);
3959 ret = -1;
3960 goto finish;
3961 }
3962 }
3963
3964finish:
3965 /* The explicit check is to quiet clang_analyzer */
3966 if (opened_fp) fclose(fp);
3967
3968 /*
3969 * Free any residual resources we loaded.
3970 */
3971 if (cc && (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0)) {
3972 fr_perror("unit_test_attribute");
3973 ret = -1;
3974 }
3975
3976 fr_dict_global_ctx_set(config->dict_gctx); /* Switch back to the main dict ctx */
3978 talloc_free(cc);
3979
3980 return ret;
3981}
3982
3983static void usage(char const *name)
3984{
3985 INFO("usage: %s [options] (-|<filename>[:<lines>] [ <filename>[:<lines>]])", name);
3986 INFO("options:");
3987 INFO(" -d <raddb> Set user dictionary path (defaults to " RADDBDIR ").");
3988 INFO(" -D <dictdir> Set main dictionary path (defaults to " DICTDIR ").");
3989 INFO(" -x Debugging mode.");
3990 INFO(" -f Print features.");
3991 INFO(" -c Print commands.");
3992 INFO(" -h Print help text.");
3993 INFO(" -M Show talloc memory report.");
3994 INFO(" -p Allow xlat_purify");
3995 INFO(" -r <receipt_file> Create the <receipt_file> as a 'success' exit.");
3996 INFO(" -w <output_file> Write 'corrected' output to <output_file>.");
3997 INFO("Where <filename> is a file containing one or more commands and '-' indicates commands should be read from stdin.");
3998 INFO("Ranges of <lines> may be specified in the format <start>[-[<end>]][,]");
3999}
4000
4001static void features_print(CONF_SECTION *features)
4002{
4003 CONF_PAIR *cp;
4004
4005 INFO("features:");
4006 for (cp = cf_pair_find(features, CF_IDENT_ANY);
4007 cp;
4008 cp = cf_pair_find_next(features, cp, CF_IDENT_ANY)) {
4009 INFO(" %s %s", cf_pair_attr(cp), cf_pair_value(cp));
4010 }
4011}
4012
4013static void commands_print(void)
4014{
4015 size_t i;
4016
4017 INFO("commands:");
4018 for (i = 0; i < commands_len; i++) {
4019 INFO(" %s:", ((command_entry_t const *)commands[i].value)->usage);
4020 INFO(" %s.", ((command_entry_t const *)commands[i].value)->description);
4021 INFO("%s", "");
4022 }
4023}
4024
4025static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *in)
4026{
4027 static bool tokens[UINT8_MAX + 1] = { [','] = true , ['-'] = true };
4028 uint32_t max = 0;
4031
4032 while (fr_sbuff_extend(in)) {
4033 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4034
4035 MEM(lr = talloc_zero(ctx, command_line_range_t));
4037
4038 fr_sbuff_out(&err, &lr->start, in);
4039 if (err != FR_SBUFF_PARSE_OK) {
4040 ERROR("Invalid line start number");
4041 error:
4043 return -1;
4044 }
4045 if (max > lr->start) {
4046 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->start);
4047 goto error;
4048 } else {
4049 max = lr->start;
4050 }
4051 lr->end = lr->start; /* Default to a single line */
4052 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4053
4054 again:
4055 if (!fr_sbuff_extend(in)) break;
4056 if (!fr_sbuff_is_in_charset(in, tokens)) {
4057 ERROR("Unexpected text \"%pV\"",
4059 goto error;
4060 }
4061
4062 fr_sbuff_switch(in, '\0') {
4063 /*
4064 * More ranges...
4065 */
4066 case ',':
4067 fr_sbuff_next(in);
4068 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4069 continue;
4070
4071 /*
4072 * <start>-<end>
4073 */
4074 case '-':
4075 {
4076 fr_sbuff_next(in);
4077 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4078
4079 /*
4080 * A bare '-' with no number means
4081 * run all remaining lines.
4082 */
4083 if (fr_sbuff_extend(in) == 0) {
4084 lr->end = UINT32_MAX;
4085 return 0;
4086 }
4087
4088 fr_sbuff_out(&err, &lr->end, in);
4089 if (err != FR_SBUFF_PARSE_OK) {
4090 ERROR("Invalid line end number");
4091 goto error;
4092 }
4093 if (lr->end < lr->start) {
4094 ERROR("Line end must be >= line start (%u < %u)", lr->end, lr->start);
4095 goto error;
4096 }
4097 if (max > lr->end) {
4098 ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->end);
4099 goto error;
4100 } else {
4101 max = lr->end;
4102 }
4103 fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
4104 }
4105 goto again;
4106 }
4107 }
4108
4109 return 0;
4110}
4111
4112/**
4113 *
4114 * @hidecallgraph
4115 */
4116int main(int argc, char *argv[])
4117{
4118 int c;
4119 char const *receipt_file = NULL;
4120 CONF_SECTION *cs;
4121 int ret = EXIT_SUCCESS;
4122 TALLOC_CTX *autofree;
4123 TALLOC_CTX *thread_ctx;
4124 bool exit_now = false;
4125
4127 .raddb_dir = RADDBDIR,
4128 .dict_dir = DICTDIR
4129 };
4130
4131 char const *name;
4132 bool do_features = false;
4133 bool do_commands = false;
4134 bool do_usage = false;
4135 bool allow_purify = false;
4136 xlat_t *xlat;
4137
4138 /*
4139 * Must be called first, so the handler is called last
4140 */
4142
4144 thread_ctx = talloc_new(autofree);
4145
4146#ifndef NDEBUG
4147 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
4148 fr_perror("unit_test_attribute");
4149 goto cleanup;
4150 }
4151#else
4153#endif
4154
4155 /*
4156 * Sync wallclock and cpu time so that we can find
4157 * uses of fr_time_[to|from]_* where
4158 * fr_unix_time_[to|from]_* should be used.
4159 *
4160 * If the wallclock/cpu offset is 0, then both sets
4161 * of macros produce the same result.
4162 */
4163 fr_time_start();
4164
4165 /*
4166 * Allocate a root config section so we can write
4167 * out features and versions.
4168 */
4169 MEM(cs = cf_section_alloc(autofree, NULL, "unit_test_attribute", NULL));
4170 MEM(config.features = cf_section_alloc(cs, cs, "feature", NULL));
4171 dependency_features_init(config.features); /* Add build time features to the config section */
4172
4173 name = argv[0];
4174
4176 default_log.fd = STDOUT_FILENO;
4177 default_log.print_level = false;
4178
4179 while ((c = getopt(argc, argv, "cd:D:F:fxMhpr:S:w:")) != -1) switch (c) {
4180 case 'c':
4181 do_commands = true;
4182 break;
4183
4184 case 'd':
4185 config.raddb_dir = optarg;
4186 break;
4187
4188 case 'D':
4189 config.dict_dir = optarg;
4190 break;
4191
4192 case 'F':
4193 config.fuzzer_dir = optarg;
4194 break;
4195
4196 case 'f':
4197 do_features = true;
4198 break;
4199
4200 case 'x':
4201 fr_debug_lvl++;
4202 if (fr_debug_lvl > 2) default_log.print_level = true;
4203 break;
4204
4205 case 'M':
4206 talloc_enable_leak_report();
4207 break;
4208
4209 case 'r':
4210 receipt_file = optarg;
4211 break;
4212
4213 case 'p':
4214 allow_purify = true;
4215 break;
4216
4217 case 'S':
4218 fprintf(stderr, "Invalid option to -S\n");
4220
4221 case 'w':
4222 write_filename = optarg;
4223 break;
4224
4225 case 'h':
4226 default:
4227 do_usage = true; /* Just set a flag, so we can process extra -x args */
4228 break;
4229 }
4230 argc -= (optind - 1);
4231 argv += (optind - 1);
4232
4233 if (do_usage) usage(name);
4234 if (do_features) features_print(config.features);
4235 if (do_commands) commands_print();
4236 if (do_usage || do_features || do_commands) {
4237 ret = EXIT_SUCCESS;
4238 goto cleanup;
4239 }
4240
4241 if (receipt_file && (fr_unlink(receipt_file) < 0)) {
4242 fr_perror("unit_test_attribute");
4244 }
4245
4246 /*
4247 * Mismatch between the binary and the libraries it depends on
4248 */
4250 fr_perror("unit_test_attribute");
4252 }
4253
4254#ifdef WITH_TLS
4255 /*
4256 * OpenSSL can only be initialised once during the lifetime
4257 * of a process. Initialise it here so that we don't attempt
4258 * to unload and load it multiple times.
4259 */
4260 if (fr_openssl_init() < 0) {
4261 fr_perror("unit_test_attribute");
4263 }
4264#endif
4265
4266 modules_init(NULL);
4267
4268 dl_loader = dl_loader_init(autofree, NULL, false, false);
4269 if (!dl_loader) {
4270 fr_perror("unit_test_attribute");
4272 }
4273
4274 config.dict_gctx = fr_dict_global_ctx_init(NULL, true, config.dict_dir);
4275 if (!config.dict_gctx) {
4276 fr_perror("unit_test_attribute");
4278 }
4279
4281 fr_perror("unit_test_attribute");
4283 }
4284
4285 /*
4286 * Always needed so we can load the list attributes
4287 * otherwise the tmpl_tokenize code fails.
4288 */
4289 if (request_global_init() < 0) {
4290 fr_perror("unit_test_attribute");
4292 }
4293
4294 /*
4295 * Initialise the interpreter, registering operations.
4296 * Needed because some keywords also register xlats.
4297 */
4298 if (unlang_global_init() < 0) {
4299 fr_perror("unit_test_attribute");
4301 }
4302
4303 /*
4304 * Create a dummy event list
4305 */
4306 if (allow_purify) {
4307 el = fr_event_list_alloc(autofree, NULL, NULL);
4308 fr_assert(el != NULL);
4309
4310 /*
4311 * Simulate thread specific instantiation
4312 */
4314 if (xlat_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
4315 }
4316
4317 unlang_thread_instantiate(thread_ctx);
4318
4319 xlat = xlat_func_register(NULL, "test", xlat_test, FR_TYPE_NULL);
4320 if (!xlat) {
4321 ERROR("Failed registering xlat");
4323 }
4325
4326 /*
4327 * And again WITHOUT arguments.
4328 */
4329 xlat = xlat_func_register(NULL, "test_no_args", xlat_test, FR_TYPE_NULL);
4330 if (!xlat) {
4331 ERROR("Failed registering xlat");
4333 }
4335
4336 /*
4337 * Disable hostname lookups, so we don't produce spurious DNS
4338 * queries, and there's no chance of spurious failures if
4339 * it takes a long time to get a response.
4340 */
4342
4343 /*
4344 * Read tests from stdin
4345 */
4346 if (argc < 2) {
4347 if (write_filename) {
4348 ERROR("Can't use '-w' with stdin");
4350 }
4351
4352 ret = process_file(&exit_now, autofree, &config, name, "-", NULL);
4353
4354 /*
4355 * ...or process each file in turn.
4356 */
4357 } else {
4358 int i;
4359
4360 if (write_filename) {
4361 if (argc != 2) { /* program name and file to write */
4362 ERROR("Can't use '-w' with multiple filenames");
4364 }
4365
4366 write_fp = fopen(write_filename, "w");
4367 if (!write_fp) {
4368 ERROR("Failed opening %s: %s", write_filename, strerror(errno));
4370 }
4371 }
4372
4373 /*
4374 * Loop over all input files.
4375 */
4376 for (i = 1; i < argc; i++) {
4377 char *dir = NULL, *file;
4378 fr_sbuff_t in = FR_SBUFF_IN(argv[i], strlen(argv[i]));
4380 L("/"),
4381 L(":")
4382 );
4383 fr_sbuff_marker_t file_start, file_end, dir_end;
4384 fr_dlist_head_t lines;
4385
4386 fr_sbuff_marker(&file_start, &in);
4387 fr_sbuff_marker(&file_end, &in);
4388 fr_sbuff_marker(&dir_end, &in);
4389 fr_sbuff_set(&file_end, fr_sbuff_end(&in));
4390
4391 fr_dlist_init(&lines, command_line_range_t, entry);
4392
4393 while (fr_sbuff_extend(&in)) {
4394 fr_sbuff_adv_until(&in, SIZE_MAX, &dir_sep, '\0');
4395
4396 fr_sbuff_switch(&in, '\0') {
4397 case '/':
4398 fr_sbuff_set(&dir_end, &in);
4399 fr_sbuff_advance(&in, 1);
4400 fr_sbuff_set(&file_start, &in);
4401 break;
4402
4403 case ':':
4404 fr_sbuff_set(&file_end, &in);
4405 fr_sbuff_advance(&in, 1);
4406 if (line_ranges_parse(autofree, &lines, &in) < 0) EXIT_WITH_FAILURE;
4407 break;
4408
4409 default:
4410 fr_sbuff_set(&file_end, &in);
4411 break;
4412 }
4413 }
4414
4416 fr_sbuff_current(&file_start), fr_sbuff_diff(&file_end, &file_start));
4417 if (fr_sbuff_used(&dir_end)) dir = talloc_bstrndup(autofree,
4419 fr_sbuff_used(&dir_end));
4420
4421 ret = process_file(&exit_now, autofree, &config, dir, file, &lines);
4422 talloc_free(dir);
4424 fr_dlist_talloc_free(&lines);
4425
4426 if ((ret != 0) || exit_now) break;
4427 }
4428
4429 if (write_fp) {
4430 fclose(write_fp);
4431 if (rename(write_filename, argv[1]) < 0) {
4432 ERROR("Failed renaming %s: %s", write_filename, strerror(errno));
4434 }
4435 }
4436 }
4437
4438 /*
4439 * Try really hard to free any allocated
4440 * memory, so we get clean talloc reports.
4441 */
4442cleanup:
4443 /*
4444 * Ensure all thread local memory is cleaned up
4445 * at the appropriate time. This emulates what's
4446 * done with worker/network threads in the
4447 * scheduler.
4448 */
4450
4451#ifdef WITH_TLS
4452 fr_openssl_free();
4453#endif
4454
4455 /*
4456 * dl_loader check needed as talloc_free
4457 * returns -1 on failure.
4458 */
4459 if (dl_loader && (talloc_free(dl_loader) < 0)) {
4460 fr_perror("unit_test_attribute - dl_loader - "); /* Print free order issues */
4462 }
4463 if (fr_dict_free(&config.dict, __FILE__) < 0) {
4464 fr_perror("unit_test_attribute");
4466 }
4467
4468 if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
4469 fr_perror("unit_test_attribute");
4471 }
4472
4473 /*
4474 * Explicitly free the autofree context
4475 * to make errors less confusing.
4476 */
4477 if (talloc_free(autofree) < 0) {
4478 fr_perror("unit_test_attribute");
4480 }
4481
4482 /*
4483 * Ensure our atexit handlers run before any other
4484 * atexit handlers registered by third party libraries.
4485 */
4487
4488 return ret;
4489}
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
ssize_t fr_base64_encode_nstd(fr_sbuff_t *out, fr_dbuff_t *in, bool add_padding, char const alphabet[static UINT8_MAX])
Base 64 encode binary data.
Definition base64.c:326
char const fr_base64_url_alphabet_encode[UINT8_MAX]
Definition base64.c:173
static TALLOC_CTX * autofree
Definition fuzzer.c:45
#define CMD_MAX_ARGV
Definition radmin.c:150
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:485
#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:2269
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:2420
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:1924
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *cs, CONF_PAIR const *prev, char const *attr)
Find a pair with a name matching attr, after specified pair.
Definition cf_util.c:1440
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:1426
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1581
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1565
#define cf_lineno_set(_ci, _lineno)
Definition cf_util.h:131
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
#define cf_filename_set(_ci, _filename)
Definition cf_util.h:128
#define CF_IDENT_ANY
Definition cf_util.h:78
int fr_command_walk(fr_cmd_t *head, void **walk_ctx, void *ctx, fr_cmd_walk_t callback)
Walk over a command hierarchy.
Definition command.c:1027
void fr_cmd_debug(FILE *fp, fr_cmd_t *head)
Definition command.c:1610
int fr_command_add(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add one command to the global command tree.
Definition command.c:733
int fr_command_tab_expand(TALLOC_CTX *ctx, fr_cmd_t *head, fr_cmd_info_t *info, int max_expansions, char const **expansions)
Get the commands && help at a particular level.
Definition command.c:1298
char const ** parents
Definition command.h:66
char const * help
help text
Definition command.h:55
int argc
current argument count
Definition command.h:39
fr_cmd_func_t func
function to process this command
Definition command.h:56
char const * syntax
Definition command.h:68
char const * syntax
e.g. "STRING"
Definition command.h:54
fr_value_box_t ** box
value_box version of commands.
Definition command.h:43
bool read_only
Definition command.h:58
char const * parent
e.g. "show module"
Definition command.h:52
fr_cmd_tab_t tab_expand
tab expand things in the syntax string
Definition command.h:57
int max_argc
maximum number of arguments
Definition command.h:40
char const * name
e.g. "stats"
Definition command.h:53
char const ** argv
text version of commands
Definition command.h:42
char const * name
Definition command.h:67
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h: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:1020
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:1055
#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
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:4728
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name, char const *dependent)
(Re-)Initialize the special internal dictionary
int fr_dict_global_ctx_dir_set(char const *dict_dir)
Allow the default dict dir to be changed after initialisation.
Definition dict_util.c:4819
static fr_slen_t err
Definition dict.h:887
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:3558
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:2619
int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent)
(Re)-initialize a protocol dictionary
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2695
void fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx)
Set a new, active, global dictionary context.
Definition dict_util.c:4789
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4360
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:4344
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4948
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:4805
int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent)
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename)
Read supplementary attribute definitions into an existing dictionary.
fr_dict_attr_err_t
Errors returned by attribute lookup functions.
Definition dict.h:318
@ FR_DICT_ATTR_OK
No error.
Definition dict.h:319
static fr_slen_t in
Definition dict.h:887
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:178
Test enumeration values.
Definition dict_test.h:92
dl_loader_t * dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
Initialise structures needed by the dynamic linker.
Definition dl.c:885
dl_t * dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
Search for a dl's shared object in various locations.
Definition dl.c:470
A dynamic loader.
Definition dl.c:81
void * handle
Handle returned by dlopen.
Definition dl.h:62
Module handle.
Definition dl.h:58
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:486
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:908
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition dlist.h:501
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
ssize_t fr_dns_label_from_value_box(size_t *need, uint8_t *buf, size_t buf_len, uint8_t *where, bool compression, fr_value_box_t const *value, fr_dns_labels_t *lb)
Encode a single value box of type string, serializing its contents to a dns label.
Definition dns.c:639
ssize_t fr_dns_label_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *dst, uint8_t const *src, size_t len, uint8_t const *label, bool tainted, fr_dns_labels_t *lb)
Decode a fr_value_box_t from one DNS label.
Definition dns.c:1225
int fr_unlink(char const *filename)
Remove a regular file from the filesystem.
Definition file.c:367
ssize_t fr_touch(int *fd_out, char const *filename, mode_t mode, bool mkdir, mode_t dir_mode)
Create an empty file.
Definition file.c:323
char * fr_realpath(TALLOC_CTX *ctx, char const *path, ssize_t len)
Convenience wrapper around realpath.
Definition file.c:284
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:625
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:677
Stores the state of the current iteration operation.
Definition hash.h:41
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
talloc_free(reap)
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2522
Stores all information relating to an event list.
Definition event.c:377
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
#define UINT8_MAX
fr_sbuff_parse_error_t
@ FR_SBUFF_PARSE_OK
No error.
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
static bool is_whitespace(char const *value)
Check whether the string is all whitespace.
Definition misc.h: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:289
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
Read valuepairs from the fp up to End-Of-File.
struct fr_pair_parse_s fr_pair_parse_t
TALLOC_CTX * ctx
Definition pair_legacy.h:43
#define is_truncated(_ret, _max)
Definition print.h:48
static const conf_parser_t config[]
Definition base.c:186
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
static bool done
Definition radclient.c:83
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define INFO(fmt,...)
Definition radict.c:56
static const char * spaces
Definition radict.c:169
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:1459
size_t fr_sbuff_trim(fr_sbuff_t *sbuff, bool const to_trim[static UINT8_MAX+1])
Trim trailing characters from a string we're composing.
Definition sbuff.c:2156
fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in)
See if the string contains a truth value.
Definition sbuff.c:1114
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:1880
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1592
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:2116
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_out_by_longest_prefix(_match_len, _out, _table, _sbuff, _def)
#define fr_sbuff_set(_dst, _src)
#define fr_sbuff_diff(_a, _b)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define fr_sbuff_current(_sbuff_or_marker)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h: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:1272
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:37
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:1904
fr_aka_sim_id_type_t type
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
fr_log_dst_t dst
Log destination.
Definition log.h:97
int fd
File descriptor to write messages to.
Definition log.h:112
bool print_level
sometimes we don't want log levels printed
Definition log.h:106
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition tmpl.h:306
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:314
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:303
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:586
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h: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:169
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
close(uq->fd)
static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
#define RETURN_OK(_len)
int fuzzer_dir
File descriptor pointing to a a directory to write fuzzer output.
#define POISONED_BUFFER_START(_p)
static size_t commands_len
static int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
static size_t command_touch(command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Touch a file to indicate a test completed.
uint32_t start
Start of line range.
static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
static dl_loader_t * dl_loader
#define BUFF_POISON_START
static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
int main(int argc, char *argv[])
static size_t command_decode_dns_label(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
static size_t command_decode_proto(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
static void unload_proto_library(void)
static size_t command_xlat_normalise(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse an reprint and xlat expansion.
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 void commands_print(void)
#define POISONED_BUFFER_END(_p)
static char proto_name_prev[128]
TALLOC_CTX * tmp_ctx
Temporary context to hold buffers in this.
static size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
Print hex string to buffer.
static size_t command_exit(command_result_t *result, UNUSED command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Exit gracefully with the specified code.
static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
#define RETURN_OK_WITH_ERROR()
static size_t command_xlat_purify(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
Parse, purify, and reprint an xlat expression expansion.
char const * filename
Current file we're operating on.
static void mismatch_print(command_file_ctx_t *cc, char const *command, char *expected, size_t expected_len, char *got, size_t got_len, bool print_diff)
static size_t command_tmpl(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 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.
static const fr_token_t token2op[UINT8_MAX+1]
tmpl_rules_t tmpl_rules
To pass to parsing functions.
size_t(* command_func_t)(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Command to execute.
#define RETURN_MISMATCH(_len)
uint32_t end
End of line range.
static char const hextab[]
static command_file_ctx_t * command_ctx_alloc(TALLOC_CTX *ctx, command_config_t const *config, char const *path, char const *filename)
static size_t command_encode_proto(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
static size_t command_load_dictionary(command_result_t *result, command_file_ctx_t *cc, UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
static void command_print(void)
#define 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 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 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 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:2283
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:3173
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
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:146
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:1890
#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:3145
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:585
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:270
static fr_slen_t parent
Definition pair.h:857
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
fr_sbuff_escape_rules_t fr_value_escape_double
Definition value.c:356
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:5819
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:3732
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:743
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:3978
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:5782
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:6043
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:4159
fr_sbuff_unescape_rules_t fr_value_unescape_double
Definition value.c:272
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:5145
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1077
static fr_slen_t data
Definition value.h:1322
#define fr_box_strvalue_len(_val, _len)
Definition value.h:308
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1023
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:609
static size_t char ** out
Definition value.h:1023
#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