udhcp: finish support of classless static routes (now the correct patch!)

Signed-off-by: Michael McTernan <Michael.McTernan.2001@cs.bris.ac.uk>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index cf6b1ca..ae0e0d3 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -57,13 +57,13 @@
 	{ OPTION_DNS_STRING | OPTION_LIST         , 0x77 }, /* DHCP_DOMAIN_SEARCH */
 	{ OPTION_SIP_SERVERS                      , 0x78 }, /* DHCP_SIP_SERVERS   */
 #endif
-	{ OPTION_STATIC_ROUTES                    , 0x79 }, /* DHCP_STATIC_ROUTES */
+	{ OPTION_STATIC_ROUTES | OPTION_LIST      , 0x79 }, /* DHCP_STATIC_ROUTES */
 #if ENABLE_FEATURE_UDHCP_8021Q
 	{ OPTION_U16                              , 0x84 }, /* DHCP_VLAN_ID       */
 	{ OPTION_U8                               , 0x85 }, /* DHCP_VLAN_PRIORITY */
 #endif
 	{ OPTION_6RD                              , 0xd4 }, /* DHCP_6RD           */
-	{ OPTION_STATIC_ROUTES                    , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
+	{ OPTION_STATIC_ROUTES | OPTION_LIST      , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
 	{ OPTION_STRING                           , 0xfc }, /* DHCP_WPAD          */
 
 	/* Options below have no match in dhcp_option_strings[],
@@ -123,8 +123,6 @@
 // is not handled yet by "string->option" conversion code:
 	"sipsrv" "\0"      /* DHCP_SIP_SERVERS    */
 #endif
-// doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES
-// is not handled yet by "string->option" conversion code:
 	"staticroutes" "\0"/* DHCP_STATIC_ROUTES  */
 #if ENABLE_FEATURE_UDHCP_8021Q
 	"vlanid" "\0"      /* DHCP_VLAN_ID        */
@@ -338,7 +336,8 @@
 	lsa = host_and_af2sockaddr(str, 0, AF_INET);
 	if (!lsa)
 		return 0;
-	*(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr;
+	/* arg maybe unaligned */
+	move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
 	free(lsa);
 	return 1;
 }
@@ -437,13 +436,14 @@
 int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
 {
 	struct option_set **opt_list = arg;
-	char *opt, *val, *endptr;
+	char *opt, *val;
 	char *str;
 	const struct dhcp_optflag *optflag;
 	struct dhcp_optflag bin_optflag;
 	unsigned optcode;
 	int retval, length;
-	char buffer[8] ALIGNED(4);
+	/* IP_PAIR needs 8 bytes, STATIC_ROUTES needs 9 max */
+	char buffer[9] ALIGNED(4);
 	uint16_t *result_u16 = (uint16_t *) buffer;
 	uint32_t *result_u32 = (uint32_t *) buffer;
 
@@ -501,34 +501,53 @@
 //			break;
 //		}
 		case OPTION_U8:
-			buffer[0] = strtoul(val, &endptr, 0);
-			retval = (endptr[0] == '\0');
+			buffer[0] = bb_strtou32(val, NULL, 0);
+			retval = (errno == 0);
 			break;
 		/* htonX are macros in older libc's, using temp var
 		 * in code below for safety */
 		/* TODO: use bb_strtoX? */
 		case OPTION_U16: {
-			unsigned long tmp = strtoul(val, &endptr, 0);
+			uint32_t tmp = bb_strtou32(val, NULL, 0);
 			*result_u16 = htons(tmp);
-			retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/);
+			retval = (errno == 0 /*&& tmp < 0x10000*/);
 			break;
 		}
 //		case OPTION_S16: {
-//			long tmp = strtol(val, &endptr, 0);
+//			long tmp = bb_strtoi32(val, NULL, 0);
 //			*result_u16 = htons(tmp);
-//			retval = (endptr[0] == '\0');
+//			retval = (errno == 0);
 //			break;
 //		}
 		case OPTION_U32: {
-			unsigned long tmp = strtoul(val, &endptr, 0);
+			uint32_t tmp = bb_strtou32(val, NULL, 0);
 			*result_u32 = htonl(tmp);
-			retval = (endptr[0] == '\0');
+			retval = (errno == 0);
 			break;
 		}
 		case OPTION_S32: {
-			long tmp = strtol(val, &endptr, 0);
+			int32_t tmp = bb_strtoi32(val, NULL, 0);
 			*result_u32 = htonl(tmp);
-			retval = (endptr[0] == '\0');
+			retval = (errno == 0);
+			break;
+		}
+		case OPTION_STATIC_ROUTES: {
+			/* Input: "a.b.c.d/m" */
+			/* Output: mask(1 byte),pfx(0-4 bytes),gw(4 bytes) */
+			unsigned mask;
+			char *slash = strchr(val, '/');
+			if (slash) {
+				*slash = '\0';
+				retval = udhcp_str2nip(val, buffer + 1);
+				buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
+				val = strtok(NULL, ", \t/-");
+				if (!val || mask > 32 || errno)
+					retval = 0;
+				if (retval) {
+					length = ((mask + 7) >> 3) + 5;
+					retval = udhcp_str2nip(val, buffer + (length - 4));
+				}
+			}
 			break;
 		}
 		case OPTION_BIN: /* handled in attach_option() */
@@ -539,7 +558,7 @@
 		}
 		if (retval)
 			attach_option(opt_list, optflag, opt, length);
-	} while (retval && optflag->flags & OPTION_LIST);
+	} while (retval && (optflag->flags & OPTION_LIST));
 
 	return retval;
 }