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
65int	parse_option_buffer(struct option_data *, unsigned char *, int);
66void	pretty_print_classless_routes(unsigned char *, size_t, unsigned char *,
67    size_t);
68void	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
90static 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
352char *
353code_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
371int
372name_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
393char *
394code_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 */
410int
411code_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 */
441int
442parse_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 */
524int
525pack_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 */
578char *
579pretty_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 */
612void
613pretty_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
651bad:
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 */
665void
666pretty_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
708bad:
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 */
716char *
717pretty_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
927done:
928	return optbuf;
929
930toobig:
931	memset(optbuf, 0, sizeof(optbuf));
932	return optbuf;
933}
934
935struct option_data *
936unpack_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
971void
972merge_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