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''.
43 #define DHCP_OPTION_DATA
47 int bad_options_max
= 5;
49 void parse_options(struct packet
*);
50 void parse_option_buffer(struct packet
*, unsigned char *, int);
51 int store_options(unsigned char *, int, struct tree_cache
**,
52 unsigned char *, int, int, int, int);
56 * Parse all available options out of the specified packet.
59 parse_options(struct packet
*packet
)
61 /* Initially, zero all option pointers. */
62 memset(packet
->options
, 0, sizeof(packet
->options
));
64 /* If we don't see the magic cookie, there's nothing to parse. */
65 if (memcmp(packet
->raw
->options
, DHCP_OPTIONS_COOKIE
, 4)) {
66 packet
->options_valid
= 0;
71 * Go through the options field, up to the end of the packet or
74 parse_option_buffer(packet
, &packet
->raw
->options
[4],
75 packet
->packet_length
- DHCP_FIXED_NON_UDP
- 4);
78 * If we parsed a DHCP Option Overload option, parse more
79 * options out of the buffer(s) containing them.
81 if (packet
->options_valid
&&
82 packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
) {
83 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 1)
84 parse_option_buffer(packet
,
85 (unsigned char *)packet
->raw
->file
,
86 sizeof(packet
->raw
->file
));
87 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 2)
88 parse_option_buffer(packet
,
89 (unsigned char *)packet
->raw
->sname
,
90 sizeof(packet
->raw
->sname
));
95 * Parse options out of the specified buffer, storing addresses of
96 * option values in packet->options and setting packet->options_valid if
97 * no errors are encountered.
100 parse_option_buffer(struct packet
*packet
,
101 unsigned char *buffer
, int length
)
103 unsigned char *s
, *t
, *end
= buffer
+ length
;
106 for (s
= buffer
; *s
!= DHO_END
&& s
< end
; ) {
109 /* Pad options don't have a length - just skip them. */
110 if (code
== DHO_PAD
) {
120 * All other fields (except end, see above) have a
126 * If the length is outrageous, silently skip the rest,
127 * and mark the packet bad. Unfortunately some crappy
128 * dhcp servers always seem to give us garbage on the
129 * end of a packet. so rather than keep refusing, give
130 * up and try to take one after seeing a few without
133 if (s
+ len
+ 2 > end
) {
136 warning("option %s (%d) %s.",
137 dhcp_options
[code
].name
, len
,
138 "larger than buffer");
139 if (bad_options
== bad_options_max
) {
140 packet
->options_valid
= 1;
142 warning("Many bogus options seen in offers. "
143 "Taking this offer in spite of bogus "
144 "options - hope for the best!");
146 warning("rejecting bogus offer.");
147 packet
->options_valid
= 0;
152 * If we haven't seen this option before, just make
153 * space for it and copy it there.
155 if (!packet
->options
[code
].data
) {
156 if (!(t
= calloc(1, len
+ 1)))
157 error("Can't allocate storage for option %s.",
158 dhcp_options
[code
].name
);
160 * Copy and NUL-terminate the option (in case
161 * it's an ASCII string.
163 memcpy(t
, &s
[2], len
);
165 packet
->options
[code
].len
= len
;
166 packet
->options
[code
].data
= t
;
169 * If it's a repeat, concatenate it to whatever
170 * we last saw. This is really only required
171 * for clients, but what the heck...
173 t
= calloc(1, len
+ packet
->options
[code
].len
+ 1);
175 error("Can't expand storage for option %s.",
176 dhcp_options
[code
].name
);
179 memcpy(t
, packet
->options
[code
].data
,
180 packet
->options
[code
].len
);
181 memcpy(t
+ packet
->options
[code
].len
,
183 packet
->options
[code
].len
+= len
;
184 t
[packet
->options
[code
].len
] = 0;
185 free(packet
->options
[code
].data
);
186 packet
->options
[code
].data
= t
;
190 packet
->options_valid
= 1;
194 * cons options into a big buffer, and then split them out into the
195 * three separate buffers if needed. This allows us to cons up a set of
196 * vendor options using the same routine.
199 cons_options(struct packet
*inpacket
, struct dhcp_packet
*outpacket
,
200 int mms
, struct tree_cache
**options
,
201 int overload
, /* Overload flags that may be set. */
202 int terminate
, int bootpp
, u_int8_t
*prl
, int prl_len
)
204 unsigned char priority_list
[300], buffer
[4096];
205 int priority_len
, main_buffer_size
, mainbufix
, bufix
;
206 int option_size
, length
;
209 * If the client has provided a maximum DHCP message size, use
210 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
211 * up to the minimum IP MTU size (576 bytes).
213 * XXX if a BOOTP client specifies a max message size, we will
218 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
&&
219 (inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].len
>=
222 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
);
225 main_buffer_size
= mms
- DHCP_FIXED_LEN
;
227 main_buffer_size
= 64;
229 main_buffer_size
= 576 - DHCP_FIXED_LEN
;
231 if (main_buffer_size
> sizeof(buffer
))
232 main_buffer_size
= sizeof(buffer
);
234 /* Preload the option priority list with mandatory options. */
236 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE_TYPE
;
237 priority_list
[priority_len
++] = DHO_DHCP_SERVER_IDENTIFIER
;
238 priority_list
[priority_len
++] = DHO_DHCP_LEASE_TIME
;
239 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE
;
242 * If the client has provided a list of options that it wishes
243 * returned, use it to prioritize. Otherwise, prioritize based
244 * on the default priority list.
247 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
) {
249 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].len
;
250 if (prlen
+ priority_len
> sizeof(priority_list
))
251 prlen
= sizeof(priority_list
) - priority_len
;
253 memcpy(&priority_list
[priority_len
],
254 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
,
256 priority_len
+= prlen
;
259 if (prl_len
+ priority_len
> sizeof(priority_list
))
260 prl_len
= sizeof(priority_list
) - priority_len
;
262 memcpy(&priority_list
[priority_len
], prl
, prl_len
);
263 priority_len
+= prl_len
;
266 memcpy(&priority_list
[priority_len
],
267 dhcp_option_default_priority_list
,
268 sizeof_dhcp_option_default_priority_list
);
269 priority_len
+= sizeof_dhcp_option_default_priority_list
;
272 /* Copy the options into the big buffer... */
273 option_size
= store_options(
275 (main_buffer_size
- 7 + ((overload
& 1) ? DHCP_FILE_LEN
: 0) +
276 ((overload
& 2) ? DHCP_SNAME_LEN
: 0)),
277 options
, priority_list
, priority_len
, main_buffer_size
,
278 (main_buffer_size
+ ((overload
& 1) ? DHCP_FILE_LEN
: 0)),
281 /* Put the cookie up front... */
282 memcpy(outpacket
->options
, DHCP_OPTIONS_COOKIE
, 4);
286 * If we're going to have to overload, store the overload option
287 * at the beginning. If we can, though, just store the whole
288 * thing in the packet's option buffer and leave it at that.
290 if (option_size
<= main_buffer_size
- mainbufix
) {
291 memcpy(&outpacket
->options
[mainbufix
],
292 buffer
, option_size
);
293 mainbufix
+= option_size
;
294 if (mainbufix
< main_buffer_size
)
295 outpacket
->options
[mainbufix
++] = DHO_END
;
296 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
298 outpacket
->options
[mainbufix
++] = DHO_DHCP_OPTION_OVERLOAD
;
299 outpacket
->options
[mainbufix
++] = 1;
301 main_buffer_size
- mainbufix
+ DHCP_FILE_LEN
)
302 outpacket
->options
[mainbufix
++] = 3;
304 outpacket
->options
[mainbufix
++] = 1;
306 memcpy(&outpacket
->options
[mainbufix
],
307 buffer
, main_buffer_size
- mainbufix
);
308 bufix
= main_buffer_size
- mainbufix
;
309 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
311 if (option_size
- bufix
<= DHCP_FILE_LEN
) {
312 memcpy(outpacket
->file
,
313 &buffer
[bufix
], option_size
- bufix
);
314 mainbufix
= option_size
- bufix
;
315 if (mainbufix
< DHCP_FILE_LEN
)
316 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
317 while (mainbufix
< DHCP_FILE_LEN
)
318 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
320 memcpy(outpacket
->file
,
321 &buffer
[bufix
], DHCP_FILE_LEN
);
322 bufix
+= DHCP_FILE_LEN
;
325 if ((overload
& 2) && option_size
< bufix
) {
326 memcpy(outpacket
->sname
,
327 &buffer
[bufix
], option_size
- bufix
);
329 mainbufix
= option_size
- bufix
;
330 if (mainbufix
< DHCP_SNAME_LEN
)
331 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
332 while (mainbufix
< DHCP_SNAME_LEN
)
333 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
340 * Store all the requested options into the requested buffer.
343 store_options(unsigned char *buffer
, int buflen
, struct tree_cache
**options
,
344 unsigned char *priority_list
, int priority_len
, int first_cutoff
,
345 int second_cutoff
, int terminate
)
347 int bufix
= 0, option_stored
[256], i
, ix
, tto
;
349 /* Zero out the stored-lengths array. */
350 memset(option_stored
, 0, sizeof(option_stored
));
353 * Copy out the options in the order that they appear in the
356 for (i
= 0; i
< priority_len
; i
++) {
357 /* Code for next option to try to store. */
358 int code
= priority_list
[i
];
362 * Number of bytes left to store (some may already have
363 * been stored by a previous pass).
367 /* If no data is available for this option, skip it. */
368 if (!options
[code
]) {
373 * The client could ask for things that are mandatory,
374 * in which case we should avoid storing them twice...
376 if (option_stored
[code
])
378 option_stored
[code
] = 1;
380 /* We should now have a constant length for the option. */
381 length
= options
[code
]->len
;
383 /* Do we add a NUL? */
384 if (terminate
&& dhcp_options
[code
].format
[0] == 't') {
390 /* Try to store the option. */
393 * If the option's length is more than 255, we must
394 * store it in multiple hunks. Store 255-byte hunks
395 * first. However, in any case, if the option data will
396 * cross a buffer boundary, split it across that
403 unsigned char incr
= length
> 255 ? 255 : length
;
406 * If this hunk of the buffer will cross a
407 * boundary, only go up to the boundary in this
410 if (bufix
< first_cutoff
&&
411 bufix
+ incr
> first_cutoff
)
412 incr
= first_cutoff
- bufix
;
413 else if (bufix
< second_cutoff
&&
414 bufix
+ incr
> second_cutoff
)
415 incr
= second_cutoff
- bufix
;
418 * If this option is going to overflow the
421 if (bufix
+ 2 + incr
> buflen
) {
426 /* Everything looks good - copy it in! */
427 buffer
[bufix
] = code
;
428 buffer
[bufix
+ 1] = incr
;
429 if (tto
&& incr
== length
) {
430 memcpy(buffer
+ bufix
+ 2,
431 options
[code
]->value
+ ix
, incr
- 1);
432 buffer
[bufix
+ 2 + incr
- 1] = 0;
434 memcpy(buffer
+ bufix
+ 2,
435 options
[code
]->value
+ ix
, incr
);
445 * Format the specified option so that a human can easily read it.
448 pretty_print_option(unsigned int code
, unsigned char *data
, int len
,
449 int emit_commas
, int emit_quotes
)
451 static char optbuf
[32768]; /* XXX */
452 int hunksize
= 0, numhunk
= -1, numelem
= 0;
453 char fmtbuf
[32], *op
= optbuf
;
454 int i
, j
, k
, opleft
= sizeof(optbuf
);
455 unsigned char *dp
= data
;
459 /* Code should be between 0 and 255. */
461 error("pretty_print_option: bad code %d", code
);
468 /* Figure out the size of the data. */
469 for (i
= 0; dhcp_options
[code
].format
[i
]; i
++) {
471 warning("%s: Excess information in format string: %s",
472 dhcp_options
[code
].name
,
473 &(dhcp_options
[code
].format
[i
]));
477 fmtbuf
[i
] = dhcp_options
[code
].format
[i
];
478 switch (dhcp_options
[code
].format
[i
]) {
485 for (k
= 0; k
< len
; k
++)
486 if (!isascii(data
[k
]) ||
522 warning("%s: garbage in format string: %s",
523 dhcp_options
[code
].name
,
524 &(dhcp_options
[code
].format
[i
]));
529 /* Check for too few bytes... */
530 if (hunksize
> len
) {
531 warning("%s: expecting at least %d bytes; got %d",
532 dhcp_options
[code
].name
, hunksize
, len
);
535 /* Check for too many bytes... */
536 if (numhunk
== -1 && hunksize
< len
)
537 warning("%s: %d extra bytes",
538 dhcp_options
[code
].name
, len
- hunksize
);
540 /* If this is an array, compute its size. */
542 numhunk
= len
/ hunksize
;
543 /* See if we got an exact number of hunks. */
544 if (numhunk
> 0 && numhunk
* hunksize
< len
)
545 warning("%s: %d extra bytes at end of array",
546 dhcp_options
[code
].name
, len
- numhunk
* hunksize
);
548 /* A one-hunk array prints the same as a single hunk. */
552 /* Cycle through the array (or hunk) printing the data. */
553 for (i
= 0; i
< numhunk
; i
++) {
554 for (j
= 0; j
< numelem
; j
++) {
562 for (; dp
< data
+ len
; dp
++) {
565 if (dp
+ 1 != data
+ len
||
567 _snprintf(op
, opleft
,
572 } else if (*dp
== '"' ||
593 foo
.s_addr
= htonl(getULong(dp
));
594 strncpy(op
, inet_ntoa(foo
), opleft
- 1);
595 op
[opleft
- 1] = ANSI_NULL
;
596 opcount
= strlen(op
);
597 if (opcount
>= opleft
)
603 opcount
= _snprintf(op
, opleft
, "%ld",
605 if (opcount
>= opleft
|| opcount
== -1)
611 opcount
= _snprintf(op
, opleft
, "%ld",
612 (unsigned long)getULong(dp
));
613 if (opcount
>= opleft
|| opcount
== -1)
619 opcount
= _snprintf(op
, opleft
, "%d",
621 if (opcount
>= opleft
|| opcount
== -1)
627 opcount
= _snprintf(op
, opleft
, "%d",
629 if (opcount
>= opleft
|| opcount
== -1)
635 opcount
= _snprintf(op
, opleft
, "%d",
637 if (opcount
>= opleft
|| opcount
== -1)
642 opcount
= _snprintf(op
, opleft
, "%d", *dp
++);
643 if (opcount
>= opleft
|| opcount
== -1)
648 opcount
= _snprintf(op
, opleft
, "%x", *dp
++);
649 if (opcount
>= opleft
|| opcount
== -1)
654 opcount
= (size_t) strncpy(op
, *dp
++ ? "true" : "false", opleft
- 1);
655 op
[opleft
- 1] = ANSI_NULL
;
656 if (opcount
>= opleft
)
661 warning("Unexpected format code %c", fmtbuf
[j
]);
664 opleft
-= strlen(op
);
667 if (j
+ 1 < numelem
&& comma
!= ':') {
672 if (i
+ 1 < numhunk
) {
682 warning("dhcp option too large");
687 do_packet(struct interface_info
*interface
, struct dhcp_packet
*packet
,
688 int len
, unsigned int from_port
, struct iaddr from
, struct hardware
*hfrom
)
693 if (packet
->hlen
> sizeof(packet
->chaddr
)) {
694 note("Discarding packet with invalid hlen.");
698 memset(&tp
, 0, sizeof(tp
));
700 tp
.packet_length
= len
;
701 tp
.client_port
= from_port
;
702 tp
.client_addr
= from
;
703 tp
.interface
= interface
;
707 if (tp
.options_valid
&&
708 tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
)
709 tp
.packet_type
= tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
[0];
715 /* Free the data associated with the options. */
716 for (i
= 0; i
< 256; i
++)
717 if (tp
.options
[i
].len
&& tp
.options
[i
].data
)
718 free(tp
.options
[i
].data
);