The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: fc277cff965464d4907475169694f931c5683d08 $
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  */
26 RCSID("$Id: fc277cff965464d4907475169694f931c5683d08 $")
27 
28 typedef 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/misc.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 <ctype.h>
59 
60 #ifdef __clangd__
61 # undef HAVE_SANITIZER_LSAN_INTERFACE_H
62 #endif
63 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
64 # include <sanitizer/asan_interface.h>
65 #endif
66 
67 #ifdef HAVE_GETOPT_H
68 # include <getopt.h>
69 #endif
70 
71 #include <assert.h>
72 #include <fcntl.h>
73 #include <libgen.h>
74 #include <limits.h>
75 #include <sys/file.h>
76 #include <sys/stat.h>
77 #include <sys/wait.h>
78 
79 #ifndef HAVE_SANITIZER_LSAN_INTERFACE_H
80 # define ASAN_POISON_MEMORY_REGION(_start, _end)
81 # define ASAN_UNPOISON_MEMORY_REGION(_start, _end)
82 #endif
83 
84 #define EXIT_WITH_FAILURE \
85 do { \
86  ret = EXIT_FAILURE; \
87  goto cleanup; \
88 } while (0)
89 
90 #define COMMAND_OUTPUT_MAX 8192
91 
92 #define RETURN_OK(_len) \
93  do { \
94  result->rcode = RESULT_OK; \
95  result->file = __FILE__; \
96  result->line = __LINE__; \
97  return (_len); \
98  } while (0)
99 
100 #define RETURN_OK_WITH_ERROR() \
101  do { \
102  result->rcode = RESULT_OK; \
103  result->file = __FILE__; \
104  result->line = __LINE__; \
105  result->error_to_data = true; \
106  return 0; \
107  } while (0)
108 
109 #define RETURN_NOOP(_len) \
110  do { \
111  result->rcode = RESULT_NOOP; \
112  result->file = __FILE__; \
113  result->line = __LINE__; \
114  return (_len); \
115  } while (0)
116 
117 #define RETURN_SKIP_FILE() \
118  do { \
119  result->rcode = RESULT_SKIP_FILE; \
120  result->file = __FILE__; \
121  result->line = __LINE__; \
122  return 0; \
123  } while (0)
124 
125 #define RETURN_PARSE_ERROR(_offset) \
126  do { \
127  result->rcode = RESULT_PARSE_ERROR; \
128  result->offset = _offset; \
129  result->file = __FILE__; \
130  result->line = __LINE__; \
131  return 0; \
132  } while (0)
133 
134 #define RETURN_COMMAND_ERROR() \
135  do { \
136  result->rcode = RESULT_COMMAND_ERROR; \
137  result->file = __FILE__; \
138  result->line = __LINE__; \
139  return 0; \
140  } while (0)
141 
142 #define RETURN_MISMATCH(_len) \
143  do { \
144  result->rcode = RESULT_MISMATCH; \
145  result->file = __FILE__; \
146  result->line = __LINE__; \
147  return (_len); \
148  } while (0)
149 
150 #define RETURN_EXIT(_ret) \
151  do { \
152  result->rcode = RESULT_EXIT; \
153  result->ret = _ret; \
154  result->file = __FILE__; \
155  result->line = __LINE__; \
156  return 0; \
157  } while (0)
158 
159 /** Default buffer size for a command_file_ctx_t
160  *
161  */
162 #define DEFAULT_BUFFER_SIZE 1024
163 
164 typedef enum {
165  RESULT_OK = 0, //!< Not an error - Result as expected.
166  RESULT_NOOP, //!< Not an error - Did nothing...
167  RESULT_SKIP_FILE, //!< Not an error - Skip the rest of this file, or until we
168  ///< reach an "eof" command.
169  RESULT_PARSE_ERROR, //!< Fatal error - Command syntax error.
170  RESULT_COMMAND_ERROR, //!< Fatal error - Command operation error.
171  RESULT_MISMATCH, //!< Fatal error - Result didn't match what we expected.
172  RESULT_EXIT, //!< Stop processing files and exit.
174 
176  { L("command-error"), RESULT_COMMAND_ERROR },
177  { L("exit"), RESULT_EXIT },
178  { L("ok"), RESULT_OK },
179  { L("parse-error"), RESULT_PARSE_ERROR },
180  { L("result-mismatch"), RESULT_MISMATCH },
181  { L("skip-file"), RESULT_SKIP_FILE },
182 };
184 
185 typedef struct {
186  TALLOC_CTX *tmp_ctx; //!< Temporary context to hold buffers
187  ///< in this
188  union {
189  size_t offset; //!< Where we failed parsing the command.
190  int ret; //!< What code we should exit with.
191  };
192  char const *file;
193  int line;
197 
198 /** Configuration parameters passed to command functions
199  *
200  */
201 typedef struct {
202  fr_dict_t *dict; //!< Dictionary to "reset" to.
203  fr_dict_gctx_t const *dict_gctx; //!< Dictionary gctx to "reset" to.
204  char const *raddb_dir;
205  char const *dict_dir;
206  char const *fuzzer_dir; //!< Where to write fuzzer files.
207  CONF_SECTION *features; //!< Enabled features.
209 
210 typedef struct {
211  TALLOC_CTX *tmp_ctx; //!< Talloc context for test points.
212 
213  char *path; //!< Current path we're operating in.
214  char const *filename; //!< Current file we're operating on.
215  uint32_t lineno; //!< Current line number.
216 
217  uint32_t test_count; //!< How many tests we've executed in this file.
218  ssize_t last_ret; //!< Last return value.
219 
220  uint8_t *buffer; //!< Temporary resizable buffer we use for
221  ///< holding non-string data.
222  uint8_t *buffer_start; //!< Where the non-poisoned region of the buffer starts.
223  uint8_t *buffer_end; //!< Where the non-poisoned region of the buffer ends.
224 
225  tmpl_rules_t tmpl_rules; //!< To pass to parsing functions.
226  fr_dict_t *test_internal_dict; //!< Internal dictionary of test_gctx.
227  fr_dict_gctx_t const *test_gctx; //!< Dictionary context for test dictionaries.
228 
229  int fuzzer_dir; //!< File descriptor pointing to a a directory to
230  ///< write fuzzer output.
233 
234 
235 typedef struct {
236  fr_dlist_t entry; //!< Entry in the dlist.
237  uint32_t start; //!< Start of line range.
238  uint32_t end; //!< End of line range.
240 
241 /** Command to execute
242  *
243  * @param[out] result Of executing the command.
244  * @param[in] cc Information about the file being processed.
245  * @param[in,out] data Output of this command, or the previous command.
246  * @param[in] data_used Length of data in the data buffer.
247  * @param[in] in Command text to process.
248  * @param[in] inlen Length of the remainder of the command to process.
249  */
251  size_t data_used, char *in, size_t inlen);
252 
253 typedef struct {
255  char const *usage;
256  char const *description;
258 
260  UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request,
261  UNUSED fr_value_box_list_t *in)
262 {
263  return XLAT_ACTION_DONE;
264 }
265 
266 static char proto_name_prev[128];
267 static dl_t *dl;
268 static dl_loader_t *dl_loader = NULL;
269 
270 static fr_event_list_t *el = NULL;
271 
272 static char const *write_filename = NULL;
273 static FILE *write_fp = NULL;
274 
275 size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen);
276 static int process_file(bool *exit_now, TALLOC_CTX *ctx,
277  command_config_t const *config, const char *root_dir, char const *filename, fr_dlist_head_t *lines);
278 
279 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
280 # define BUFF_POISON_START 1024
281 # define BUFF_POISON_END 1024
282 
283 /** Unpoison the start and end regions of the buffer
284  *
285  */
286 static int _free_buffer(uint8_t *buff)
287 {
288  size_t size = talloc_array_length(buff) - (BUFF_POISON_START + BUFF_POISON_END);
289 
292 
293  return 0;
294 }
295 #else
296 # define BUFF_POISON_START 0
297 # define BUFF_POISON_END 0
298 #endif
299 
300 /** Allocate a special buffer with poisoned memory regions at the start and end
301  *
302  */
303 static int poisoned_buffer_allocate(TALLOC_CTX *ctx, uint8_t **buff, size_t size)
304 {
305  uint8_t *our_buff = *buff;
306 
307  if (our_buff) {
308  /*
309  * If it's already the correct length
310  * don't bother re-allocing the buffer,
311  * just memset it to zero.
312  */
313  if ((size + BUFF_POISON_START + BUFF_POISON_END) == talloc_array_length(our_buff)) {
314  memset(our_buff + BUFF_POISON_START, 0, size);
315  return 0;
316  }
317 
318  talloc_free(our_buff); /* Destructor de-poisons */
319  *buff = NULL;
320  }
321 
322  our_buff = talloc_array(ctx, uint8_t, size + BUFF_POISON_START + BUFF_POISON_END);
323  if (!our_buff) return -1;
324 
325 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
326  talloc_set_destructor(our_buff, _free_buffer);
327 
328  /*
329  * Poison regions before and after the buffer
330  */
333 #endif
334 
335  *buff = our_buff;
336 
337  return 0;
338 }
339 #define POISONED_BUFFER_START(_p) ((_p) + BUFF_POISON_START)
340 #define POISONED_BUFFER_END(_p) ((_p) + BUFF_POISON_START + (talloc_array_length(_p) - (BUFF_POISON_START + BUFF_POISON_END)))
341 
342 static void mismatch_print(command_file_ctx_t *cc, char const *command,
343  char *expected, size_t expected_len, char *got, size_t got_len,
344  bool print_diff)
345 {
346  char *g, *e;
347  char *spaces;
348 
349  ERROR("%s failed %s/%s:%d", command, cc->path, cc->filename, cc->lineno);
350  ERROR(" got : %.*s", (int) got_len, got);
351  ERROR(" expected : %.*s", (int) expected_len, expected);
352 
353  if (print_diff) {
354  g = got;
355  e = expected;
356 
357  while (*g && *e && (*g == *e)) {
358  g++;
359  e++;
360  }
361 
362  spaces = talloc_zero_array(NULL, char, (e - expected) + 1);
363  memset(spaces, ' ', talloc_array_length(spaces) - 1);
364  if (((e - expected) < 80) && (expected_len < 80)) {
365  ERROR(" %s^ differs here", spaces);
366  } else {
367  ERROR(" %s^ differs here (%zu) ... %.*s ...", spaces, e - expected,
368  16, e);
369  }
371  }
372 }
373 
374 /** Print hex string to buffer
375  *
376  */
377 static inline CC_HINT(nonnull) size_t hex_print(char *out, size_t outlen, uint8_t const *in, size_t inlen)
378 {
379  char *p = out;
380  char *end = p + outlen;
381  size_t i;
382 
383  if (inlen == 0) {
384  *p = '\0';
385  return 0;
386  }
387 
388  for (i = 0; i < inlen; i++) {
389  size_t len;
390 
391  len = snprintf(p, end - p, "%02x ", in[i]);
392  if (is_truncated(len, end - p)) return 0;
393 
394  p += len;
395  }
396 
397  *(--p) = '\0';
398 
399  return p - out;
400 }
401 
402 /** Concatenate error stack
403  */
404 static inline size_t strerror_concat(char *out, size_t outlen)
405 {
406  char *end = out + outlen;
407  char *p = out;
408  char const *err;
409 
410  while ((p < end) && (err = fr_strerror_pop())) {
411  if (*fr_strerror_peek()) {
412  p += snprintf(p, end - p, "%s: ", err);
413  } else {
414  p += strlcpy(p, err, end - p);
415  }
416  }
417 
418  return p - out;
419 }
420 
421 static inline CC_HINT(nonnull) int dump_fuzzer_data(int fd_dir, char const *text, uint8_t const *data, size_t data_len)
422 {
423  fr_sha1_ctx ctx;
424  uint8_t digest[SHA1_DIGEST_LENGTH];
425  char digest_str[(SHA1_DIGEST_LENGTH * 2) + 1];
426  int file_fd;
427 
428  fr_sha1_init(&ctx);
429  fr_sha1_update(&ctx, (uint8_t const *)text, strlen(text));
430  fr_sha1_final(digest, &ctx);
431 
432  /*
433  * We need to use the url alphabet as the standard
434  * one contains forwarded slashes which openat
435  * doesn't like.
436  */
437  fr_base64_encode_nstd(&FR_SBUFF_OUT(digest_str, sizeof(digest_str)), &FR_DBUFF_TMP(digest, sizeof(digest)),
439 
440  file_fd = openat(fd_dir, digest_str, O_RDWR | O_CREAT | O_TRUNC,
441  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
442  if (file_fd < 0) {
443  fr_strerror_printf("Failed opening or creating corpus seed file \"%s\": %s",
444  digest_str, fr_syserror(errno));
445  return -1;
446  }
447 
448  if (flock(file_fd, LOCK_EX) < 0) {
449  fr_strerror_printf("Failed locking corpus seed file \"%s\": %s",
450  digest_str, fr_syserror(errno));
451  return -1;
452  }
453 
454  while (data_len) {
455  ssize_t ret;
456 
457  ret = write(file_fd, data, data_len);
458  if (ret < 0) {
459  fr_strerror_printf("Failed writing to corpus seed file \"%s\": %s",
460  digest_str, fr_syserror(errno));
461  (void)flock(file_fd, LOCK_UN);
462  unlinkat(fd_dir, digest_str, 0);
463  return -1;
464  }
465  data_len -= ret;
466  data += ret;
467  }
468  (void)flock(file_fd, LOCK_UN);
469 
470  return 0;
471 }
472 
473 /*
474  * End of hacks for xlat
475  *
476  **********************************************************************/
477 
478 static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen);
479 
480 static char const hextab[] = "0123456789abcdef";
481 
482 static ssize_t encode_data_string(char *buffer, uint8_t *output, size_t outlen)
483 {
484  ssize_t slen = 0;
485  char *p;
486 
487  p = buffer + 1;
488 
489  while (*p && (outlen > 0)) {
490  if (*p == '"') {
491  return slen;
492  }
493 
494  if (*p != '\\') {
495  *(output++) = *(p++);
496  outlen--;
497  slen++;
498  continue;
499  }
500 
501  switch (p[1]) {
502  default:
503  *(output++) = p[1];
504  break;
505 
506  case 'n':
507  *(output++) = '\n';
508  break;
509 
510  case 'r':
511  *(output++) = '\r';
512  break;
513 
514  case 't':
515  *(output++) = '\t';
516  break;
517  }
518 
519  outlen--;
520  slen++;
521  }
522 
523  ERROR("String is not terminated");
524  return 0;
525 }
526 
527 static ssize_t encode_data_tlv(char *buffer, char **endptr, uint8_t *output, size_t outlen)
528 {
529  int depth = 0;
530  ssize_t slen;
531  char *p;
532 
533  for (p = buffer; *p != '\0'; p++) {
534  if (*p == '{') depth++;
535  if (*p == '}') {
536  depth--;
537  if (depth == 0) break;
538  }
539  }
540 
541  if (*p != '}') {
542  ERROR("No trailing '}' in string starting with \"%s\"", buffer);
543  return 0;
544  }
545 
546  *endptr = p + 1;
547  *p = '\0';
548 
549  p = buffer + 1;
551 
552  slen = encode_tlv(p, output, outlen);
553  if (slen <= 0) return 0;
554 
555  return slen;
556 }
557 
558 static ssize_t hex_to_bin(uint8_t *out, size_t outlen, char *in, size_t inlen)
559 {
560  char *p = in;
561  char *end = in + inlen;
562  uint8_t *out_p = out, *out_end = out_p + outlen;
563 
564  while (p < end) {
565  char *c1, *c2;
566 
567  if (out_p >= out_end) {
568  fr_strerror_const("Would overflow output buffer");
569  return -(p - in);
570  }
571 
573 
574  if (!*p) break;
575 
576  c1 = memchr(hextab, tolower((uint8_t) *p++), sizeof(hextab));
577  if (!c1) {
578  bad_input:
579  fr_strerror_printf("Invalid hex data starting at \"%s\"", p);
580  return -(p - in);
581  }
582 
583  c2 = memchr(hextab, tolower((uint8_t)*p++), sizeof(hextab));
584  if (!c2) goto bad_input;
585 
586  *out_p++ = ((c1 - hextab) << 4) + (c2 - hextab);
587  }
588 
589  return out_p - out;
590 }
591 
592 
593 static ssize_t encode_data(char *p, uint8_t *output, size_t outlen)
594 {
595  ssize_t slen;
596 
597  if (!isspace((uint8_t) *p)) {
598  ERROR("Invalid character following attribute definition");
599  return 0;
600  }
601 
603 
604  if (*p == '{') {
605  size_t sublen;
606  char *q;
607 
608  slen = 0;
609 
610  do {
612  if (!*p) {
613  if (slen == 0) {
614  ERROR("No data");
615  return 0;
616  }
617 
618  break;
619  }
620 
621  sublen = encode_data_tlv(p, &q, output, outlen);
622  if (sublen <= 0) return 0;
623 
624  slen += sublen;
625  output += sublen;
626  outlen -= sublen;
627  p = q;
628  } while (*q);
629 
630  return slen;
631  }
632 
633  if (*p == '"') {
634  slen = encode_data_string(p, output, outlen);
635  return slen;
636  }
637 
638  slen = hex_to_bin(output, outlen, p, strlen(p));
639  if (slen <= 0) {
640  fr_strerror_const_push("Empty hex string");
641  return slen;
642  }
643 
644  return slen;
645 }
646 
647 static int decode_attr(char *buffer, char **endptr)
648 {
649  long attr;
650 
651  attr = strtol(buffer, endptr, 10);
652  if (*endptr == buffer) {
653  ERROR("No valid number found in string starting with \"%s\"", buffer);
654  return 0;
655  }
656 
657  if (!**endptr) {
658  ERROR("Nothing follows attribute number");
659  return 0;
660  }
661 
662  if ((attr <= 0) || (attr > 256)) {
663  ERROR("Attribute number is out of valid range");
664  return 0;
665  }
666 
667  return (int) attr;
668 }
669 
670 static int decode_vendor(char *buffer, char **endptr)
671 {
672  long vendor;
673 
674  if (*buffer != '.') {
675  ERROR("Invalid separator before vendor id");
676  return 0;
677  }
678 
679  vendor = strtol(buffer + 1, endptr, 10);
680  if (*endptr == (buffer + 1)) {
681  ERROR("No valid vendor number found");
682  return 0;
683  }
684 
685  if (!**endptr) {
686  ERROR("Nothing follows vendor number");
687  return 0;
688  }
689 
690  if ((vendor <= 0) || (vendor > (1 << 24))) {
691  ERROR("Vendor number is out of valid range");
692  return 0;
693  }
694 
695  if (**endptr != '.') {
696  ERROR("Invalid data following vendor number");
697  return 0;
698  }
699  (*endptr)++;
700 
701  return (int) vendor;
702 }
703 
704 static ssize_t encode_tlv(char *buffer, uint8_t *output, size_t outlen)
705 {
706  int attr;
707  ssize_t slen;
708  char *p;
709 
710  attr = decode_attr(buffer, &p);
711  if (attr == 0) return 0;
712 
713  output[0] = attr;
714  output[1] = 2;
715 
716  if (*p == '.') {
717  p++;
718  slen = encode_tlv(p, output + 2, outlen - 2);
719 
720  } else {
721  slen = encode_data(p, output + 2, outlen - 2);
722  }
723 
724  if (slen <= 0) return slen;
725  if (slen > (255 - 2)) {
726  ERROR("TLV data is too long");
727  return 0;
728  }
729 
730  output[1] += slen;
731 
732  return slen + 2;
733 }
734 
735 static ssize_t encode_vsa(char *buffer, uint8_t *output, size_t outlen)
736 {
737  int vendor;
738  ssize_t slen;
739  char *p;
740 
741  vendor = decode_vendor(buffer, &p);
742  if (vendor == 0) return 0;
743 
744  output[0] = 0;
745  output[1] = (vendor >> 16) & 0xff;
746  output[2] = (vendor >> 8) & 0xff;
747  output[3] = vendor & 0xff;
748 
749  slen = encode_tlv(p, output + 4, outlen - 4);
750  if (slen <= 0) return slen;
751  if (slen > (255 - 6)) {
752  ERROR("VSA data is too long");
753  return 0;
754  }
755 
756  return slen + 4;
757 }
758 
759 static ssize_t encode_evs(char *buffer, uint8_t *output, size_t outlen)
760 {
761  int vendor;
762  int attr;
763  ssize_t slen;
764  char *p;
765 
766  vendor = decode_vendor(buffer, &p);
767  if (vendor == 0) return 0;
768 
769  attr = decode_attr(p, &p);
770  if (attr == 0) return 0;
771 
772  output[0] = 0;
773  output[1] = (vendor >> 16) & 0xff;
774  output[2] = (vendor >> 8) & 0xff;
775  output[3] = vendor & 0xff;
776  output[4] = attr;
777 
778  slen = encode_data(p, output + 5, outlen - 5);
779  if (slen <= 0) return slen;
780 
781  return slen + 5;
782 }
783 
784 static ssize_t encode_extended(char *buffer, uint8_t *output, size_t outlen)
785 {
786  int attr;
787  ssize_t slen;
788  char *p;
789 
790  attr = decode_attr(buffer, &p);
791  if (attr == 0) return 0;
792 
793  output[0] = attr;
794 
795  if (attr == 26) {
796  slen = encode_evs(p, output + 1, outlen - 1);
797  } else {
798  slen = encode_data(p, output + 1, outlen - 1);
799  }
800  if (slen <= 0) return slen;
801  if (slen > (255 - 3)) {
802  ERROR("Extended Attr data is too long");
803  return 0;
804  }
805 
806  return slen + 1;
807 }
808 
809 static ssize_t encode_long_extended(char *buffer, uint8_t *output, size_t outlen)
810 {
811  int attr;
812  ssize_t slen, total;
813  char *p;
814 
815  attr = decode_attr(buffer, &p);
816  if (attr == 0) return 0;
817 
818  /* output[0] is the extended attribute */
819  output[1] = 4;
820  output[2] = attr;
821  output[3] = 0;
822 
823  if (attr == 26) {
824  slen = encode_evs(p, output + 4, outlen - 4);
825  if (slen <= 0) return slen;
826 
827  output[1] += 5;
828  slen -= 5;
829  } else {
830  slen = encode_data(p, output + 4, outlen - 4);
831  }
832  if (slen <= 0) return slen;
833 
834  total = 0;
835  while (1) {
836  int sublen = 255 - output[1];
837 
838  if (slen <= sublen) {
839  output[1] += slen;
840  total += output[1];
841  break;
842  }
843 
844  slen -= sublen;
845 
846  memmove(output + 255 + 4, output + 255, slen);
847  memcpy(output + 255, output, 4);
848 
849  output[1] = 255;
850  output[3] |= 0x80;
851 
852  output += 255;
853  output[1] = 4;
854  total += 255;
855  }
856 
857  return total;
858 }
859 
860 static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
861 {
862  int attr;
863  ssize_t slen, sublen;
864  char *p;
865 
866  attr = decode_attr(buffer, &p);
867  if (attr == 0) return 0;
868 
869  slen = 2;
870  output[0] = attr;
871  output[1] = 2;
872 
873  if (attr == 26) {
874  sublen = encode_vsa(p, output + 2, outlen - 2);
875 
876  } else if ((attr < 241) || (attr > 246)) {
877  sublen = encode_data(p, output + 2, outlen - 2);
878 
879  } else {
880  if (*p != '.') {
881  ERROR("Invalid data following attribute number");
882  return 0;
883  }
884 
885  if (attr < 245) {
886  sublen = encode_extended(p + 1, output + 2, outlen - 2);
887  } else {
888  /*
889  * Not like the others!
890  */
891  return encode_long_extended(p + 1, output, outlen);
892  }
893  }
894  if (sublen <= 0) return sublen;
895  if (sublen > (255 -2)) {
896  ERROR("RFC Data is too long");
897  return 0;
898  }
899 
900  output[1] += sublen;
901  return slen + sublen;
902 }
903 
904 
905 static void unload_proto_library(void)
906 {
907  TALLOC_FREE(dl);
908 }
909 
910 static ssize_t load_proto_library(char const *proto_name)
911 {
912  char dl_name[128];
913 
914  if (strcmp(proto_name_prev, proto_name) != 0) {
915  /*
916  * Ensure the old proto library is unloaded
917  */
919 
920  snprintf(dl_name, sizeof(dl_name), "libfreeradius-%s", proto_name);
921  if (dl) TALLOC_FREE(dl);
922 
923  dl = dl_by_name(dl_loader, dl_name, NULL, false);
924  if (!dl) {
925  fr_perror("Failed to link to library \"%s\"", dl_name);
927  return 0;
928  }
929 
930  strlcpy(proto_name_prev, proto_name, sizeof(proto_name_prev));
931  }
932 
933  return strlen(proto_name);
934 }
935 
936 static ssize_t load_test_point_by_command(void **symbol, char *command, char const *dflt_symbol)
937 {
938  char buffer[256];
939  char const *p, *q;
940  void *dl_symbol;
941 
942  if (!dl) {
943  fr_strerror_printf("No protocol library loaded. Specify library with \"load <proto name>\"");
944  return 0;
945  }
946 
947  p = command;
948 
949  /*
950  * Use the dflt_symbol name as the test point
951  */
952  if ((*p == '.') && (q = strchr(p, ' ')) && (q != (p + 1)) && ((size_t)(q - p) < sizeof(buffer))) {
953  p++;
954  strlcpy(buffer, p, (q - p) + 1);
955  p = q + 1;
956  } else {
957  snprintf(buffer, sizeof(buffer), "%s_%s", proto_name_prev, dflt_symbol);
958  }
959 
960  dl_symbol = dlsym(dl->handle, buffer);
961  if (!dl_symbol) {
962  fr_strerror_printf("Test point (symbol \"%s\") not exported by library", buffer);
964  return 0;
965  }
966  *symbol = dl_symbol;
967 
968  return p - command;
969 }
970 
972 {
973  if (cc->tmpl_rules.attr.dict_def) {
974  return UNCONST(fr_dict_t *, cc->tmpl_rules.attr.dict_def);
975  }
976 
977  return cc->config->dict;
978 }
979 
980 /** Common dictionary load function
981  *
982  * Callers call fr_dict_global_ctx_set to set the context
983  * the dictionaries will be loaded into.
984  */
985 static int dictionary_load_common(command_result_t *result, command_file_ctx_t *cc, char const *in, char const *default_subdir)
986 {
987  char const *dir;
988  char *q;
989  char const *name;
990  char *tmp = NULL;
991  int ret;
992  fr_dict_t *dict;
993 
994  if (in[0] == '\0') {
995  fr_strerror_const("Missing dictionary name");
997  }
998 
999  /*
1000  * Decrease ref count if we're loading in a new dictionary
1001  */
1002  if (cc->tmpl_rules.attr.dict_def) {
1003  if (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0) RETURN_COMMAND_ERROR();
1004  }
1005 
1006  q = strchr(in, ' ');
1007  if (q) {
1008  name = tmp = talloc_bstrndup(NULL, in, q - in);
1009  q++;
1010  dir = q;
1011  } else {
1012  name = in;
1013  dir = default_subdir;
1014  }
1015 
1016  ret = fr_dict_protocol_afrom_file(&dict, name, dir, __FILE__);
1017  talloc_free(tmp);
1018  if (ret < 0) RETURN_COMMAND_ERROR();
1019 
1020  cc->tmpl_rules.attr.dict_def = dict;
1021  cc->tmpl_rules.attr.namespace = fr_dict_root(dict);
1022 
1023  /*
1024  * Dump the dictionary if we're in super debug mode
1025  */
1027 
1028  RETURN_OK(0);
1029 }
1030 
1031 static size_t parse_typed_value(command_result_t *result, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
1032 {
1033  fr_type_t type;
1034  size_t match_len;
1035  ssize_t slen;
1036  char const *p;
1037  fr_sbuff_t sbuff;
1038 
1039  /*
1040  * Parse data types
1041  */
1043  if (fr_type_is_null(type)) {
1044  RETURN_PARSE_ERROR(0);
1045  }
1046  fr_assert(match_len < inlen);
1047 
1048  p = in + match_len;
1049  fr_skip_whitespace(p);
1050  *out = p;
1051 
1052  /*
1053  * As a hack, allow most things to be inside
1054  * double-quoted strings. This is really only for dates,
1055  * which are space-delimited.
1056  */
1057  if (*p == '"'){
1058  p++;
1059  sbuff = FR_SBUFF_IN(p, strlen(p));
1060  slen = fr_value_box_from_substr(box, box, FR_TYPE_STRING, NULL,
1061  &sbuff,
1063  false);
1064  if (slen < 0) {
1066  }
1067 
1068  p += fr_sbuff_used(&sbuff);
1069  if (*p != '"') {
1070  RETURN_PARSE_ERROR(0);
1071  }
1072  p++;
1073 
1074  if (type != FR_TYPE_STRING) {
1075  if (fr_value_box_cast_in_place(box, box, type, NULL) < 0) {
1076  RETURN_PARSE_ERROR(0);
1077  }
1078  }
1079 
1080  } else {
1081  sbuff = FR_SBUFF_IN(p, strlen(p));
1082 
1083  slen = fr_value_box_from_substr(box, box, type, NULL,
1084  &sbuff,
1086  false);
1087  if (slen < 0) {
1089  }
1090  p += fr_sbuff_used(&sbuff);
1091  }
1092  fr_skip_whitespace(p);
1093 
1094  RETURN_OK(p - in);
1095 }
1096 
1097 static fr_cmd_t *command_head = NULL;
1098 
1099 static int command_func(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
1100 {
1101  return 0;
1102 }
1103 
1104 static int command_walk(UNUSED void *ctx, fr_cmd_walk_info_t *info)
1105 {
1106  int i;
1107 
1108  for (i = 0; i < info->num_parents; i++) {
1109  printf("%s ", info->parents[i]);
1110  }
1111 
1112  printf(":%s ", info->name);
1113  if (info->syntax) printf("%s", info->syntax);
1114  printf("%s", "");
1115 
1116  return 1;
1117 }
1118 
1119 static void command_print(void)
1120 {
1121  void *walk_ctx = NULL;
1122 
1123  printf("Command hierarchy --------");
1124  fr_command_debug(stdout, command_head);
1125 
1126  printf("Command list --------");
1127  while (fr_command_walk(command_head, &walk_ctx, NULL, command_walk) == 1) {
1128  // do nothing
1129  }
1130 }
1131 
1132 #define CLEAR_TEST_POINT(_cc) \
1133 do { \
1134  talloc_free_children((_cc)->tmp_ctx); \
1135  tp = NULL; \
1136 } while (0)
1137 
1138 /** Placeholder function for comments
1139  *
1140  */
1142  UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1143 {
1144  return 0;
1145 }
1146 
1147 /** Execute another test file
1148  *
1149  */
1151  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1152 {
1153  char *q;
1154  bool exit_now = false;
1155  int ret;
1156 
1157  if (write_fp) {
1158  fprintf(stderr, "Can't do $INCLUDE with -w %s\n", write_filename);
1159  RETURN_EXIT(1);
1160  }
1161 
1162  q = strrchr(cc->path, '/');
1163  if (q) {
1164  *q = '\0';
1165  ret = process_file(&exit_now, cc->tmp_ctx, cc->config, cc->path, in, NULL);
1166  if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1167  *q = '/';
1168  RETURN_OK(0);
1169  }
1170 
1171  ret = process_file(&exit_now, cc->tmp_ctx, cc->config, NULL, in, NULL);
1172  if (exit_now || (ret != 0)) RETURN_EXIT(ret);
1173 
1174  RETURN_OK(0);
1175 }
1176 
1177 /** Determine if unresolved attributes are allowed
1178  *
1179  */
1181  UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
1182 {
1183  fr_sbuff_t our_in = FR_SBUFF_IN(in, inlen);
1184  bool res;
1185 
1186  if (fr_sbuff_out_bool(&res, &our_in) == 0) {
1187  fr_strerror_printf("Invalid boolean value, must be \"yes\" or \"no\"");
1189  }
1190  cc->tmpl_rules.attr.allow_unresolved = res;
1191 
1192  RETURN_OK(0);
1193 }
1194 
1195 static const fr_token_t token2op[UINT8_MAX + 1] = {
1196  [ '+' ] = T_ADD,
1197  [ '-' ] = T_SUB,
1198  [ '*' ] = T_MUL,
1199  [ '/' ] = T_DIV,
1200  [ '^' ] = T_XOR,
1201  [ '.' ] = T_ADD,
1202  [ '&' ] = T_AND,
1203  [ '|' ] = T_OR,
1204  [ '%' ] = T_MOD,
1205 };
1206 
1207 /** Perform calculations
1208  *
1209  */
1211  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1212 {
1213  fr_value_box_t *a, *b, *out;
1214  size_t match_len;
1215  fr_type_t type;
1216  fr_token_t op;
1217  char const *p, *value, *end;
1218  size_t slen;
1219  bool assignment;
1220 
1221  a = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1222  b = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1223 
1224  p = in;
1225  end = in + inlen;
1226 
1227  match_len = parse_typed_value(result, a, &value, p, end - p);
1228  if (match_len == 0) return 0; /* errors have already been updated */
1229 
1230  p += match_len;
1231  fr_skip_whitespace(p);
1232 
1233  op = fr_table_value_by_longest_prefix(&match_len, fr_tokens_table, p, end - p, T_INVALID);
1234  if (op != T_INVALID) {
1235  p += match_len;
1236  assignment = fr_assignment_op[op];
1237 
1238  } else {
1239  op = token2op[(uint8_t) p[0]];
1240  if (op == T_INVALID) {
1241  fr_strerror_printf("Unknown operator '%c'", p[0]);
1242  RETURN_PARSE_ERROR(0);
1243  }
1244  p++;
1245 
1246  assignment = false;
1247  }
1248  fr_skip_whitespace(p);
1249 
1250  match_len = parse_typed_value(result, b, &value, p, end - p);
1251  if (match_len == 0) return 0;
1252 
1253  p += match_len;
1254  fr_skip_whitespace(p);
1255 
1256  if (assignment) {
1257  if (fr_value_calc_assignment_op(cc->tmp_ctx, a, op, b) < 0) {
1259  }
1260  out = a;
1261 
1262  } else {
1263  out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1264 
1265  /*
1266  * If there's no output data type, then the code tries to
1267  * figure one out automatically.
1268  */
1269  if (!*p) {
1270  type = FR_TYPE_NULL;
1271  } else {
1272  if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1273  p += 2;
1274  fr_skip_whitespace(p);
1275 
1276  type = fr_table_value_by_longest_prefix(&match_len, fr_type_table, p, end - p, FR_TYPE_MAX);
1277  if (type == FR_TYPE_MAX) RETURN_PARSE_ERROR(0);
1278  fr_value_box_init(out, type, NULL, false);
1279  }
1280 
1281  if (fr_value_calc_binary_op(cc->tmp_ctx, out, type, a, op, b) < 0) {
1283  }
1284  }
1285 
1287  if (slen <= 0) RETURN_OK_WITH_ERROR();
1288 
1289  RETURN_OK(slen);
1290 }
1291 
1292 /** Perform calculations on multi-valued ops
1293  *
1294  */
1296  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1297 {
1298  fr_value_box_t *group, *a, *out;
1299  size_t match_len;
1300  fr_type_t type;
1301  fr_token_t op;
1302  char const *p, *value, *end;
1303  size_t slen;
1304 
1305  group = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1306  fr_value_box_init(group, FR_TYPE_GROUP, NULL, false);
1307 
1308  p = in;
1309  end = in + inlen;
1310 
1311  /*
1312  * Multi-valued operations
1313  */
1314  op = token2op[(uint8_t) p[0]];
1315  if (op == T_INVALID) {
1316  fr_strerror_printf("Unknown operator '%c'", p[0]);
1317  RETURN_PARSE_ERROR(0);
1318  }
1319  p++;
1320 
1321  while (p < end) {
1322  fr_skip_whitespace(p);
1323 
1324  a = talloc_zero(group, fr_value_box_t);
1325 
1326  match_len = parse_typed_value(result, a, &value, p, end - p);
1327  if (match_len == 0) return 0; /* errors have already been updated */
1328 
1329  fr_value_box_list_insert_tail(&group->vb_group, a);
1330 
1331  p += match_len;
1332 
1333  if (strncmp(p, "->", 2) == 0) break;
1334  }
1335 
1336  out = talloc_zero(cc->tmp_ctx, fr_value_box_t);
1337 
1338  if (strncmp(p, "->", 2) != 0) RETURN_PARSE_ERROR(0);
1339  p += 2;
1340  fr_skip_whitespace(p);
1341 
1342  type = fr_table_value_by_longest_prefix(&match_len, fr_type_table, p, end - p, FR_TYPE_MAX);
1343  if (type == FR_TYPE_MAX) RETURN_PARSE_ERROR(0);
1344 
1345 
1346  if (fr_value_calc_nary_op(cc->tmp_ctx, out, type, op, group) < 0) {
1348  }
1349 
1351  if (slen <= 0) RETURN_OK_WITH_ERROR();
1352 
1353  RETURN_OK(slen);
1354 }
1355 
1356 /** Change the working directory
1357  *
1358  */
1360  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1361 {
1362  TALLOC_FREE(cc->path); /* Free old directories */
1363 
1364  cc->path = fr_realpath(cc->tmp_ctx, in, inlen);
1365  if (!cc->path) RETURN_COMMAND_ERROR();
1366 
1368 
1369  RETURN_OK(talloc_array_length(cc->path) - 1);
1370 }
1371 
1372 /*
1373  * Clear the data buffer
1374  */
1376  char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
1377 {
1378  memset(data, 0, COMMAND_OUTPUT_MAX);
1379  RETURN_NOOP(0);
1380 }
1381 
1382 /*
1383  * Add a command by talloc'ing a table for it.
1384  */
1386  char *data, size_t UNUSED data_used, char *in, UNUSED size_t inlen)
1387 {
1388  char *p, *name;
1389  char *parent = NULL;
1390  fr_cmd_table_t *table;
1391  char buffer[8192];
1392 
1393  table = talloc_zero(cc->tmp_ctx, fr_cmd_table_t);
1394 
1395  strlcpy(buffer, in, sizeof(buffer));
1396 
1397  p = strchr(buffer, ':');
1398  if (!p) {
1399  fr_strerror_const("no ':name' specified");
1400  RETURN_PARSE_ERROR(0);
1401  }
1402 
1403  *p = '\0';
1404  p++;
1405 
1406  parent = talloc_strdup(cc->tmp_ctx, in);
1407 
1408  /*
1409  * Set the name and try to find the syntax.
1410  */
1411  name = p;
1412  fr_skip_whitespace(p);
1413 
1414  if (isspace((uint8_t) *p)) {
1415  *p = '\0';
1416  p++;
1417  }
1418 
1419  fr_skip_whitespace(p);
1420 
1421  if (*p) {
1422  table->syntax = talloc_strdup(table, p);
1423  }
1424  table->parent = parent;
1425  table->name = name;
1426  table->help = NULL;
1427  table->func = command_func;
1428  table->tab_expand = NULL;
1429  table->read_only = true;
1430 
1431  if (fr_command_add(table, &command_head, NULL, NULL, table) < 0) {
1432  fr_strerror_const_push("ERROR: Failed adding command");
1434  }
1435 
1436  if (fr_debug_lvl) command_print();
1437 
1439 }
1440 
1441 /*
1442  * Do tab completion on a command
1443  */
1445  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1446 {
1447  int i;
1448  int num_expansions;
1449  char const *expansions[CMD_MAX_ARGV];
1450  char *p = data, *end = p + COMMAND_OUTPUT_MAX, **argv;
1451  fr_cmd_info_t info;
1452  size_t len;
1453 
1454  info.argc = 0;
1455  info.max_argc = CMD_MAX_ARGV;
1456  info.argv = talloc_zero_array(cc->tmp_ctx, char const *, CMD_MAX_ARGV);
1457  info.box = talloc_zero_array(cc->tmp_ctx, fr_value_box_t *, CMD_MAX_ARGV);
1458 
1459  memcpy(&argv, &info.argv, sizeof(argv)); /* const issues */
1460  info.argc = fr_dict_str_to_argv(in, argv, CMD_MAX_ARGV);
1461  if (info.argc <= 0) {
1462  fr_strerror_const("Failed splitting input");
1463  RETURN_PARSE_ERROR(-(info.argc));
1464  }
1465 
1466  num_expansions = fr_command_tab_expand(cc->tmp_ctx, command_head, &info, CMD_MAX_ARGV, expansions);
1467 
1468  len = snprintf(p, end - p, "%d - ", num_expansions);
1469  if (is_truncated(len, end - p)) {
1470  oob:
1471  fr_strerror_const("Out of output buffer space for radmin command");
1473  }
1474  p += len;
1475 
1476  for (i = 0; i < num_expansions; i++) {
1477  len = snprintf(p, end - p, "'%s', ", expansions[i]);
1478  if (is_truncated(len, end - p)) goto oob;
1479  p += len;
1480  }
1481 
1482  /*
1483  * Remove the trailing ", "
1484  */
1485  if (num_expansions > 0) {
1486  p -= 2;
1487  *p = '\0';
1488  }
1489 
1490  return p - data;
1491 }
1492 
1493 /** Parse and reprint a condition
1494  *
1495  */
1497  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1498 {
1499  ssize_t slen;
1500  CONF_SECTION *cs;
1501  size_t len;
1502  xlat_exp_head_t *head = NULL;
1503 
1504  cs = cf_section_alloc(NULL, NULL, "if", "condition");
1505  if (!cs) {
1506  fr_strerror_const("Out of memory");
1508  }
1509  cf_filename_set(cs, cc->filename);
1510  cf_lineno_set(cs, cc->lineno);
1511 
1513 
1514  slen = xlat_tokenize_condition(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, inlen), NULL, &cc->tmpl_rules);
1515  if (slen <= 0) {
1516  fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
1517  talloc_free(cs);
1519  }
1521 
1522  talloc_free(head);
1523  talloc_free(cs);
1524 
1525  RETURN_OK(len);
1526 }
1527 
1529  char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1530 {
1531  size_t len;
1532 
1533  len = snprintf(data, COMMAND_OUTPUT_MAX, "%u", cc->test_count);
1534  if (is_truncated(len, COMMAND_OUTPUT_MAX)) {
1535  fr_strerror_const("Command count would overflow data buffer (shouldn't happen)");
1537  }
1538 
1539  RETURN_OK(len);
1540 }
1541 
1543  char *data, size_t data_used, char *in, size_t inlen)
1544 {
1545  fr_test_point_pair_decode_t *tp = NULL;
1546  void *decode_ctx = NULL;
1547  char *p;
1548  uint8_t *to_dec;
1549  uint8_t *to_dec_end;
1550  ssize_t slen;
1551 
1552  fr_dict_attr_t const *da;
1553  fr_pair_t *head;
1554 
1555  da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1556  fr_assert(da != NULL);
1557  head = fr_pair_afrom_da(cc->tmp_ctx, da);
1558  if (!head) {
1559  fr_strerror_const_push("Failed allocating memory");
1561  }
1562 
1563  p = in;
1564 
1565  slen = load_test_point_by_command((void **)&tp, in, "tp_decode_pair");
1566  if (!tp) {
1567  fr_strerror_const_push("Failed locating decoder testpoint");
1569  }
1570 
1571  p += slen;
1572  fr_skip_whitespace(p);
1573 
1574  if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx) < 0)) {
1575  fr_strerror_const_push("Failed initialising decoder testpoint");
1577  }
1578 
1579  /*
1580  * Hack because we consume more of the command string
1581  * so we need to check this again.
1582  */
1583  if (*p == '-') {
1584  p = data;
1585  inlen = data_used;
1586  }
1587 
1588  /*
1589  * Decode hex from input text
1590  */
1592  if (slen <= 0) {
1593  CLEAR_TEST_POINT(cc);
1594  RETURN_PARSE_ERROR(-(slen));
1595  }
1596 
1597  to_dec = (uint8_t *)data;
1598  to_dec_end = to_dec + slen;
1599 
1600  ASAN_POISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1601 
1602  /*
1603  * Run the input data through the test
1604  * point to produce fr_pair_ts.
1605  */
1606  while (to_dec < to_dec_end) {
1607  slen = tp->func(head, &head->vp_group, cc->tmpl_rules.attr.namespace,
1608  (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1609  cc->last_ret = slen;
1610  if (slen <= 0) {
1611  ASAN_UNPOISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1612  CLEAR_TEST_POINT(cc);
1614  }
1615  if ((size_t)slen > (size_t)(to_dec_end - to_dec)) {
1616  fr_perror("%s: Internal sanity check failed at %d", __FUNCTION__, __LINE__);
1617  ASAN_UNPOISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1618  CLEAR_TEST_POINT(cc);
1620  }
1621  to_dec += slen;
1622  }
1623 
1624  /*
1625  * Clear any spurious errors
1626  */
1628  ASAN_UNPOISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1629 
1630  /*
1631  * Output may be an error, and we ignore
1632  * it if so.
1633  */
1634  slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1635  if (slen <= 0) {
1637  }
1638 
1639  CLEAR_TEST_POINT(cc);
1640  RETURN_OK(slen);
1641 }
1642 
1644  char *data, size_t data_used, char *in, size_t inlen)
1645 {
1646  fr_test_point_proto_decode_t *tp = NULL;
1647  void *decode_ctx = NULL;
1648  char *p;
1649  uint8_t *to_dec;
1650  uint8_t *to_dec_end;
1651  ssize_t slen;
1652 
1653  fr_dict_attr_t const *da;
1654  fr_pair_t *head;
1655 
1656  da = fr_dict_attr_by_name(NULL, fr_dict_root(fr_dict_internal()), "request");
1657  fr_assert(da != NULL);
1658  head = fr_pair_afrom_da(cc->tmp_ctx, da);
1659  if (!head) {
1660  fr_strerror_const_push("Failed allocating memory");
1662  }
1663 
1664  p = in;
1665 
1666  slen = load_test_point_by_command((void **)&tp, in, "tp_decode_proto");
1667  if (!tp) {
1668  fr_strerror_const_push("Failed locating decoder testpoint");
1670  }
1671 
1672  p += slen;
1673  fr_skip_whitespace(p);
1674 
1675  if (tp->test_ctx && (tp->test_ctx(&decode_ctx, cc->tmp_ctx) < 0)) {
1676  fr_strerror_const_push("Failed initialising decoder testpoint");
1678  }
1679 
1680  /*
1681  * Hack because we consume more of the command string
1682  * so we need to check this again.
1683  */
1684  if (*p == '-') {
1685  p = data;
1686  inlen = data_used;
1687  }
1688 
1689  /*
1690  * Decode hex from input text
1691  */
1693  if (slen <= 0) {
1694  CLEAR_TEST_POINT(cc);
1695  RETURN_PARSE_ERROR(-(slen));
1696  }
1697 
1698  to_dec = (uint8_t *)data;
1699  to_dec_end = to_dec + slen;
1700 
1701  ASAN_POISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1702 
1703  slen = tp->func(head, &head->vp_group,
1704  (uint8_t *)to_dec, (to_dec_end - to_dec), decode_ctx);
1705  cc->last_ret = slen;
1706  if (slen <= 0) {
1707  ASAN_UNPOISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1708  CLEAR_TEST_POINT(cc);
1710  }
1711 
1712  /*
1713  * Clear any spurious errors
1714  */
1716  ASAN_UNPOISON_MEMORY_REGION(to_dec_end, COMMAND_OUTPUT_MAX - slen);
1717 
1718  /*
1719  * Output may be an error, and we ignore
1720  * it if so.
1721  */
1722 
1723  /*
1724  * Print the pairs.
1725  */
1726  slen = fr_pair_list_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), NULL, &head->vp_group);
1727  if (slen <= 0) {
1728  fr_assert(0);
1730  }
1731 
1732  CLEAR_TEST_POINT(cc);
1733  RETURN_OK(slen);
1734 }
1735 
1736 /** Parse a dictionary attribute, writing "ok" to the data buffer is everything was ok
1737  *
1738  */
1740  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1741 {
1743 
1745 }
1746 
1747 /** Print the currently loaded dictionary
1748  *
1749  */
1751  UNUSED char *data, size_t data_used, UNUSED char *in, UNUSED size_t inlen)
1752 {
1754 
1755  /*
1756  * Don't modify the contents of the data buffer
1757  */
1758  RETURN_OK(data_used);
1759 }
1760 
1761 static CC_HINT(nonnull)
1763  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
1764 {
1765  size_t need;
1766  ssize_t ret;
1767  char *p, *next;
1768  uint8_t *enc_p;
1769  char buffer[8192];
1770 
1771  strlcpy(buffer, in, sizeof(buffer));
1772 
1773  p = buffer;
1774  next = strchr(p, ',');
1775  if (next) *next = 0;
1776 
1777  enc_p = cc->buffer_start;
1778 
1779  while (true) {
1780  fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
1781 
1782  fr_skip_whitespace(p);
1783 
1784  if (fr_value_box_from_str(box, box, FR_TYPE_STRING, NULL,
1785  p, strlen(p),
1787  false) < 0) {
1788  talloc_free(box);
1790  }
1791 
1792  ret = fr_dns_label_from_value_box(&need,
1793  cc->buffer_start, cc->buffer_end - cc->buffer_start, enc_p, true, box, NULL);
1794  talloc_free(box);
1795 
1796  if (ret < 0) RETURN_OK_WITH_ERROR();
1797 
1798  if (ret == 0) RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "need=%zd", need));
1799 
1800  enc_p += ret;
1801 
1802  /*
1803  * Go to the next input string
1804  */
1805  if (!next) break;
1806 
1807  p = next + 1;
1808  next = strchr(p, ',');
1809  if (next) *next = 0;
1810  }
1811 
1812  if ((cc->fuzzer_dir >= 0) &&
1813  (dump_fuzzer_data(cc->fuzzer_dir, in, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
1815  }
1816 
1817  RETURN_OK(hex_print(data, COMMAND_OUTPUT_MAX, cc->buffer_start, enc_p - cc->buffer_start));
1818 }
1819 
1821  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1822 {
1823  ssize_t slen, total, i, outlen;
1824  char *out, *end;
1825  fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
1826 
1827  /*
1828  * Decode hex from input text
1829  */
1830  total = hex_to_bin(cc->buffer_start, cc->buffer_end - cc->buffer_start, in, inlen);
1831  if (total <= 0) RETURN_PARSE_ERROR(-total);
1832 
1833  out = data;
1834  end = data + COMMAND_OUTPUT_MAX;
1835 
1836  for (i = 0; i < total; i += slen) {
1837  slen = fr_dns_label_to_value_box(box, box, cc->buffer_start, total, cc->buffer_start + i, false, NULL);
1838  if (slen <= 0) {
1839  error:
1840  talloc_free(box);
1842  }
1843 
1844  /*
1845  * Separate names by commas
1846  */
1847  if (i > 0) *(out++) = ',';
1848 
1849  /*
1850  * We don't print it with quotes.
1851  */
1852  outlen = fr_value_box_print(&FR_SBUFF_OUT(out, end - out), box, NULL);
1853  if (outlen <= 0) goto error;
1854  out += outlen;
1855 
1856  fr_value_box_clear(box);
1857  }
1858 
1859  talloc_free(box);
1860  RETURN_OK(out - data);
1861 }
1862 
1864  char *data, UNUSED size_t data_used, char *in, size_t inlen)
1865 {
1866  fr_test_point_pair_encode_t *tp = NULL;
1867 
1868  fr_dcursor_t cursor;
1869  void *encode_ctx = NULL;
1870  ssize_t slen;
1871  char *p = in;
1872 
1873  uint8_t *enc_p, *enc_end;
1875  fr_pair_t *vp;
1876  bool truncate = false;
1877 
1878  size_t iterations = 0;
1879  fr_pair_parse_t root, relative;
1880 
1882 
1883  slen = load_test_point_by_command((void **)&tp, p, "tp_encode_pair");
1884  if (!tp) {
1885  fr_strerror_const_push("Failed locating encode testpoint");
1886  CLEAR_TEST_POINT(cc);
1888  }
1889 
1890  p += ((size_t)slen);
1891  fr_skip_whitespace(p);
1892 
1893  /*
1894  * The truncate torture test.
1895  *
1896  * Increase the buffer one byte at a time until all items in the cursor
1897  * have been encoded.
1898  *
1899  * The poisoned region at the end of the buffer will detect overruns
1900  * if we're running with asan.
1901  *
1902  */
1903  if (strncmp(p, "truncate", sizeof("truncate") - 1) == 0) {
1904  truncate = true;
1905  p += sizeof("truncate") - 1;
1906  fr_skip_whitespace(p);
1907  }
1908 
1909  if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx) < 0)) {
1910  fr_strerror_const_push("Failed initialising encoder testpoint");
1911  CLEAR_TEST_POINT(cc);
1913  }
1914 
1915  root = (fr_pair_parse_t) {
1916  .ctx = cc->tmp_ctx,
1917  .da = cc->tmpl_rules.attr.namespace,
1918  .list = &head,
1919  };
1920  relative = (fr_pair_parse_t) { };
1921 
1922  slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
1923  if (slen <= 0) {
1924  CLEAR_TEST_POINT(cc);
1926  }
1927 
1929 
1930  /*
1931  * Outer loop implements truncate test
1932  */
1933  do {
1934  enc_p = cc->buffer_start;
1935  enc_end = truncate ? cc->buffer_start + iterations++ : cc->buffer_end;
1936 
1937  if (truncate) {
1938 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1939  /*
1940  * Poison the region between the subset of the buffer
1941  * we're using and the end of the buffer.
1942  */
1943  ASAN_POISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
1944 
1945  DEBUG("%s[%d]: Iteration %zu - Safe region %p-%p (%zu bytes), "
1946  "poisoned region %p-%p (%zu bytes)", cc->filename, cc->lineno, iterations - 1,
1947  enc_p, enc_end, enc_end - enc_p, enc_end, cc->buffer_end, cc->buffer_end - enc_end);
1948 #else
1949  DEBUG("%s[%d]: Iteration %zu - Allowed region %p-%p (%zu bytes)",
1950  cc->filename, cc->lineno, iterations - 1, enc_p, enc_end, enc_end - enc_p);
1951 #endif
1952  }
1953 
1954  for (vp = fr_pair_dcursor_iter_init(&cursor, &head,
1956  dictionary_current(cc));
1957  vp;
1958  vp = fr_dcursor_current(&cursor)) {
1959  slen = tp->func(&FR_DBUFF_TMP(enc_p, enc_end), &cursor, encode_ctx);
1960  cc->last_ret = slen;
1961 
1962  if (truncate) DEBUG("%s[%d]: Iteration %zu - Result %zd%s%s",
1963  cc->filename, cc->lineno, iterations - 1, slen,
1964  *fr_strerror_peek() != '\0' ? " - " : "",
1965  *fr_strerror_peek() != '\0' ? fr_strerror_peek() : "");
1966  if (slen < 0) break;
1967 
1968  /*
1969  * Encoder indicated it encoded too much data
1970  */
1971  if (slen > (enc_end - enc_p)) {
1972  fr_strerror_printf("Expected returned encoded length <= %zu bytes, got %zu bytes",
1973  (enc_end - enc_p), (size_t)slen);
1974 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1975  if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
1976 #endif
1978  CLEAR_TEST_POINT(cc);
1980  }
1981 
1982  enc_p += slen;
1983 
1984  if (slen == 0) break;
1985 
1986  }
1987 
1988 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
1989  /*
1990  * un-poison the region between the subset of the buffer
1991  * we're using and the end of the buffer.
1992  */
1993  if (truncate) ASAN_UNPOISON_MEMORY_REGION(enc_end, (cc->buffer_end) - enc_end);
1994 #endif
1995  /*
1996  * We consumed all the VPs, so presumably encoded the
1997  * complete pair list.
1998  */
1999  if (!vp) break;
2000  } while (truncate && (enc_end < cc->buffer_end));
2001 
2002  /*
2003  * Last iteration result in an error
2004  */
2005  if (slen < 0) {
2007  CLEAR_TEST_POINT(cc);
2009  }
2010 
2011  /*
2012  * Clear any spurious errors
2013  */
2015 
2017 
2018  CLEAR_TEST_POINT(cc);
2019 
2020  if ((cc->fuzzer_dir >= 0) &&
2021  (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, enc_p - cc->buffer_start) < 0)) {
2023  }
2024 
2026 }
2027 
2028 /** Encode a RADIUS attribute writing the result to the data buffer as space separated hexits
2029  *
2030  */
2032  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2033 {
2034  size_t len;
2035  char buffer[8192];
2036 
2037  strlcpy(buffer, in, sizeof(buffer));
2038 
2039  len = encode_rfc(buffer, cc->buffer_start, cc->buffer_end - cc->buffer_start);
2040  if (len <= 0) RETURN_PARSE_ERROR(0);
2041 
2042  if (len >= (size_t)(cc->buffer_end - cc->buffer_start)) {
2043  fr_strerror_const("Encoder output would overflow output buffer");
2045  }
2046 
2048 }
2049 
2050 /** Parse a list of pairs
2051  *
2052  */
2054  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2055 {
2056  ssize_t slen;
2058  bool done = false;
2059  char *filename;
2060  FILE *fp;
2061 
2062  filename = talloc_asprintf(cc->tmp_ctx, "%s/%s", cc->path, in);
2063 
2064  fp = fopen(filename, "r");
2065  talloc_free(filename);
2066 
2067  if (!fp) {
2068  fr_strerror_printf("Failed opening %s - %s", in, fr_syserror(errno));
2070  }
2071 
2073  slen = fr_pair_list_afrom_file(cc->tmp_ctx, cc->tmpl_rules.attr.dict_def, &head, fp, &done);
2074  fclose(fp);
2075  if (slen < 0) {
2077  }
2078 
2079  /*
2080  * Print the pairs.
2081  */
2083  if (slen <= 0) {
2084  fr_assert(0);
2086  }
2087 
2088  if (!done) {
2089  strlcpy(data + slen, "!DONE", COMMAND_OUTPUT_MAX - slen);
2090  slen += 5;
2091  }
2092 
2094 
2095  RETURN_OK(slen);
2096 }
2097 
2098 
2100  char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2101 {
2103 }
2104 
2106  char *data, UNUSED size_t data_used, char *in, size_t inlen)
2107 {
2108  fr_test_point_proto_encode_t *tp = NULL;
2109 
2110  void *encode_ctx = NULL;
2111  ssize_t slen;
2112  char *p = in;
2113 
2115  fr_pair_parse_t root, relative;
2116 
2118 
2119  slen = load_test_point_by_command((void **)&tp, p, "tp_encode_proto");
2120  if (!tp) {
2121  fr_strerror_const_push("Failed locating encode testpoint");
2122  CLEAR_TEST_POINT(cc);
2124  }
2125 
2126  p += ((size_t)slen);
2127  fr_skip_whitespace(p);
2128  if (tp->test_ctx && (tp->test_ctx(&encode_ctx, cc->tmp_ctx) < 0)) {
2129  fr_strerror_const_push("Failed initialising encoder testpoint");
2130  CLEAR_TEST_POINT(cc);
2132  }
2133 
2134  root = (fr_pair_parse_t) {
2135  .ctx = cc->tmp_ctx,
2136  .da = cc->tmpl_rules.attr.namespace,
2137  .list = &head,
2138  };
2139  relative = (fr_pair_parse_t) { };
2140 
2141  slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(p, inlen - (p - in)));
2142  if (slen <= 0) {
2143  CLEAR_TEST_POINT(cc);
2145  }
2146 
2147  slen = tp->func(cc->tmp_ctx, &head, cc->buffer_start, cc->buffer_end - cc->buffer_start, encode_ctx);
2149  cc->last_ret = slen;
2150  if (slen < 0) {
2151  CLEAR_TEST_POINT(cc);
2153  }
2154  /*
2155  * Clear any spurious errors
2156  */
2158 
2159  CLEAR_TEST_POINT(cc);
2160 
2161  if ((cc->fuzzer_dir >= 0) &&
2162  (dump_fuzzer_data(cc->fuzzer_dir, p, cc->buffer_start, slen) < 0)) {
2164  }
2165 
2167 }
2168 
2169 /** Command eof
2170  *
2171  * Mark the end of a test file if we're reading from stdin.
2172  *
2173  * Doesn't actually do anything, is just a placeholder for the command processing loop.
2174  */
2176  UNUSED char *data, UNUSED size_t data_used, UNUSED char *in, UNUSED size_t inlen)
2177 {
2178  return 0;
2179 }
2180 
2181 /** Enable fuzzer output
2182  *
2183  * Any commands that produce potentially useful corpus seed data will write that out data
2184  * to files in the specified directory, using the md5 of the text input at as the file name.
2185  *
2186  */
2188  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2189 {
2190  int fd;
2191  struct stat sdir;
2192  char *fuzzer_dir;
2193  bool retry_dir = true;
2194 
2195  /*
2196  * Close any open fuzzer output dirs
2197  */
2198  if (cc->fuzzer_dir >= 0) {
2199  close(cc->fuzzer_dir);
2200  cc->fuzzer_dir = -1;
2201  }
2202 
2203  if (in[0] == '\0') {
2204  fr_strerror_const("Missing directory name");
2205  RETURN_PARSE_ERROR(0);
2206  }
2207 
2208  fuzzer_dir = talloc_asprintf(cc->tmp_ctx, "%s/%s",
2209  cc->config->fuzzer_dir ? cc->config->fuzzer_dir : cc->path, in);
2210 
2211 again:
2212  fd = open(fuzzer_dir, O_RDONLY);
2213  if (fd < 0) {
2214  if (mkdir(fuzzer_dir, 0777) == 0) {
2215  fd = open(fuzzer_dir, O_RDONLY);
2216  if (fd >= 0) goto stat;
2217  /*
2218  * Prevent race if multiple unit_test_attribute instances
2219  * attempt to create the same output dir.
2220  */
2221  } else if ((errno == EEXIST) && retry_dir) {
2222  retry_dir = false; /* Only allow this once */
2223  goto again;
2224  }
2225 
2226  fr_strerror_printf("fuzzer-out \"%s\" doesn't exist: %s", fuzzer_dir, fr_syserror(errno));
2227  RETURN_PARSE_ERROR(0);
2228  }
2229 
2230 stat:
2231  if (fstat(fd, &sdir) < 0) {
2232  close(fd);
2233  fr_strerror_printf("failed statting fuzzer-out \"%s\": %s", fuzzer_dir, fr_syserror(errno));
2234  RETURN_PARSE_ERROR(0);
2235  }
2236 
2237  if (!(sdir.st_mode & S_IFDIR)) {
2238  close(fd);
2239  fr_strerror_printf("fuzzer-out \"%s\" is not a directory", fuzzer_dir);
2240  RETURN_PARSE_ERROR(0);
2241  }
2242  cc->fuzzer_dir = fd;
2243  talloc_free(fuzzer_dir);
2244 
2245  return 0;
2246 }
2247 
2248 /** Exit gracefully with the specified code
2249  *
2250  */
2252  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2253 {
2254  if (!*in) RETURN_EXIT(0);
2255 
2256  RETURN_EXIT(atoi(in));
2257 }
2258 
2260  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2261 {
2262  char *name, *tmp = NULL;
2263  char const *dir;
2264  char *q;
2265  int ret;
2266 
2268 
2269  if (in[0] == '\0') {
2270  fr_strerror_const("Missing dictionary name");
2271  RETURN_PARSE_ERROR(0);
2272  }
2273 
2274  q = strchr(in, ' ');
2275  if (q) {
2276  name = tmp = talloc_bstrndup(NULL, in, q - in);
2277  q++;
2278  dir = q;
2279  } else {
2280  name = in;
2281  dir = cc->path;
2282  }
2283 
2284  ret = fr_dict_read(UNCONST(fr_dict_t *, cc->tmpl_rules.attr.dict_def), dir, name);
2285  talloc_free(tmp);
2286  if (ret < 0) RETURN_COMMAND_ERROR();
2287 
2288  RETURN_OK(0);
2289 }
2290 
2291 
2292 /** Compare the data buffer to an expected value
2293  *
2294  */
2296  char *data, size_t data_used, char *in, size_t inlen)
2297 {
2298  if (strcmp(in, data) != 0) {
2299  if (write_fp) {
2300  strcpy(in, data);
2301  RETURN_OK(data_used);
2302  }
2303 
2304  mismatch_print(cc, "match", in, inlen, data, data_used, true);
2305  RETURN_MISMATCH(data_used);
2306  }
2307 
2308  /*
2309  * We didn't actually write anything, but this
2310  * keeps the contents of the data buffer around
2311  * for the next command to operate on.
2312  */
2313  RETURN_OK(data_used);
2314 }
2315 
2316 /** Compare the data buffer against an expected expression
2317  *
2318  */
2320  char *data, size_t data_used, char *in, size_t inlen)
2321 {
2322  ssize_t slen;
2323  regex_t *regex;
2324  int ret;
2325 
2326  slen = regex_compile(cc->tmp_ctx, &regex, in, inlen, NULL, false, true);
2327  if (slen <= 0) RETURN_COMMAND_ERROR();
2328 
2329  ret = regex_exec(regex, data, data_used, NULL);
2330  talloc_free(regex);
2331 
2332  switch (ret) {
2333  case -1:
2334  default:
2336 
2337  case 0:
2338  mismatch_print(cc, "match-regex", in, inlen, data, data_used, false);
2339  RETURN_MISMATCH(data_used);
2340 
2341  case 1:
2342  RETURN_OK(data_used);
2343  }
2344 }
2345 
2346 /** Artificially limit the maximum packet size.
2347  *
2348  */
2350  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2351 {
2352  unsigned long size;
2353  char *end;
2354 
2356 
2357  if (*in != '\0') {
2358  size = strtoul(in, &end, 10);
2359  if ((size == ULONG_MAX) || *end || (size >= 65536)) {
2360  fr_strerror_const_push("Invalid integer");
2362  }
2363  } else {
2364  size = DEFAULT_BUFFER_SIZE;
2365  }
2366 
2367  if (poisoned_buffer_allocate(cc, &cc->buffer, size) < 0) RETURN_EXIT(1);
2370 
2371  RETURN_OK(snprintf(data, COMMAND_OUTPUT_MAX, "%ld", size));
2372 }
2373 
2374 extern bool tmpl_require_enum_prefix;
2375 
2376 /** Set or clear migration flags.
2377  *
2378  */
2380  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2381 {
2382  char *p;
2383  bool *out;
2384 
2386  p = in;
2387 
2388  if (strncmp(p, "xlat_new_functions", sizeof("xlat_new_functions") - 1) == 0) {
2389  p += sizeof("xlat_new_functions") - 1;
2391 
2392  } else if (strncmp(p, "tmpl_require_enum_prefix", sizeof("tmpl_require_enum_prefix") - 1) == 0) {
2393  p += sizeof("tmpl_require_enum_prefix") - 1;
2395 
2396  } else {
2397  fr_strerror_const("Unknown migration flag");
2398  RETURN_PARSE_ERROR(0);
2399  }
2400 
2401  fr_skip_whitespace(p);
2402  if (*p != '=') {
2403  fr_strerror_const("Missing '=' after flag");
2404  RETURN_PARSE_ERROR(0);
2405  }
2406  p++;
2407 
2408  fr_skip_whitespace(p);
2409  if ((strcmp(p, "yes") == 0) || (strcmp(p, "true") == 0) || (strcmp(p, "1") == 0)) {
2410  *out = true;
2411 
2412  } else if ((strcmp(p, "no") == 0) || (strcmp(p, "false") == 0) || (strcmp(p, "0") == 0)) {
2413  *out = false;
2414 
2415  } else {
2416  fr_strerror_const("Invalid value for flag");
2417  RETURN_PARSE_ERROR(0);
2418  }
2419 
2420  RETURN_OK(0);
2421 }
2422 
2423 /** Skip the test file if we're missing a particular feature
2424  *
2425  */
2427  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2428 {
2429  CONF_PAIR *cp;
2430 
2431  if (in[0] == '\0') {
2432  fr_strerror_printf("Prerequisite syntax is \"need-feature <feature>\". "
2433  "Use -f to print features");
2434  RETURN_PARSE_ERROR(0);
2435  }
2436 
2437  cp = cf_pair_find(cc->config->features, in);
2438  if (!cp || (strcmp(cf_pair_value(cp), "yes") != 0)) {
2439  DEBUG("Skipping, missing feature \"%s\"", in);
2440  RETURN_SKIP_FILE();
2441  }
2442 
2443  RETURN_NOOP(0);
2444 }
2445 
2446 /** Negate the result of a match command or any command which returns "OK"
2447  *
2448  */
2450  char *data, size_t data_used, char *in, size_t inlen)
2451 {
2452  data_used = process_line(result, cc, data, data_used, in, inlen);
2453  switch (result->rcode) {
2454  /*
2455  * OK becomes a command error
2456  */
2457  case RESULT_OK:
2458  ERROR("%s[%d]: %.*s: returned 'ok', where we expected 'result-mismatch'",
2459  cc->filename, cc->lineno, (int) inlen, in);
2460  RETURN_MISMATCH(data_used);
2461 
2462  /*
2463  * Mismatch becomes OK
2464  */
2465  case RESULT_MISMATCH:
2466  RETURN_OK(data_used);
2467 
2468  /*
2469  * The rest are unchanged...
2470  */
2471  default:
2472  break;
2473  }
2474 
2475  return data_used;
2476 }
2477 
2478 /** Parse an print an attribute pair or pair list.
2479  *
2480  */
2482  char *data, UNUSED size_t data_used, char *in, size_t inlen)
2483 {
2485  ssize_t slen;
2486  fr_dict_t const *dict = dictionary_current(cc);
2487  fr_pair_parse_t root, relative;
2488 
2490 
2491  root = (fr_pair_parse_t) {
2492  .ctx = cc->tmp_ctx,
2493  .da = fr_dict_root(dict),
2494  .list = &head,
2495  };
2496  relative = (fr_pair_parse_t) { };
2497 
2498  slen = fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(in, inlen));
2499  if (slen <= 0) {
2500 // fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
2503  }
2504 
2505  /*
2506  * Output may be an error, and we ignore
2507  * it if so.
2508  */
2509 
2511  if (slen <= 0) {
2514  }
2515 
2517  RETURN_OK(slen);
2518 }
2519 
2520 /** Dynamically load a protocol library
2521  *
2522  */
2524  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2525 {
2526  ssize_t slen;
2527 
2528  if (*in == '\0') {
2529  fr_strerror_printf("Load syntax is \"proto <lib_name>\"");
2530  RETURN_PARSE_ERROR(0);
2531  }
2532 
2534  slen = load_proto_library(in);
2535  if (slen <= 0) RETURN_PARSE_ERROR(-(slen));
2536 
2537  RETURN_OK(0);
2538 }
2539 
2541  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2542 {
2544  return dictionary_load_common(result, cc, in, NULL);
2545 }
2546 
2548  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2549 {
2550  fr_dict_t const *dict = dictionary_current(cc);
2551  fr_dict_attr_t const *root_da = fr_dict_root(dict);
2552  fr_dict_attr_t const *new_root;
2553 
2554  if (is_whitespace(in) || (*in == '\0')) {
2555  new_root = fr_dict_root(dict);
2556  } else {
2557  new_root = fr_dict_attr_by_name(NULL, fr_dict_root(dict), in);
2558  if (!new_root) {
2559  fr_strerror_printf("dictionary attribute \"%s\" not found in %s", in, root_da->name);
2560  RETURN_PARSE_ERROR(0);
2561  }
2562  }
2563 
2564  cc->tmpl_rules.attr.namespace = new_root;
2565 
2566  RETURN_OK(0);
2567 }
2568 
2569 /** Touch a file to indicate a test completed
2570  *
2571  */
2573  UNUSED char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2574 {
2575  if (fr_unlink(in) < 0) RETURN_COMMAND_ERROR();
2576  if (fr_touch(NULL, in, 0644, true, 0755) <= 0) RETURN_COMMAND_ERROR();
2577 
2578  RETURN_OK(0);
2579 }
2580 
2581 /** Callback for a tmpl rule parser
2582  *
2583  */
2584 typedef ssize_t(*command_tmpl_rule_func)(TALLOC_CTX *ctx, tmpl_rules_t *rules, fr_sbuff_t *value);
2585 
2587 {
2588  bool res;
2589  ssize_t slen;
2590 
2591  slen = fr_sbuff_out_bool(&res, value);
2592  rules->attr.allow_foreign = res;
2593  return slen;
2594 }
2595 
2597 {
2598  bool res;
2599  ssize_t slen;
2600 
2601  slen = fr_sbuff_out_bool(&res, value);
2602  rules->attr.allow_unknown = res;
2603  return slen;
2604 }
2605 
2607 {
2608  bool res;
2609  ssize_t slen;
2610 
2611  slen = fr_sbuff_out_bool(&res, value);
2612  rules->attr.allow_unresolved = res;
2613  return slen;
2614 }
2615 
2617 {
2619  fr_slen_t slen;
2620 
2622  &rules->attr.namespace,
2623  rules->attr.dict_def ? fr_dict_root(rules->attr.dict_def) :
2625  value, NULL);
2627  return slen;
2628 }
2629 
2631 {
2632  ssize_t slen;
2633 
2634  slen = tmpl_attr_list_from_substr(&rules->attr.list_def, value);
2635 
2636  if (slen == 0) {
2637  fr_strerror_printf("Invalid list specifier \"%pV\"",
2639  }
2640 
2641  return slen;
2642 }
2643 
2645 {
2646  fr_slen_t slen;
2647 
2648  slen = tmpl_request_ref_list_afrom_substr(ctx, NULL,
2649  &rules->attr.request_def,
2650  value);
2651  if (slen < 0) {
2652  fr_strerror_printf("Invalid request specifier \"%pV\"",
2654  }
2655 
2656  return slen;
2657 }
2658 
2660  UNUSED char *data, UNUSED size_t data_used, char *in, size_t inlen)
2661 {
2662  fr_sbuff_t sbuff = FR_SBUFF_IN(in, inlen);
2663  ssize_t slen;
2665  void *res;
2666 
2667  static fr_table_ptr_sorted_t tmpl_rule_func_table[] = {
2668  { L("allow_foreign"), (void *)command_tmpl_rule_allow_foreign },
2669  { L("allow_unknown"), (void *)command_tmpl_rule_allow_unknown },
2670  { L("allow_unresolved"), (void *)command_tmpl_rule_allow_unresolved },
2671  { L("attr_parent"), (void *)command_tmpl_rule_attr_parent },
2672  { L("list_def"), (void *)command_tmpl_rule_list_def },
2673  { L("request_def"), (void *)command_tmpl_rule_request_def }
2674  };
2675  static size_t tmpl_rule_func_table_len = NUM_ELEMENTS(tmpl_rule_func_table);
2676 
2677  while (fr_sbuff_extend(&sbuff)) {
2678  fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2679 
2680  fr_sbuff_out_by_longest_prefix(&slen, &res, tmpl_rule_func_table, &sbuff, NULL);
2681  if (res == NULL) {
2682  fr_strerror_printf("Specified rule \"%pV\" is invalid",
2685  }
2686  func = (command_tmpl_rule_func)res; /* -Wpedantic */
2687 
2688  fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2689 
2690  if (!fr_sbuff_next_if_char(&sbuff, '=')) {
2691  fr_strerror_printf("Expected '=' after rule identifier, got \"%pV\"",
2694  }
2695 
2696  fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
2697 
2698  if (func(cc->tmp_ctx, &cc->tmpl_rules, &sbuff) <= 0) RETURN_COMMAND_ERROR();
2699  }
2700 
2701  return fr_sbuff_used(&sbuff);
2702 }
2703 
2705  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2706 {
2707  fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
2708  fr_value_box_t *box2;
2709  char const *value;
2710  size_t match_len;
2711  ssize_t slen;
2712  fr_type_t type;
2713 
2714  match_len = parse_typed_value(result, box, &value, in, strlen(in));
2715  if (match_len == 0) {
2716  talloc_free(box);
2717  return 0; /* errors have already been updated */
2718  }
2719 
2720  type = box->type;
2721 
2722  /*
2723  * Don't print dates with enclosing quotation marks.
2724  */
2725  if (type != FR_TYPE_DATE) {
2728  } else {
2730  }
2731  if (slen <= 0) {
2732  talloc_free(box);
2734  }
2735 
2736  /*
2737  * Behind the scenes, parse the data
2738  * string. We should get the same value
2739  * box as last time.
2740  */
2741  box2 = talloc_zero(NULL, fr_value_box_t);
2742  if (fr_value_box_from_str(box2, box2, type, NULL,
2743  data, slen,
2744  &fr_value_unescape_double, false) < 0) {
2745  talloc_free(box2);
2746  talloc_free(box);
2748  }
2749 
2750  /*
2751  * They MUST be identical
2752  */
2753  if (fr_value_box_cmp(box, box2) != 0) {
2754  fr_strerror_const("ERROR value box reparsing failed. Results not identical");
2755  fr_strerror_printf_push("out: %pV (as string %.*s)", box2, (int) slen, data);
2756  fr_strerror_printf_push("in: %pV (from string %s)", box, value);
2757  talloc_free(box2);
2758  talloc_free(box);
2760  }
2761 
2762  /*
2763  * Store <type><value str...>
2764  */
2765  if (cc->fuzzer_dir >= 0) {
2766  char fuzzer_buffer[1024];
2767  char *fuzzer_p = fuzzer_buffer, *fuzzer_end = fuzzer_p + sizeof(fuzzer_buffer);
2768 
2769  *fuzzer_p++ = (uint8_t)type; /* Fuzzer uses first byte for type */
2770 
2771  strlcpy(fuzzer_p, data, slen > fuzzer_end - fuzzer_p ? fuzzer_end - fuzzer_p : slen);
2772 
2773  if (dump_fuzzer_data(cc->fuzzer_dir, fuzzer_buffer,
2774  (uint8_t *)fuzzer_buffer, strlen(fuzzer_buffer)) < 0) {
2776  }
2777  }
2778 
2779  talloc_free(box2);
2780  talloc_free(box);
2781  RETURN_OK(slen);
2782 }
2783 
2785  char *data, size_t data_used, char *in, size_t inlen)
2786 {
2787  int fd;
2788  char *path;
2789  bool locked = false;
2790 
2791  path = talloc_bstrndup(cc->tmp_ctx, in, inlen);
2792 
2793  fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
2794  if (fd < 0) {
2795  fr_strerror_printf("Failed opening \"%s\": %s", path, fr_syserror(errno));
2796  error:
2797  talloc_free(path);
2798  if (fd >= 0) {
2799  if (locked) (void)flock(fd, LOCK_UN);
2800  close(fd);
2801  }
2803  }
2804 
2805  if (flock(fd, LOCK_EX) < 0) {
2806  fr_strerror_printf("Failed locking \"%s\": %s", path, fr_syserror(errno));
2807  goto error;
2808  }
2809  locked = true;
2810 
2811  while (data_used) {
2812  ssize_t ret;
2813  ret = write(fd, data, data_used);
2814  if (ret < 0) {
2815  fr_strerror_printf("Failed writing to \"%s\": %s", path, fr_syserror(errno));
2816  goto error;
2817  }
2818  data_used -= ret;
2819  data += ret;
2820  }
2821  (void)flock(fd, LOCK_UN);
2822  talloc_free(path);
2823  close(fd);
2824 
2825  RETURN_OK(data_used);
2826 }
2827 
2828 /** Parse an reprint and xlat expansion
2829  *
2830  */
2832  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2833 {
2834  ssize_t dec_len;
2835  xlat_exp_head_t *head = NULL;
2836  size_t input_len = strlen(in), escaped_len;
2837  fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
2838 
2839  dec_len = xlat_tokenize(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), &p_rules,
2840  &(tmpl_rules_t) {
2841  .attr = {
2842  .dict_def = dictionary_current(cc),
2843  .list_def = request_attr_request,
2844  .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2845  },
2846  .xlat = cc->tmpl_rules.xlat,
2847  },
2848  0);
2849  if (dec_len <= 0) {
2850  fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
2851 
2852  return_error:
2854  }
2855 
2856  if (((size_t) dec_len != input_len)) {
2857  fr_strerror_printf_push_head("offset %d 'Too much text'", (int) dec_len);
2858  goto return_error;
2859  }
2860 
2862  RETURN_OK(escaped_len);
2863 }
2864 
2865 /** Parse and reprint an xlat expression expansion
2866  *
2867  */
2869  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2870 {
2871  ssize_t dec_len;
2872  xlat_exp_head_t *head = NULL;
2873  size_t input_len = strlen(in), escaped_len;
2874 // fr_sbuff_parse_rules_t p_rules = { .escapes = &fr_value_unescape_double };
2875 
2876  dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL,
2877  &(tmpl_rules_t) {
2878  .attr = {
2879  .dict_def = dictionary_current(cc),
2880  .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
2881  .list_def = request_attr_request,
2882  }
2883  });
2884  if (dec_len <= 0) {
2885  fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
2886 
2887  return_error:
2889  }
2890 
2891  if (((size_t) dec_len != input_len)) {
2892  fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, dec_len);
2893  goto return_error;
2894  }
2895 
2897  RETURN_OK(escaped_len);
2898 }
2899 
2900 /** Parse, purify, and reprint an xlat expression expansion
2901  *
2902  */
2904  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2905 {
2906  ssize_t dec_len;
2907  xlat_exp_head_t *head = NULL;
2908  size_t input_len = strlen(in), escaped_len;
2909  tmpl_rules_t t_rules = (tmpl_rules_t) {
2910  .attr = {
2912  .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved,
2913  .list_def = request_attr_request,
2914  },
2915  .xlat = cc->tmpl_rules.xlat,
2916  .at_runtime = true,
2917  };
2918 
2919  if (!el) {
2920  fr_strerror_const("Flag '-p' not used. xlat_purify is disabled");
2921  goto return_error;
2922  }
2923  t_rules.xlat.runtime_el = el;
2924 
2925  dec_len = xlat_tokenize_expression(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), NULL, &t_rules);
2926  if (dec_len <= 0) {
2927  fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
2928 
2929  return_error:
2931  }
2932 
2933  if (((size_t) dec_len != input_len)) {
2934  fr_strerror_printf_push_head("Passed in %zu characters, but only parsed %zd characters", input_len, dec_len);
2935  goto return_error;
2936  }
2937 
2938  if (fr_debug_lvl > 2) {
2939  DEBUG("Before purify --------------------------------------------------");
2941  }
2942 
2943  (void) xlat_purify(head, NULL);
2944 
2945  if (fr_debug_lvl > 2) {
2946  DEBUG("After purify --------------------------------------------------");
2948  }
2949 
2951  RETURN_OK(escaped_len);
2952 }
2953 
2954 
2955 /** Parse an reprint and xlat argv expansion
2956  *
2957  */
2959  char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
2960 {
2961  int i, argc;
2962  char *p;
2963  ssize_t slen;
2964  xlat_exp_head_t *head = NULL;
2965  xlat_exp_head_t **argv;
2966  size_t len;
2967  size_t input_len = strlen(in);
2968  char buff[1024];
2969 
2970  slen = xlat_tokenize_argv(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len),
2971  NULL, NULL,
2972  &(tmpl_rules_t) {
2973  .attr = {
2974  .dict_def = dictionary_current(cc),
2975  .list_def = request_attr_request,
2976  .allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
2977  },
2978  }, false, false);
2979  if (slen <= 0) {
2980  fr_strerror_printf_push_head("ERROR offset %d", (int) -slen);
2982  }
2983 
2984  argc = xlat_flatten_compiled_argv(cc->tmp_ctx, &argv, head);
2985  if (argc <= 0) {
2986  fr_strerror_printf_push("ERROR in argument %d", (int) -argc);
2988  }
2989 
2990  for (i = 0, p = data; i < argc; i++) {
2991  (void) xlat_print(&FR_SBUFF_OUT(buff, sizeof(buff)), argv[i], NULL);
2992 
2993  len = snprintf(p, data + COMMAND_OUTPUT_MAX - p, "[%d]{ %s }, ", i, buff);
2994  p += len;
2995  }
2996 
2997  p -= 2;
2998  *p = '\0';
2999 
3000  RETURN_OK(p - data);
3001 }
3002 
3004  { L("#"), &(command_entry_t){
3005  .func = command_comment,
3006  .usage = "#<string>",
3007  .description = "A comment - not processed"
3008  }},
3009  { L("$INCLUDE "), &(command_entry_t){
3010  .func = command_include,
3011  .usage = "$INCLUDE <relative_path>",
3012  .description = "Execute a test file"
3013  }},
3014  { L("allow-unresolved "), &(command_entry_t){
3015  .func = command_allow_unresolved,
3016  .usage = "allow-unresolved yes|no",
3017  .description = "Allow or disallow unresolved attributes in xlats and references"
3018  }},
3019  { L("calc "), &(command_entry_t){
3020  .func = command_calc,
3021  .usage = "calc <type1> <value1> <operator> <type2> <value2> -> <output-type>",
3022  .description = "Perform calculations on value boxes",
3023  }},
3024  { L("calc_nary "), &(command_entry_t){
3025  .func = command_calc_nary,
3026  .usage = "calc_nary op <type1> <value1> <type2> <value2> ... -> <output-type>",
3027  .description = "Perform calculations on value boxes",
3028  }},
3029  { L("cd "), &(command_entry_t){
3030  .func = command_cd,
3031  .usage = "cd <path>",
3032  .description = "Change the directory for loading dictionaries and $INCLUDEs, writing the full path into the data buffer on success"
3033  }},
3034  { L("clear"), &(command_entry_t){
3035  .func = command_clear,
3036  .usage = "clear",
3037  .description = "Explicitly zero out the contents of the data buffer"
3038  }},
3039  { L("command add "), &(command_entry_t){
3040  .func = command_radmin_add,
3041  .usage = "command add <string>",
3042  .description = "Add a command to a radmin command tree"
3043  }},
3044  { L("command tab "), &(command_entry_t){
3045  .func = command_radmin_tab,
3046  .usage = "command tab <string>",
3047  .description = "Test a tab completion against a radmin command tree"
3048  }},
3049  { L("condition "), &(command_entry_t){
3051  .usage = "condition <string>",
3052  .description = "Parse and reprint a condition, writing the normalised condition to the data buffer on success"
3053  }},
3054  { L("count"), &(command_entry_t){
3055  .func = command_count,
3056  .usage = "count",
3057  .description = "Write the number of executed tests to the data buffer. A test is any command that should return 'ok'"
3058  }},
3059  { L("decode-dns-label "), &(command_entry_t){
3060  .func = command_decode_dns_label,
3061  .usage = "decode-dns-label (-|<hex_string>)",
3062  .description = "Decode one or more DNS labels, writing the decoded strings to the data buffer.",
3063  }},
3064  { L("decode-pair"), &(command_entry_t){
3065  .func = command_decode_pair,
3066  .usage = "decode-pair[.<testpoint_symbol>] (-|<hex_string>)",
3067  .description = "Produce an attribute value pair from a binary value using a specified protocol decoder. Protocol must be loaded with \"load <protocol>\" first",
3068  }},
3069  { L("decode-proto"), &(command_entry_t){
3070  .func = command_decode_proto,
3071  .usage = "decode-proto[.<testpoint_symbol>] (-|<hex string>)",
3072  .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",
3073  }},
3074  { L("dictionary "), &(command_entry_t){
3076  .usage = "dictionary <string>",
3077  .description = "Parse dictionary attribute definition, writing \"ok\" to the data buffer if successful",
3078  }},
3079  { L("dictionary-dump"), &(command_entry_t){
3080  .func = command_dictionary_dump,
3081  .usage = "dictionary-dump",
3082  .description = "Print the contents of the currently active dictionary to stdout",
3083  }},
3084  { L("encode-dns-label "), &(command_entry_t){
3085  .func = command_encode_dns_label,
3086  .usage = "encode-dns-label (-|string[,string])",
3087  .description = "Encode one or more DNS labels, writing a hex string to the data buffer.",
3088  }},
3089  { L("encode-pair"), &(command_entry_t){
3090  .func = command_encode_pair,
3091  .usage = "encode-pair[.<testpoint_symbol>] [truncate] (-|<attribute> = <value>[,<attribute = <value>])",
3092  .description = "Encode one or more attribute value pairs, writing a hex string to the data buffer. Protocol must be loaded with \"load <protocol>\" first",
3093  }},
3094  { L("encode-proto"), &(command_entry_t){
3095  .func = command_encode_proto,
3096  .usage = "encode-proto[.<testpoint_symbol>] (-|<attribute> = <value>[,<attribute = <value>])",
3097  .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"
3098  }},
3099  { L("eof"), &(command_entry_t){
3100  .func = command_eof,
3101  .usage = "eof",
3102  .description = "Mark the end of a 'virtual' file. Used to prevent 'need-feature' skipping all the content of a command stream or file",
3103  }},
3104  { L("exit"), &(command_entry_t){
3105  .func = command_exit,
3106  .usage = "exit[ <num>]",
3107  .description = "Exit with the specified error number. If no <num> is provided, process will exit with 0"
3108  }},
3109  { L("fuzzer-out"), &(command_entry_t){
3110  .func = command_fuzzer_out,
3111  .usage = "fuzzer-out <dir>",
3112  .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",
3113  }},
3114  { L("load-dictionary "),&(command_entry_t){
3115  .func = command_load_dictionary,
3116  .usage = "load-dictionary <name> [<dir>]",
3117  .description = "Load an additional dictionary from the same directory as the input file. "
3118  "Optionally you can specify a full path via <dir>. ",
3119  }},
3120  { L("match"), &(command_entry_t){
3121  .func = command_match,
3122  .usage = "match <string>",
3123  .description = "Compare the contents of the data buffer with an expected value"
3124  }},
3125  { L("match-regex "), &(command_entry_t){
3126  .func = command_match_regex,
3127  .usage = "match-regex <regex>",
3128  .description = "Compare the contents of the data buffer with a regular expression"
3129  }},
3130  { L("max-buffer-size"), &(command_entry_t){
3131  .func = command_max_buffer_size,
3132  .usage = "max-buffer-size[ <integer>]",
3133  .description = "Limit the maximum temporary buffer space available for any command which uses it"
3134  }},
3135  { L("migrate "), &(command_entry_t){
3136  .func = command_migrate,
3137  .usage = "migrate <flag>=<value>",
3138  .description = "Set migration flag"
3139  }},
3140  { L("need-feature "), &(command_entry_t){
3141  .func = command_need_feature,
3142  .usage = "need-feature <feature>",
3143  .description = "Skip the contents of the current file, or up to the next \"eof\" command if a particular feature is not available"
3144  }},
3145  { L("no "), &(command_entry_t){
3146  .func = command_no,
3147  .usage = "no ...",
3148  .description = "Negate the result of a command returning 'ok'"
3149  }},
3150  { L("pair "), &(command_entry_t){
3151  .func = command_pair,
3152  .usage = "pair ... data ...",
3153  .description = "Parse a list of pairs",
3154  }},
3155  { L("proto "), &(command_entry_t){
3156  .func = command_proto,
3157  .usage = "proto <protocol>",
3158  .description = "Switch the active protocol to the one specified, unloading the previous protocol",
3159  }},
3160  { L("proto-dictionary "),&(command_entry_t){
3161  .func = command_proto_dictionary,
3162  .usage = "proto-dictionary <proto_name> [<proto_dir>]",
3163  .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.",
3164  }},
3165 
3166 
3167  { L("proto-dictionary-root "), &(command_entry_t){
3169  .usage = "proto-dictionary-root[ <root_attribute>]",
3170  .description = "Set the root attribute for the current protocol dictionary. "
3171  "If no attribute name is provided, the root will be reset to the root of the current dictionary",
3172  }},
3173  { L("raw "), &(command_entry_t){
3174  .func = command_encode_raw,
3175  .usage = "raw <string>",
3176  .description = "Create nested attributes from OID strings and values"
3177  }},
3178  { L("read_file "), &(command_entry_t){
3179  .func = command_read_file,
3180  .usage = "read_file <filename>",
3181  .description = "Read a list of pairs from a file",
3182  }},
3183  { L("returned"), &(command_entry_t){
3184  .func = command_returned,
3185  .usage = "returned",
3186  .description = "Print the returned value to the data buffer"
3187  }},
3188 
3189  { L("tmpl-rules "), &(command_entry_t){
3190  .func = command_tmpl_rules,
3191  .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]",
3192  .description = "Alter the tmpl parsing rules for subsequent tmpl parsing commands in the same command context"
3193  }},
3194  { L("touch "), &(command_entry_t){
3195  .func = command_touch,
3196  .usage = "touch <file>",
3197  .description = "Touch a file, updating its created timestamp. Useful for marking the completion of a series of tests"
3198  }},
3199  { L("value "), &(command_entry_t){
3201  .usage = "value <type> <string>",
3202  .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"
3203  }},
3204  { L("write "), &(command_entry_t){
3205  .func = command_write,
3206  .usage = "write <file>",
3207  .description = "Write the contents of the data buffer (as a raw binary string) to the specified file"
3208  }},
3209  { L("xlat "), &(command_entry_t){
3210  .func = command_xlat_normalise,
3211  .usage = "xlat <string>",
3212  .description = "Parse then print an xlat expansion, writing the normalised xlat expansion to the data buffer"
3213  }},
3214 
3215  { L("xlat_argv "), &(command_entry_t){
3216  .func = command_xlat_argv,
3217  .usage = "xlat_argv <string>",
3218  .description = "Parse then print an xlat expansion argv, writing the normalised xlat expansion arguments to the data buffer"
3219  }},
3220 
3221  { L("xlat_expr "), &(command_entry_t){
3222  .func = command_xlat_expr,
3223  .usage = "xlat_expr <string>",
3224  .description = "Parse then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3225  }},
3226 
3227  { L("xlat_purify "), &(command_entry_t){
3228  .func = command_xlat_purify,
3229  .usage = "xlat_purify <string>",
3230  .description = "Parse, purify, then print an xlat expression, writing the normalised xlat expansion to the data buffer"
3231  }},
3232 
3233 };
3235 
3236 size_t process_line(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used,
3237  char *in, UNUSED size_t inlen)
3238 {
3239 
3240  command_entry_t *command;
3241  size_t match_len;
3242  char *p;
3243 
3244  p = in;
3245  fr_skip_whitespace(p);
3246 
3247  /*
3248  * Skip empty lines and comments.
3249  */
3250  if (!*p || (*p == '#')) {
3251  /*
3252  * Dump the input to the output.
3253  */
3254  if (write_fp) {
3255  fputs(in, write_fp);
3256  fputs("\n", write_fp);
3257  }
3258 
3259  RETURN_NOOP(data_used);
3260  }
3261 
3262  DEBUG2("%s[%d]: %s", cc->filename, cc->lineno, p);
3263 
3264  /*
3265  * Look up the command by longest prefix
3266  */
3267  command = fr_table_value_by_longest_prefix(&match_len, commands, p, -1, NULL);
3268  if (!command) {
3269  fr_strerror_printf("Unknown command: %s", p);
3271  }
3272 
3273  p += match_len; /* Jump to after the command */
3274  fr_skip_whitespace(p); /* Skip any whitespace */
3275 
3276  /*
3277  * Feed the data buffer in as the command
3278  */
3279  if ((p[0] == '-') && ((p[1] == ' ') || (p[1] == '\0'))) {
3280  data_used = command->func(result, cc, data, data_used, data, data_used);
3281  }
3282  else {
3283  data_used = command->func(result, cc, data, data_used, p, strlen(p));
3284  }
3285 
3286  /*
3287  * Dump the contents of the error stack
3288  * to the data buffer.
3289  *
3290  * This is then what's checked in
3291  * subsequent match commands.
3292  */
3293  if (result->error_to_data) data_used = strerror_concat(data, COMMAND_OUTPUT_MAX);
3294 
3295  fr_assert((size_t)data_used < COMMAND_OUTPUT_MAX);
3296  data[data_used] = '\0'; /* Ensure the data buffer is \0 terminated */
3297 
3298  if (data_used) {
3299  DEBUG2("%s[%d]: --> %s (%zu bytes in buffer)", cc->filename, cc->lineno,
3300  fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"), data_used);
3301  } else {
3302  DEBUG2("%s[%d]: --> %s", cc->filename, cc->lineno,
3303  fr_table_str_by_value(command_rcode_table, result->rcode, "<INVALID>"));
3304  }
3305 
3306  /*
3307  * Dump the input to the output.
3308  */
3309  if (write_fp) {
3310  fputs(in, write_fp);
3311  fputs("\n", write_fp);
3312  };
3313 
3314  talloc_free_children(cc->tmp_ctx);
3315 
3316  return data_used;
3317 }
3318 
3320 {
3321  if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3322  fr_perror("unit_test_attribute");
3323  return -1;
3324  }
3325  if (fr_dict_global_ctx_free(cc->test_gctx) < 0) {
3326  fr_perror("unit_test_attribute");
3327  return -1;
3328  }
3329  if (cc->fuzzer_dir >= 0) {
3330  close(cc->fuzzer_dir);
3331  cc->fuzzer_dir = -1;
3332  }
3333  return 0;
3334 }
3335 
3336 static command_file_ctx_t *command_ctx_alloc(TALLOC_CTX *ctx,
3337  command_config_t const *config, char const *path, char const *filename)
3338 {
3339  command_file_ctx_t *cc;
3340 
3341  cc = talloc_zero(ctx, command_file_ctx_t);
3342  talloc_set_destructor(cc, _command_ctx_free);
3343 
3344  cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3345  cc->path = talloc_strdup(cc, path);
3346  cc->filename = filename;
3347  cc->config = config;
3348 
3349  /*
3350  * Allocate a special buffer with poisoned regions
3351  * at either end.
3352  */
3354  talloc_free(cc);
3355  return NULL;
3356  }
3359 
3360  /*
3361  * Initialise a special temporary dictionary context
3362  *
3363  * Any protocol dictionaries loaded by "test-dictionary"
3364  * go in this context, and don't affect the main
3365  * dictionary context.
3366  */
3367  cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3368  if (!cc->test_gctx) {
3369  fr_perror("Failed allocating test dict_gctx");
3370  return NULL;
3371  }
3372 
3375  fr_perror("Failed loading test dict_gctx internal dictionary");
3376  return NULL;
3377  }
3378 
3379  fr_dict_global_ctx_dir_set(cc->path); /* Load new dictionaries relative to the test file */
3381 
3382  cc->fuzzer_dir = -1;
3383 
3385  cc->tmpl_rules.attr.namespace = fr_dict_root(cc->config->dict);
3386 
3387  return cc;
3388 }
3389 
3390 static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
3391 {
3392  talloc_free(cc->tmp_ctx);
3393  cc->tmp_ctx = talloc_named_const(ctx, 0, "tmp_ctx");
3394  cc->test_count = 0;
3395 
3396  if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
3397  fr_perror("unit_test_attribute");
3398  }
3399 
3400  if (fr_dict_global_ctx_free(cc->test_gctx) < 0) fr_perror("unit_test_attribute");
3401 
3402  cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
3404  fr_perror("Failed loading test dict_gctx internal dictionary");
3405  }
3406 
3407  if (cc->fuzzer_dir >= 0) {
3408  close(cc->fuzzer_dir);
3409  cc->fuzzer_dir = -1;
3410  }
3411 }
3412 
3413 static int process_file(bool *exit_now, TALLOC_CTX *ctx, command_config_t const *config,
3414  const char *root_dir, char const *filename, fr_dlist_head_t *lines)
3415 {
3416  int ret = 0;
3417  FILE *fp; /* File we're reading from */
3418  char buffer[8192]; /* Command buffer */
3419  char data[COMMAND_OUTPUT_MAX + 1]; /* Data written by previous command */
3420  ssize_t data_used = 0; /* How much data the last command wrote */
3421  static char path[PATH_MAX] = "";
3422  command_line_range_t *lr = NULL;
3423 
3424  command_file_ctx_t *cc;
3425 
3426  cc = command_ctx_alloc(ctx, config, root_dir, filename);
3427 
3428  /*
3429  * Open the file, or stdin
3430  */
3431  if (strcmp(filename, "-") == 0) {
3432  fp = stdin;
3433  filename = "<stdin>";
3434  } else {
3435  if (root_dir && *root_dir) {
3436  snprintf(path, sizeof(path), "%s/%s", root_dir, filename);
3437  } else {
3438  strlcpy(path, filename, sizeof(path));
3439  }
3440 
3441  fp = fopen(path, "r");
3442  if (!fp) {
3443  ERROR("Error opening test file \"%s\": %s", path, fr_syserror(errno));
3444  ret = -1;
3445  goto finish;
3446  }
3447 
3448  filename = path;
3449  }
3450 
3451  if (lines && !fr_dlist_empty(lines)) lr = fr_dlist_head(lines);
3452 
3453  /*
3454  * Loop over lines in the file or stdin
3455  */
3456  while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3457  command_result_t result = { .rcode = RESULT_OK }; /* Reset to OK */
3458  char *p = strchr(buffer, '\n');
3459 
3461  cc->lineno++; /* The first line of the file becomes line 1 */
3462 
3463  if (lr) {
3464  if (cc->lineno > lr->end) {
3465  lr = fr_dlist_next(lines, lr);
3466  if (!lr) goto finish;
3467  }
3468 
3469  if (cc->lineno < lr->start) continue;
3470  }
3471 
3472  if (!p) {
3473  if (!feof(fp)) {
3474  ERROR("Line %d too long in %s/%s", cc->lineno, cc->path, cc->filename);
3475  ret = -1;
3476  goto finish;
3477  }
3478  } else {
3479  *p = '\0';
3480  }
3481 
3482  data_used = process_line(&result, cc, data, data_used, buffer, strlen(buffer));
3483  switch (result.rcode) {
3484  /*
3485  * Command completed successfully
3486  */
3487  case RESULT_OK:
3488  cc->test_count++;
3489  continue;
3490 
3491  /*
3492  * Did nothing (not a test)
3493  */
3494  case RESULT_NOOP:
3495  continue;
3496 
3497  /*
3498  * If this is a file, then break out of the loop
3499  * and cleanup, otherwise we need to find the
3500  * EOF marker in the input stream.
3501  */
3502  case RESULT_SKIP_FILE:
3503  if (fp != stdin) goto finish;
3504 
3505  /*
3506  * Skip over the input stream until we
3507  * find an eof command, or the stream
3508  * is closed.
3509  */
3510  while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3511  command_entry_t *command;
3512  size_t match_len;
3513 
3514  command = fr_table_value_by_longest_prefix(&match_len, commands, buffer, -1, NULL);
3515  if (!command) {
3516  ERROR("%s[%d]: Unknown command: %s", cc->path, cc->lineno, p);
3517  ret = -1;
3518  goto finish;
3519  }
3520 
3521  if (command->func == command_eof) {
3522  command_ctx_reset(cc, ctx);
3523  break;
3524  }
3525  }
3526  break;
3527 
3528  /*
3529  * Fatal error parsing a command
3530  */
3531  case RESULT_PARSE_ERROR:
3532  case RESULT_COMMAND_ERROR:
3533  fr_perror("%s[%d]", filename, cc->lineno);
3534  ret = -1;
3535  goto finish;
3536 
3537  /*
3538  * Result didn't match what we expected
3539  */
3540  case RESULT_MISMATCH:
3541  {
3542  ret = EXIT_FAILURE;
3543  goto finish;
3544  }
3545 
3546  case RESULT_EXIT:
3547  ret = result.ret;
3548  *exit_now = true;
3549  goto finish;
3550 
3551  default:
3552  /*
3553  * If this happens, fix the damn command.
3554  */
3555  fr_assert_msg(false, "Command exited with invalid return code (%i)", result.rcode);
3556  ret = -1;
3557  goto finish;
3558  }
3559  }
3560 
3561 finish:
3562  if (fp && (fp != stdin)) fclose(fp);
3563 
3564  /*
3565  * Free any residual resources we loaded.
3566  */
3567  if (cc && (fr_dict_const_free(&cc->tmpl_rules.attr.dict_def, __FILE__) < 0)) {
3568  fr_perror("unit_test_attribute");
3569  ret = -1;
3570  }
3571 
3572  fr_dict_global_ctx_set(config->dict_gctx); /* Switch back to the main dict ctx */
3574  talloc_free(cc);
3575 
3576  return ret;
3577 }
3578 
3579 static void usage(char const *name)
3580 {
3581  INFO("usage: %s [options] (-|<filename>[:<lines>] [ <filename>[:<lines>]])", name);
3582  INFO("options:");
3583  INFO(" -d <raddb> Set user dictionary path (defaults to " RADDBDIR ").");
3584  INFO(" -D <dictdir> Set main dictionary path (defaults to " DICTDIR ").");
3585  INFO(" -x Debugging mode.");
3586  INFO(" -f Print features.");
3587  INFO(" -c Print commands.");
3588  INFO(" -h Print help text.");
3589  INFO(" -M Show talloc memory report.");
3590  INFO(" -p Allow xlat_purify");
3591  INFO(" -r <receipt_file> Create the <receipt_file> as a 'success' exit.");
3592  INFO(" -w <output_file> Write 'corrected' output to <output_file>.");
3593  INFO("Where <filename> is a file containing one or more commands and '-' indicates commands should be read from stdin.");
3594  INFO("Ranges of <lines> may be specified in the format <start>[-[<end>]][,]");
3595 }
3596 
3597 static void features_print(CONF_SECTION *features)
3598 {
3599  CONF_PAIR *cp;
3600 
3601  INFO("features:");
3602  for (cp = cf_pair_find(features, CF_IDENT_ANY);
3603  cp;
3604  cp = cf_pair_find_next(features, cp, CF_IDENT_ANY)) {
3605  INFO(" %s %s", cf_pair_attr(cp), cf_pair_value(cp));
3606  }
3607 }
3608 
3609 static void commands_print(void)
3610 {
3611  size_t i;
3612 
3613  INFO("commands:");
3614  for (i = 0; i < commands_len; i++) {
3615  INFO(" %s:", ((command_entry_t const *)commands[i].value)->usage);
3616  INFO(" %s.", ((command_entry_t const *)commands[i].value)->description);
3617  INFO("%s", "");
3618  }
3619 }
3620 
3621 static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *in)
3622 {
3623  static bool tokens[UINT8_MAX + 1] = { [','] = true , ['-'] = true };
3624  uint32_t max = 0;
3627 
3628  while (fr_sbuff_extend(in)) {
3629  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3630 
3631  MEM(lr = talloc_zero(ctx, command_line_range_t));
3633 
3634  fr_sbuff_out(&err, &lr->start, in);
3635  if (err != FR_SBUFF_PARSE_OK) {
3636  ERROR("Invalid line start number");
3637  error:
3639  return -1;
3640  }
3641  if (max > lr->start) {
3642  ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->start);
3643  goto error;
3644  } else {
3645  max = lr->start;
3646  }
3647  lr->end = lr->start; /* Default to a single line */
3648  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3649 
3650  again:
3651  if (!fr_sbuff_extend(in)) break;
3652  if (!fr_sbuff_is_in_charset(in, tokens)) {
3653  ERROR("Unexpected text \"%pV\"",
3655  goto error;
3656  }
3657 
3658  fr_sbuff_switch(in, '\0') {
3659  /*
3660  * More ranges...
3661  */
3662  case ',':
3663  fr_sbuff_next(in);
3664  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3665  continue;
3666 
3667  /*
3668  * <start>-<end>
3669  */
3670  case '-':
3671  {
3672  fr_sbuff_next(in);
3673  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3674 
3675  /*
3676  * A bare '-' with no number means
3677  * run all remaining lines.
3678  */
3679  if (fr_sbuff_extend(in) == 0) {
3680  lr->end = UINT32_MAX;
3681  return 0;
3682  }
3683 
3684  fr_sbuff_out(&err, &lr->end, in);
3685  if (err != FR_SBUFF_PARSE_OK) {
3686  ERROR("Invalid line end number");
3687  goto error;
3688  }
3689  if (lr->end < lr->start) {
3690  ERROR("Line end must be >= line start (%u < %u)", lr->end, lr->start);
3691  goto error;
3692  }
3693  if (max > lr->end) {
3694  ERROR("Out of order line numbers (%u > %u) not allowed", max, lr->end);
3695  goto error;
3696  } else {
3697  max = lr->end;
3698  }
3699  fr_sbuff_adv_past_whitespace(in, SIZE_MAX, NULL);
3700  }
3701  goto again;
3702  }
3703  }
3704 
3705  return 0;
3706 }
3707 
3708 /**
3709  *
3710  * @hidecallgraph
3711  */
3712 int main(int argc, char *argv[])
3713 {
3714  int c;
3715  char const *receipt_file = NULL;
3716  CONF_SECTION *cs;
3717  int ret = EXIT_SUCCESS;
3718  TALLOC_CTX *autofree;
3719  TALLOC_CTX *thread_ctx;
3720  bool exit_now = false;
3721 
3723  .raddb_dir = RADDBDIR,
3724  .dict_dir = DICTDIR
3725  };
3726 
3727  char const *name;
3728  bool do_features = false;
3729  bool do_commands = false;
3730  bool do_usage = false;
3731  bool allow_purify = false;
3732 
3733  /*
3734  * Must be called first, so the handler is called last
3735  */
3737 
3739  thread_ctx = talloc_new(autofree);
3740 
3741 #ifndef NDEBUG
3742  if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
3743  fr_perror("unit_test_attribute");
3744  goto cleanup;
3745  }
3746 #else
3748 #endif
3749 
3750  /*
3751  * Sync wallclock and cpu time so that we can find
3752  * uses of fr_time_[to|from]_* where
3753  * fr_unix_time_[to|from]_* should be used.
3754  *
3755  * If the wallclock/cpu offset is 0, then both sets
3756  * of macros produce the same result.
3757  */
3758  fr_time_start();
3759 
3760  /*
3761  * Allocate a root config section so we can write
3762  * out features and versions.
3763  */
3764  MEM(cs = cf_section_alloc(autofree, NULL, "unit_test_attribute", NULL));
3765  MEM(config.features = cf_section_alloc(cs, cs, "feature", NULL));
3766  dependency_features_init(config.features); /* Add build time features to the config section */
3767 
3768  name = argv[0];
3769 
3771  default_log.fd = STDOUT_FILENO;
3772  default_log.print_level = false;
3773 
3774  while ((c = getopt(argc, argv, "cd:D:F:fxMhpr:w:")) != -1) switch (c) {
3775  case 'c':
3776  do_commands = true;
3777  break;
3778 
3779  case 'd':
3780  config.raddb_dir = optarg;
3781  break;
3782 
3783  case 'D':
3784  config.dict_dir = optarg;
3785  break;
3786 
3787  case 'F':
3788  config.fuzzer_dir = optarg;
3789  break;
3790 
3791  case 'f':
3792  do_features = true;
3793  break;
3794 
3795  case 'x':
3796  fr_debug_lvl++;
3797  if (fr_debug_lvl > 2) default_log.print_level = true;
3798  break;
3799 
3800  case 'M':
3801  talloc_enable_leak_report();
3802  break;
3803 
3804  case 'r':
3805  receipt_file = optarg;
3806  break;
3807 
3808  case 'p':
3809  allow_purify = true;
3810  break;
3811 
3812  case 'w':
3813  write_filename = optarg;
3814  break;
3815 
3816  case 'h':
3817  default:
3818  do_usage = true; /* Just set a flag, so we can process extra -x args */
3819  break;
3820  }
3821  argc -= (optind - 1);
3822  argv += (optind - 1);
3823 
3824  if (do_usage) usage(name);
3825  if (do_features) features_print(config.features);
3826  if (do_commands) commands_print();
3827  if (do_usage || do_features || do_commands) {
3828  ret = EXIT_SUCCESS;
3829  goto cleanup;
3830  }
3831 
3832  if (receipt_file && (fr_unlink(receipt_file) < 0)) {
3833  fr_perror("unit_test_attribute");
3835  }
3836 
3837  /*
3838  * Mismatch between the binary and the libraries it depends on
3839  */
3841  fr_perror("unit_test_attribute");
3843  }
3844 
3845 #ifdef WITH_TLS
3846  /*
3847  * OpenSSL can only be initialised once during the lifetime
3848  * of a process. Initialise it here so that we don't attempt
3849  * to unload and load it multiple times.
3850  */
3851  if (fr_openssl_init() < 0) {
3852  fr_perror("unit_test_attribute");
3854  }
3855 #endif
3856 
3857  modules_init(NULL);
3858 
3859  dl_loader = dl_loader_init(autofree, NULL, false, false);
3860  if (!dl_loader) {
3861  fr_perror("unit_test_attribute");
3863  }
3864 
3865  config.dict_gctx = fr_dict_global_ctx_init(NULL, true, config.dict_dir);
3866  if (!config.dict_gctx) {
3867  fr_perror("unit_test_attribute");
3869  }
3870 
3872  fr_perror("unit_test_attribute");
3874  }
3875 
3876  /*
3877  * Always needed so we can load the list attributes
3878  * otherwise the tmpl_tokenize code fails.
3879  */
3880  if (request_global_init() < 0) {
3881  fr_perror("unit_test_attribute");
3883  }
3884 
3885  /*
3886  * Initialise the interpreter, registering operations.
3887  * Needed because some keywords also register xlats.
3888  */
3889  if (unlang_global_init() < 0) {
3890  fr_perror("unit_test_attribute");
3892  }
3893 
3894  /*
3895  * Create a dummy event list
3896  */
3897  if (allow_purify) {
3898  el = fr_event_list_alloc(autofree, NULL, NULL);
3899  fr_assert(el != NULL);
3900 
3901  /*
3902  * Simulate thread specific instantiation
3903  */
3905  if (xlat_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
3906  }
3907 
3908  unlang_thread_instantiate(thread_ctx);
3909 
3910  if (!xlat_func_register(NULL, "test", xlat_test, FR_TYPE_NULL)) {
3911  ERROR("Failed registering xlat");
3913  }
3914 
3915  /*
3916  * Disable hostname lookups, so we don't produce spurious DNS
3917  * queries, and there's no chance of spurious failures if
3918  * it takes a long time to get a response.
3919  */
3921 
3922  /*
3923  * Read tests from stdin
3924  */
3925  if (argc < 2) {
3926  if (write_filename) {
3927  ERROR("Can't use '-w' with stdin");
3929  }
3930 
3931  ret = process_file(&exit_now, autofree, &config, name, "-", NULL);
3932 
3933  /*
3934  * ...or process each file in turn.
3935  */
3936  } else {
3937  int i;
3938 
3939  if (write_filename) {
3940  if (argc != 2) { /* program name and file to write */
3941  ERROR("Can't use '-w' with multiple filenames");
3943  }
3944 
3945  write_fp = fopen(write_filename, "w");
3946  if (!write_fp) {
3947  ERROR("Failed opening %s: %s", write_filename, strerror(errno));
3949  }
3950  }
3951 
3952  /*
3953  * Loop over all input files.
3954  */
3955  for (i = 1; i < argc; i++) {
3956  char *dir = NULL, *file;
3957  fr_sbuff_t in = FR_SBUFF_IN(argv[i], strlen(argv[i]));
3958  fr_sbuff_term_t dir_sep = FR_SBUFF_TERMS(
3959  L("/"),
3960  L(":")
3961  );
3962  fr_sbuff_marker_t file_start, file_end, dir_end;
3963  fr_dlist_head_t lines;
3964 
3965  fr_sbuff_marker(&file_start, &in);
3966  fr_sbuff_marker(&file_end, &in);
3967  fr_sbuff_marker(&dir_end, &in);
3968  fr_sbuff_set(&file_end, fr_sbuff_end(&in));
3969 
3970  fr_dlist_init(&lines, command_line_range_t, entry);
3971 
3972  while (fr_sbuff_extend(&in)) {
3973  fr_sbuff_adv_until(&in, SIZE_MAX, &dir_sep, '\0');
3974 
3975  fr_sbuff_switch(&in, '\0') {
3976  case '/':
3977  fr_sbuff_set(&dir_end, &in);
3978  fr_sbuff_advance(&in, 1);
3979  fr_sbuff_set(&file_start, &in);
3980  break;
3981 
3982  case ':':
3983  fr_sbuff_set(&file_end, &in);
3984  fr_sbuff_advance(&in, 1);
3985  if (line_ranges_parse(autofree, &lines, &in) < 0) EXIT_WITH_FAILURE;
3986  break;
3987 
3988  default:
3989  fr_sbuff_set(&file_end, &in);
3990  break;
3991  }
3992  }
3993 
3995  fr_sbuff_current(&file_start), fr_sbuff_diff(&file_end, &file_start));
3996  if (fr_sbuff_used(&dir_end)) dir = talloc_bstrndup(autofree,
3997  fr_sbuff_start(&in),
3998  fr_sbuff_used(&dir_end));
3999 
4000  ret = process_file(&exit_now, autofree, &config, dir, file, &lines);
4001  talloc_free(dir);
4002  talloc_free(file);
4003  fr_dlist_talloc_free(&lines);
4004 
4005  if ((ret != 0) || exit_now) break;
4006  }
4007 
4008  if (write_fp) {
4009  fclose(write_fp);
4010  if (rename(write_filename, argv[1]) < 0) {
4011  ERROR("Failed renaming %s: %s", write_filename, strerror(errno));
4013  }
4014  }
4015  }
4016 
4017  /*
4018  * Try really hard to free any allocated
4019  * memory, so we get clean talloc reports.
4020  */
4021 cleanup:
4022  /*
4023  * Ensure all thread local memory is cleaned up
4024  * at the appropriate time. This emulates what's
4025  * done with worker/network threads in the
4026  * scheduler.
4027  */
4029 
4030 #ifdef WITH_TLS
4031  fr_openssl_free();
4032 #endif
4033 
4034  /*
4035  * dl_loader check needed as talloc_free
4036  * returns -1 on failure.
4037  */
4038  if (dl_loader && (talloc_free(dl_loader) < 0)) {
4039  fr_perror("unit_test_attribute - dl_loader - "); /* Print free order issues */
4041  }
4042  if (fr_dict_free(&config.dict, __FILE__) < 0) {
4043  fr_perror("unit_test_attribute");
4045  }
4046 
4047  if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
4048  fr_perror("unit_test_attribute");
4050  }
4051 
4052  /*
4053  * Explicitly free the autofree context
4054  * to make errors less confusing.
4055  */
4056  if (talloc_free(autofree) < 0) {
4057  fr_perror("unit_test_attribute");
4059  }
4060 
4061  /*
4062  * Ensure our atexit handlers run before any other
4063  * atexit handlers registered by third party libraries.
4064  */
4066 
4067  return ret;
4068 }
#define true
Definition: abinary.c:57
static int const char char buffer[256]
Definition: acutest.h:574
int const char * file
Definition: acutest.h:702
strcpy(log_entry->msg, buffer)
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition: atexit.c: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 fr_dict_t * dict
Definition: fuzzer.c:46
#define CMD_MAX_ARGV
Definition: radmin.c:150
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define RCSID(id)
Definition: build.h:481
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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:2239
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:2399
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:1894
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1578
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:1439
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1594
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:1453
#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:1019
void fr_command_debug(FILE *fp, fr_cmd_t *head)
Definition: command.c:1602
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:725
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:1290
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:514
next
Definition: dcursor.h:178
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
Definition: debug.c:1207
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:1242
#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:210
void dependency_features_init(CONF_SECTION *cs)
Initialise core feature flags.
Definition: dependency.c:184
static char const * spaces
Definition: dependency.c:371
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
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:4483
static fr_slen_t err
Definition: dict.h:821
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:3263
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:2324
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)
Definition: dict_tokenize.c:86
void fr_dict_debug(fr_dict_t const *dict)
Definition: dict_print.c:258
void fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx)
Set a new, active, global dictionary context.
Definition: dict_util.c:4453
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:2400
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition: dict_util.c:4024
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:4008
fr_dict_t const * fr_dict_internal(void)
Definition: dict_util.c:4610
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:4469
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:289
@ FR_DICT_ATTR_OK
No error.
Definition: dict.h:290
static fr_slen_t in
Definition: dict.h:821
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:4392
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_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
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 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 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
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
bool fr_hostname_lookups
hostname -> IP lookups?
Definition: inet.c:52
bool fr_reverse_lookups
IP -> hostname lookups?
Definition: inet.c:51
int unlang_global_init(void)
Definition: base.c:133
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:2899
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
int fr_debug_lvl
Definition: log.c:43
fr_log_t default_log
Definition: log.c:291
@ L_DST_STDOUT
Log to stdout.
Definition: log.h:78
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_MAX
Number of defined data types.
Definition: merged_model.c:130
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
@ FR_TYPE_GROUP
A grouping of other attributes.
Definition: merged_model.c:124
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
unsigned long int size_t
Definition: merged_model.c:25
#define UINT8_MAX
Definition: merged_model.c:32
fr_sbuff_parse_error_t
Definition: merged_model.c:45
@ FR_SBUFF_PARSE_OK
No error.
Definition: merged_model.c:46
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
static bool is_whitespace(char const *value)
Check whether the string is all whitespace.
Definition: misc.h:86
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition: misc.h:59
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:283
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.
Definition: pair_legacy.c:150
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.
Definition: pair_legacy.c:648
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:183
void * fr_proto_next_encodable(fr_dlist_head_t *list, 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
static TALLOC_CTX * autofree
Definition: radclient-ng.c:107
static bool done
Definition: radclient.c:80
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define INFO(fmt,...)
Definition: radict.c:54
static bool cleanup
Definition: radsniff.c:60
fr_dict_attr_t const * request_attr_request
Definition: request.c:45
int request_global_init(void)
Definition: request.c:722
static char const * name
fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in)
See if the string contains a truth value.
Definition: sbuff.c:1087
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:1830
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:2066
#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:167
#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.
Definition: merged_model.c:161
tmpl_xlat_rules_t xlat
Rules/data for parsing xlats.
Definition: tmpl.h:345
bool new_functions
new function syntax
Definition: tmpl.h:335
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:344
struct tmpl_rules_s tmpl_rules_t
Definition: tmpl.h:236
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:333
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
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
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:1910
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:307
uint8_t allow_unresolved
Allow attributes that look valid but were not found in the dictionaries.
Definition: tmpl.h:319
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition: tmpl.h:327
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition: tmpl.h:285
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition: tmpl.h:316
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_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:564
#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:104
fr_tp_proto_decode_t func
Decoder for proto layer.
Definition: test_point.h:87
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:112
fr_dcursor_iter_t next_encodable
Iterator to use to select attributes to encode.
Definition: test_point.h:114
fr_tp_proto_encode_t func
Encoder for proto layer.
Definition: test_point.h:95
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:94
fr_pair_decode_t func
Decoder for pairs.
Definition: test_point.h:105
fr_pair_encode_t func
Encoder for pairs.
Definition: test_point.h:113
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:86
Entry point for pair decoders.
Definition: test_point.h:103
Entry point for pair encoders.
Definition: test_point.h:111
Entry point for protocol decoders.
Definition: test_point.h:85
Entry point for protocol encoders.
Definition: test_point.h:93
int fr_time_start(void)
Initialize the local time.
Definition: time.c:150
const bool fr_assignment_op[T_TOKEN_LAST]
Definition: token.c:168
fr_table_num_ordered_t const fr_tokens_table[]
Definition: token.c:33
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_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 fr_dict_t * dictionary_current(command_file_ctx_t *cc)
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.
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_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_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.
bool tmpl_require_enum_prefix
Definition: tmpl_tokenize.c:49
static ssize_t encode_rfc(char *buffer, uint8_t *output, size_t outlen)
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.
static void usage(char const *name)
ssize_t last_ret
Last return value.
#define ASAN_POISON_MEMORY_REGION(_start, _end)
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 int decode_attr(char *buffer, char **endptr)
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 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)
static command_file_ctx_t * command_ctx_alloc(TALLOC_CTX *ctx, command_config_t const *config, char const *path, char const *filename)
#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 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 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 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
static size_t command_pair(command_result_t *result, command_file_ctx_t *cc, char *data, UNUSED size_t data_used, char *in, size_t inlen)
Parse an print an attribute pair or pair list.
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.
#define ASAN_UNPOISON_MEMORY_REGION(_start, _end)
#define RETURN_SKIP_FILE()
command_func_t func
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 parse_typed_value(command_result_t *result, fr_value_box_t *box, char const **out, char const *in, size_t inlen)
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)
command_rcode_t
@ 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_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.
char const * description
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
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:5122
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_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, xlat_t const *xlat, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr)
Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments.
static fr_slen_t head
Definition: xlat.h:406
int xlat_instantiate(void)
Call instantiation functions for all registered, "permanent" xlats.
Definition: xlat_inst.c:513
int xlat_flatten_compiled_argv(TALLOC_CTX *ctx, xlat_exp_head_t ***argv, xlat_exp_head_t *head)
Turn xlat_tokenize_argv() into an argv[] array, and nuke the input list.
Definition: xlat_eval.c:1613
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:444
int xlat_purify(xlat_exp_head_t *head, unlang_interpret_t *intp)
Purify an xlat.
Definition: xlat_purify.c:194
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, fr_value_box_safe_for_t literals_safe_for)
Tokenize an xlat expansion.
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
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:3177
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:3171
return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def)
#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:569
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
#define PAIR_LIST_VERIFY(_x)
Definition: pair.h:194
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:233
static fr_slen_t parent
Definition: pair.h:851
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition: strerror.c:733
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
char const * fr_strerror_peek(void)
Get the last library error.
Definition: strerror.c:627
char const * fr_strerror_pop(void)
Pop the last library error.
Definition: strerror.c:681
#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
#define fr_type_is_null(_x)
Definition: types.h:326
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:350
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, bool tainted)
Convert string value to a fr_value_box_t type.
Definition: value.c:4802
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, bool tainted)
Definition: value.c:5315
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:5352
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition: value.c:676
fr_sbuff_parse_rules_t const value_parse_rules_bareword_unquoted
Default formatting rules.
Definition: value.c:480
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:3572
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:5540
fr_sbuff_parse_rules_t const value_parse_rules_double_quoted
Definition: value.c:547
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition: value.c:3723
fr_sbuff_unescape_rules_t fr_value_unescape_double
Definition: value.c:266
static fr_slen_t data
Definition: value.h:1265
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:286
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:997
int nonnull(2, 5))
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:587
static size_t char ** out
Definition: value.h:997
An xlat calling ctx.
Definition: xlat_ctx.h:49
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:218