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