2 * Copyright (c) 2002-2019 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * - routines to parse and access dhcp options
31 * and create new dhcp option areas
32 * - handles overloaded areas as well as vendor-specific options
33 * that are encoded using the RFC 2132 encoding
37 * Modification History
39 * March 15, 2002 Dieter Siegmund (dieter@apple)
40 * - imported from bootp project
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <netinet/in.h>
47 #include <sys/malloc.h>
48 #include <netinet/dhcp.h>
49 #include <netinet/dhcp_options.h>
51 #ifndef TEST_DHCP_OPTIONS
52 #include <libkern/libkern.h>
55 #define dprintf(x) printf x;
56 #else /* !DHCP_DEBUG */
58 #endif /* DHCP_DEBUG */
60 static __inline__
void
66 static __inline__
void *
70 MALLOC(data
, void *, size
, M_TEMP
, M_WAITOK
);
74 static __inline__
void *
75 my_realloc(void * oldptr
, int oldsize
, int newsize
)
79 MALLOC(data
, void *, newsize
, M_TEMP
, M_WAITOK
);
80 bcopy(oldptr
, data
, oldsize
);
87 * xcrun -sdk macosx.internal cc -DTEST_DHCP_OPTIONS -o /tmp/dhcp_options dhcp_options.c -I ..
93 #define my_malloc malloc
94 #define my_realloc(ptr, old_size, new_size) realloc(ptr, new_size)
95 #define dprintf(x) printf x;
99 * Functions: ptrlist_*
101 * A dynamically growable array of pointers.
104 #define PTRLIST_NUMBER 16
107 ptrlist_init(ptrlist_t
* list
)
109 bzero(list
, sizeof(*list
));
114 ptrlist_free(ptrlist_t
* list
)
117 my_free(list
->array
);
124 ptrlist_count(ptrlist_t
* list
)
126 if (list
== NULL
|| list
->array
== NULL
) {
134 ptrlist_element(ptrlist_t
* list
, int i
)
136 if (list
->array
== NULL
) {
139 if (i
< list
->count
) {
140 return list
->array
[i
];
147 ptrlist_grow(ptrlist_t
* list
)
149 if (list
->array
== NULL
) {
150 if (list
->size
== 0) {
151 list
->size
= PTRLIST_NUMBER
;
154 list
->array
= my_malloc(sizeof(*list
->array
) * list
->size
);
155 } else if (list
->size
== list
->count
) {
156 dprintf(("doubling %d to %d\n", list
->size
, list
->size
* 2));
157 list
->array
= my_realloc(list
->array
,
158 sizeof(*list
->array
) * list
->size
,
159 sizeof(*list
->array
) * list
->size
* 2);
162 if (list
->array
== NULL
) {
169 ptrlist_add(ptrlist_t
* list
, const void * element
)
171 if (ptrlist_grow(list
) == FALSE
) {
175 list
->array
[list
->count
++] = element
;
179 /* concatenates extra onto list */
181 ptrlist_concat(ptrlist_t
* list
, ptrlist_t
* extra
)
183 if (extra
->count
== 0) {
187 if ((extra
->count
+ list
->count
) > list
->size
) {
188 int old_size
= list
->size
;
190 list
->size
= extra
->count
+ list
->count
;
191 if (list
->array
== NULL
) {
192 list
->array
= my_malloc(sizeof(*list
->array
) * list
->size
);
194 list
->array
= my_realloc(list
->array
, old_size
,
195 sizeof(*list
->array
) * list
->size
);
198 if (list
->array
== NULL
) {
201 bcopy(extra
->array
, list
->array
+ list
->count
,
202 extra
->count
* sizeof(*list
->array
));
203 list
->count
+= extra
->count
;
209 * Functions: dhcpol_*
212 * Routines to parse/access existing options buffers.
215 dhcpol_add(dhcpol_t
* list
, const void * element
)
217 return ptrlist_add((ptrlist_t
*)list
, element
);
221 dhcpol_count(dhcpol_t
* list
)
223 return ptrlist_count((ptrlist_t
*)list
);
227 dhcpol_element(dhcpol_t
* list
, int i
)
229 return ptrlist_element((ptrlist_t
*)list
, i
);
233 dhcpol_init(dhcpol_t
* list
)
235 ptrlist_init((ptrlist_t
*)list
);
239 dhcpol_free(dhcpol_t
* list
)
241 ptrlist_free((ptrlist_t
*)list
);
245 dhcpol_concat(dhcpol_t
* list
, dhcpol_t
* extra
)
247 return ptrlist_concat((ptrlist_t
*)list
, (ptrlist_t
*)extra
);
251 * Function: dhcpol_parse_buffer
254 * Parse the given buffer into DHCP options, returning the
255 * list of option pointers in the given dhcpol_t.
256 * Parsing continues until we hit the end of the buffer or
260 dhcpol_parse_buffer(dhcpol_t
* list
, const void * buffer
, int length
)
263 const uint8_t * scan
;
270 for (scan
= (const uint8_t *)buffer
;
271 tag
!= dhcptag_end_e
&& len
> DHCP_TAG_OFFSET
;) {
272 tag
= scan
[DHCP_TAG_OFFSET
];
276 /* remember that it was terminated */
277 dhcpol_add(list
, scan
);
281 case dhcptag_pad_e
: /* ignore pad */
286 if (len
> DHCP_LEN_OFFSET
) {
289 option_len
= scan
[DHCP_LEN_OFFSET
];
290 dhcpol_add(list
, scan
);
291 len
-= (option_len
+ DHCP_OPTION_OFFSET
);
292 scan
+= (option_len
+ DHCP_OPTION_OFFSET
);
300 /* ran off the end */
301 dprintf(("dhcp_options: parse failed near tag %d\n", tag
));
309 * Function: dhcpol_find
312 * Finds the first occurence of the given option, and returns its
313 * length and the option data pointer.
315 * The optional start parameter allows this function to
316 * return the next start point so that successive
317 * calls will retrieve the next occurence of the option.
318 * Before the first call, *start should be set to 0.
321 dhcpol_find(dhcpol_t
* list
, int tag
, int * len_p
, int * start
)
325 if (tag
== dhcptag_end_e
|| tag
== dhcptag_pad_e
) {
333 for (; i
< dhcpol_count(list
); i
++) {
334 const uint8_t * option
= dhcpol_element(list
, i
);
336 if (option
[DHCP_TAG_OFFSET
] == tag
) {
338 *len_p
= option
[DHCP_LEN_OFFSET
];
343 return option
+ DHCP_OPTION_OFFSET
;
350 * Function: dhcpol_parse_packet
353 * Parse the option areas in the DHCP packet.
354 * Verifies that the packet has the right magic number,
355 * then parses and accumulates the option areas.
356 * First the pkt->dp_options is parsed. If that contains
357 * the overload option, it parses pkt->dp_file if specified,
358 * then parses pkt->dp_sname if specified.
361 dhcpol_parse_packet(dhcpol_t
* options
, const struct dhcp
* pkt
, int len
)
363 char rfc_magic
[4] = RFC_OPTIONS_MAGIC
;
365 dhcpol_init(options
); /* make sure it's empty */
367 if (len
< (sizeof(*pkt
) + RFC_MAGIC_SIZE
)) {
368 dprintf(("dhcp_options: packet is too short: %d < %d\n",
369 len
, (int)sizeof(*pkt
) + RFC_MAGIC_SIZE
));
372 if (bcmp(pkt
->dp_options
, rfc_magic
, RFC_MAGIC_SIZE
)) {
373 dprintf(("dhcp_options: missing magic number\n"));
376 if (dhcpol_parse_buffer(options
, pkt
->dp_options
+ RFC_MAGIC_SIZE
,
377 len
- sizeof(*pkt
) - RFC_MAGIC_SIZE
) == FALSE
) {
380 { /* get overloaded options */
381 const uint8_t * overload
;
384 overload
= dhcpol_find(options
, dhcptag_option_overload_e
,
385 &overload_len
, NULL
);
386 if (overload
&& overload_len
== 1) { /* has overloaded options */
390 if (*overload
== DHCP_OVERLOAD_FILE
391 || *overload
== DHCP_OVERLOAD_BOTH
) {
392 if (dhcpol_parse_buffer(&extra
, pkt
->dp_file
,
393 sizeof(pkt
->dp_file
))) {
394 dhcpol_concat(options
, &extra
);
398 if (*overload
== DHCP_OVERLOAD_SNAME
399 || *overload
== DHCP_OVERLOAD_BOTH
) {
400 if (dhcpol_parse_buffer(&extra
, pkt
->dp_sname
,
401 sizeof(pkt
->dp_sname
))) {
402 dhcpol_concat(options
, &extra
);
411 #ifdef TEST_DHCP_OPTIONS
412 char test_empty
[] = {
417 char test_short
[] = {
422 char test_simple
[] = {
424 1, 4, 255, 255, 252, 0,
425 3, 4, 17, 202, 40, 1,
429 char test_vendor
[] = {
431 1, 4, 255, 255, 252, 0,
432 3, 4, 17, 202, 40, 1,
433 43, 6, 1, 4, 1, 2, 3, 4,
434 43, 6, 1, 4, 1, 2, 3, 4,
438 char test_no_end
[] = {
439 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
440 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x80,
441 0x00, 0x80, 0x00, 0x01, 0x04, 0xff, 0xff, 0xff,
442 0x00, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x06,
443 0x0c, 0x18, 0x1a, 0xa3, 0x21, 0x18, 0x1a, 0xa3,
444 0x20, 0x18, 0x5e, 0xa3, 0x21, 0x00, 0x00, 0x00,
445 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
449 char test_no_magic
[] = {
459 struct test tests
[] = {
460 { .name
= "empty", .data
= test_empty
, .len
= sizeof(test_empty
), .result
= TRUE
},
461 { .name
= "simple", .data
= test_simple
, .len
= sizeof(test_simple
), .result
= TRUE
},
462 { .name
= "vendor", .data
= test_vendor
, .len
= sizeof(test_vendor
), .result
= TRUE
},
463 { .name
= "no_end", .data
= test_no_end
, .len
= sizeof(test_no_end
), .result
= TRUE
},
464 { .name
= "no magic", .data
= test_no_magic
, .len
= sizeof(test_no_magic
), .result
= FALSE
},
465 { .name
= "short", .data
= test_short
, .len
= sizeof(test_short
), .result
= FALSE
},
466 { .name
= NULL
, .data
= NULL
, .len
= 0, .result
= FALSE
},
470 static char buf
[2048];
477 struct dhcp
* pkt
= (struct dhcp
*)buf
;
479 dhcpol_init(&options
);
481 for (i
= 0; tests
[i
].name
; i
++) {
482 printf("\nTest %d: ", i
);
483 bcopy(tests
[i
].data
, pkt
->dp_options
, tests
[i
].len
);
484 if (dhcpol_parse_packet(&options
, pkt
,
485 sizeof(*pkt
) + tests
[i
].len
)
486 != tests
[i
].result
) {
487 printf("test '%s' FAILED\n", tests
[i
].name
);
489 printf("test '%s' PASSED\n", tests
[i
].name
);
491 dhcpol_free(&options
);
495 #endif /* TEST_DHCP_OPTIONS */