1 /* $OpenBSD: src/sbin/dhclient/options.c,v 1.42 2012/10/27 23:08:53 krw 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''.
47 int parse_option_buffer(struct option_data
*, unsigned char *, int);
50 * Parse options out of the specified buffer, storing addresses of
51 * option values in options and setting client->options_valid if
52 * no errors are encountered.
55 parse_option_buffer(struct option_data
*options
, unsigned char *buffer
,
58 unsigned char *s
, *t
, *end
= buffer
+ length
;
61 for (s
= buffer
; *s
!= DHO_END
&& s
< end
; ) {
64 /* Pad options don't have a length - just skip them. */
65 if (code
== DHO_PAD
) {
71 * All options other than DHO_PAD and DHO_END have a one-byte
72 * length field. It could be 0! Make sure that the length byte
73 * is present, and all the data is available.
77 if (s
+ 1 + len
< end
) {
78 ; /* option data is all there. */
80 warning("option %s (%d) larger than buffer.",
81 dhcp_options
[code
].name
, len
);
82 warning("rejecting bogus offer.");
86 warning("option %s has no length field.",
87 dhcp_options
[code
].name
);
88 warning("rejecting bogus offer.");
93 * Strip trailing NULs from ascii ('t') options. They
94 * will be treated as DHO_PAD options. i.e. ignored. RFC 2132
95 * says "Options containing NVT ASCII data SHOULD NOT include
96 * a trailing NULL; however, the receiver of such options
97 * MUST be prepared to delete trailing nulls if they exist."
99 if (dhcp_options
[code
].format
[0] == 't') {
100 while (len
> 0 && s
[len
+ 1] == '\0')
105 * If we haven't seen this option before, just make
106 * space for it and copy it there.
108 if (!options
[code
].data
) {
109 if (!(t
= calloc(1, len
+ 1)))
110 error("Can't allocate storage for option %s.",
111 dhcp_options
[code
].name
);
113 * Copy and NUL-terminate the option (in case
114 * it's an ASCII string).
116 memcpy(t
, &s
[2], len
);
118 options
[code
].len
= len
;
119 options
[code
].data
= t
;
122 * If it's a repeat, concatenate it to whatever
123 * we last saw. This is really only required
124 * for clients, but what the heck...
126 t
= calloc(1, len
+ options
[code
].len
+ 1);
128 error("Can't expand storage for option %s.",
129 dhcp_options
[code
].name
);
130 memcpy(t
, options
[code
].data
, options
[code
].len
);
131 memcpy(t
+ options
[code
].len
, &s
[2], len
);
132 options
[code
].len
+= len
;
133 t
[options
[code
].len
] = 0;
134 free(options
[code
].data
);
135 options
[code
].data
= t
;
144 * Copy as many options as fit in buflen bytes of buf. Return the
145 * offset of the start of the last option copied. A caller can check
146 * to see if it's DHO_END to decide if all the options were copied.
149 cons_options(struct option_data
*options
)
151 unsigned char *buf
= client
->packet
.options
;
152 int buflen
= 576 - DHCP_FIXED_LEN
;
153 int ix
, incr
, length
, bufix
, code
, lastopt
= -1;
157 memcpy(buf
, DHCP_OPTIONS_COOKIE
, 4);
158 if (options
[DHO_DHCP_MESSAGE_TYPE
].data
) {
159 memcpy(&buf
[4], DHCP_OPTIONS_MESSAGE_TYPE
, 3);
160 buf
[6] = options
[DHO_DHCP_MESSAGE_TYPE
].data
[0];
165 for (code
= DHO_SUBNET_MASK
; code
< DHO_END
; code
++) {
166 if (!options
[code
].data
|| code
== DHO_DHCP_MESSAGE_TYPE
)
169 length
= options
[code
].len
;
170 if (bufix
+ length
+ 2*((length
+254)/255) >= buflen
)
177 incr
= length
> 255 ? 255 : length
;
181 memcpy(buf
+ bufix
, options
[code
].data
+ ix
, incr
);
189 if (bufix
< buflen
) {
190 buf
[bufix
] = DHO_END
;
198 * Format the specified option so that a human can easily read it.
201 pretty_print_option(unsigned int code
, struct option_data
*option
,
204 static char optbuf
[32768]; /* XXX */
205 int hunksize
= 0, numhunk
= -1, numelem
= 0;
206 char fmtbuf
[32], *op
= optbuf
;
207 int i
, j
, k
, opleft
= sizeof(optbuf
);
208 unsigned char *data
= option
->data
;
209 unsigned char *dp
= data
;
210 int len
= option
->len
;
214 /* Code should be between 0 and 255. */
216 error("pretty_print_option: bad code %d", code
);
223 /* Figure out the size of the data. */
224 for (i
= 0; dhcp_options
[code
].format
[i
]; i
++) {
226 warning("%s: Excess information in format string: %s",
227 dhcp_options
[code
].name
,
228 &(dhcp_options
[code
].format
[i
]));
232 fmtbuf
[i
] = dhcp_options
[code
].format
[i
];
233 switch (dhcp_options
[code
].format
[i
]) {
239 warning("%s: no size indicator before A"
240 " in format string: %s",
241 dhcp_options
[code
].name
,
242 dhcp_options
[code
].format
);
243 return ("<fmt error>");
247 for (k
= 0; k
< len
; k
++)
248 if (!isascii(data
[k
]) ||
284 warning("%s: garbage in format string: %s",
285 dhcp_options
[code
].name
,
286 &(dhcp_options
[code
].format
[i
]));
291 /* Check for too few bytes... */
292 if (hunksize
> len
) {
293 warning("%s: expecting at least %d bytes; got %d",
294 dhcp_options
[code
].name
, hunksize
, len
);
297 /* Check for too many bytes... */
298 if (numhunk
== -1 && hunksize
< len
)
299 warning("%s: %d extra bytes",
300 dhcp_options
[code
].name
, len
- hunksize
);
302 /* If this is an array, compute its size. */
304 numhunk
= len
/ hunksize
;
305 /* See if we got an exact number of hunks. */
306 if (numhunk
> 0 && numhunk
* hunksize
< len
)
307 warning("%s: %d extra bytes at end of array",
308 dhcp_options
[code
].name
, len
- numhunk
* hunksize
);
310 /* A one-hunk array prints the same as a single hunk. */
314 /* Cycle through the array (or hunk) printing the data. */
315 for (i
= 0; i
< numhunk
; i
++) {
316 for (j
= 0; j
< numelem
; j
++) {
325 for (; dp
< data
+ len
; dp
++) {
328 if (dp
+ 1 != data
+ len
||
337 } else if (*dp
== '"' ||
358 foo
.s_addr
= htonl(getULong(dp
));
359 opcount
= strlcpy(op
, inet_ntoa(foo
), opleft
);
360 if (opcount
>= opleft
)
366 opcount
= snprintf(op
, opleft
, "%ld",
368 if (opcount
>= opleft
|| opcount
== -1)
374 opcount
= snprintf(op
, opleft
, "%ld",
375 (unsigned long)getULong(dp
));
376 if (opcount
>= opleft
|| opcount
== -1)
382 opcount
= snprintf(op
, opleft
, "%d",
384 if (opcount
>= opleft
|| opcount
== -1)
390 opcount
= snprintf(op
, opleft
, "%d",
392 if (opcount
>= opleft
|| opcount
== -1)
398 opcount
= snprintf(op
, opleft
, "%d",
400 if (opcount
>= opleft
|| opcount
== -1)
405 opcount
= snprintf(op
, opleft
, "%d", *dp
++);
406 if (opcount
>= opleft
|| opcount
== -1)
411 opcount
= snprintf(op
, opleft
, "%x", *dp
++);
412 if (opcount
>= opleft
|| opcount
== -1)
417 opcount
= strlcpy(op
,
418 *dp
++ ? "true" : "false", opleft
);
419 if (opcount
>= opleft
)
424 warning("Unexpected format code %c", fmtbuf
[j
]);
431 if (j
+ 1 < numelem
&& comma
!= ':') {
436 if (i
+ 1 < numhunk
) {
446 warning("dhcp option too large");
451 do_packet(int len
, unsigned int from_port
, struct iaddr from
,
452 struct hardware
*hfrom
)
454 struct dhcp_packet
*packet
= &client
->packet
;
455 struct option_data options
[256];
456 struct iaddrlist
*ap
;
457 void (*handler
)(struct iaddr
, struct option_data
*);
459 int i
, options_valid
= 1;
461 if (packet
->hlen
> sizeof(packet
->chaddr
)) {
462 note("Discarding packet with invalid hlen.");
467 * Silently drop the packet if the client hardware address in the
468 * packet is not the hardware address of the interface being managed.
470 if ((ifi
->hw_address
.hlen
!= packet
->hlen
) ||
471 (memcmp(ifi
->hw_address
.haddr
, packet
->chaddr
, packet
->hlen
)))
474 memset(options
, 0, sizeof(options
));
476 if (memcmp(&packet
->options
, DHCP_OPTIONS_COOKIE
, 4) == 0) {
477 /* Parse the BOOTP/DHCP options field. */
478 options_valid
= parse_option_buffer(options
,
479 &packet
->options
[4], sizeof(packet
->options
) - 4);
481 /* Only DHCP packets have overload areas for options. */
483 options
[DHO_DHCP_MESSAGE_TYPE
].data
&&
484 options
[DHO_DHCP_OPTION_OVERLOAD
].data
) {
485 if (options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 1)
486 options_valid
= parse_option_buffer(options
,
487 (unsigned char *)packet
->file
,
488 sizeof(packet
->file
));
490 options
[DHO_DHCP_OPTION_OVERLOAD
].data
[0] & 2)
491 options_valid
= parse_option_buffer(options
,
492 (unsigned char *)packet
->sname
,
493 sizeof(packet
->sname
));
500 if (options
[DHO_DHCP_MESSAGE_TYPE
].data
) {
501 /* Always try a DHCP packet, even if a bad option was seen. */
502 switch (options
[DHO_DHCP_MESSAGE_TYPE
].data
[0]) {
518 } else if (options_valid
&& packet
->op
== BOOTREPLY
) {
523 if (handler
&& client
->xid
== client
->packet
.xid
) {
524 if (hfrom
->hlen
== 6)
525 note("%s from %s (%s)", type
, piaddr(from
),
526 ether_ntoa((struct ether_addr
*)hfrom
->haddr
));
528 note("%s from %s", type
, piaddr(from
));
532 for (ap
= config
->reject_list
; ap
&& handler
; ap
= ap
->next
)
533 if (addr_eq(from
, ap
->addr
)) {
534 note("%s from %s rejected.", type
, piaddr(from
));
539 (*handler
)(from
, options
);
541 for (i
= 0; i
< 256; i
++)
542 if (options
[i
].len
&& options
[i
].data
)
543 free(options
[i
].data
);