2 * radattr.c RADIUS Attribute debugging tool.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2010 Alan DeKok <aland@freeradius.org>
25 #include <freeradius-devel/libradius.h>
27 typedef struct REQUEST REQUEST;
29 #include <freeradius-devel/parser.h>
30 #include <freeradius-devel/xlat.h>
31 #include <freeradius-devel/conf.h>
32 #include <freeradius-devel/radpaths.h>
33 #include <freeradius-devel/dhcp.h>
43 #include <freeradius-devel/log.h>
44 extern log_lvl_t rad_debug_lvl;
48 pid_t rad_waitpid(pid_t pid, int *status);
55 pid_t rad_waitpid(pid_t pid, int *status)
57 return waitpid(pid, status, 0);
60 static ssize_t xlat_test(UNUSED void *instance, UNUSED REQUEST *request,
61 UNUSED char const *fmt, UNUSED char *out, UNUSED size_t outlen)
66 static RADIUS_PACKET my_original = {
69 .code = PW_CODE_ACCESS_REQUEST,
70 .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
74 static RADIUS_PACKET my_packet = {
77 .code = PW_CODE_ACCESS_ACCEPT,
78 .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
82 static char const *my_secret = "testing123";
85 * End of hacks for xlat
87 **********************************************************************/
89 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
91 static char const hextab[] = "0123456789abcdef";
93 static int encode_data_string(char *buffer,
94 uint8_t *output, size_t outlen)
101 while (*p && (outlen > 0)) {
107 *(output++) = *(p++);
135 fprintf(stderr, "String is not terminated\n");
139 static int encode_data_tlv(char *buffer, char **endptr,
140 uint8_t *output, size_t outlen)
146 for (p = buffer; *p != '\0'; p++) {
147 if (*p == '{') depth++;
150 if (depth == 0) break;
155 fprintf(stderr, "No trailing '}' in string starting "
165 while (isspace((int) *p)) p++;
167 length = encode_tlv(p, output, outlen);
168 if (length == 0) return 0;
173 static int encode_hex(char *p, uint8_t *output, size_t outlen)
179 while (isspace((int) *p)) p++;
183 if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) ||
184 !(c2 = memchr(hextab, tolower((int) p[1]), 16))) {
185 fprintf(stderr, "Invalid data starting at "
190 *output = ((c1 - hextab) << 4) + (c2 - hextab);
197 fprintf(stderr, "Too much data\n");
206 static int encode_data(char *p, uint8_t *output, size_t outlen)
210 if (!isspace((int) *p)) {
211 fprintf(stderr, "Invalid character following attribute "
216 while (isspace((int) *p)) p++;
225 while (isspace((int) *p)) p++;
228 fprintf(stderr, "No data\n");
235 sublen = encode_data_tlv(p, &q, output, outlen);
236 if (sublen == 0) return 0;
248 length = encode_data_string(p, output, outlen);
252 length = encode_hex(p, output, outlen);
255 fprintf(stderr, "Empty string\n");
262 static int decode_attr(char *buffer, char **endptr)
266 attr = strtol(buffer, endptr, 10);
267 if (*endptr == buffer) {
268 fprintf(stderr, "No valid number found in string "
269 "starting with \"%s\"\n", buffer);
274 fprintf(stderr, "Nothing follows attribute number\n");
278 if ((attr <= 0) || (attr > 256)) {
279 fprintf(stderr, "Attribute number is out of valid "
287 static int decode_vendor(char *buffer, char **endptr)
291 if (*buffer != '.') {
292 fprintf(stderr, "Invalid separator before vendor id\n");
296 vendor = strtol(buffer + 1, endptr, 10);
297 if (*endptr == (buffer + 1)) {
298 fprintf(stderr, "No valid vendor number found\n");
303 fprintf(stderr, "Nothing follows vendor number\n");
307 if ((vendor <= 0) || (vendor > (1 << 24))) {
308 fprintf(stderr, "Vendor number is out of valid range\n");
312 if (**endptr != '.') {
313 fprintf(stderr, "Invalid data following vendor number\n");
321 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen)
327 attr = decode_attr(buffer, &p);
328 if (attr == 0) return 0;
335 length = encode_tlv(p, output + 2, outlen - 2);
338 length = encode_data(p, output + 2, outlen - 2);
341 if (length == 0) return 0;
342 if (length > (255 - 2)) {
343 fprintf(stderr, "TLV data is too long\n");
352 static int encode_vsa(char *buffer, uint8_t *output, size_t outlen)
358 vendor = decode_vendor(buffer, &p);
359 if (vendor == 0) return 0;
362 output[1] = (vendor >> 16) & 0xff;
363 output[2] = (vendor >> 8) & 0xff;
364 output[3] = vendor & 0xff;
366 length = encode_tlv(p, output + 4, outlen - 4);
367 if (length == 0) return 0;
368 if (length > (255 - 6)) {
369 fprintf(stderr, "VSA data is too long\n");
377 static int encode_evs(char *buffer, uint8_t *output, size_t outlen)
384 vendor = decode_vendor(buffer, &p);
385 if (vendor == 0) return 0;
387 attr = decode_attr(p, &p);
388 if (attr == 0) return 0;
391 output[1] = (vendor >> 16) & 0xff;
392 output[2] = (vendor >> 8) & 0xff;
393 output[3] = vendor & 0xff;
396 length = encode_data(p, output + 5, outlen - 5);
397 if (length == 0) return 0;
402 static int encode_extended(char *buffer,
403 uint8_t *output, size_t outlen)
409 attr = decode_attr(buffer, &p);
410 if (attr == 0) return 0;
415 length = encode_evs(p, output + 1, outlen - 1);
417 length = encode_data(p, output + 1, outlen - 1);
419 if (length == 0) return 0;
420 if (length > (255 - 3)) {
421 fprintf(stderr, "Extended Attr data is too long\n");
428 static int encode_long_extended(char *buffer,
429 uint8_t *output, size_t outlen)
435 attr = decode_attr(buffer, &p);
436 if (attr == 0) return 0;
438 /* output[0] is the extended attribute */
444 length = encode_evs(p, output + 4, outlen - 4);
445 if (length == 0) return 0;
450 length = encode_data(p, output + 4, outlen - 4);
452 if (length == 0) return 0;
456 int sublen = 255 - output[1];
458 if (length <= sublen) {
466 memmove(output + 255 + 4, output + 255, length);
467 memcpy(output + 255, output, 4);
480 static int encode_rfc(char *buffer, uint8_t *output, size_t outlen)
486 attr = decode_attr(buffer, &p);
487 if (attr == 0) return 0;
494 sublen = encode_vsa(p, output + 2, outlen - 2);
496 } else if ((attr < 241) || (attr > 246)) {
497 sublen = encode_data(p, output + 2, outlen - 2);
501 fprintf(stderr, "Invalid data following "
502 "attribute number\n");
507 sublen = encode_extended(p + 1,
508 output + 2, outlen - 2);
512 * Not like the others!
514 return encode_long_extended(p + 1, output, outlen);
517 if (sublen == 0) return 0;
518 if (sublen > (255 -2)) {
519 fprintf(stderr, "RFC Data is too long\n");
524 return length + sublen;
527 static void parse_condition(char const *input, char *output, size_t outlen)
530 char const *error = NULL;
533 slen = fr_condition_tokenize(NULL, NULL, input, &cond, &error, FR_COND_ONE_PASS);
535 snprintf(output, outlen, "ERROR offset %d %s", (int) -slen, error);
540 if (*input != '\0') {
542 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
546 fr_cond_sprint(output, outlen, cond);
551 static void parse_xlat(char const *input, char *output, size_t outlen)
554 char const *error = NULL;
555 char *fmt = talloc_typed_strdup(NULL, input);
558 slen = xlat_tokenize(fmt, fmt, &head, &error);
560 snprintf(output, outlen, "ERROR offset %d '%s'", (int) -slen, error);
564 if (input[slen] != '\0') {
565 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
569 xlat_sprint(output, outlen, head);
573 static void process_file(const char *root_dir, char const *filename)
577 ssize_t len, data_len;
579 char input[8192], buffer[8192];
581 char directory[8192];
582 uint8_t *attr, data[2048];
584 if (strcmp(filename, "-") == 0) {
589 if (root_dir && *root_dir) {
590 snprintf(directory, sizeof(directory), "%s/%s", root_dir, filename);
592 strlcpy(directory, filename, sizeof(directory));
595 fp = fopen(directory, "r");
597 fprintf(stderr, "Error opening %s: %s\n",
598 directory, fr_syserror(errno));
602 filename = directory;
609 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
610 char *p = strchr(buffer, '\n');
611 VALUE_PAIR *vp, *head;
612 VALUE_PAIR **tail = &head;
619 fprintf(stderr, "Line %d too long in %s\n",
628 * Comments, with hacks for User-Name[#]
630 p = strchr(buffer, '#');
631 if (p && ((p == buffer) ||
632 ((p > buffer) && (p[-1] != '[')))) *p = '\0';
635 while (isspace((int) *p)) p++;
638 DEBUG2("%s[%d]: %s\n", filename, lineno, buffer);
640 strlcpy(input, p, sizeof(input));
642 if (strncmp(p, "raw ", 4) == 0) {
643 outlen = encode_rfc(p + 4, data, sizeof(data));
645 fprintf(stderr, "Parse error in line %d of %s\n",
656 if (outlen > sizeof(data)) outlen = sizeof(data);
658 if (outlen >= (sizeof(output) / 2)) {
659 outlen = (sizeof(output) / 2) - 1;
663 for (i = 0; i < outlen; i++) {
664 if (sizeof(output) < (3*i)) break;
666 snprintf(output + 3*i, sizeof(output) - (3*i) - 1,
669 outlen = strlen(output);
670 output[outlen - 1] = '\0';
674 if (strncmp(p, "data ", 5) == 0) {
675 if (strcmp(p + 5, output) != 0) {
676 fprintf(stderr, "Mismatch at line %d of %s\n\tgot : %s\n\texpected : %s\n",
677 lineno, directory, output, p + 5);
683 if (strncmp(p, "encode ", 7) == 0) {
684 if (strcmp(p + 7, "-") == 0) {
690 if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
691 strlcpy(output, fr_strerror(), sizeof(output));
698 len = rad_vp2attr(&my_packet, &my_original, my_secret, (VALUE_PAIR const **)(void **)&vp,
699 attr, data + sizeof(data) - attr);
701 fprintf(stderr, "Failed encoding %s: %s\n",
702 vp->da->name, fr_strerror());
710 fr_pair_list_free(&head);
711 outlen = attr - data;
715 if (strncmp(p, "decode ", 7) == 0) {
718 if (strcmp(p + 7, "-") == 0) {
723 len = encode_hex(p + 7, data, sizeof(data));
725 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
733 my_len = rad_attr2vp(NULL, &my_packet, &my_original, my_secret, attr, len, &vp);
735 fr_pair_list_free(&head);
740 fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
755 * Output may be an error, and we ignore
761 for (vp = fr_cursor_init(&cursor, &head);
763 vp = fr_cursor_next(&cursor)) {
764 vp_prints(p, sizeof(output) - (p - output), vp);
767 if (vp->next) {strcpy(p, ", ");
772 fr_pair_list_free(&head);
773 } else if (my_len < 0) {
774 strlcpy(output, fr_strerror(), sizeof(output));
776 } else { /* zero-length attribute */
783 * And some DHCP tests
785 if (strncmp(p, "encode-dhcp ", 12) == 0) {
788 if (strcmp(p + 12, "-") == 0) {
794 if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
795 strlcpy(output, fr_strerror(), sizeof(output));
799 fr_cursor_init(&cursor, &head);
805 while ((vp = fr_cursor_current(&cursor))) {
806 len = fr_dhcp_encode_option(NULL, attr, data + sizeof(data) - attr, &cursor);
808 fprintf(stderr, "Failed encoding %s: %s\n",
809 vp->da->name, fr_strerror());
815 fr_pair_list_free(&head);
816 outlen = attr - data;
820 if (strncmp(p, "decode-dhcp ", 12) == 0) {
823 if (strcmp(p + 12, "-") == 0) {
828 len = encode_hex(p + 12, data, sizeof(data));
830 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
835 my_len = fr_dhcp_decode_options(NULL, &head, attr, len);
838 * Output may be an error, and we ignore
844 for (vp = fr_cursor_init(&cursor, &head);
846 vp = fr_cursor_next(&cursor)) {
847 vp_prints(p, sizeof(output) - (p - output), vp);
850 if (vp->next) {strcpy(p, ", ");
855 fr_pair_list_free(&head);
856 } else if (my_len < 0) {
857 strlcpy(output, fr_strerror(), sizeof(output));
859 } else { /* zero-length attribute */
865 if (strncmp(p, "attribute ", 10) == 0) {
868 if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
869 strlcpy(output, fr_strerror(), sizeof(output));
873 vp_prints(output, sizeof(output), head);
877 if (strncmp(p, "$INCLUDE ", 9) == 0) {
881 while (isspace((int) *p)) p++;
883 q = strrchr(directory, '/');
886 process_file(directory, p);
889 process_file(NULL, p);
894 if (strncmp(p, "condition ", 10) == 0) {
896 parse_condition(p, output, sizeof(output));
900 if (strncmp(p, "xlat ", 5) == 0) {
902 parse_xlat(p, output, sizeof(output));
906 fprintf(stderr, "Unknown input at line %d of %s\n",
911 if (fp != stdin) fclose(fp);
914 static void NEVER_RETURNS usage(void)
916 fprintf(stderr, "usage: radattr [OPTS] filename\n");
917 fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
918 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
919 fprintf(stderr, " -x Debugging mode.\n");
920 fprintf(stderr, " -M Show talloc memory report.\n");
925 int main(int argc, char *argv[])
929 char const *radius_dir = RADDBDIR;
930 char const *dict_dir = DICTDIR;
933 cf_new_escape = true; /* fix the tests */
936 if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
937 fr_perror("radattr");
942 while ((c = getopt(argc, argv, "d:D:xMh")) != EOF) switch (c) {
951 rad_debug_lvl = fr_debug_lvl;
960 argc -= (optind - 1);
961 argv += (optind - 1);
964 * Mismatch between the binary and the libraries it depends on
966 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
967 fr_perror("radattr");
971 if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
972 fr_perror("radattr");
976 if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
977 fr_perror("radattr");
981 if (xlat_register("test", xlat_test, NULL, inst) < 0) {
982 fprintf(stderr, "Failed registering xlat");
987 process_file(NULL, "-");
990 process_file(NULL, argv[1]);
995 fr_log_talloc_report(NULL);