From 5066473d411d6a474af3393d1b62a58ee3313861 Mon Sep 17 00:00:00 2001
From: Denis Vlasenko <vda.linux@googlemail.com>
Date: Tue, 27 Feb 2007 21:15:08 +0000
Subject: [PATCH] udhcp: optionally support RFC3397 (by Gabriel L. Somlo
 <somlo@cmu.edu>)

---
 networking/udhcp/Config.in |  8 ++++++++
 networking/udhcp/Kbuild    | 19 ++++++++++---------
 networking/udhcp/files.c   | 23 +++++++++++++++++++++++
 networking/udhcp/options.c |  8 +++++++-
 networking/udhcp/options.h |  7 +++++++
 networking/udhcp/script.c  | 33 ++++++++++++++++++++++-----------
 6 files changed, 77 insertions(+), 21 deletions(-)

diff --git a/networking/udhcp/Config.in b/networking/udhcp/Config.in
index f633473eb..9dd02c497 100644
--- a/networking/udhcp/Config.in
+++ b/networking/udhcp/Config.in
@@ -65,3 +65,11 @@ config FEATURE_UDHCP_DEBUG
 	  the background.
 
 	  See http://udhcp.busybox.net for further details.
+
+config FEATURE_RFC3397
+	bool "Support for RFC3397 domain search (experimental)"
+	default n
+	depends on APP_UDHCPD || APP_UDHCPC
+	help
+	  If selected, both client and server will support passing of domain
+	  search lists via option 119, specified in RFC3397.
diff --git a/networking/udhcp/Kbuild b/networking/udhcp/Kbuild
index dc2c01f61..57d2511f0 100644
--- a/networking/udhcp/Kbuild
+++ b/networking/udhcp/Kbuild
@@ -6,13 +6,14 @@
 #
 
 lib-y:=
-lib-$(CONFIG_APP_UDHCPC)	+= common.o options.o packet.o pidfile.o \
-				   signalpipe.o socket.o
-lib-$(CONFIG_APP_UDHCPD)	+= common.o options.o packet.o pidfile.o \
-				   signalpipe.o socket.o
-lib-$(CONFIG_APP_UDHCPC)	+= dhcpc.o clientpacket.o clientsocket.o \
-				   script.o
-lib-$(CONFIG_APP_UDHCPD)	+= dhcpd.o arpping.o files.o leases.o \
-				   serverpacket.o static_leases.o
-lib-$(CONFIG_APP_DUMPLEASES)	+= dumpleases.o
+lib-$(CONFIG_APP_UDHCPC)        += common.o options.o packet.o pidfile.o \
+                                   signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPD)        += common.o options.o packet.o pidfile.o \
+                                   signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPC)        += dhcpc.o clientpacket.o clientsocket.o \
+                                   script.o
+lib-$(CONFIG_APP_UDHCPD)        += dhcpd.o arpping.o files.o leases.o \
+                                   serverpacket.o static_leases.o
+lib-$(CONFIG_APP_DUMPLEASES)    += dumpleases.o
 lib-$(CONFIG_APP_DHCPRELAY)     += dhcprelay.o
+lib-$(CONFIG_FEATURE_RFC3397)   += domain_codec.o
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
index e35f50a17..ab6f4a3e4 100644
--- a/networking/udhcp/files.c
+++ b/networking/udhcp/files.c
@@ -104,6 +104,12 @@ static void attach_option(struct option_set **opt_list,
 	if (!existing) {
 		DEBUG("Attaching option %s to list", option->name);
 
+#if ENABLE_FEATURE_RFC3397
+		if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+			/* reuse buffer and length for RFC1035-formatted string */
+			buffer = dname_enc(NULL, 0, buffer, &length);
+#endif
+
 		/* make a new option */
 		new = xmalloc(sizeof(struct option_set));
 		new->data = xmalloc(length + 2);
@@ -117,12 +123,22 @@ static void attach_option(struct option_set **opt_list,
 
 		new->next = *curr;
 		*curr = new;
+#if ENABLE_FEATURE_RFC3397
+		if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+			free(buffer);
+#endif
 		return;
 	}
 
 	/* add it to an existing option */
 	DEBUG("Attaching option %s to existing member of list", option->name);
 	if (option->flags & OPTION_LIST) {
+#if ENABLE_FEATURE_RFC3397
+		if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+			/* reuse buffer and length for RFC1035-formatted string */
+			buffer = dname_enc(existing->data + 2,
+					existing->data[OPT_LEN], buffer, &length);
+#endif
 		if (existing->data[OPT_LEN] + length <= 255) {
 			existing->data = xrealloc(existing->data,
 					existing->data[OPT_LEN] + length + 3);
@@ -137,6 +153,10 @@ static void attach_option(struct option_set **opt_list,
 			memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
 			existing->data[OPT_LEN] += length;
 		} /* else, ignore the data, we could put this in a second option in the future */
+#if ENABLE_FEATURE_RFC3397
+		if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+			free(buffer);
+#endif
 	} /* else, ignore the new data */
 }
 
@@ -183,6 +203,9 @@ static int read_opt(const char *const_line, void *arg)
 			if (retval) retval = read_ip(val, buffer + 4);
 			break;
 		case OPTION_STRING:
+#if ENABLE_FEATURE_RFC3397
+		case OPTION_STR1035:
+#endif
 			length = strlen(val);
 			if (length > 0) {
 				if (length > 254) length = 254;
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c
index bda6efc7e..a58adb9a9 100644
--- a/networking/udhcp/options.c
+++ b/networking/udhcp/options.c
@@ -11,7 +11,7 @@
 
 /* supported options are easily added here */
 const struct dhcp_option dhcp_options[] = {
-	/* name[10]     flags                                   code */
+	/* name[12]     flags                                   code */
 	{"subnet",      OPTION_IP | OPTION_REQ,                 0x01},
 	{"timezone",    OPTION_S32,                             0x02},
 	{"router",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x03},
@@ -43,6 +43,9 @@ const struct dhcp_option dhcp_options[] = {
 	{"tftp",        OPTION_STRING,                          0x42},
 	{"bootfile",    OPTION_STRING,                          0x43},
 	{"userclass",   OPTION_STRING,                          0x4D},
+#if ENABLE_FEATURE_RFC3397
+	{"search",      OPTION_STR1035 | OPTION_LIST | OPTION_REQ, 0x77},
+#endif
 	/* MSIE's "Web Proxy Autodiscovery Protocol" support */
 	{"wpad",        OPTION_STRING,                          0xfc},
 	{"",            0x00,                                   0x00}
@@ -54,6 +57,9 @@ const unsigned char option_lengths[] = {
 	[OPTION_IP_PAIR] = 8,
 	[OPTION_BOOLEAN] = 1,
 	[OPTION_STRING] =  1,
+#if ENABLE_FEATURE_RFC3397
+	[OPTION_STR1035] = 1,
+#endif
 	[OPTION_U8] =      1,
 	[OPTION_U16] =     2,
 	[OPTION_S16] =     2,
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h
index 588504e5d..11f013fd4 100644
--- a/networking/udhcp/options.h
+++ b/networking/udhcp/options.h
@@ -9,6 +9,9 @@ enum {
 	OPTION_IP=1,
 	OPTION_IP_PAIR,
 	OPTION_STRING,
+#if ENABLE_FEATURE_RFC3397
+	OPTION_STR1035,	/* RFC1035 compressed domain name list */
+#endif
 	OPTION_BOOLEAN,
 	OPTION_U8,
 	OPTION_U16,
@@ -33,5 +36,9 @@ uint8_t *get_option(struct dhcpMessage *packet, int code);
 int end_option(uint8_t *optionptr);
 int add_option_string(uint8_t *optionptr, uint8_t *string);
 int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
+#if ENABLE_FEATURE_RFC3397
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre);
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen);
+#endif
 
 #endif
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c
index d2b0bb05b..dc8ff7a1c 100644
--- a/networking/udhcp/script.c
+++ b/networking/udhcp/script.c
@@ -19,6 +19,9 @@ static const int max_option_length[] = {
 	[OPTION_IP] =		sizeof("255.255.255.255 "),
 	[OPTION_IP_PAIR] =	sizeof("255.255.255.255 ") * 2,
 	[OPTION_STRING] =	1,
+#if ENABLE_FEATURE_RFC3397
+	[OPTION_STR1035] =	1,
+#endif
 	[OPTION_BOOLEAN] =	sizeof("yes "),
 	[OPTION_U8] =		sizeof("255 "),
 	[OPTION_U16] =		sizeof("65535 "),
@@ -53,21 +56,23 @@ static int mton(struct in_addr *mask)
 }
 
 
-/* Fill dest with the text of option 'option'. */
-static void fill_options(char *dest, uint8_t *option,
-			const struct dhcp_option *type_p)
+/* Allocate and fill with the text of option 'option'. */
+static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p)
 {
-	int type, optlen;
+	int len, type, optlen;
 	uint16_t val_u16;
 	int16_t val_s16;
 	uint32_t val_u32;
 	int32_t val_s32;
-	int len = option[OPT_LEN - 2];
-
-	dest += sprintf(dest, "%s=", type_p->name);
+	char *dest, *ret;
 
+	len = option[OPT_LEN - 2];
 	type = type_p->flags & TYPE_MASK;
 	optlen = option_lengths[type];
+
+	dest = ret = xmalloc(upper_length(len, type) + strlen(type_p->name) + 2);
+	dest += sprintf(ret, "%s=", type_p->name);
+
 	for (;;) {
 		switch (type) {
 		case OPTION_IP_PAIR:
@@ -103,13 +108,21 @@ static void fill_options(char *dest, uint8_t *option,
 		case OPTION_STRING:
 			memcpy(dest, option, len);
 			dest[len] = '\0';
-			return;	 /* Short circuit this case */
+			return ret;	 /* Short circuit this case */
+#if ENABLE_FEATURE_RFC3397
+		case OPTION_STR1035:
+			/* unpack option into dest; use ret for prefix (i.e., "optname=") */
+			dest = dname_dec(option, len, ret);
+			free(ret);
+			return dest;
+#endif
 		}
 		option += optlen;
 		len -= optlen;
 		if (len <= 0) break;
 		dest += sprintf(dest, " ");
 	}
+	return ret;
 }
 
 
@@ -155,9 +168,7 @@ static char **fill_envp(struct dhcpMessage *packet)
 		temp = get_option(packet, dhcp_options[i].code);
 		if (!temp)
 			continue;
-		envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
-			dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
-		fill_options(envp[j++], temp, &dhcp_options[i]);
+		envp[j++] = alloc_fill_opts(temp, &dhcp_options[i]);
 
 		/* Fill in a subnet bits option for things like /24 */
 		if (dhcp_options[i].code == DHCP_SUBNET) {