getopt32: remove applet_long_options
[oweals/busybox.git] / networking / udhcp / common.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
4  *
5  * Licensed under GPLv2, see file LICENSE in this source tree.
6  */
7 #include "common.h"
8
9 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
10 unsigned dhcp_verbose;
11 #endif
12
13 const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
14         0xff, 0xff, 0xff, 0xff, 0xff, 0xff
15 };
16
17 #if ENABLE_UDHCPC || ENABLE_UDHCPD
18 /* Supported options are easily added here.
19  * See RFC2132 for more options.
20  * OPTION_REQ: these options are requested by udhcpc (unless -o).
21  */
22 const struct dhcp_optflag dhcp_optflags[] = {
23         /* flags                                    code */
24         { OPTION_IP                   | OPTION_REQ, 0x01 }, /* DHCP_SUBNET        */
25         { OPTION_S32                              , 0x02 }, /* DHCP_TIME_OFFSET   */
26         { OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x03 }, /* DHCP_ROUTER        */
27 //      { OPTION_IP | OPTION_LIST                 , 0x04 }, /* DHCP_TIME_SERVER   */
28 //      { OPTION_IP | OPTION_LIST                 , 0x05 }, /* DHCP_NAME_SERVER   */
29         { OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x06 }, /* DHCP_DNS_SERVER    */
30 //      { OPTION_IP | OPTION_LIST                 , 0x07 }, /* DHCP_LOG_SERVER    */
31 //      { OPTION_IP | OPTION_LIST                 , 0x08 }, /* DHCP_COOKIE_SERVER */
32         { OPTION_IP | OPTION_LIST                 , 0x09 }, /* DHCP_LPR_SERVER    */
33         { OPTION_STRING_HOST          | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME     */
34         { OPTION_U16                              , 0x0d }, /* DHCP_BOOT_SIZE     */
35         { OPTION_STRING_HOST          | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME   */
36         { OPTION_IP                               , 0x10 }, /* DHCP_SWAP_SERVER   */
37         { OPTION_STRING                           , 0x11 }, /* DHCP_ROOT_PATH     */
38         { OPTION_U8                               , 0x17 }, /* DHCP_IP_TTL        */
39         { OPTION_U16                              , 0x1a }, /* DHCP_MTU           */
40 //TODO: why do we request DHCP_BROADCAST? Can't we assume that
41 //in the unlikely case it is different from typical N.N.255.255,
42 //server would let us know anyway?
43         { OPTION_IP                   | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST     */
44         { OPTION_IP_PAIR | OPTION_LIST            , 0x21 }, /* DHCP_ROUTES        */
45         { OPTION_STRING_HOST                      , 0x28 }, /* DHCP_NIS_DOMAIN    */
46         { OPTION_IP | OPTION_LIST                 , 0x29 }, /* DHCP_NIS_SERVER    */
47         { OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER    */
48         { OPTION_IP | OPTION_LIST                 , 0x2c }, /* DHCP_WINS_SERVER   */
49         { OPTION_U32                              , 0x33 }, /* DHCP_LEASE_TIME    */
50         { OPTION_IP                               , 0x36 }, /* DHCP_SERVER_ID     */
51         { OPTION_STRING                           , 0x38 }, /* DHCP_ERR_MESSAGE   */
52 //TODO: must be combined with 'sname' and 'file' handling:
53         { OPTION_STRING_HOST                      , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
54         { OPTION_STRING                           , 0x43 }, /* DHCP_BOOT_FILE     */
55 //TODO: not a string, but a set of LASCII strings:
56 //      { OPTION_STRING                           , 0x4D }, /* DHCP_USER_CLASS    */
57 #if ENABLE_FEATURE_UDHCP_RFC3397
58         { OPTION_DNS_STRING | OPTION_LIST         , 0x77 }, /* DHCP_DOMAIN_SEARCH */
59         { OPTION_SIP_SERVERS                      , 0x78 }, /* DHCP_SIP_SERVERS   */
60 #endif
61         { OPTION_STATIC_ROUTES | OPTION_LIST      , 0x79 }, /* DHCP_STATIC_ROUTES */
62 #if ENABLE_FEATURE_UDHCP_8021Q
63         { OPTION_U16                              , 0x84 }, /* DHCP_VLAN_ID       */
64         { OPTION_U8                               , 0x85 }, /* DHCP_VLAN_PRIORITY */
65 #endif
66         { OPTION_STRING                           , 0xd1 }, /* DHCP_PXE_CONF_FILE */
67         { OPTION_STRING                           , 0xd2 }, /* DHCP_PXE_PATH_PREFIX */
68         { OPTION_6RD                              , 0xd4 }, /* DHCP_6RD           */
69         { OPTION_STATIC_ROUTES | OPTION_LIST      , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
70         { OPTION_STRING                           , 0xfc }, /* DHCP_WPAD          */
71
72         /* Options below have no match in dhcp_option_strings[],
73          * are not passed to dhcpc scripts, and cannot be specified
74          * with "option XXX YYY" syntax in dhcpd config file.
75          * These entries are only used internally by udhcp[cd]
76          * to correctly encode options into packets.
77          */
78
79         { OPTION_IP                               , 0x32 }, /* DHCP_REQUESTED_IP  */
80         { OPTION_U8                               , 0x35 }, /* DHCP_MESSAGE_TYPE  */
81         { OPTION_U16                              , 0x39 }, /* DHCP_MAX_SIZE      */
82 //looks like these opts will work just fine even without these defs:
83 //      { OPTION_STRING                           , 0x3c }, /* DHCP_VENDOR        */
84 //      /* not really a string: */
85 //      { OPTION_STRING                           , 0x3d }, /* DHCP_CLIENT_ID     */
86         { 0, 0 } /* zeroed terminating entry */
87 };
88
89 /* Used for converting options from incoming packets to env variables
90  * for udhcpc script, and for setting options for udhcpd via
91  * "opt OPTION_NAME OPTION_VALUE" directives in udhcpd.conf file.
92  */
93 /* Must match dhcp_optflags[] order */
94 const char dhcp_option_strings[] ALIGN1 =
95         "subnet" "\0"      /* DHCP_SUBNET         */
96         "timezone" "\0"    /* DHCP_TIME_OFFSET    */
97         "router" "\0"      /* DHCP_ROUTER         */
98 //      "timesrv" "\0"     /* DHCP_TIME_SERVER    */
99 //      "namesrv" "\0"     /* DHCP_NAME_SERVER    */
100         "dns" "\0"         /* DHCP_DNS_SERVER     */
101 //      "logsrv" "\0"      /* DHCP_LOG_SERVER     */
102 //      "cookiesrv" "\0"   /* DHCP_COOKIE_SERVER  */
103         "lprsrv" "\0"      /* DHCP_LPR_SERVER     */
104         "hostname" "\0"    /* DHCP_HOST_NAME      */
105         "bootsize" "\0"    /* DHCP_BOOT_SIZE      */
106         "domain" "\0"      /* DHCP_DOMAIN_NAME    */
107         "swapsrv" "\0"     /* DHCP_SWAP_SERVER    */
108         "rootpath" "\0"    /* DHCP_ROOT_PATH      */
109         "ipttl" "\0"       /* DHCP_IP_TTL         */
110         "mtu" "\0"         /* DHCP_MTU            */
111         "broadcast" "\0"   /* DHCP_BROADCAST      */
112         "routes" "\0"      /* DHCP_ROUTES         */
113         "nisdomain" "\0"   /* DHCP_NIS_DOMAIN     */
114         "nissrv" "\0"      /* DHCP_NIS_SERVER     */
115         "ntpsrv" "\0"      /* DHCP_NTP_SERVER     */
116         "wins" "\0"        /* DHCP_WINS_SERVER    */
117         "lease" "\0"       /* DHCP_LEASE_TIME     */
118         "serverid" "\0"    /* DHCP_SERVER_ID      */
119         "message" "\0"     /* DHCP_ERR_MESSAGE    */
120         "tftp" "\0"        /* DHCP_TFTP_SERVER_NAME */
121         "bootfile" "\0"    /* DHCP_BOOT_FILE      */
122 //      "userclass" "\0"   /* DHCP_USER_CLASS     */
123 #if ENABLE_FEATURE_UDHCP_RFC3397
124         "search" "\0"      /* DHCP_DOMAIN_SEARCH  */
125 // doesn't work in udhcpd.conf since OPTION_SIP_SERVERS
126 // is not handled yet by "string->option" conversion code:
127         "sipsrv" "\0"      /* DHCP_SIP_SERVERS    */
128 #endif
129         "staticroutes" "\0"/* DHCP_STATIC_ROUTES  */
130 #if ENABLE_FEATURE_UDHCP_8021Q
131         "vlanid" "\0"      /* DHCP_VLAN_ID        */
132         "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY  */
133 #endif
134         "pxeconffile" "\0" /* DHCP_PXE_CONF_FILE  */
135         "pxepathprefix" "\0" /* DHCP_PXE_PATH_PREFIX  */
136         "ip6rd" "\0"       /* DHCP_6RD            */
137         "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
138         "wpad" "\0"        /* DHCP_WPAD           */
139         ;
140 #endif
141
142 /* Lengths of the option types in binary form.
143  * Used by:
144  * udhcp_str2optset: to determine how many bytes to allocate.
145  * xmalloc_optname_optval: to estimate string length
146  * from binary option length: (option[LEN] / dhcp_option_lengths[opt_type])
147  * is the number of elements, multiply it by one element's string width
148  * (len_of_option_as_string[opt_type]) and you know how wide string you need.
149  */
150 const uint8_t dhcp_option_lengths[] ALIGN1 = {
151         [OPTION_IP] =      4,
152         [OPTION_IP_PAIR] = 8,
153 //      [OPTION_BOOLEAN] = 1,
154         [OPTION_STRING] =  1,  /* ignored by udhcp_str2optset */
155         [OPTION_STRING_HOST] = 1,  /* ignored by udhcp_str2optset */
156 #if ENABLE_FEATURE_UDHCP_RFC3397
157         [OPTION_DNS_STRING] = 1,  /* ignored by both udhcp_str2optset and xmalloc_optname_optval */
158         [OPTION_SIP_SERVERS] = 1,
159 #endif
160         [OPTION_U8] =      1,
161         [OPTION_U16] =     2,
162 //      [OPTION_S16] =     2,
163         [OPTION_U32] =     4,
164         [OPTION_S32] =     4,
165         /* Just like OPTION_STRING, we use minimum length here */
166         [OPTION_STATIC_ROUTES] = 5,
167         [OPTION_6RD] =    12,  /* ignored by udhcp_str2optset */
168         /* The above value was chosen as follows:
169          * len_of_option_as_string[] for this option is >60: it's a string of the form
170          * "32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 ".
171          * Each additional ipv4 address takes 4 bytes in binary option and appends
172          * another "255.255.255.255 " 16-byte string. We can set [OPTION_6RD] = 4
173          * but this severely overestimates string length: instead of 16 bytes,
174          * it adds >60 for every 4 bytes in binary option.
175          * We cheat and declare here that option is in units of 12 bytes.
176          * This adds more than 60 bytes for every three ipv4 addresses - more than enough.
177          * (Even 16 instead of 12 should work, but let's be paranoid).
178          */
179 };
180
181
182 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
183 static void log_option(const char *pfx, const uint8_t *opt)
184 {
185         if (dhcp_verbose >= 2) {
186                 char buf[256 * 2 + 2];
187                 *bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
188                 bb_error_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
189         }
190 }
191 #else
192 # define log_option(pfx, opt) ((void)0)
193 #endif
194
195 unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings)
196 {
197         int n = index_in_strings(option_strings, name);
198         if (n >= 0)
199                 return n;
200
201         {
202                 char *buf, *d;
203                 const char *s;
204
205                 s = option_strings;
206                 while (*s)
207                         s += strlen(s) + 1;
208
209                 d = buf = xzalloc(s - option_strings);
210                 s = option_strings;
211                 while (!(*s == '\0' && s[1] == '\0')) {
212                         *d++ = (*s == '\0' ? ' ' : *s);
213                         s++;
214                 }
215                 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
216         }
217 }
218
219 /* Get an option with bounds checking (warning, result is not aligned) */
220 uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
221 {
222         uint8_t *optionptr;
223         int len;
224         int rem;
225         int overload = 0;
226         enum {
227                 FILE_FIELD101  = FILE_FIELD  * 0x101,
228                 SNAME_FIELD101 = SNAME_FIELD * 0x101,
229         };
230
231         /* option bytes: [code][len][data1][data2]..[dataLEN] */
232         optionptr = packet->options;
233         rem = sizeof(packet->options);
234         while (1) {
235                 if (rem <= 0) {
236  complain:
237                         bb_error_msg("bad packet, malformed option field");
238                         return NULL;
239                 }
240
241                 /* DHCP_PADDING and DHCP_END have no [len] byte */
242                 if (optionptr[OPT_CODE] == DHCP_PADDING) {
243                         rem--;
244                         optionptr++;
245                         continue;
246                 }
247                 if (optionptr[OPT_CODE] == DHCP_END) {
248                         if ((overload & FILE_FIELD101) == FILE_FIELD) {
249                                 /* can use packet->file, and didn't look at it yet */
250                                 overload |= FILE_FIELD101; /* "we looked at it" */
251                                 optionptr = packet->file;
252                                 rem = sizeof(packet->file);
253                                 continue;
254                         }
255                         if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
256                                 /* can use packet->sname, and didn't look at it yet */
257                                 overload |= SNAME_FIELD101; /* "we looked at it" */
258                                 optionptr = packet->sname;
259                                 rem = sizeof(packet->sname);
260                                 continue;
261                         }
262                         break;
263                 }
264
265                 if (rem <= OPT_LEN)
266                         goto complain; /* complain and return NULL */
267                 len = 2 + optionptr[OPT_LEN];
268                 rem -= len;
269                 if (rem < 0)
270                         goto complain; /* complain and return NULL */
271
272                 if (optionptr[OPT_CODE] == code) {
273                         log_option("option found", optionptr);
274                         return optionptr + OPT_DATA;
275                 }
276
277                 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
278                         if (len >= 3)
279                                 overload |= optionptr[OPT_DATA];
280                         /* fall through */
281                 }
282                 optionptr += len;
283         }
284
285         /* log3 because udhcpc uses it a lot - very noisy */
286         log3("option 0x%02x not found", code);
287         return NULL;
288 }
289
290 /* Return the position of the 'end' option (no bounds checking) */
291 int FAST_FUNC udhcp_end_option(uint8_t *optionptr)
292 {
293         int i = 0;
294
295         while (optionptr[i] != DHCP_END) {
296                 if (optionptr[i] != DHCP_PADDING)
297                         i += optionptr[i + OPT_LEN] + OPT_DATA-1;
298                 i++;
299         }
300         return i;
301 }
302
303 /* Add an option (supplied in binary form) to the options.
304  * Option format: [code][len][data1][data2]..[dataLEN]
305  */
306 void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
307 {
308         unsigned len;
309         uint8_t *optionptr = packet->options;
310         unsigned end = udhcp_end_option(optionptr);
311
312         len = OPT_DATA + addopt[OPT_LEN];
313         /* end position + (option code/length + addopt length) + end option */
314         if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) {
315 //TODO: learn how to use overflow option if we exhaust packet->options[]
316                 bb_error_msg("option 0x%02x did not fit into the packet",
317                                 addopt[OPT_CODE]);
318                 return;
319         }
320         log_option("adding option", addopt);
321         memcpy(optionptr + end, addopt, len);
322         optionptr[end + len] = DHCP_END;
323 }
324
325 #if ENABLE_UDHCPC || ENABLE_UDHCPD
326 /* Add an one to four byte option to a packet */
327 void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
328 {
329         const struct dhcp_optflag *dh;
330
331         for (dh = dhcp_optflags; dh->code; dh++) {
332                 if (dh->code == code) {
333                         uint8_t option[6], len;
334
335                         option[OPT_CODE] = code;
336                         len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
337                         option[OPT_LEN] = len;
338                         if (BB_BIG_ENDIAN)
339                                 data <<= 8 * (4 - len);
340                         /* Assignment is unaligned! */
341                         move_to_unaligned32(&option[OPT_DATA], data);
342                         udhcp_add_binary_option(packet, option);
343                         return;
344                 }
345         }
346
347         bb_error_msg("can't add option 0x%02x", code);
348 }
349 #endif
350
351 /* Find option 'code' in opt_list */
352 struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
353 {
354         while (opt_list && opt_list->data[OPT_CODE] < code)
355                 opt_list = opt_list->next;
356
357         if (opt_list && opt_list->data[OPT_CODE] == code)
358                 return opt_list;
359         return NULL;
360 }
361
362 /* Parse string to IP in network order */
363 int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
364 {
365         len_and_sockaddr *lsa;
366
367         lsa = host_and_af2sockaddr(str, 0, AF_INET);
368         if (!lsa)
369                 return 0;
370         /* arg maybe unaligned */
371         move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
372         free(lsa);
373         return 1;
374 }
375
376 /* udhcp_str2optset:
377  * Parse string option representation to binary form and add it to opt_list.
378  * Called to parse "udhcpc -x OPTNAME:OPTVAL"
379  * and to parse udhcpd.conf's "opt OPTNAME OPTVAL" directives.
380  */
381 /* helper for the helper */
382 static char *allocate_tempopt_if_needed(
383                 const struct dhcp_optflag *optflag,
384                 char *buffer,
385                 int *length_p)
386 {
387         char *allocated = NULL;
388         if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
389                 const char *end;
390                 allocated = xstrdup(buffer); /* more than enough */
391                 end = hex2bin(allocated, buffer, 255);
392                 if (errno)
393                         bb_error_msg_and_die("malformed hex string '%s'", buffer);
394                 *length_p = end - allocated;
395         }
396         return allocated;
397 }
398 /* helper: add an option to the opt_list */
399 static NOINLINE void attach_option(
400                 struct option_set **opt_list,
401                 const struct dhcp_optflag *optflag,
402                 char *buffer,
403                 int length)
404 {
405         struct option_set *existing;
406         char *allocated;
407
408         allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
409 #if ENABLE_FEATURE_UDHCP_RFC3397
410         if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
411                 /* reuse buffer and length for RFC1035-formatted string */
412                 allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
413         }
414 #endif
415
416         existing = udhcp_find_option(*opt_list, optflag->code);
417         if (!existing) {
418                 struct option_set *new, **curr;
419
420                 /* make a new option */
421                 log2("attaching option %02x to list", optflag->code);
422                 new = xmalloc(sizeof(*new));
423                 new->data = xmalloc(length + OPT_DATA);
424                 new->data[OPT_CODE] = optflag->code;
425                 new->data[OPT_LEN] = length;
426                 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
427
428                 curr = opt_list;
429                 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
430                         curr = &(*curr)->next;
431
432                 new->next = *curr;
433                 *curr = new;
434                 goto ret;
435         }
436
437         if (optflag->flags & OPTION_LIST) {
438                 unsigned old_len;
439
440                 /* add it to an existing option */
441                 log2("attaching option %02x to existing member of list", optflag->code);
442                 old_len = existing->data[OPT_LEN];
443                 if (old_len + length < 255) {
444                         /* actually 255 is ok too, but adding a space can overlow it */
445
446                         existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
447                         if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING
448                          || (optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING_HOST
449                         ) {
450                                 /* add space separator between STRING options in a list */
451                                 existing->data[OPT_DATA + old_len] = ' ';
452                                 old_len++;
453                         }
454                         memcpy(existing->data + OPT_DATA + old_len, (allocated ? allocated : buffer), length);
455                         existing->data[OPT_LEN] = old_len + length;
456                 } /* else, ignore the data, we could put this in a second option in the future */
457         } /* else, ignore the new data */
458
459  ret:
460         free(allocated);
461 }
462
463 int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg, const struct dhcp_optflag *optflags, const char *option_strings)
464 {
465         struct option_set **opt_list = arg;
466         char *opt, *val;
467         char *str;
468         const struct dhcp_optflag *optflag;
469         struct dhcp_optflag bin_optflag;
470         unsigned optcode;
471         int retval, length;
472         /* IP_PAIR needs 8 bytes, STATIC_ROUTES needs 9 max */
473         char buffer[9] ALIGNED(4);
474         uint16_t *result_u16 = (uint16_t *) buffer;
475         uint32_t *result_u32 = (uint32_t *) buffer;
476
477         /* Cheat, the only *const* str possible is "" */
478         str = (char *) const_str;
479         opt = strtok(str, " \t=");
480         if (!opt)
481                 return 0;
482
483         optcode = bb_strtou(opt, NULL, 0);
484         if (!errno && optcode < 255) {
485                 /* Raw (numeric) option code */
486                 bin_optflag.flags = OPTION_BIN;
487                 bin_optflag.code = optcode;
488                 optflag = &bin_optflag;
489         } else {
490                 optflag = &optflags[udhcp_option_idx(opt, option_strings)];
491         }
492
493         retval = 0;
494         do {
495                 val = strtok(NULL, ", \t");
496                 if (!val)
497                         break;
498                 length = dhcp_option_lengths[optflag->flags & OPTION_TYPE_MASK];
499                 retval = 0;
500                 opt = buffer; /* new meaning for variable opt */
501                 switch (optflag->flags & OPTION_TYPE_MASK) {
502                 case OPTION_IP:
503                         retval = udhcp_str2nip(val, buffer);
504                         break;
505                 case OPTION_IP_PAIR:
506                         retval = udhcp_str2nip(val, buffer);
507                         val = strtok(NULL, ", \t/-");
508                         if (!val)
509                                 retval = 0;
510                         if (retval)
511                                 retval = udhcp_str2nip(val, buffer + 4);
512                         break;
513                 case OPTION_STRING:
514                 case OPTION_STRING_HOST:
515 #if ENABLE_FEATURE_UDHCP_RFC3397
516                 case OPTION_DNS_STRING:
517 #endif
518                         length = strnlen(val, 254);
519                         if (length > 0) {
520                                 opt = val;
521                                 retval = 1;
522                         }
523                         break;
524 //              case OPTION_BOOLEAN: {
525 //                      static const char no_yes[] ALIGN1 = "no\0yes\0";
526 //                      buffer[0] = retval = index_in_strings(no_yes, val);
527 //                      retval++; /* 0 - bad; 1: "no" 2: "yes" */
528 //                      break;
529 //              }
530                 case OPTION_U8:
531                         buffer[0] = bb_strtou32(val, NULL, 0);
532                         retval = (errno == 0);
533                         break;
534                 /* htonX are macros in older libc's, using temp var
535                  * in code below for safety */
536                 /* TODO: use bb_strtoX? */
537                 case OPTION_U16: {
538                         uint32_t tmp = bb_strtou32(val, NULL, 0);
539                         *result_u16 = htons(tmp);
540                         retval = (errno == 0 /*&& tmp < 0x10000*/);
541                         break;
542                 }
543 //              case OPTION_S16: {
544 //                      long tmp = bb_strtoi32(val, NULL, 0);
545 //                      *result_u16 = htons(tmp);
546 //                      retval = (errno == 0);
547 //                      break;
548 //              }
549                 case OPTION_U32: {
550                         uint32_t tmp = bb_strtou32(val, NULL, 0);
551                         *result_u32 = htonl(tmp);
552                         retval = (errno == 0);
553                         break;
554                 }
555                 case OPTION_S32: {
556                         int32_t tmp = bb_strtoi32(val, NULL, 0);
557                         *result_u32 = htonl(tmp);
558                         retval = (errno == 0);
559                         break;
560                 }
561                 case OPTION_STATIC_ROUTES: {
562                         /* Input: "a.b.c.d/m" */
563                         /* Output: mask(1 byte),pfx(0-4 bytes),gw(4 bytes) */
564                         unsigned mask;
565                         char *slash = strchr(val, '/');
566                         if (slash) {
567                                 *slash = '\0';
568                                 retval = udhcp_str2nip(val, buffer + 1);
569                                 buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
570                                 val = strtok(NULL, ", \t/-");
571                                 if (!val || mask > 32 || errno)
572                                         retval = 0;
573                                 if (retval) {
574                                         length = ((mask + 7) >> 3) + 5;
575                                         retval = udhcp_str2nip(val, buffer + (length - 4));
576                                 }
577                         }
578                         break;
579                 }
580                 case OPTION_BIN: /* handled in attach_option() */
581                         opt = val;
582                         retval = 1;
583                 default:
584                         break;
585                 }
586                 if (retval)
587                         attach_option(opt_list, optflag, opt, length);
588         } while (retval && (optflag->flags & OPTION_LIST));
589
590         return retval;
591 }
592
593 /* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
594 int FAST_FUNC sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip)
595 {
596         char hexstrbuf[16 * 2];
597         bin2hex(hexstrbuf, (void*)ip, 16);
598         return sprintf(dest, /* "%s" */
599                 "%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s",
600                 /* pre, */
601                 hexstrbuf + 0 * 4,
602                 hexstrbuf + 1 * 4,
603                 hexstrbuf + 2 * 4,
604                 hexstrbuf + 3 * 4,
605                 hexstrbuf + 4 * 4,
606                 hexstrbuf + 5 * 4,
607                 hexstrbuf + 6 * 4,
608                 hexstrbuf + 7 * 4
609         );
610 }