xref: /openbsd/sbin/dhclient/options.c (revision 09cbf102)
1 /*	$OpenBSD: options.c,v 1.123 2020/07/07 19:48:31 krw Exp $	*/
2 
3 /* DHCP options parsing and reassembly. */
4 
5 /*
6  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
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  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include <sys/queue.h>
44 #include <sys/socket.h>
45 
46 #include <arpa/inet.h>
47 
48 #include <net/if.h>
49 
50 #include <netinet/in.h>
51 #include <netinet/if_ether.h>
52 
53 #include <ctype.h>
54 #include <resolv.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <vis.h>
60 
61 #include "dhcp.h"
62 #include "dhcpd.h"
63 #include "log.h"
64 
65 int	parse_option_buffer(struct option_data *, unsigned char *, int);
66 void	pretty_print_classless_routes(unsigned char *, size_t, unsigned char *,
67     size_t);
68 void	pretty_print_domain_list(unsigned char *, size_t, unsigned char *,
69     size_t);
70 
71 /*
72  * DHCP Option names, formats and codes, from RFC1533.
73  *
74  * Format codes:
75  *
76  * e - end of data
77  * I - IP address
78  * l - 32-bit signed integer
79  * L - 32-bit unsigned integer
80  * S - 16-bit unsigned integer
81  * B - 8-bit unsigned integer
82  * t - ASCII text
83  * f - flag (true or false)
84  * A - array of whatever precedes (e.g., IA means array of IP addresses)
85  * C - CIDR description
86  * X - hex octets
87  * D - domain name list, comma separated list of domain names.
88  */
89 
90 static const struct {
91 	char *name;
92 	char *format;
93 } dhcp_options[DHO_COUNT] = {
94 	/*   0 */ { "pad", "" },
95 	/*   1 */ { "subnet-mask", "I" },
96 	/*   2 */ { "time-offset", "l" },
97 	/*   3 */ { "routers", "IA" },
98 	/*   4 */ { "time-servers", "IA" },
99 	/*   5 */ { "ien116-name-servers", "IA" },
100 	/*   6 */ { "domain-name-servers", "IA" },
101 	/*   7 */ { "log-servers", "IA" },
102 	/*   8 */ { "cookie-servers", "IA" },
103 	/*   9 */ { "lpr-servers", "IA" },
104 	/*  10 */ { "impress-servers", "IA" },
105 	/*  11 */ { "resource-location-servers", "IA" },
106 	/*  12 */ { "host-name", "t" },
107 	/*  13 */ { "boot-size", "S" },
108 	/*  14 */ { "merit-dump", "t" },
109 	/*  15 */ { "domain-name", "t" },
110 	/*  16 */ { "swap-server", "I" },
111 	/*  17 */ { "root-path", "t" },
112 	/*  18 */ { "extensions-path", "t" },
113 	/*  19 */ { "ip-forwarding", "f" },
114 	/*  20 */ { "non-local-source-routing", "f" },
115 	/*  21 */ { "policy-filter", "IIA" },
116 	/*  22 */ { "max-dgram-reassembly", "S" },
117 	/*  23 */ { "default-ip-ttl", "B" },
118 	/*  24 */ { "path-mtu-aging-timeout", "L" },
119 	/*  25 */ { "path-mtu-plateau-table", "SA" },
120 	/*  26 */ { "interface-mtu", "S" },
121 	/*  27 */ { "all-subnets-local", "f" },
122 	/*  28 */ { "broadcast-address", "I" },
123 	/*  29 */ { "perform-mask-discovery", "f" },
124 	/*  30 */ { "mask-supplier", "f" },
125 	/*  31 */ { "router-discovery", "f" },
126 	/*  32 */ { "router-solicitation-address", "I" },
127 	/*  33 */ { "static-routes", "IIA" },
128 	/*  34 */ { "trailer-encapsulation", "f" },
129 	/*  35 */ { "arp-cache-timeout", "L" },
130 	/*  36 */ { "ieee802-3-encapsulation", "f" },
131 	/*  37 */ { "default-tcp-ttl", "B" },
132 	/*  38 */ { "tcp-keepalive-interval", "L" },
133 	/*  39 */ { "tcp-keepalive-garbage", "f" },
134 	/*  40 */ { "nis-domain", "t" },
135 	/*  41 */ { "nis-servers", "IA" },
136 	/*  42 */ { "ntp-servers", "IA" },
137 	/*  43 */ { "vendor-encapsulated-options", "X" },
138 	/*  44 */ { "netbios-name-servers", "IA" },
139 	/*  45 */ { "netbios-dd-server", "IA" },
140 	/*  46 */ { "netbios-node-type", "B" },
141 	/*  47 */ { "netbios-scope", "t" },
142 	/*  48 */ { "font-servers", "IA" },
143 	/*  49 */ { "x-display-manager", "IA" },
144 	/*  50 */ { "dhcp-requested-address", "I" },
145 	/*  51 */ { "dhcp-lease-time", "L" },
146 	/*  52 */ { "dhcp-option-overload", "B" },
147 	/*  53 */ { "dhcp-message-type", "B" },
148 	/*  54 */ { "dhcp-server-identifier", "I" },
149 	/*  55 */ { "dhcp-parameter-request-list", "BA" },
150 	/*  56 */ { "dhcp-message", "t" },
151 	/*  57 */ { "dhcp-max-message-size", "S" },
152 	/*  58 */ { "dhcp-renewal-time", "L" },
153 	/*  59 */ { "dhcp-rebinding-time", "L" },
154 	/*  60 */ { "dhcp-class-identifier", "t" },
155 	/*  61 */ { "dhcp-client-identifier", "X" },
156 	/*  62 */ { NULL, NULL },
157 	/*  63 */ { NULL, NULL },
158 	/*  64 */ { "nisplus-domain", "t" },
159 	/*  65 */ { "nisplus-servers", "IA" },
160 	/*  66 */ { "tftp-server-name", "t" },
161 	/*  67 */ { "bootfile-name", "t" },
162 	/*  68 */ { "mobile-ip-home-agent", "IA" },
163 	/*  69 */ { "smtp-server", "IA" },
164 	/*  70 */ { "pop-server", "IA" },
165 	/*  71 */ { "nntp-server", "IA" },
166 	/*  72 */ { "www-server", "IA" },
167 	/*  73 */ { "finger-server", "IA" },
168 	/*  74 */ { "irc-server", "IA" },
169 	/*  75 */ { "streettalk-server", "IA" },
170 	/*  76 */ { "streettalk-directory-assistance-server", "IA" },
171 	/*  77 */ { "user-class", "t" },
172 	/*  78 */ { NULL, NULL },
173 	/*  79 */ { NULL, NULL },
174 	/*  80 */ { NULL, NULL },
175 	/*  81 */ { NULL, NULL },
176 	/*  82 */ { "relay-agent-information", "X" },
177 	/*  83 */ { NULL, NULL },
178 	/*  84 */ { NULL, NULL },
179 	/*  85 */ { "nds-servers", "IA" },
180 	/*  86 */ { "nds-tree-name", "X" },
181 	/*  87 */ { "nds-context", "X" },
182 	/*  88 */ { NULL, NULL },
183 	/*  89 */ { NULL, NULL },
184 	/*  90 */ { NULL, NULL },
185 	/*  91 */ { NULL, NULL },
186 	/*  92 */ { NULL, NULL },
187 	/*  93 */ { NULL, NULL },
188 	/*  94 */ { NULL, NULL },
189 	/*  95 */ { NULL, NULL },
190 	/*  96 */ { NULL, NULL },
191 	/*  97 */ { NULL, NULL },
192 	/*  98 */ { NULL, NULL },
193 	/*  99 */ { NULL, NULL },
194 	/* 100 */ { NULL, NULL },
195 	/* 101 */ { NULL, NULL },
196 	/* 102 */ { NULL, NULL },
197 	/* 103 */ { NULL, NULL },
198 	/* 104 */ { NULL, NULL },
199 	/* 105 */ { NULL, NULL },
200 	/* 106 */ { NULL, NULL },
201 	/* 107 */ { NULL, NULL },
202 	/* 108 */ { NULL, NULL },
203 	/* 109 */ { NULL, NULL },
204 	/* 110 */ { NULL, NULL },
205 	/* 111 */ { NULL, NULL },
206 	/* 112 */ { NULL, NULL },
207 	/* 113 */ { NULL, NULL },
208 	/* 114 */ { NULL, NULL },
209 	/* 115 */ { NULL, NULL },
210 	/* 116 */ { NULL, NULL },
211 	/* 117 */ { NULL, NULL },
212 	/* 118 */ { NULL, NULL },
213 	/* 119 */ { "domain-search", "D" },
214 	/* 120 */ { NULL, NULL },
215 	/* 121 */ { "classless-static-routes", "CIA" },
216 	/* 122 */ { NULL, NULL },
217 	/* 123 */ { NULL, NULL },
218 	/* 124 */ { NULL, NULL },
219 	/* 125 */ { NULL, NULL },
220 	/* 126 */ { NULL, NULL },
221 	/* 127 */ { NULL, NULL },
222 	/* 128 */ { NULL, NULL },
223 	/* 129 */ { NULL, NULL },
224 	/* 130 */ { NULL, NULL },
225 	/* 131 */ { NULL, NULL },
226 	/* 132 */ { NULL, NULL },
227 	/* 133 */ { NULL, NULL },
228 	/* 134 */ { NULL, NULL },
229 	/* 135 */ { NULL, NULL },
230 	/* 136 */ { NULL, NULL },
231 	/* 137 */ { NULL, NULL },
232 	/* 138 */ { NULL, NULL },
233 	/* 139 */ { NULL, NULL },
234 	/* 140 */ { NULL, NULL },
235 	/* 141 */ { NULL, NULL },
236 	/* 142 */ { NULL, NULL },
237 	/* 143 */ { NULL, NULL },
238 	/* 144 */ { "tftp-config-file", "t" },
239 	/* 145 */ { NULL, NULL },
240 	/* 146 */ { NULL, NULL },
241 	/* 147 */ { NULL, NULL },
242 	/* 148 */ { NULL, NULL },
243 	/* 149 */ { NULL, NULL },
244 	/* 150 */ { "voip-configuration-server", "IA" },
245 	/* 151 */ { NULL, NULL },
246 	/* 152 */ { NULL, NULL },
247 	/* 153 */ { NULL, NULL },
248 	/* 154 */ { NULL, NULL },
249 	/* 155 */ { NULL, NULL },
250 	/* 156 */ { NULL, NULL },
251 	/* 157 */ { NULL, NULL },
252 	/* 158 */ { NULL, NULL },
253 	/* 159 */ { NULL, NULL },
254 	/* 160 */ { NULL, NULL },
255 	/* 161 */ { NULL, NULL },
256 	/* 162 */ { NULL, NULL },
257 	/* 163 */ { NULL, NULL },
258 	/* 164 */ { NULL, NULL },
259 	/* 165 */ { NULL, NULL },
260 	/* 166 */ { NULL, NULL },
261 	/* 167 */ { NULL, NULL },
262 	/* 168 */ { NULL, NULL },
263 	/* 169 */ { NULL, NULL },
264 	/* 170 */ { NULL, NULL },
265 	/* 171 */ { NULL, NULL },
266 	/* 172 */ { NULL, NULL },
267 	/* 173 */ { NULL, NULL },
268 	/* 174 */ { NULL, NULL },
269 	/* 175 */ { NULL, NULL },
270 	/* 176 */ { NULL, NULL },
271 	/* 177 */ { NULL, NULL },
272 	/* 178 */ { NULL, NULL },
273 	/* 179 */ { NULL, NULL },
274 	/* 180 */ { NULL, NULL },
275 	/* 181 */ { NULL, NULL },
276 	/* 182 */ { NULL, NULL },
277 	/* 183 */ { NULL, NULL },
278 	/* 184 */ { NULL, NULL },
279 	/* 185 */ { NULL, NULL },
280 	/* 186 */ { NULL, NULL },
281 	/* 187 */ { NULL, NULL },
282 	/* 188 */ { NULL, NULL },
283 	/* 189 */ { NULL, NULL },
284 	/* 190 */ { NULL, NULL },
285 	/* 191 */ { NULL, NULL },
286 	/* 192 */ { NULL, NULL },
287 	/* 193 */ { NULL, NULL },
288 	/* 194 */ { NULL, NULL },
289 	/* 195 */ { NULL, NULL },
290 	/* 196 */ { NULL, NULL },
291 	/* 197 */ { NULL, NULL },
292 	/* 198 */ { NULL, NULL },
293 	/* 199 */ { NULL, NULL },
294 	/* 200 */ { NULL, NULL },
295 	/* 201 */ { NULL, NULL },
296 	/* 202 */ { NULL, NULL },
297 	/* 203 */ { NULL, NULL },
298 	/* 204 */ { NULL, NULL },
299 	/* 205 */ { NULL, NULL },
300 	/* 206 */ { NULL, NULL },
301 	/* 207 */ { NULL, NULL },
302 	/* 208 */ { NULL, NULL },
303 	/* 209 */ { NULL, NULL },
304 	/* 210 */ { NULL, NULL },
305 	/* 211 */ { NULL, NULL },
306 	/* 212 */ { NULL, NULL },
307 	/* 213 */ { NULL, NULL },
308 	/* 214 */ { NULL, NULL },
309 	/* 215 */ { NULL, NULL },
310 	/* 216 */ { NULL, NULL },
311 	/* 217 */ { NULL, NULL },
312 	/* 218 */ { NULL, NULL },
313 	/* 219 */ { NULL, NULL },
314 	/* 220 */ { NULL, NULL },
315 	/* 221 */ { NULL, NULL },
316 	/* 222 */ { NULL, NULL },
317 	/* 223 */ { NULL, NULL },
318 	/* 224 */ { NULL, NULL },
319 	/* 225 */ { NULL, NULL },
320 	/* 226 */ { NULL, NULL },
321 	/* 227 */ { NULL, NULL },
322 	/* 228 */ { NULL, NULL },
323 	/* 229 */ { NULL, NULL },
324 	/* 230 */ { NULL, NULL },
325 	/* 231 */ { NULL, NULL },
326 	/* 232 */ { NULL, NULL },
327 	/* 233 */ { NULL, NULL },
328 	/* 234 */ { NULL, NULL },
329 	/* 235 */ { NULL, NULL },
330 	/* 236 */ { NULL, NULL },
331 	/* 237 */ { NULL, NULL },
332 	/* 238 */ { NULL, NULL },
333 	/* 239 */ { NULL, NULL },
334 	/* 240 */ { NULL, NULL },
335 	/* 241 */ { NULL, NULL },
336 	/* 242 */ { NULL, NULL },
337 	/* 243 */ { NULL, NULL },
338 	/* 244 */ { NULL, NULL },
339 	/* 245 */ { NULL, NULL },
340 	/* 246 */ { NULL, NULL },
341 	/* 247 */ { NULL, NULL },
342 	/* 248 */ { NULL, NULL },
343 	/* 249 */ { "classless-ms-static-routes", "CIA" },
344 	/* 250 */ { NULL, NULL },
345 	/* 251 */ { NULL, NULL },
346 	/* 252 */ { "autoproxy-script", "t" },
347 	/* 253 */ { NULL, NULL },
348 	/* 254 */ { NULL, NULL },
349 	/* 255 */ { "option-end", "e" },
350 };
351 
352 char *
code_to_name(int code)353 code_to_name(int code)
354 {
355 	static char	 unknown[11];	/* "option-NNN" */
356 	int		 ret;
357 
358 	if (code < 0 || code >= DHO_COUNT)
359 		return "";
360 
361 	if (dhcp_options[code].name != NULL)
362 		return dhcp_options[code].name;
363 
364 	ret = snprintf(unknown, sizeof(unknown), "option-%d", code);
365 	if (ret < 0 || ret >= (int)sizeof(unknown))
366 		return "";
367 
368 	return unknown;
369 }
370 
371 int
name_to_code(char * name)372 name_to_code(char *name)
373 {
374 	char	unknown[11];	/* "option-NNN" */
375 	int	code, ret;
376 
377 	for (code = 1; code < DHO_END; code++) {
378 		if (dhcp_options[code].name == NULL) {
379 			ret = snprintf(unknown, sizeof(unknown), "option-%d",
380 			    code);
381 			if (ret < 0 || ret >= (int)sizeof(unknown))
382 				return DHO_END;
383 			if (strcasecmp(unknown, name) == 0)
384 				return code;
385 		} else if (strcasecmp(dhcp_options[code].name, name) == 0) {
386 			return code;
387 		}
388 	}
389 
390 	return DHO_END;
391 }
392 
393 char *
code_to_format(int code)394 code_to_format(int code)
395 {
396 	if (code < 0 || code >= DHO_COUNT)
397 		return "";
398 
399 	if (dhcp_options[code].format == NULL)
400 		return "X";
401 
402 	return dhcp_options[code].format;
403 }
404 
405 /*
406  * Some option data types cannot be appended or prepended to. For
407  * such options change ACTION_PREPEND to ACTION_SUPERSEDE and
408  * ACTION_APPEND to ACTION_DEFAULT.
409  */
410 int
code_to_action(int code,int action)411 code_to_action(int code, int action)
412 {
413 	char	*fmt;
414 
415 	fmt = code_to_format(code);
416 	if (fmt == NULL || strpbrk(fmt, "ADtX") != NULL)
417 		return action;
418 
419 	/*
420 	 * For our protection all formats which have been excluded shall be
421 	 * deemed included.
422 	 */
423 	switch (action) {
424 	case ACTION_APPEND:
425 		action = ACTION_DEFAULT;
426 		break;
427 	case ACTION_PREPEND:
428 		action = ACTION_SUPERSEDE;
429 		break;
430 	default:
431 		break;
432 	}
433 
434 	return action;
435 }
436 
437 /*
438  * Parse options out of the specified buffer, storing addresses of
439  * option values in options. Return 0 if errors, 1 if not.
440  */
441 int
parse_option_buffer(struct option_data * options,unsigned char * buffer,int length)442 parse_option_buffer(struct option_data *options, unsigned char *buffer,
443     int length)
444 {
445 	unsigned char	*s, *t, *end;
446 	char		*name, *fmt;
447 	int		 code, len, newlen;
448 
449 	s = buffer;
450 	end = s + length;
451 	while (s < end) {
452 		code = s[0];
453 
454 		/* End options terminate processing. */
455 		if (code == DHO_END)
456 			break;
457 
458 		/* Pad options don't have a length - just skip them. */
459 		if (code == DHO_PAD) {
460 			s++;
461 			continue;
462 		}
463 
464 		name = code_to_name(code);
465 		fmt = code_to_format(code);
466 
467 		/*
468 		 * All options other than DHO_PAD and DHO_END have a one-byte
469 		 * length field. It could be 0! Make sure that the length byte
470 		 * is present, and all the data is available.
471 		 */
472 		if (s + 1 < end) {
473 			len = s[1];
474 			if (s + 1 + len < end) {
475 				; /* option data is all there. */
476 			} else {
477 				log_warnx("%s: option %s (%d) larger than "
478 				    "buffer", log_procname, name, len);
479 				return 0;
480 			}
481 		} else {
482 			log_warnx("%s: option %s has no length field",
483 			    log_procname, name);
484 			return 0;
485 		}
486 
487 		/*
488 		 * Strip trailing NULs from ascii ('t') options. RFC 2132
489 		 * says "Options containing NVT ASCII data SHOULD NOT include
490 		 * a trailing NULL; however, the receiver of such options
491 		 * MUST be prepared to delete trailing nulls if they exist."
492 		 */
493 		if (fmt[0] == 't') {
494 			while (len > 0 && s[len + 1] == '\0')
495 				len--;
496 		}
497 
498 		/*
499 		 * Concatenate new data + NUL to existing option data.
500 		 *
501 		 * Note that the NUL is *not* counted in the len field!
502 		 */
503 		newlen = options[code].len + len;
504 		if ((t = realloc(options[code].data, newlen + 1)) == NULL)
505 			fatal("option %s", name);
506 
507 		memcpy(t + options[code].len, &s[2], len);
508 		t[newlen] = 0;
509 
510 		options[code].len = newlen;
511 		options[code].data = t;
512 
513 		s += s[1] + 2;
514 	}
515 
516 	return 1;
517 }
518 
519 /*
520  * Pack as many options as fit in buflen bytes of buf. Return the
521  * offset of the start of the last option copied. A caller can check
522  * to see if it's DHO_END to decide if all the options were copied.
523  */
524 int
pack_options(unsigned char * buf,int buflen,struct option_data * options)525 pack_options(unsigned char *buf, int buflen, struct option_data *options)
526 {
527 	int	 ix, incr, length, bufix, code, lastopt = -1;
528 
529 	memset(buf, 0, buflen);
530 
531 	memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
532 	if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL) {
533 		memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
534 		buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
535 		bufix = 7;
536 	} else
537 		bufix = 4;
538 
539 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
540 		if (options[code].data == NULL ||
541 		    code == DHO_DHCP_MESSAGE_TYPE)
542 			continue;
543 
544 		length = options[code].len;
545 		if (bufix + length + 2*((length+254)/255) >= buflen)
546 			return lastopt;
547 
548 		lastopt = bufix;
549 		ix = 0;
550 
551 		while (length) {
552 			incr = length > 255 ? 255 : length;
553 
554 			buf[bufix++] = code;
555 			buf[bufix++] = incr;
556 			memcpy(buf + bufix, options[code].data + ix, incr);
557 
558 			length -= incr;
559 			ix += incr;
560 			bufix += incr;
561 		}
562 	}
563 
564 	if (bufix < buflen) {
565 		buf[bufix] = DHO_END;
566 		lastopt = bufix;
567 	}
568 
569 	return lastopt;
570 }
571 
572 /*
573  * Use vis() to encode characters of src and append encoded characters onto
574  * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be
575  * represented as '"' delimited strings and safely passed to scripts. Surround
576  * result with double quotes if emit_punct is true.
577  */
578 char *
pretty_print_string(unsigned char * src,size_t srclen,int emit_punct)579 pretty_print_string(unsigned char *src, size_t srclen, int emit_punct)
580 {
581 	static char	 string[8196];
582 	char		 visbuf[5];
583 	unsigned char	*origsrc = src;
584 	size_t		 rslt = 0;
585 
586 	memset(string, 0, sizeof(string));
587 
588 	if (emit_punct != 0)
589 		rslt = strlcat(string, "\"", sizeof(string));
590 
591 	for (; src < origsrc + srclen; src++) {
592 		if (*src && strchr("\"'$`\\", *src))
593 			vis(visbuf, *src, VIS_ALL | VIS_OCTAL, *src+1);
594 		else
595 			vis(visbuf, *src, VIS_OCTAL, *src+1);
596 		rslt = strlcat(string, visbuf, sizeof(string));
597 	}
598 
599 	if (emit_punct != 0)
600 		rslt = strlcat(string, "\"", sizeof(string));
601 
602 	if (rslt >= sizeof(string))
603 		return NULL;
604 
605 	return string;
606 }
607 
608 /*
609  * Must special case *_CLASSLESS_* route options due to the variable size
610  * of the CIDR element in its CIA format.
611  */
612 void
pretty_print_classless_routes(unsigned char * src,size_t srclen,unsigned char * buf,size_t buflen)613 pretty_print_classless_routes(unsigned char *src, size_t srclen,
614     unsigned char *buf, size_t buflen)
615 {
616 	char		 bitsbuf[5];	/* to hold "/nn " */
617 	struct in_addr	 dest, netmask, gateway;
618 	unsigned int	 bits, i, len;
619 	uint32_t	 m;
620 	int		 rslt;
621 
622 	i = 0;
623 	while (i < srclen) {
624 		len = extract_route(&src[i], srclen - i, &dest.s_addr,
625 		    &netmask.s_addr, &gateway.s_addr);
626 		if (len == 0)
627 			goto bad;
628 		i += len;
629 
630 		m = ntohl(netmask.s_addr);
631 		bits = 32;
632 		while ((bits > 0) && ((m & 1) == 0)) {
633 			m >>= 1;
634 			bits--;
635 		}
636 
637 		rslt = snprintf(bitsbuf, sizeof(bitsbuf), "/%d ", bits);
638 		if (rslt < 0 || (unsigned int)rslt >= sizeof(bitsbuf))
639 			goto bad;
640 
641 		if (strlen(buf) > 0)
642 			strlcat(buf, ", ", buflen);
643 		strlcat(buf, inet_ntoa(dest), buflen);
644 		strlcat(buf, bitsbuf, buflen);
645 		if (strlcat(buf, inet_ntoa(gateway), buflen) >= buflen)
646 			goto bad;
647 	}
648 
649 	return;
650 
651 bad:
652 	memset(buf, 0, buflen);
653 }
654 
655 /*
656  * Print string containing blank separated list of domain names
657  * as a comma separated list of double-quote delimited strings.
658  *
659  * e.g.  "eng.apple.com. marketing.apple.com."
660  *
661  * will be translated to
662  *
663  * "eng.apple.com.", "marketing.apple.com."
664  */
665 void
pretty_print_domain_list(unsigned char * src,size_t srclen,unsigned char * buf,size_t buflen)666 pretty_print_domain_list(unsigned char *src, size_t srclen,
667     unsigned char *buf, size_t buflen)
668 {
669 	char	*dupnames, *hn, *inputstring;
670 	int	 count;
671 
672 	memset(buf, 0, buflen);
673 
674 	/*
675 	 * N.B.: option data is *NOT* guaranteed to be NUL
676 	 *	 terminated. Avoid strlen(), strdup(), etc.!
677 	 */
678 	if (srclen >= DHCP_DOMAIN_SEARCH_LEN || src[0] == '\0')
679 		return;
680 
681 	inputstring = malloc(srclen + 1);
682 	if (inputstring == NULL)
683 		fatal("domain name list");
684 	memcpy(inputstring, src, srclen);
685 	inputstring[srclen] = '\0';
686 	dupnames = inputstring;
687 
688 	count = 0;
689 	while ((hn = strsep(&inputstring, " \t")) != NULL) {
690 		if (strlen(hn) == 0)
691 			continue;
692 		if (res_hnok(hn) == 0)
693 			goto bad;
694 		if (count > 0)
695 			strlcat(buf, ", ", buflen);
696 		strlcat(buf, "\"", buflen);
697 		strlcat(buf, hn, buflen);
698 		if (strlcat(buf, "\"", buflen) >= buflen)
699 			goto bad;
700 		count++;
701 		if (count > DHCP_DOMAIN_SEARCH_CNT)
702 			goto bad;
703 	}
704 
705 	free(dupnames);
706 	return;
707 
708 bad:
709 	free(dupnames);
710 	memset(buf, 0, buflen);
711 }
712 
713 /*
714  * Format the specified option so that a human can easily read it.
715  */
716 char *
pretty_print_option(unsigned int code,struct option_data * option,int emit_punct)717 pretty_print_option(unsigned int code, struct option_data *option,
718     int emit_punct)
719 {
720 	static char	 optbuf[8192]; /* XXX */
721 	char		 fmtbuf[32];
722 	struct in_addr	 foo;
723 	unsigned char	*data = option->data;
724 	unsigned char	*dp = data;
725 	char		*op = optbuf, *buf, *name, *fmt;
726 	int		 hunksize = 0, numhunk = -1, numelem = 0;
727 	int		 i, j, k, opleft = sizeof(optbuf);
728 	int		 len = option->len;
729 	int		 opcount = 0;
730 	int32_t		 int32val;
731 	uint32_t	 uint32val;
732 	uint16_t	 uint16val;
733 	char		 comma;
734 
735 	memset(optbuf, 0, sizeof(optbuf));
736 
737 	/* Code should be between 0 and 255. */
738 	if (code > 255) {
739 		log_warnx("%s: pretty_print_option: bad code %d", log_procname,
740 		    code);
741 		goto done;
742 	}
743 
744 	if (emit_punct != 0)
745 		comma = ',';
746 	else
747 		comma = ' ';
748 
749 	/* Handle the princess class options with weirdo formats. */
750 	switch (code) {
751 	case DHO_CLASSLESS_STATIC_ROUTES:
752 	case DHO_CLASSLESS_MS_STATIC_ROUTES:
753 		pretty_print_classless_routes(dp, len, optbuf, sizeof(optbuf));
754 		goto done;
755 	case DHO_DOMAIN_SEARCH:
756 		pretty_print_domain_list(dp, len, optbuf, sizeof(optbuf));
757 		goto done;
758 	default:
759 		break;
760 	}
761 
762 	name = code_to_name(code);
763 	fmt = code_to_format(code);
764 
765 	/* Figure out the size of the data. */
766 	for (i = 0; fmt[i]; i++) {
767 		if (numhunk == 0) {
768 			log_warnx("%s: %s: excess information in format "
769 			    "string: %s", log_procname, name, &fmt[i]);
770 			goto done;
771 		}
772 		numelem++;
773 		fmtbuf[i] = fmt[i];
774 		switch (fmt[i]) {
775 		case 'A':
776 			--numelem;
777 			fmtbuf[i] = 0;
778 			numhunk = 0;
779 			if (hunksize == 0) {
780 				log_warnx("%s: %s: no size indicator before A"
781 				    " in format string: %s", log_procname,
782 				    name, fmt);
783 				goto done;
784 			}
785 			break;
786 		case 'X':
787 			for (k = 0; k < len; k++)
788 				if (isascii(data[k]) == 0 ||
789 				    isprint(data[k]) == 0)
790 					break;
791 			if (k == len) {
792 				fmtbuf[i] = 't';
793 				numhunk = -2;
794 			} else {
795 				hunksize++;
796 				comma = ':';
797 				numhunk = 0;
798 			}
799 			fmtbuf[i + 1] = 0;
800 			break;
801 		case 't':
802 			fmtbuf[i + 1] = 0;
803 			numhunk = -2;
804 			break;
805 		case 'I':
806 		case 'l':
807 		case 'L':
808 			hunksize += 4;
809 			break;
810 		case 'S':
811 			hunksize += 2;
812 			break;
813 		case 'B':
814 		case 'f':
815 			hunksize++;
816 			break;
817 		case 'e':
818 			break;
819 		default:
820 			log_warnx("%s: %s: garbage in format string: %s",
821 			    log_procname, name, &fmt[i]);
822 			goto done;
823 		}
824 	}
825 
826 	/* Check for too few bytes. */
827 	if (hunksize > len) {
828 		log_warnx("%s: %s: expecting at least %d bytes; got %d",
829 		    log_procname, name, hunksize, len);
830 		goto done;
831 	}
832 	/* Check for too many bytes. */
833 	if (numhunk == -1 && hunksize < len) {
834 		log_warnx("%s: %s: expecting only %d bytes: got %d",
835 		    log_procname, name, hunksize, len);
836 		goto done;
837 	}
838 
839 	/* If this is an array, compute its size. */
840 	if (numhunk == 0)
841 		numhunk = len / hunksize;
842 	/* See if we got an exact number of hunks. */
843 	if (numhunk > 0 && numhunk * hunksize != len) {
844 		log_warnx("%s: %s: expecting %d bytes: got %d", log_procname,
845 		    name, numhunk * hunksize, len);
846 		goto done;
847 	}
848 
849 	/* A one-hunk array prints the same as a single hunk. */
850 	if (numhunk < 0)
851 		numhunk = 1;
852 
853 	/* Cycle through the array (or hunk) printing the data. */
854 	for (i = 0; i < numhunk; i++) {
855 		for (j = 0; j < numelem; j++) {
856 			switch (fmtbuf[j]) {
857 			case 't':
858 				buf = pretty_print_string(dp, len, emit_punct);
859 				if (buf == NULL)
860 					opcount = -1;
861 				else
862 					opcount = strlcat(op, buf, opleft);
863 				break;
864 			case 'I':
865 				memcpy(&foo.s_addr, dp, sizeof(foo.s_addr));
866 				opcount = snprintf(op, opleft, "%s",
867 				    inet_ntoa(foo));
868 				dp += sizeof(foo.s_addr);
869 				break;
870 			case 'l':
871 				memcpy(&int32val, dp, sizeof(int32val));
872 				opcount = snprintf(op, opleft, "%d",
873 				    ntohl(int32val));
874 				dp += sizeof(int32val);
875 				break;
876 			case 'L':
877 				memcpy(&uint32val, dp, sizeof(uint32val));
878 				opcount = snprintf(op, opleft, "%u",
879 				    ntohl(uint32val));
880 				dp += sizeof(uint32val);
881 				break;
882 			case 'S':
883 				memcpy(&uint16val, dp, sizeof(uint16val));
884 				opcount = snprintf(op, opleft, "%hu",
885 				    ntohs(uint16val));
886 				dp += sizeof(uint16val);
887 				break;
888 			case 'B':
889 				opcount = snprintf(op, opleft, "%u", *dp);
890 				dp++;
891 				break;
892 			case 'X':
893 				opcount = snprintf(op, opleft, "%x", *dp);
894 				dp++;
895 				break;
896 			case 'f':
897 				opcount = snprintf(op, opleft, "%s",
898 				    *dp ? "true" : "false");
899 				dp++;
900 				break;
901 			default:
902 				log_warnx("%s: unexpected format code %c",
903 				    log_procname, fmtbuf[j]);
904 				goto toobig;
905 			}
906 			if (opcount < 0 || opcount >= opleft)
907 				goto toobig;
908 			opleft -= opcount;
909 			op += opcount;
910 			if (j + 1 < numelem && comma != ':') {
911 				opcount = snprintf(op, opleft, " ");
912 				if (opcount < 0 || opcount >= opleft)
913 					goto toobig;
914 				opleft -= opcount;
915 				op += opcount;
916 			}
917 		}
918 		if (i + 1 < numhunk) {
919 			opcount = snprintf(op, opleft, "%c", comma);
920 			if (opcount < 0 || opcount >= opleft)
921 				goto toobig;
922 			opleft -= opcount;
923 			op += opcount;
924 		}
925 	}
926 
927 done:
928 	return optbuf;
929 
930 toobig:
931 	memset(optbuf, 0, sizeof(optbuf));
932 	return optbuf;
933 }
934 
935 struct option_data *
unpack_options(struct dhcp_packet * packet)936 unpack_options(struct dhcp_packet *packet)
937 {
938 	static struct option_data	 options[DHO_COUNT];
939 	int				 i;
940 
941 	for (i = 0; i < DHO_COUNT; i++) {
942 		free(options[i].data);
943 		options[i].data = NULL;
944 		options[i].len = 0;
945 	}
946 
947 	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
948 		/* Parse the BOOTP/DHCP options field. */
949 		parse_option_buffer(options, &packet->options[4],
950 		    sizeof(packet->options) - 4);
951 
952 		/* DHCP packets can also use overload areas for options. */
953 		if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL &&
954 		    options[DHO_DHCP_OPTION_OVERLOAD].data != NULL) {
955 			if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) !=
956 			    0)
957 				parse_option_buffer(options,
958 				    (unsigned char *)packet->file,
959 				    sizeof(packet->file));
960 			if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) !=
961 			    0)
962 				parse_option_buffer(options,
963 				    (unsigned char *)packet->sname,
964 				    sizeof(packet->sname));
965 		}
966 	}
967 
968 	return options;
969 }
970 
971 void
merge_option_data(char * fmt,struct option_data * first,struct option_data * second,struct option_data * dest)972 merge_option_data(char *fmt, struct option_data *first,
973     struct option_data *second, struct option_data *dest)
974 {
975 	int space = 0;
976 
977 	free(dest->data);
978 	dest->data = NULL;
979 	dest->len = first->len + second->len;
980 	if (dest->len == 0)
981 		return;
982 
983 	/*
984 	 * N.B.: option data is *NOT* guaranteed to be NUL
985 	 *	 terminated. Avoid strlen(), strdup(), etc.!
986 	 */
987 	if (fmt[0] == 'D') {
988 		if (first->len > 0 && second->len > 0)
989 			space = 1;
990 	}
991 
992 	dest->len += space;
993 	dest->data = malloc(dest->len);
994 	if (dest->data == NULL)
995 		fatal("merged option data");
996 
997 	memcpy(dest->data, first->data, first->len);
998 	if (space == 1)
999 		dest->data[first->len] = ' ';
1000 	memcpy(dest->data + first->len + space, second->data, second->len);
1001 }
1002