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>
26 #include <freeradius-devel/parser.h>
27 #include <freeradius-devel/conf.h>
28 #include <freeradius-devel/radpaths.h>
38 typedef struct REQUEST REQUEST;
40 #include <freeradius-devel/log.h>
41 log_debug_t debug_flag = 0;
43 /**********************************************************************
46 typedef size_t (*RADIUS_ESCAPE_STRING)(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
47 typedef ssize_t (*RAD_XLAT_FUNC)(void *instance, REQUEST *, char const *, char *, size_t);
48 int xlat_register(char const *module, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape,
52 pid_t rad_waitpid(pid_t pid, int *status);
59 pid_t rad_waitpid(pid_t pid, int *status)
61 return waitpid(pid, status, 0);
64 static ssize_t xlat_test(UNUSED void *instance, UNUSED REQUEST *request,
65 UNUSED char const *fmt, UNUSED char *out, UNUSED size_t outlen)
71 * End of hacks for xlat
73 **********************************************************************/
75 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
77 static char const hextab[] = "0123456789abcdef";
79 static int encode_data_string(char *buffer,
80 uint8_t *output, size_t outlen)
87 while (*p && (outlen > 0)) {
121 fprintf(stderr, "String is not terminated\n");
125 static int encode_data_tlv(char *buffer, char **endptr,
126 uint8_t *output, size_t outlen)
132 for (p = buffer; *p != '\0'; p++) {
133 if (*p == '{') depth++;
136 if (depth == 0) break;
141 fprintf(stderr, "No trailing '}' in string starting "
151 while (isspace((int) *p)) p++;
153 length = encode_tlv(p, output, outlen);
154 if (length == 0) return 0;
159 static int encode_hex(char *p, uint8_t *output, size_t outlen)
165 while (isspace((int) *p)) p++;
169 if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) ||
170 !(c2 = memchr(hextab, tolower((int) p[1]), 16))) {
171 fprintf(stderr, "Invalid data starting at "
176 *output = ((c1 - hextab) << 4) + (c2 - hextab);
183 fprintf(stderr, "Too much data\n");
192 static int encode_data(char *p, uint8_t *output, size_t outlen)
196 if (!isspace((int) *p)) {
197 fprintf(stderr, "Invalid character following attribute "
202 while (isspace((int) *p)) p++;
211 while (isspace((int) *p)) p++;
214 fprintf(stderr, "No data\n");
221 sublen = encode_data_tlv(p, &q, output, outlen);
222 if (sublen == 0) return 0;
234 length = encode_data_string(p, output, outlen);
238 length = encode_hex(p, output, outlen);
241 fprintf(stderr, "Empty string\n");
248 static int decode_attr(char *buffer, char **endptr)
252 attr = strtol(buffer, endptr, 10);
253 if (*endptr == buffer) {
254 fprintf(stderr, "No valid number found in string "
255 "starting with \"%s\"\n", buffer);
260 fprintf(stderr, "Nothing follows attribute number\n");
264 if ((attr <= 0) || (attr > 256)) {
265 fprintf(stderr, "Attribute number is out of valid "
273 static int decode_vendor(char *buffer, char **endptr)
277 if (*buffer != '.') {
278 fprintf(stderr, "Invalid separator before vendor id\n");
282 vendor = strtol(buffer + 1, endptr, 10);
283 if (*endptr == (buffer + 1)) {
284 fprintf(stderr, "No valid vendor number found\n");
289 fprintf(stderr, "Nothing follows vendor number\n");
293 if ((vendor <= 0) || (vendor > (1 << 24))) {
294 fprintf(stderr, "Vendor number is out of valid range\n");
298 if (**endptr != '.') {
299 fprintf(stderr, "Invalid data following vendor number\n");
307 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen)
313 attr = decode_attr(buffer, &p);
314 if (attr == 0) return 0;
321 length = encode_tlv(p, output + 2, outlen - 2);
324 length = encode_data(p, output + 2, outlen - 2);
327 if (length == 0) return 0;
328 if (length > (255 - 2)) {
329 fprintf(stderr, "TLV data is too long\n");
338 static int encode_vsa(char *buffer, uint8_t *output, size_t outlen)
344 vendor = decode_vendor(buffer, &p);
345 if (vendor == 0) return 0;
348 output[1] = (vendor >> 16) & 0xff;
349 output[2] = (vendor >> 8) & 0xff;
350 output[3] = vendor & 0xff;
352 length = encode_tlv(p, output + 4, outlen - 4);
353 if (length == 0) return 0;
354 if (length > (255 - 6)) {
355 fprintf(stderr, "VSA data is too long\n");
363 static int encode_evs(char *buffer, uint8_t *output, size_t outlen)
370 vendor = decode_vendor(buffer, &p);
371 if (vendor == 0) return 0;
373 attr = decode_attr(p, &p);
374 if (attr == 0) return 0;
377 output[1] = (vendor >> 16) & 0xff;
378 output[2] = (vendor >> 8) & 0xff;
379 output[3] = vendor & 0xff;
382 length = encode_data(p, output + 5, outlen - 5);
383 if (length == 0) return 0;
388 static int encode_extended(char *buffer,
389 uint8_t *output, size_t outlen)
395 attr = decode_attr(buffer, &p);
396 if (attr == 0) return 0;
401 length = encode_evs(p, output + 1, outlen - 1);
403 length = encode_data(p, output + 1, outlen - 1);
405 if (length == 0) return 0;
406 if (length > (255 - 3)) {
407 fprintf(stderr, "Extended Attr data is too long\n");
414 static int encode_long_extended(char *buffer,
415 uint8_t *output, size_t outlen)
421 attr = decode_attr(buffer, &p);
422 if (attr == 0) return 0;
424 /* output[0] is the extended attribute */
430 length = encode_evs(p, output + 4, outlen - 4);
431 if (length == 0) return 0;
436 length = encode_data(p, output + 4, outlen - 4);
438 if (length == 0) return 0;
442 int sublen = 255 - output[1];
444 if (length <= sublen) {
452 memmove(output + 255 + 4, output + 255, length);
453 memcpy(output + 255, output, 4);
466 static int encode_rfc(char *buffer, uint8_t *output, size_t outlen)
472 attr = decode_attr(buffer, &p);
473 if (attr == 0) return 0;
480 sublen = encode_vsa(p, output + 2, outlen - 2);
482 } else if ((attr < 241) || (attr > 246)) {
483 sublen = encode_data(p, output + 2, outlen - 2);
487 fprintf(stderr, "Invalid data following "
488 "attribute number\n");
493 sublen = encode_extended(p + 1,
494 output + 2, outlen - 2);
498 * Not like the others!
500 return encode_long_extended(p + 1, output, outlen);
503 if (sublen == 0) return 0;
504 if (sublen > (255 -2)) {
505 fprintf(stderr, "RFC Data is too long\n");
510 return length + sublen;
513 static void parse_condition(char const *input, char *output, size_t outlen)
516 char const *error = NULL;
519 slen = fr_condition_tokenize(NULL, NULL, input, &cond, &error, FR_COND_ONE_PASS);
521 snprintf(output, outlen, "ERROR offset %d %s", (int) -slen, error);
526 if (*input != '\0') {
528 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
532 fr_cond_sprint(output, outlen, cond);
537 static void parse_xlat(char const *input, char *output, size_t outlen)
540 char const *error = NULL;
541 char *fmt = talloc_typed_strdup(NULL, input);
544 slen = xlat_tokenize(fmt, fmt, &head, &error);
546 snprintf(output, outlen, "ERROR offset %d '%s'", (int) -slen, error);
550 if (input[slen] != '\0') {
551 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
555 xlat_sprint(output, outlen, head);
559 static void process_file(const char *root_dir, char const *filename)
563 ssize_t len, data_len;
565 char input[8192], buffer[8192];
567 char directory[8192];
568 uint8_t *attr, data[2048];
570 if (strcmp(filename, "-") == 0) {
575 if (root_dir && *root_dir) {
576 snprintf(directory, sizeof(directory), "%s/%s", root_dir, filename);
578 strlcpy(directory, filename, sizeof(directory));
581 fp = fopen(directory, "r");
583 fprintf(stderr, "Error opening %s: %s\n",
584 directory, fr_syserror(errno));
588 filename = directory;
595 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
596 char *p = strchr(buffer, '\n');
597 VALUE_PAIR *vp, *head = NULL;
598 VALUE_PAIR **tail = &head;
604 fprintf(stderr, "Line %d too long in %s\n",
613 * Comments, with hacks for User-Name[#]
615 p = strchr(buffer, '#');
616 if (p && ((p == buffer) ||
617 ((p > buffer) && (p[-1] != '[')))) *p = '\0';
620 while (isspace((int) *p)) p++;
623 DEBUG2("%s[%d]: %s\n", filename, lineno, buffer);
625 strlcpy(input, p, sizeof(input));
627 if (strncmp(p, "raw ", 4) == 0) {
628 outlen = encode_rfc(p + 4, data, sizeof(data));
630 fprintf(stderr, "Parse error in line %d of %s\n",
641 if (outlen > sizeof(data)) outlen = sizeof(data);
643 if (outlen >= (sizeof(output) / 2)) {
644 outlen = (sizeof(output) / 2) - 1;
648 for (i = 0; i < outlen; i++) {
649 if (sizeof(output) < (3*i)) break;
651 snprintf(output + 3*i, sizeof(output) - (3*i) - 1,
654 outlen = strlen(output);
655 output[outlen - 1] = '\0';
659 if (strncmp(p, "data ", 5) == 0) {
660 if (strcmp(p + 5, output) != 0) {
661 fprintf(stderr, "Mismatch in line %d of %s, got: %s expected: %s\n",
662 lineno, directory, output, p + 5);
668 if (strncmp(p, "encode ", 7) == 0) {
669 if (strcmp(p + 7, "-") == 0) {
675 if (userparse(NULL, p, &head) != T_EOL) {
676 strlcpy(output, fr_strerror(), sizeof(output));
684 len = rad_vp2attr(NULL, NULL, NULL, (VALUE_PAIR const **)(void **)&vp,
685 attr, sizeof(data) - (attr - data));
687 fprintf(stderr, "Failed encoding %s: %s\n",
688 vp->da->name, fr_strerror());
701 if (strncmp(p, "decode ", 7) == 0) {
704 if (strcmp(p + 7, "-") == 0) {
709 len = encode_hex(p + 7, data, sizeof(data));
711 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
719 my_len = rad_attr2vp(NULL, NULL, NULL, NULL, attr, len, &vp);
726 fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
741 * Output may be an error, and we ignore
747 for (vp = fr_cursor_init(&cursor, &head);
749 vp = fr_cursor_next(&cursor)) {
750 vp_prints(p, sizeof(output) - (p - output), vp);
753 if (vp->next) {strcpy(p, ", ");
759 } else if (my_len < 0) {
760 strlcpy(output, fr_strerror(), sizeof(output));
762 } else { /* zero-length attribute */
768 if (strncmp(p, "attribute ", 10) == 0) {
771 if (userparse(NULL, p, &head) != T_EOL) {
772 strlcpy(output, fr_strerror(), sizeof(output));
776 vp_prints(output, sizeof(output), head);
780 if (strncmp(p, "$INCLUDE ", 9) == 0) {
784 while (isspace((int) *p)) p++;
786 q = strrchr(directory, '/');
789 process_file(directory, p);
792 process_file(NULL, p);
797 if (strncmp(p, "condition ", 10) == 0) {
799 parse_condition(p, output, sizeof(output));
803 if (strncmp(p, "xlat ", 5) == 0) {
805 parse_xlat(p, output, sizeof(output));
809 fprintf(stderr, "Unknown input at line %d of %s\n",
814 if (fp != stdin) fclose(fp);
817 int main(int argc, char *argv[])
821 char const *radius_dir = RADDBDIR;
822 char const *dict_dir = DICTDIR;
825 if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
826 fr_perror("radattr");
831 while ((c = getopt(argc, argv, "d:D:xM")) != EOF) switch(c) {
840 debug_flag = fr_debug_flag;
846 fprintf(stderr, "usage: radattr [OPTS] filename\n");
849 argc -= (optind - 1);
850 argv += (optind - 1);
853 * Mismatch between the binary and the libraries it depends on
855 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
856 fr_perror("radattr");
860 if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
861 fr_perror("radattr");
865 if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
866 fr_perror("radattr");
870 if (xlat_register("test", xlat_test, NULL, NULL) < 0) {
871 fprintf(stderr, "Failed registering xlat");
876 process_file(NULL, "-");
879 process_file(NULL, argv[1]);
884 fr_log_talloc_report(NULL);