1*09cbf102Skrw /* $OpenBSD: options.c,v 1.123 2020/07/07 19:48:31 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>
54f1e54185Skrw #include <resolv.h>
55711cae1eSkrw #include <signal.h>
56711cae1eSkrw #include <stdio.h>
57711cae1eSkrw #include <stdlib.h>
58711cae1eSkrw #include <string.h>
598d2bd14bSkrw #include <vis.h>
608d2bd14bSkrw
61711cae1eSkrw #include "dhcp.h"
62711cae1eSkrw #include "dhcpd.h"
63385a6373Skrw #include "log.h"
64711cae1eSkrw
6502e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int);
661abdd88fSkrw void pretty_print_classless_routes(unsigned char *, size_t, unsigned char *,
671abdd88fSkrw size_t);
68f1e54185Skrw void pretty_print_domain_list(unsigned char *, size_t, unsigned char *,
69f1e54185Skrw size_t);
709a2590e5Sderaadt
71c714dadcShenning /*
728457ebc2Skrw * DHCP Option names, formats and codes, from RFC1533.
738457ebc2Skrw *
748457ebc2Skrw * Format codes:
758457ebc2Skrw *
768457ebc2Skrw * e - end of data
778457ebc2Skrw * I - IP address
788457ebc2Skrw * l - 32-bit signed integer
798457ebc2Skrw * L - 32-bit unsigned integer
808457ebc2Skrw * S - 16-bit unsigned integer
818457ebc2Skrw * B - 8-bit unsigned integer
828457ebc2Skrw * t - ASCII text
838457ebc2Skrw * f - flag (true or false)
848457ebc2Skrw * A - array of whatever precedes (e.g., IA means array of IP addresses)
858457ebc2Skrw * C - CIDR description
86d712a45dSkrw * X - hex octets
87f1e54185Skrw * D - domain name list, comma separated list of domain names.
888457ebc2Skrw */
898457ebc2Skrw
908457ebc2Skrw static const struct {
918457ebc2Skrw char *name;
928457ebc2Skrw char *format;
938457ebc2Skrw } dhcp_options[DHO_COUNT] = {
948457ebc2Skrw /* 0 */ { "pad", "" },
958457ebc2Skrw /* 1 */ { "subnet-mask", "I" },
968457ebc2Skrw /* 2 */ { "time-offset", "l" },
978457ebc2Skrw /* 3 */ { "routers", "IA" },
988457ebc2Skrw /* 4 */ { "time-servers", "IA" },
998457ebc2Skrw /* 5 */ { "ien116-name-servers", "IA" },
1008457ebc2Skrw /* 6 */ { "domain-name-servers", "IA" },
1018457ebc2Skrw /* 7 */ { "log-servers", "IA" },
1028457ebc2Skrw /* 8 */ { "cookie-servers", "IA" },
1038457ebc2Skrw /* 9 */ { "lpr-servers", "IA" },
1048457ebc2Skrw /* 10 */ { "impress-servers", "IA" },
1058457ebc2Skrw /* 11 */ { "resource-location-servers", "IA" },
1068457ebc2Skrw /* 12 */ { "host-name", "t" },
1078457ebc2Skrw /* 13 */ { "boot-size", "S" },
1088457ebc2Skrw /* 14 */ { "merit-dump", "t" },
1098457ebc2Skrw /* 15 */ { "domain-name", "t" },
1108457ebc2Skrw /* 16 */ { "swap-server", "I" },
1118457ebc2Skrw /* 17 */ { "root-path", "t" },
1128457ebc2Skrw /* 18 */ { "extensions-path", "t" },
1138457ebc2Skrw /* 19 */ { "ip-forwarding", "f" },
1148457ebc2Skrw /* 20 */ { "non-local-source-routing", "f" },
1158457ebc2Skrw /* 21 */ { "policy-filter", "IIA" },
1168457ebc2Skrw /* 22 */ { "max-dgram-reassembly", "S" },
1178457ebc2Skrw /* 23 */ { "default-ip-ttl", "B" },
1188457ebc2Skrw /* 24 */ { "path-mtu-aging-timeout", "L" },
1198457ebc2Skrw /* 25 */ { "path-mtu-plateau-table", "SA" },
1208457ebc2Skrw /* 26 */ { "interface-mtu", "S" },
1218457ebc2Skrw /* 27 */ { "all-subnets-local", "f" },
1228457ebc2Skrw /* 28 */ { "broadcast-address", "I" },
1238457ebc2Skrw /* 29 */ { "perform-mask-discovery", "f" },
1248457ebc2Skrw /* 30 */ { "mask-supplier", "f" },
1258457ebc2Skrw /* 31 */ { "router-discovery", "f" },
1268457ebc2Skrw /* 32 */ { "router-solicitation-address", "I" },
1278457ebc2Skrw /* 33 */ { "static-routes", "IIA" },
1288457ebc2Skrw /* 34 */ { "trailer-encapsulation", "f" },
1298457ebc2Skrw /* 35 */ { "arp-cache-timeout", "L" },
1308457ebc2Skrw /* 36 */ { "ieee802-3-encapsulation", "f" },
1318457ebc2Skrw /* 37 */ { "default-tcp-ttl", "B" },
1328457ebc2Skrw /* 38 */ { "tcp-keepalive-interval", "L" },
1338457ebc2Skrw /* 39 */ { "tcp-keepalive-garbage", "f" },
1348457ebc2Skrw /* 40 */ { "nis-domain", "t" },
1358457ebc2Skrw /* 41 */ { "nis-servers", "IA" },
1368457ebc2Skrw /* 42 */ { "ntp-servers", "IA" },
1378457ebc2Skrw /* 43 */ { "vendor-encapsulated-options", "X" },
1388457ebc2Skrw /* 44 */ { "netbios-name-servers", "IA" },
1398457ebc2Skrw /* 45 */ { "netbios-dd-server", "IA" },
1408457ebc2Skrw /* 46 */ { "netbios-node-type", "B" },
1418457ebc2Skrw /* 47 */ { "netbios-scope", "t" },
1428457ebc2Skrw /* 48 */ { "font-servers", "IA" },
1438457ebc2Skrw /* 49 */ { "x-display-manager", "IA" },
1448457ebc2Skrw /* 50 */ { "dhcp-requested-address", "I" },
1458457ebc2Skrw /* 51 */ { "dhcp-lease-time", "L" },
1468457ebc2Skrw /* 52 */ { "dhcp-option-overload", "B" },
1478457ebc2Skrw /* 53 */ { "dhcp-message-type", "B" },
1488457ebc2Skrw /* 54 */ { "dhcp-server-identifier", "I" },
1498457ebc2Skrw /* 55 */ { "dhcp-parameter-request-list", "BA" },
1508457ebc2Skrw /* 56 */ { "dhcp-message", "t" },
1518457ebc2Skrw /* 57 */ { "dhcp-max-message-size", "S" },
1528457ebc2Skrw /* 58 */ { "dhcp-renewal-time", "L" },
1538457ebc2Skrw /* 59 */ { "dhcp-rebinding-time", "L" },
1548457ebc2Skrw /* 60 */ { "dhcp-class-identifier", "t" },
1558457ebc2Skrw /* 61 */ { "dhcp-client-identifier", "X" },
1568457ebc2Skrw /* 62 */ { NULL, NULL },
1578457ebc2Skrw /* 63 */ { NULL, NULL },
1588457ebc2Skrw /* 64 */ { "nisplus-domain", "t" },
1598457ebc2Skrw /* 65 */ { "nisplus-servers", "IA" },
1608457ebc2Skrw /* 66 */ { "tftp-server-name", "t" },
1618457ebc2Skrw /* 67 */ { "bootfile-name", "t" },
1628457ebc2Skrw /* 68 */ { "mobile-ip-home-agent", "IA" },
1638457ebc2Skrw /* 69 */ { "smtp-server", "IA" },
1648457ebc2Skrw /* 70 */ { "pop-server", "IA" },
1658457ebc2Skrw /* 71 */ { "nntp-server", "IA" },
1668457ebc2Skrw /* 72 */ { "www-server", "IA" },
1678457ebc2Skrw /* 73 */ { "finger-server", "IA" },
1688457ebc2Skrw /* 74 */ { "irc-server", "IA" },
1698457ebc2Skrw /* 75 */ { "streettalk-server", "IA" },
1708457ebc2Skrw /* 76 */ { "streettalk-directory-assistance-server", "IA" },
1718457ebc2Skrw /* 77 */ { "user-class", "t" },
1728457ebc2Skrw /* 78 */ { NULL, NULL },
1738457ebc2Skrw /* 79 */ { NULL, NULL },
1748457ebc2Skrw /* 80 */ { NULL, NULL },
1758457ebc2Skrw /* 81 */ { NULL, NULL },
1768457ebc2Skrw /* 82 */ { "relay-agent-information", "X" },
1778457ebc2Skrw /* 83 */ { NULL, NULL },
1788457ebc2Skrw /* 84 */ { NULL, NULL },
1798457ebc2Skrw /* 85 */ { "nds-servers", "IA" },
1808457ebc2Skrw /* 86 */ { "nds-tree-name", "X" },
1818457ebc2Skrw /* 87 */ { "nds-context", "X" },
1828457ebc2Skrw /* 88 */ { NULL, NULL },
1838457ebc2Skrw /* 89 */ { NULL, NULL },
1848457ebc2Skrw /* 90 */ { NULL, NULL },
1858457ebc2Skrw /* 91 */ { NULL, NULL },
1868457ebc2Skrw /* 92 */ { NULL, NULL },
1878457ebc2Skrw /* 93 */ { NULL, NULL },
1888457ebc2Skrw /* 94 */ { NULL, NULL },
1898457ebc2Skrw /* 95 */ { NULL, NULL },
1908457ebc2Skrw /* 96 */ { NULL, NULL },
1918457ebc2Skrw /* 97 */ { NULL, NULL },
1928457ebc2Skrw /* 98 */ { NULL, NULL },
1938457ebc2Skrw /* 99 */ { NULL, NULL },
1948457ebc2Skrw /* 100 */ { NULL, NULL },
1958457ebc2Skrw /* 101 */ { NULL, NULL },
1968457ebc2Skrw /* 102 */ { NULL, NULL },
1978457ebc2Skrw /* 103 */ { NULL, NULL },
1988457ebc2Skrw /* 104 */ { NULL, NULL },
1998457ebc2Skrw /* 105 */ { NULL, NULL },
2008457ebc2Skrw /* 106 */ { NULL, NULL },
2018457ebc2Skrw /* 107 */ { NULL, NULL },
2028457ebc2Skrw /* 108 */ { NULL, NULL },
2038457ebc2Skrw /* 109 */ { NULL, NULL },
2048457ebc2Skrw /* 110 */ { NULL, NULL },
2058457ebc2Skrw /* 111 */ { NULL, NULL },
2068457ebc2Skrw /* 112 */ { NULL, NULL },
2078457ebc2Skrw /* 113 */ { NULL, NULL },
2088457ebc2Skrw /* 114 */ { NULL, NULL },
2098457ebc2Skrw /* 115 */ { NULL, NULL },
2108457ebc2Skrw /* 116 */ { NULL, NULL },
2118457ebc2Skrw /* 117 */ { NULL, NULL },
2128457ebc2Skrw /* 118 */ { NULL, NULL },
213f1e54185Skrw /* 119 */ { "domain-search", "D" },
2148457ebc2Skrw /* 120 */ { NULL, NULL },
2158457ebc2Skrw /* 121 */ { "classless-static-routes", "CIA" },
2168457ebc2Skrw /* 122 */ { NULL, NULL },
2178457ebc2Skrw /* 123 */ { NULL, NULL },
2188457ebc2Skrw /* 124 */ { NULL, NULL },
2198457ebc2Skrw /* 125 */ { NULL, NULL },
2208457ebc2Skrw /* 126 */ { NULL, NULL },
2218457ebc2Skrw /* 127 */ { NULL, NULL },
2228457ebc2Skrw /* 128 */ { NULL, NULL },
2238457ebc2Skrw /* 129 */ { NULL, NULL },
2248457ebc2Skrw /* 130 */ { NULL, NULL },
2258457ebc2Skrw /* 131 */ { NULL, NULL },
2268457ebc2Skrw /* 132 */ { NULL, NULL },
2278457ebc2Skrw /* 133 */ { NULL, NULL },
2288457ebc2Skrw /* 134 */ { NULL, NULL },
2298457ebc2Skrw /* 135 */ { NULL, NULL },
2308457ebc2Skrw /* 136 */ { NULL, NULL },
2318457ebc2Skrw /* 137 */ { NULL, NULL },
2328457ebc2Skrw /* 138 */ { NULL, NULL },
2338457ebc2Skrw /* 139 */ { NULL, NULL },
2348457ebc2Skrw /* 140 */ { NULL, NULL },
2358457ebc2Skrw /* 141 */ { NULL, NULL },
2368457ebc2Skrw /* 142 */ { NULL, NULL },
2378457ebc2Skrw /* 143 */ { NULL, NULL },
2388457ebc2Skrw /* 144 */ { "tftp-config-file", "t" },
2398457ebc2Skrw /* 145 */ { NULL, NULL },
2408457ebc2Skrw /* 146 */ { NULL, NULL },
2418457ebc2Skrw /* 147 */ { NULL, NULL },
2428457ebc2Skrw /* 148 */ { NULL, NULL },
2438457ebc2Skrw /* 149 */ { NULL, NULL },
2448457ebc2Skrw /* 150 */ { "voip-configuration-server", "IA" },
2458457ebc2Skrw /* 151 */ { NULL, NULL },
2468457ebc2Skrw /* 152 */ { NULL, NULL },
2478457ebc2Skrw /* 153 */ { NULL, NULL },
2488457ebc2Skrw /* 154 */ { NULL, NULL },
2498457ebc2Skrw /* 155 */ { NULL, NULL },
2508457ebc2Skrw /* 156 */ { NULL, NULL },
2518457ebc2Skrw /* 157 */ { NULL, NULL },
2528457ebc2Skrw /* 158 */ { NULL, NULL },
2538457ebc2Skrw /* 159 */ { NULL, NULL },
2548457ebc2Skrw /* 160 */ { NULL, NULL },
2558457ebc2Skrw /* 161 */ { NULL, NULL },
2568457ebc2Skrw /* 162 */ { NULL, NULL },
2578457ebc2Skrw /* 163 */ { NULL, NULL },
2588457ebc2Skrw /* 164 */ { NULL, NULL },
2598457ebc2Skrw /* 165 */ { NULL, NULL },
2608457ebc2Skrw /* 166 */ { NULL, NULL },
2618457ebc2Skrw /* 167 */ { NULL, NULL },
2628457ebc2Skrw /* 168 */ { NULL, NULL },
2638457ebc2Skrw /* 169 */ { NULL, NULL },
2648457ebc2Skrw /* 170 */ { NULL, NULL },
2658457ebc2Skrw /* 171 */ { NULL, NULL },
2668457ebc2Skrw /* 172 */ { NULL, NULL },
2678457ebc2Skrw /* 173 */ { NULL, NULL },
2688457ebc2Skrw /* 174 */ { NULL, NULL },
2698457ebc2Skrw /* 175 */ { NULL, NULL },
2708457ebc2Skrw /* 176 */ { NULL, NULL },
2718457ebc2Skrw /* 177 */ { NULL, NULL },
2728457ebc2Skrw /* 178 */ { NULL, NULL },
2738457ebc2Skrw /* 179 */ { NULL, NULL },
2748457ebc2Skrw /* 180 */ { NULL, NULL },
2758457ebc2Skrw /* 181 */ { NULL, NULL },
2768457ebc2Skrw /* 182 */ { NULL, NULL },
2778457ebc2Skrw /* 183 */ { NULL, NULL },
2788457ebc2Skrw /* 184 */ { NULL, NULL },
2798457ebc2Skrw /* 185 */ { NULL, NULL },
2808457ebc2Skrw /* 186 */ { NULL, NULL },
2818457ebc2Skrw /* 187 */ { NULL, NULL },
2828457ebc2Skrw /* 188 */ { NULL, NULL },
2838457ebc2Skrw /* 189 */ { NULL, NULL },
2848457ebc2Skrw /* 190 */ { NULL, NULL },
2858457ebc2Skrw /* 191 */ { NULL, NULL },
2868457ebc2Skrw /* 192 */ { NULL, NULL },
2878457ebc2Skrw /* 193 */ { NULL, NULL },
2888457ebc2Skrw /* 194 */ { NULL, NULL },
2898457ebc2Skrw /* 195 */ { NULL, NULL },
2908457ebc2Skrw /* 196 */ { NULL, NULL },
2918457ebc2Skrw /* 197 */ { NULL, NULL },
2928457ebc2Skrw /* 198 */ { NULL, NULL },
2938457ebc2Skrw /* 199 */ { NULL, NULL },
2948457ebc2Skrw /* 200 */ { NULL, NULL },
2958457ebc2Skrw /* 201 */ { NULL, NULL },
2968457ebc2Skrw /* 202 */ { NULL, NULL },
2978457ebc2Skrw /* 203 */ { NULL, NULL },
2988457ebc2Skrw /* 204 */ { NULL, NULL },
2998457ebc2Skrw /* 205 */ { NULL, NULL },
3008457ebc2Skrw /* 206 */ { NULL, NULL },
3018457ebc2Skrw /* 207 */ { NULL, NULL },
3028457ebc2Skrw /* 208 */ { NULL, NULL },
3038457ebc2Skrw /* 209 */ { NULL, NULL },
3048457ebc2Skrw /* 210 */ { NULL, NULL },
3058457ebc2Skrw /* 211 */ { NULL, NULL },
3068457ebc2Skrw /* 212 */ { NULL, NULL },
3078457ebc2Skrw /* 213 */ { NULL, NULL },
3088457ebc2Skrw /* 214 */ { NULL, NULL },
3098457ebc2Skrw /* 215 */ { NULL, NULL },
3108457ebc2Skrw /* 216 */ { NULL, NULL },
3118457ebc2Skrw /* 217 */ { NULL, NULL },
3128457ebc2Skrw /* 218 */ { NULL, NULL },
3138457ebc2Skrw /* 219 */ { NULL, NULL },
3148457ebc2Skrw /* 220 */ { NULL, NULL },
3158457ebc2Skrw /* 221 */ { NULL, NULL },
3168457ebc2Skrw /* 222 */ { NULL, NULL },
3178457ebc2Skrw /* 223 */ { NULL, NULL },
3188457ebc2Skrw /* 224 */ { NULL, NULL },
3198457ebc2Skrw /* 225 */ { NULL, NULL },
3208457ebc2Skrw /* 226 */ { NULL, NULL },
3218457ebc2Skrw /* 227 */ { NULL, NULL },
3228457ebc2Skrw /* 228 */ { NULL, NULL },
3238457ebc2Skrw /* 229 */ { NULL, NULL },
3248457ebc2Skrw /* 230 */ { NULL, NULL },
3258457ebc2Skrw /* 231 */ { NULL, NULL },
3268457ebc2Skrw /* 232 */ { NULL, NULL },
3278457ebc2Skrw /* 233 */ { NULL, NULL },
3288457ebc2Skrw /* 234 */ { NULL, NULL },
3298457ebc2Skrw /* 235 */ { NULL, NULL },
3308457ebc2Skrw /* 236 */ { NULL, NULL },
3318457ebc2Skrw /* 237 */ { NULL, NULL },
3328457ebc2Skrw /* 238 */ { NULL, NULL },
3338457ebc2Skrw /* 239 */ { NULL, NULL },
3348457ebc2Skrw /* 240 */ { NULL, NULL },
3358457ebc2Skrw /* 241 */ { NULL, NULL },
3368457ebc2Skrw /* 242 */ { NULL, NULL },
3378457ebc2Skrw /* 243 */ { NULL, NULL },
3388457ebc2Skrw /* 244 */ { NULL, NULL },
3398457ebc2Skrw /* 245 */ { NULL, NULL },
3408457ebc2Skrw /* 246 */ { NULL, NULL },
3418457ebc2Skrw /* 247 */ { NULL, NULL },
3428457ebc2Skrw /* 248 */ { NULL, NULL },
3438457ebc2Skrw /* 249 */ { "classless-ms-static-routes", "CIA" },
3448457ebc2Skrw /* 250 */ { NULL, NULL },
3458457ebc2Skrw /* 251 */ { NULL, NULL },
3468457ebc2Skrw /* 252 */ { "autoproxy-script", "t" },
3478457ebc2Skrw /* 253 */ { NULL, NULL },
3488457ebc2Skrw /* 254 */ { NULL, NULL },
3498457ebc2Skrw /* 255 */ { "option-end", "e" },
3508457ebc2Skrw };
3518457ebc2Skrw
3528457ebc2Skrw char *
code_to_name(int code)3538457ebc2Skrw code_to_name(int code)
3548457ebc2Skrw {
3558457ebc2Skrw static char unknown[11]; /* "option-NNN" */
3568457ebc2Skrw int ret;
3578457ebc2Skrw
3588457ebc2Skrw if (code < 0 || code >= DHO_COUNT)
3598457ebc2Skrw return "";
3608457ebc2Skrw
3618457ebc2Skrw if (dhcp_options[code].name != NULL)
3628457ebc2Skrw return dhcp_options[code].name;
3638457ebc2Skrw
3648457ebc2Skrw ret = snprintf(unknown, sizeof(unknown), "option-%d", code);
365515e489cSderaadt if (ret < 0 || ret >= (int)sizeof(unknown))
3668457ebc2Skrw return "";
3678457ebc2Skrw
3688457ebc2Skrw return unknown;
3698457ebc2Skrw }
3708457ebc2Skrw
3718457ebc2Skrw int
name_to_code(char * name)3728457ebc2Skrw name_to_code(char *name)
3738457ebc2Skrw {
3748457ebc2Skrw char unknown[11]; /* "option-NNN" */
3758457ebc2Skrw int code, ret;
3768457ebc2Skrw
3778457ebc2Skrw for (code = 1; code < DHO_END; code++) {
3788457ebc2Skrw if (dhcp_options[code].name == NULL) {
3798457ebc2Skrw ret = snprintf(unknown, sizeof(unknown), "option-%d",
3808457ebc2Skrw code);
381515e489cSderaadt if (ret < 0 || ret >= (int)sizeof(unknown))
3828457ebc2Skrw return DHO_END;
3838457ebc2Skrw if (strcasecmp(unknown, name) == 0)
3848457ebc2Skrw return code;
3858457ebc2Skrw } else if (strcasecmp(dhcp_options[code].name, name) == 0) {
3868457ebc2Skrw return code;
3878457ebc2Skrw }
3888457ebc2Skrw }
3898457ebc2Skrw
3908457ebc2Skrw return DHO_END;
3918457ebc2Skrw }
3928457ebc2Skrw
3938457ebc2Skrw char *
code_to_format(int code)3948457ebc2Skrw code_to_format(int code)
3958457ebc2Skrw {
3968457ebc2Skrw if (code < 0 || code >= DHO_COUNT)
3978457ebc2Skrw return "";
3988457ebc2Skrw
3998457ebc2Skrw if (dhcp_options[code].format == NULL)
4008457ebc2Skrw return "X";
4018457ebc2Skrw
4028457ebc2Skrw return dhcp_options[code].format;
4038457ebc2Skrw }
4048457ebc2Skrw
4058457ebc2Skrw /*
406d712a45dSkrw * Some option data types cannot be appended or prepended to. For
407d712a45dSkrw * such options change ACTION_PREPEND to ACTION_SUPERSEDE and
408d712a45dSkrw * ACTION_APPEND to ACTION_DEFAULT.
409d712a45dSkrw */
410d712a45dSkrw int
code_to_action(int code,int action)411d712a45dSkrw code_to_action(int code, int action)
412d712a45dSkrw {
413d712a45dSkrw char *fmt;
414d712a45dSkrw
415d712a45dSkrw fmt = code_to_format(code);
416f1e54185Skrw if (fmt == NULL || strpbrk(fmt, "ADtX") != NULL)
417d712a45dSkrw return action;
418d712a45dSkrw
419d712a45dSkrw /*
420d712a45dSkrw * For our protection all formats which have been excluded shall be
421d712a45dSkrw * deemed included.
422d712a45dSkrw */
423d712a45dSkrw switch (action) {
424d712a45dSkrw case ACTION_APPEND:
425d712a45dSkrw action = ACTION_DEFAULT;
426d712a45dSkrw break;
427d712a45dSkrw case ACTION_PREPEND:
428d712a45dSkrw action = ACTION_SUPERSEDE;
429d712a45dSkrw break;
430d712a45dSkrw default:
431d712a45dSkrw break;
432d712a45dSkrw }
433d712a45dSkrw
434d712a45dSkrw return action;
435d712a45dSkrw }
436d712a45dSkrw
437d712a45dSkrw /*
438c714dadcShenning * Parse options out of the specified buffer, storing addresses of
43992018899Skrw * option values in options. Return 0 if errors, 1 if not.
440c714dadcShenning */
44102e02bd5Skrw int
parse_option_buffer(struct option_data * options,unsigned char * buffer,int length)4424f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer,
4434f062ee3Skrw int length)
4449a2590e5Sderaadt {
445373f0df1Skrw unsigned char *s, *t, *end;
4468457ebc2Skrw char *name, *fmt;
4470fe2d5abSkrw int code, len, newlen;
4489a2590e5Sderaadt
449373f0df1Skrw s = buffer;
450373f0df1Skrw end = s + length;
451373f0df1Skrw while (s < end) {
4529a2590e5Sderaadt code = s[0];
4539a2590e5Sderaadt
454373f0df1Skrw /* End options terminate processing. */
455373f0df1Skrw if (code == DHO_END)
456373f0df1Skrw break;
457373f0df1Skrw
4589a2590e5Sderaadt /* Pad options don't have a length - just skip them. */
4599a2590e5Sderaadt if (code == DHO_PAD) {
460f1e89499Shenning s++;
4619a2590e5Sderaadt continue;
4629a2590e5Sderaadt }
4639a2590e5Sderaadt
4648457ebc2Skrw name = code_to_name(code);
4658457ebc2Skrw fmt = code_to_format(code);
4668457ebc2Skrw
467c714dadcShenning /*
46899c003b1Skrw * All options other than DHO_PAD and DHO_END have a one-byte
46999c003b1Skrw * length field. It could be 0! Make sure that the length byte
47099c003b1Skrw * is present, and all the data is available.
471c714dadcShenning */
47299c003b1Skrw if (s + 1 < end) {
4739a2590e5Sderaadt len = s[1];
47499c003b1Skrw if (s + 1 + len < end) {
47599c003b1Skrw ; /* option data is all there. */
47699c003b1Skrw } else {
477b53f3a4dSkrw log_warnx("%s: option %s (%d) larger than "
478b53f3a4dSkrw "buffer", log_procname, name, len);
47951a35423Skrw return 0;
4809a2590e5Sderaadt }
48199c003b1Skrw } else {
482b53f3a4dSkrw log_warnx("%s: option %s has no length field",
483b53f3a4dSkrw log_procname, name);
48451a35423Skrw return 0;
48599c003b1Skrw }
486df453039Skrw
487df453039Skrw /*
488587003c7Skrw * Strip trailing NULs from ascii ('t') options. RFC 2132
489df453039Skrw * says "Options containing NVT ASCII data SHOULD NOT include
490df453039Skrw * a trailing NULL; however, the receiver of such options
491df453039Skrw * MUST be prepared to delete trailing nulls if they exist."
492df453039Skrw */
4938457ebc2Skrw if (fmt[0] == 't') {
49499c003b1Skrw while (len > 0 && s[len + 1] == '\0')
49599c003b1Skrw len--;
496df453039Skrw }
497df453039Skrw
498c714dadcShenning /*
4990fe2d5abSkrw * Concatenate new data + NUL to existing option data.
5000fe2d5abSkrw *
5010fe2d5abSkrw * Note that the NUL is *not* counted in the len field!
502c714dadcShenning */
5030fe2d5abSkrw newlen = options[code].len + len;
5040fe2d5abSkrw if ((t = realloc(options[code].data, newlen + 1)) == NULL)
50540c65bfbSkrw fatal("option %s", name);
5060fe2d5abSkrw
5074f062ee3Skrw memcpy(t + options[code].len, &s[2], len);
5080fe2d5abSkrw t[newlen] = 0;
5090fe2d5abSkrw
5100fe2d5abSkrw options[code].len = newlen;
5114f062ee3Skrw options[code].data = t;
5120fe2d5abSkrw
513ef234d22Skrw s += s[1] + 2;
5149a2590e5Sderaadt }
51502e02bd5Skrw
51651a35423Skrw return 1;
5179a2590e5Sderaadt }
5189a2590e5Sderaadt
519c714dadcShenning /*
520b414edd1Skrw * Pack as many options as fit in buflen bytes of buf. Return the
52196978980Skrw * offset of the start of the last option copied. A caller can check
52296978980Skrw * to see if it's DHO_END to decide if all the options were copied.
523c714dadcShenning */
524c714dadcShenning int
pack_options(unsigned char * buf,int buflen,struct option_data * options)525b414edd1Skrw pack_options(unsigned char *buf, int buflen, struct option_data *options)
5269a2590e5Sderaadt {
52796978980Skrw int ix, incr, length, bufix, code, lastopt = -1;
5289a2590e5Sderaadt
529736b0ed2Skrw memset(buf, 0, buflen);
5309a2590e5Sderaadt
53196978980Skrw memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
5326fe46e93Skrw if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL) {
533d6a67f0fSkrw memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
534d6a67f0fSkrw buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
535d6a67f0fSkrw bufix = 7;
536d6a67f0fSkrw } else
53796978980Skrw bufix = 4;
5389a2590e5Sderaadt
53996978980Skrw for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
540561b23c2Skrw if (options[code].data == NULL ||
541561b23c2Skrw code == DHO_DHCP_MESSAGE_TYPE)
5429a2590e5Sderaadt continue;
5439a2590e5Sderaadt
544d7d9bbf5Skrw length = options[code].len;
54596978980Skrw if (bufix + length + 2*((length+254)/255) >= buflen)
54651a35423Skrw return lastopt;
5479a2590e5Sderaadt
54896978980Skrw lastopt = bufix;
5499a2590e5Sderaadt ix = 0;
5509a2590e5Sderaadt
5519a2590e5Sderaadt while (length) {
55296978980Skrw incr = length > 255 ? 255 : length;
5539a2590e5Sderaadt
55496978980Skrw buf[bufix++] = code;
55596978980Skrw buf[bufix++] = incr;
55696978980Skrw memcpy(buf + bufix, options[code].data + ix, incr);
5579a2590e5Sderaadt
5589a2590e5Sderaadt length -= incr;
5599a2590e5Sderaadt ix += incr;
5606fc9f4f6Skrw bufix += incr;
5619a2590e5Sderaadt }
5629a2590e5Sderaadt }
56396978980Skrw
56496978980Skrw if (bufix < buflen) {
56596978980Skrw buf[bufix] = DHO_END;
56696978980Skrw lastopt = bufix;
56796978980Skrw }
56896978980Skrw
56951a35423Skrw return lastopt;
5709a2590e5Sderaadt }
5719a2590e5Sderaadt
572c714dadcShenning /*
573482123e8Skrw * Use vis() to encode characters of src and append encoded characters onto
574482123e8Skrw * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be
575482123e8Skrw * represented as '"' delimited strings and safely passed to scripts. Surround
576482123e8Skrw * result with double quotes if emit_punct is true.
577482123e8Skrw */
578bee06f07Skrw char *
pretty_print_string(unsigned char * src,size_t srclen,int emit_punct)579bee06f07Skrw pretty_print_string(unsigned char *src, size_t srclen, int emit_punct)
580482123e8Skrw {
581bee06f07Skrw static char string[8196];
582482123e8Skrw char visbuf[5];
583482123e8Skrw unsigned char *origsrc = src;
584bee06f07Skrw size_t rslt = 0;
585482123e8Skrw
586bee06f07Skrw memset(string, 0, sizeof(string));
587bee06f07Skrw
5886fe46e93Skrw if (emit_punct != 0)
589bee06f07Skrw rslt = strlcat(string, "\"", sizeof(string));
590482123e8Skrw
591482123e8Skrw for (; src < origsrc + srclen; src++) {
592482123e8Skrw if (*src && strchr("\"'$`\\", *src))
593642cc348Skrw vis(visbuf, *src, VIS_ALL | VIS_OCTAL, *src+1);
594642cc348Skrw else
595482123e8Skrw vis(visbuf, *src, VIS_OCTAL, *src+1);
596bee06f07Skrw rslt = strlcat(string, visbuf, sizeof(string));
597482123e8Skrw }
598482123e8Skrw
5996fe46e93Skrw if (emit_punct != 0)
600bee06f07Skrw rslt = strlcat(string, "\"", sizeof(string));
601bee06f07Skrw
602bee06f07Skrw if (rslt >= sizeof(string))
60351a35423Skrw return NULL;
604bee06f07Skrw
60551a35423Skrw return string;
606482123e8Skrw }
607482123e8Skrw
608482123e8Skrw /*
6095714f486Skrw * Must special case *_CLASSLESS_* route options due to the variable size
6105714f486Skrw * of the CIDR element in its CIA format.
6115714f486Skrw */
6125c6453b8Skrw void
pretty_print_classless_routes(unsigned char * src,size_t srclen,unsigned char * buf,size_t buflen)6135c6453b8Skrw pretty_print_classless_routes(unsigned char *src, size_t srclen,
6145c6453b8Skrw unsigned char *buf, size_t buflen)
6155714f486Skrw {
616da2eb076Skrw char bitsbuf[5]; /* to hold "/nn " */
617f267fa4cSkrw struct in_addr dest, netmask, gateway;
6182aaf2bf4Skrw unsigned int bits, i, len;
6192aaf2bf4Skrw uint32_t m;
6205cbe9918Skrw int rslt;
6215714f486Skrw
622f267fa4cSkrw i = 0;
623f267fa4cSkrw while (i < srclen) {
6241989501aSkrw len = extract_route(&src[i], srclen - i, &dest.s_addr,
6251989501aSkrw &netmask.s_addr, &gateway.s_addr);
6265cbe9918Skrw if (len == 0)
627f267fa4cSkrw goto bad;
628f267fa4cSkrw i += len;
629da2eb076Skrw
6302aaf2bf4Skrw m = ntohl(netmask.s_addr);
6312aaf2bf4Skrw bits = 32;
6322aaf2bf4Skrw while ((bits > 0) && ((m & 1) == 0)) {
6332aaf2bf4Skrw m >>= 1;
6342aaf2bf4Skrw bits--;
6352aaf2bf4Skrw }
6362aaf2bf4Skrw
6372aaf2bf4Skrw rslt = snprintf(bitsbuf, sizeof(bitsbuf), "/%d ", bits);
638515e489cSderaadt if (rslt < 0 || (unsigned int)rslt >= sizeof(bitsbuf))
639f267fa4cSkrw goto bad;
640da2eb076Skrw
6415c6453b8Skrw if (strlen(buf) > 0)
6425c6453b8Skrw strlcat(buf, ", ", buflen);
643f267fa4cSkrw strlcat(buf, inet_ntoa(dest), buflen);
6445c6453b8Skrw strlcat(buf, bitsbuf, buflen);
6455c6453b8Skrw if (strlcat(buf, inet_ntoa(gateway), buflen) >= buflen)
646f267fa4cSkrw goto bad;
6475714f486Skrw }
6485714f486Skrw
6495c6453b8Skrw return;
6505c6453b8Skrw
651f267fa4cSkrw bad:
6525c6453b8Skrw memset(buf, 0, buflen);
6535714f486Skrw }
6545714f486Skrw
655f1e54185Skrw /*
656f1e54185Skrw * Print string containing blank separated list of domain names
657f1e54185Skrw * as a comma separated list of double-quote delimited strings.
658f1e54185Skrw *
659f1e54185Skrw * e.g. "eng.apple.com. marketing.apple.com."
660f1e54185Skrw *
661f1e54185Skrw * will be translated to
662f1e54185Skrw *
663f1e54185Skrw * "eng.apple.com.", "marketing.apple.com."
664f1e54185Skrw */
665f1e54185Skrw void
pretty_print_domain_list(unsigned char * src,size_t srclen,unsigned char * buf,size_t buflen)666f1e54185Skrw pretty_print_domain_list(unsigned char *src, size_t srclen,
667f1e54185Skrw unsigned char *buf, size_t buflen)
668968fe952Skrw {
669f1e54185Skrw char *dupnames, *hn, *inputstring;
670f1e54185Skrw int count;
671968fe952Skrw
672f1e54185Skrw memset(buf, 0, buflen);
673968fe952Skrw
674*09cbf102Skrw /*
675*09cbf102Skrw * N.B.: option data is *NOT* guaranteed to be NUL
676*09cbf102Skrw * terminated. Avoid strlen(), strdup(), etc.!
677*09cbf102Skrw */
678*09cbf102Skrw if (srclen >= DHCP_DOMAIN_SEARCH_LEN || src[0] == '\0')
679f1e54185Skrw return;
680f1e54185Skrw
681*09cbf102Skrw inputstring = malloc(srclen + 1);
682f1e54185Skrw if (inputstring == NULL)
683f1e54185Skrw fatal("domain name list");
684*09cbf102Skrw memcpy(inputstring, src, srclen);
685*09cbf102Skrw inputstring[srclen] = '\0';
686*09cbf102Skrw dupnames = inputstring;
687f1e54185Skrw
688f1e54185Skrw count = 0;
689f1e54185Skrw while ((hn = strsep(&inputstring, " \t")) != NULL) {
690f1e54185Skrw if (strlen(hn) == 0)
691f1e54185Skrw continue;
692f1e54185Skrw if (res_hnok(hn) == 0)
693f1e54185Skrw goto bad;
694f1e54185Skrw if (count > 0)
695f1e54185Skrw strlcat(buf, ", ", buflen);
696f1e54185Skrw strlcat(buf, "\"", buflen);
697f1e54185Skrw strlcat(buf, hn, buflen);
698f1e54185Skrw if (strlcat(buf, "\"", buflen) >= buflen)
699f1e54185Skrw goto bad;
700f1e54185Skrw count++;
701f1e54185Skrw if (count > DHCP_DOMAIN_SEARCH_CNT)
702f1e54185Skrw goto bad;
703968fe952Skrw }
704968fe952Skrw
705f1e54185Skrw free(dupnames);
706f1e54185Skrw return;
707968fe952Skrw
708f1e54185Skrw bad:
709f1e54185Skrw free(dupnames);
710f1e54185Skrw memset(buf, 0, buflen);
711968fe952Skrw }
712968fe952Skrw
7135714f486Skrw /*
714c714dadcShenning * Format the specified option so that a human can easily read it.
715c714dadcShenning */
716c714dadcShenning char *
pretty_print_option(unsigned int code,struct option_data * option,int emit_punct)717acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option,
718acf4c28bSkrw int emit_punct)
7199a2590e5Sderaadt {
720bee06f07Skrw static char optbuf[8192]; /* XXX */
721024801d2Skrw char fmtbuf[32];
722024801d2Skrw struct in_addr foo;
723acf4c28bSkrw unsigned char *data = option->data;
7249a2590e5Sderaadt unsigned char *dp = data;
7258457ebc2Skrw char *op = optbuf, *buf, *name, *fmt;
726024801d2Skrw int hunksize = 0, numhunk = -1, numelem = 0;
727024801d2Skrw int i, j, k, opleft = sizeof(optbuf);
728acf4c28bSkrw int len = option->len;
729f3a8c5fdSkrw int opcount = 0;
730bce09e58Skrw int32_t int32val;
731024801d2Skrw uint32_t uint32val;
732024801d2Skrw uint16_t uint16val;
733024801d2Skrw char comma;
7349a2590e5Sderaadt
7352f18daabSkrw memset(optbuf, 0, sizeof(optbuf));
7362f18daabSkrw
7379a2590e5Sderaadt /* Code should be between 0 and 255. */
7382f18daabSkrw if (code > 255) {
739b4afd8f1Skrw log_warnx("%s: pretty_print_option: bad code %d", log_procname,
740b4afd8f1Skrw code);
7412f18daabSkrw goto done;
7422f18daabSkrw }
7439a2590e5Sderaadt
7446fe46e93Skrw if (emit_punct != 0)
7459a2590e5Sderaadt comma = ',';
7469a2590e5Sderaadt else
7479a2590e5Sderaadt comma = ' ';
7489a2590e5Sderaadt
7495714f486Skrw /* Handle the princess class options with weirdo formats. */
7505714f486Skrw switch (code) {
7515714f486Skrw case DHO_CLASSLESS_STATIC_ROUTES:
7525714f486Skrw case DHO_CLASSLESS_MS_STATIC_ROUTES:
7531abdd88fSkrw pretty_print_classless_routes(dp, len, optbuf, sizeof(optbuf));
7541abdd88fSkrw goto done;
755f1e54185Skrw case DHO_DOMAIN_SEARCH:
756f1e54185Skrw pretty_print_domain_list(dp, len, optbuf, sizeof(optbuf));
757f1e54185Skrw goto done;
7585714f486Skrw default:
7595714f486Skrw break;
7605714f486Skrw }
7615714f486Skrw
7628457ebc2Skrw name = code_to_name(code);
7638457ebc2Skrw fmt = code_to_format(code);
7648457ebc2Skrw
7659a2590e5Sderaadt /* Figure out the size of the data. */
7668457ebc2Skrw for (i = 0; fmt[i]; i++) {
7676fe46e93Skrw if (numhunk == 0) {
768b4afd8f1Skrw log_warnx("%s: %s: excess information in format "
769b53f3a4dSkrw "string: %s", log_procname, name, &fmt[i]);
7702f18daabSkrw goto done;
7719a2590e5Sderaadt }
7729a2590e5Sderaadt numelem++;
7738457ebc2Skrw fmtbuf[i] = fmt[i];
7748457ebc2Skrw switch (fmt[i]) {
7759a2590e5Sderaadt case 'A':
7769a2590e5Sderaadt --numelem;
7779a2590e5Sderaadt fmtbuf[i] = 0;
7789a2590e5Sderaadt numhunk = 0;
77929432cd9Sphessler if (hunksize == 0) {
780b53f3a4dSkrw log_warnx("%s: %s: no size indicator before A"
781b53f3a4dSkrw " in format string: %s", log_procname,
782b53f3a4dSkrw name, fmt);
7832f18daabSkrw goto done;
78429432cd9Sphessler }
7859a2590e5Sderaadt break;
7869a2590e5Sderaadt case 'X':
787c714dadcShenning for (k = 0; k < len; k++)
7886fe46e93Skrw if (isascii(data[k]) == 0 ||
7896fe46e93Skrw isprint(data[k]) == 0)
7909a2590e5Sderaadt break;
791b54c879eShenning if (k == len) {
7929a2590e5Sderaadt fmtbuf[i] = 't';
7939a2590e5Sderaadt numhunk = -2;
7949a2590e5Sderaadt } else {
7959a2590e5Sderaadt hunksize++;
7969a2590e5Sderaadt comma = ':';
7979a2590e5Sderaadt numhunk = 0;
7989a2590e5Sderaadt }
7999a2590e5Sderaadt fmtbuf[i + 1] = 0;
8009a2590e5Sderaadt break;
8019a2590e5Sderaadt case 't':
8029a2590e5Sderaadt fmtbuf[i + 1] = 0;
8039a2590e5Sderaadt numhunk = -2;
8049a2590e5Sderaadt break;
8059a2590e5Sderaadt case 'I':
8069a2590e5Sderaadt case 'l':
8079a2590e5Sderaadt case 'L':
8089a2590e5Sderaadt hunksize += 4;
8099a2590e5Sderaadt break;
8109a2590e5Sderaadt case 'S':
8119a2590e5Sderaadt hunksize += 2;
8129a2590e5Sderaadt break;
8139a2590e5Sderaadt case 'B':
8149a2590e5Sderaadt case 'f':
8159a2590e5Sderaadt hunksize++;
8169a2590e5Sderaadt break;
8179a2590e5Sderaadt case 'e':
8189a2590e5Sderaadt break;
8199a2590e5Sderaadt default:
820b53f3a4dSkrw log_warnx("%s: %s: garbage in format string: %s",
821b53f3a4dSkrw log_procname, name, &fmt[i]);
8222f18daabSkrw goto done;
8239a2590e5Sderaadt }
8249a2590e5Sderaadt }
8259a2590e5Sderaadt
826d22f105fSkrw /* Check for too few bytes. */
8279a2590e5Sderaadt if (hunksize > len) {
828b53f3a4dSkrw log_warnx("%s: %s: expecting at least %d bytes; got %d",
829b53f3a4dSkrw log_procname, name, hunksize, len);
8302f18daabSkrw goto done;
8319a2590e5Sderaadt }
832d22f105fSkrw /* Check for too many bytes. */
8332f18daabSkrw if (numhunk == -1 && hunksize < len) {
834b53f3a4dSkrw log_warnx("%s: %s: expecting only %d bytes: got %d",
835b53f3a4dSkrw log_procname, name, hunksize, len);
8362f18daabSkrw goto done;
8372f18daabSkrw }
8389a2590e5Sderaadt
8399a2590e5Sderaadt /* If this is an array, compute its size. */
8406fe46e93Skrw if (numhunk == 0)
8419a2590e5Sderaadt numhunk = len / hunksize;
8429a2590e5Sderaadt /* See if we got an exact number of hunks. */
8432f18daabSkrw if (numhunk > 0 && numhunk * hunksize != len) {
844b53f3a4dSkrw log_warnx("%s: %s: expecting %d bytes: got %d", log_procname,
845b53f3a4dSkrw name, numhunk * hunksize, len);
8462f18daabSkrw goto done;
8472f18daabSkrw }
8489a2590e5Sderaadt
8499a2590e5Sderaadt /* A one-hunk array prints the same as a single hunk. */
8509a2590e5Sderaadt if (numhunk < 0)
8519a2590e5Sderaadt numhunk = 1;
8529a2590e5Sderaadt
8539a2590e5Sderaadt /* Cycle through the array (or hunk) printing the data. */
8549a2590e5Sderaadt for (i = 0; i < numhunk; i++) {
8559a2590e5Sderaadt for (j = 0; j < numelem; j++) {
8569a2590e5Sderaadt switch (fmtbuf[j]) {
8579a2590e5Sderaadt case 't':
858bee06f07Skrw buf = pretty_print_string(dp, len, emit_punct);
859bee06f07Skrw if (buf == NULL)
860bee06f07Skrw opcount = -1;
861bee06f07Skrw else
862bee06f07Skrw opcount = strlcat(op, buf, opleft);
8639a2590e5Sderaadt break;
8649a2590e5Sderaadt case 'I':
865e95625edSkrw memcpy(&foo.s_addr, dp, sizeof(foo.s_addr));
866f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s",
867f3a8c5fdSkrw inet_ntoa(foo));
868e95625edSkrw dp += sizeof(foo.s_addr);
8699a2590e5Sderaadt break;
8709a2590e5Sderaadt case 'l':
871bce09e58Skrw memcpy(&int32val, dp, sizeof(int32val));
872bce09e58Skrw opcount = snprintf(op, opleft, "%d",
873bce09e58Skrw ntohl(int32val));
874bce09e58Skrw dp += sizeof(int32val);
8759a2590e5Sderaadt break;
8769a2590e5Sderaadt case 'L':
877bce09e58Skrw memcpy(&uint32val, dp, sizeof(uint32val));
878bce09e58Skrw opcount = snprintf(op, opleft, "%u",
879bce09e58Skrw ntohl(uint32val));
880bce09e58Skrw dp += sizeof(uint32val);
8819a2590e5Sderaadt break;
8829a2590e5Sderaadt case 'S':
883bce09e58Skrw memcpy(&uint16val, dp, sizeof(uint16val));
884bce09e58Skrw opcount = snprintf(op, opleft, "%hu",
885bce09e58Skrw ntohs(uint16val));
886bce09e58Skrw dp += sizeof(uint16val);
8879a2590e5Sderaadt break;
8889a2590e5Sderaadt case 'B':
889221bd6c0Skrw opcount = snprintf(op, opleft, "%u", *dp);
890de3ca9dbSkrw dp++;
8919a2590e5Sderaadt break;
892920d03efSkrw case 'X':
893de3ca9dbSkrw opcount = snprintf(op, opleft, "%x", *dp);
894de3ca9dbSkrw dp++;
8959a2590e5Sderaadt break;
8969a2590e5Sderaadt case 'f':
897f3a8c5fdSkrw opcount = snprintf(op, opleft, "%s",
898f3a8c5fdSkrw *dp ? "true" : "false");
899de3ca9dbSkrw dp++;
9009a2590e5Sderaadt break;
9019a2590e5Sderaadt default:
902b53f3a4dSkrw log_warnx("%s: unexpected format code %c",
903b53f3a4dSkrw log_procname, fmtbuf[j]);
9049a2590e5Sderaadt goto toobig;
905f3a8c5fdSkrw }
906515e489cSderaadt if (opcount < 0 || opcount >= opleft)
907f3a8c5fdSkrw goto toobig;
908f3a8c5fdSkrw opleft -= opcount;
909f3a8c5fdSkrw op += opcount;
9109a2590e5Sderaadt if (j + 1 < numelem && comma != ':') {
911f3a8c5fdSkrw opcount = snprintf(op, opleft, " ");
912515e489cSderaadt if (opcount < 0 || opcount >= opleft)
913f3a8c5fdSkrw goto toobig;
914f3a8c5fdSkrw opleft -= opcount;
915f3a8c5fdSkrw op += opcount;
9169a2590e5Sderaadt }
9179a2590e5Sderaadt }
9189a2590e5Sderaadt if (i + 1 < numhunk) {
919f3a8c5fdSkrw opcount = snprintf(op, opleft, "%c", comma);
920515e489cSderaadt if (opcount < 0 || opcount >= opleft)
9219a2590e5Sderaadt goto toobig;
922f3a8c5fdSkrw opleft -= opcount;
923f3a8c5fdSkrw op += opcount;
924f3a8c5fdSkrw }
9259a2590e5Sderaadt }
9262f18daabSkrw
9272f18daabSkrw done:
92851a35423Skrw return optbuf;
9292f18daabSkrw
9309a2590e5Sderaadt toobig:
9312f18daabSkrw memset(optbuf, 0, sizeof(optbuf));
93251a35423Skrw return optbuf;
9339a2590e5Sderaadt }
9349a2590e5Sderaadt
935b414edd1Skrw struct option_data *
unpack_options(struct dhcp_packet * packet)936b414edd1Skrw unpack_options(struct dhcp_packet *packet)
9379a2590e5Sderaadt {
938cb26da20Skrw static struct option_data options[DHO_COUNT];
939b414edd1Skrw int i;
9409a2590e5Sderaadt
941cb26da20Skrw for (i = 0; i < DHO_COUNT; i++) {
942b414edd1Skrw free(options[i].data);
943b414edd1Skrw options[i].data = NULL;
944b414edd1Skrw options[i].len = 0;
945b414edd1Skrw }
94602e02bd5Skrw
94702e02bd5Skrw if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
94802e02bd5Skrw /* Parse the BOOTP/DHCP options field. */
949b414edd1Skrw parse_option_buffer(options, &packet->options[4],
950b414edd1Skrw sizeof(packet->options) - 4);
95102e02bd5Skrw
952b414edd1Skrw /* DHCP packets can also use overload areas for options. */
9536fe46e93Skrw if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL &&
9546fe46e93Skrw options[DHO_DHCP_OPTION_OVERLOAD].data != NULL) {
955b4afd8f1Skrw if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) !=
956b4afd8f1Skrw 0)
957b414edd1Skrw parse_option_buffer(options,
95802e02bd5Skrw (unsigned char *)packet->file,
95902e02bd5Skrw sizeof(packet->file));
960b4afd8f1Skrw if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) !=
961b4afd8f1Skrw 0)
962b414edd1Skrw parse_option_buffer(options,
96302e02bd5Skrw (unsigned char *)packet->sname,
96402e02bd5Skrw sizeof(packet->sname));
96502e02bd5Skrw }
96602e02bd5Skrw }
96702e02bd5Skrw
968b414edd1Skrw return options;
9699a2590e5Sderaadt }
970d712a45dSkrw
971d712a45dSkrw void
merge_option_data(char * fmt,struct option_data * first,struct option_data * second,struct option_data * dest)9726562b533Skrw merge_option_data(char *fmt, struct option_data *first,
973d712a45dSkrw struct option_data *second, struct option_data *dest)
974d712a45dSkrw {
975*09cbf102Skrw int space = 0;
9766562b533Skrw
977d712a45dSkrw free(dest->data);
9786562b533Skrw dest->data = NULL;
979d712a45dSkrw dest->len = first->len + second->len;
9806562b533Skrw if (dest->len == 0)
9816562b533Skrw return;
9826562b533Skrw
983*09cbf102Skrw /*
984*09cbf102Skrw * N.B.: option data is *NOT* guaranteed to be NUL
985*09cbf102Skrw * terminated. Avoid strlen(), strdup(), etc.!
986*09cbf102Skrw */
987*09cbf102Skrw if (fmt[0] == 'D') {
988*09cbf102Skrw if (first->len > 0 && second->len > 0)
989*09cbf102Skrw space = 1;
990*09cbf102Skrw }
991*09cbf102Skrw
992*09cbf102Skrw dest->len += space;
993*09cbf102Skrw dest->data = malloc(dest->len);
994d712a45dSkrw if (dest->data == NULL)
995d712a45dSkrw fatal("merged option data");
996*09cbf102Skrw
997d712a45dSkrw memcpy(dest->data, first->data, first->len);
998*09cbf102Skrw if (space == 1)
999*09cbf102Skrw dest->data[first->len] = ' ';
1000*09cbf102Skrw memcpy(dest->data + first->len + space, second->data, second->len);
1001d712a45dSkrw }
1002