2  * Copyright (c) 2002-2007 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 <libkern/libkern.h> 
  50 #include <netinet/dhcp.h> 
  51 #include <netinet/dhcp_options.h> 
  54 #define dprintf(x) printf x; 
  55 #else   /* !DHCP_DEBUG */ 
  57 #endif  /* DHCP_DEBUG */ 
  59 static __inline__ 
void 
  65 static __inline__ 
void * 
  69     MALLOC(data
, void *, size
, M_TEMP
, M_WAITOK
); 
  73 static __inline__ 
void * 
  74 my_realloc(void * oldptr
, int oldsize
, int newsize
) 
  78     MALLOC(data
, void *, newsize
, M_TEMP
, M_WAITOK
); 
  79     bcopy(oldptr
, data
, oldsize
); 
  85  * Functions: ptrlist_* 
  87  *   A dynamically growable array of pointers. 
  90 #define PTRLIST_NUMBER          16 
  93 ptrlist_init(ptrlist_t 
* list
) 
  95     bzero(list
, sizeof(*list
)); 
 100 ptrlist_free(ptrlist_t 
* list
) 
 103         my_free(list
->array
); 
 109 ptrlist_count(ptrlist_t 
* list
) 
 111     if (list 
== NULL 
|| list
->array 
== NULL
) 
 114     return (list
->count
); 
 118 ptrlist_element(ptrlist_t 
* list
, int i
) 
 120     if (list
->array 
== NULL
) 
 123         return (list
->array
[i
]); 
 129 ptrlist_grow(ptrlist_t 
* list
) 
 131     if (list
->array 
== NULL
) { 
 133             list
->size 
= PTRLIST_NUMBER
; 
 135         list
->array 
= my_malloc(sizeof(*list
->array
) * list
->size
); 
 137     else if (list
->size 
== list
->count
) { 
 138         dprintf(("doubling %d to %d\n", list
->size
, list
->size 
* 2)); 
 139         list
->array 
= my_realloc(list
->array
,  
 140                                  sizeof(*list
->array
) * list
->size
, 
 141                                  sizeof(*list
->array
) * list
->size 
* 2); 
 144     if (list
->array 
== NULL
) 
 150 ptrlist_add(ptrlist_t 
* list
, const void * element
) 
 152     if (ptrlist_grow(list
) == FALSE
) 
 155     list
->array
[list
->count
++] = element
; 
 159 /* concatenates extra onto list */ 
 161 ptrlist_concat(ptrlist_t 
* list
, ptrlist_t 
* extra
) 
 163     if (extra
->count 
== 0) 
 166     if ((extra
->count 
+ list
->count
) > list
->size
) { 
 167         int old_size 
= list
->size
; 
 169         list
->size 
= extra
->count 
+ list
->count
; 
 170         if (list
->array 
== NULL
) 
 171             list
->array 
= my_malloc(sizeof(*list
->array
) * list
->size
); 
 173             list
->array 
= my_realloc(list
->array
, old_size
, 
 174                                      sizeof(*list
->array
) * list
->size
); 
 176     if (list
->array 
== NULL
) 
 178     bcopy(extra
->array
, list
->array 
+ list
->count
,  
 179           extra
->count 
* sizeof(*list
->array
)); 
 180     list
->count 
+= extra
->count
; 
 186  * Functions: dhcpol_*  
 189  *   Routines to parse/access existing options buffers. 
 192 dhcpol_add(dhcpol_t 
* list
, const void * element
) 
 194     return (ptrlist_add((ptrlist_t 
*)list
, element
)); 
 198 dhcpol_count(dhcpol_t 
* list
) 
 200     return (ptrlist_count((ptrlist_t 
*)list
)); 
 204 dhcpol_element(dhcpol_t 
* list
, int i
) 
 206     return (ptrlist_element((ptrlist_t 
*)list
, i
)); 
 210 dhcpol_init(dhcpol_t 
* list
) 
 212     ptrlist_init((ptrlist_t 
*)list
); 
 216 dhcpol_free(dhcpol_t 
* list
) 
 218     ptrlist_free((ptrlist_t 
*)list
); 
 222 dhcpol_concat(dhcpol_t 
* list
, dhcpol_t 
* extra
) 
 224     return (ptrlist_concat((ptrlist_t 
*)list
, (ptrlist_t 
*)extra
)); 
 228  * Function: dhcpol_parse_buffer 
 231  *   Parse the given buffer into DHCP options, returning the 
 232  *   list of option pointers in the given dhcpol_t. 
 233  *   Parsing continues until we hit the end of the buffer or 
 237 dhcpol_parse_buffer(dhcpol_t 
* list
, const void * buffer
, int length
) 
 240     const uint8_t *     scan
; 
 247     for (scan 
= (const uint8_t *)buffer
; tag 
!= dhcptag_end_e 
&& len 
> 0; ) { 
 249         tag 
= scan
[DHCP_TAG_OFFSET
]; 
 253               /* remember that it was terminated */ 
 254               dhcpol_add(list
, scan
); 
 258           case dhcptag_pad_e
: /* ignore pad */ 
 263               uint8_t   option_len 
= scan
[DHCP_LEN_OFFSET
]; 
 265               dhcpol_add(list
, scan
); 
 266               len 
-= (option_len 
+ 2); 
 267               scan 
+= (option_len 
+ 2); 
 273         /* ran off the end */ 
 274         dprintf(("dhcp_options: parse failed near tag %d", tag
)); 
 282  * Function: dhcpol_find 
 285  *   Finds the first occurence of the given option, and returns its 
 286  *   length and the option data pointer. 
 288  *   The optional start parameter allows this function to  
 289  *   return the next start point so that successive 
 290  *   calls will retrieve the next occurence of the option. 
 291  *   Before the first call, *start should be set to 0. 
 294 dhcpol_find(dhcpol_t 
* list
, int tag
, int * len_p
, int * start
) 
 298     if (tag 
== dhcptag_end_e 
|| tag 
== dhcptag_pad_e
) 
 304     for (; i 
< dhcpol_count(list
); i
++) { 
 305         const uint8_t *         option 
= dhcpol_element(list
, i
); 
 307         if (option
[DHCP_TAG_OFFSET
] == tag
) { 
 309                 *len_p 
= option
[DHCP_LEN_OFFSET
]; 
 312             return (option 
+ DHCP_OPTION_OFFSET
); 
 320  * Function: dhcpol_get 
 323  *   Accumulate all occurences of the given option into a 
 324  *   malloc'd buffer, and return its length.  Used to get 
 325  *   all occurrences of a particular option in a single 
 328  *   Use _FREE(val, M_TEMP) to free the returned data area. 
 331 dhcpol_get(dhcpol_t 
* list
, int tag
, int * len_p
) 
 337     if (tag 
== dhcptag_end_e 
|| tag 
== dhcptag_pad_e
) 
 340     for (i 
= 0; i 
< dhcpol_count(list
); i
++) { 
 341         const uint8_t *         option 
= dhcpol_element(list
, i
); 
 343         if (option
[DHCP_TAG_OFFSET
] == tag
) { 
 344             int len 
= option
[DHCP_LEN_OFFSET
]; 
 347                 data 
= my_malloc(len
); 
 350                 data 
= my_realloc(data
, data_len
, data_len 
+ len
); 
 352                 FIX ME
: test data NULL
 
 353             bcopy(option 
+ DHCP_OPTION_OFFSET
, data 
+ data_len
, len
); 
 363  * Function: dhcpol_parse_packet 
 366  *    Parse the option areas in the DHCP packet. 
 367  *    Verifies that the packet has the right magic number, 
 368  *    then parses and accumulates the option areas. 
 369  *    First the pkt->dp_options is parsed.  If that contains 
 370  *    the overload option, it parses pkt->dp_file if specified, 
 371  *    then parses pkt->dp_sname if specified. 
 374 dhcpol_parse_packet(dhcpol_t 
* options
, const struct dhcp 
* pkt
, int len
) 
 376     char                rfc_magic
[4] = RFC_OPTIONS_MAGIC
; 
 378     dhcpol_init(options
);       /* make sure it's empty */ 
 380     if (len 
< (sizeof(*pkt
) + RFC_MAGIC_SIZE
)) { 
 381         dprintf(("dhcp_options: packet is too short: %d < %d\n", 
 382                  len
, (int)sizeof(*pkt
) + RFC_MAGIC_SIZE
)); 
 385     if (bcmp(pkt
->dp_options
, rfc_magic
, RFC_MAGIC_SIZE
)) { 
 386         dprintf(("dhcp_options: missing magic number\n")); 
 389     if (dhcpol_parse_buffer(options
, pkt
->dp_options 
+ RFC_MAGIC_SIZE
, 
 390                             len 
- sizeof(*pkt
) - RFC_MAGIC_SIZE
) == FALSE
) 
 392     { /* get overloaded options */ 
 393         const uint8_t * overload
; 
 396         overload 
= dhcpol_find(options
, dhcptag_option_overload_e
,  
 397                                &overload_len
, NULL
); 
 398         if (overload 
&& overload_len 
== 1) { /* has overloaded options */ 
 402             if (*overload 
== DHCP_OVERLOAD_FILE
 
 403                 || *overload 
== DHCP_OVERLOAD_BOTH
) { 
 404                 if (dhcpol_parse_buffer(&extra
, pkt
->dp_file
,  
 405                                         sizeof(pkt
->dp_file
))) { 
 406                     dhcpol_concat(options
, &extra
); 
 410             if (*overload 
== DHCP_OVERLOAD_SNAME
 
 411                 || *overload 
== DHCP_OVERLOAD_BOTH
) { 
 412                 if (dhcpol_parse_buffer(&extra
, pkt
->dp_sname
,  
 413                                         sizeof(pkt
->dp_sname
))) { 
 414                     dhcpol_concat(options
, &extra
); 
 427  *   Types and functions to create new dhcp option areas. 
 431  * Function: dhcpoa_{init_common, init_no_end, init} 
 434  *   Initialize an option area structure so that it can be used 
 435  *   in calling the dhcpoa_* routines. 
 438 dhcpoa_init_common(dhcpoa_t 
* oa_p
, void * buffer
, int size
, int reserve
) 
 440     bzero(oa_p
, sizeof(*oa_p
)); 
 441     oa_p
->oa_buffer 
= buffer
; 
 442     oa_p
->oa_size 
= size
; 
 443     oa_p
->oa_reserve 
= reserve
; 
 447 dhcpoa_init_no_end(dhcpoa_t 
* oa_p
, void * buffer
, int size
) 
 449     dhcpoa_init_common(oa_p
, buffer
, size
, 0); 
 454 dhcpoa_size(dhcpoa_t 
* oa_p
) 
 456     return (oa_p
->oa_size
); 
 460 dhcpoa_init(dhcpoa_t 
* oa_p
, void * buffer
, int size
) 
 462     /* initialize the area, reserve space for the end tag */ 
 463     dhcpoa_init_common(oa_p
, buffer
, size
, 1); 
 467  * Function: dhcpoa_add 
 470  *   Add an option to the option area. 
 473 dhcpoa_add(dhcpoa_t 
* oa_p
, dhcptag_t tag
, int len
, const void * option
) 
 475     if (len 
> DHCP_OPTION_SIZE_MAX
) { 
 476         dprintf(("tag %d option %d > %d\n", tag
, len
, DHCP_OPTION_SIZE_MAX
)); 
 477         return (dhcpoa_failed_e
); 
 480     if (oa_p
->oa_end_tag
) { 
 481         dprintf(("attempt to add data after end tag\n")); 
 482         return (dhcpoa_failed_e
); 
 487         if ((oa_p
->oa_offset 
+ 1) > oa_p
->oa_size
) { 
 488             /* this can't happen since we're careful to leave space */ 
 489             dprintf(("can't add end tag %d > %d\n", 
 490                      oa_p
->oa_offset 
+ oa_p
->oa_reserve
, oa_p
->oa_size
)); 
 491             return (dhcpoa_failed_e
); 
 493         ((uint8_t *)oa_p
->oa_buffer
)[oa_p
->oa_offset 
+ DHCP_TAG_OFFSET
] = tag
; 
 495         oa_p
->oa_end_tag 
= 1; 
 500         if ((oa_p
->oa_offset 
+ oa_p
->oa_reserve 
+ 1) > oa_p
->oa_size
) { 
 501             dprintf(("can't add pad tag %d > %d\n", 
 502                      oa_p
->oa_offset 
+ oa_p
->oa_reserve 
+ 1, oa_p
->oa_size
)); 
 503             return (dhcpoa_full_e
); 
 505         ((uint8_t *)oa_p
->oa_buffer
)[oa_p
->oa_offset 
+ DHCP_TAG_OFFSET
] = tag
; 
 511         if ((oa_p
->oa_offset 
+ len 
+ 2 + oa_p
->oa_reserve
) > oa_p
->oa_size
) { 
 512             dprintf(("can't add tag %d (%d > %d)\n", tag
, 
 513                      oa_p
->oa_offset 
+ len 
+ 2 + oa_p
->oa_reserve
,  
 515             return (dhcpoa_full_e
); 
 517         ((uint8_t *)oa_p
->oa_buffer
)[oa_p
->oa_offset 
+ DHCP_TAG_OFFSET
] = tag
; 
 518         ((uint8_t *)oa_p
->oa_buffer
)[oa_p
->oa_offset 
+ DHCP_LEN_OFFSET
] = (uint8_t)len
; 
 520             memcpy(oa_p
->oa_buffer 
+ (DHCP_OPTION_OFFSET 
+ oa_p
->oa_offset
), 
 523         oa_p
->oa_offset 
+= len 
+ DHCP_OPTION_OFFSET
; 
 526     oa_p
->oa_option_count
++; 
 527     return (dhcpoa_success_e
); 
 531  * Function: dhcpoa_add_dhcpmsg 
 534  *   Add a dhcp message option to the option area. 
 537 dhcpoa_add_dhcpmsg(dhcpoa_t 
* oa_p
, dhcp_msgtype_t msgtype
) 
 539     return (dhcpoa_add(oa_p
, dhcptag_dhcp_message_type_e
, 
 540                        sizeof(msgtype
), &msgtype
)); 
 544 dhcpoa_used(dhcpoa_t 
* oa_p
) 
 546     return (oa_p
->oa_offset
); 
 550 dhcpoa_freespace(dhcpoa_t 
* oa_p
) 
 554     freespace 
= oa_p
->oa_size 
- oa_p
->oa_offset 
- oa_p
->oa_reserve
; 
 562 dhcpoa_count(dhcpoa_t 
* oa_p
) 
 564     return (oa_p
->oa_option_count
); 
 568 dhcpoa_buffer(dhcpoa_t 
* oa_p
)  
 570     return (oa_p
->oa_buffer
); 
 574 #ifdef TEST_DHCP_OPTIONS 
 575 char test_empty
[] = { 
 580 char test_simple
[] = { 
 582     1, 4, 255, 255, 252, 0, 
 583     3, 4, 17, 202, 40, 1, 
 587 char test_vendor
[] = { 
 589     1, 4, 255, 255, 252, 0, 
 590     3, 4, 17, 202, 40, 1, 
 591     43, 6, 1, 4, 1, 2, 3, 4, 
 592     43, 6, 1, 4, 1, 2, 3, 4, 
 596 char test_no_end
[] = { 
 597     0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,  
 598     0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x80, 
 599     0x00, 0x80, 0x00, 0x01, 0x04, 0xff, 0xff, 0xff, 
 600     0x00, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x06, 
 601     0x0c, 0x18, 0x1a, 0xa3, 0x21, 0x18, 0x1a, 0xa3, 
 602     0x20, 0x18, 0x5e, 0xa3, 0x21, 0x00, 0x00, 0x00, 
 603     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 604     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 
 607 char test_too_short
[] = { 
 617 struct test tests
[] = { 
 618     { "empty", test_empty
, sizeof(test_empty
), TRUE 
}, 
 619     { "simple", test_simple
, sizeof(test_simple
), TRUE 
}, 
 620     { "vendor", test_vendor
, sizeof(test_vendor
), TRUE 
}, 
 621     { "no_end", test_no_end
, sizeof(test_no_end
), TRUE 
}, 
 622     { "too_short", test_too_short
, sizeof(test_too_short
), FALSE 
}, 
 623     { NULL
, NULL
, 0, FALSE 
}, 
 627 static char buf
[2048]; 
 634     struct dhcp 
* pkt 
= (struct dhcp 
*)buf
; 
 636     dhcpol_init(&options
); 
 638     for (i 
= 0; tests
[i
].name
; i
++) { 
 639         printf("\nTest %d: ", i
); 
 640         bcopy(tests
[i
].data
, pkt
->dp_options
, tests
[i
].len
); 
 641         if (dhcpol_parse_packet(&options
, pkt
,  
 642                                 sizeof(*pkt
) + tests
[i
].len
) 
 643             != tests
[i
].result
) { 
 644             printf("test '%s' FAILED\n", tests
[i
].name
); 
 647             printf("test '%s' PASSED\n", tests
[i
].name
); 
 649         dhcpol_free(&options
); 
 653 #endif /* TEST_DHCP_OPTIONS */