xref: /minix3/external/bsd/dhcpcd/dist/dhcp-common.c (revision 9f20bfa6c4c442e2e798d91b11c2a5f8d6833a41)
1 #include <sys/cdefs.h>
2  __RCSID("$NetBSD: dhcp-common.c,v 1.10 2015/07/09 10:15:34 roy Exp $");
3 
4 /*
5  * dhcpcd - DHCP client daemon
6  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
7  * All rights reserved
8 
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/utsname.h>
32 
33 #include <arpa/nameser.h>
34 
35 #include <ctype.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <inttypes.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "config.h"
44 
45 #include "common.h"
46 #include "dhcp-common.h"
47 #include "dhcp.h"
48 #include "if.h"
49 #include "ipv6.h"
50 
51 /* Support very old arpa/nameser.h as found in OpenBSD */
52 #ifndef NS_MAXDNAME
53 #define NS_MAXCDNAME MAXCDNAME
54 #define NS_MAXDNAME MAXDNAME
55 #define NS_MAXLABEL MAXLABEL
56 #endif
57 
58 void
dhcp_print_option_encoding(const struct dhcp_opt * opt,int cols)59 dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
60 {
61 
62 	while (cols < 40) {
63 		putchar(' ');
64 		cols++;
65 	}
66 	putchar('\t');
67 	if (opt->type & EMBED)
68 		printf(" embed");
69 	if (opt->type & ENCAP)
70 		printf(" encap");
71 	if (opt->type & INDEX)
72 		printf(" index");
73 	if (opt->type & ARRAY)
74 		printf(" array");
75 	if (opt->type & UINT8)
76 		printf(" byte");
77 	else if (opt->type & UINT16)
78 		printf(" uint16");
79 	else if (opt->type & SINT16)
80 		printf(" sint16");
81 	else if (opt->type & UINT32)
82 		printf(" uint32");
83 	else if (opt->type & SINT32)
84 		printf(" sint32");
85 	else if (opt->type & ADDRIPV4)
86 		printf(" ipaddress");
87 	else if (opt->type & ADDRIPV6)
88 		printf(" ip6address");
89 	else if (opt->type & FLAG)
90 		printf(" flag");
91 	else if (opt->type & BITFLAG)
92 		printf(" bitflags");
93 	else if (opt->type & RFC1035)
94 		printf(" domain");
95 	else if (opt->type & DOMAIN)
96 		printf(" dname");
97 	else if (opt->type & ASCII)
98 		printf(" ascii");
99 	else if (opt->type & RAW)
100 		printf(" raw");
101 	else if (opt->type & BINHEX)
102 		printf(" binhex");
103 	else if (opt->type & STRING)
104 		printf(" string");
105 	if (opt->type & RFC3361)
106 		printf(" rfc3361");
107 	if (opt->type & RFC3442)
108 		printf(" rfc3442");
109 	if (opt->type & RFC5969)
110 		printf(" rfc5969");
111 	if (opt->type & REQUEST)
112 		printf(" request");
113 	if (opt->type & NOREQ)
114 		printf(" norequest");
115 	putchar('\n');
116 }
117 
118 struct dhcp_opt *
vivso_find(uint32_t iana_en,const void * arg)119 vivso_find(uint32_t iana_en, const void *arg)
120 {
121 	const struct interface *ifp;
122 	size_t i;
123 	struct dhcp_opt *opt;
124 
125 	ifp = arg;
126 	for (i = 0, opt = ifp->options->vivso_override;
127 	    i < ifp->options->vivso_override_len;
128 	    i++, opt++)
129 		if (opt->option == iana_en)
130 			return opt;
131 	for (i = 0, opt = ifp->ctx->vivso;
132 	    i < ifp->ctx->vivso_len;
133 	    i++, opt++)
134 		if (opt->option == iana_en)
135 			return opt;
136 	return NULL;
137 }
138 
139 ssize_t
dhcp_vendor(char * str,size_t len)140 dhcp_vendor(char *str, size_t len)
141 {
142 	struct utsname utn;
143 	char *p;
144 	int l;
145 
146 	if (uname(&utn) != 0)
147 		return (ssize_t)snprintf(str, len, "%s-%s",
148 		    PACKAGE, VERSION);
149 	p = str;
150 	l = snprintf(p, len,
151 	    "%s-%s:%s-%s:%s", PACKAGE, VERSION,
152 	    utn.sysname, utn.release, utn.machine);
153 	if (l == -1 || (size_t)(l + 1) > len)
154 		return -1;
155 	p += l;
156 	len -= (size_t)l;
157 	l = if_machinearch(p, len);
158 	if (l == -1 || (size_t)(l + 1) > len)
159 		return -1;
160 	p += l;
161 	return p - str;
162 }
163 
164 int
make_option_mask(const struct dhcp_opt * dopts,size_t dopts_len,const struct dhcp_opt * odopts,size_t odopts_len,uint8_t * mask,const char * opts,int add)165 make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
166     const struct dhcp_opt *odopts, size_t odopts_len,
167     uint8_t *mask, const char *opts, int add)
168 {
169 	char *token, *o, *p;
170 	const struct dhcp_opt *opt;
171 	int match, e;
172 	unsigned int n;
173 	size_t i;
174 
175 	if (opts == NULL)
176 		return -1;
177 	o = p = strdup(opts);
178 	while ((token = strsep(&p, ", "))) {
179 		if (*token == '\0')
180 			continue;
181 		match = 0;
182 		for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
183 			if (strcmp(opt->var, token) == 0)
184 				match = 1;
185 			else {
186 				n = (unsigned int)strtou(token, NULL, 0,
187 				    0, UINT_MAX, &e);
188 				if (e == 0 && opt->option == n)
189 					match = 1;
190 			}
191 			if (match)
192 				break;
193 		}
194 		if (match == 0) {
195 			for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
196 				if (strcmp(opt->var, token) == 0)
197 				        match = 1;
198 				else {
199 					n = (unsigned int)strtou(token, NULL, 0,
200 					    0, UINT_MAX, &e);
201 					if (e == 0 && opt->option == n)
202 						match = 1;
203 				}
204 				if (match)
205 					break;
206 			}
207 		}
208 		if (!match || !opt->option) {
209 			free(o);
210 			errno = ENOENT;
211 			return -1;
212 		}
213 		if (add == 2 && !(opt->type & ADDRIPV4)) {
214 			free(o);
215 			errno = EINVAL;
216 			return -1;
217 		}
218 		if (add == 1 || add == 2)
219 			add_option_mask(mask, opt->option);
220 		else
221 			del_option_mask(mask, opt->option);
222 	}
223 	free(o);
224 	return 0;
225 }
226 
227 size_t
encode_rfc1035(const char * src,uint8_t * dst)228 encode_rfc1035(const char *src, uint8_t *dst)
229 {
230 	uint8_t *p;
231 	uint8_t *lp;
232 	size_t len;
233 	uint8_t has_dot;
234 
235 	if (src == NULL || *src == '\0')
236 		return 0;
237 
238 	if (dst) {
239 		p = dst;
240 		lp = p++;
241 	}
242 	/* Silence bogus GCC warnings */
243 	else
244 		p = lp = NULL;
245 
246 	len = 1;
247 	has_dot = 0;
248 	for (; *src; src++) {
249 		if (*src == '\0')
250 			break;
251 		if (*src == '.') {
252 			/* Skip the trailing . */
253 			if (src[1] == '\0')
254 				break;
255 			has_dot = 1;
256 			if (dst) {
257 				*lp = (uint8_t)(p - lp - 1);
258 				if (*lp == '\0')
259 					return len;
260 				lp = p++;
261 			}
262 		} else if (dst)
263 			*p++ = (uint8_t)*src;
264 		len++;
265 	}
266 
267 	if (dst) {
268 		*lp = (uint8_t)(p - lp - 1);
269 		if (has_dot)
270 			*p++ = '\0';
271 	}
272 
273 	if (has_dot)
274 		len++;
275 
276 	return len;
277 }
278 
279 /* Decode an RFC1035 DNS search order option into a space
280  * separated string. Returns length of string (including
281  * terminating zero) or zero on error. out may be NULL
282  * to just determine output length. */
283 ssize_t
decode_rfc1035(char * out,size_t len,const uint8_t * p,size_t pl)284 decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)
285 {
286 	const char *start;
287 	size_t start_len, l, count;
288 	const uint8_t *r, *q = p, *e;
289 	int hops;
290 	uint8_t ltype;
291 
292 	if (pl > NS_MAXCDNAME) {
293 		errno = E2BIG;
294 		return -1;
295 	}
296 
297 	count = 0;
298 	start = out;
299 	start_len = len;
300 	q = p;
301 	e = p + pl;
302 	while (q < e) {
303 		r = NULL;
304 		hops = 0;
305 		/* Check we are inside our length again in-case
306 		 * the name isn't fully qualified (ie, not terminated) */
307 		while (q < e && (l = (size_t)*q++)) {
308 			ltype = l & 0xc0;
309 			if (ltype == 0x80 || ltype == 0x40) {
310 				/* Currently reserved for future use as noted
311 				 * in RFC1035 4.1.4 as the 10 and 01
312 				 * combinations. */
313 				errno = ENOTSUP;
314 				return -1;
315 			}
316 			else if (ltype == 0xc0) { /* pointer */
317 				if (q == e) {
318 					errno = ERANGE;
319 					return -1;
320 				}
321 				l = (l & 0x3f) << 8;
322 				l |= *q++;
323 				/* save source of first jump. */
324 				if (!r)
325 					r = q;
326 				hops++;
327 				if (hops > 255) {
328 					errno = ERANGE;
329 					return -1;
330 				}
331 				q = p + l;
332 				if (q >= e) {
333 					errno = ERANGE;
334 					return -1;
335 				}
336 			} else {
337 				/* straightforward name segment, add with '.' */
338 				if (q + l > e) {
339 					errno = ERANGE;
340 					return -1;
341 				}
342 				count += l + 1;
343 				if (out) {
344 					if (l + 1 > len) {
345 						errno = ENOBUFS;
346 						return -1;
347 					}
348 					if (l + 1 > NS_MAXLABEL) {
349 						errno = EINVAL;
350 						return -1;
351 					}
352 					memcpy(out, q, l);
353 					out += l;
354 					*out++ = '.';
355 					len -= l;
356 					len--;
357 				}
358 				q += l;
359 			}
360 		}
361 		/* change last dot to space */
362 		if (out && out != start)
363 			*(out - 1) = ' ';
364 		if (r)
365 			q = r;
366 	}
367 
368 	/* change last space to zero terminator */
369 	if (out) {
370 		if (out != start)
371 			*(out - 1) = '\0';
372 		else if (start_len > 0)
373 			*out = '\0';
374 	}
375 
376 	if (count)
377 		/* Don't count the trailing NUL */
378 		count--;
379 	if (count > NS_MAXDNAME) {
380 		errno = E2BIG;
381 		return -1;
382 	}
383 	return (ssize_t)count;
384 }
385 
386 /* Check for a valid domain name as per RFC1123 with the exception of
387  * allowing - and _ (but not at start or end) as they seem to be widely used. */
388 static int
valid_domainname(char * lbl,int type)389 valid_domainname(char *lbl, int type)
390 {
391 	char *slbl, *lst;
392 	unsigned char c;
393 	int start, len, errset;
394 
395 	if (lbl == NULL || *lbl == '\0') {
396 		errno = EINVAL;
397 		return 0;
398 	}
399 
400 	slbl = lbl;
401 	lst = NULL;
402 	start = 1;
403 	len = errset = 0;
404 	for (;;) {
405 		c = (unsigned char)*lbl++;
406 		if (c == '\0')
407 			return 1;
408 		if (c == ' ') {
409 			if (lbl - 1 == slbl) /* No space at start */
410 				break;
411 			if (!(type & ARRAY))
412 				break;
413 			/* Skip to the next label */
414 			if (!start) {
415 				start = 1;
416 				lst = lbl - 1;
417 			}
418 			if (len)
419 				len = 0;
420 			continue;
421 		}
422 		if (c == '.') {
423 			if (*lbl == '.')
424 				break;
425 			len = 0;
426 			continue;
427 		}
428 		if (((c == '-' || c == '_') &&
429 		    !start && *lbl != ' ' && *lbl != '\0') ||
430 		    isalnum(c))
431 		{
432 			if (++len > NS_MAXLABEL) {
433 				errno = ERANGE;
434 				errset = 1;
435 				break;
436 			}
437 		} else
438 			break;
439 		if (start)
440 			start = 0;
441 	}
442 
443 	if (!errset)
444 		errno = EINVAL;
445 	if (lst) {
446 		/* At least one valid domain, return it */
447 		*lst = '\0';
448 		return 1;
449 	}
450 	return 0;
451 }
452 
453 /*
454  * Prints a chunk of data to a string.
455  * PS_SHELL goes as it is these days, it's upto the target to validate it.
456  * PS_SAFE has all non ascii and non printables changes to escaped octal.
457  */
458 static const char hexchrs[] = "0123456789abcdef";
459 ssize_t
print_string(char * dst,size_t len,int type,const uint8_t * data,size_t dl)460 print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
461 {
462 	char *odst;
463 	uint8_t c;
464 	const uint8_t *e;
465 	size_t bytes;
466 
467 	odst = dst;
468 	bytes = 0;
469 	e = data + dl;
470 
471 	while (data < e) {
472 		c = *data++;
473 		if (type & BINHEX) {
474 			if (dst) {
475 				if (len  == 0 || len == 1) {
476 					errno = ENOSPC;
477 					return -1;
478 				}
479 				*dst++ = hexchrs[(c & 0xF0) >> 4];
480 				*dst++ = hexchrs[(c & 0x0F)];
481 				len -= 2;
482 			}
483 			bytes += 2;
484 			continue;
485 		}
486 		if (type & ASCII && (!isascii(c))) {
487 			errno = EINVAL;
488 			break;
489 		}
490 		if (!(type & (ASCII | RAW | ESCSTRING | ESCFILE)) /* plain */ &&
491 		    (!isascii(c) && !isprint(c)))
492 		{
493 			errno = EINVAL;
494 			break;
495 		}
496 		if ((type & (ESCSTRING | ESCFILE) &&
497 		    (c == '\\' || !isascii(c) || !isprint(c))) ||
498 		    (type & ESCFILE && (c == '/' || c == ' ')))
499 		{
500 			errno = EINVAL;
501 			if (c == '\\') {
502 				if (dst) {
503 					if (len  == 0 || len == 1) {
504 						errno = ENOSPC;
505 						return -1;
506 					}
507 					*dst++ = '\\'; *dst++ = '\\';
508 					len -= 2;
509 				}
510 				bytes += 2;
511 				continue;
512 			}
513 			if (dst) {
514 				if (len < 5) {
515 					errno = ENOSPC;
516 					return -1;
517 				}
518 				*dst++ = '\\';
519 		                *dst++ = (char)(((c >> 6) & 03) + '0');
520 		                *dst++ = (char)(((c >> 3) & 07) + '0');
521 		                *dst++ = (char)(( c       & 07) + '0');
522 				len -= 4;
523 			}
524 			bytes += 4;
525 		} else {
526 			if (dst) {
527 				if (len == 0) {
528 					errno = ENOSPC;
529 					return -1;
530 				}
531 				*dst++ = (char)c;
532 				len--;
533 			}
534 			bytes++;
535 		}
536 	}
537 
538 	/* NULL */
539 	if (dst) {
540 		if (len == 0) {
541 			errno = ENOSPC;
542 			return -1;
543 		}
544 		*dst = '\0';
545 
546 		/* Now we've printed it, validate the domain */
547 		if (type & DOMAIN && !valid_domainname(odst, type)) {
548 			*odst = '\0';
549 			return 1;
550 		}
551 
552 	}
553 
554 	return (ssize_t)bytes;
555 }
556 
557 #define ADDRSZ		4
558 #define ADDR6SZ		16
559 static ssize_t
dhcp_optlen(const struct dhcp_opt * opt,size_t dl)560 dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
561 {
562 	size_t sz;
563 
564 	if (opt->type == 0 ||
565 	    opt->type & (STRING | BINHEX | RFC3442 | RFC5969))
566 	{
567 		if (opt->len) {
568 			if ((size_t)opt->len > dl)
569 				return -1;
570 			return (ssize_t)opt->len;
571 		}
572 		return (ssize_t)dl;
573 	}
574 
575 	if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) {
576 		if (dl < ADDRSZ)
577 			return -1;
578 		return (ssize_t)(dl - (dl % ADDRSZ));
579 	}
580 
581 	if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) {
582 		if (dl < ADDR6SZ)
583 			return -1;
584 		return (ssize_t)(dl - (dl % ADDR6SZ));
585 	}
586 
587 	if (opt->type & (UINT32 | ADDRIPV4))
588 		sz = sizeof(uint32_t);
589 	else if (opt->type & UINT16)
590 		sz = sizeof(uint16_t);
591 	else if (opt->type & (UINT8 | BITFLAG))
592 		sz = sizeof(uint8_t);
593 	else if (opt->type & ADDRIPV6)
594 		sz = ADDR6SZ;
595 	else
596 		/* If we don't know the size, assume it's valid */
597 		return (ssize_t)dl;
598 	return dl < sz ? -1 : (ssize_t)sz;
599 }
600 
601 static ssize_t
print_option(char * s,size_t len,const struct dhcp_opt * opt,const uint8_t * data,size_t dl,const char * ifname)602 print_option(char *s, size_t len, const struct dhcp_opt *opt,
603     const uint8_t *data, size_t dl, const char *ifname)
604 {
605 	const uint8_t *e, *t;
606 	uint16_t u16;
607 	int16_t s16;
608 	uint32_t u32;
609 	int32_t s32;
610 	struct in_addr addr;
611 	ssize_t bytes = 0, sl;
612 	size_t l;
613 	char *tmp;
614 
615 #ifndef INET6
616 	UNUSED(ifname);
617 #endif
618 
619 	if (opt->type & RFC1035) {
620 		sl = decode_rfc1035(NULL, 0, data, dl);
621 		if (sl == 0 || sl == -1)
622 			return sl;
623 		l = (size_t)sl + 1;
624 		tmp = malloc(l);
625 		if (tmp == NULL)
626 			return -1;
627 		decode_rfc1035(tmp, l, data, dl);
628 		sl = print_string(s, len, opt->type, (uint8_t *)tmp, l - 1);
629 		free(tmp);
630 		return sl;
631 	}
632 
633 #ifdef INET
634 	if (opt->type & RFC3361) {
635 		if ((tmp = decode_rfc3361(data, dl)) == NULL)
636 			return -1;
637 		l = strlen(tmp);
638 		sl = print_string(s, len, opt->type, (uint8_t *)tmp, l);
639 		free(tmp);
640 		return sl;
641 	}
642 
643 	if (opt->type & RFC3442)
644 		return decode_rfc3442(s, len, data, dl);
645 
646 	if (opt->type & RFC5969)
647 		return decode_rfc5969(s, len, data, dl);
648 #endif
649 
650 	if (opt->type & STRING)
651 		return print_string(s, len, opt->type, data, dl);
652 
653 	if (opt->type & FLAG) {
654 		if (s) {
655 			*s++ = '1';
656 			*s = '\0';
657 		}
658 		return 1;
659 	}
660 
661 	if (opt->type & BITFLAG) {
662 		/* bitflags are a string, MSB first, such as ABCDEFGH
663 		 * where A is 10000000, B is 01000000, etc. */
664 		bytes = 0;
665 		for (l = 0, sl = sizeof(opt->bitflags) - 1;
666 		    l < sizeof(opt->bitflags);
667 		    l++, sl--)
668 		{
669 			/* Don't print NULL or 0 flags */
670 			if (opt->bitflags[l] != '\0' &&
671 			    opt->bitflags[l] != '0' &&
672 			    *data & (1 << sl))
673 			{
674 				if (s)
675 					*s++ = opt->bitflags[l];
676 				bytes++;
677 			}
678 		}
679 		if (s)
680 			*s = '\0';
681 		return bytes;
682 	}
683 
684 	if (!s) {
685 		if (opt->type & UINT8)
686 			l = 3;
687 		else if (opt->type & UINT16) {
688 			l = 5;
689 			dl /= 2;
690 		} else if (opt->type & SINT16) {
691 			l = 6;
692 			dl /= 2;
693 		} else if (opt->type & UINT32) {
694 			l = 10;
695 			dl /= 4;
696 		} else if (opt->type & SINT32) {
697 			l = 11;
698 			dl /= 4;
699 		} else if (opt->type & ADDRIPV4) {
700 			l = 16;
701 			dl /= 4;
702 		}
703 #ifdef INET6
704 		else if (opt->type & ADDRIPV6) {
705 			e = data + dl;
706 			l = 0;
707 			while (data < e) {
708 				if (l)
709 					l++; /* space */
710 				sl = ipv6_printaddr(NULL, 0, data, ifname);
711 				if (sl != -1)
712 					l += (size_t)sl;
713 				data += 16;
714 			}
715 			return (ssize_t)l;
716 		}
717 #endif
718 		else {
719 			errno = EINVAL;
720 			return -1;
721 		}
722 		return (ssize_t)(l * dl);
723 	}
724 
725 	t = data;
726 	e = data + dl;
727 	while (data < e) {
728 		if (data != t) {
729 			*s++ = ' ';
730 			bytes++;
731 			len--;
732 		}
733 		if (opt->type & UINT8) {
734 			sl = snprintf(s, len, "%u", *data);
735 			data++;
736 		} else if (opt->type & UINT16) {
737 			memcpy(&u16, data, sizeof(u16));
738 			u16 = ntohs(u16);
739 			sl = snprintf(s, len, "%u", u16);
740 			data += sizeof(u16);
741 		} else if (opt->type & SINT16) {
742 			memcpy(&u16, data, sizeof(u16));
743 			s16 = (int16_t)ntohs(u16);
744 			sl = snprintf(s, len, "%d", s16);
745 			data += sizeof(u16);
746 		} else if (opt->type & UINT32) {
747 			memcpy(&u32, data, sizeof(u32));
748 			u32 = ntohl(u32);
749 			sl = snprintf(s, len, "%u", u32);
750 			data += sizeof(u32);
751 		} else if (opt->type & SINT32) {
752 			memcpy(&u32, data, sizeof(u32));
753 			s32 = (int32_t)ntohl(u32);
754 			sl = snprintf(s, len, "%d", s32);
755 			data += sizeof(u32);
756 		} else if (opt->type & ADDRIPV4) {
757 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
758 			sl = snprintf(s, len, "%s", inet_ntoa(addr));
759 			data += sizeof(addr.s_addr);
760 		}
761 #ifdef INET6
762 		else if (opt->type & ADDRIPV6) {
763 			ssize_t r;
764 
765 			r = ipv6_printaddr(s, len, data, ifname);
766 			if (r != -1)
767 				sl = r;
768 			else
769 				sl = 0;
770 			data += 16;
771 		}
772 #endif
773 		else
774 			sl = 0;
775 		len -= (size_t)sl;
776 		bytes += sl;
777 		s += sl;
778 	}
779 
780 	return bytes;
781 }
782 
783 int
dhcp_set_leasefile(char * leasefile,size_t len,int family,const struct interface * ifp)784 dhcp_set_leasefile(char *leasefile, size_t len, int family,
785     const struct interface *ifp)
786 {
787 	char ssid[len];
788 
789 	if (ifp->name[0] == '\0') {
790 		strlcpy(leasefile, ifp->ctx->pidfile, len);
791 		return 0;
792 	}
793 
794 	switch (family) {
795 	case AF_INET:
796 	case AF_INET6:
797 		break;
798 	default:
799 		errno = EINVAL;
800 		return -1;
801 	}
802 
803 	if (ifp->wireless) {
804 		ssid[0] = '-';
805 		print_string(ssid + 1, sizeof(ssid) - 1,
806 		    ESCFILE,
807 		    (const uint8_t *)ifp->ssid, ifp->ssid_len);
808 	} else
809 		ssid[0] = '\0';
810 	return snprintf(leasefile, len,
811 	    family == AF_INET ? LEASEFILE : LEASEFILE6,
812 	    ifp->name, ssid);
813 }
814 
815 static size_t
dhcp_envoption1(struct dhcpcd_ctx * ctx,char ** env,const char * prefix,const struct dhcp_opt * opt,int vname,const uint8_t * od,size_t ol,const char * ifname)816 dhcp_envoption1(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
817     const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol,
818     const char *ifname)
819 {
820 	ssize_t len;
821 	size_t e;
822 	char *v, *val;
823 
824 	if (opt->len && opt->len < ol)
825 		ol = opt->len;
826 	len = print_option(NULL, 0, opt, od, ol, ifname);
827 	if (len < 0)
828 		return 0;
829 	if (vname)
830 		e = strlen(opt->var) + 1;
831 	else
832 		e = 0;
833 	if (prefix)
834 		e += strlen(prefix);
835 	e += (size_t)len + 2;
836 	if (env == NULL)
837 		return e;
838 	v = val = *env = malloc(e);
839 	if (v == NULL) {
840 		logger(ctx, LOG_ERR, "%s: %m", __func__);
841 		return 0;
842 	}
843 	if (vname)
844 		v += snprintf(val, e, "%s_%s=", prefix, opt->var);
845 	else
846 		v += snprintf(val, e, "%s=", prefix);
847 	if (len != 0)
848 		print_option(v, (size_t)len + 1, opt, od, ol, ifname);
849 	return e;
850 }
851 
852 size_t
dhcp_envoption(struct dhcpcd_ctx * ctx,char ** env,const char * prefix,const char * ifname,struct dhcp_opt * opt,const uint8_t * (* dgetopt)(struct dhcpcd_ctx *,size_t *,unsigned int *,size_t *,const uint8_t *,size_t,struct dhcp_opt **),const uint8_t * od,size_t ol)853 dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
854     const char *ifname, struct dhcp_opt *opt,
855     const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
856     size_t *, unsigned int *, size_t *,
857     const uint8_t *, size_t, struct dhcp_opt **),
858     const uint8_t *od, size_t ol)
859 {
860 	size_t e, i, n, eos, eol;
861 	ssize_t eo;
862 	unsigned int eoc;
863 	const uint8_t *eod;
864 	int ov;
865 	struct dhcp_opt *eopt, *oopt;
866 	char *pfx;
867 
868 	/* If no embedded or encapsulated options, it's easy */
869 	if (opt->embopts_len == 0 && opt->encopts_len == 0) {
870 		if (!(opt->type & RESERVED) &&
871 		    dhcp_envoption1(ctx, env == NULL ? NULL : &env[0],
872 		    prefix, opt, 1, od, ol, ifname))
873 			return 1;
874 		return 0;
875 	}
876 
877 	/* Create a new prefix based on the option */
878 	if (env) {
879 		if (opt->type & INDEX) {
880 			if (opt->index > 999) {
881 				errno = ENOBUFS;
882 				logger(ctx, LOG_ERR, "%s: %m", __func__);
883 				return 0;
884 			}
885 		}
886 		e = strlen(prefix) + strlen(opt->var) + 2 +
887 		    (opt->type & INDEX ? 3 : 0);
888 		pfx = malloc(e);
889 		if (pfx == NULL) {
890 			logger(ctx, LOG_ERR, "%s: %m", __func__);
891 			return 0;
892 		}
893 		if (opt->type & INDEX)
894 			snprintf(pfx, e, "%s_%s%d", prefix,
895 			    opt->var, ++opt->index);
896 		else
897 			snprintf(pfx, e, "%s_%s", prefix, opt->var);
898 	} else
899 		pfx = NULL;
900 
901 	/* Embedded options are always processed first as that
902 	 * is a fixed layout */
903 	n = 0;
904 	for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
905 		eo = dhcp_optlen(eopt, ol);
906 		if (eo == -1) {
907 			if (env == NULL)
908 				logger(ctx, LOG_ERR,
909 				    "%s: %s: malformed embedded option %d:%d",
910 				    ifname, __func__, opt->option,
911 				    eopt->option);
912 			goto out;
913 		}
914 		if (eo == 0) {
915 			/* An option was expected, but there is no data
916 			 * data for it.
917 			 * This may not be an error as some options like
918 			 * DHCP FQDN in RFC4702 have a string as the last
919 			 * option which is optional.
920 			 * FIXME: Add an flag to the options to indicate
921 			 * wether this is allowable or not. */
922 			if (env == NULL &&
923 			    (ol != 0 || i + 1 < opt->embopts_len))
924 				logger(ctx, LOG_ERR,
925 				    "%s: %s: malformed embedded option %d:%d",
926 				    ifname, __func__, opt->option,
927 				    eopt->option);
928 			goto out;
929 		}
930 		/* Use the option prefix if the embedded option
931 		 * name is different.
932 		 * This avoids new_fqdn_fqdn which would be silly. */
933 		if (!(eopt->type & RESERVED)) {
934 			ov = strcmp(opt->var, eopt->var);
935 			if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[n],
936 			    pfx, eopt, ov, od, (size_t)eo, ifname))
937 				n++;
938 		}
939 		od += (size_t)eo;
940 		ol -= (size_t)eo;
941 	}
942 
943 	/* Enumerate our encapsulated options */
944 	if (opt->encopts_len && ol > 0) {
945 		/* Zero any option indexes
946 		 * We assume that referenced encapsulated options are NEVER
947 		 * recursive as the index order could break. */
948 		for (i = 0, eopt = opt->encopts;
949 		    i < opt->encopts_len;
950 		    i++, eopt++)
951 		{
952 			eoc = opt->option;
953 			if (eopt->type & OPTION) {
954 				dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
955 				if (oopt)
956 					oopt->index = 0;
957 			}
958 		}
959 
960 		while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
961 			for (i = 0, eopt = opt->encopts;
962 			    i < opt->encopts_len;
963 			    i++, eopt++)
964 			{
965 				if (eopt->option == eoc) {
966 					if (eopt->type & OPTION) {
967 						if (oopt == NULL)
968 							/* Report error? */
969 							continue;
970 					}
971 					n += dhcp_envoption(ctx,
972 					    env == NULL ? NULL : &env[n], pfx,
973 					    ifname,
974 					    eopt->type & OPTION ? oopt : eopt,
975 					    dgetopt, eod, eol);
976 					break;
977 				}
978 			}
979 			od += eos + eol;
980 			ol -= eos + eol;
981 		}
982 	}
983 
984 out:
985 	if (env)
986 		free(pfx);
987 
988 	/* Return number of options found */
989 	return n;
990 }
991 
992 void
dhcp_zero_index(struct dhcp_opt * opt)993 dhcp_zero_index(struct dhcp_opt *opt)
994 {
995 	size_t i;
996 	struct dhcp_opt *o;
997 
998 	opt->index = 0;
999 	for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
1000 		dhcp_zero_index(o);
1001 	for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
1002 		dhcp_zero_index(o);
1003 }
1004