xref: /openbsd/sbin/dhclient/options.c (revision 8457ebc2)
1*8457ebc2Skrw /*	$OpenBSD: options.c,v 1.97 2017/07/08 20:38: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>
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 /*
68*8457ebc2Skrw  * DHCP Option names, formats and codes, from RFC1533.
69*8457ebc2Skrw  *
70*8457ebc2Skrw  * Format codes:
71*8457ebc2Skrw  *
72*8457ebc2Skrw  * e - end of data
73*8457ebc2Skrw  * I - IP address
74*8457ebc2Skrw  * l - 32-bit signed integer
75*8457ebc2Skrw  * L - 32-bit unsigned integer
76*8457ebc2Skrw  * S - 16-bit unsigned integer
77*8457ebc2Skrw  * B - 8-bit unsigned integer
78*8457ebc2Skrw  * t - ASCII text
79*8457ebc2Skrw  * f - flag (true or false)
80*8457ebc2Skrw  * A - array of whatever precedes (e.g., IA means array of IP addresses)
81*8457ebc2Skrw  * C - CIDR description
82*8457ebc2Skrw  */
83*8457ebc2Skrw 
84*8457ebc2Skrw static const struct {
85*8457ebc2Skrw 	char *name;
86*8457ebc2Skrw 	char *format;
87*8457ebc2Skrw } dhcp_options[DHO_COUNT] = {
88*8457ebc2Skrw 	/*   0 */ { "pad", "" },
89*8457ebc2Skrw 	/*   1 */ { "subnet-mask", "I" },
90*8457ebc2Skrw 	/*   2 */ { "time-offset", "l" },
91*8457ebc2Skrw 	/*   3 */ { "routers", "IA" },
92*8457ebc2Skrw 	/*   4 */ { "time-servers", "IA" },
93*8457ebc2Skrw 	/*   5 */ { "ien116-name-servers", "IA" },
94*8457ebc2Skrw 	/*   6 */ { "domain-name-servers", "IA" },
95*8457ebc2Skrw 	/*   7 */ { "log-servers", "IA" },
96*8457ebc2Skrw 	/*   8 */ { "cookie-servers", "IA" },
97*8457ebc2Skrw 	/*   9 */ { "lpr-servers", "IA" },
98*8457ebc2Skrw 	/*  10 */ { "impress-servers", "IA" },
99*8457ebc2Skrw 	/*  11 */ { "resource-location-servers", "IA" },
100*8457ebc2Skrw 	/*  12 */ { "host-name", "t" },
101*8457ebc2Skrw 	/*  13 */ { "boot-size", "S" },
102*8457ebc2Skrw 	/*  14 */ { "merit-dump", "t" },
103*8457ebc2Skrw 	/*  15 */ { "domain-name", "t" },
104*8457ebc2Skrw 	/*  16 */ { "swap-server", "I" },
105*8457ebc2Skrw 	/*  17 */ { "root-path", "t" },
106*8457ebc2Skrw 	/*  18 */ { "extensions-path", "t" },
107*8457ebc2Skrw 	/*  19 */ { "ip-forwarding", "f" },
108*8457ebc2Skrw 	/*  20 */ { "non-local-source-routing", "f" },
109*8457ebc2Skrw 	/*  21 */ { "policy-filter", "IIA" },
110*8457ebc2Skrw 	/*  22 */ { "max-dgram-reassembly", "S" },
111*8457ebc2Skrw 	/*  23 */ { "default-ip-ttl", "B" },
112*8457ebc2Skrw 	/*  24 */ { "path-mtu-aging-timeout", "L" },
113*8457ebc2Skrw 	/*  25 */ { "path-mtu-plateau-table", "SA" },
114*8457ebc2Skrw 	/*  26 */ { "interface-mtu", "S" },
115*8457ebc2Skrw 	/*  27 */ { "all-subnets-local", "f" },
116*8457ebc2Skrw 	/*  28 */ { "broadcast-address", "I" },
117*8457ebc2Skrw 	/*  29 */ { "perform-mask-discovery", "f" },
118*8457ebc2Skrw 	/*  30 */ { "mask-supplier", "f" },
119*8457ebc2Skrw 	/*  31 */ { "router-discovery", "f" },
120*8457ebc2Skrw 	/*  32 */ { "router-solicitation-address", "I" },
121*8457ebc2Skrw 	/*  33 */ { "static-routes", "IIA" },
122*8457ebc2Skrw 	/*  34 */ { "trailer-encapsulation", "f" },
123*8457ebc2Skrw 	/*  35 */ { "arp-cache-timeout", "L" },
124*8457ebc2Skrw 	/*  36 */ { "ieee802-3-encapsulation", "f" },
125*8457ebc2Skrw 	/*  37 */ { "default-tcp-ttl", "B" },
126*8457ebc2Skrw 	/*  38 */ { "tcp-keepalive-interval", "L" },
127*8457ebc2Skrw 	/*  39 */ { "tcp-keepalive-garbage", "f" },
128*8457ebc2Skrw 	/*  40 */ { "nis-domain", "t" },
129*8457ebc2Skrw 	/*  41 */ { "nis-servers", "IA" },
130*8457ebc2Skrw 	/*  42 */ { "ntp-servers", "IA" },
131*8457ebc2Skrw 	/*  43 */ { "vendor-encapsulated-options", "X" },
132*8457ebc2Skrw 	/*  44 */ { "netbios-name-servers", "IA" },
133*8457ebc2Skrw 	/*  45 */ { "netbios-dd-server", "IA" },
134*8457ebc2Skrw 	/*  46 */ { "netbios-node-type", "B" },
135*8457ebc2Skrw 	/*  47 */ { "netbios-scope", "t" },
136*8457ebc2Skrw 	/*  48 */ { "font-servers", "IA" },
137*8457ebc2Skrw 	/*  49 */ { "x-display-manager", "IA" },
138*8457ebc2Skrw 	/*  50 */ { "dhcp-requested-address", "I" },
139*8457ebc2Skrw 	/*  51 */ { "dhcp-lease-time", "L" },
140*8457ebc2Skrw 	/*  52 */ { "dhcp-option-overload", "B" },
141*8457ebc2Skrw 	/*  53 */ { "dhcp-message-type", "B" },
142*8457ebc2Skrw 	/*  54 */ { "dhcp-server-identifier", "I" },
143*8457ebc2Skrw 	/*  55 */ { "dhcp-parameter-request-list", "BA" },
144*8457ebc2Skrw 	/*  56 */ { "dhcp-message", "t" },
145*8457ebc2Skrw 	/*  57 */ { "dhcp-max-message-size", "S" },
146*8457ebc2Skrw 	/*  58 */ { "dhcp-renewal-time", "L" },
147*8457ebc2Skrw 	/*  59 */ { "dhcp-rebinding-time", "L" },
148*8457ebc2Skrw 	/*  60 */ { "dhcp-class-identifier", "t" },
149*8457ebc2Skrw 	/*  61 */ { "dhcp-client-identifier", "X" },
150*8457ebc2Skrw 	/*  62 */ { NULL, NULL },
151*8457ebc2Skrw 	/*  63 */ { NULL, NULL },
152*8457ebc2Skrw 	/*  64 */ { "nisplus-domain", "t" },
153*8457ebc2Skrw 	/*  65 */ { "nisplus-servers", "IA" },
154*8457ebc2Skrw 	/*  66 */ { "tftp-server-name", "t" },
155*8457ebc2Skrw 	/*  67 */ { "bootfile-name", "t" },
156*8457ebc2Skrw 	/*  68 */ { "mobile-ip-home-agent", "IA" },
157*8457ebc2Skrw 	/*  69 */ { "smtp-server", "IA" },
158*8457ebc2Skrw 	/*  70 */ { "pop-server", "IA" },
159*8457ebc2Skrw 	/*  71 */ { "nntp-server", "IA" },
160*8457ebc2Skrw 	/*  72 */ { "www-server", "IA" },
161*8457ebc2Skrw 	/*  73 */ { "finger-server", "IA" },
162*8457ebc2Skrw 	/*  74 */ { "irc-server", "IA" },
163*8457ebc2Skrw 	/*  75 */ { "streettalk-server", "IA" },
164*8457ebc2Skrw 	/*  76 */ { "streettalk-directory-assistance-server", "IA" },
165*8457ebc2Skrw 	/*  77 */ { "user-class", "t" },
166*8457ebc2Skrw 	/*  78 */ { NULL, NULL },
167*8457ebc2Skrw 	/*  79 */ { NULL, NULL },
168*8457ebc2Skrw 	/*  80 */ { NULL, NULL },
169*8457ebc2Skrw 	/*  81 */ { NULL, NULL },
170*8457ebc2Skrw 	/*  82 */ { "relay-agent-information", "X" },
171*8457ebc2Skrw 	/*  83 */ { NULL, NULL },
172*8457ebc2Skrw 	/*  84 */ { NULL, NULL },
173*8457ebc2Skrw 	/*  85 */ { "nds-servers", "IA" },
174*8457ebc2Skrw 	/*  86 */ { "nds-tree-name", "X" },
175*8457ebc2Skrw 	/*  87 */ { "nds-context", "X" },
176*8457ebc2Skrw 	/*  88 */ { NULL, NULL },
177*8457ebc2Skrw 	/*  89 */ { NULL, NULL },
178*8457ebc2Skrw 	/*  90 */ { NULL, NULL },
179*8457ebc2Skrw 	/*  91 */ { NULL, NULL },
180*8457ebc2Skrw 	/*  92 */ { NULL, NULL },
181*8457ebc2Skrw 	/*  93 */ { NULL, NULL },
182*8457ebc2Skrw 	/*  94 */ { NULL, NULL },
183*8457ebc2Skrw 	/*  95 */ { NULL, NULL },
184*8457ebc2Skrw 	/*  96 */ { NULL, NULL },
185*8457ebc2Skrw 	/*  97 */ { NULL, NULL },
186*8457ebc2Skrw 	/*  98 */ { NULL, NULL },
187*8457ebc2Skrw 	/*  99 */ { NULL, NULL },
188*8457ebc2Skrw 	/* 100 */ { NULL, NULL },
189*8457ebc2Skrw 	/* 101 */ { NULL, NULL },
190*8457ebc2Skrw 	/* 102 */ { NULL, NULL },
191*8457ebc2Skrw 	/* 103 */ { NULL, NULL },
192*8457ebc2Skrw 	/* 104 */ { NULL, NULL },
193*8457ebc2Skrw 	/* 105 */ { NULL, NULL },
194*8457ebc2Skrw 	/* 106 */ { NULL, NULL },
195*8457ebc2Skrw 	/* 107 */ { NULL, NULL },
196*8457ebc2Skrw 	/* 108 */ { NULL, NULL },
197*8457ebc2Skrw 	/* 109 */ { NULL, NULL },
198*8457ebc2Skrw 	/* 110 */ { NULL, NULL },
199*8457ebc2Skrw 	/* 111 */ { NULL, NULL },
200*8457ebc2Skrw 	/* 112 */ { NULL, NULL },
201*8457ebc2Skrw 	/* 113 */ { NULL, NULL },
202*8457ebc2Skrw 	/* 114 */ { NULL, NULL },
203*8457ebc2Skrw 	/* 115 */ { NULL, NULL },
204*8457ebc2Skrw 	/* 116 */ { NULL, NULL },
205*8457ebc2Skrw 	/* 117 */ { NULL, NULL },
206*8457ebc2Skrw 	/* 118 */ { NULL, NULL },
207*8457ebc2Skrw 	/* 119 */ { "domain-search", "X" },
208*8457ebc2Skrw 	/* 120 */ { NULL, NULL },
209*8457ebc2Skrw 	/* 121 */ { "classless-static-routes", "CIA" },
210*8457ebc2Skrw 	/* 122 */ { NULL, NULL },
211*8457ebc2Skrw 	/* 123 */ { NULL, NULL },
212*8457ebc2Skrw 	/* 124 */ { NULL, NULL },
213*8457ebc2Skrw 	/* 125 */ { NULL, NULL },
214*8457ebc2Skrw 	/* 126 */ { NULL, NULL },
215*8457ebc2Skrw 	/* 127 */ { NULL, NULL },
216*8457ebc2Skrw 	/* 128 */ { NULL, NULL },
217*8457ebc2Skrw 	/* 129 */ { NULL, NULL },
218*8457ebc2Skrw 	/* 130 */ { NULL, NULL },
219*8457ebc2Skrw 	/* 131 */ { NULL, NULL },
220*8457ebc2Skrw 	/* 132 */ { NULL, NULL },
221*8457ebc2Skrw 	/* 133 */ { NULL, NULL },
222*8457ebc2Skrw 	/* 134 */ { NULL, NULL },
223*8457ebc2Skrw 	/* 135 */ { NULL, NULL },
224*8457ebc2Skrw 	/* 136 */ { NULL, NULL },
225*8457ebc2Skrw 	/* 137 */ { NULL, NULL },
226*8457ebc2Skrw 	/* 138 */ { NULL, NULL },
227*8457ebc2Skrw 	/* 139 */ { NULL, NULL },
228*8457ebc2Skrw 	/* 140 */ { NULL, NULL },
229*8457ebc2Skrw 	/* 141 */ { NULL, NULL },
230*8457ebc2Skrw 	/* 142 */ { NULL, NULL },
231*8457ebc2Skrw 	/* 143 */ { NULL, NULL },
232*8457ebc2Skrw 	/* 144 */ { "tftp-config-file", "t" },
233*8457ebc2Skrw 	/* 145 */ { NULL, NULL },
234*8457ebc2Skrw 	/* 146 */ { NULL, NULL },
235*8457ebc2Skrw 	/* 147 */ { NULL, NULL },
236*8457ebc2Skrw 	/* 148 */ { NULL, NULL },
237*8457ebc2Skrw 	/* 149 */ { NULL, NULL },
238*8457ebc2Skrw 	/* 150 */ { "voip-configuration-server", "IA" },
239*8457ebc2Skrw 	/* 151 */ { NULL, NULL },
240*8457ebc2Skrw 	/* 152 */ { NULL, NULL },
241*8457ebc2Skrw 	/* 153 */ { NULL, NULL },
242*8457ebc2Skrw 	/* 154 */ { NULL, NULL },
243*8457ebc2Skrw 	/* 155 */ { NULL, NULL },
244*8457ebc2Skrw 	/* 156 */ { NULL, NULL },
245*8457ebc2Skrw 	/* 157 */ { NULL, NULL },
246*8457ebc2Skrw 	/* 158 */ { NULL, NULL },
247*8457ebc2Skrw 	/* 159 */ { NULL, NULL },
248*8457ebc2Skrw 	/* 160 */ { NULL, NULL },
249*8457ebc2Skrw 	/* 161 */ { NULL, NULL },
250*8457ebc2Skrw 	/* 162 */ { NULL, NULL },
251*8457ebc2Skrw 	/* 163 */ { NULL, NULL },
252*8457ebc2Skrw 	/* 164 */ { NULL, NULL },
253*8457ebc2Skrw 	/* 165 */ { NULL, NULL },
254*8457ebc2Skrw 	/* 166 */ { NULL, NULL },
255*8457ebc2Skrw 	/* 167 */ { NULL, NULL },
256*8457ebc2Skrw 	/* 168 */ { NULL, NULL },
257*8457ebc2Skrw 	/* 169 */ { NULL, NULL },
258*8457ebc2Skrw 	/* 170 */ { NULL, NULL },
259*8457ebc2Skrw 	/* 171 */ { NULL, NULL },
260*8457ebc2Skrw 	/* 172 */ { NULL, NULL },
261*8457ebc2Skrw 	/* 173 */ { NULL, NULL },
262*8457ebc2Skrw 	/* 174 */ { NULL, NULL },
263*8457ebc2Skrw 	/* 175 */ { NULL, NULL },
264*8457ebc2Skrw 	/* 176 */ { NULL, NULL },
265*8457ebc2Skrw 	/* 177 */ { NULL, NULL },
266*8457ebc2Skrw 	/* 178 */ { NULL, NULL },
267*8457ebc2Skrw 	/* 179 */ { NULL, NULL },
268*8457ebc2Skrw 	/* 180 */ { NULL, NULL },
269*8457ebc2Skrw 	/* 181 */ { NULL, NULL },
270*8457ebc2Skrw 	/* 182 */ { NULL, NULL },
271*8457ebc2Skrw 	/* 183 */ { NULL, NULL },
272*8457ebc2Skrw 	/* 184 */ { NULL, NULL },
273*8457ebc2Skrw 	/* 185 */ { NULL, NULL },
274*8457ebc2Skrw 	/* 186 */ { NULL, NULL },
275*8457ebc2Skrw 	/* 187 */ { NULL, NULL },
276*8457ebc2Skrw 	/* 188 */ { NULL, NULL },
277*8457ebc2Skrw 	/* 189 */ { NULL, NULL },
278*8457ebc2Skrw 	/* 190 */ { NULL, NULL },
279*8457ebc2Skrw 	/* 191 */ { NULL, NULL },
280*8457ebc2Skrw 	/* 192 */ { NULL, NULL },
281*8457ebc2Skrw 	/* 193 */ { NULL, NULL },
282*8457ebc2Skrw 	/* 194 */ { NULL, NULL },
283*8457ebc2Skrw 	/* 195 */ { NULL, NULL },
284*8457ebc2Skrw 	/* 196 */ { NULL, NULL },
285*8457ebc2Skrw 	/* 197 */ { NULL, NULL },
286*8457ebc2Skrw 	/* 198 */ { NULL, NULL },
287*8457ebc2Skrw 	/* 199 */ { NULL, NULL },
288*8457ebc2Skrw 	/* 200 */ { NULL, NULL },
289*8457ebc2Skrw 	/* 201 */ { NULL, NULL },
290*8457ebc2Skrw 	/* 202 */ { NULL, NULL },
291*8457ebc2Skrw 	/* 203 */ { NULL, NULL },
292*8457ebc2Skrw 	/* 204 */ { NULL, NULL },
293*8457ebc2Skrw 	/* 205 */ { NULL, NULL },
294*8457ebc2Skrw 	/* 206 */ { NULL, NULL },
295*8457ebc2Skrw 	/* 207 */ { NULL, NULL },
296*8457ebc2Skrw 	/* 208 */ { NULL, NULL },
297*8457ebc2Skrw 	/* 209 */ { NULL, NULL },
298*8457ebc2Skrw 	/* 210 */ { NULL, NULL },
299*8457ebc2Skrw 	/* 211 */ { NULL, NULL },
300*8457ebc2Skrw 	/* 212 */ { NULL, NULL },
301*8457ebc2Skrw 	/* 213 */ { NULL, NULL },
302*8457ebc2Skrw 	/* 214 */ { NULL, NULL },
303*8457ebc2Skrw 	/* 215 */ { NULL, NULL },
304*8457ebc2Skrw 	/* 216 */ { NULL, NULL },
305*8457ebc2Skrw 	/* 217 */ { NULL, NULL },
306*8457ebc2Skrw 	/* 218 */ { NULL, NULL },
307*8457ebc2Skrw 	/* 219 */ { NULL, NULL },
308*8457ebc2Skrw 	/* 220 */ { NULL, NULL },
309*8457ebc2Skrw 	/* 221 */ { NULL, NULL },
310*8457ebc2Skrw 	/* 222 */ { NULL, NULL },
311*8457ebc2Skrw 	/* 223 */ { NULL, NULL },
312*8457ebc2Skrw 	/* 224 */ { NULL, NULL },
313*8457ebc2Skrw 	/* 225 */ { NULL, NULL },
314*8457ebc2Skrw 	/* 226 */ { NULL, NULL },
315*8457ebc2Skrw 	/* 227 */ { NULL, NULL },
316*8457ebc2Skrw 	/* 228 */ { NULL, NULL },
317*8457ebc2Skrw 	/* 229 */ { NULL, NULL },
318*8457ebc2Skrw 	/* 230 */ { NULL, NULL },
319*8457ebc2Skrw 	/* 231 */ { NULL, NULL },
320*8457ebc2Skrw 	/* 232 */ { NULL, NULL },
321*8457ebc2Skrw 	/* 233 */ { NULL, NULL },
322*8457ebc2Skrw 	/* 234 */ { NULL, NULL },
323*8457ebc2Skrw 	/* 235 */ { NULL, NULL },
324*8457ebc2Skrw 	/* 236 */ { NULL, NULL },
325*8457ebc2Skrw 	/* 237 */ { NULL, NULL },
326*8457ebc2Skrw 	/* 238 */ { NULL, NULL },
327*8457ebc2Skrw 	/* 239 */ { NULL, NULL },
328*8457ebc2Skrw 	/* 240 */ { NULL, NULL },
329*8457ebc2Skrw 	/* 241 */ { NULL, NULL },
330*8457ebc2Skrw 	/* 242 */ { NULL, NULL },
331*8457ebc2Skrw 	/* 243 */ { NULL, NULL },
332*8457ebc2Skrw 	/* 244 */ { NULL, NULL },
333*8457ebc2Skrw 	/* 245 */ { NULL, NULL },
334*8457ebc2Skrw 	/* 246 */ { NULL, NULL },
335*8457ebc2Skrw 	/* 247 */ { NULL, NULL },
336*8457ebc2Skrw 	/* 248 */ { NULL, NULL },
337*8457ebc2Skrw 	/* 249 */ { "classless-ms-static-routes", "CIA" },
338*8457ebc2Skrw 	/* 250 */ { NULL, NULL },
339*8457ebc2Skrw 	/* 251 */ { NULL, NULL },
340*8457ebc2Skrw 	/* 252 */ { "autoproxy-script", "t" },
341*8457ebc2Skrw 	/* 253 */ { NULL, NULL },
342*8457ebc2Skrw 	/* 254 */ { NULL, NULL },
343*8457ebc2Skrw 	/* 255 */ { "option-end", "e" },
344*8457ebc2Skrw };
345*8457ebc2Skrw 
346*8457ebc2Skrw char *
347*8457ebc2Skrw code_to_name(int code)
348*8457ebc2Skrw {
349*8457ebc2Skrw 	static char	 unknown[11];	/* "option-NNN" */
350*8457ebc2Skrw 	int		 ret;
351*8457ebc2Skrw 
352*8457ebc2Skrw 	if (code < 0 || code >= DHO_COUNT)
353*8457ebc2Skrw 		return "";
354*8457ebc2Skrw 
355*8457ebc2Skrw 	if (dhcp_options[code].name != NULL)
356*8457ebc2Skrw 		return dhcp_options[code].name;
357*8457ebc2Skrw 
358*8457ebc2Skrw 	ret = snprintf(unknown, sizeof(unknown), "option-%d", code);
359*8457ebc2Skrw 	if (ret == -1 || ret >= (int)sizeof(unknown))
360*8457ebc2Skrw 		return "";
361*8457ebc2Skrw 
362*8457ebc2Skrw 	return unknown;
363*8457ebc2Skrw }
364*8457ebc2Skrw 
365*8457ebc2Skrw int
366*8457ebc2Skrw name_to_code(char *name)
367*8457ebc2Skrw {
368*8457ebc2Skrw 	char	unknown[11];	/* "option-NNN" */
369*8457ebc2Skrw 	int	code, ret;
370*8457ebc2Skrw 
371*8457ebc2Skrw 	for (code = 1; code < DHO_END; code++) {
372*8457ebc2Skrw 		if (dhcp_options[code].name == NULL) {
373*8457ebc2Skrw 			ret = snprintf(unknown, sizeof(unknown), "option-%d",
374*8457ebc2Skrw 			    code);
375*8457ebc2Skrw 			if (ret == -1 || ret >= (int)sizeof(unknown))
376*8457ebc2Skrw 				return DHO_END;
377*8457ebc2Skrw 			if (strcasecmp(unknown, name) == 0)
378*8457ebc2Skrw 				return code;
379*8457ebc2Skrw 		} else if (strcasecmp(dhcp_options[code].name, name) == 0) {
380*8457ebc2Skrw 			return code;
381*8457ebc2Skrw 		}
382*8457ebc2Skrw 	}
383*8457ebc2Skrw 
384*8457ebc2Skrw 	return DHO_END;
385*8457ebc2Skrw }
386*8457ebc2Skrw 
387*8457ebc2Skrw char *
388*8457ebc2Skrw code_to_format(int code)
389*8457ebc2Skrw {
390*8457ebc2Skrw 	if (code < 0 || code >= DHO_COUNT)
391*8457ebc2Skrw 		return "";
392*8457ebc2Skrw 
393*8457ebc2Skrw 	if (dhcp_options[code].format == NULL)
394*8457ebc2Skrw 		return "X";
395*8457ebc2Skrw 
396*8457ebc2Skrw 	return dhcp_options[code].format;
397*8457ebc2Skrw }
398*8457ebc2Skrw 
399*8457ebc2Skrw /*
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;
408*8457ebc2Skrw 	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 
420*8457ebc2Skrw 		name = code_to_name(code);
421*8457ebc2Skrw 		fmt = code_to_format(code);
422*8457ebc2Skrw 
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.",
434*8457ebc2Skrw 				    name, len);
43502e02bd5Skrw 				return (0);
4369a2590e5Sderaadt 			}
43799c003b1Skrw 		} else {
438*8457ebc2Skrw 			log_warnx("option %s has no length field.", name);
43999c003b1Skrw 			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 		 */
449*8457ebc2Skrw 		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 		 */
4584f062ee3Skrw 		if (!options[code].data) {
4598e916ab9Shenning 			if (!(t = calloc(1, len + 1)))
460385a6373Skrw 				fatalx("Can't allocate storage for option %s.",
461*8457ebc2Skrw 				    name);
462c714dadcShenning 			/*
463c714dadcShenning 			 * Copy and NUL-terminate the option (in case
464cff08477Sstevesk 			 * it's an ASCII string).
465c714dadcShenning 			 */
4669a2590e5Sderaadt 			memcpy(t, &s[2], len);
4679a2590e5Sderaadt 			t[len] = 0;
4684f062ee3Skrw 			options[code].len = len;
4694f062ee3Skrw 			options[code].data = t;
4709a2590e5Sderaadt 		} else {
471c714dadcShenning 			/*
472c714dadcShenning 			 * If it's a repeat, concatenate it to whatever
47392018899Skrw 			 * we last saw.
474c714dadcShenning 			 */
4754f062ee3Skrw 			t = calloc(1, len + options[code].len + 1);
4769a2590e5Sderaadt 			if (!t)
477385a6373Skrw 				fatalx("Can't expand storage for option %s.",
478*8457ebc2Skrw 				    name);
4794f062ee3Skrw 			memcpy(t, options[code].data, options[code].len);
4804f062ee3Skrw 			memcpy(t + options[code].len, &s[2], len);
4814f062ee3Skrw 			options[code].len += len;
4824f062ee3Skrw 			t[options[code].len] = 0;
4834f062ee3Skrw 			free(options[code].data);
4844f062ee3Skrw 			options[code].data = t;
4859a2590e5Sderaadt 		}
4869a2590e5Sderaadt 		s += len + 2;
4879a2590e5Sderaadt 	}
48802e02bd5Skrw 
48902e02bd5Skrw 	return (1);
4909a2590e5Sderaadt }
4919a2590e5Sderaadt 
492c714dadcShenning /*
493b414edd1Skrw  * Pack as many options as fit in buflen bytes of buf. Return the
49496978980Skrw  * offset of the start of the last option copied. A caller can check
49596978980Skrw  * to see if it's DHO_END to decide if all the options were copied.
496c714dadcShenning  */
497c714dadcShenning int
498b414edd1Skrw pack_options(unsigned char *buf, int buflen, struct option_data *options)
4999a2590e5Sderaadt {
50096978980Skrw 	int ix, incr, length, bufix, code, lastopt = -1;
5019a2590e5Sderaadt 
502736b0ed2Skrw 	memset(buf, 0, buflen);
5039a2590e5Sderaadt 
50496978980Skrw 	memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
505d6a67f0fSkrw 	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
506d6a67f0fSkrw 		memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
507d6a67f0fSkrw 		buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
508d6a67f0fSkrw 		bufix = 7;
509d6a67f0fSkrw 	} else
51096978980Skrw 		bufix = 4;
5119a2590e5Sderaadt 
51296978980Skrw 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
513d6a67f0fSkrw 		if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE)
5149a2590e5Sderaadt 			continue;
5159a2590e5Sderaadt 
516d7d9bbf5Skrw 		length = options[code].len;
51796978980Skrw 		if (bufix + length + 2*((length+254)/255) >= buflen)
51896978980Skrw 			return (lastopt);
5199a2590e5Sderaadt 
52096978980Skrw 		lastopt = bufix;
5219a2590e5Sderaadt 		ix = 0;
5229a2590e5Sderaadt 
5239a2590e5Sderaadt 		while (length) {
52496978980Skrw 			incr = length > 255 ? 255 : length;
5259a2590e5Sderaadt 
52696978980Skrw 			buf[bufix++] = code;
52796978980Skrw 			buf[bufix++] = incr;
52896978980Skrw 			memcpy(buf + bufix, options[code].data + ix, incr);
5299a2590e5Sderaadt 
5309a2590e5Sderaadt 			length -= incr;
5319a2590e5Sderaadt 			ix += incr;
5326fc9f4f6Skrw 			bufix += incr;
5339a2590e5Sderaadt 		}
5349a2590e5Sderaadt 	}
53596978980Skrw 
53696978980Skrw 	if (bufix < buflen) {
53796978980Skrw 		buf[bufix] = DHO_END;
53896978980Skrw 		lastopt = bufix;
53996978980Skrw 	}
54096978980Skrw 
54196978980Skrw 	return (lastopt);
5429a2590e5Sderaadt }
5439a2590e5Sderaadt 
544c714dadcShenning /*
545482123e8Skrw  * Use vis() to encode characters of src and append encoded characters onto
546482123e8Skrw  * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be
547482123e8Skrw  * represented as '"' delimited strings and safely passed to scripts. Surround
548482123e8Skrw  * result with double quotes if emit_punct is true.
549482123e8Skrw  */
550bee06f07Skrw char *
551bee06f07Skrw pretty_print_string(unsigned char *src, size_t srclen, int emit_punct)
552482123e8Skrw {
553bee06f07Skrw 	static char string[8196];
554482123e8Skrw 	char visbuf[5];
555482123e8Skrw 	unsigned char *origsrc = src;
556bee06f07Skrw 	size_t rslt = 0;
557482123e8Skrw 
558bee06f07Skrw 	memset(string, 0, sizeof(string));
559bee06f07Skrw 
560bee06f07Skrw 	if (emit_punct)
561bee06f07Skrw 		rslt = strlcat(string, "\"", sizeof(string));
562482123e8Skrw 
563482123e8Skrw 	for (; src < origsrc + srclen; src++) {
564482123e8Skrw 		if (*src && strchr("\"'$`\\", *src))
565642cc348Skrw 			vis(visbuf, *src, VIS_ALL | VIS_OCTAL, *src+1);
566642cc348Skrw 		else
567482123e8Skrw 			vis(visbuf, *src, VIS_OCTAL, *src+1);
568bee06f07Skrw 		rslt = strlcat(string, visbuf, sizeof(string));
569482123e8Skrw 	}
570482123e8Skrw 
571bee06f07Skrw 	if (emit_punct)
572bee06f07Skrw 		rslt = strlcat(string, "\"", sizeof(string));
573bee06f07Skrw 
574bee06f07Skrw 	if (rslt >= sizeof(string))
575bee06f07Skrw 		return (NULL);
576bee06f07Skrw 
577bee06f07Skrw 	return (string);
578482123e8Skrw }
579482123e8Skrw 
580482123e8Skrw /*
5815714f486Skrw  * Must special case *_CLASSLESS_* route options due to the variable size
5825714f486Skrw  * of the CIDR element in its CIA format.
5835714f486Skrw  */
584da2eb076Skrw char *
585da2eb076Skrw pretty_print_classless_routes(unsigned char *src, size_t srclen)
5865714f486Skrw {
587da2eb076Skrw 	static char string[8196];
588da2eb076Skrw 	char bitsbuf[5];	/* to hold "/nn " */
589da2eb076Skrw 	struct in_addr net, gateway;
590e0a6d2f1Skrw 	unsigned int bytes;
591e0a6d2f1Skrw 	int bits, rslt;
5925714f486Skrw 
593da2eb076Skrw 	memset(string, 0, sizeof(string));
594da2eb076Skrw 
595da2eb076Skrw 	while (srclen) {
5965714f486Skrw 		bits = *src;
5975714f486Skrw 		src++;
5985714f486Skrw 		srclen--;
599da2eb076Skrw 
6005714f486Skrw 		bytes = (bits + 7) / 8;
601da2eb076Skrw 		if (srclen < (bytes + sizeof(gateway.s_addr)) ||
602da2eb076Skrw 		    bytes > sizeof(net.s_addr))
603da2eb076Skrw 			return (NULL);
604da2eb076Skrw 		rslt = snprintf(bitsbuf, sizeof(bitsbuf), "/%d ", bits);
6051be0b429Skrw 		if (rslt == -1 || (unsigned int)rslt >= sizeof(bitsbuf))
606da2eb076Skrw 			return (NULL);
607da2eb076Skrw 
608da2eb076Skrw 		memset(&net, 0, sizeof(net));
609da2eb076Skrw 		memcpy(&net.s_addr, src, bytes);
6105714f486Skrw 		src += bytes;
6115714f486Skrw 		srclen -= bytes;
612da2eb076Skrw 
6135714f486Skrw 		memcpy(&gateway.s_addr, src, sizeof(gateway.s_addr));
6145714f486Skrw 		src += sizeof(gateway.s_addr);
6155714f486Skrw 		srclen -= sizeof(gateway.s_addr);
616da2eb076Skrw 
617da2eb076Skrw 		if (strlen(string) > 0)
618da2eb076Skrw 			strlcat(string, ", ", sizeof(string));
619da2eb076Skrw 		strlcat(string, inet_ntoa(net), sizeof(string));
620da2eb076Skrw 		strlcat(string, bitsbuf, sizeof(string));
6211be0b429Skrw 		if (strlcat(string, inet_ntoa(gateway), sizeof(string)) >=
6221be0b429Skrw 		    sizeof(string))
623da2eb076Skrw 			return (NULL);
6245714f486Skrw 	}
6255714f486Skrw 
626da2eb076Skrw 	return (string);
6275714f486Skrw }
6285714f486Skrw 
629968fe952Skrw int
630968fe952Skrw expand_search_domain_name(unsigned char *src, size_t srclen, int *offset,
631968fe952Skrw     unsigned char *domain_search)
632968fe952Skrw {
633e0a6d2f1Skrw 	unsigned int i;
634e0a6d2f1Skrw 	int domain_name_len, label_len, pointer, pointed_len;
635968fe952Skrw 	char *cursor;
636968fe952Skrw 
637968fe952Skrw 	cursor = domain_search + strlen(domain_search);
638968fe952Skrw 	domain_name_len = 0;
639968fe952Skrw 
640968fe952Skrw 	i = *offset;
641968fe952Skrw 	while (i <= srclen) {
642968fe952Skrw 		label_len = src[i];
643968fe952Skrw 		if (label_len == 0) {
644968fe952Skrw 			/*
645968fe952Skrw 			 * A zero-length label marks the end of this
646968fe952Skrw 			 * domain name.
647968fe952Skrw 			 */
648968fe952Skrw 			*offset = i + 1;
649968fe952Skrw 			return (domain_name_len);
650968fe952Skrw 		} else if (label_len & 0xC0) {
651968fe952Skrw 			/* This is a pointer to another list of labels. */
652968fe952Skrw 			if (i + 1 >= srclen) {
653968fe952Skrw 				/* The pointer is truncated. */
654385a6373Skrw 				log_warnx("Truncated pointer in DHCP Domain "
655968fe952Skrw 				    "Search option.");
656968fe952Skrw 				return (-1);
657968fe952Skrw 			}
658968fe952Skrw 
659968fe952Skrw 			pointer = ((label_len & ~(0xC0)) << 8) + src[i + 1];
660968fe952Skrw 			if (pointer >= *offset) {
661968fe952Skrw 				/*
662968fe952Skrw 				 * The pointer must indicates a prior
663968fe952Skrw 				 * occurance.
664968fe952Skrw 				 */
665385a6373Skrw 				log_warnx("Invalid forward pointer in DHCP "
666968fe952Skrw 				    "Domain Search option compression.");
667968fe952Skrw 				return (-1);
668968fe952Skrw 			}
669968fe952Skrw 
670968fe952Skrw 			pointed_len = expand_search_domain_name(src, srclen,
671968fe952Skrw 			    &pointer, domain_search);
672968fe952Skrw 			domain_name_len += pointed_len;
673968fe952Skrw 
674968fe952Skrw 			*offset = i + 2;
675968fe952Skrw 			return (domain_name_len);
676968fe952Skrw 		}
677968fe952Skrw 		if (i + label_len + 1 > srclen) {
678385a6373Skrw 			log_warnx("Truncated label in DHCP Domain Search "
679968fe952Skrw 			    "option.");
680968fe952Skrw 			return (-1);
681968fe952Skrw 		}
682968fe952Skrw 		/*
683968fe952Skrw 		 * Update the domain name length with the length of the
684968fe952Skrw 		 * current label, plus a trailing dot ('.').
685968fe952Skrw 		 */
686968fe952Skrw 		domain_name_len += label_len + 1;
687968fe952Skrw 
688968fe952Skrw 		if (strlen(domain_search) + domain_name_len >=
689968fe952Skrw 		    DHCP_DOMAIN_SEARCH_LEN) {
690385a6373Skrw 			log_warnx("Domain search list too long.");
691968fe952Skrw 			return (-1);
692968fe952Skrw 		}
693968fe952Skrw 
694968fe952Skrw 		/* Copy the label found. */
695968fe952Skrw 		memcpy(cursor, src + i + 1, label_len);
696968fe952Skrw 		cursor[label_len] = '.';
697968fe952Skrw 
698968fe952Skrw 		/* Move cursor. */
699968fe952Skrw 		i += label_len + 1;
700968fe952Skrw 		cursor += label_len + 1;
701968fe952Skrw 	}
702968fe952Skrw 
703385a6373Skrw 	log_warnx("Truncated DHCP Domain Search option.");
704968fe952Skrw 
705968fe952Skrw 	return (-1);
706968fe952Skrw }
707968fe952Skrw 
708968fe952Skrw /*
709968fe952Skrw  * Must special case DHO_DOMAIN_SEARCH because it is encoded as described
710968fe952Skrw  * in RFC 1035 section 4.1.4.
711968fe952Skrw  */
7124d36d16aSkrw char *
7134d36d16aSkrw pretty_print_domain_search(unsigned char *src, size_t srclen)
714968fe952Skrw {
7154d36d16aSkrw 	static char domain_search[DHCP_DOMAIN_SEARCH_LEN];
716e0a6d2f1Skrw 	unsigned int offset;
717e0a6d2f1Skrw 	int len, expanded_len, domains;
7184d36d16aSkrw 	unsigned char *cursor;
719968fe952Skrw 
7204d36d16aSkrw 	memset(domain_search, 0, sizeof(domain_search));
721968fe952Skrw 
722968fe952Skrw 	/* Compute expanded length. */
723968fe952Skrw 	expanded_len = len = 0;
724968fe952Skrw 	domains = 0;
725968fe952Skrw 	offset = 0;
726968fe952Skrw 	while (offset < srclen) {
727968fe952Skrw 		cursor = domain_search + strlen(domain_search);
728968fe952Skrw 		if (domain_search[0]) {
729968fe952Skrw 			*cursor = ' ';
730968fe952Skrw 			expanded_len++;
731968fe952Skrw 		}
732968fe952Skrw 		len = expand_search_domain_name(src, srclen, &offset,
733968fe952Skrw 		    domain_search);
7344d36d16aSkrw 		if (len == -1)
7354d36d16aSkrw 			return (NULL);
736968fe952Skrw 		domains++;
737968fe952Skrw 		expanded_len += len;
7384d36d16aSkrw 		if (domains > DHCP_DOMAIN_SEARCH_CNT)
7394d36d16aSkrw 			return (NULL);
740968fe952Skrw 	}
741968fe952Skrw 
7424d36d16aSkrw 	return (domain_search);
743968fe952Skrw }
744968fe952Skrw 
7455714f486Skrw /*
746c714dadcShenning  * Format the specified option so that a human can easily read it.
747c714dadcShenning  */
748c714dadcShenning char *
749acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option,
750acf4c28bSkrw     int emit_punct)
7519a2590e5Sderaadt {
752bee06f07Skrw 	static char	 optbuf[8192]; /* XXX */
753024801d2Skrw 	char		 fmtbuf[32];
754024801d2Skrw 	struct in_addr	 foo;
755acf4c28bSkrw 	unsigned char	*data = option->data;
7569a2590e5Sderaadt 	unsigned char	*dp = data;
757*8457ebc2Skrw 	char		*op = optbuf, *buf, *name, *fmt;
758024801d2Skrw 	int		 hunksize = 0, numhunk = -1, numelem = 0;
759024801d2Skrw 	int		 i, j, k, opleft = sizeof(optbuf);
760acf4c28bSkrw 	int		 len = option->len;
761f3a8c5fdSkrw 	int		 opcount = 0;
762bce09e58Skrw 	int32_t		 int32val;
763024801d2Skrw 	uint32_t	 uint32val;
764024801d2Skrw 	uint16_t	 uint16val;
765024801d2Skrw 	char		 comma;
7669a2590e5Sderaadt 
7672f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
7682f18daabSkrw 
7699a2590e5Sderaadt 	/* Code should be between 0 and 255. */
7702f18daabSkrw 	if (code > 255) {
771385a6373Skrw 		log_warnx("pretty_print_option: bad code %d", code);
7722f18daabSkrw 		goto done;
7732f18daabSkrw 	}
7749a2590e5Sderaadt 
775acf4c28bSkrw 	if (emit_punct)
7769a2590e5Sderaadt 		comma = ',';
7779a2590e5Sderaadt 	else
7789a2590e5Sderaadt 		comma = ' ';
7799a2590e5Sderaadt 
7805714f486Skrw 	/* Handle the princess class options with weirdo formats. */
7815714f486Skrw 	switch (code) {
7825714f486Skrw 	case DHO_CLASSLESS_STATIC_ROUTES:
7835714f486Skrw 	case DHO_CLASSLESS_MS_STATIC_ROUTES:
784da2eb076Skrw 		buf = pretty_print_classless_routes(dp, len);
785da2eb076Skrw 		if (buf == NULL)
7865714f486Skrw 			goto toobig;
787da2eb076Skrw 		strlcat(optbuf, buf, sizeof(optbuf));
7885714f486Skrw 		goto done;
7895714f486Skrw 	default:
7905714f486Skrw 		break;
7915714f486Skrw 	}
7925714f486Skrw 
793*8457ebc2Skrw 	name = code_to_name(code);
794*8457ebc2Skrw 	fmt = code_to_format(code);
795*8457ebc2Skrw 
7969a2590e5Sderaadt 	/* Figure out the size of the data. */
797*8457ebc2Skrw 	for (i = 0; fmt[i]; i++) {
7989a2590e5Sderaadt 		if (!numhunk) {
799833082e5Skrw 			log_warnx("%s: Excess information in format string: "
800*8457ebc2Skrw 			    "%s", name, &fmt[i]);
8012f18daabSkrw 			goto done;
8029a2590e5Sderaadt 		}
8039a2590e5Sderaadt 		numelem++;
804*8457ebc2Skrw 		fmtbuf[i] = fmt[i];
805*8457ebc2Skrw 		switch (fmt[i]) {
8069a2590e5Sderaadt 		case 'A':
8079a2590e5Sderaadt 			--numelem;
8089a2590e5Sderaadt 			fmtbuf[i] = 0;
8099a2590e5Sderaadt 			numhunk = 0;
81029432cd9Sphessler 			if (hunksize == 0) {
811385a6373Skrw 				log_warnx("%s: no size indicator before A"
812*8457ebc2Skrw 				    " in format string: %s", name, fmt);
8132f18daabSkrw 				goto done;
81429432cd9Sphessler 			}
8159a2590e5Sderaadt 			break;
8169a2590e5Sderaadt 		case 'X':
817c714dadcShenning 			for (k = 0; k < len; k++)
8189a2590e5Sderaadt 				if (!isascii(data[k]) ||
8199a2590e5Sderaadt 				    !isprint(data[k]))
8209a2590e5Sderaadt 					break;
821b54c879eShenning 			if (k == len) {
8229a2590e5Sderaadt 				fmtbuf[i] = 't';
8239a2590e5Sderaadt 				numhunk = -2;
8249a2590e5Sderaadt 			} else {
8259a2590e5Sderaadt 				hunksize++;
8269a2590e5Sderaadt 				comma = ':';
8279a2590e5Sderaadt 				numhunk = 0;
8289a2590e5Sderaadt 			}
8299a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
8309a2590e5Sderaadt 			break;
8319a2590e5Sderaadt 		case 't':
8329a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
8339a2590e5Sderaadt 			numhunk = -2;
8349a2590e5Sderaadt 			break;
8359a2590e5Sderaadt 		case 'I':
8369a2590e5Sderaadt 		case 'l':
8379a2590e5Sderaadt 		case 'L':
8389a2590e5Sderaadt 			hunksize += 4;
8399a2590e5Sderaadt 			break;
8409a2590e5Sderaadt 		case 'S':
8419a2590e5Sderaadt 			hunksize += 2;
8429a2590e5Sderaadt 			break;
8439a2590e5Sderaadt 		case 'B':
8449a2590e5Sderaadt 		case 'f':
8459a2590e5Sderaadt 			hunksize++;
8469a2590e5Sderaadt 			break;
8479a2590e5Sderaadt 		case 'e':
8489a2590e5Sderaadt 			break;
8499a2590e5Sderaadt 		default:
850*8457ebc2Skrw 			log_warnx("%s: garbage in format string: %s", name,
851*8457ebc2Skrw 			    &fmt[i]);
8522f18daabSkrw 			goto done;
8539a2590e5Sderaadt 		}
8549a2590e5Sderaadt 	}
8559a2590e5Sderaadt 
856d22f105fSkrw 	/* Check for too few bytes. */
8579a2590e5Sderaadt 	if (hunksize > len) {
858*8457ebc2Skrw 		log_warnx("%s: expecting at least %d bytes; got %d", name,
859*8457ebc2Skrw 		    hunksize, len);
8602f18daabSkrw 		goto done;
8619a2590e5Sderaadt 	}
862d22f105fSkrw 	/* Check for too many bytes. */
8632f18daabSkrw 	if (numhunk == -1 && hunksize < len) {
864*8457ebc2Skrw 		log_warnx("%s: expecting only %d bytes: got %d", name,
865*8457ebc2Skrw 		    hunksize, len);
8662f18daabSkrw 		goto done;
8672f18daabSkrw 	}
8689a2590e5Sderaadt 
8699a2590e5Sderaadt 	/* If this is an array, compute its size. */
8709a2590e5Sderaadt 	if (!numhunk)
8719a2590e5Sderaadt 		numhunk = len / hunksize;
8729a2590e5Sderaadt 	/* See if we got an exact number of hunks. */
8732f18daabSkrw 	if (numhunk > 0 && numhunk * hunksize != len) {
874*8457ebc2Skrw 		log_warnx("%s: expecting %d bytes: got %d", name,
875*8457ebc2Skrw 		    numhunk * hunksize, len);
8762f18daabSkrw 		goto done;
8772f18daabSkrw 	}
8789a2590e5Sderaadt 
8799a2590e5Sderaadt 	/* A one-hunk array prints the same as a single hunk. */
8809a2590e5Sderaadt 	if (numhunk < 0)
8819a2590e5Sderaadt 		numhunk = 1;
8829a2590e5Sderaadt 
8839a2590e5Sderaadt 	/* Cycle through the array (or hunk) printing the data. */
8849a2590e5Sderaadt 	for (i = 0; i < numhunk; i++) {
8859a2590e5Sderaadt 		for (j = 0; j < numelem; j++) {
8869a2590e5Sderaadt 			switch (fmtbuf[j]) {
8879a2590e5Sderaadt 			case 't':
888bee06f07Skrw 				buf = pretty_print_string(dp, len, emit_punct);
889bee06f07Skrw 				if (buf == NULL)
890bee06f07Skrw 					opcount = -1;
891bee06f07Skrw 				else
892bee06f07Skrw 					opcount = strlcat(op, buf, opleft);
8939a2590e5Sderaadt 				break;
8949a2590e5Sderaadt 			case 'I':
895e95625edSkrw 				memcpy(&foo.s_addr, dp, sizeof(foo.s_addr));
896f3a8c5fdSkrw 				opcount = snprintf(op, opleft, "%s",
897f3a8c5fdSkrw 				    inet_ntoa(foo));
898e95625edSkrw 				dp += sizeof(foo.s_addr);
8999a2590e5Sderaadt 				break;
9009a2590e5Sderaadt 			case 'l':
901bce09e58Skrw 				memcpy(&int32val, dp, sizeof(int32val));
902bce09e58Skrw 				opcount = snprintf(op, opleft, "%d",
903bce09e58Skrw 				    ntohl(int32val));
904bce09e58Skrw 				dp += sizeof(int32val);
9059a2590e5Sderaadt 				break;
9069a2590e5Sderaadt 			case 'L':
907bce09e58Skrw 				memcpy(&uint32val, dp, sizeof(uint32val));
908bce09e58Skrw 				opcount = snprintf(op, opleft, "%u",
909bce09e58Skrw 				    ntohl(uint32val));
910bce09e58Skrw 				dp += sizeof(uint32val);
9119a2590e5Sderaadt 				break;
9129a2590e5Sderaadt 			case 'S':
913bce09e58Skrw 				memcpy(&uint16val, dp, sizeof(uint16val));
914bce09e58Skrw 				opcount = snprintf(op, opleft, "%hu",
915bce09e58Skrw 				    ntohs(uint16val));
916bce09e58Skrw 				dp += sizeof(uint16val);
9179a2590e5Sderaadt 				break;
9189a2590e5Sderaadt 			case 'B':
919221bd6c0Skrw 				opcount = snprintf(op, opleft, "%u", *dp);
920de3ca9dbSkrw 				dp++;
9219a2590e5Sderaadt 				break;
922920d03efSkrw 			case 'X':
923de3ca9dbSkrw 				opcount = snprintf(op, opleft, "%x", *dp);
924de3ca9dbSkrw 				dp++;
9259a2590e5Sderaadt 				break;
9269a2590e5Sderaadt 			case 'f':
927f3a8c5fdSkrw 				opcount = snprintf(op, opleft, "%s",
928f3a8c5fdSkrw 				    *dp ? "true" : "false");
929de3ca9dbSkrw 				dp++;
9309a2590e5Sderaadt 				break;
9319a2590e5Sderaadt 			default:
932833082e5Skrw 				log_warnx("Unexpected format code %c",
933833082e5Skrw 				    fmtbuf[j]);
9349a2590e5Sderaadt 				goto toobig;
935f3a8c5fdSkrw 			}
936f3a8c5fdSkrw 			if (opcount >= opleft || opcount == -1)
937f3a8c5fdSkrw 				goto toobig;
938f3a8c5fdSkrw 			opleft -= opcount;
939f3a8c5fdSkrw 			op += opcount;
9409a2590e5Sderaadt 			if (j + 1 < numelem && comma != ':') {
941f3a8c5fdSkrw 				opcount = snprintf(op, opleft, " ");
942f3a8c5fdSkrw 				if (opcount >= opleft || opcount == -1)
943f3a8c5fdSkrw 					goto toobig;
944f3a8c5fdSkrw 				opleft -= opcount;
945f3a8c5fdSkrw 				op += opcount;
9469a2590e5Sderaadt 			}
9479a2590e5Sderaadt 		}
9489a2590e5Sderaadt 		if (i + 1 < numhunk) {
949f3a8c5fdSkrw 			opcount = snprintf(op, opleft, "%c", comma);
950f3a8c5fdSkrw 			if (opcount >= opleft || opcount == -1)
9519a2590e5Sderaadt 				goto toobig;
952f3a8c5fdSkrw 			opleft -= opcount;
953f3a8c5fdSkrw 			op += opcount;
954f3a8c5fdSkrw 		}
9559a2590e5Sderaadt 	}
9562f18daabSkrw 
9572f18daabSkrw done:
958c714dadcShenning 	return (optbuf);
9592f18daabSkrw 
9609a2590e5Sderaadt toobig:
9612f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
9622f18daabSkrw 	return (optbuf);
9639a2590e5Sderaadt }
9649a2590e5Sderaadt 
965b414edd1Skrw struct option_data *
966b414edd1Skrw unpack_options(struct dhcp_packet *packet)
9679a2590e5Sderaadt {
968cb26da20Skrw 	static struct option_data options[DHO_COUNT];
969b414edd1Skrw 	int i;
9709a2590e5Sderaadt 
971cb26da20Skrw 	for (i = 0; i < DHO_COUNT; i++) {
972b414edd1Skrw 		free(options[i].data);
973b414edd1Skrw 		options[i].data = NULL;
974b414edd1Skrw 		options[i].len = 0;
975b414edd1Skrw 	}
97602e02bd5Skrw 
97702e02bd5Skrw 	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
97802e02bd5Skrw 		/* Parse the BOOTP/DHCP options field. */
979b414edd1Skrw 		parse_option_buffer(options, &packet->options[4],
980b414edd1Skrw 		    sizeof(packet->options) - 4);
98102e02bd5Skrw 
982b414edd1Skrw 		/* DHCP packets can also use overload areas for options. */
983b414edd1Skrw 		if (options[DHO_DHCP_MESSAGE_TYPE].data &&
98402e02bd5Skrw 		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
98502e02bd5Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
986b414edd1Skrw 				parse_option_buffer(options,
98702e02bd5Skrw 				    (unsigned char *)packet->file,
98802e02bd5Skrw 				    sizeof(packet->file));
989b414edd1Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
990b414edd1Skrw 				parse_option_buffer(options,
99102e02bd5Skrw 				    (unsigned char *)packet->sname,
99202e02bd5Skrw 				    sizeof(packet->sname));
99302e02bd5Skrw 		}
99402e02bd5Skrw 	}
99502e02bd5Skrw 
996b414edd1Skrw 	return options;
9979a2590e5Sderaadt }
998