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''.
46 #define DHCP_OPTION_DATA
51 int bad_options_max
= 5;
53 void parse_options(struct packet
*);
54 void parse_option_buffer(struct packet
*, unsigned char *, int);
55 int store_options(unsigned char *, int, struct tree_cache
**,
56 unsigned char *, int, int, int, int);
60 * Parse all available options out of the specified packet.
63 parse_options(struct packet
*packet
)
65 /* Initially, zero all option pointers. */
66 memset(packet
->options
, 0, sizeof(packet
->options
));
68 /* If we don't see the magic cookie, there's nothing to parse. */
69 if (memcmp(packet
->raw
->options
, DHCP_OPTIONS_COOKIE
, 4)) {
70 packet
->options_valid
= 0;
75 * Go through the options field, up to the end of the packet or
78 parse_option_buffer(packet
, &packet
->raw
->options
[4],
79 packet
->packet_length
- DHCP_FIXED_NON_UDP
- 4);
82 * If we parsed a DHCP Option Overload option, parse more
83 * options out of the buffer(s) containing them.
85 if (packet
->options_valid
&&
86 packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
) {
87 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 1)
88 parse_option_buffer(packet
,
89 (unsigned char *)packet
->raw
->file
,
90 sizeof(packet
->raw
->file
));
91 if (packet
->options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 2)
92 parse_option_buffer(packet
,
93 (unsigned char *)packet
->raw
->sname
,
94 sizeof(packet
->raw
->sname
));
99 * Parse options out of the specified buffer, storing addresses of
100 * option values in packet->options and setting packet->options_valid if
101 * no errors are encountered.
104 parse_option_buffer(struct packet
*packet
,
105 unsigned char *buffer
, int length
)
107 unsigned char *s
, *t
, *end
= buffer
+ length
;
110 for (s
= buffer
; *s
!= DHO_END
&& s
< end
; ) {
113 /* Pad options don't have a length - just skip them. */
114 if (code
== DHO_PAD
) {
124 * All other fields (except end, see above) have a
130 * If the length is outrageous, silently skip the rest,
131 * and mark the packet bad. Unfortunately some crappy
132 * dhcp servers always seem to give us garbage on the
133 * end of a packet. so rather than keep refusing, give
134 * up and try to take one after seeing a few without
137 if (s
+ len
+ 2 > end
) {
140 warning("option %s (%d) %s.",
141 dhcp_options
[code
].name
, len
,
142 "larger than buffer");
143 if (bad_options
== bad_options_max
) {
144 packet
->options_valid
= 1;
146 warning("Many bogus options seen in offers. "
147 "Taking this offer in spite of bogus "
148 "options - hope for the best!");
150 warning("rejecting bogus offer.");
151 packet
->options_valid
= 0;
156 * If we haven't seen this option before, just make
157 * space for it and copy it there.
159 if (!packet
->options
[code
].data
) {
160 if (!(t
= calloc(1, len
+ 1)))
161 error("Can't allocate storage for option %s.",
162 dhcp_options
[code
].name
);
164 * Copy and NUL-terminate the option (in case
165 * it's an ASCII string.
167 memcpy(t
, &s
[2], len
);
169 packet
->options
[code
].len
= len
;
170 packet
->options
[code
].data
= t
;
173 * If it's a repeat, concatenate it to whatever
174 * we last saw. This is really only required
175 * for clients, but what the heck...
177 t
= calloc(1, len
+ packet
->options
[code
].len
+ 1);
179 error("Can't expand storage for option %s.",
180 dhcp_options
[code
].name
);
181 memcpy(t
, packet
->options
[code
].data
,
182 packet
->options
[code
].len
);
183 memcpy(t
+ packet
->options
[code
].len
,
185 packet
->options
[code
].len
+= len
;
186 t
[packet
->options
[code
].len
] = 0;
187 free(packet
->options
[code
].data
);
188 packet
->options
[code
].data
= t
;
192 packet
->options_valid
= 1;
196 * cons options into a big buffer, and then split them out into the
197 * three separate buffers if needed. This allows us to cons up a set of
198 * vendor options using the same routine.
201 cons_options(struct packet
*inpacket
, struct dhcp_packet
*outpacket
,
202 int mms
, struct tree_cache
**options
,
203 int overload
, /* Overload flags that may be set. */
204 int terminate
, int bootpp
, u_int8_t
*prl
, int prl_len
)
206 unsigned char priority_list
[300], buffer
[4096];
207 int priority_len
, main_buffer_size
, mainbufix
, bufix
;
208 int option_size
, length
;
211 * If the client has provided a maximum DHCP message size, use
212 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
213 * up to the minimum IP MTU size (576 bytes).
215 * XXX if a BOOTP client specifies a max message size, we will
220 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
&&
221 (inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].len
>=
224 inpacket
->options
[DHO_DHCP_MAX_MESSAGE_SIZE
].data
);
227 main_buffer_size
= mms
- DHCP_FIXED_LEN
;
229 main_buffer_size
= 64;
231 main_buffer_size
= 576 - DHCP_FIXED_LEN
;
233 if (main_buffer_size
> sizeof(buffer
))
234 main_buffer_size
= sizeof(buffer
);
236 /* Preload the option priority list with mandatory options. */
238 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE_TYPE
;
239 priority_list
[priority_len
++] = DHO_DHCP_SERVER_IDENTIFIER
;
240 priority_list
[priority_len
++] = DHO_DHCP_LEASE_TIME
;
241 priority_list
[priority_len
++] = DHO_DHCP_MESSAGE
;
244 * If the client has provided a list of options that it wishes
245 * returned, use it to prioritize. Otherwise, prioritize based
246 * on the default priority list.
249 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
) {
251 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].len
;
252 if (prlen
+ priority_len
> sizeof(priority_list
))
253 prlen
= sizeof(priority_list
) - priority_len
;
255 memcpy(&priority_list
[priority_len
],
256 inpacket
->options
[DHO_DHCP_PARAMETER_REQUEST_LIST
].data
,
258 priority_len
+= prlen
;
261 if (prl_len
+ priority_len
> sizeof(priority_list
))
262 prl_len
= sizeof(priority_list
) - priority_len
;
264 memcpy(&priority_list
[priority_len
], prl
, prl_len
);
265 priority_len
+= prl_len
;
268 memcpy(&priority_list
[priority_len
],
269 dhcp_option_default_priority_list
,
270 sizeof_dhcp_option_default_priority_list
);
271 priority_len
+= sizeof_dhcp_option_default_priority_list
;
274 /* Copy the options into the big buffer... */
275 option_size
= store_options(
277 (main_buffer_size
- 7 + ((overload
& 1) ? DHCP_FILE_LEN
: 0) +
278 ((overload
& 2) ? DHCP_SNAME_LEN
: 0)),
279 options
, priority_list
, priority_len
, main_buffer_size
,
280 (main_buffer_size
+ ((overload
& 1) ? DHCP_FILE_LEN
: 0)),
283 /* Put the cookie up front... */
284 memcpy(outpacket
->options
, DHCP_OPTIONS_COOKIE
, 4);
288 * If we're going to have to overload, store the overload option
289 * at the beginning. If we can, though, just store the whole
290 * thing in the packet's option buffer and leave it at that.
292 if (option_size
<= main_buffer_size
- mainbufix
) {
293 memcpy(&outpacket
->options
[mainbufix
],
294 buffer
, option_size
);
295 mainbufix
+= option_size
;
296 if (mainbufix
< main_buffer_size
)
297 outpacket
->options
[mainbufix
++] = DHO_END
;
298 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
300 outpacket
->options
[mainbufix
++] = DHO_DHCP_OPTION_OVERLOAD
;
301 outpacket
->options
[mainbufix
++] = 1;
303 main_buffer_size
- mainbufix
+ DHCP_FILE_LEN
)
304 outpacket
->options
[mainbufix
++] = 3;
306 outpacket
->options
[mainbufix
++] = 1;
308 memcpy(&outpacket
->options
[mainbufix
],
309 buffer
, main_buffer_size
- mainbufix
);
310 bufix
= main_buffer_size
- mainbufix
;
311 length
= DHCP_FIXED_NON_UDP
+ mainbufix
;
313 if (option_size
- bufix
<= DHCP_FILE_LEN
) {
314 memcpy(outpacket
->file
,
315 &buffer
[bufix
], option_size
- bufix
);
316 mainbufix
= option_size
- bufix
;
317 if (mainbufix
< DHCP_FILE_LEN
)
318 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
319 while (mainbufix
< DHCP_FILE_LEN
)
320 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
322 memcpy(outpacket
->file
,
323 &buffer
[bufix
], DHCP_FILE_LEN
);
324 bufix
+= DHCP_FILE_LEN
;
327 if ((overload
& 2) && option_size
< bufix
) {
328 memcpy(outpacket
->sname
,
329 &buffer
[bufix
], option_size
- bufix
);
331 mainbufix
= option_size
- bufix
;
332 if (mainbufix
< DHCP_SNAME_LEN
)
333 outpacket
->file
[mainbufix
++] = (char)DHO_END
;
334 while (mainbufix
< DHCP_SNAME_LEN
)
335 outpacket
->file
[mainbufix
++] = (char)DHO_PAD
;
342 * Store all the requested options into the requested buffer.
345 store_options(unsigned char *buffer
, int buflen
, struct tree_cache
**options
,
346 unsigned char *priority_list
, int priority_len
, int first_cutoff
,
347 int second_cutoff
, int terminate
)
349 int bufix
= 0, option_stored
[256], i
, ix
, tto
;
351 /* Zero out the stored-lengths array. */
352 memset(option_stored
, 0, sizeof(option_stored
));
355 * Copy out the options in the order that they appear in the
358 for (i
= 0; i
< priority_len
; i
++) {
359 /* Code for next option to try to store. */
360 int code
= priority_list
[i
];
364 * Number of bytes left to store (some may already have
365 * been stored by a previous pass).
369 /* If no data is available for this option, skip it. */
370 if (!options
[code
]) {
375 * The client could ask for things that are mandatory,
376 * in which case we should avoid storing them twice...
378 if (option_stored
[code
])
380 option_stored
[code
] = 1;
382 /* We should now have a constant length for the option. */
383 length
= options
[code
]->len
;
385 /* Do we add a NUL? */
386 if (terminate
&& dhcp_options
[code
].format
[0] == 't') {
392 /* Try to store the option. */
395 * If the option's length is more than 255, we must
396 * store it in multiple hunks. Store 255-byte hunks
397 * first. However, in any case, if the option data will
398 * cross a buffer boundary, split it across that
405 unsigned char incr
= length
> 255 ? 255 : length
;
408 * If this hunk of the buffer will cross a
409 * boundary, only go up to the boundary in this
412 if (bufix
< first_cutoff
&&
413 bufix
+ incr
> first_cutoff
)
414 incr
= first_cutoff
- bufix
;
415 else if (bufix
< second_cutoff
&&
416 bufix
+ incr
> second_cutoff
)
417 incr
= second_cutoff
- bufix
;
420 * If this option is going to overflow the
423 if (bufix
+ 2 + incr
> buflen
) {
428 /* Everything looks good - copy it in! */
429 buffer
[bufix
] = code
;
430 buffer
[bufix
+ 1] = incr
;
431 if (tto
&& incr
== length
) {
432 memcpy(buffer
+ bufix
+ 2,
433 options
[code
]->value
+ ix
, incr
- 1);
434 buffer
[bufix
+ 2 + incr
- 1] = 0;
436 memcpy(buffer
+ bufix
+ 2,
437 options
[code
]->value
+ ix
, incr
);
447 * Format the specified option so that a human can easily read it.
450 pretty_print_option(unsigned int code
, unsigned char *data
, int len
,
451 int emit_commas
, int emit_quotes
)
453 static char optbuf
[32768]; /* XXX */
454 int hunksize
= 0, numhunk
= -1, numelem
= 0;
455 char fmtbuf
[32], *op
= optbuf
;
456 int i
, j
, k
, opleft
= sizeof(optbuf
);
457 unsigned char *dp
= data
;
461 /* Code should be between 0 and 255. */
463 error("pretty_print_option: bad code %d", code
);
470 /* Figure out the size of the data. */
471 for (i
= 0; dhcp_options
[code
].format
[i
]; i
++) {
473 warning("%s: Excess information in format string: %s",
474 dhcp_options
[code
].name
,
475 &(dhcp_options
[code
].format
[i
]));
479 fmtbuf
[i
] = dhcp_options
[code
].format
[i
];
480 switch (dhcp_options
[code
].format
[i
]) {
487 for (k
= 0; k
< len
; k
++)
488 if (!isascii(data
[k
]) ||
524 warning("%s: garbage in format string: %s",
525 dhcp_options
[code
].name
,
526 &(dhcp_options
[code
].format
[i
]));
531 /* Check for too few bytes... */
532 if (hunksize
> len
) {
533 warning("%s: expecting at least %d bytes; got %d",
534 dhcp_options
[code
].name
, hunksize
, len
);
537 /* Check for too many bytes... */
538 if (numhunk
== -1 && hunksize
< len
)
539 warning("%s: %d extra bytes",
540 dhcp_options
[code
].name
, len
- hunksize
);
542 /* If this is an array, compute its size. */
544 numhunk
= len
/ hunksize
;
545 /* See if we got an exact number of hunks. */
546 if (numhunk
> 0 && numhunk
* hunksize
< len
)
547 warning("%s: %d extra bytes at end of array",
548 dhcp_options
[code
].name
, len
- numhunk
* hunksize
);
550 /* A one-hunk array prints the same as a single hunk. */
554 /* Cycle through the array (or hunk) printing the data. */
555 for (i
= 0; i
< numhunk
; i
++) {
556 for (j
= 0; j
< numelem
; j
++) {
564 for (; dp
< data
+ len
; dp
++) {
567 if (dp
+ 1 != data
+ len
||
574 } else if (*dp
== '"' ||
595 foo
.s_addr
= htonl(getULong(dp
));
596 strncpy(op
, inet_ntoa(foo
), opleft
- 1);
597 op
[opleft
- 1] = ANSI_NULL
;
598 opcount
= strlen(op
);
599 if (opcount
>= opleft
)
605 opcount
= snprintf(op
, opleft
, "%ld",
607 if (opcount
>= opleft
|| opcount
== -1)
613 opcount
= snprintf(op
, opleft
, "%ld",
614 (unsigned long)getULong(dp
));
615 if (opcount
>= opleft
|| opcount
== -1)
621 opcount
= snprintf(op
, opleft
, "%d",
623 if (opcount
>= opleft
|| opcount
== -1)
629 opcount
= snprintf(op
, opleft
, "%d",
631 if (opcount
>= opleft
|| opcount
== -1)
637 opcount
= snprintf(op
, opleft
, "%d",
639 if (opcount
>= opleft
|| opcount
== -1)
644 opcount
= snprintf(op
, opleft
, "%d", *dp
++);
645 if (opcount
>= opleft
|| opcount
== -1)
650 opcount
= snprintf(op
, opleft
, "%x", *dp
++);
651 if (opcount
>= opleft
|| opcount
== -1)
656 opcount
= (size_t) strncpy(op
, *dp
++ ? "true" : "false", opleft
- 1);
657 op
[opleft
- 1] = ANSI_NULL
;
658 if (opcount
>= opleft
)
663 warning("Unexpected format code %c", fmtbuf
[j
]);
666 opleft
-= strlen(op
);
669 if (j
+ 1 < numelem
&& comma
!= ':') {
674 if (i
+ 1 < numhunk
) {
684 warning("dhcp option too large");
689 do_packet(struct interface_info
*interface
, struct dhcp_packet
*packet
,
690 int len
, unsigned int from_port
, struct iaddr from
, struct hardware
*hfrom
)
695 if (packet
->hlen
> sizeof(packet
->chaddr
)) {
696 note("Discarding packet with invalid hlen.");
700 memset(&tp
, 0, sizeof(tp
));
702 tp
.packet_length
= len
;
703 tp
.client_port
= from_port
;
704 tp
.client_addr
= from
;
705 tp
.interface
= interface
;
709 if (tp
.options_valid
&&
710 tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
)
711 tp
.packet_type
= tp
.options
[DHO_DHCP_MESSAGE_TYPE
].data
[0];
717 /* Free the data associated with the options. */
718 for (i
= 0; i
< 256; i
++)
719 if (tp
.options
[i
].len
&& tp
.options
[i
].data
)
720 free(tp
.options
[i
].data
);