xref: /openbsd/sbin/dhclient/options.c (revision 561b23c2)
1*561b23c2Skrw /*	$OpenBSD: options.c,v 1.100 2017/07/14 14:03:15 krw Exp $	*/
29a2590e5Sderaadt 
3e7eb2effShenning /* DHCP options parsing and reassembly. */
49a2590e5Sderaadt 
59a2590e5Sderaadt /*
69a2590e5Sderaadt  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
79a2590e5Sderaadt  * All rights reserved.
89a2590e5Sderaadt  *
99a2590e5Sderaadt  * Redistribution and use in source and binary forms, with or without
109a2590e5Sderaadt  * modification, are permitted provided that the following conditions
119a2590e5Sderaadt  * are met:
129a2590e5Sderaadt  *
139a2590e5Sderaadt  * 1. Redistributions of source code must retain the above copyright
149a2590e5Sderaadt  *    notice, this list of conditions and the following disclaimer.
159a2590e5Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
169a2590e5Sderaadt  *    notice, this list of conditions and the following disclaimer in the
179a2590e5Sderaadt  *    documentation and/or other materials provided with the distribution.
189a2590e5Sderaadt  * 3. Neither the name of The Internet Software Consortium nor the names
199a2590e5Sderaadt  *    of its contributors may be used to endorse or promote products derived
209a2590e5Sderaadt  *    from this software without specific prior written permission.
219a2590e5Sderaadt  *
229a2590e5Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
239a2590e5Sderaadt  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
249a2590e5Sderaadt  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
259a2590e5Sderaadt  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
269a2590e5Sderaadt  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
279a2590e5Sderaadt  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
289a2590e5Sderaadt  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
299a2590e5Sderaadt  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
309a2590e5Sderaadt  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
319a2590e5Sderaadt  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
329a2590e5Sderaadt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
339a2590e5Sderaadt  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
349a2590e5Sderaadt  * SUCH DAMAGE.
359a2590e5Sderaadt  *
369a2590e5Sderaadt  * This software has been written for the Internet Software Consortium
379a2590e5Sderaadt  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
389a2590e5Sderaadt  * Enterprises.  To learn more about the Internet Software Consortium,
399a2590e5Sderaadt  * see ``http://www.vix.com/isc''.  To learn more about Vixie
409a2590e5Sderaadt  * Enterprises, see ``http://www.vix.com''.
419a2590e5Sderaadt  */
429a2590e5Sderaadt 
43711cae1eSkrw #include <sys/queue.h>
44711cae1eSkrw #include <sys/socket.h>
459a2590e5Sderaadt 
46711cae1eSkrw #include <arpa/inet.h>
47711cae1eSkrw 
48711cae1eSkrw #include <net/if.h>
49711cae1eSkrw 
50711cae1eSkrw #include <netinet/in.h>
51711cae1eSkrw #include <netinet/if_ether.h>
52711cae1eSkrw 
53711cae1eSkrw #include <ctype.h>
54711cae1eSkrw #include <signal.h>
55711cae1eSkrw #include <stdio.h>
56711cae1eSkrw #include <stdlib.h>
57711cae1eSkrw #include <string.h>
588d2bd14bSkrw #include <vis.h>
598d2bd14bSkrw 
60711cae1eSkrw #include "dhcp.h"
61711cae1eSkrw #include "dhcpd.h"
62385a6373Skrw #include "log.h"
63711cae1eSkrw 
6402e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int);
65968fe952Skrw int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
669a2590e5Sderaadt 
67c714dadcShenning /*
688457ebc2Skrw  * DHCP Option names, formats and codes, from RFC1533.
698457ebc2Skrw  *
708457ebc2Skrw  * Format codes:
718457ebc2Skrw  *
728457ebc2Skrw  * e - end of data
738457ebc2Skrw  * I - IP address
748457ebc2Skrw  * l - 32-bit signed integer
758457ebc2Skrw  * L - 32-bit unsigned integer
768457ebc2Skrw  * S - 16-bit unsigned integer
778457ebc2Skrw  * B - 8-bit unsigned integer
788457ebc2Skrw  * t - ASCII text
798457ebc2Skrw  * f - flag (true or false)
808457ebc2Skrw  * A - array of whatever precedes (e.g., IA means array of IP addresses)
818457ebc2Skrw  * C - CIDR description
828457ebc2Skrw  */
838457ebc2Skrw 
848457ebc2Skrw static const struct {
858457ebc2Skrw 	char *name;
868457ebc2Skrw 	char *format;
878457ebc2Skrw } dhcp_options[DHO_COUNT] = {
888457ebc2Skrw 	/*   0 */ { "pad", "" },
898457ebc2Skrw 	/*   1 */ { "subnet-mask", "I" },
908457ebc2Skrw 	/*   2 */ { "time-offset", "l" },
918457ebc2Skrw 	/*   3 */ { "routers", "IA" },
928457ebc2Skrw 	/*   4 */ { "time-servers", "IA" },
938457ebc2Skrw 	/*   5 */ { "ien116-name-servers", "IA" },
948457ebc2Skrw 	/*   6 */ { "domain-name-servers", "IA" },
958457ebc2Skrw 	/*   7 */ { "log-servers", "IA" },
968457ebc2Skrw 	/*   8 */ { "cookie-servers", "IA" },
978457ebc2Skrw 	/*   9 */ { "lpr-servers", "IA" },
988457ebc2Skrw 	/*  10 */ { "impress-servers", "IA" },
998457ebc2Skrw 	/*  11 */ { "resource-location-servers", "IA" },
1008457ebc2Skrw 	/*  12 */ { "host-name", "t" },
1018457ebc2Skrw 	/*  13 */ { "boot-size", "S" },
1028457ebc2Skrw 	/*  14 */ { "merit-dump", "t" },
1038457ebc2Skrw 	/*  15 */ { "domain-name", "t" },
1048457ebc2Skrw 	/*  16 */ { "swap-server", "I" },
1058457ebc2Skrw 	/*  17 */ { "root-path", "t" },
1068457ebc2Skrw 	/*  18 */ { "extensions-path", "t" },
1078457ebc2Skrw 	/*  19 */ { "ip-forwarding", "f" },
1088457ebc2Skrw 	/*  20 */ { "non-local-source-routing", "f" },
1098457ebc2Skrw 	/*  21 */ { "policy-filter", "IIA" },
1108457ebc2Skrw 	/*  22 */ { "max-dgram-reassembly", "S" },
1118457ebc2Skrw 	/*  23 */ { "default-ip-ttl", "B" },
1128457ebc2Skrw 	/*  24 */ { "path-mtu-aging-timeout", "L" },
1138457ebc2Skrw 	/*  25 */ { "path-mtu-plateau-table", "SA" },
1148457ebc2Skrw 	/*  26 */ { "interface-mtu", "S" },
1158457ebc2Skrw 	/*  27 */ { "all-subnets-local", "f" },
1168457ebc2Skrw 	/*  28 */ { "broadcast-address", "I" },
1178457ebc2Skrw 	/*  29 */ { "perform-mask-discovery", "f" },
1188457ebc2Skrw 	/*  30 */ { "mask-supplier", "f" },
1198457ebc2Skrw 	/*  31 */ { "router-discovery", "f" },
1208457ebc2Skrw 	/*  32 */ { "router-solicitation-address", "I" },
1218457ebc2Skrw 	/*  33 */ { "static-routes", "IIA" },
1228457ebc2Skrw 	/*  34 */ { "trailer-encapsulation", "f" },
1238457ebc2Skrw 	/*  35 */ { "arp-cache-timeout", "L" },
1248457ebc2Skrw 	/*  36 */ { "ieee802-3-encapsulation", "f" },
1258457ebc2Skrw 	/*  37 */ { "default-tcp-ttl", "B" },
1268457ebc2Skrw 	/*  38 */ { "tcp-keepalive-interval", "L" },
1278457ebc2Skrw 	/*  39 */ { "tcp-keepalive-garbage", "f" },
1288457ebc2Skrw 	/*  40 */ { "nis-domain", "t" },
1298457ebc2Skrw 	/*  41 */ { "nis-servers", "IA" },
1308457ebc2Skrw 	/*  42 */ { "ntp-servers", "IA" },
1318457ebc2Skrw 	/*  43 */ { "vendor-encapsulated-options", "X" },
1328457ebc2Skrw 	/*  44 */ { "netbios-name-servers", "IA" },
1338457ebc2Skrw 	/*  45 */ { "netbios-dd-server", "IA" },
1348457ebc2Skrw 	/*  46 */ { "netbios-node-type", "B" },
1358457ebc2Skrw 	/*  47 */ { "netbios-scope", "t" },
1368457ebc2Skrw 	/*  48 */ { "font-servers", "IA" },
1378457ebc2Skrw 	/*  49 */ { "x-display-manager", "IA" },
1388457ebc2Skrw 	/*  50 */ { "dhcp-requested-address", "I" },
1398457ebc2Skrw 	/*  51 */ { "dhcp-lease-time", "L" },
1408457ebc2Skrw 	/*  52 */ { "dhcp-option-overload", "B" },
1418457ebc2Skrw 	/*  53 */ { "dhcp-message-type", "B" },
1428457ebc2Skrw 	/*  54 */ { "dhcp-server-identifier", "I" },
1438457ebc2Skrw 	/*  55 */ { "dhcp-parameter-request-list", "BA" },
1448457ebc2Skrw 	/*  56 */ { "dhcp-message", "t" },
1458457ebc2Skrw 	/*  57 */ { "dhcp-max-message-size", "S" },
1468457ebc2Skrw 	/*  58 */ { "dhcp-renewal-time", "L" },
1478457ebc2Skrw 	/*  59 */ { "dhcp-rebinding-time", "L" },
1488457ebc2Skrw 	/*  60 */ { "dhcp-class-identifier", "t" },
1498457ebc2Skrw 	/*  61 */ { "dhcp-client-identifier", "X" },
1508457ebc2Skrw 	/*  62 */ { NULL, NULL },
1518457ebc2Skrw 	/*  63 */ { NULL, NULL },
1528457ebc2Skrw 	/*  64 */ { "nisplus-domain", "t" },
1538457ebc2Skrw 	/*  65 */ { "nisplus-servers", "IA" },
1548457ebc2Skrw 	/*  66 */ { "tftp-server-name", "t" },
1558457ebc2Skrw 	/*  67 */ { "bootfile-name", "t" },
1568457ebc2Skrw 	/*  68 */ { "mobile-ip-home-agent", "IA" },
1578457ebc2Skrw 	/*  69 */ { "smtp-server", "IA" },
1588457ebc2Skrw 	/*  70 */ { "pop-server", "IA" },
1598457ebc2Skrw 	/*  71 */ { "nntp-server", "IA" },
1608457ebc2Skrw 	/*  72 */ { "www-server", "IA" },
1618457ebc2Skrw 	/*  73 */ { "finger-server", "IA" },
1628457ebc2Skrw 	/*  74 */ { "irc-server", "IA" },
1638457ebc2Skrw 	/*  75 */ { "streettalk-server", "IA" },
1648457ebc2Skrw 	/*  76 */ { "streettalk-directory-assistance-server", "IA" },
1658457ebc2Skrw 	/*  77 */ { "user-class", "t" },
1668457ebc2Skrw 	/*  78 */ { NULL, NULL },
1678457ebc2Skrw 	/*  79 */ { NULL, NULL },
1688457ebc2Skrw 	/*  80 */ { NULL, NULL },
1698457ebc2Skrw 	/*  81 */ { NULL, NULL },
1708457ebc2Skrw 	/*  82 */ { "relay-agent-information", "X" },
1718457ebc2Skrw 	/*  83 */ { NULL, NULL },
1728457ebc2Skrw 	/*  84 */ { NULL, NULL },
1738457ebc2Skrw 	/*  85 */ { "nds-servers", "IA" },
1748457ebc2Skrw 	/*  86 */ { "nds-tree-name", "X" },
1758457ebc2Skrw 	/*  87 */ { "nds-context", "X" },
1768457ebc2Skrw 	/*  88 */ { NULL, NULL },
1778457ebc2Skrw 	/*  89 */ { NULL, NULL },
1788457ebc2Skrw 	/*  90 */ { NULL, NULL },
1798457ebc2Skrw 	/*  91 */ { NULL, NULL },
1808457ebc2Skrw 	/*  92 */ { NULL, NULL },
1818457ebc2Skrw 	/*  93 */ { NULL, NULL },
1828457ebc2Skrw 	/*  94 */ { NULL, NULL },
1838457ebc2Skrw 	/*  95 */ { NULL, NULL },
1848457ebc2Skrw 	/*  96 */ { NULL, NULL },
1858457ebc2Skrw 	/*  97 */ { NULL, NULL },
1868457ebc2Skrw 	/*  98 */ { NULL, NULL },
1878457ebc2Skrw 	/*  99 */ { NULL, NULL },
1888457ebc2Skrw 	/* 100 */ { NULL, NULL },
1898457ebc2Skrw 	/* 101 */ { NULL, NULL },
1908457ebc2Skrw 	/* 102 */ { NULL, NULL },
1918457ebc2Skrw 	/* 103 */ { NULL, NULL },
1928457ebc2Skrw 	/* 104 */ { NULL, NULL },
1938457ebc2Skrw 	/* 105 */ { NULL, NULL },
1948457ebc2Skrw 	/* 106 */ { NULL, NULL },
1958457ebc2Skrw 	/* 107 */ { NULL, NULL },
1968457ebc2Skrw 	/* 108 */ { NULL, NULL },
1978457ebc2Skrw 	/* 109 */ { NULL, NULL },
1988457ebc2Skrw 	/* 110 */ { NULL, NULL },
1998457ebc2Skrw 	/* 111 */ { NULL, NULL },
2008457ebc2Skrw 	/* 112 */ { NULL, NULL },
2018457ebc2Skrw 	/* 113 */ { NULL, NULL },
2028457ebc2Skrw 	/* 114 */ { NULL, NULL },
2038457ebc2Skrw 	/* 115 */ { NULL, NULL },
2048457ebc2Skrw 	/* 116 */ { NULL, NULL },
2058457ebc2Skrw 	/* 117 */ { NULL, NULL },
2068457ebc2Skrw 	/* 118 */ { NULL, NULL },
2078457ebc2Skrw 	/* 119 */ { "domain-search", "X" },
2088457ebc2Skrw 	/* 120 */ { NULL, NULL },
2098457ebc2Skrw 	/* 121 */ { "classless-static-routes", "CIA" },
2108457ebc2Skrw 	/* 122 */ { NULL, NULL },
2118457ebc2Skrw 	/* 123 */ { NULL, NULL },
2128457ebc2Skrw 	/* 124 */ { NULL, NULL },
2138457ebc2Skrw 	/* 125 */ { NULL, NULL },
2148457ebc2Skrw 	/* 126 */ { NULL, NULL },
2158457ebc2Skrw 	/* 127 */ { NULL, NULL },
2168457ebc2Skrw 	/* 128 */ { NULL, NULL },
2178457ebc2Skrw 	/* 129 */ { NULL, NULL },
2188457ebc2Skrw 	/* 130 */ { NULL, NULL },
2198457ebc2Skrw 	/* 131 */ { NULL, NULL },
2208457ebc2Skrw 	/* 132 */ { NULL, NULL },
2218457ebc2Skrw 	/* 133 */ { NULL, NULL },
2228457ebc2Skrw 	/* 134 */ { NULL, NULL },
2238457ebc2Skrw 	/* 135 */ { NULL, NULL },
2248457ebc2Skrw 	/* 136 */ { NULL, NULL },
2258457ebc2Skrw 	/* 137 */ { NULL, NULL },
2268457ebc2Skrw 	/* 138 */ { NULL, NULL },
2278457ebc2Skrw 	/* 139 */ { NULL, NULL },
2288457ebc2Skrw 	/* 140 */ { NULL, NULL },
2298457ebc2Skrw 	/* 141 */ { NULL, NULL },
2308457ebc2Skrw 	/* 142 */ { NULL, NULL },
2318457ebc2Skrw 	/* 143 */ { NULL, NULL },
2328457ebc2Skrw 	/* 144 */ { "tftp-config-file", "t" },
2338457ebc2Skrw 	/* 145 */ { NULL, NULL },
2348457ebc2Skrw 	/* 146 */ { NULL, NULL },
2358457ebc2Skrw 	/* 147 */ { NULL, NULL },
2368457ebc2Skrw 	/* 148 */ { NULL, NULL },
2378457ebc2Skrw 	/* 149 */ { NULL, NULL },
2388457ebc2Skrw 	/* 150 */ { "voip-configuration-server", "IA" },
2398457ebc2Skrw 	/* 151 */ { NULL, NULL },
2408457ebc2Skrw 	/* 152 */ { NULL, NULL },
2418457ebc2Skrw 	/* 153 */ { NULL, NULL },
2428457ebc2Skrw 	/* 154 */ { NULL, NULL },
2438457ebc2Skrw 	/* 155 */ { NULL, NULL },
2448457ebc2Skrw 	/* 156 */ { NULL, NULL },
2458457ebc2Skrw 	/* 157 */ { NULL, NULL },
2468457ebc2Skrw 	/* 158 */ { NULL, NULL },
2478457ebc2Skrw 	/* 159 */ { NULL, NULL },
2488457ebc2Skrw 	/* 160 */ { NULL, NULL },
2498457ebc2Skrw 	/* 161 */ { NULL, NULL },
2508457ebc2Skrw 	/* 162 */ { NULL, NULL },
2518457ebc2Skrw 	/* 163 */ { NULL, NULL },
2528457ebc2Skrw 	/* 164 */ { NULL, NULL },
2538457ebc2Skrw 	/* 165 */ { NULL, NULL },
2548457ebc2Skrw 	/* 166 */ { NULL, NULL },
2558457ebc2Skrw 	/* 167 */ { NULL, NULL },
2568457ebc2Skrw 	/* 168 */ { NULL, NULL },
2578457ebc2Skrw 	/* 169 */ { NULL, NULL },
2588457ebc2Skrw 	/* 170 */ { NULL, NULL },
2598457ebc2Skrw 	/* 171 */ { NULL, NULL },
2608457ebc2Skrw 	/* 172 */ { NULL, NULL },
2618457ebc2Skrw 	/* 173 */ { NULL, NULL },
2628457ebc2Skrw 	/* 174 */ { NULL, NULL },
2638457ebc2Skrw 	/* 175 */ { NULL, NULL },
2648457ebc2Skrw 	/* 176 */ { NULL, NULL },
2658457ebc2Skrw 	/* 177 */ { NULL, NULL },
2668457ebc2Skrw 	/* 178 */ { NULL, NULL },
2678457ebc2Skrw 	/* 179 */ { NULL, NULL },
2688457ebc2Skrw 	/* 180 */ { NULL, NULL },
2698457ebc2Skrw 	/* 181 */ { NULL, NULL },
2708457ebc2Skrw 	/* 182 */ { NULL, NULL },
2718457ebc2Skrw 	/* 183 */ { NULL, NULL },
2728457ebc2Skrw 	/* 184 */ { NULL, NULL },
2738457ebc2Skrw 	/* 185 */ { NULL, NULL },
2748457ebc2Skrw 	/* 186 */ { NULL, NULL },
2758457ebc2Skrw 	/* 187 */ { NULL, NULL },
2768457ebc2Skrw 	/* 188 */ { NULL, NULL },
2778457ebc2Skrw 	/* 189 */ { NULL, NULL },
2788457ebc2Skrw 	/* 190 */ { NULL, NULL },
2798457ebc2Skrw 	/* 191 */ { NULL, NULL },
2808457ebc2Skrw 	/* 192 */ { NULL, NULL },
2818457ebc2Skrw 	/* 193 */ { NULL, NULL },
2828457ebc2Skrw 	/* 194 */ { NULL, NULL },
2838457ebc2Skrw 	/* 195 */ { NULL, NULL },
2848457ebc2Skrw 	/* 196 */ { NULL, NULL },
2858457ebc2Skrw 	/* 197 */ { NULL, NULL },
2868457ebc2Skrw 	/* 198 */ { NULL, NULL },
2878457ebc2Skrw 	/* 199 */ { NULL, NULL },
2888457ebc2Skrw 	/* 200 */ { NULL, NULL },
2898457ebc2Skrw 	/* 201 */ { NULL, NULL },
2908457ebc2Skrw 	/* 202 */ { NULL, NULL },
2918457ebc2Skrw 	/* 203 */ { NULL, NULL },
2928457ebc2Skrw 	/* 204 */ { NULL, NULL },
2938457ebc2Skrw 	/* 205 */ { NULL, NULL },
2948457ebc2Skrw 	/* 206 */ { NULL, NULL },
2958457ebc2Skrw 	/* 207 */ { NULL, NULL },
2968457ebc2Skrw 	/* 208 */ { NULL, NULL },
2978457ebc2Skrw 	/* 209 */ { NULL, NULL },
2988457ebc2Skrw 	/* 210 */ { NULL, NULL },
2998457ebc2Skrw 	/* 211 */ { NULL, NULL },
3008457ebc2Skrw 	/* 212 */ { NULL, NULL },
3018457ebc2Skrw 	/* 213 */ { NULL, NULL },
3028457ebc2Skrw 	/* 214 */ { NULL, NULL },
3038457ebc2Skrw 	/* 215 */ { NULL, NULL },
3048457ebc2Skrw 	/* 216 */ { NULL, NULL },
3058457ebc2Skrw 	/* 217 */ { NULL, NULL },
3068457ebc2Skrw 	/* 218 */ { NULL, NULL },
3078457ebc2Skrw 	/* 219 */ { NULL, NULL },
3088457ebc2Skrw 	/* 220 */ { NULL, NULL },
3098457ebc2Skrw 	/* 221 */ { NULL, NULL },
3108457ebc2Skrw 	/* 222 */ { NULL, NULL },
3118457ebc2Skrw 	/* 223 */ { NULL, NULL },
3128457ebc2Skrw 	/* 224 */ { NULL, NULL },
3138457ebc2Skrw 	/* 225 */ { NULL, NULL },
3148457ebc2Skrw 	/* 226 */ { NULL, NULL },
3158457ebc2Skrw 	/* 227 */ { NULL, NULL },
3168457ebc2Skrw 	/* 228 */ { NULL, NULL },
3178457ebc2Skrw 	/* 229 */ { NULL, NULL },
3188457ebc2Skrw 	/* 230 */ { NULL, NULL },
3198457ebc2Skrw 	/* 231 */ { NULL, NULL },
3208457ebc2Skrw 	/* 232 */ { NULL, NULL },
3218457ebc2Skrw 	/* 233 */ { NULL, NULL },
3228457ebc2Skrw 	/* 234 */ { NULL, NULL },
3238457ebc2Skrw 	/* 235 */ { NULL, NULL },
3248457ebc2Skrw 	/* 236 */ { NULL, NULL },
3258457ebc2Skrw 	/* 237 */ { NULL, NULL },
3268457ebc2Skrw 	/* 238 */ { NULL, NULL },
3278457ebc2Skrw 	/* 239 */ { NULL, NULL },
3288457ebc2Skrw 	/* 240 */ { NULL, NULL },
3298457ebc2Skrw 	/* 241 */ { NULL, NULL },
3308457ebc2Skrw 	/* 242 */ { NULL, NULL },
3318457ebc2Skrw 	/* 243 */ { NULL, NULL },
3328457ebc2Skrw 	/* 244 */ { NULL, NULL },
3338457ebc2Skrw 	/* 245 */ { NULL, NULL },
3348457ebc2Skrw 	/* 246 */ { NULL, NULL },
3358457ebc2Skrw 	/* 247 */ { NULL, NULL },
3368457ebc2Skrw 	/* 248 */ { NULL, NULL },
3378457ebc2Skrw 	/* 249 */ { "classless-ms-static-routes", "CIA" },
3388457ebc2Skrw 	/* 250 */ { NULL, NULL },
3398457ebc2Skrw 	/* 251 */ { NULL, NULL },
3408457ebc2Skrw 	/* 252 */ { "autoproxy-script", "t" },
3418457ebc2Skrw 	/* 253 */ { NULL, NULL },
3428457ebc2Skrw 	/* 254 */ { NULL, NULL },
3438457ebc2Skrw 	/* 255 */ { "option-end", "e" },
3448457ebc2Skrw };
3458457ebc2Skrw 
3468457ebc2Skrw char *
3478457ebc2Skrw code_to_name(int code)
3488457ebc2Skrw {
3498457ebc2Skrw 	static char	 unknown[11];	/* "option-NNN" */
3508457ebc2Skrw 	int		 ret;
3518457ebc2Skrw 
3528457ebc2Skrw 	if (code < 0 || code >= DHO_COUNT)
3538457ebc2Skrw 		return "";
3548457ebc2Skrw 
3558457ebc2Skrw 	if (dhcp_options[code].name != NULL)
3568457ebc2Skrw 		return dhcp_options[code].name;
3578457ebc2Skrw 
3588457ebc2Skrw 	ret = snprintf(unknown, sizeof(unknown), "option-%d", code);
3598457ebc2Skrw 	if (ret == -1 || ret >= (int)sizeof(unknown))
3608457ebc2Skrw 		return "";
3618457ebc2Skrw 
3628457ebc2Skrw 	return unknown;
3638457ebc2Skrw }
3648457ebc2Skrw 
3658457ebc2Skrw int
3668457ebc2Skrw name_to_code(char *name)
3678457ebc2Skrw {
3688457ebc2Skrw 	char	unknown[11];	/* "option-NNN" */
3698457ebc2Skrw 	int	code, ret;
3708457ebc2Skrw 
3718457ebc2Skrw 	for (code = 1; code < DHO_END; code++) {
3728457ebc2Skrw 		if (dhcp_options[code].name == NULL) {
3738457ebc2Skrw 			ret = snprintf(unknown, sizeof(unknown), "option-%d",
3748457ebc2Skrw 			    code);
3758457ebc2Skrw 			if (ret == -1 || ret >= (int)sizeof(unknown))
3768457ebc2Skrw 				return DHO_END;
3778457ebc2Skrw 			if (strcasecmp(unknown, name) == 0)
3788457ebc2Skrw 				return code;
3798457ebc2Skrw 		} else if (strcasecmp(dhcp_options[code].name, name) == 0) {
3808457ebc2Skrw 			return code;
3818457ebc2Skrw 		}
3828457ebc2Skrw 	}
3838457ebc2Skrw 
3848457ebc2Skrw 	return DHO_END;
3858457ebc2Skrw }
3868457ebc2Skrw 
3878457ebc2Skrw char *
3888457ebc2Skrw code_to_format(int code)
3898457ebc2Skrw {
3908457ebc2Skrw 	if (code < 0 || code >= DHO_COUNT)
3918457ebc2Skrw 		return "";
3928457ebc2Skrw 
3938457ebc2Skrw 	if (dhcp_options[code].format == NULL)
3948457ebc2Skrw 		return "X";
3958457ebc2Skrw 
3968457ebc2Skrw 	return dhcp_options[code].format;
3978457ebc2Skrw }
3988457ebc2Skrw 
3998457ebc2Skrw /*
400c714dadcShenning  * Parse options out of the specified buffer, storing addresses of
40192018899Skrw  * option values in options. Return 0 if errors, 1 if not.
402c714dadcShenning  */
40302e02bd5Skrw int
4044f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer,
4054f062ee3Skrw     int length)
4069a2590e5Sderaadt {
407285f06efSderaadt 	unsigned char	*s, *t, *end = buffer + length;
4088457ebc2Skrw 	char		*name, *fmt;
409285f06efSderaadt 	int		 len, code;
4109a2590e5Sderaadt 
4119a2590e5Sderaadt 	for (s = buffer; *s != DHO_END && s < end; ) {
4129a2590e5Sderaadt 		code = s[0];
4139a2590e5Sderaadt 
4149a2590e5Sderaadt 		/* Pad options don't have a length - just skip them. */
4159a2590e5Sderaadt 		if (code == DHO_PAD) {
416f1e89499Shenning 			s++;
4179a2590e5Sderaadt 			continue;
4189a2590e5Sderaadt 		}
4199a2590e5Sderaadt 
4208457ebc2Skrw 		name = code_to_name(code);
4218457ebc2Skrw 		fmt = code_to_format(code);
4228457ebc2Skrw 
423c714dadcShenning 		/*
42499c003b1Skrw 		 * All options other than DHO_PAD and DHO_END have a one-byte
42599c003b1Skrw 		 * length field. It could be 0! Make sure that the length byte
42699c003b1Skrw 		 * is present, and all the data is available.
427c714dadcShenning 		 */
42899c003b1Skrw 		if (s + 1 < end) {
4299a2590e5Sderaadt 			len = s[1];
43099c003b1Skrw 			if (s + 1 + len < end) {
43199c003b1Skrw 				; /* option data is all there. */
43299c003b1Skrw 			} else {
433385a6373Skrw 				log_warnx("option %s (%d) larger than buffer.",
4348457ebc2Skrw 				    name, len);
43551a35423Skrw 				return 0;
4369a2590e5Sderaadt 			}
43799c003b1Skrw 		} else {
4388457ebc2Skrw 			log_warnx("option %s has no length field.", name);
43951a35423Skrw 			return 0;
44099c003b1Skrw 		}
441df453039Skrw 
442df453039Skrw 		/*
443df453039Skrw 		 * Strip trailing NULs from ascii ('t') options. They
444df453039Skrw 		 * will be treated as DHO_PAD options. i.e. ignored. RFC 2132
445df453039Skrw 		 * says "Options containing NVT ASCII data SHOULD NOT include
446df453039Skrw 		 * a trailing NULL; however, the receiver of such options
447df453039Skrw 		 * MUST be prepared to delete trailing nulls if they exist."
448df453039Skrw 		 */
4498457ebc2Skrw 		if (fmt[0] == 't') {
45099c003b1Skrw 			while (len > 0 && s[len + 1] == '\0')
45199c003b1Skrw 				len--;
452df453039Skrw 		}
453df453039Skrw 
454c714dadcShenning 		/*
455c714dadcShenning 		 * If we haven't seen this option before, just make
456c714dadcShenning 		 * space for it and copy it there.
457c714dadcShenning 		 */
458*561b23c2Skrw 		if (options[code].data == NULL) {
459*561b23c2Skrw 			t = calloc(1, len + 1);
460*561b23c2Skrw 			if (t == NULL)
461385a6373Skrw 				fatalx("Can't allocate storage for option %s.",
4628457ebc2Skrw 				    name);
463c714dadcShenning 			/*
464c714dadcShenning 			 * Copy and NUL-terminate the option (in case
465cff08477Sstevesk 			 * it's an ASCII string).
466c714dadcShenning 			 */
4679a2590e5Sderaadt 			memcpy(t, &s[2], len);
4689a2590e5Sderaadt 			t[len] = 0;
4694f062ee3Skrw 			options[code].len = len;
4704f062ee3Skrw 			options[code].data = t;
4719a2590e5Sderaadt 		} else {
472c714dadcShenning 			/*
473c714dadcShenning 			 * If it's a repeat, concatenate it to whatever
47492018899Skrw 			 * we last saw.
475c714dadcShenning 			 */
4764f062ee3Skrw 			t = calloc(1, len + options[code].len + 1);
477*561b23c2Skrw 			if (t == NULL)
478385a6373Skrw 				fatalx("Can't expand storage for option %s.",
4798457ebc2Skrw 				    name);
4804f062ee3Skrw 			memcpy(t, options[code].data, options[code].len);
4814f062ee3Skrw 			memcpy(t + options[code].len, &s[2], len);
4824f062ee3Skrw 			options[code].len += len;
4834f062ee3Skrw 			t[options[code].len] = 0;
4844f062ee3Skrw 			free(options[code].data);
4854f062ee3Skrw 			options[code].data = t;
4869a2590e5Sderaadt 		}
4879a2590e5Sderaadt 		s += len + 2;
4889a2590e5Sderaadt 	}
48902e02bd5Skrw 
49051a35423Skrw 	return 1;
4919a2590e5Sderaadt }
4929a2590e5Sderaadt 
493c714dadcShenning /*
494b414edd1Skrw  * Pack as many options as fit in buflen bytes of buf. Return the
49596978980Skrw  * offset of the start of the last option copied. A caller can check
49696978980Skrw  * to see if it's DHO_END to decide if all the options were copied.
497c714dadcShenning  */
498c714dadcShenning int
499b414edd1Skrw pack_options(unsigned char *buf, int buflen, struct option_data *options)
5009a2590e5Sderaadt {
50196978980Skrw 	int	 ix, incr, length, bufix, code, lastopt = -1;
5029a2590e5Sderaadt 
503736b0ed2Skrw 	memset(buf, 0, buflen);
5049a2590e5Sderaadt 
50596978980Skrw 	memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
506d6a67f0fSkrw 	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
507d6a67f0fSkrw 		memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
508d6a67f0fSkrw 		buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
509d6a67f0fSkrw 		bufix = 7;
510d6a67f0fSkrw 	} else
51196978980Skrw 		bufix = 4;
5129a2590e5Sderaadt 
51396978980Skrw 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
514*561b23c2Skrw 		if (options[code].data == NULL ||
515*561b23c2Skrw 		    code == DHO_DHCP_MESSAGE_TYPE)
5169a2590e5Sderaadt 			continue;
5179a2590e5Sderaadt 
518d7d9bbf5Skrw 		length = options[code].len;
51996978980Skrw 		if (bufix + length + 2*((length+254)/255) >= buflen)
52051a35423Skrw 			return lastopt;
5219a2590e5Sderaadt 
52296978980Skrw 		lastopt = bufix;
5239a2590e5Sderaadt 		ix = 0;
5249a2590e5Sderaadt 
5259a2590e5Sderaadt 		while (length) {
52696978980Skrw 			incr = length > 255 ? 255 : length;
5279a2590e5Sderaadt 
52896978980Skrw 			buf[bufix++] = code;
52996978980Skrw 			buf[bufix++] = incr;
53096978980Skrw 			memcpy(buf + bufix, options[code].data + ix, incr);
5319a2590e5Sderaadt 
5329a2590e5Sderaadt 			length -= incr;
5339a2590e5Sderaadt 			ix += incr;
5346fc9f4f6Skrw 			bufix += incr;
5359a2590e5Sderaadt 		}
5369a2590e5Sderaadt 	}
53796978980Skrw 
53896978980Skrw 	if (bufix < buflen) {
53996978980Skrw 		buf[bufix] = DHO_END;
54096978980Skrw 		lastopt = bufix;
54196978980Skrw 	}
54296978980Skrw 
54351a35423Skrw 	return lastopt;
5449a2590e5Sderaadt }
5459a2590e5Sderaadt 
546c714dadcShenning /*
547482123e8Skrw  * Use vis() to encode characters of src and append encoded characters onto
548482123e8Skrw  * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be
549482123e8Skrw  * represented as '"' delimited strings and safely passed to scripts. Surround
550482123e8Skrw  * result with double quotes if emit_punct is true.
551482123e8Skrw  */
552bee06f07Skrw char *
553bee06f07Skrw pretty_print_string(unsigned char *src, size_t srclen, int emit_punct)
554482123e8Skrw {
555bee06f07Skrw 	static char	 string[8196];
556482123e8Skrw 	char		 visbuf[5];
557482123e8Skrw 	unsigned char	*origsrc = src;
558bee06f07Skrw 	size_t		 rslt = 0;
559482123e8Skrw 
560bee06f07Skrw 	memset(string, 0, sizeof(string));
561bee06f07Skrw 
562bee06f07Skrw 	if (emit_punct)
563bee06f07Skrw 		rslt = strlcat(string, "\"", sizeof(string));
564482123e8Skrw 
565482123e8Skrw 	for (; src < origsrc + srclen; src++) {
566482123e8Skrw 		if (*src && strchr("\"'$`\\", *src))
567642cc348Skrw 			vis(visbuf, *src, VIS_ALL | VIS_OCTAL, *src+1);
568642cc348Skrw 		else
569482123e8Skrw 			vis(visbuf, *src, VIS_OCTAL, *src+1);
570bee06f07Skrw 		rslt = strlcat(string, visbuf, sizeof(string));
571482123e8Skrw 	}
572482123e8Skrw 
573bee06f07Skrw 	if (emit_punct)
574bee06f07Skrw 		rslt = strlcat(string, "\"", sizeof(string));
575bee06f07Skrw 
576bee06f07Skrw 	if (rslt >= sizeof(string))
57751a35423Skrw 		return NULL;
578bee06f07Skrw 
57951a35423Skrw 	return string;
580482123e8Skrw }
581482123e8Skrw 
582482123e8Skrw /*
5835714f486Skrw  * Must special case *_CLASSLESS_* route options due to the variable size
5845714f486Skrw  * of the CIDR element in its CIA format.
5855714f486Skrw  */
586da2eb076Skrw char *
587da2eb076Skrw pretty_print_classless_routes(unsigned char *src, size_t srclen)
5885714f486Skrw {
589da2eb076Skrw 	static char	 string[8196];
590da2eb076Skrw 	char		 bitsbuf[5];	/* to hold "/nn " */
591da2eb076Skrw 	struct in_addr	 net, gateway;
592e0a6d2f1Skrw 	unsigned int	 bytes;
593e0a6d2f1Skrw 	int		 bits, rslt;
5945714f486Skrw 
595da2eb076Skrw 	memset(string, 0, sizeof(string));
596da2eb076Skrw 
597da2eb076Skrw 	while (srclen) {
5985714f486Skrw 		bits = *src;
5995714f486Skrw 		src++;
6005714f486Skrw 		srclen--;
601da2eb076Skrw 
6025714f486Skrw 		bytes = (bits + 7) / 8;
603da2eb076Skrw 		if (srclen < (bytes + sizeof(gateway.s_addr)) ||
604da2eb076Skrw 		    bytes > sizeof(net.s_addr))
60551a35423Skrw 			return NULL;
606da2eb076Skrw 		rslt = snprintf(bitsbuf, sizeof(bitsbuf), "/%d ", bits);
6071be0b429Skrw 		if (rslt == -1 || (unsigned int)rslt >= sizeof(bitsbuf))
60851a35423Skrw 			return NULL;
609da2eb076Skrw 
610da2eb076Skrw 		memset(&net, 0, sizeof(net));
611da2eb076Skrw 		memcpy(&net.s_addr, src, bytes);
6125714f486Skrw 		src += bytes;
6135714f486Skrw 		srclen -= bytes;
614da2eb076Skrw 
6155714f486Skrw 		memcpy(&gateway.s_addr, src, sizeof(gateway.s_addr));
6165714f486Skrw 		src += sizeof(gateway.s_addr);
6175714f486Skrw 		srclen -= sizeof(gateway.s_addr);
618da2eb076Skrw 
619da2eb076Skrw 		if (strlen(string) > 0)
620da2eb076Skrw 			strlcat(string, ", ", sizeof(string));
621da2eb076Skrw 		strlcat(string, inet_ntoa(net), sizeof(string));
622da2eb076Skrw 		strlcat(string, bitsbuf, sizeof(string));
6231be0b429Skrw 		if (strlcat(string, inet_ntoa(gateway), sizeof(string)) >=
6241be0b429Skrw 		    sizeof(string))
62551a35423Skrw 			return NULL;
6265714f486Skrw 	}
6275714f486Skrw 
62851a35423Skrw 	return string;
6295714f486Skrw }
6305714f486Skrw 
631968fe952Skrw int
632968fe952Skrw expand_search_domain_name(unsigned char *src, size_t srclen, int *offset,
633968fe952Skrw     unsigned char *domain_search)
634968fe952Skrw {
6355d0bc970Skrw 	char		*cursor;
636e0a6d2f1Skrw 	unsigned int	 i;
637e0a6d2f1Skrw 	int		 domain_name_len, label_len, pointer, pointed_len;
638968fe952Skrw 
639968fe952Skrw 	cursor = domain_search + strlen(domain_search);
640968fe952Skrw 	domain_name_len = 0;
641968fe952Skrw 
642968fe952Skrw 	i = *offset;
643968fe952Skrw 	while (i <= srclen) {
644968fe952Skrw 		label_len = src[i];
645968fe952Skrw 		if (label_len == 0) {
646968fe952Skrw 			/*
647968fe952Skrw 			 * A zero-length label marks the end of this
648968fe952Skrw 			 * domain name.
649968fe952Skrw 			 */
650968fe952Skrw 			*offset = i + 1;
65151a35423Skrw 			return domain_name_len;
652968fe952Skrw 		} else if (label_len & 0xC0) {
653968fe952Skrw 			/* This is a pointer to another list of labels. */
654968fe952Skrw 			if (i + 1 >= srclen) {
655968fe952Skrw 				/* The pointer is truncated. */
656385a6373Skrw 				log_warnx("Truncated pointer in DHCP Domain "
657968fe952Skrw 				    "Search option.");
65851a35423Skrw 				return -1;
659968fe952Skrw 			}
660968fe952Skrw 
661968fe952Skrw 			pointer = ((label_len & ~(0xC0)) << 8) + src[i + 1];
662968fe952Skrw 			if (pointer >= *offset) {
663968fe952Skrw 				/*
664968fe952Skrw 				 * The pointer must indicates a prior
665968fe952Skrw 				 * occurance.
666968fe952Skrw 				 */
667385a6373Skrw 				log_warnx("Invalid forward pointer in DHCP "
668968fe952Skrw 				    "Domain Search option compression.");
66951a35423Skrw 				return -1;
670968fe952Skrw 			}
671968fe952Skrw 
672968fe952Skrw 			pointed_len = expand_search_domain_name(src, srclen,
673968fe952Skrw 			    &pointer, domain_search);
674968fe952Skrw 			domain_name_len += pointed_len;
675968fe952Skrw 
676968fe952Skrw 			*offset = i + 2;
67751a35423Skrw 			return domain_name_len;
678968fe952Skrw 		}
679968fe952Skrw 		if (i + label_len + 1 > srclen) {
680385a6373Skrw 			log_warnx("Truncated label in DHCP Domain Search "
681968fe952Skrw 			    "option.");
68251a35423Skrw 			return -1;
683968fe952Skrw 		}
684968fe952Skrw 		/*
685968fe952Skrw 		 * Update the domain name length with the length of the
686968fe952Skrw 		 * current label, plus a trailing dot ('.').
687968fe952Skrw 		 */
688968fe952Skrw 		domain_name_len += label_len + 1;
689968fe952Skrw 
690968fe952Skrw 		if (strlen(domain_search) + domain_name_len >=
691968fe952Skrw 		    DHCP_DOMAIN_SEARCH_LEN) {
692385a6373Skrw 			log_warnx("Domain search list too long.");
69351a35423Skrw 			return -1;
694968fe952Skrw 		}
695968fe952Skrw 
696968fe952Skrw 		/* Copy the label found. */
697968fe952Skrw 		memcpy(cursor, src + i + 1, label_len);
698968fe952Skrw 		cursor[label_len] = '.';
699968fe952Skrw 
700968fe952Skrw 		/* Move cursor. */
701968fe952Skrw 		i += label_len + 1;
702968fe952Skrw 		cursor += label_len + 1;
703968fe952Skrw 	}
704968fe952Skrw 
705385a6373Skrw 	log_warnx("Truncated DHCP Domain Search option.");
706968fe952Skrw 
70751a35423Skrw 	return -1;
708968fe952Skrw }
709968fe952Skrw 
710968fe952Skrw /*
711968fe952Skrw  * Must special case DHO_DOMAIN_SEARCH because it is encoded as described
712968fe952Skrw  * in RFC 1035 section 4.1.4.
713968fe952Skrw  */
7144d36d16aSkrw char *
7154d36d16aSkrw pretty_print_domain_search(unsigned char *src, size_t srclen)
716968fe952Skrw {
7174d36d16aSkrw 	static char	 domain_search[DHCP_DOMAIN_SEARCH_LEN];
7185d0bc970Skrw 	unsigned char	*cursor;
719e0a6d2f1Skrw 	unsigned int	 offset;
720e0a6d2f1Skrw 	int		 len, expanded_len, domains;
721968fe952Skrw 
7224d36d16aSkrw 	memset(domain_search, 0, sizeof(domain_search));
723968fe952Skrw 
724968fe952Skrw 	/* Compute expanded length. */
725968fe952Skrw 	expanded_len = len = 0;
726968fe952Skrw 	domains = 0;
727968fe952Skrw 	offset = 0;
728968fe952Skrw 	while (offset < srclen) {
729968fe952Skrw 		cursor = domain_search + strlen(domain_search);
730968fe952Skrw 		if (domain_search[0]) {
731968fe952Skrw 			*cursor = ' ';
732968fe952Skrw 			expanded_len++;
733968fe952Skrw 		}
734968fe952Skrw 		len = expand_search_domain_name(src, srclen, &offset,
735968fe952Skrw 		    domain_search);
7364d36d16aSkrw 		if (len == -1)
73751a35423Skrw 			return NULL;
738968fe952Skrw 		domains++;
739968fe952Skrw 		expanded_len += len;
7404d36d16aSkrw 		if (domains > DHCP_DOMAIN_SEARCH_CNT)
74151a35423Skrw 			return NULL;
742968fe952Skrw 	}
743968fe952Skrw 
74451a35423Skrw 	return domain_search;
745968fe952Skrw }
746968fe952Skrw 
7475714f486Skrw /*
748c714dadcShenning  * Format the specified option so that a human can easily read it.
749c714dadcShenning  */
750c714dadcShenning char *
751acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option,
752acf4c28bSkrw     int emit_punct)
7539a2590e5Sderaadt {
754bee06f07Skrw 	static char	 optbuf[8192]; /* XXX */
755024801d2Skrw 	char		 fmtbuf[32];
756024801d2Skrw 	struct in_addr	 foo;
757acf4c28bSkrw 	unsigned char	*data = option->data;
7589a2590e5Sderaadt 	unsigned char	*dp = data;
7598457ebc2Skrw 	char		*op = optbuf, *buf, *name, *fmt;
760024801d2Skrw 	int		 hunksize = 0, numhunk = -1, numelem = 0;
761024801d2Skrw 	int		 i, j, k, opleft = sizeof(optbuf);
762acf4c28bSkrw 	int		 len = option->len;
763f3a8c5fdSkrw 	int		 opcount = 0;
764bce09e58Skrw 	int32_t		 int32val;
765024801d2Skrw 	uint32_t	 uint32val;
766024801d2Skrw 	uint16_t	 uint16val;
767024801d2Skrw 	char		 comma;
7689a2590e5Sderaadt 
7692f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
7702f18daabSkrw 
7719a2590e5Sderaadt 	/* Code should be between 0 and 255. */
7722f18daabSkrw 	if (code > 255) {
773385a6373Skrw 		log_warnx("pretty_print_option: bad code %d", code);
7742f18daabSkrw 		goto done;
7752f18daabSkrw 	}
7769a2590e5Sderaadt 
777acf4c28bSkrw 	if (emit_punct)
7789a2590e5Sderaadt 		comma = ',';
7799a2590e5Sderaadt 	else
7809a2590e5Sderaadt 		comma = ' ';
7819a2590e5Sderaadt 
7825714f486Skrw 	/* Handle the princess class options with weirdo formats. */
7835714f486Skrw 	switch (code) {
7845714f486Skrw 	case DHO_CLASSLESS_STATIC_ROUTES:
7855714f486Skrw 	case DHO_CLASSLESS_MS_STATIC_ROUTES:
786da2eb076Skrw 		buf = pretty_print_classless_routes(dp, len);
787da2eb076Skrw 		if (buf == NULL)
7885714f486Skrw 			goto toobig;
789da2eb076Skrw 		strlcat(optbuf, buf, sizeof(optbuf));
7905714f486Skrw 		goto done;
7915714f486Skrw 	default:
7925714f486Skrw 		break;
7935714f486Skrw 	}
7945714f486Skrw 
7958457ebc2Skrw 	name = code_to_name(code);
7968457ebc2Skrw 	fmt = code_to_format(code);
7978457ebc2Skrw 
7989a2590e5Sderaadt 	/* Figure out the size of the data. */
7998457ebc2Skrw 	for (i = 0; fmt[i]; i++) {
8009a2590e5Sderaadt 		if (!numhunk) {
801833082e5Skrw 			log_warnx("%s: Excess information in format string: "
8028457ebc2Skrw 			    "%s", name, &fmt[i]);
8032f18daabSkrw 			goto done;
8049a2590e5Sderaadt 		}
8059a2590e5Sderaadt 		numelem++;
8068457ebc2Skrw 		fmtbuf[i] = fmt[i];
8078457ebc2Skrw 		switch (fmt[i]) {
8089a2590e5Sderaadt 		case 'A':
8099a2590e5Sderaadt 			--numelem;
8109a2590e5Sderaadt 			fmtbuf[i] = 0;
8119a2590e5Sderaadt 			numhunk = 0;
81229432cd9Sphessler 			if (hunksize == 0) {
813385a6373Skrw 				log_warnx("%s: no size indicator before A"
8148457ebc2Skrw 				    " in format string: %s", name, fmt);
8152f18daabSkrw 				goto done;
81629432cd9Sphessler 			}
8179a2590e5Sderaadt 			break;
8189a2590e5Sderaadt 		case 'X':
819c714dadcShenning 			for (k = 0; k < len; k++)
8209a2590e5Sderaadt 				if (!isascii(data[k]) ||
8219a2590e5Sderaadt 				    !isprint(data[k]))
8229a2590e5Sderaadt 					break;
823b54c879eShenning 			if (k == len) {
8249a2590e5Sderaadt 				fmtbuf[i] = 't';
8259a2590e5Sderaadt 				numhunk = -2;
8269a2590e5Sderaadt 			} else {
8279a2590e5Sderaadt 				hunksize++;
8289a2590e5Sderaadt 				comma = ':';
8299a2590e5Sderaadt 				numhunk = 0;
8309a2590e5Sderaadt 			}
8319a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
8329a2590e5Sderaadt 			break;
8339a2590e5Sderaadt 		case 't':
8349a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
8359a2590e5Sderaadt 			numhunk = -2;
8369a2590e5Sderaadt 			break;
8379a2590e5Sderaadt 		case 'I':
8389a2590e5Sderaadt 		case 'l':
8399a2590e5Sderaadt 		case 'L':
8409a2590e5Sderaadt 			hunksize += 4;
8419a2590e5Sderaadt 			break;
8429a2590e5Sderaadt 		case 'S':
8439a2590e5Sderaadt 			hunksize += 2;
8449a2590e5Sderaadt 			break;
8459a2590e5Sderaadt 		case 'B':
8469a2590e5Sderaadt 		case 'f':
8479a2590e5Sderaadt 			hunksize++;
8489a2590e5Sderaadt 			break;
8499a2590e5Sderaadt 		case 'e':
8509a2590e5Sderaadt 			break;
8519a2590e5Sderaadt 		default:
8528457ebc2Skrw 			log_warnx("%s: garbage in format string: %s", name,
8538457ebc2Skrw 			    &fmt[i]);
8542f18daabSkrw 			goto done;
8559a2590e5Sderaadt 		}
8569a2590e5Sderaadt 	}
8579a2590e5Sderaadt 
858d22f105fSkrw 	/* Check for too few bytes. */
8599a2590e5Sderaadt 	if (hunksize > len) {
8608457ebc2Skrw 		log_warnx("%s: expecting at least %d bytes; got %d", name,
8618457ebc2Skrw 		    hunksize, len);
8622f18daabSkrw 		goto done;
8639a2590e5Sderaadt 	}
864d22f105fSkrw 	/* Check for too many bytes. */
8652f18daabSkrw 	if (numhunk == -1 && hunksize < len) {
8668457ebc2Skrw 		log_warnx("%s: expecting only %d bytes: got %d", name,
8678457ebc2Skrw 		    hunksize, len);
8682f18daabSkrw 		goto done;
8692f18daabSkrw 	}
8709a2590e5Sderaadt 
8719a2590e5Sderaadt 	/* If this is an array, compute its size. */
8729a2590e5Sderaadt 	if (!numhunk)
8739a2590e5Sderaadt 		numhunk = len / hunksize;
8749a2590e5Sderaadt 	/* See if we got an exact number of hunks. */
8752f18daabSkrw 	if (numhunk > 0 && numhunk * hunksize != len) {
8768457ebc2Skrw 		log_warnx("%s: expecting %d bytes: got %d", name,
8778457ebc2Skrw 		    numhunk * hunksize, len);
8782f18daabSkrw 		goto done;
8792f18daabSkrw 	}
8809a2590e5Sderaadt 
8819a2590e5Sderaadt 	/* A one-hunk array prints the same as a single hunk. */
8829a2590e5Sderaadt 	if (numhunk < 0)
8839a2590e5Sderaadt 		numhunk = 1;
8849a2590e5Sderaadt 
8859a2590e5Sderaadt 	/* Cycle through the array (or hunk) printing the data. */
8869a2590e5Sderaadt 	for (i = 0; i < numhunk; i++) {
8879a2590e5Sderaadt 		for (j = 0; j < numelem; j++) {
8889a2590e5Sderaadt 			switch (fmtbuf[j]) {
8899a2590e5Sderaadt 			case 't':
890bee06f07Skrw 				buf = pretty_print_string(dp, len, emit_punct);
891bee06f07Skrw 				if (buf == NULL)
892bee06f07Skrw 					opcount = -1;
893bee06f07Skrw 				else
894bee06f07Skrw 					opcount = strlcat(op, buf, opleft);
8959a2590e5Sderaadt 				break;
8969a2590e5Sderaadt 			case 'I':
897e95625edSkrw 				memcpy(&foo.s_addr, dp, sizeof(foo.s_addr));
898f3a8c5fdSkrw 				opcount = snprintf(op, opleft, "%s",
899f3a8c5fdSkrw 				    inet_ntoa(foo));
900e95625edSkrw 				dp += sizeof(foo.s_addr);
9019a2590e5Sderaadt 				break;
9029a2590e5Sderaadt 			case 'l':
903bce09e58Skrw 				memcpy(&int32val, dp, sizeof(int32val));
904bce09e58Skrw 				opcount = snprintf(op, opleft, "%d",
905bce09e58Skrw 				    ntohl(int32val));
906bce09e58Skrw 				dp += sizeof(int32val);
9079a2590e5Sderaadt 				break;
9089a2590e5Sderaadt 			case 'L':
909bce09e58Skrw 				memcpy(&uint32val, dp, sizeof(uint32val));
910bce09e58Skrw 				opcount = snprintf(op, opleft, "%u",
911bce09e58Skrw 				    ntohl(uint32val));
912bce09e58Skrw 				dp += sizeof(uint32val);
9139a2590e5Sderaadt 				break;
9149a2590e5Sderaadt 			case 'S':
915bce09e58Skrw 				memcpy(&uint16val, dp, sizeof(uint16val));
916bce09e58Skrw 				opcount = snprintf(op, opleft, "%hu",
917bce09e58Skrw 				    ntohs(uint16val));
918bce09e58Skrw 				dp += sizeof(uint16val);
9199a2590e5Sderaadt 				break;
9209a2590e5Sderaadt 			case 'B':
921221bd6c0Skrw 				opcount = snprintf(op, opleft, "%u", *dp);
922de3ca9dbSkrw 				dp++;
9239a2590e5Sderaadt 				break;
924920d03efSkrw 			case 'X':
925de3ca9dbSkrw 				opcount = snprintf(op, opleft, "%x", *dp);
926de3ca9dbSkrw 				dp++;
9279a2590e5Sderaadt 				break;
9289a2590e5Sderaadt 			case 'f':
929f3a8c5fdSkrw 				opcount = snprintf(op, opleft, "%s",
930f3a8c5fdSkrw 				    *dp ? "true" : "false");
931de3ca9dbSkrw 				dp++;
9329a2590e5Sderaadt 				break;
9339a2590e5Sderaadt 			default:
934833082e5Skrw 				log_warnx("Unexpected format code %c",
935833082e5Skrw 				    fmtbuf[j]);
9369a2590e5Sderaadt 				goto toobig;
937f3a8c5fdSkrw 			}
938f3a8c5fdSkrw 			if (opcount >= opleft || opcount == -1)
939f3a8c5fdSkrw 				goto toobig;
940f3a8c5fdSkrw 			opleft -= opcount;
941f3a8c5fdSkrw 			op += opcount;
9429a2590e5Sderaadt 			if (j + 1 < numelem && comma != ':') {
943f3a8c5fdSkrw 				opcount = snprintf(op, opleft, " ");
944f3a8c5fdSkrw 				if (opcount >= opleft || opcount == -1)
945f3a8c5fdSkrw 					goto toobig;
946f3a8c5fdSkrw 				opleft -= opcount;
947f3a8c5fdSkrw 				op += opcount;
9489a2590e5Sderaadt 			}
9499a2590e5Sderaadt 		}
9509a2590e5Sderaadt 		if (i + 1 < numhunk) {
951f3a8c5fdSkrw 			opcount = snprintf(op, opleft, "%c", comma);
952f3a8c5fdSkrw 			if (opcount >= opleft || opcount == -1)
9539a2590e5Sderaadt 				goto toobig;
954f3a8c5fdSkrw 			opleft -= opcount;
955f3a8c5fdSkrw 			op += opcount;
956f3a8c5fdSkrw 		}
9579a2590e5Sderaadt 	}
9582f18daabSkrw 
9592f18daabSkrw done:
96051a35423Skrw 	return optbuf;
9612f18daabSkrw 
9629a2590e5Sderaadt toobig:
9632f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
96451a35423Skrw 	return optbuf;
9659a2590e5Sderaadt }
9669a2590e5Sderaadt 
967b414edd1Skrw struct option_data *
968b414edd1Skrw unpack_options(struct dhcp_packet *packet)
9699a2590e5Sderaadt {
970cb26da20Skrw 	static struct option_data	 options[DHO_COUNT];
971b414edd1Skrw 	int				 i;
9729a2590e5Sderaadt 
973cb26da20Skrw 	for (i = 0; i < DHO_COUNT; i++) {
974b414edd1Skrw 		free(options[i].data);
975b414edd1Skrw 		options[i].data = NULL;
976b414edd1Skrw 		options[i].len = 0;
977b414edd1Skrw 	}
97802e02bd5Skrw 
97902e02bd5Skrw 	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
98002e02bd5Skrw 		/* Parse the BOOTP/DHCP options field. */
981b414edd1Skrw 		parse_option_buffer(options, &packet->options[4],
982b414edd1Skrw 		    sizeof(packet->options) - 4);
98302e02bd5Skrw 
984b414edd1Skrw 		/* DHCP packets can also use overload areas for options. */
985b414edd1Skrw 		if (options[DHO_DHCP_MESSAGE_TYPE].data &&
98602e02bd5Skrw 		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
98702e02bd5Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
988b414edd1Skrw 				parse_option_buffer(options,
98902e02bd5Skrw 				    (unsigned char *)packet->file,
99002e02bd5Skrw 				    sizeof(packet->file));
991b414edd1Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
992b414edd1Skrw 				parse_option_buffer(options,
99302e02bd5Skrw 				    (unsigned char *)packet->sname,
99402e02bd5Skrw 				    sizeof(packet->sname));
99502e02bd5Skrw 		}
99602e02bd5Skrw 	}
99702e02bd5Skrw 
998b414edd1Skrw 	return options;
9999a2590e5Sderaadt }
1000