2 * Copyright (c) 2002-2016 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
);
123 ptrlist_count(ptrlist_t
* list
)
125 if (list
== NULL
|| list
->array
== NULL
)
128 return (list
->count
);
132 ptrlist_element(ptrlist_t
* list
, int i
)
134 if (list
->array
== NULL
)
137 return (list
->array
[i
]);
143 ptrlist_grow(ptrlist_t
* list
)
145 if (list
->array
== NULL
) {
147 list
->size
= PTRLIST_NUMBER
;
149 list
->array
= my_malloc(sizeof(*list
->array
) * list
->size
);
151 else if (list
->size
== list
->count
) {
152 dprintf(("doubling %d to %d\n", list
->size
, list
->size
* 2));
153 list
->array
= my_realloc(list
->array
,
154 sizeof(*list
->array
) * list
->size
,
155 sizeof(*list
->array
) * list
->size
* 2);
158 if (list
->array
== NULL
)
164 ptrlist_add(ptrlist_t
* list
, const void * element
)
166 if (ptrlist_grow(list
) == FALSE
)
169 list
->array
[list
->count
++] = element
;
173 /* concatenates extra onto list */
175 ptrlist_concat(ptrlist_t
* list
, ptrlist_t
* extra
)
177 if (extra
->count
== 0)
180 if ((extra
->count
+ list
->count
) > list
->size
) {
181 int old_size
= list
->size
;
183 list
->size
= extra
->count
+ list
->count
;
184 if (list
->array
== NULL
)
185 list
->array
= my_malloc(sizeof(*list
->array
) * list
->size
);
187 list
->array
= my_realloc(list
->array
, old_size
,
188 sizeof(*list
->array
) * list
->size
);
190 if (list
->array
== NULL
)
192 bcopy(extra
->array
, list
->array
+ list
->count
,
193 extra
->count
* sizeof(*list
->array
));
194 list
->count
+= extra
->count
;
200 * Functions: dhcpol_*
203 * Routines to parse/access existing options buffers.
206 dhcpol_add(dhcpol_t
* list
, const void * element
)
208 return (ptrlist_add((ptrlist_t
*)list
, element
));
212 dhcpol_count(dhcpol_t
* list
)
214 return (ptrlist_count((ptrlist_t
*)list
));
218 dhcpol_element(dhcpol_t
* list
, int i
)
220 return (ptrlist_element((ptrlist_t
*)list
, i
));
224 dhcpol_init(dhcpol_t
* list
)
226 ptrlist_init((ptrlist_t
*)list
);
230 dhcpol_free(dhcpol_t
* list
)
232 ptrlist_free((ptrlist_t
*)list
);
236 dhcpol_concat(dhcpol_t
* list
, dhcpol_t
* extra
)
238 return (ptrlist_concat((ptrlist_t
*)list
, (ptrlist_t
*)extra
));
242 * Function: dhcpol_parse_buffer
245 * Parse the given buffer into DHCP options, returning the
246 * list of option pointers in the given dhcpol_t.
247 * Parsing continues until we hit the end of the buffer or
251 dhcpol_parse_buffer(dhcpol_t
* list
, const void * buffer
, int length
)
254 const uint8_t * scan
;
261 for (scan
= (const uint8_t *)buffer
;
262 tag
!= dhcptag_end_e
&& len
> DHCP_TAG_OFFSET
; ) {
264 tag
= scan
[DHCP_TAG_OFFSET
];
268 /* remember that it was terminated */
269 dhcpol_add(list
, scan
);
273 case dhcptag_pad_e
: /* ignore pad */
278 if (len
> DHCP_LEN_OFFSET
) {
281 option_len
= scan
[DHCP_LEN_OFFSET
];
282 dhcpol_add(list
, scan
);
283 len
-= (option_len
+ DHCP_OPTION_OFFSET
);
284 scan
+= (option_len
+ DHCP_OPTION_OFFSET
);
293 /* ran off the end */
294 dprintf(("dhcp_options: parse failed near tag %d\n", tag
));
302 * Function: dhcpol_find
305 * Finds the first occurence of the given option, and returns its
306 * length and the option data pointer.
308 * The optional start parameter allows this function to
309 * return the next start point so that successive
310 * calls will retrieve the next occurence of the option.
311 * Before the first call, *start should be set to 0.
314 dhcpol_find(dhcpol_t
* list
, int tag
, int * len_p
, int * start
)
318 if (tag
== dhcptag_end_e
|| tag
== dhcptag_pad_e
)
324 for (; i
< dhcpol_count(list
); i
++) {
325 const uint8_t * option
= dhcpol_element(list
, i
);
327 if (option
[DHCP_TAG_OFFSET
] == tag
) {
329 *len_p
= option
[DHCP_LEN_OFFSET
];
332 return (option
+ DHCP_OPTION_OFFSET
);
339 * Function: dhcpol_parse_packet
342 * Parse the option areas in the DHCP packet.
343 * Verifies that the packet has the right magic number,
344 * then parses and accumulates the option areas.
345 * First the pkt->dp_options is parsed. If that contains
346 * the overload option, it parses pkt->dp_file if specified,
347 * then parses pkt->dp_sname if specified.
350 dhcpol_parse_packet(dhcpol_t
* options
, const struct dhcp
* pkt
, int len
)
352 char rfc_magic
[4] = RFC_OPTIONS_MAGIC
;
354 dhcpol_init(options
); /* make sure it's empty */
356 if (len
< (sizeof(*pkt
) + RFC_MAGIC_SIZE
)) {
357 dprintf(("dhcp_options: packet is too short: %d < %d\n",
358 len
, (int)sizeof(*pkt
) + RFC_MAGIC_SIZE
));
361 if (bcmp(pkt
->dp_options
, rfc_magic
, RFC_MAGIC_SIZE
)) {
362 dprintf(("dhcp_options: missing magic number\n"));
365 if (dhcpol_parse_buffer(options
, pkt
->dp_options
+ RFC_MAGIC_SIZE
,
366 len
- sizeof(*pkt
) - RFC_MAGIC_SIZE
) == FALSE
)
368 { /* get overloaded options */
369 const uint8_t * overload
;
372 overload
= dhcpol_find(options
, dhcptag_option_overload_e
,
373 &overload_len
, NULL
);
374 if (overload
&& overload_len
== 1) { /* has overloaded options */
378 if (*overload
== DHCP_OVERLOAD_FILE
379 || *overload
== DHCP_OVERLOAD_BOTH
) {
380 if (dhcpol_parse_buffer(&extra
, pkt
->dp_file
,
381 sizeof(pkt
->dp_file
))) {
382 dhcpol_concat(options
, &extra
);
386 if (*overload
== DHCP_OVERLOAD_SNAME
387 || *overload
== DHCP_OVERLOAD_BOTH
) {
388 if (dhcpol_parse_buffer(&extra
, pkt
->dp_sname
,
389 sizeof(pkt
->dp_sname
))) {
390 dhcpol_concat(options
, &extra
);
399 #ifdef TEST_DHCP_OPTIONS
400 char test_empty
[] = {
405 char test_short
[] = {
410 char test_simple
[] = {
412 1, 4, 255, 255, 252, 0,
413 3, 4, 17, 202, 40, 1,
417 char test_vendor
[] = {
419 1, 4, 255, 255, 252, 0,
420 3, 4, 17, 202, 40, 1,
421 43, 6, 1, 4, 1, 2, 3, 4,
422 43, 6, 1, 4, 1, 2, 3, 4,
426 char test_no_end
[] = {
427 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
428 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x80,
429 0x00, 0x80, 0x00, 0x01, 0x04, 0xff, 0xff, 0xff,
430 0x00, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x06,
431 0x0c, 0x18, 0x1a, 0xa3, 0x21, 0x18, 0x1a, 0xa3,
432 0x20, 0x18, 0x5e, 0xa3, 0x21, 0x00, 0x00, 0x00,
433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
437 char test_no_magic
[] = {
447 struct test tests
[] = {
448 { "empty", test_empty
, sizeof(test_empty
), TRUE
},
449 { "simple", test_simple
, sizeof(test_simple
), TRUE
},
450 { "vendor", test_vendor
, sizeof(test_vendor
), TRUE
},
451 { "no_end", test_no_end
, sizeof(test_no_end
), TRUE
},
452 { "no magic", test_no_magic
, sizeof(test_no_magic
), FALSE
},
453 { "short", test_short
, sizeof(test_short
), FALSE
},
454 { NULL
, NULL
, 0, FALSE
},
458 static char buf
[2048];
465 struct dhcp
* pkt
= (struct dhcp
*)buf
;
467 dhcpol_init(&options
);
469 for (i
= 0; tests
[i
].name
; i
++) {
470 printf("\nTest %d: ", i
);
471 bcopy(tests
[i
].data
, pkt
->dp_options
, tests
[i
].len
);
472 if (dhcpol_parse_packet(&options
, pkt
,
473 sizeof(*pkt
) + tests
[i
].len
)
474 != tests
[i
].result
) {
475 printf("test '%s' FAILED\n", tests
[i
].name
);
478 printf("test '%s' PASSED\n", tests
[i
].name
);
480 dhcpol_free(&options
);
484 #endif /* TEST_DHCP_OPTIONS */