1 /* $OpenBSD: options.c,v 1.36 2007/06/02 01:29:11 pvalchev Exp $ */
2 /* $DragonFly: src/sbin/dhclient/options.c,v 1.1 2008/08/30 16:07:58 hasso Exp $ */
4 /* DHCP options parsing and reassembly. */
7 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of The Internet Software Consortium nor the names
20 * of its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * This software has been written for the Internet Software Consortium
38 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
39 * Enterprises. To learn more about the Internet Software Consortium,
40 * see ``http://www.vix.com/isc''. To learn more about Vixie
41 * Enterprises, see ``http://www.vix.com''.
48 int parse_option_buffer(struct option_data
*, unsigned char *, int);
51 * Parse options out of the specified buffer, storing addresses of
52 * option values in options and setting client->options_valid if
53 * no errors are encountered.
56 parse_option_buffer(struct option_data
*options
, unsigned char *buffer
,
59 unsigned char *s
, *t
, *end
= buffer
+ length
;
62 for (s
= buffer
; *s
!= DHO_END
&& s
< end
; ) {
65 /* Pad options don't have a length - just skip them. */
66 if (code
== DHO_PAD
) {
72 * All options other than DHO_PAD and DHO_END have a
73 * one-byte length field.
81 * If the option claims to extend beyond the end of the buffer
82 * then mark the options buffer bad.
84 if (s
+ len
+ 2 > end
) {
85 warning("option %s (%d) larger than buffer.",
86 dhcp_options
[code
].name
, len
);
87 warning("rejecting bogus offer.");
91 * If we haven't seen this option before, just make
92 * space for it and copy it there.
94 if (!options
[code
].data
) {
95 if (!(t
= calloc(1, len
+ 1)))
96 error("Can't allocate storage for option %s.",
97 dhcp_options
[code
].name
);
99 * Copy and NUL-terminate the option (in case
100 * it's an ASCII string).
102 memcpy(t
, &s
[2], len
);
104 options
[code
].len
= len
;
105 options
[code
].data
= t
;
108 * If it's a repeat, concatenate it to whatever
109 * we last saw. This is really only required
110 * for clients, but what the heck...
112 t
= calloc(1, len
+ options
[code
].len
+ 1);
114 error("Can't expand storage for option %s.",
115 dhcp_options
[code
].name
);
116 memcpy(t
, options
[code
].data
, options
[code
].len
);
117 memcpy(t
+ options
[code
].len
, &s
[2], len
);
118 options
[code
].len
+= len
;
119 t
[options
[code
].len
] = 0;
120 free(options
[code
].data
);
121 options
[code
].data
= t
;
130 * Copy as many options as fit in buflen bytes of buf. Return the
131 * offset of the start of the last option copied. A caller can check
132 * to see if it's DHO_END to decide if all the options were copied.
135 cons_options(unsigned char *buf
, const int buflen
, struct option_data
*options
)
137 int ix
, incr
, length
, bufix
, code
, lastopt
= -1;
142 memcpy(buf
, DHCP_OPTIONS_COOKIE
, 4);
145 for (code
= DHO_SUBNET_MASK
; code
< DHO_END
; code
++) {
146 if (!options
[code
].data
)
149 length
= options
[code
].len
;
150 if (bufix
+ length
+ 2*((length
+254)/255) >= buflen
)
157 incr
= length
> 255 ? 255 : length
;
161 memcpy(buf
+ bufix
, options
[code
].data
+ ix
, incr
);
169 if (bufix
< buflen
) {
170 buf
[bufix
] = DHO_END
;
178 * Format the specified option so that a human can easily read it.
181 pretty_print_option(unsigned int code
, unsigned char *data
, int len
,
182 int emit_commas
, int emit_quotes
)
184 static char optbuf
[32768]; /* XXX */
185 int hunksize
= 0, numhunk
= -1, numelem
= 0;
186 char fmtbuf
[32], *op
= optbuf
;
187 int i
, j
, k
, opleft
= sizeof(optbuf
);
188 unsigned char *dp
= data
;
192 /* Code should be between 0 and 255. */
194 error("pretty_print_option: bad code %d", code
);
201 /* Figure out the size of the data. */
202 for (i
= 0; dhcp_options
[code
].format
[i
]; i
++) {
204 warning("%s: Excess information in format string: %s",
205 dhcp_options
[code
].name
,
206 &(dhcp_options
[code
].format
[i
]));
210 fmtbuf
[i
] = dhcp_options
[code
].format
[i
];
211 switch (dhcp_options
[code
].format
[i
]) {
218 for (k
= 0; k
< len
; k
++)
219 if (!isascii(data
[k
]) ||
255 warning("%s: garbage in format string: %s",
256 dhcp_options
[code
].name
,
257 &(dhcp_options
[code
].format
[i
]));
262 /* Check for too few bytes... */
263 if (hunksize
> len
) {
264 warning("%s: expecting at least %d bytes; got %d",
265 dhcp_options
[code
].name
, hunksize
, len
);
268 /* Check for too many bytes... */
269 if (numhunk
== -1 && hunksize
< len
)
270 warning("%s: %d extra bytes",
271 dhcp_options
[code
].name
, len
- hunksize
);
273 /* If this is an array, compute its size. */
275 numhunk
= len
/ hunksize
;
276 /* See if we got an exact number of hunks. */
277 if (numhunk
> 0 && numhunk
* hunksize
< len
)
278 warning("%s: %d extra bytes at end of array",
279 dhcp_options
[code
].name
, len
- numhunk
* hunksize
);
281 /* A one-hunk array prints the same as a single hunk. */
285 /* Cycle through the array (or hunk) printing the data. */
286 for (i
= 0; i
< numhunk
; i
++) {
287 for (j
= 0; j
< numelem
; j
++) {
296 for (; dp
< data
+ len
; dp
++) {
299 if (dp
+ 1 != data
+ len
||
308 } else if (*dp
== '"' ||
329 foo
.s_addr
= htonl(getULong(dp
));
330 opcount
= strlcpy(op
, inet_ntoa(foo
), opleft
);
331 if (opcount
>= opleft
)
337 opcount
= snprintf(op
, opleft
, "%ld",
339 if (opcount
>= opleft
|| opcount
== -1)
345 opcount
= snprintf(op
, opleft
, "%ld",
346 (unsigned long)getULong(dp
));
347 if (opcount
>= opleft
|| opcount
== -1)
353 opcount
= snprintf(op
, opleft
, "%d",
355 if (opcount
>= opleft
|| opcount
== -1)
361 opcount
= snprintf(op
, opleft
, "%d",
363 if (opcount
>= opleft
|| opcount
== -1)
369 opcount
= snprintf(op
, opleft
, "%d",
371 if (opcount
>= opleft
|| opcount
== -1)
376 opcount
= snprintf(op
, opleft
, "%d", *dp
++);
377 if (opcount
>= opleft
|| opcount
== -1)
382 opcount
= snprintf(op
, opleft
, "%x", *dp
++);
383 if (opcount
>= opleft
|| opcount
== -1)
388 opcount
= strlcpy(op
,
389 *dp
++ ? "true" : "false", opleft
);
390 if (opcount
>= opleft
)
395 warning("Unexpected format code %c", fmtbuf
[j
]);
402 if (j
+ 1 < numelem
&& comma
!= ':') {
407 if (i
+ 1 < numhunk
) {
417 warning("dhcp option too large");
422 do_packet(int len
, unsigned int from_port
, struct iaddr from
,
423 struct hardware
*hfrom
)
425 struct dhcp_packet
*packet
= &client
->packet
;
426 struct option_data options
[256];
427 struct iaddrlist
*ap
;
428 void (*handler
)(struct iaddr
, struct option_data
*);
430 int i
, options_valid
= 1;
432 if (packet
->hlen
> sizeof(packet
->chaddr
)) {
433 note("Discarding packet with invalid hlen.");
438 * Silently drop the packet if the client hardware address in the
439 * packet is not the hardware address of the interface being managed.
441 if ((ifi
->hw_address
.hlen
!= packet
->hlen
) ||
442 (memcmp(ifi
->hw_address
.haddr
, packet
->chaddr
, packet
->hlen
)))
445 memset(options
, 0, sizeof(options
));
447 if (memcmp(&packet
->options
, DHCP_OPTIONS_COOKIE
, 4) == 0) {
448 /* Parse the BOOTP/DHCP options field. */
449 options_valid
= parse_option_buffer(options
,
450 &packet
->options
[4], sizeof(packet
->options
) - 4);
452 /* Only DHCP packets have overload areas for options. */
454 options
[DHO_DHCP_MESSAGE_TYPE
].data
&&
455 options
[DHO_DHCP_OPTION_OVERLOAD
].data
) {
456 if (options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 1)
457 options_valid
= parse_option_buffer(options
,
458 (unsigned char *)packet
->file
,
459 sizeof(packet
->file
));
461 options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 2)
462 options_valid
= parse_option_buffer(options
,
463 (unsigned char *)packet
->sname
,
464 sizeof(packet
->sname
));
471 if (options
[DHO_DHCP_MESSAGE_TYPE
].data
) {
472 /* Always try a DHCP packet, even if a bad option was seen. */
473 switch (options
[DHO_DHCP_MESSAGE_TYPE
].data
[0]) {
489 } else if (options_valid
&& packet
->op
== BOOTREPLY
) {
494 for (ap
= config
->reject_list
; ap
&& handler
; ap
= ap
->next
)
495 if (addr_eq(from
, ap
->addr
)) {
496 note("%s from %s rejected.", type
, piaddr(from
));
501 (*handler
)(from
, options
);
503 for (i
= 0; i
< 256; i
++)
504 if (options
[i
].len
&& options
[i
].data
)
505 free(options
[i
].data
);