summaryrefslogtreecommitdiffstats
path: root/src/main/radattr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:11:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 14:11:00 +0000
commitaf754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch)
treeb2f334c2b55ede42081aa6710a72da784547d8ea /src/main/radattr.c
parentInitial commit. (diff)
downloadfreeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz
freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/main/radattr.c')
-rw-r--r--src/main/radattr.c1123
1 files changed, 1123 insertions, 0 deletions
diff --git a/src/main/radattr.c b/src/main/radattr.c
new file mode 100644
index 0000000..8accd0d
--- /dev/null
+++ b/src/main/radattr.c
@@ -0,0 +1,1123 @@
+/*
+ * radattr.c RADIUS Attribute debugging tool.
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2010 Alan DeKok <aland@freeradius.org>
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+typedef struct REQUEST REQUEST;
+
+#include <freeradius-devel/parser.h>
+#include <freeradius-devel/xlat.h>
+#include <freeradius-devel/conf.h>
+#include <freeradius-devel/radpaths.h>
+#include <freeradius-devel/dhcp.h>
+
+#include <ctype.h>
+
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+
+#include <assert.h>
+
+#include <freeradius-devel/log.h>
+extern log_lvl_t rad_debug_lvl;
+
+#include <sys/wait.h>
+#ifdef HAVE_PTHREAD_H
+pid_t rad_fork(void);
+pid_t rad_waitpid(pid_t pid, int *status);
+
+pid_t rad_fork(void)
+{
+ return fork();
+}
+
+pid_t rad_waitpid(pid_t pid, int *status)
+{
+ return waitpid(pid, status, 0);
+}
+#endif
+
+static TALLOC_CTX *autofree;
+
+static ssize_t xlat_test(UNUSED void *instance, UNUSED REQUEST *request,
+ UNUSED char const *fmt, UNUSED char *out, UNUSED size_t outlen)
+{
+ return 0;
+}
+
+static RADIUS_PACKET access_request = {
+ .sockfd = -1,
+ .id = 0,
+ .code = PW_CODE_ACCESS_REQUEST,
+ .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+};
+
+static RADIUS_PACKET access_accept = {
+ .sockfd = -1,
+ .id = 0,
+ .code = PW_CODE_ACCESS_ACCEPT,
+ .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+};
+
+static RADIUS_PACKET coa_request = {
+ .sockfd = -1,
+ .id = 0,
+ .code = PW_CODE_COA_REQUEST,
+ .vector = { 0 },
+};
+
+static RADIUS_PACKET *my_original = &access_request;
+static RADIUS_PACKET *my_packet = &access_accept;
+
+static char const *my_secret = "testing123";
+
+/*
+ * End of hacks for xlat
+ *
+ **********************************************************************/
+
+static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
+
+static char const hextab[] = "0123456789abcdef";
+
+static int encode_data_string(char *buffer,
+ uint8_t *output, size_t outlen)
+{
+ int length = 0;
+ char *p;
+
+ p = buffer + 1;
+
+ while (*p && (outlen > 0)) {
+ if (*p == '"') {
+ return length;
+ }
+
+ if (*p != '\\') {
+ *(output++) = *(p++);
+ outlen--;
+ length++;
+ continue;
+ }
+
+ switch (p[1]) {
+ default:
+ *(output++) = p[1];
+ break;
+
+ case 'n':
+ *(output++) = '\n';
+ break;
+
+ case 'r':
+ *(output++) = '\r';
+ break;
+
+ case 't':
+ *(output++) = '\t';
+ break;
+ }
+
+ outlen--;
+ length++;
+ }
+
+ fprintf(stderr, "String is not terminated\n");
+ return 0;
+}
+
+static int encode_data_tlv(char *buffer, char **endptr,
+ uint8_t *output, size_t outlen)
+{
+ int depth = 0;
+ int length;
+ char *p;
+
+ for (p = buffer; *p != '\0'; p++) {
+ if (*p == '{') depth++;
+ if (*p == '}') {
+ depth--;
+ if (depth == 0) break;
+ }
+ }
+
+ if (*p != '}') {
+ fprintf(stderr, "No trailing '}' in string starting "
+ "with \"%s\"\n",
+ buffer);
+ return 0;
+ }
+
+ *endptr = p + 1;
+ *p = '\0';
+
+ p = buffer + 1;
+ while (isspace((uint8_t) *p)) p++;
+
+ length = encode_tlv(p, output, outlen);
+ if (length == 0) return 0;
+
+ return length;
+}
+
+static int encode_hex(char *p, uint8_t *output, size_t outlen)
+{
+ int length = 0;
+ while (*p) {
+ char *c1, *c2;
+
+ while (isspace((uint8_t) *p)) p++;
+
+ if (!*p) break;
+
+ if(!(c1 = memchr(hextab, tolower((uint8_t) p[0]), 16)) ||
+ !(c2 = memchr(hextab, tolower((uint8_t) p[1]), 16))) {
+ fprintf(stderr, "Invalid data starting at "
+ "\"%s\"\n", p);
+ return 0;
+ }
+
+ *output = ((c1 - hextab) << 4) + (c2 - hextab);
+ output++;
+ length++;
+ p += 2;
+
+ outlen--;
+ if (outlen == 0) {
+ fprintf(stderr, "Too much data\n");
+ return 0;
+ }
+ }
+
+ return length;
+}
+
+
+static int encode_data(char *p, uint8_t *output, size_t outlen)
+{
+ int length;
+
+ if (!isspace((uint8_t) *p)) {
+ fprintf(stderr, "Invalid character following attribute "
+ "definition\n");
+ return 0;
+ }
+
+ while (isspace((uint8_t) *p)) p++;
+
+ if (*p == '{') {
+ int sublen;
+ char *q;
+
+ length = 0;
+
+ do {
+ while (isspace((uint8_t) *p)) p++;
+ if (!*p) {
+ if (length == 0) {
+ fprintf(stderr, "No data\n");
+ return 0;
+ }
+
+ break;
+ }
+
+ sublen = encode_data_tlv(p, &q, output, outlen);
+ if (sublen == 0) return 0;
+
+ length += sublen;
+ output += sublen;
+ outlen -= sublen;
+ p = q;
+ } while (*q);
+
+ return length;
+ }
+
+ if (*p == '"') {
+ length = encode_data_string(p, output, outlen);
+ return length;
+ }
+
+ length = encode_hex(p, output, outlen);
+
+ if (length == 0) {
+ fprintf(stderr, "Empty string\n");
+ return 0;
+ }
+
+ return length;
+}
+
+static int decode_attr(char *buffer, char **endptr)
+{
+ long attr;
+
+ attr = strtol(buffer, endptr, 10);
+ if (*endptr == buffer) {
+ fprintf(stderr, "No valid number found in string "
+ "starting with \"%s\"\n", buffer);
+ return 0;
+ }
+
+ if (!**endptr) {
+ fprintf(stderr, "Nothing follows attribute number\n");
+ return 0;
+ }
+
+ if ((attr <= 0) || (attr > 256)) {
+ fprintf(stderr, "Attribute number is out of valid "
+ "range\n");
+ return 0;
+ }
+
+ return (int) attr;
+}
+
+static int decode_vendor(char *buffer, char **endptr)
+{
+ long vendor;
+
+ if (*buffer != '.') {
+ fprintf(stderr, "Invalid separator before vendor id\n");
+ return 0;
+ }
+
+ vendor = strtol(buffer + 1, endptr, 10);
+ if (*endptr == (buffer + 1)) {
+ fprintf(stderr, "No valid vendor number found\n");
+ return 0;
+ }
+
+ if (!**endptr) {
+ fprintf(stderr, "Nothing follows vendor number\n");
+ return 0;
+ }
+
+ if ((vendor <= 0) || (vendor > (1 << 24))) {
+ fprintf(stderr, "Vendor number is out of valid range\n");
+ return 0;
+ }
+
+ if (**endptr != '.') {
+ fprintf(stderr, "Invalid data following vendor number\n");
+ return 0;
+ }
+ (*endptr)++;
+
+ return (int) vendor;
+}
+
+static int encode_tlv(char *buffer, uint8_t *output, size_t outlen)
+{
+ int attr;
+ int length;
+ char *p;
+
+ attr = decode_attr(buffer, &p);
+ if (attr == 0) return 0;
+
+ output[0] = attr;
+ output[1] = 2;
+
+ if (*p == '.') {
+ p++;
+ length = encode_tlv(p, output + 2, outlen - 2);
+
+ } else {
+ length = encode_data(p, output + 2, outlen - 2);
+ }
+
+ if (length == 0) return 0;
+ if (length > (255 - 2)) {
+ fprintf(stderr, "TLV data is too long\n");
+ return 0;
+ }
+
+ output[1] += length;
+
+ return length + 2;
+}
+
+static int encode_vsa(char *buffer, uint8_t *output, size_t outlen)
+{
+ int vendor;
+ int length;
+ char *p;
+
+ vendor = decode_vendor(buffer, &p);
+ if (vendor == 0) return 0;
+
+ output[0] = 0;
+ output[1] = (vendor >> 16) & 0xff;
+ output[2] = (vendor >> 8) & 0xff;
+ output[3] = vendor & 0xff;
+
+ length = encode_tlv(p, output + 4, outlen - 4);
+ if (length == 0) return 0;
+ if (length > (255 - 6)) {
+ fprintf(stderr, "VSA data is too long\n");
+ return 0;
+ }
+
+
+ return length + 4;
+}
+
+static int encode_evs(char *buffer, uint8_t *output, size_t outlen)
+{
+ int vendor;
+ int attr;
+ int length;
+ char *p;
+
+ vendor = decode_vendor(buffer, &p);
+ if (vendor == 0) return 0;
+
+ attr = decode_attr(p, &p);
+ if (attr == 0) return 0;
+
+ output[0] = 0;
+ output[1] = (vendor >> 16) & 0xff;
+ output[2] = (vendor >> 8) & 0xff;
+ output[3] = vendor & 0xff;
+ output[4] = attr;
+
+ length = encode_data(p, output + 5, outlen - 5);
+ if (length == 0) return 0;
+
+ return length + 5;
+}
+
+static int encode_extended(char *buffer,
+ uint8_t *output, size_t outlen)
+{
+ int attr;
+ int length;
+ char *p;
+
+ attr = decode_attr(buffer, &p);
+ if (attr == 0) return 0;
+
+ output[0] = attr;
+
+ if (attr == 26) {
+ length = encode_evs(p, output + 1, outlen - 1);
+ } else {
+ length = encode_data(p, output + 1, outlen - 1);
+ }
+ if (length == 0) return 0;
+ if (length > (255 - 3)) {
+ fprintf(stderr, "Extended Attr data is too long\n");
+ return 0;
+ }
+
+ return length + 1;
+}
+
+static int encode_long_extended(char *buffer,
+ uint8_t *output, size_t outlen)
+{
+ int attr;
+ int length, total;
+ char *p;
+
+ attr = decode_attr(buffer, &p);
+ if (attr == 0) return 0;
+
+ /* output[0] is the extended attribute */
+ output[1] = 4;
+ output[2] = attr;
+ output[3] = 0;
+
+ if (attr == 26) {
+ length = encode_evs(p, output + 4, outlen - 4);
+ if (length == 0) return 0;
+
+ output[1] += 5;
+ length -= 5;
+ } else {
+ length = encode_data(p, output + 4, outlen - 4);
+ }
+ if (length == 0) return 0;
+
+ total = 0;
+ while (1) {
+ int sublen = 255 - output[1];
+
+ if (length <= sublen) {
+ output[1] += length;
+ total += output[1];
+ break;
+ }
+
+ length -= sublen;
+
+ memmove(output + 255 + 4, output + 255, length);
+ memcpy(output + 255, output, 4);
+
+ output[1] = 255;
+ output[3] |= 0x80;
+
+ output += 255;
+ output[1] = 4;
+ total += 255;
+ }
+
+ return total;
+}
+
+static int encode_rfc(char *buffer, uint8_t *output, size_t outlen)
+{
+ int attr;
+ int length, sublen;
+ char *p;
+
+ attr = decode_attr(buffer, &p);
+ if (attr == 0) return 0;
+
+ length = 2;
+ output[0] = attr;
+ output[1] = 2;
+
+ if (attr == 26) {
+ sublen = encode_vsa(p, output + 2, outlen - 2);
+
+ } else if ((attr < 241) || (attr > 246)) {
+ sublen = encode_data(p, output + 2, outlen - 2);
+
+ } else {
+ if (*p != '.') {
+ fprintf(stderr, "Invalid data following "
+ "attribute number\n");
+ return 0;
+ }
+
+ if (attr < 245) {
+ sublen = encode_extended(p + 1,
+ output + 2, outlen - 2);
+ } else {
+
+ /*
+ * Not like the others!
+ */
+ return encode_long_extended(p + 1, output, outlen);
+ }
+ }
+ if (sublen == 0) return 0;
+ if (sublen > (255 -2)) {
+ fprintf(stderr, "RFC Data is too long\n");
+ return 0;
+ }
+
+ output[1] += sublen;
+ return length + sublen;
+}
+
+static void parse_condition(char const *input, char *output, size_t outlen)
+{
+ ssize_t slen;
+ char const *error = NULL;
+ fr_cond_t *cond;
+
+ slen = fr_condition_tokenize(NULL, NULL, input, &cond, &error, FR_COND_ONE_PASS);
+ if (slen <= 0) {
+ snprintf(output, outlen, "ERROR offset %d %s", (int) -slen, error);
+ return;
+ }
+
+ input += slen;
+ if (*input != '\0') {
+ talloc_free(cond);
+ snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
+ return;
+ }
+
+ fr_cond_sprint(output, outlen, cond);
+
+ talloc_free(cond);
+}
+
+static void parse_xlat(char const *input, char *output, size_t outlen)
+{
+ ssize_t slen;
+ char const *error = NULL;
+ char *fmt = talloc_typed_strdup(autofree, input);
+ xlat_exp_t *head;
+
+ slen = xlat_tokenize(autofree, fmt, &head, &error);
+ if (slen <= 0) {
+ snprintf(output, outlen, "ERROR offset %d '%s'", (int) -slen, error);
+ return;
+ }
+
+ if (input[slen] != '\0') {
+ snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
+ talloc_free(fmt);
+ return;
+ }
+
+ xlat_sprint(output, outlen, head);
+ talloc_free(fmt);
+}
+
+static void process_file(const char *root_dir, char const *filename)
+{
+ int lineno;
+ size_t i, outlen;
+ ssize_t len, data_len;
+ FILE *fp;
+ char input[8192], buffer[8192];
+ char output[8192];
+ char directory[8192];
+ uint8_t *attr, data[2048];
+
+ if (strcmp(filename, "-") == 0) {
+ fp = stdin;
+ directory[0] = '\0';
+
+ } else {
+ if (root_dir && *root_dir) {
+ snprintf(directory, sizeof(directory), "%s/%s", root_dir, filename);
+ } else {
+ strlcpy(directory, filename, sizeof(directory));
+ }
+
+ fp = fopen(directory, "r");
+ if (!fp) {
+ fprintf(stderr, "Error opening %s: %s\n",
+ directory, fr_syserror(errno));
+ exit(1);
+ }
+
+ filename = directory;
+ }
+
+ lineno = 0;
+ *output = '\0';
+ data_len = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ char *p = strchr(buffer, '\n');
+ VALUE_PAIR *vp, *head;
+ VALUE_PAIR **tail = &head;
+
+ lineno++;
+ head = NULL;
+
+ if (!p) {
+ if (!feof(fp)) {
+ fprintf(stderr, "Line %d too long in %s\n",
+ lineno, directory);
+ exit(1);
+ }
+ } else {
+ *p = '\0';
+ }
+
+ /*
+ * Comments, with hacks for User-Name[#]
+ */
+ p = strchr(buffer, '#');
+ if (p && ((p == buffer) ||
+ ((p > buffer) && (p[-1] != '[')))) *p = '\0';
+
+ p = buffer;
+ while (isspace((uint8_t) *p)) p++;
+ if (!*p) continue;
+
+ DEBUG2("%s[%d]: %s\n", filename, lineno, buffer);
+
+ strlcpy(input, p, sizeof(input));
+
+ if (strncmp(p, "raw ", 4) == 0) {
+ outlen = encode_rfc(p + 4, data, sizeof(data));
+ if (outlen == 0) {
+ fprintf(stderr, "Parse error in line %d of %s\n",
+ lineno, directory);
+ exit(1);
+ }
+
+ print_hex:
+ if (outlen == 0) {
+ output[0] = 0;
+ continue;
+ }
+
+ if (outlen > sizeof(data)) outlen = sizeof(data);
+
+ if (outlen >= (sizeof(output) / 2)) {
+ outlen = (sizeof(output) / 2) - 1;
+ }
+
+ data_len = outlen;
+ for (i = 0; i < outlen; i++) {
+ if (sizeof(output) < (3*i)) break;
+
+ snprintf(output + 3*i, sizeof(output) - (3*i) - 1,
+ "%02x ", data[i]);
+ }
+ outlen = strlen(output);
+ output[outlen - 1] = '\0';
+ continue;
+ }
+
+ if (strncmp(p, "data ", 5) == 0) {
+ if (strcmp(p + 5, output) != 0) {
+ fprintf(stderr, "Mismatch at line %d of %s\n\tgot : %s\n\texpected : %s\n",
+ lineno, directory, output, p + 5);
+ exit(1);
+ }
+ continue;
+ }
+
+ if (strncmp(p, "packet ", 7) == 0) {
+ p += 7;
+ if (strncmp(p, "access_accept", 13) == 0) {
+ my_packet = &access_accept;
+ } else if (strncmp(p, "coa_request", 11) == 0) {
+ my_packet = &coa_request;
+ } else {
+ fprintf(stderr, "Unsupported packet type at line %d of %s: %s\n",
+ lineno, directory, p);
+ exit(1);
+ }
+ continue;
+ }
+ if (strncmp(p, "original ", 9) == 0) {
+ p += 9;
+ if (strncmp(p, "null", 4) == 0) {
+ my_original = NULL;
+ } else if (strncmp(p, "access_request", 14) == 0) {
+ my_original = &access_request;
+ } else {
+ fprintf(stderr, "Unsupported original type at line %d of %s: %s\n",
+ lineno, directory, p);
+ exit(1);
+ }
+ continue;
+ }
+
+ if (strncmp(p, "encode ", 7) == 0) {
+ if (strcmp(p + 7, "-") == 0) {
+ p = output;
+ } else {
+ p += 7;
+ }
+
+ if (fr_pair_list_afrom_str(autofree, p, &head) != T_EOL) {
+ strlcpy(output, fr_strerror(), sizeof(output));
+ continue;
+ }
+
+ attr = data;
+ vp = head;
+ while (vp) {
+ VALUE_PAIR **pvp = &vp;
+ VALUE_PAIR const **qvp;
+
+ memcpy(&qvp, &pvp, sizeof(pvp));
+
+ len = rad_vp2attr(my_packet, my_original, my_secret, qvp,
+ attr, data + sizeof(data) - attr);
+ if (len < 0) {
+ fprintf(stderr, "Failed encoding %s: %s\n",
+ vp->da->name, fr_strerror());
+ fr_pair_list_free(&head);
+ exit(1);
+ }
+
+ attr += len;
+ if (len == 0) break;
+ }
+
+ fr_pair_list_free(&head);
+ outlen = attr - data;
+ goto print_hex;
+ }
+
+ if (strncmp(p, "decode ", 7) == 0) {
+ ssize_t my_len;
+
+ if (strcmp(p + 7, "-") == 0) {
+ attr = data;
+ len = data_len;
+ } else {
+ attr = data;
+ len = encode_hex(p + 7, data, sizeof(data));
+ if (len == 0) {
+ fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
+ exit(1);
+ }
+ }
+
+ my_len = 0;
+ while (len > 0) {
+ vp = NULL;
+ my_len = rad_attr2vp(autofree, my_packet, my_original, my_secret, attr, len, &vp);
+ if (my_len < 0) {
+ fr_pair_list_free(&head);
+ break;
+ }
+
+ if (my_len > len) {
+ fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
+ exit(1);
+ }
+
+ *tail = vp;
+ while (vp) {
+ tail = &(vp->next);
+ vp = vp->next;
+ }
+
+ attr += my_len;
+ len -= my_len;
+ }
+
+ /*
+ * Output may be an error, and we ignore
+ * it if so.
+ */
+ if (head) {
+ vp_cursor_t cursor;
+ p = output;
+ for (vp = fr_cursor_init(&cursor, &head);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ vp_prints(p, sizeof(output) - (p - output), vp);
+ p += strlen(p);
+
+ if (vp->next) {
+ strcpy(p, ", ");
+ p += 2;
+ }
+ }
+
+ fr_pair_list_free(&head);
+ } else if (my_len < 0) {
+ strlcpy(output, fr_strerror(), sizeof(output));
+
+ } else { /* zero-length attribute */
+ *output = '\0';
+ }
+
+ continue;
+ }
+
+#ifdef WITH_DHCP
+ /*
+ * And some DHCP tests
+ */
+ if (strncmp(p, "encode-dhcp ", 12) == 0) {
+ vp_cursor_t cursor;
+
+ if (strcmp(p + 12, "-") == 0) {
+ p = output;
+ } else {
+ p += 12;
+ }
+
+ if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
+ strlcpy(output, fr_strerror(), sizeof(output));
+ continue;
+ }
+
+ fr_cursor_init(&cursor, &head);
+
+
+ attr = data;
+ vp = head;
+
+ while ((vp = fr_cursor_current(&cursor))) {
+ len = fr_dhcp_encode_option(NULL, attr, data + sizeof(data) - attr, &cursor);
+ if (len < 0) {
+ fprintf(stderr, "Failed encoding %s: %s\n",
+ vp->da->name, fr_strerror());
+ exit(1);
+ }
+ attr += len;
+ };
+
+ fr_pair_list_free(&head);
+ outlen = attr - data;
+ goto print_hex;
+ }
+
+ if (strncmp(p, "decode-dhcp ", 12) == 0) {
+ ssize_t my_len;
+
+ if (strcmp(p + 12, "-") == 0) {
+ attr = data;
+ len = data_len;
+ } else {
+ attr = data;
+ len = encode_hex(p + 12, data, sizeof(data));
+ if (len == 0) {
+ fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
+ exit(1);
+ }
+ }
+
+ my_len = fr_dhcp_decode_options(NULL, &head, attr, len);
+
+ /*
+ * Output may be an error, and we ignore
+ * it if so.
+ */
+ if (head) {
+ vp_cursor_t cursor;
+ p = output;
+ for (vp = fr_cursor_init(&cursor, &head);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ vp_prints(p, sizeof(output) - (p - output), vp);
+ p += strlen(p);
+
+ if (vp->next) {strcpy(p, ", ");
+ p += 2;
+ }
+ }
+
+ fr_pair_list_free(&head);
+ } else if (my_len < 0) {
+ strlcpy(output, fr_strerror(), sizeof(output));
+
+ } else { /* zero-length attribute */
+ *output = '\0';
+ }
+ continue;
+ }
+#endif
+
+ if (strncmp(p, "attribute ", 10) == 0) {
+ p += 10;
+
+ if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
+ strlcpy(output, fr_strerror(), sizeof(output));
+ continue;
+ }
+
+ vp_prints(output, sizeof(output), head);
+
+ fr_pair_list_free(&head);
+ continue;
+ }
+
+ if (strncmp(p, "$INCLUDE ", 9) == 0) {
+ char *q;
+
+ p += 9;
+ while (isspace((uint8_t) *p)) p++;
+
+ q = strrchr(directory, '/');
+ if (q) {
+ *q = '\0';
+ process_file(directory, p);
+ *q = '/';
+ } else {
+ process_file(NULL, p);
+ }
+ continue;
+ }
+
+ if (strncmp(p, "condition ", 10) == 0) {
+ p += 10;
+ parse_condition(p, output, sizeof(output));
+ continue;
+ }
+
+ if (strncmp(p, "xlat ", 5) == 0) {
+ p += 5;
+ parse_xlat(p, output, sizeof(output));
+ continue;
+ }
+
+ fprintf(stderr, "Unknown input at line %d of %s\n",
+ lineno, directory);
+ exit(1);
+ }
+
+ if (fp != stdin) fclose(fp);
+}
+
+/** Dump all of the dictionary entries as
+ *
+ * ALIAS name OID
+ *
+ * To create dictionaries which allow files to be used with v4.
+ *
+ * rm -rf alias;mkdir alias;./build/make/jlibtool --mode=execute ./build/bin/radattr -D ./share/ -A | sort -n -k6 -k7 -k8 -k9 -k10 -k11 | gawk '{printf "%s\t%-40s\t%s\n", $1, $2, $3 >> "alias/alias." tolower($5) }'
+ *
+ * And then post-process each file to remove the comments.
+ *
+ * Note that we have to use GNU Awk, as OSX awk doesn't like redirection to a file which includes a variable.
+ */
+static int dump_aliases(void *ctx, void *data)
+{
+ DICT_ATTR *da = data;
+ FILE *fp = ctx;
+ int nest, attr, dv_type;
+ DICT_VENDOR *dv;
+ char buffer[1024];
+
+ if (!da->vendor || (da->vendor > FR_MAX_VENDOR)) return 0;
+
+ dv = dict_vendorbyvalue(da->vendor);
+ dv_type = dv->type;
+
+ (void) dict_print_oid(buffer, sizeof(buffer), da);
+ fprintf(fp, "ALIAS\t%s\t%s # %s %u", da->name, buffer, dv->name, da->vendor);
+
+ attr = da->attr;
+ switch (dv_type) {
+ default:
+ case 1:
+ fprintf(fp, " %u", attr & 0xff);
+
+ /*
+ * Only these ones are bit-packed.
+ */
+ for (nest = 1; nest <= fr_attr_max_tlv; nest++) {
+ if (((attr >> fr_attr_shift[nest]) & fr_attr_mask[nest]) == 0) break;
+
+ fprintf(fp, " %u",
+ (attr >> fr_attr_shift[nest]) & fr_attr_mask[nest]);
+ }
+ break;
+
+ case 2:
+ fprintf(fp, " %u", attr & 0xffff);
+ break;
+
+ case 4:
+ fprintf(fp, " %u", attr);
+ break;
+ }
+
+ printf("\n");
+
+ return 0;
+}
+
+static void NEVER_RETURNS usage(void)
+{
+ fprintf(stderr, "usage: radattr [OPTS] filename\n");
+ fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
+ fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
+ fprintf(stderr, " -x Debugging mode.\n");
+ fprintf(stderr, " -M Show talloc memory report.\n");
+
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ bool report = false;
+ bool dump_alias = false;
+ char const *radius_dir = RADDBDIR;
+ char const *dict_dir = DICTDIR;
+ int *inst = &c;
+
+DIAG_OFF(deprecated-declarations)
+ autofree = talloc_autofree_context();
+DIAG_ON(deprecated-declarations)
+
+ cf_new_escape = true; /* fix the tests */
+
+#ifndef NDEBUG
+ if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+ fr_perror("radattr");
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ while ((c = getopt(argc, argv, "Ad:D:xMh")) != EOF) switch (c) {
+ case 'A':
+ dump_alias = true;
+ break;
+ case 'd':
+ radius_dir = optarg;
+ break;
+ case 'D':
+ dict_dir = optarg;
+ break;
+ case 'x':
+ fr_debug_lvl++;
+ rad_debug_lvl = fr_debug_lvl;
+ break;
+ case 'M':
+ report = true;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ argc -= (optind - 1);
+ argv += (optind - 1);
+
+ /*
+ * Mismatch between the binary and the libraries it depends on
+ */
+ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+ fr_perror("radattr");
+ return 1;
+ }
+
+ if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+ fr_perror("radattr");
+ return 1;
+ }
+
+ if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+ fr_perror("radattr");
+ return 1;
+ }
+
+ if (xlat_register("test", xlat_test, NULL, inst) < 0) {
+ fprintf(stderr, "Failed registering xlat");
+ return 1;
+ }
+
+ if (dump_alias) {
+ (void) dict_walk(dump_aliases, stdout);
+ return 0;
+ }
+
+ if (argc < 2) {
+ process_file(NULL, "-");
+
+ } else {
+ process_file(NULL, argv[1]);
+ }
+
+ if (report) {
+ dict_free();
+ fr_log_talloc_report(NULL);
+ }
+
+ return 0;
+}