]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet/dhcp_options.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / netinet / dhcp_options.c
CommitLineData
2d21ac55 1/*
cb323159 2 * Copyright (c) 2002-2019 Apple Inc. All rights reserved.
2d21ac55
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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.
0a7de745 25 *
2d21ac55
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
9bccf70c
A
28/*
29 * dhcp_options.c
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
34 */
35
0a7de745 36/*
9bccf70c
A
37 * Modification History
38 *
39 * March 15, 2002 Dieter Siegmund (dieter@apple)
40 * - imported from bootp project
41 */
42
2d21ac55 43#include <string.h>
9bccf70c
A
44#include <sys/types.h>
45#include <sys/param.h>
46#include <netinet/in.h>
47#include <sys/malloc.h>
9bccf70c
A
48#include <netinet/dhcp.h>
49#include <netinet/dhcp_options.h>
50
ecc0ceb4
A
51#ifndef TEST_DHCP_OPTIONS
52#include <libkern/libkern.h>
53
0a7de745
A
54#ifdef DHCP_DEBUG
55#define dprintf(x) printf x;
56#else /* !DHCP_DEBUG */
57#define dprintf(x)
58#endif /* DHCP_DEBUG */
2d21ac55 59
9bccf70c
A
60static __inline__ void
61my_free(void * ptr)
62{
0a7de745 63 _FREE(ptr, M_TEMP);
9bccf70c
A
64}
65
66static __inline__ void *
67my_malloc(int size)
68{
0a7de745
A
69 void * data;
70 MALLOC(data, void *, size, M_TEMP, M_WAITOK);
71 return data;
9bccf70c
A
72}
73
74static __inline__ void *
75my_realloc(void * oldptr, int oldsize, int newsize)
76{
0a7de745 77 void * data;
9bccf70c 78
0a7de745
A
79 MALLOC(data, void *, newsize, M_TEMP, M_WAITOK);
80 bcopy(oldptr, data, oldsize);
81 my_free(oldptr);
82 return data;
9bccf70c 83}
ecc0ceb4
A
84#else
85/*
86 * To build:
0a7de745 87 * xcrun -sdk macosx.internal cc -DTEST_DHCP_OPTIONS -o /tmp/dhcp_options dhcp_options.c -I ..
ecc0ceb4
A
88 */
89#include <stdlib.h>
90#include <unistd.h>
91#include <stdio.h>
0a7de745 92#define my_free free
ecc0ceb4
A
93#define my_malloc malloc
94#define my_realloc(ptr, old_size, new_size) realloc(ptr, new_size)
0a7de745 95#define dprintf(x) printf x;
ecc0ceb4 96#endif
9bccf70c
A
97
98/*
99 * Functions: ptrlist_*
100 * Purpose:
101 * A dynamically growable array of pointers.
102 */
103
0a7de745 104#define PTRLIST_NUMBER 16
9bccf70c
A
105
106static void
107ptrlist_init(ptrlist_t * list)
108{
0a7de745
A
109 bzero(list, sizeof(*list));
110 return;
9bccf70c
A
111}
112
113static void
114ptrlist_free(ptrlist_t * list)
115{
0a7de745
A
116 if (list->array) {
117 my_free(list->array);
118 }
119 ptrlist_init(list);
120 return;
9bccf70c
A
121}
122
123static int
124ptrlist_count(ptrlist_t * list)
125{
0a7de745
A
126 if (list == NULL || list->array == NULL) {
127 return 0;
128 }
9bccf70c 129
0a7de745 130 return list->count;
9bccf70c
A
131}
132
2d21ac55 133static const void *
9bccf70c
A
134ptrlist_element(ptrlist_t * list, int i)
135{
0a7de745
A
136 if (list->array == NULL) {
137 return NULL;
138 }
139 if (i < list->count) {
140 return list->array[i];
141 }
142 return NULL;
9bccf70c
A
143}
144
145
146static boolean_t
147ptrlist_grow(ptrlist_t * list)
148{
0a7de745
A
149 if (list->array == NULL) {
150 if (list->size == 0) {
151 list->size = PTRLIST_NUMBER;
152 }
153 list->count = 0;
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);
160 list->size *= 2;
161 }
162 if (list->array == NULL) {
163 return FALSE;
164 }
165 return TRUE;
9bccf70c
A
166}
167
168static boolean_t
2d21ac55 169ptrlist_add(ptrlist_t * list, const void * element)
9bccf70c 170{
0a7de745
A
171 if (ptrlist_grow(list) == FALSE) {
172 return FALSE;
173 }
9bccf70c 174
0a7de745
A
175 list->array[list->count++] = element;
176 return TRUE;
9bccf70c
A
177}
178
179/* concatenates extra onto list */
180static boolean_t
181ptrlist_concat(ptrlist_t * list, ptrlist_t * extra)
182{
0a7de745
A
183 if (extra->count == 0) {
184 return TRUE;
185 }
186
187 if ((extra->count + list->count) > list->size) {
188 int old_size = list->size;
189
190 list->size = extra->count + list->count;
191 if (list->array == NULL) {
192 list->array = my_malloc(sizeof(*list->array) * list->size);
193 } else {
194 list->array = my_realloc(list->array, old_size,
195 sizeof(*list->array) * list->size);
196 }
197 }
198 if (list->array == NULL) {
199 return FALSE;
200 }
201 bcopy(extra->array, list->array + list->count,
202 extra->count * sizeof(*list->array));
203 list->count += extra->count;
204 return TRUE;
9bccf70c
A
205}
206
207
208/*
0a7de745 209 * Functions: dhcpol_*
9bccf70c
A
210 *
211 * Purpose:
212 * Routines to parse/access existing options buffers.
213 */
214boolean_t
2d21ac55 215dhcpol_add(dhcpol_t * list, const void * element)
9bccf70c 216{
0a7de745 217 return ptrlist_add((ptrlist_t *)list, element);
9bccf70c
A
218}
219
220int
221dhcpol_count(dhcpol_t * list)
222{
0a7de745 223 return ptrlist_count((ptrlist_t *)list);
9bccf70c
A
224}
225
2d21ac55 226const void *
9bccf70c
A
227dhcpol_element(dhcpol_t * list, int i)
228{
0a7de745 229 return ptrlist_element((ptrlist_t *)list, i);
9bccf70c
A
230}
231
232void
233dhcpol_init(dhcpol_t * list)
234{
0a7de745 235 ptrlist_init((ptrlist_t *)list);
9bccf70c
A
236}
237
238void
239dhcpol_free(dhcpol_t * list)
240{
0a7de745 241 ptrlist_free((ptrlist_t *)list);
9bccf70c
A
242}
243
244boolean_t
245dhcpol_concat(dhcpol_t * list, dhcpol_t * extra)
246{
0a7de745 247 return ptrlist_concat((ptrlist_t *)list, (ptrlist_t *)extra);
9bccf70c
A
248}
249
250/*
251 * Function: dhcpol_parse_buffer
252 *
253 * Purpose:
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
257 * the end tag.
258 */
259boolean_t
2d21ac55 260dhcpol_parse_buffer(dhcpol_t * list, const void * buffer, int length)
9bccf70c 261{
0a7de745
A
262 int len;
263 const uint8_t * scan;
264 uint8_t tag;
265
266 dhcpol_init(list);
267
268 len = length;
269 tag = dhcptag_pad_e;
270 for (scan = (const uint8_t *)buffer;
271 tag != dhcptag_end_e && len > DHCP_TAG_OFFSET;) {
272 tag = scan[DHCP_TAG_OFFSET];
273
274 switch (tag) {
275 case dhcptag_end_e:
276 /* remember that it was terminated */
277 dhcpol_add(list, scan);
278 scan++;
279 len--;
280 break;
281 case dhcptag_pad_e: /* ignore pad */
282 scan++;
283 len--;
284 break;
285 default:
286 if (len > DHCP_LEN_OFFSET) {
287 uint8_t option_len;
288
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);
293 } else {
294 len = -1;
295 }
296 break;
297 }
298 }
299 if (len < 0) {
300 /* ran off the end */
301 dprintf(("dhcp_options: parse failed near tag %d\n", tag));
302 dhcpol_free(list);
303 return FALSE;
9bccf70c 304 }
0a7de745 305 return TRUE;
9bccf70c
A
306}
307
308/*
309 * Function: dhcpol_find
310 *
311 * Purpose:
312 * Finds the first occurence of the given option, and returns its
313 * length and the option data pointer.
314 *
0a7de745 315 * The optional start parameter allows this function to
9bccf70c
A
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.
319 */
2d21ac55 320const void *
9bccf70c
A
321dhcpol_find(dhcpol_t * list, int tag, int * len_p, int * start)
322{
0a7de745
A
323 int i = 0;
324
325 if (tag == dhcptag_end_e || tag == dhcptag_pad_e) {
326 return NULL;
327 }
328
329 if (start) {
330 i = *start;
331 }
332
333 for (; i < dhcpol_count(list); i++) {
334 const uint8_t * option = dhcpol_element(list, i);
335
336 if (option[DHCP_TAG_OFFSET] == tag) {
337 if (len_p) {
338 *len_p = option[DHCP_LEN_OFFSET];
339 }
340 if (start) {
341 *start = i + 1;
342 }
343 return option + DHCP_OPTION_OFFSET;
344 }
9bccf70c 345 }
0a7de745 346 return NULL;
9bccf70c
A
347}
348
9bccf70c
A
349/*
350 * Function: dhcpol_parse_packet
351 *
352 * Purpose:
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.
359 */
360boolean_t
2d21ac55 361dhcpol_parse_packet(dhcpol_t * options, const struct dhcp * pkt, int len)
9bccf70c 362{
0a7de745
A
363 char rfc_magic[4] = RFC_OPTIONS_MAGIC;
364
365 dhcpol_init(options); /* make sure it's empty */
366
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));
370 return FALSE;
371 }
372 if (bcmp(pkt->dp_options, rfc_magic, RFC_MAGIC_SIZE)) {
373 dprintf(("dhcp_options: missing magic number\n"));
374 return FALSE;
375 }
376 if (dhcpol_parse_buffer(options, pkt->dp_options + RFC_MAGIC_SIZE,
377 len - sizeof(*pkt) - RFC_MAGIC_SIZE) == FALSE) {
378 return FALSE;
379 }
380 { /* get overloaded options */
381 const uint8_t * overload;
382 int overload_len;
383
384 overload = dhcpol_find(options, dhcptag_option_overload_e,
385 &overload_len, NULL);
386 if (overload && overload_len == 1) { /* has overloaded options */
387 dhcpol_t extra;
388
389 dhcpol_init(&extra);
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);
395 dhcpol_free(&extra);
396 }
397 }
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);
403 dhcpol_free(&extra);
404 }
405 }
9bccf70c 406 }
9bccf70c 407 }
0a7de745 408 return TRUE;
9bccf70c
A
409}
410
9bccf70c
A
411#ifdef TEST_DHCP_OPTIONS
412char test_empty[] = {
0a7de745
A
413 99, 130, 83, 99,
414 255,
9bccf70c
A
415};
416
ecc0ceb4 417char test_short[] = {
0a7de745
A
418 99, 130, 83, 99,
419 1,
ecc0ceb4
A
420};
421
9bccf70c 422char test_simple[] = {
0a7de745
A
423 99, 130, 83, 99,
424 1, 4, 255, 255, 252, 0,
425 3, 4, 17, 202, 40, 1,
426 255,
9bccf70c
A
427};
428
429char test_vendor[] = {
0a7de745
A
430 99, 130, 83, 99,
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,
435 255,
9bccf70c
A
436};
437
438char test_no_end[] = {
0a7de745
A
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
9bccf70c
A
447};
448
ecc0ceb4 449char test_no_magic[] = {
0a7de745 450 0x1
9bccf70c
A
451};
452struct test {
0a7de745
A
453 char * name;
454 char * data;
455 int len;
456 boolean_t result;
9bccf70c
A
457};
458
459struct test tests[] = {
cb323159
A
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 },
9bccf70c
A
467};
468
469
470static char buf[2048];
471
472int
cb323159 473main(void)
9bccf70c 474{
0a7de745
A
475 int i;
476 dhcpol_t options;
477 struct dhcp * pkt = (struct dhcp *)buf;
478
479 dhcpol_init(&options);
480
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);
488 } else {
489 printf("test '%s' PASSED\n", tests[i].name);
490 }
491 dhcpol_free(&options);
9bccf70c 492 }
0a7de745 493 exit(0);
9bccf70c 494}
55e303ae 495#endif /* TEST_DHCP_OPTIONS */