aboutsummaryrefslogtreecommitdiff
path: root/networking/udhcp/dhcpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/udhcp/dhcpc.c')
-rw-r--r--networking/udhcp/dhcpc.c301
1 files changed, 113 insertions, 188 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 5a1f8fd7a..50dfead63 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -159,61 +159,27 @@ static int mton(uint32_t mask)
159} 159}
160 160
161#if ENABLE_FEATURE_UDHCPC_SANITIZEOPT 161#if ENABLE_FEATURE_UDHCPC_SANITIZEOPT
162/* Check if a given label represents a valid DNS label 162/* Check if a given name represents a valid DNS name */
163 * Return pointer to the first character after the label 163/* See RFC1035, 2.3.1 */
164 * (NUL or dot) upon success, NULL otherwise.
165 * See RFC1035, 2.3.1
166 */
167/* We don't need to be particularly anal. For example, allowing _, hyphen 164/* We don't need to be particularly anal. For example, allowing _, hyphen
168 * at the end, or leading and trailing dots would be ok, since it 165 * at the end, or leading and trailing dots would be ok, since it
169 * can't be used for attacks. (Leading hyphen can be, if someone uses 166 * can't be used for attacks. (Leading hyphen can be, if someone uses cmd "$hostname"
170 * cmd "$hostname"
171 * in the script: then hostname may be treated as an option) 167 * in the script: then hostname may be treated as an option)
172 */ 168 */
173static const char *valid_domain_label(const char *label)
174{
175 unsigned char ch;
176 //unsigned pos = 0;
177
178 if (label[0] == '-')
179 return NULL;
180 for (;;) {
181 ch = *label;
182 if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
183 if (ch < '0' || ch > '9') {
184 if (ch == '\0' || ch == '.')
185 return label;
186 /* DNS allows only '-', but we are more permissive */
187 if (ch != '-' && ch != '_')
188 return NULL;
189 }
190 }
191 label++;
192 //pos++;
193 //Do we want this?
194 //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */
195 // return NULL;
196 }
197}
198
199/* Check if a given name represents a valid DNS name */
200/* See RFC1035, 2.3.1 */
201static int good_hostname(const char *name) 169static int good_hostname(const char *name)
202{ 170{
203 //const char *start = name; 171 if (*name == '-') /* Can't start with '-' */
204 172 return 0;
205 for (;;) { 173
206 name = valid_domain_label(name); 174 while (*name) {
207 if (!name) 175 unsigned char ch = *name++;
208 return 0; 176 if (!isalnum(ch))
209 if (!name[0]) 177 /* DNS allows only '-', but we are more permissive */
210 return 1; 178 if (ch != '-' && ch != '_' && ch != '.')
211 //Do we want this? 179 return 0;
212 //return ((name - start) < 1025); /* NS_MAXDNAME */ 180 // TODO: do we want to validate lengths against NS_MAXLABEL and NS_MAXDNAME?
213 name++;
214 if (*name == '\0')
215 return 1; // We allow trailing dot too
216 } 181 }
182 return 1;
217} 183}
218#else 184#else
219# define good_hostname(name) 1 185# define good_hostname(name) 1
@@ -242,9 +208,8 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
242 case OPTION_IP: 208 case OPTION_IP:
243 case OPTION_IP_PAIR: 209 case OPTION_IP_PAIR:
244 dest += sprint_nip(dest, "", option); 210 dest += sprint_nip(dest, "", option);
245 if (type == OPTION_IP) 211 if (type == OPTION_IP_PAIR)
246 break; 212 dest += sprint_nip(dest, "/", option + 4);
247 dest += sprint_nip(dest, "/", option + 4);
248 break; 213 break;
249// case OPTION_BOOLEAN: 214// case OPTION_BOOLEAN:
250// dest += sprintf(dest, *option ? "yes" : "no"); 215// dest += sprintf(dest, *option ? "yes" : "no");
@@ -346,7 +311,7 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
346 * IPv4MaskLen <= 32, 311 * IPv4MaskLen <= 32,
347 * 6rdPrefixLen <= 128, 312 * 6rdPrefixLen <= 128,
348 * 6rdPrefixLen + (32 - IPv4MaskLen) <= 128 313 * 6rdPrefixLen + (32 - IPv4MaskLen) <= 128
349 * (2nd condition need no check - it follows from 1st and 3rd). 314 * (2nd condition needs no check - it follows from 1st and 3rd).
350 * Else, return envvar with empty value ("optname=") 315 * Else, return envvar with empty value ("optname=")
351 */ 316 */
352 if (len >= (1 + 1 + 16 + 4) 317 if (len >= (1 + 1 + 16 + 4)
@@ -360,17 +325,12 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
360 /* 6rdPrefix */ 325 /* 6rdPrefix */
361 dest += sprint_nip6(dest, /* "", */ option); 326 dest += sprint_nip6(dest, /* "", */ option);
362 option += 16; 327 option += 16;
363 len -= 1 + 1 + 16 + 4; 328 len -= 1 + 1 + 16;
364 /* "+ 4" above corresponds to the length of IPv4 addr 329 *dest++ = ' ';
365 * we consume in the loop below */ 330 /* 6rdBRIPv4Address(es), use common IPv4 logic to process them */
366 while (1) { 331 type = OPTION_IP;
367 /* 6rdBRIPv4Address(es) */ 332 optlen = 4;
368 dest += sprint_nip(dest, " ", option); 333 continue;
369 option += 4;
370 len -= 4; /* do we have yet another 4+ bytes? */
371 if (len < 0)
372 break; /* no */
373 }
374 } 334 }
375 335
376 return ret; 336 return ret;
@@ -392,23 +352,18 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
392 */ 352 */
393 option++; 353 option++;
394 len--; 354 len--;
355 if (option[-1] == 1) {
356 /* use common IPv4 logic to process IP addrs */
357 type = OPTION_IP;
358 optlen = 4;
359 continue;
360 }
395 if (option[-1] == 0) { 361 if (option[-1] == 0) {
396 dest = dname_dec(option, len, ret); 362 dest = dname_dec(option, len, ret);
397 if (dest) { 363 if (dest) {
398 free(ret); 364 free(ret);
399 return dest; 365 return dest;
400 } 366 }
401 } else
402 if (option[-1] == 1) {
403 const char *pfx = "";
404 while (1) {
405 len -= 4;
406 if (len < 0)
407 break;
408 dest += sprint_nip(dest, pfx, option);
409 pfx = " ";
410 option += 4;
411 }
412 } 367 }
413 return ret; 368 return ret;
414#endif 369#endif
@@ -431,59 +386,78 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
431 return ret; 386 return ret;
432} 387}
433 388
434/* put all the parameters into the environment */ 389static void putenvp(llist_t **envp, char *new_opt)
435static char **fill_envp(struct dhcp_packet *packet) 390{
391 putenv(new_opt);
392 log2(" %s", new_opt);
393 llist_add_to(envp, new_opt);
394}
395
396static const char* get_optname(uint8_t code, const struct dhcp_optflag **dh)
436{ 397{
437 int envc; 398 /* Find the option:
438 int i; 399 * dhcp_optflags is sorted so we stop searching when dh->code >= code, which is faster
439 char **envp, **curr; 400 * than iterating over the entire array.
440 const char *opt_name; 401 * Options which don't have a match in dhcp_option_strings[], e.g DHCP_REQUESTED_IP,
441 uint8_t *temp; 402 * are located after the sorted array, so these entries will never be reached
442 uint8_t overload = 0; 403 * and they'll count as unknown options.
443
444#define BITMAP unsigned
445#define BBITS (sizeof(BITMAP) * 8)
446#define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1)))
447#define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS])
448 BITMAP found_opts[256 / BBITS];
449
450 memset(found_opts, 0, sizeof(found_opts));
451
452 /* We need 7 elements for:
453 * "interface=IFACE"
454 * "ip=N.N.N.N" from packet->yiaddr
455 * "giaddr=IP" from packet->gateway_nip (unless 0)
456 * "siaddr=IP" from packet->siaddr_nip (unless 0)
457 * "boot_file=FILE" from packet->file (unless overloaded)
458 * "sname=SERVER_HOSTNAME" from packet->sname (unless overloaded)
459 * terminating NULL
460 */ 404 */
461 envc = 7; 405 for (*dh = dhcp_optflags; (*dh)->code && (*dh)->code < code; (*dh)++)
462 /* +1 element for each option, +2 for subnet option: */ 406 continue;
463 if (packet) { 407
464 /* note: do not search for "pad" (0) and "end" (255) options */ 408 if ((*dh)->code == code)
465//TODO: change logic to scan packet _once_ 409 return nth_string(dhcp_option_strings, (*dh - dhcp_optflags));
466 for (i = 1; i < 255; i++) { 410
467 temp = udhcp_get_option(packet, i); 411 return NULL;
468 if (temp) { 412}
469 if (i == DHCP_OPTION_OVERLOAD) 413
470 overload |= *temp; 414/* put all the parameters into the environment */
471 else if (i == DHCP_SUBNET) 415static llist_t *fill_envp(struct dhcp_packet *packet)
472 envc++; /* for $mask */ 416{
473 envc++; 417 uint8_t *optptr;
474 /*if (i != DHCP_MESSAGE_TYPE)*/ 418 struct dhcp_scan_state scan_state;
475 FOUND_OPTS(i) |= BMASK(i); 419 char *new_opt;
476 } 420 llist_t *envp = NULL;
477 }
478 }
479 curr = envp = xzalloc(sizeof(envp[0]) * envc);
480 421
481 *curr = xasprintf("interface=%s", client_data.interface); 422 putenvp(&envp, xasprintf("interface=%s", client_data.interface));
482 putenv(*curr++);
483 423
484 if (!packet) 424 if (!packet)
485 return envp; 425 return envp;
486 426
427 init_scan_state(packet, &scan_state);
428
429 /* Iterate over the packet options.
430 * Handle each option based on whether it's an unknown / known option.
431 * There may be (although unlikely) duplicate options. For now, only the last
432 * appearing option will be stored in the environment, and all duplicates
433 * are freed properly.
434 * Long options may be implemented in the future (see RFC 3396) if needed.
435 */
436 while ((optptr = udhcp_scan_options(packet, &scan_state)) != NULL) {
437 const struct dhcp_optflag *dh;
438 const char *opt_name;
439 uint8_t code = optptr[OPT_CODE];
440 uint8_t len = optptr[OPT_LEN];
441 uint8_t *data = optptr + OPT_DATA;
442
443 opt_name = get_optname(code, &dh);
444 if (opt_name) {
445 new_opt = xmalloc_optname_optval(data, dh, opt_name);
446 if (code == DHCP_SUBNET && len == 4) {
447 uint32_t subnet;
448 putenvp(&envp, new_opt);
449 move_from_unaligned32(subnet, data);
450 new_opt = xasprintf("mask=%u", mton(subnet));
451 }
452 } else {
453 unsigned ofs;
454 new_opt = xmalloc(sizeof("optNNN=") + 1 + len*2);
455 ofs = sprintf(new_opt, "opt%u=", code);
456 bin2hex(new_opt + ofs, (char *)data, len)[0] = '\0';
457 }
458 putenvp(&envp, new_opt);
459 }
460
487 /* Export BOOTP fields. Fields we don't (yet?) export: 461 /* Export BOOTP fields. Fields we don't (yet?) export:
488 * uint8_t op; // always BOOTREPLY 462 * uint8_t op; // always BOOTREPLY
489 * uint8_t htype; // hardware address type. 1 = 10mb ethernet 463 * uint8_t htype; // hardware address type. 1 = 10mb ethernet
@@ -497,77 +471,31 @@ static char **fill_envp(struct dhcp_packet *packet)
497 * uint8_t chaddr[16]; // link-layer client hardware address (MAC) 471 * uint8_t chaddr[16]; // link-layer client hardware address (MAC)
498 */ 472 */
499 /* Most important one: yiaddr as $ip */ 473 /* Most important one: yiaddr as $ip */
500 *curr = xmalloc(sizeof("ip=255.255.255.255")); 474 new_opt = xmalloc(sizeof("ip=255.255.255.255"));
501 sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr); 475 sprint_nip(new_opt, "ip=", (uint8_t *) &packet->yiaddr);
502 putenv(*curr++); 476 putenvp(&envp, new_opt);
477
503 if (packet->siaddr_nip) { 478 if (packet->siaddr_nip) {
504 /* IP address of next server to use in bootstrap */ 479 /* IP address of next server to use in bootstrap */
505 *curr = xmalloc(sizeof("siaddr=255.255.255.255")); 480 new_opt = xmalloc(sizeof("siaddr=255.255.255.255"));
506 sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip); 481 sprint_nip(new_opt, "siaddr=", (uint8_t *) &packet->siaddr_nip);
507 putenv(*curr++); 482 putenvp(&envp, new_opt);
508 } 483 }
509 if (packet->gateway_nip) { 484 if (packet->gateway_nip) {
510 /* IP address of DHCP relay agent */ 485 /* IP address of DHCP relay agent */
511 *curr = xmalloc(sizeof("giaddr=255.255.255.255")); 486 new_opt = xmalloc(sizeof("giaddr=255.255.255.255"));
512 sprint_nip(*curr, "giaddr=", (uint8_t *) &packet->gateway_nip); 487 sprint_nip(new_opt, "giaddr=", (uint8_t *) &packet->gateway_nip);
513 putenv(*curr++); 488 putenvp(&envp, new_opt);
514 } 489 }
515 if (!(overload & FILE_FIELD) && packet->file[0]) { 490 if (!(scan_state.overload & FILE_FIELD) && packet->file[0]) {
516 /* watch out for invalid packets */ 491 /* watch out for invalid packets */
517 *curr = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file); 492 new_opt = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file);
518 putenv(*curr++); 493 putenvp(&envp, new_opt);
519 } 494 }
520 if (!(overload & SNAME_FIELD) && packet->sname[0]) { 495 if (!(scan_state.overload & SNAME_FIELD) && packet->sname[0]) {
521 /* watch out for invalid packets */ 496 /* watch out for invalid packets */
522 *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); 497 new_opt = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname);
523 putenv(*curr++); 498 putenvp(&envp, new_opt);
524 }
525
526 /* Export known DHCP options */
527 opt_name = dhcp_option_strings;
528 i = 0;
529 while (*opt_name) {
530 uint8_t code = dhcp_optflags[i].code;
531 BITMAP *found_ptr = &FOUND_OPTS(code);
532 BITMAP found_mask = BMASK(code);
533 if (!(*found_ptr & found_mask))
534 goto next;
535 *found_ptr &= ~found_mask; /* leave only unknown options */
536 temp = udhcp_get_option(packet, code);
537 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name);
538 putenv(*curr++);
539 if (code == DHCP_SUBNET && temp[-OPT_DATA + OPT_LEN] == 4) {
540 /* Subnet option: make things like "$ip/$mask" possible */
541 uint32_t subnet;
542 move_from_unaligned32(subnet, temp);
543 *curr = xasprintf("mask=%u", mton(subnet));
544 putenv(*curr++);
545 }
546 next:
547 opt_name += strlen(opt_name) + 1;
548 i++;
549 }
550 /* Export unknown options */
551 for (i = 0; i < 256;) {
552 BITMAP bitmap = FOUND_OPTS(i);
553 if (!bitmap) {
554 i += BBITS;
555 continue;
556 }
557 if (bitmap & BMASK(i)) {
558 unsigned len, ofs;
559
560 temp = udhcp_get_option(packet, i);
561 /* udhcp_get_option returns ptr to data portion,
562 * need to go back to get len
563 */
564 len = temp[-OPT_DATA + OPT_LEN];
565 *curr = xmalloc(sizeof("optNNN=") + 1 + len*2);
566 ofs = sprintf(*curr, "opt%u=", i);
567 *bin2hex(*curr + ofs, (void*) temp, len) = '\0';
568 putenv(*curr++);
569 }
570 i++;
571 } 499 }
572 500
573 return envp; 501 return envp;
@@ -576,7 +504,7 @@ static char **fill_envp(struct dhcp_packet *packet)
576/* Call a script with a par file and env vars */ 504/* Call a script with a par file and env vars */
577static void udhcp_run_script(struct dhcp_packet *packet, const char *name) 505static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
578{ 506{
579 char **envp, **curr; 507 llist_t *envp;
580 char *argv[3]; 508 char *argv[3];
581 509
582 envp = fill_envp(packet); 510 envp = fill_envp(packet);
@@ -588,11 +516,8 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
588 argv[2] = NULL; 516 argv[2] = NULL;
589 spawn_and_wait(argv); 517 spawn_and_wait(argv);
590 518
591 for (curr = envp; *curr; curr++) { 519 /* Free all allocated environment variables */
592 log2(" %s", *curr); 520 llist_free(envp, (void (*)(void *))bb_unsetenv_and_free);
593 bb_unsetenv_and_free(*curr);
594 }
595 free(envp);
596} 521}
597 522
598 523