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_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, strerror(errno));
593 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
594 char *p = strchr(buffer, '\n');
595 VALUE_PAIR *vp, *head = NULL;
596 VALUE_PAIR **tail = &head;
602 fprintf(stderr, "Line %d too long in %s\n",
611 * Comments, with hacks for User-Name[#]
613 p = strchr(buffer, '#');
614 if (p && ((p == buffer) ||
615 ((p > buffer) && (p[-1] != '[')))) *p = '\0';
618 while (isspace((int) *p)) p++;
621 strlcpy(input, p, sizeof(input));
623 if (strncmp(p, "raw ", 4) == 0) {
624 outlen = encode_rfc(p + 4, data, sizeof(data));
626 fprintf(stderr, "Parse error in line %d of %s\n",
638 for (i = 0; i < outlen; i++) {
639 snprintf(output + 3*i, sizeof(output),
642 outlen = strlen(output);
643 output[outlen - 1] = '\0';
647 if (strncmp(p, "data ", 5) == 0) {
648 if (strcmp(p + 5, output) != 0) {
649 fprintf(stderr, "Mismatch in line %d of %s, expected: %s\n",
650 lineno, directory, output);
656 if (strncmp(p, "encode ", 7) == 0) {
657 if (strcmp(p + 7, "-") == 0) {
663 if (userparse(NULL, p, &head) != T_EOL) {
664 strlcpy(output, fr_strerror(), sizeof(output));
672 len = rad_vp2attr(NULL, NULL, NULL, (VALUE_PAIR const **)(void **)&vp,
673 attr, sizeof(data) - (attr - data));
675 fprintf(stderr, "Failed encoding %s: %s\n",
676 vp->da->name, fr_strerror());
689 if (strncmp(p, "decode ", 7) == 0) {
692 if (strcmp(p + 7, "-") == 0) {
697 len = encode_hex(p + 7, data, sizeof(data));
699 fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
707 my_len = rad_attr2vp(NULL, NULL, NULL,
715 fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
730 * Output may be an error, and we ignore
736 for (vp = paircursor(&cursor, &head);
738 vp = pairnext(&cursor)) {
739 vp_prints(p, sizeof(output) - (p - output), vp);
742 if (vp->next) {strcpy(p, ", ");
748 } else if (my_len < 0) {
749 strlcpy(output, fr_strerror(), sizeof(output));
751 } else { /* zero-length attribute */
757 if (strncmp(p, "$INCLUDE ", 9) == 0) {
761 while (isspace((int) *p)) p++;
763 q = strrchr(directory, '/');
766 process_file(directory, p);
769 process_file(NULL, p);
774 if (strncmp(p, "condition ", 10) == 0) {
776 parse_condition(p, output, sizeof(output));
780 if (strncmp(p, "xlat ", 5) == 0) {
782 parse_xlat(p, output, sizeof(output));
786 fprintf(stderr, "Unknown input at line %d of %s\n",
791 if (fp != stdin) fclose(fp);
794 int main(int argc, char *argv[])
798 char const *radius_dir = RADDBDIR;
800 while ((c = getopt(argc, argv, "d:xM")) != EOF) switch(c) {
806 debug_flag = fr_debug_flag;
812 fprintf(stderr, "usage: radattr [OPTS] filename\n");
815 argc -= (optind - 1);
816 argv += (optind - 1);
819 talloc_enable_null_tracking();
821 talloc_set_log_fn(log_talloc);
824 * Mismatch between the binary and the libraries it depends on
826 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
827 fr_perror("radattr");
831 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
832 fr_perror("radattr");
836 if (xlat_register("test", xlat_test, NULL, NULL) < 0) {
837 fprintf(stderr, "Failed registering xlat");
842 process_file(NULL, "-");
845 process_file(NULL, argv[1]);
850 log_talloc_report(NULL);