1 /* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */
3 /* DHCP options parsing and reassembly. */
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
45 #define DHCP_OPTION_DATA
50 int bad_options_max
= 5;
52 void parse_options(struct packet
*);
53 void parse_option_buffer(struct packet
*, unsigned char *, int);
54 int store_options(unsigned char *, int, struct tree_cache
**,
55 unsigned char *, int, int, int, int);
59 * Parse all available options out of the specified packet.
62 parse_options(struct packet
*packet
)
64 /* Initially, zero all option pointers. */
65 memset(packet
->options
, 0, sizeof(packet
->options
));
67 /* If we don't see the magic cookie, there's nothing to parse. */
68 if (memcmp(packet
->raw
->options
, DHCP_OPTIONS_COOKIE
, 4)) {
69 packet
->options_valid
= 0;
74 * Go through the options field, up to the end of the packet or
77 parse_option_buffer(packet
, &packet
->raw
->options
[4],
78 packet
->packet_length
- DHCP_FIXED_NON_UDP
- 4);
81 * If we parsed a DHCP Option Overload option, parse more
82 * options out of the buffer(s) containing them.
84 if (packet
->options_valid
&&
85 packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
) {
86 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 1)
87 parse_option_buffer(packet
,
88 (unsigned char *)packet
->raw
->file
,
89 sizeof(packet
->raw
->file
));
90 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 2)
91 parse_option_buffer(packet
,
92 (unsigned char *)packet
->raw
->sname
,
93 sizeof(packet
->raw
->sname
));
98 * Parse options out of the specified buffer, storing addresses of
99 * option values in packet->options and setting packet->options_valid if
100 * no errors are encountered.
103 parse_option_buffer(struct packet
*packet
,
104 unsigned char *buffer
, int length
)
106 unsigned char *s
, *t
, *end
= buffer
+ length
;
109 for (s
= buffer
; *s
!= DHO_END
&& s
< end
; ) {
112 /* Pad options don't have a length - just skip them. */
113 if (code
== DHO_PAD
) {
123 * All other fields (except end, see above) have a
129 * If the length is outrageous, silently skip the rest,
130 * and mark the packet bad. Unfortunately some crappy
131 * dhcp servers always seem to give us garbage on the
132 * end of a packet. so rather than keep refusing, give
133 * up and try to take one after seeing a few without
136 if (s
+ len
+ 2 > end
) {
139 warning("option %s (%d) %s.",
140 dhcp_options
[code
].name
, len
,
141 "larger than buffer");
142 if (bad_options
== bad_options_max
) {
143 packet
->options_valid
= 1;
145 warning("Many bogus options seen in offers. "
146 "Taking this offer in spite of bogus "
147 "options - hope for the best!");
149 warning("rejecting bogus offer.");
150 packet
->options_valid
= 0;
155 * If we haven't seen this option before, just make
156 * space for it and copy it there.
158 if (!packet
->options
[code
].data
) {
159 if (!(t
= calloc(1, len
+ 1)))
160 error("Can't allocate storage for option %s.",
161 dhcp_options
[code
].name
);
163 * Copy and NUL-terminate the option (in case
164 * it's an ASCII string.
166 memcpy(t
, &s
[2], len
);
168 packet
->options
[code
].len
= len
;
169 packet
->options
[code
].data
= t
;
172 * If it's a repeat, concatenate it to whatever
173 * we last saw. This is really only required
174 * for clients, but what the heck...
176 t
= calloc(1, len
+ packet
->options
[code
].len
+ 1);
178 error("Can't expand storage for option %s.",
179 dhcp_options
[code
].name
);
180 memcpy(t
, packet
->options
[code
].data
,
181 packet
->options
[code
].len
);
182 memcpy(t
+ packet
->options
[code
].len
,
184 packet
->options
[code
].len
+= len
;
185 t
[packet
->options
[code
].len
] = 0;
186 free(packet
->options
[code
].data
);
187 packet
->options
[code
].data
= t
;
191 packet
->options_valid
= 1;
195 * cons options into a big buffer, and then split them out into the
196 * three separate buffers if needed. This allows us to cons up a set of
197 * vendor options using the same routine.
200 cons_options(struct packet
*inpacket
, struct dhcp_packet
*outpacket
,
201 int mms
, struct tree_cache
**options
,
202 int overload
, /* Overload flags that may be set. */
203 int terminate
, int bootpp
, u_int8_t
*prl
, int prl_len
)
205 unsigned char priority_list
[300], buffer
[4096];
206 int priority_len
, main_buffer_size
, mainbufix
, bufix
;
207 int option_size
, length
;
210 * If the client has provided a maximum DHCP message size, use
211 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
212 * up to the minimum IP MTU size (576 bytes).
214 * XXX if a BOOTP client specifies a max message size, we will
219 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
&&
220 (inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].len
>=
223 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
);
226 main_buffer_size
= mms
- DHCP_FIXED_LEN
;
228 main_buffer_size
= 64;
230 main_buffer_size
= 576 - DHCP_FIXED_LEN
;
232 if (main_buffer_size
> sizeof(buffer
))
233 main_buffer_size
= sizeof(buffer
);
235 /* Preload the option priority list with mandatory options. */
237 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE_TYPE
;
238 priority_list
[priority_len
++] = DHO_DHCP_SERVER_IDENTIFIER
;
239 priority_list
[priority_len
++] = DHO_DHCP_LEASE_TIME
;
240 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE
;
243 * If the client has provided a list of options that it wishes
244 * returned, use it to prioritize. Otherwise, prioritize based
245 * on the default priority list.
248 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
) {
250 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].len
;
251 if (prlen
+ priority_len
> sizeof(priority_list
))
252 prlen
= sizeof(priority_list
) - priority_len
;
254 memcpy(&priority_list
[priority_len
],
255 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
,
257 priority_len
+= prlen
;
260 if (prl_len
+ priority_len
> sizeof(priority_list
))
261 prl_len
= sizeof(priority_list
) - priority_len
;
263 memcpy(&priority_list
[priority_len
], prl
, prl_len
);
264 priority_len
+= prl_len
;
267 memcpy(&priority_list
[priority_len
],
268 dhcp_option_default_priority_list
,
269 sizeof_dhcp_option_default_priority_list
);
270 priority_len
+= sizeof_dhcp_option_default_priority_list
;
273 /* Copy the options into the big buffer... */
274 option_size
= store_options(
276 (main_buffer_size
- 7 + ((overload
& 1) ? DHCP_FILE_LEN
: 0) +
277 ((overload
& 2) ? DHCP_SNAME_LEN
: 0)),
278 options
, priority_list
, priority_len
, main_buffer_size
,
279 (main_buffer_size
+ ((overload
& 1) ? DHCP_FILE_LEN
: 0)),
282 /* Put the cookie up front... */
283 memcpy(outpacket
->options
, DHCP_OPTIONS_COOKIE
, 4);
287 * If we're going to have to overload, store the overload option
288 * at the beginning. If we can, though, just store the whole
289 * thing in the packet's option buffer and leave it at that.
291 if (option_size
<= main_buffer_size
- mainbufix
) {
292 memcpy(&outpacket
->options
[mainbufix
],
293 buffer
, option_size
);
294 mainbufix
+= option_size
;
295 if (mainbufix
< main_buffer_size
)
296 outpacket
->options
[mainbufix
++] = DHO_END
;
297 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
299 outpacket
->options
[mainbufix
++] = DHO_DHCP_OPTION_OVERLOAD
;
300 outpacket
->options
[mainbufix
++] = 1;
302 main_buffer_size
- mainbufix
+ DHCP_FILE_LEN
)
303 outpacket
->options
[mainbufix
++] = 3;
305 outpacket
->options
[mainbufix
++] = 1;
307 memcpy(&outpacket
->options
[mainbufix
],
308 buffer
, main_buffer_size
- mainbufix
);
309 bufix
= main_buffer_size
- mainbufix
;
310 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
312 if (option_size
- bufix
<= DHCP_FILE_LEN
) {
313 memcpy(outpacket
->file
,
314 &buffer
[bufix
], option_size
- bufix
);
315 mainbufix
= option_size
- bufix
;
316 if (mainbufix
< DHCP_FILE_LEN
)
317 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
318 while (mainbufix
< DHCP_FILE_LEN
)
319 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
321 memcpy(outpacket
->file
,
322 &buffer
[bufix
], DHCP_FILE_LEN
);
323 bufix
+= DHCP_FILE_LEN
;
326 if ((overload
& 2) && option_size
< bufix
) {
327 memcpy(outpacket
->sname
,
328 &buffer
[bufix
], option_size
- bufix
);
330 mainbufix
= option_size
- bufix
;
331 if (mainbufix
< DHCP_SNAME_LEN
)
332 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
333 while (mainbufix
< DHCP_SNAME_LEN
)
334 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
341 * Store all the requested options into the requested buffer.
344 store_options(unsigned char *buffer
, int buflen
, struct tree_cache
**options
,
345 unsigned char *priority_list
, int priority_len
, int first_cutoff
,
346 int second_cutoff
, int terminate
)
348 int bufix
= 0, option_stored
[256], i
, ix
, tto
;
350 /* Zero out the stored-lengths array. */
351 memset(option_stored
, 0, sizeof(option_stored
));
354 * Copy out the options in the order that they appear in the
357 for (i
= 0; i
< priority_len
; i
++) {
358 /* Code for next option to try to store. */
359 int code
= priority_list
[i
];
363 * Number of bytes left to store (some may already have
364 * been stored by a previous pass).
368 /* If no data is available for this option, skip it. */
369 if (!options
[code
]) {
374 * The client could ask for things that are mandatory,
375 * in which case we should avoid storing them twice...
377 if (option_stored
[code
])
379 option_stored
[code
] = 1;
381 /* We should now have a constant length for the option. */
382 length
= options
[code
]->len
;
384 /* Do we add a NUL? */
385 if (terminate
&& dhcp_options
[code
].format
[0] == 't') {
391 /* Try to store the option. */
394 * If the option's length is more than 255, we must
395 * store it in multiple hunks. Store 255-byte hunks
396 * first. However, in any case, if the option data will
397 * cross a buffer boundary, split it across that
404 unsigned char incr
= length
> 255 ? 255 : length
;
407 * If this hunk of the buffer will cross a
408 * boundary, only go up to the boundary in this
411 if (bufix
< first_cutoff
&&
412 bufix
+ incr
> first_cutoff
)
413 incr
= first_cutoff
- bufix
;
414 else if (bufix
< second_cutoff
&&
415 bufix
+ incr
> second_cutoff
)
416 incr
= second_cutoff
- bufix
;
419 * If this option is going to overflow the
422 if (bufix
+ 2 + incr
> buflen
) {
427 /* Everything looks good - copy it in! */
428 buffer
[bufix
] = code
;
429 buffer
[bufix
+ 1] = incr
;
430 if (tto
&& incr
== length
) {
431 memcpy(buffer
+ bufix
+ 2,
432 options
[code
]->value
+ ix
, incr
- 1);
433 buffer
[bufix
+ 2 + incr
- 1] = 0;
435 memcpy(buffer
+ bufix
+ 2,
436 options
[code
]->value
+ ix
, incr
);
446 * Format the specified option so that a human can easily read it.
449 pretty_print_option(unsigned int code
, unsigned char *data
, int len
,
450 int emit_commas
, int emit_quotes
)
452 static char optbuf
[32768]; /* XXX */
453 int hunksize
= 0, numhunk
= -1, numelem
= 0;
454 char fmtbuf
[32], *op
= optbuf
;
455 int i
, j
, k
, opleft
= sizeof(optbuf
);
456 unsigned char *dp
= data
;
460 /* Code should be between 0 and 255. */
462 error("pretty_print_option: bad code %d", code
);
469 /* Figure out the size of the data. */
470 for (i
= 0; dhcp_options
[code
].format
[i
]; i
++) {
472 warning("%s: Excess information in format string: %s",
473 dhcp_options
[code
].name
,
474 &(dhcp_options
[code
].format
[i
]));
478 fmtbuf
[i
] = dhcp_options
[code
].format
[i
];
479 switch (dhcp_options
[code
].format
[i
]) {
486 for (k
= 0; k
< len
; k
++)
487 if (!isascii(data
[k
]) ||
523 warning("%s: garbage in format string: %s",
524 dhcp_options
[code
].name
,
525 &(dhcp_options
[code
].format
[i
]));
530 /* Check for too few bytes... */
531 if (hunksize
> len
) {
532 warning("%s: expecting at least %d bytes; got %d",
533 dhcp_options
[code
].name
, hunksize
, len
);
536 /* Check for too many bytes... */
537 if (numhunk
== -1 && hunksize
< len
)
538 warning("%s: %d extra bytes",
539 dhcp_options
[code
].name
, len
- hunksize
);
541 /* If this is an array, compute its size. */
543 numhunk
= len
/ hunksize
;
544 /* See if we got an exact number of hunks. */
545 if (numhunk
> 0 && numhunk
* hunksize
< len
)
546 warning("%s: %d extra bytes at end of array",
547 dhcp_options
[code
].name
, len
- numhunk
* hunksize
);
549 /* A one-hunk array prints the same as a single hunk. */
553 /* Cycle through the array (or hunk) printing the data. */
554 for (i
= 0; i
< numhunk
; i
++) {
555 for (j
= 0; j
< numelem
; j
++) {
563 for (; dp
< data
+ len
; dp
++) {
566 if (dp
+ 1 != data
+ len
||
573 } else if (*dp
== '"' ||
594 foo
.s_addr
= htonl(getULong(dp
));
595 opcount
= strlcpy(op
, inet_ntoa(foo
), opleft
);
596 if (opcount
>= opleft
)
602 opcount
= snprintf(op
, opleft
, "%ld",
604 if (opcount
>= opleft
|| opcount
== -1)
610 opcount
= snprintf(op
, opleft
, "%ld",
611 (unsigned long)getULong(dp
));
612 if (opcount
>= opleft
|| opcount
== -1)
618 opcount
= snprintf(op
, opleft
, "%d",
620 if (opcount
>= opleft
|| opcount
== -1)
626 opcount
= snprintf(op
, opleft
, "%d",
628 if (opcount
>= opleft
|| opcount
== -1)
634 opcount
= snprintf(op
, opleft
, "%d",
636 if (opcount
>= opleft
|| opcount
== -1)
641 opcount
= snprintf(op
, opleft
, "%d", *dp
++);
642 if (opcount
>= opleft
|| opcount
== -1)
647 opcount
= snprintf(op
, opleft
, "%x", *dp
++);
648 if (opcount
>= opleft
|| opcount
== -1)
653 opcount
= strlcpy(op
,
654 *dp
++ ? "true" : "false", opleft
);
655 if (opcount
>= opleft
)
660 warning("Unexpected format code %c", fmtbuf
[j
]);
663 opleft
-= strlen(op
);
666 if (j
+ 1 < numelem
&& comma
!= ':') {
671 if (i
+ 1 < numhunk
) {
681 warning("dhcp option too large");
686 do_packet(struct interface_info
*interface
, struct dhcp_packet
*packet
,
687 int len
, unsigned int from_port
, struct iaddr from
, struct hardware
*hfrom
)
692 if (packet
->hlen
> sizeof(packet
->chaddr
)) {
693 note("Discarding packet with invalid hlen.");
697 memset(&tp
, 0, sizeof(tp
));
699 tp
.packet_length
= len
;
700 tp
.client_port
= from_port
;
701 tp
.client_addr
= from
;
702 tp
.interface
= interface
;
706 if (tp
.options_valid
&&
707 tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
)
708 tp
.packet_type
= tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
[0];
714 /* Free the data associated with the options. */
715 for (i
= 0; i
< 256; i
++)
716 if (tp
.options
[i
].len
&& tp
.options
[i
].data
)
717 free(tp
.options
[i
].data
);