From 7ec93ff9e4d979e4bbcf33f9c90c94dc9d3cdba9 Mon Sep 17 00:00:00 2001
From: Luke Howard <lukeh@padl.com>
Date: Sun, 13 Nov 2011 16:16:05 +1100
Subject: add new RADIUS client library

---
 lib/radius/tests/Makefile  |  25 ++
 lib/radius/tests/radattr.c | 769 +++++++++++++++++++++++++++++++++++++++++++++
 lib/radius/tests/rfc.txt   | 144 +++++++++
 3 files changed, 938 insertions(+)
 create mode 100644 lib/radius/tests/Makefile
 create mode 100644 lib/radius/tests/radattr.c
 create mode 100644 lib/radius/tests/rfc.txt

(limited to 'lib/radius/tests')

diff --git a/lib/radius/tests/Makefile b/lib/radius/tests/Makefile
new file mode 100644
index 0000000..b9d74ad
--- /dev/null
+++ b/lib/radius/tests/Makefile
@@ -0,0 +1,25 @@
+#
+#  GNU Makefile
+#
+.PHONY: all clean
+all: radattr
+
+HEADERS		:= ../client.h ../radius.h
+CFLAGS		:= -g
+
+%.o : %.c
+	$(CC) $(CFLAGS) -I.. -I. -c $<
+
+%.o: ${HEADERS}
+
+LIBS	:= -lcrypto -lssl
+LDFLAGS = -L.. -lnetworkradius-client
+
+../libnetworkradius-client.a:
+	@${MAKE} -C .. libnetworkradius-client.a
+
+radattr: radattr.o ../libnetworkradius-client.a
+	${CC} ${LFDLAGS} ${LIBS} -o $@ $^
+
+clean:
+	@rm -rf *.o *.a *~
diff --git a/lib/radius/tests/radattr.c b/lib/radius/tests/radattr.c
new file mode 100644
index 0000000..d41499a
--- /dev/null
+++ b/lib/radius/tests/radattr.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2011 Network RADIUS SARL <info@networkradius.com>
+ *
+ * This software may not be redistributed in any form without the prior
+ * written consent of Network RADIUS.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <networkradius-devel/client.h>
+
+#include <ctype.h>
+
+#include <assert.h>
+
+static int packet_code = PW_ACCESS_REQUEST;
+static int packet_id = 1;
+static uint8_t packet_vector[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0 };
+static char secret[256] = "testing123";
+
+static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
+
+static const char *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((int) *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((int) *p)) p++;
+
+		if (!*p) break;
+
+		if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) ||
+		   !(c2 = memchr(hextab, tolower((int)  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((int) *p)) {
+		fprintf(stderr, "Invalid character following attribute "
+			"definition\n");
+		return 0;
+	}
+
+	while (isspace((int) *p)) p++;
+
+	if (*p == '{') {
+		int sublen;
+		char *q;
+
+		length = 0;
+
+		do {
+			while (isspace((int) *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_extended_flags(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_extended_flags(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 int walk_callback(void *ctx, const DICT_ATTR *da,
+			 const uint8_t *data, size_t sizeof_data)
+{
+	char **p = ctx;
+
+	sprintf(*p, "v%u a%u l%ld,",
+		da->vendor, da->attr, sizeof_data);
+
+	*p += strlen(*p);
+}
+
+static void process_file(const char *filename)
+{
+	int lineno, rcode;
+	size_t i, outlen;
+	ssize_t len, data_len;
+	FILE *fp;
+	RADIUS_PACKET packet;
+	char input[8192], buffer[8192];
+	char output[8192];
+	uint8_t *attr, data[2048];
+
+	if (strcmp(filename, "-") == 0) {
+		fp = stdin;
+		filename = "<stdin>";
+
+	} else {
+		fp = fopen(filename, "r");
+		if (!fp) {
+			fprintf(stderr, "Error opening %s: %s\n",
+				filename, strerror(errno));
+			exit(1);
+		}
+	}
+
+	lineno = 0;
+	*output = '\0';
+	data_len = 0;
+
+	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+		char *p = strchr(buffer, '\n');
+		VALUE_PAIR *vp, *head = NULL;
+		VALUE_PAIR **tail = &head;
+
+		lineno++;
+
+		if (!p) {
+			if (!feof(fp)) {
+				fprintf(stderr, "Line %d too long in %s\n",
+					lineno, filename);
+				exit(1);
+			}
+		} else {
+			*p = '\0';
+		}
+
+		p = strchr(buffer, '#');
+		if (p) *p = '\0';
+
+		p = buffer;
+		while (isspace((int) *p)) p++;
+		if (!*p) continue;
+
+		strcpy(input, p);
+
+		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, filename);
+				exit(1);
+			}
+
+		print_hex:
+			if (outlen == 0) {
+				output[0] = 0;
+				continue;
+			}
+
+			data_len = outlen;
+			for (i = 0; i < outlen; i++) {
+				snprintf(output + 3*i, sizeof(output),
+					 "%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 in line %d of %s, expected: %s\n",
+					lineno, filename, output);
+				exit(1);
+			}
+			continue;
+		}
+
+		head = NULL;
+		if (strncmp(p, "encode ", 7) == 0) {
+			if (strcmp(p + 7, "-") == 0) {
+				p = output;
+			} else {
+				p += 7;
+			}
+
+			rcode = nr_vp_sscanf(p, &head);
+			if (rcode < 0) {
+				strcpy(output, nr_strerror(rcode));
+				continue;
+			}
+
+			attr = data;
+			vp = head;
+			while (vp != NULL) {
+				len = nr_vp2attr(NULL, NULL, &vp,
+						 attr, sizeof(data) - (attr - data));
+				if (len < 0) {
+					fprintf(stderr, "Failed encoding %s: %s\n",
+						vp->da->name, nr_strerror(len));
+					exit(1);
+				}
+
+				attr += len;
+				if (len == 0) break;
+			}
+			
+			nr_vp_free(&head);
+			outlen = len;
+			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, filename);
+					exit(1);
+				}
+			}
+
+			while (len > 0) {
+				vp = NULL;
+				my_len = nr_attr2vp(NULL, NULL,
+						     attr, len, &vp);
+				if (my_len < 0) {
+					nr_vp_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) {
+				p = output;
+				for (vp = head; vp != NULL; vp = vp->next) {
+					nr_vp_snprintf(p, sizeof(output) - (p - output), vp);
+					p += strlen(p);
+					
+					if (vp->next) {strcpy(p, ", ");
+						p += 2;
+					}
+				}
+				
+				nr_vp_free(&head);
+			} else if (my_len < 0) {
+				strcpy(output, nr_strerror(my_len));
+
+			} else { /* zero-length attribute */
+				*output = '\0';
+			}
+			continue;
+		}
+
+		if (strncmp(p, "walk ", 5) == 0) {
+			len = encode_hex(p + 5, data + 20, sizeof(data) - 20);
+
+			if (len == 0) {
+				fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, filename);
+				exit(1);
+			}
+
+			memset(data, 0, 20);
+			packet.data = data;
+			packet.length = len + 20;
+			packet.data[2] = ((len + 20) >> 8) & 0xff;
+			packet.data[3] = (len + 20) & 0xff;
+
+			*output = '\0';
+			p = output;
+
+			rcode = nr_packet_walk(&packet, &p, walk_callback);
+			if (rcode < 0) {
+				snprintf(output, sizeof(output), "%d", rcode);
+				continue;
+			}
+
+			if (*output) output[strlen(output) - 1] = '\0';
+			continue;
+		}
+
+		if (strncmp(p, "$INCLUDE ", 9) == 0) {
+			p += 9;
+			while (isspace((int) *p)) p++;
+
+			process_file(p);
+			continue;
+		}
+
+		if (strncmp(p, "secret ", 7) == 0) {
+			strlcpy(secret, p + 7, sizeof(secret));
+			strlcpy(output, secret, sizeof(output));
+			continue;
+		}
+
+		if (strncmp(p, "code ", 5) == 0) {
+			packet_code = atoi(p + 5);
+			snprintf(output, sizeof(output), "%u", packet_code);
+			continue;
+		}
+
+		if (strncmp(p, "sign ", 5) == 0) {
+			len = encode_hex(p + 5, data + 20, sizeof(data) - 20);
+			if (len == 0) {
+				fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, filename);
+				exit(1);
+			}
+
+			memset(&packet, 0, sizeof(packet));
+			packet.secret = secret;
+			packet.sizeof_secret = strlen(secret);
+			packet.code = packet_code;
+			packet.id = packet_id;
+			memcpy(packet.vector, packet_vector, 16);
+			packet.data = data;
+			packet.length = len + 20;
+
+			/*
+			 *	Hack encode the packet.
+			 */
+			packet.data[0] = packet_code;
+			packet.data[1] = packet_id;
+			packet.data[2] = ((len + 20) >> 8) & 0xff;
+			packet.data[3] = (len + 20) & 0xff;
+			memcpy(packet.data + 4, packet_vector, 16);
+
+			rcode = nr_packet_sign(&packet, NULL);
+			if (rcode < 0) {
+				snprintf(output, sizeof(output), "%d", rcode);
+				continue;
+			}
+
+			memcpy(data, packet.vector, sizeof(packet.vector));
+			outlen = sizeof(packet.vector);
+			goto print_hex;
+		}
+
+		fprintf(stderr, "Unknown input at line %d of %s\n",
+			lineno, filename);
+		exit(1);
+	}
+
+	if (fp != stdin) fclose(fp);
+}
+
+int main(int argc, char *argv[])
+{
+	int c;
+
+	if (argc < 2) {
+		process_file("-");
+		
+	} else {
+		process_file(argv[1]);
+	}
+
+	return 0;
+}
diff --git a/lib/radius/tests/rfc.txt b/lib/radius/tests/rfc.txt
new file mode 100644
index 0000000..d8bd613
--- /dev/null
+++ b/lib/radius/tests/rfc.txt
@@ -0,0 +1,144 @@
+#  All attribute lengths are implicit, and are calculated automatically
+#
+#  Input is of the form:
+#
+#	WORD ...
+#
+#  The WORD is a keyword which indicates the format of the following text.
+#  WORD is one of:
+#
+#	raw - read the grammar defined below, and encode an attribute.
+#	      The grammer supports a trivial way of describing RADIUS
+#	      attributes, without reference to dictionaries or fancy
+#	      parsers
+#
+#	encode - reads "Attribute-Name = value", encodes it, and prints
+#	         the result as text.
+#		use "-" to encode the output of the last command
+#
+#	decode - reads hex, and decodes it "Attribute-Name = value"
+#		use "-" to decode the output of the last command
+#
+#	data - the expected output of the previous command, in ASCII form.
+#	       if the actual command output is different, an error message
+#	       is produced, and the program terminates.
+#
+#
+#  The "raw" input satisfies the following grammar:
+#
+#	Identifier = 1*DIGIT *( "." 1*DIGIT )
+#
+#	HEXCHAR = HEXDIG HEXDIG 
+#
+#	STRING = DQUOTE *CHAR DQUOTE
+#
+#	TLV = "{" 1*DIGIT DATA "}"
+#
+#	DATA = 1*HEXCHAR / 1*TLV / STRING
+#
+#	LINE = Identifier DATA
+#
+#  The "Identifier" is a RADIUS attribute identifier, as given in the draft.
+#
+#	e.g.	1		for User-Name
+#		26.9.1		Vendor-Specific, Cisco, Cisco-AVPAir
+#		241.1		Extended Attribute, number 1
+#		241.2.3		Extended Attribute 2, data type TLV, TLV type 3
+#		etc.
+#
+#  The "DATA" portion is the contents of the RADIUS Attribute.
+#
+#		123456789abcdef hex string
+#		12 34 56 ab	with spaces for clarity
+#		"hello"	 	Text string
+#		{ 1 abcdef }	TLV, TLV-Type 1, data "abcdef"
+#
+#  TLVs can be nested:
+#
+#	{ tlv-type { tlv-type data } }		{ 3 { 4 01020304 } }
+#
+#  TLVs can be concatencated
+#
+#	{tlv-type data } { tlv-type data}	{ 3 040506 } { 8 aabbcc }
+#
+#  The "raw" data is encoded without reference to dictionaries.  Any
+#  valid string is parsed to a RADIUS attribute.  The resulting RADIUS
+#  attribute *may not* be correctly formatted to the relevant RADIUS
+#  specifications.  i.e. you can use this tool to create attribute 1
+#  (User-Name), which is encoded as a series of TLVs.  That's up to you.
+#
+#  The purpose of the "raw" command is to have a simple way of encoding
+#  attributes which is independent of any dictionaries or packet processing
+#  routines.
+#
+#  The output data is the hex version of the encoded attribute.
+#
+
+encode User-Name = bob
+data 01 05 62 6f 62
+
+decode -
+data User-Name = "bob"
+
+decode 01 05 62 6f 62
+data User-Name = "bob"
+
+#
+#  The Type/Length is OK, but the attribute data is of the wrong size.
+#
+decode 04 04 ab cd
+data Attr-4 = 0xabcd
+
+#  Zero-length attributes
+decode 01 02
+data 
+
+# don't encode zero-length attributes
+#encode User-Name = ""
+#data 
+
+# except for CUI.  Thank you, WiMAX!
+decode 59 02
+data Chargeable-User-Identity = ""
+
+# Hah! Thought you had it figured out, didn't you?
+#encode -
+#data 59 02
+
+encode NAS-Port = 10
+data 05 06 00 00 00 0a
+
+decode -
+data NAS-Port = 10
+
+walk 05 06 00 00 00 0a
+data v0 a5 l4
+
+walk 05 06 00 00 00 0a 02 06 00 00 00 0a
+data v0 a5 l4,v0 a2 l4
+
+walk 1a 0c 00 00 00 01 05 06 00 00 00 0a
+data v1 a5 l4
+
+walk 1a 12 00 00 00 01 05 06 00 00 00 0a 03 06 00 00 00 0a
+data v1 a5 l4,v1 a3 l4
+
+# Access-Request, code 1, authentication vector of zero
+sign 05 06 00 00 00 0a
+data 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+code 4
+
+sign 05 06 00 00 00 0a
+data 62 63 f1 db 80 70 a6 64 37 31 63 e4 aa 95 5a 68
+
+sign 05 06 00 00 00 0a
+data 62 63 f1 db 80 70 a6 64 37 31 63 e4 aa 95 5a 68
+
+secret hello
+sign 05 06 00 00 00 0a
+data 69 20 c0 b9 e1 2f 12 54 9f 92 16 5e f4 64 9b fd
+
+secret testing123
+sign 05 06 00 00 00 0a
+data 62 63 f1 db 80 70 a6 64 37 31 63 e4 aa 95 5a 68
-- 
cgit v1.1