+/*
+ * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
/*
* dhcp_options.c
* - routines to parse and access dhcp options
* - imported from bootp project
*/
+#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/malloc.h>
-
#include <netinet/dhcp.h>
#include <netinet/dhcp_options.h>
+#ifndef TEST_DHCP_OPTIONS
+#include <libkern/libkern.h>
+
+#ifdef DHCP_DEBUG
+#define dprintf(x) printf x;
+#else /* !DHCP_DEBUG */
+#define dprintf(x)
+#endif /* DHCP_DEBUG */
+
static __inline__ void
my_free(void * ptr)
{
my_free(oldptr);
return (data);
}
+#else
+/*
+ * To build:
+ * xcrun -sdk macosx.internal cc -DTEST_DHCP_OPTIONS -o /tmp/dhcp_options dhcp_options.c -I ..
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#define my_free free
+#define my_malloc malloc
+#define my_realloc(ptr, old_size, new_size) realloc(ptr, new_size)
+#define dprintf(x) printf x;
+#endif
/*
* Functions: ptrlist_*
return (list->count);
}
-static void *
+static const void *
ptrlist_element(ptrlist_t * list, int i)
{
if (list->array == NULL)
list->array = my_malloc(sizeof(*list->array) * list->size);
}
else if (list->size == list->count) {
-#ifdef DEBUG
- printf("doubling %d to %d\n", list->size, list->size * 2);
-#endif DEBUG
+ dprintf(("doubling %d to %d\n", list->size, list->size * 2));
list->array = my_realloc(list->array,
sizeof(*list->array) * list->size,
sizeof(*list->array) * list->size * 2);
}
static boolean_t
-ptrlist_add(ptrlist_t * list, void * element)
+ptrlist_add(ptrlist_t * list, const void * element)
{
if (ptrlist_grow(list) == FALSE)
return (FALSE);
* Routines to parse/access existing options buffers.
*/
boolean_t
-dhcpol_add(dhcpol_t * list, void * element)
+dhcpol_add(dhcpol_t * list, const void * element)
{
return (ptrlist_add((ptrlist_t *)list, element));
}
return (ptrlist_count((ptrlist_t *)list));
}
-void *
+const void *
dhcpol_element(dhcpol_t * list, int i)
{
return (ptrlist_element((ptrlist_t *)list, i));
* the end tag.
*/
boolean_t
-dhcpol_parse_buffer(dhcpol_t * list, void * buffer, int length,
- unsigned char * err)
+dhcpol_parse_buffer(dhcpol_t * list, const void * buffer, int length)
{
int len;
- unsigned char * scan;
- unsigned char tag;
-
- if (err)
- err[0] = '\0';
+ const uint8_t * scan;
+ uint8_t tag;
dhcpol_init(list);
len = length;
tag = dhcptag_pad_e;
- for (scan = (unsigned char *)buffer; tag != dhcptag_end_e && len > 0; ) {
+ for (scan = (const uint8_t *)buffer;
+ tag != dhcptag_end_e && len > DHCP_TAG_OFFSET; ) {
tag = scan[DHCP_TAG_OFFSET];
switch (tag) {
case dhcptag_end_e:
- dhcpol_add(list, scan); /* remember that it was terminated */
+ /* remember that it was terminated */
+ dhcpol_add(list, scan);
scan++;
len--;
break;
scan++;
len--;
break;
- default: {
- unsigned char option_len = scan[DHCP_LEN_OFFSET];
-
- dhcpol_add(list, scan);
- len -= (option_len + 2);
- scan += (option_len + 2);
+ default:
+ if (len > DHCP_LEN_OFFSET) {
+ uint8_t option_len;
+
+ option_len = scan[DHCP_LEN_OFFSET];
+ dhcpol_add(list, scan);
+ len -= (option_len + DHCP_OPTION_OFFSET);
+ scan += (option_len + DHCP_OPTION_OFFSET);
+ }
+ else {
+ len = -1;
+ }
break;
- }
}
}
if (len < 0) {
/* ran off the end */
- if (err)
- sprintf(err, "parse failed near tag %d", tag);
+ dprintf(("dhcp_options: parse failed near tag %d\n", tag));
dhcpol_free(list);
return (FALSE);
}
* calls will retrieve the next occurence of the option.
* Before the first call, *start should be set to 0.
*/
-void *
+const void *
dhcpol_find(dhcpol_t * list, int tag, int * len_p, int * start)
{
int i = 0;
i = *start;
for (; i < dhcpol_count(list); i++) {
- unsigned char * option = dhcpol_element(list, i);
+ const uint8_t * option = dhcpol_element(list, i);
if (option[DHCP_TAG_OFFSET] == tag) {
if (len_p)
return (NULL);
}
-/*
- * Function: dhcpol_get
- *
- * Purpose:
- * Accumulate all occurences of the given option into a
- * malloc'd buffer, and return its length. Used to get
- * all occurrences of a particular option in a single
- * data area.
- * Note:
- * Use _FREE(val, M_TEMP) to free the returned data area.
- */
-void *
-dhcpol_get(dhcpol_t * list, int tag, int * len_p)
-{
- int i;
- char * data = NULL;
- int data_len = 0;
-
- if (tag == dhcptag_end_e || tag == dhcptag_pad_e)
- return (NULL);
-
- for (i = 0; i < dhcpol_count(list); i++) {
- unsigned char * option = dhcpol_element(list, i);
-
- if (option[DHCP_TAG_OFFSET] == tag) {
- int len = option[DHCP_LEN_OFFSET];
-
- if (data_len == 0) {
- data = my_malloc(len);
- }
- else {
- data = my_realloc(data, data_len, data_len + len);
- }
- bcopy(option + DHCP_OPTION_OFFSET, data + data_len, len);
- data_len += len;
- }
- }
- *len_p = data_len;
- return (data);
-}
-
/*
* Function: dhcpol_parse_packet
*
* then parses pkt->dp_sname if specified.
*/
boolean_t
-dhcpol_parse_packet(dhcpol_t * options, struct dhcp * pkt, int len,
- unsigned char * err)
+dhcpol_parse_packet(dhcpol_t * options, const struct dhcp * pkt, int len)
{
char rfc_magic[4] = RFC_OPTIONS_MAGIC;
dhcpol_init(options); /* make sure it's empty */
- if (err)
- err[0] = '\0';
-
if (len < (sizeof(*pkt) + RFC_MAGIC_SIZE)) {
- if (err) {
- sprintf(err, "packet is too short: %d < %d",
- len, (int)sizeof(*pkt) + RFC_MAGIC_SIZE);
- }
+ dprintf(("dhcp_options: packet is too short: %d < %d\n",
+ len, (int)sizeof(*pkt) + RFC_MAGIC_SIZE));
return (FALSE);
}
if (bcmp(pkt->dp_options, rfc_magic, RFC_MAGIC_SIZE)) {
- if (err)
- sprintf(err, "missing magic number");
+ dprintf(("dhcp_options: missing magic number\n"));
return (FALSE);
}
if (dhcpol_parse_buffer(options, pkt->dp_options + RFC_MAGIC_SIZE,
- len - sizeof(*pkt) - RFC_MAGIC_SIZE, err) == FALSE)
+ len - sizeof(*pkt) - RFC_MAGIC_SIZE) == FALSE)
return (FALSE);
{ /* get overloaded options */
- unsigned char * overload;
+ const uint8_t * overload;
int overload_len;
- overload = (unsigned char *)
- dhcpol_find(options, dhcptag_option_overload_e,
- &overload_len, NULL);
+ overload = dhcpol_find(options, dhcptag_option_overload_e,
+ &overload_len, NULL);
if (overload && overload_len == 1) { /* has overloaded options */
dhcpol_t extra;
-
+
dhcpol_init(&extra);
if (*overload == DHCP_OVERLOAD_FILE
|| *overload == DHCP_OVERLOAD_BOTH) {
if (dhcpol_parse_buffer(&extra, pkt->dp_file,
- sizeof(pkt->dp_file), NULL)) {
+ sizeof(pkt->dp_file))) {
dhcpol_concat(options, &extra);
dhcpol_free(&extra);
}
if (*overload == DHCP_OVERLOAD_SNAME
|| *overload == DHCP_OVERLOAD_BOTH) {
if (dhcpol_parse_buffer(&extra, pkt->dp_sname,
- sizeof(pkt->dp_sname), NULL)) {
+ sizeof(pkt->dp_sname))) {
dhcpol_concat(options, &extra);
dhcpol_free(&extra);
}
return (TRUE);
}
-/*
- * Function: dhcpol_parse_vendor
- *
- * Purpose:
- * Given a set of options, find the vendor specific option(s)
- * and parse all of them into a single option list.
- *
- * Return value:
- * TRUE if vendor specific options existed and were parsed succesfully,
- * FALSE otherwise.
- */
-boolean_t
-dhcpol_parse_vendor(dhcpol_t * vendor, dhcpol_t * options,
- unsigned char * err)
-{
- dhcpol_t extra;
- boolean_t ret = FALSE;
- int start = 0;
-
- if (err)
- err[0] = '\0';
-
- dhcpol_init(vendor);
- dhcpol_init(&extra);
-
- for (;;) {
- void * data;
- int len;
-
- data = dhcpol_find(options, dhcptag_vendor_specific_e, &len, &start);
- if (data == NULL) {
- break; /* out of for */
- }
-
- if (dhcpol_parse_buffer(&extra, data, len, err) == FALSE) {
- goto failed;
- }
-
- if (dhcpol_concat(vendor, &extra) == FALSE) {
- if (err)
- sprintf(err, "dhcpol_concat() failed at %d\n", start);
- goto failed;
- }
- dhcpol_free(&extra);
- ret = TRUE;
- }
- if (ret == FALSE) {
- if (err)
- strcpy(err, "missing vendor specific options");
- }
- return (ret);
-
- failed:
- dhcpol_free(vendor);
- dhcpol_free(&extra);
- return (FALSE);
-}
-
#ifdef TEST_DHCP_OPTIONS
char test_empty[] = {
99, 130, 83, 99,
255,
};
+char test_short[] = {
+ 99, 130, 83, 99,
+ 1,
+};
+
char test_simple[] = {
99, 130, 83, 99,
1, 4, 255, 255, 252, 0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-char test_too_short[] = {
+char test_no_magic[] = {
0x1
};
struct test {
{ "simple", test_simple, sizeof(test_simple), TRUE },
{ "vendor", test_vendor, sizeof(test_vendor), TRUE },
{ "no_end", test_no_end, sizeof(test_no_end), TRUE },
- { "too_short", test_too_short, sizeof(test_too_short), FALSE },
+ { "no magic", test_no_magic, sizeof(test_no_magic), FALSE },
+ { "short", test_short, sizeof(test_short), FALSE },
{ NULL, NULL, 0, FALSE },
};
{
int i;
dhcpol_t options;
- char error[256];
struct dhcp * pkt = (struct dhcp *)buf;
dhcpol_init(&options);
printf("\nTest %d: ", i);
bcopy(tests[i].data, pkt->dp_options, tests[i].len);
if (dhcpol_parse_packet(&options, pkt,
- sizeof(*pkt) + tests[i].len,
- error) != tests[i].result) {
+ sizeof(*pkt) + tests[i].len)
+ != tests[i].result) {
printf("test '%s' FAILED\n", tests[i].name);
- if (tests[i].result == TRUE) {
- printf("error message returned was %s\n", error);
- }
}
else {
printf("test '%s' PASSED\n", tests[i].name);
- if (tests[i].result == FALSE) {
- printf("error message returned was %s\n", error);
- }
}
dhcpol_free(&options);
}
exit(0);
}
-#endif TEST_DHCP_OPTIONS
+#endif /* TEST_DHCP_OPTIONS */