udhcp: dname_dec may return NULL, account for that case

Other random cleanips included...

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c
index 21c1a7b..84a6765 100644
--- a/networking/udhcp/clientpacket.c
+++ b/networking/udhcp/clientpacket.c
@@ -166,7 +166,7 @@
 }
 
 
-/* Unicasts or broadcasts a DHCP renew message */
+/* Unicast or broadcast a DHCP renew message */
 int FAST_FUNC send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
 {
 	struct dhcp_packet packet;
@@ -186,7 +186,7 @@
 }
 
 
-/* Unicasts a DHCP release message */
+/* Unicast a DHCP release message */
 int FAST_FUNC send_release(uint32_t server, uint32_t ciaddr)
 {
 	struct dhcp_packet packet;
diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c
index 6f051c4..45354e7 100644
--- a/networking/udhcp/domain_codec.c
+++ b/networking/udhcp/domain_codec.c
@@ -25,16 +25,9 @@
  */
 char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
 {
-	const uint8_t *c;
-	int crtpos, retpos, depth, plen = 0, len = 0;
+	char *ret = ret; /* for compiler */
 	char *dst = NULL;
 
-	if (!cstr)
-		return NULL;
-
-	if (pre)
-		plen = strlen(pre);
-
 	/* We make two passes over the cstr string. First, we compute
 	 * how long the resulting string would be. Then we allocate a
 	 * new buffer of the required length, and fill it in with the
@@ -42,59 +35,71 @@
 	 * having to deal with requiring callers to supply their own
 	 * buffer, then having to check if it's sufficiently large, etc.
 	 */
-
-	while (!dst) {
-
-		if (len > 0) {	/* second pass? allocate dst buffer and copy pre */
-			dst = xmalloc(len + plen);
-			memcpy(dst, pre, plen);
-		}
+	while (1) {
+		/* note: "return NULL" below are leak-safe since
+		 * dst isn't yet allocated */
+		const uint8_t *c;
+		unsigned crtpos, retpos, depth, len;
 
 		crtpos = retpos = depth = len = 0;
-
 		while (crtpos < clen) {
 			c = cstr + crtpos;
 
-			if ((*c & NS_CMPRSFLGS) != 0) {	/* pointer */
-				if (crtpos + 2 > clen)		/* no offset to jump to? abort */
+			if (*c & NS_CMPRSFLGS) {
+				/* pointer */
+				if (crtpos + 2 > clen) /* no offset to jump to? abort */
 					return NULL;
-				if (retpos == 0)			/* toplevel? save return spot */
+				if (retpos == 0) /* toplevel? save return spot */
 					retpos = crtpos + 2;
 				depth++;
-				crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */
-			} else if (*c) {			/* label */
-				if (crtpos + *c + 1 > clen)		/* label too long? abort */
+				crtpos = ((c[0] & 0x3f) << 8) | (c[1] & 0xff); /* jump */
+			} else if (*c) {
+				/* label */
+				if (crtpos + *c + 1 > clen) /* label too long? abort */
 					return NULL;
 				if (dst)
-					memcpy(dst + plen + len, c + 1, *c);
+					memcpy(dst + len, c + 1, *c);
 				len += *c + 1;
 				crtpos += *c + 1;
 				if (dst)
-					*(dst + plen + len - 1) = '.';
-			} else {					/* null: end of current domain name */
-				if (retpos == 0) {			/* toplevel? keep going */
+					dst[len - 1] = '.';
+			} else {
+				/* null: end of current domain name */
+				if (retpos == 0) {
+					/* toplevel? keep going */
 					crtpos++;
-				} else {					/* return to toplevel saved spot */
+				} else {
+					/* return to toplevel saved spot */
 					crtpos = retpos;
 					retpos = depth = 0;
 				}
 				if (dst)
-					*(dst + plen + len - 1) = ' ';
+					dst[len - 1] = ' ';
 			}
 
-			if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */
-				len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */
+			if (depth > NS_MAXDNSRCH /* too many jumps? abort, it's a loop */
+			 || len > NS_MAXDNAME * NS_MAXDNSRCH /* result too long? abort */
+			) {
 				return NULL;
+			}
 		}
 
-		if (!len)			/* expanded string has 0 length? abort */
+		if (!len) /* expanded string has 0 length? abort */
 			return NULL;
 
-		if (dst)
-			*(dst + plen + len - 1) = '\0';
+		if (!dst) { /* first pass? */
+			/* allocate dst buffer and copy pre */
+			unsigned plen = strlen(pre);
+			ret = dst = xmalloc(plen + len);
+			memcpy(dst, pre, plen);
+			dst += plen;
+		} else {
+			dst[len - 1] = '\0';
+			break;
+		}
 	}
 
-	return dst;
+	return ret;
 }
 
 /* Convert a domain name (src) from human-readable "foo.blah.com" format into
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c
index 0f17feb..76d6e37 100644
--- a/networking/udhcp/options.c
+++ b/networking/udhcp/options.c
@@ -115,7 +115,7 @@
 	[OPTION_U16] =     2,
 	[OPTION_S16] =     2,
 	[OPTION_U32] =     4,
-	[OPTION_S32] =     4
+	[OPTION_S32] =     4,
 };
 
 
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h
index 8c80485..9e62431 100644
--- a/networking/udhcp/options.h
+++ b/networking/udhcp/options.h
@@ -19,11 +19,13 @@
 	OPTION_U16,
 	OPTION_S16,
 	OPTION_U32,
-	OPTION_S32
+	OPTION_S32,
 };
 
-#define OPTION_REQ      0x10 /* have the client request this option */
-#define OPTION_LIST     0x20 /* There can be a list of 1 or more of these */
+/* Client requests this option by default */
+#define OPTION_REQ      0x10
+/* There can be a list of 1 or more of these */
+#define OPTION_LIST     0x20
 
 /*****************************************************************/
 /* Do not modify below here unless you know what you are doing!! */
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c
index 7ebef35..94dabf4 100644
--- a/networking/udhcp/script.c
+++ b/networking/udhcp/script.c
@@ -14,7 +14,7 @@
 
 
 /* get a rough idea of how long an option will be (rounding up...) */
-static const uint8_t max_option_length[] = {
+static const uint8_t len_of_option_as_string[] = {
 	[OPTION_IP] =		sizeof("255.255.255.255 "),
 	[OPTION_IP_PAIR] =	sizeof("255.255.255.255 ") * 2,
 	[OPTION_STRING] =	1,
@@ -30,17 +30,10 @@
 };
 
 
-static inline int upper_length(int length, int opt_index)
-{
-	return max_option_length[opt_index] *
-		(length / dhcp_option_lengths[opt_index]);
-}
-
-
 /* note: ip is a pointer to an IP in network order, possibly misaliged */
 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
 {
-	return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
+	return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
 }
 
 
@@ -57,9 +50,10 @@
 }
 
 
-/* Allocate and fill with the text of option 'option'. */
-static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
+/* Create "opt_name=opt_value" string */
+static char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
 {
+	unsigned upper_length;
 	int len, type, optlen;
 	uint16_t val_u16;
 	int16_t val_s16;
@@ -67,14 +61,16 @@
 	int32_t val_s32;
 	char *dest, *ret;
 
-	len = option[OPT_LEN - 2];
+	/* option points to OPT_DATA, need to go back and get OPT_LEN */
+	len = option[OPT_LEN - OPT_DATA];
 	type = type_p->flags & TYPE_MASK;
 	optlen = dhcp_option_lengths[type];
+	upper_length = len_of_option_as_string[type] * (len / optlen);
 
-	dest = ret = xmalloc(upper_length(len, type) + strlen(opt_name) + 2);
+	dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
 	dest += sprintf(ret, "%s=", opt_name);
 
-	for (;;) {
+	while (len >= optlen) {
 		switch (type) {
 		case OPTION_IP_PAIR:
 			dest += sprint_nip(dest, "", option);
@@ -114,15 +110,20 @@
 		case OPTION_STR1035:
 			/* unpack option into dest; use ret for prefix (i.e., "optname=") */
 			dest = dname_dec(option, len, ret);
-			free(ret);
-			return dest;
+			if (dest) {
+				free(ret);
+				return dest;
+			}
+			/* error. return "optname=" string */
+			return ret;
 #endif
 		}
 		option += optlen;
 		len -= optlen;
 		if (len <= 0)
 			break;
-		dest += sprintf(dest, " ");
+		*dest++ = ' ';
+		*dest = '\0';
 	}
 	return ret;
 }
@@ -174,7 +175,7 @@
 		temp = get_option(packet, dhcp_options[i].code);
 		if (!temp)
 			goto next;
-		*curr = alloc_fill_opts(temp, &dhcp_options[i], opt_name);
+		*curr = xmalloc_optname_optval(temp, &dhcp_options[i], opt_name);
 		putenv(*curr++);
 
 		/* Fill in a subnet bits option for things like /24 */