1/*
2 * Copyright (c) 2009-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * DHCPv6Options.c
26 * - definitions and API's to handle DHCPv6 options
27 */
28
29/*
30 * Modification History
31 *
32 * September 18, 2009		Dieter Siegmund (dieter@apple.com)
33 * - created
34 */
35
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <stddef.h>
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <net/if.h>
44#include <arpa/inet.h>
45#include <mach/boolean.h>
46#include <string.h>
47#include <errno.h>
48#include "DHCPv6.h"
49#include "DHCPv6Options.h"
50#include "ptrlist.h"
51#include "util.h"
52#include "DNSNameList.h"
53#include "cfutil.h"
54#include <SystemConfiguration/SCPrivate.h>
55
56STATIC void
57DHCPv6OptionIA_NAPrintLevelToString(CFMutableStringRef str,
58				    const DHCPv6OptionIA_NARef ia_na,
59				    int ia_na_len, int level);
60
61STATIC void
62DHCPv6OptionIAADDRPrintLevelToString(CFMutableStringRef str,
63				     DHCPv6OptionIAADDRRef ia_addr,
64				     int ia_addr_len, int level);
65
66STATIC void
67DHCPv6OptionSTATUS_CODEPrintToString(CFMutableStringRef str,
68				     DHCPv6OptionSTATUS_CODERef status_p,
69				     int status_len);
70
71PRIVATE_EXTERN DHCPv6OptionType
72DHCPv6OptionCodeGetType(int option_code)
73{
74    DHCPv6OptionType	type = kDHCPv6OptionTypeUnknown;
75
76    switch (option_code) {
77    case kDHCPv6OPTION_CLIENTID:
78    case kDHCPv6OPTION_SERVERID:
79	type = kDHCPv6OptionTypeDUID;
80	break;
81    case kDHCPv6OPTION_ORO:
82	type = kDHCPv6OptionTypeUInt16;
83	break;
84    case kDHCPv6OPTION_ELAPSED_TIME:
85	type = kDHCPv6OptionTypeUInt16;
86	break;
87    case kDHCPv6OPTION_UNICAST:
88	type = kDHCPv6OptionTypeIPv6Address;
89	break;
90    case kDHCPv6OPTION_RAPID_COMMIT:
91	type = kDHCPv6OptionTypeNone; /* i.e. no data */
92	break;
93    case kDHCPv6OPTION_DNS_SERVERS:
94	type = kDHCPv6OptionTypeIPv6Address;
95	break;
96    case kDHCPv6OPTION_DOMAIN_LIST:
97	type = kDHCPv6OptionTypeDNSNameList;
98	break;
99    case kDHCPv6OPTION_IA_NA:
100	type = kDHCPv6OptionTypeIA_NA;
101	break;
102    case kDHCPv6OPTION_IAADDR:
103	type = kDHCPv6OptionTypeIAADDR;
104	break;
105    case kDHCPv6OPTION_STATUS_CODE:
106	type = kDHCPv6OptionTypeStatusCode;
107	break;
108    case kDHCPv6OPTION_IA_TA:
109    case kDHCPv6OPTION_PREFERENCE:
110    case kDHCPv6OPTION_RELAY_MSG:
111    case kDHCPv6OPTION_AUTH:
112    case kDHCPv6OPTION_USER_CLASS:
113    case kDHCPv6OPTION_VENDOR_CLASS:
114    case kDHCPv6OPTION_VENDOR_OPTS:
115    case kDHCPv6OPTION_INTERFACE_ID:
116    case kDHCPv6OPTION_RECONF_MSG:
117    case kDHCPv6OPTION_RECONF_ACCEPT:
118    default:
119	break;
120    }
121    return (type);
122}
123
124PRIVATE_EXTERN const char *
125DHCPv6OptionCodeGetName(int code)
126{
127    const char * 	str;
128
129    switch (code) {
130    case kDHCPv6OPTION_CLIENTID:
131	str = "CLIENTID";
132	break;
133    case kDHCPv6OPTION_SERVERID:
134	str = "SERVERID";
135	break;
136    case kDHCPv6OPTION_IA_NA:
137	str = "IA_NA";
138	break;
139    case kDHCPv6OPTION_IA_TA:
140	str = "IA_TA";
141	break;
142    case kDHCPv6OPTION_IAADDR:
143	str = "IAADDR";
144	break;
145    case kDHCPv6OPTION_ORO:
146	str = "ORO";
147	break;
148    case kDHCPv6OPTION_PREFERENCE:
149	str = "PREFERENCE";
150	break;
151    case kDHCPv6OPTION_ELAPSED_TIME:
152	str = "ELAPSED_TIME";
153	break;
154    case kDHCPv6OPTION_RELAY_MSG:
155	str = "RELAY_MSG";
156	break;
157    case kDHCPv6OPTION_AUTH:
158	str = "AUTH";
159	break;
160    case kDHCPv6OPTION_UNICAST:
161	str = "UNICAST";
162	break;
163    case kDHCPv6OPTION_STATUS_CODE:
164	str = "STATUS_CODE";
165	break;
166    case kDHCPv6OPTION_RAPID_COMMIT:
167	str = "RAPID_COMMIT";
168	break;
169    case kDHCPv6OPTION_USER_CLASS:
170	str = "USER_CLASS";
171	break;
172    case kDHCPv6OPTION_VENDOR_CLASS:
173	str = "VENDOR_CLASS";
174	break;
175    case kDHCPv6OPTION_VENDOR_OPTS:
176	str = "VENDOR_OPTS";
177	break;
178    case kDHCPv6OPTION_INTERFACE_ID:
179	str = "INTERFACE_ID";
180	break;
181    case kDHCPv6OPTION_RECONF_MSG:
182	str = "RECONF_MSG";
183	break;
184    case kDHCPv6OPTION_RECONF_ACCEPT:
185	str = "RECONF_ACCEPT";
186	break;
187    case kDHCPv6OPTION_DNS_SERVERS:
188	str = "DNS_SERVERS";
189	break;
190    case kDHCPv6OPTION_DOMAIN_LIST:
191	str = "DOMAIN_LIST";
192	break;
193    default:
194	str = "<unknown>";
195	break;
196    }
197    return (str);
198}
199
200PRIVATE_EXTERN void
201DHCPv6OptionAreaInit(DHCPv6OptionAreaRef oa_p, uint8_t * buf, int size)
202{
203    oa_p->buf = buf;
204    oa_p->size = size;
205    oa_p->used = 0;
206    return;
207}
208
209PRIVATE_EXTERN int
210DHCPv6OptionAreaGetUsedLength(DHCPv6OptionAreaRef oa_p)
211{
212    return (oa_p->used);
213}
214
215PRIVATE_EXTERN bool
216DHCPv6OptionAreaAddOption(DHCPv6OptionAreaRef oa_p, int option_code,
217			  int option_len, const void * option_data,
218			  DHCPv6OptionErrorString * err_p)
219
220{
221    int			left = oa_p->size - oa_p->used;
222    DHCPv6OptionRef	opt;
223    int			required_space;
224
225    required_space = offsetof(DHCPv6Option, data) + option_len;
226    err_p->str[0] = '\0';
227    if (left < required_space) {
228	if (err_p != NULL) {
229	    snprintf(err_p->str, sizeof(err_p->str),
230		     "No room for option %s (%d), %d < %d",
231		     DHCPv6OptionCodeGetName(option_code),
232		     option_code, left, required_space);
233	}
234	return (FALSE);
235    }
236    opt = (DHCPv6OptionRef)(oa_p->buf + oa_p->used);
237    DHCPv6OptionSetCode(opt, option_code);
238    DHCPv6OptionSetLength(opt, option_len);
239    if (option_len > 0) {
240	bcopy(option_data, opt->data, option_len);
241    }
242    oa_p->used += required_space;
243    return (TRUE);
244}
245
246PRIVATE_EXTERN bool
247DHCPv6OptionAreaAddOptionRequestOption(DHCPv6OptionAreaRef oa_p,
248				       const uint16_t * requested_options,
249				       int count,
250				       DHCPv6OptionErrorString * err_p)
251
252{
253    int			i;
254    uint16_t		oro[count];
255
256    /* convert requested options to network byte order */
257    for (i = 0; i < count; i++) {
258	oro[i] = htons(requested_options[i]);
259    }
260    return (DHCPv6OptionAreaAddOption(oa_p, kDHCPv6OPTION_ORO,
261				      sizeof(oro), oro, err_p));
262}
263
264
265/**
266 ** DHCPv6OptionList
267 **/
268struct DHCPv6OptionList {
269    ptrlist_t		list;
270};
271typedef struct DHCPv6OptionList DHCPv6OptionList;
272
273STATIC bool
274DHCPv6OptionListParse(DHCPv6OptionListRef options,
275		      const uint8_t * buf, int buf_size,
276		      DHCPv6OptionErrorString * err_p)
277{
278    int			left;
279    const uint8_t *	scan;
280
281    ptrlist_init(&options->list);
282    for (left = buf_size, scan = buf; TRUE; ) {
283	DHCPv6OptionRef	option = (DHCPv6OptionRef)scan;
284	int		option_code;
285	int		option_len;
286	int		option_need;
287
288	if (left < DHCPV6_OPTION_HEADER_SIZE) {
289	    if (left == 0) {
290		/* we're done */
291		break;
292	    }
293	    if (err_p != NULL) {
294		snprintf(err_p->str, sizeof(err_p->str),
295			 "truncated buffer at offset %d\n",
296			 (int)(scan - buf));
297	    }
298	    goto failed;
299	}
300	option_code = DHCPv6OptionGetCode(option);
301	option_len = DHCPv6OptionGetLength(option);
302	option_need = DHCPV6_OPTION_HEADER_SIZE + option_len;
303	if (left < option_need) {
304	    if (err_p != NULL) {
305		snprintf(err_p->str, sizeof(err_p->str),
306			 "truncated option %s (%d) at offset %d,"
307			 " left %d < need %d",
308			 DHCPv6OptionCodeGetName(option_code), option_code,
309			 (int)(scan - buf), left, option_need);
310	    }
311	    goto failed;
312	}
313	ptrlist_add(&options->list, (void *)scan);
314	scan += option_need;
315	left -= option_need;
316    }
317    return (TRUE);
318
319 failed:
320    ptrlist_free(&options->list);
321    return (NULL);
322}
323
324PRIVATE_EXTERN DHCPv6OptionListRef
325DHCPv6OptionListCreate(const uint8_t * buf, int buf_size,
326		       DHCPv6OptionErrorString * err_p)
327{
328    DHCPv6OptionList	options;
329    DHCPv6OptionListRef	ret;
330
331    if (DHCPv6OptionListParse(&options, buf, buf_size, err_p) == FALSE) {
332	return (NULL);
333    }
334    ret = (DHCPv6OptionListRef)malloc(sizeof(*ret));
335    ptrlist_dup(&ret->list, &options.list);
336    ptrlist_free(&options.list);
337    return (ret);
338}
339
340PRIVATE_EXTERN DHCPv6OptionListRef
341DHCPv6OptionListCreateWithPacket(const DHCPv6PacketRef pkt, int pkt_len,
342				 DHCPv6OptionErrorString * err_p)
343{
344    int		option_len;
345
346    if (pkt_len < DHCPV6_PACKET_HEADER_LENGTH) {
347	return (NULL);
348    }
349    option_len = pkt_len - DHCPV6_PACKET_HEADER_LENGTH;
350    return (DHCPv6OptionListCreate(pkt->options, option_len, err_p));
351}
352
353PRIVATE_EXTERN void
354DHCPv6OptionListRelease(DHCPv6OptionListRef * options_p)
355{
356    DHCPv6OptionListRef		options = *options_p;
357
358    if (options == NULL) {
359	return;
360    }
361    *options_p = NULL;
362    ptrlist_free(&options->list);
363    free(options);
364    return;
365}
366
367STATIC void
368DHCPv6OptionListPrintLevelToString(CFMutableStringRef str,
369				   DHCPv6OptionListRef options,
370				   int level)
371{
372    int 	count = DHCPv6OptionListGetCount(options);
373    int		i;
374    int		lev;
375
376    STRING_APPEND(str, "Options[%d] = {\n", count);
377    for (i = 0; i < count; i++) {
378	DHCPDUIDRef		duid;
379	DHCPv6OptionRef		option;
380	int			option_code;
381	int			option_len;
382	const uint8_t *		option_data;
383	DHCPv6OptionType	type;
384
385	option = DHCPv6OptionListGetOptionAtIndex(options, i);
386	option_code = DHCPv6OptionGetCode(option);
387	option_len = DHCPv6OptionGetLength(option);
388	option_data = DHCPv6OptionGetData(option);
389	for (lev = 0; lev < level; lev++) {
390	    STRING_APPEND(str, "  ");
391	}
392	STRING_APPEND(str, "  %s (%d) Length %d",
393		      DHCPv6OptionCodeGetName(option_code), option_code, option_len);
394	type = DHCPv6OptionCodeGetType(option_code);
395	switch (type) {
396	case kDHCPv6OptionTypeNone:
397	    break;
398	case kDHCPv6OptionTypeDUID:
399	    STRING_APPEND(str, " ");
400	    duid = (DHCPDUIDRef)option_data;
401	    DHCPDUIDPrintToString(str, duid, option_len);
402	    STRING_APPEND(str, "\n");
403	    break;
404	case kDHCPv6OptionTypeUInt16: {
405	    int		j;
406	    void *	scan;
407
408	    scan = (void *)option_data;
409	    STRING_APPEND(str, ": ");
410	    for (j = 0; j < option_len / sizeof(uint16_t);
411		 j++, scan += sizeof(uint16_t)) {
412		uint16_t	val;
413
414		val = net_uint16_get(scan);
415		if (option_code == kDHCPv6OPTION_ORO) {
416		    STRING_APPEND(str, "%s%s (%d)", (j == 0) ? "" : ", ",
417				  DHCPv6OptionCodeGetName(val), val);
418		}
419		else {
420		    STRING_APPEND(str, "%s%d", (j == 0) ? "" : ", ", val);
421		}
422	    }
423	    STRING_APPEND(str, "\n");
424	    break;
425	}
426	case kDHCPv6OptionTypeUInt32: {
427	    int		j;
428	    void *	scan;
429
430	    scan = (void *)option_data;
431	    STRING_APPEND(str, ": ");
432	    for (j = 0; j < option_len / sizeof(uint32_t);
433		 j++, scan += sizeof(uint32_t)) {
434		STRING_APPEND(str, "%s%d", (j == 0) ? "" : ", ",
435			      net_uint32_get(scan));
436	    }
437	    STRING_APPEND(str, "\n");
438	    break;
439	}
440	case kDHCPv6OptionTypeIPv6Address: {
441	    int				j;
442	    void *			scan;
443	    char 			ntopbuf[INET6_ADDRSTRLEN];
444
445	    scan = (void *)option_data;
446	    for (j = 0; j < option_len / sizeof(struct in6_addr);
447		 j++, scan += sizeof(struct in6_addr)) {
448		STRING_APPEND(str, " %s\n",
449			      inet_ntop(AF_INET6, scan, ntopbuf, sizeof(ntopbuf)));
450	    }
451	    break;
452	}
453	case kDHCPv6OptionTypeDNSNameList: {
454	    int			j;
455	    const char * *	list;
456	    int			list_count;
457
458	    list = DNSNameListCreate(option_data, option_len, &list_count);
459	    if (list == NULL) {
460		STRING_APPEND(str, " Invalid");
461		goto print_option_data;
462	    }
463	    STRING_APPEND(str, ": ");
464	    for (j = 0; j < list_count; j++) {
465		STRING_APPEND(str, "%s%s", (j == 0) ? "" : ", ", list[j]);
466	    }
467	    free(list);
468	    STRING_APPEND(str, "\n");
469	    break;
470	}
471	case kDHCPv6OptionTypeIA_NA:
472	    DHCPv6OptionIA_NAPrintLevelToString(str,
473						(DHCPv6OptionIA_NARef)
474						option_data,
475						option_len, level);
476	    break;
477
478	case kDHCPv6OptionTypeIAADDR:
479	    DHCPv6OptionIAADDRPrintLevelToString(str,
480						 (DHCPv6OptionIAADDRRef)
481						 option_data,
482						 option_len, level);
483	    break;
484
485	case kDHCPv6OptionTypeStatusCode:
486	    DHCPv6OptionSTATUS_CODEPrintToString(str,
487						 (DHCPv6OptionSTATUS_CODERef)
488						 option_data, option_len);
489	    break;
490
491	print_option_data:
492	default:
493	case kDHCPv6OptionTypeUnknown:
494	    if (option_len != 0) {
495		STRING_APPEND(str, " Data ");
496		print_bytes_cfstr(str,
497				  (void *)DHCPv6OptionGetData(option),
498				  option_len);
499	    }
500	    STRING_APPEND(str, "\n");
501	    break;
502	}
503    }
504    for (lev = 0; lev < level; lev++) {
505	STRING_APPEND(str, "  ");
506    }
507    STRING_APPEND(str, "}");
508    return;
509}
510
511PRIVATE_EXTERN void
512DHCPv6OptionListPrintToString(CFMutableStringRef str,
513			      DHCPv6OptionListRef options)
514{
515    DHCPv6OptionListPrintLevelToString(str, options, 0);
516    return;
517}
518
519void
520DHCPv6OptionListFPrint(FILE * file, DHCPv6OptionListRef options)
521{
522    CFMutableStringRef	str;
523
524    str = CFStringCreateMutable(NULL, 0);
525    DHCPv6OptionListPrintLevelToString(str, options, 0);
526    SCPrint(TRUE, file, CFSTR("%@\n"), str);
527    CFRelease(str);
528    return;
529}
530
531PRIVATE_EXTERN int
532DHCPv6OptionListGetCount(DHCPv6OptionListRef options)
533{
534    return (ptrlist_count(&options->list));
535}
536
537PRIVATE_EXTERN DHCPv6OptionRef
538DHCPv6OptionListGetOptionAtIndex(DHCPv6OptionListRef options, int i)
539{
540    return ((DHCPv6OptionRef)ptrlist_element(&options->list, i));
541}
542
543PRIVATE_EXTERN const uint8_t *
544DHCPv6OptionListGetOptionDataAndLength(DHCPv6OptionListRef options,
545				       int option_code, int * ret_length,
546				       int * start_index)
547{
548    int		count = DHCPv6OptionListGetCount(options);
549    int		i = 0;
550
551    if (start_index != NULL) {
552	i = *start_index;
553    }
554    for (; i < count; i++) {
555	DHCPv6OptionRef		option;
556
557	option = DHCPv6OptionListGetOptionAtIndex(options, i);
558	if (DHCPv6OptionGetCode(option) == option_code) {
559	    if (start_index != NULL) {
560		*start_index = i + 1;
561	    }
562	    *ret_length = DHCPv6OptionGetLength(option);
563	    return (DHCPv6OptionGetData(option));
564	}
565    }
566    return (NULL);
567}
568
569/**
570 ** DHCPv6OptionIA_NA
571 **/
572STATIC void
573DHCPv6OptionIA_NAPrintLevelToString(CFMutableStringRef str,
574				    const DHCPv6OptionIA_NARef ia_na,
575				    int ia_na_len, int level)
576{
577    int		option_len;
578
579    if (ia_na_len < DHCPv6OptionIA_NA_MIN_LENGTH) {
580	STRING_APPEND(str, " IA_NA option is too short %d < %d\n",
581		      ia_na_len, DHCPv6OptionIA_NA_MIN_LENGTH);
582	return;
583    }
584    option_len = ia_na_len - DHCPv6OptionIA_NA_MIN_LENGTH;
585    STRING_APPEND(str, " IA_NA IAID=%d T1=%d T2=%d",
586		  DHCPv6OptionIA_NAGetIAID(ia_na),
587		  DHCPv6OptionIA_NAGetT1(ia_na),
588		  DHCPv6OptionIA_NAGetT2(ia_na));
589
590    if (option_len == 0) {
591	STRING_APPEND(str, "\n");
592    }
593    else {
594	DHCPv6OptionErrorString 	err;
595	DHCPv6OptionListRef		options;
596
597	options = DHCPv6OptionListCreate(ia_na->options, option_len, &err);
598	if (options == NULL) {
599	    STRING_APPEND(str, " options invalid:\n\t%s\n",
600			  err.str);
601	}
602	else {
603	    STRING_APPEND(str, " ");
604	    /* possibly recurse */
605	    DHCPv6OptionListPrintLevelToString(str, options, level + 1);
606	}
607	DHCPv6OptionListRelease(&options);
608    }
609    return;
610}
611
612/**
613 ** DHCPv6OptionIAADDR
614 **/
615STATIC void
616DHCPv6OptionIAADDRPrintLevelToString(CFMutableStringRef str,
617				     DHCPv6OptionIAADDRRef ia_addr,
618				     int ia_addr_len, int level)
619{
620    char 	ntopbuf[INET6_ADDRSTRLEN];
621    int		option_len;
622
623
624    if (ia_addr_len < DHCPv6OptionIAADDR_MIN_LENGTH) {
625	STRING_APPEND(str, " IAADDR option is too short %d < %d\n",
626		      ia_addr_len, DHCPv6OptionIAADDR_MIN_LENGTH);
627	return;
628    }
629    option_len = ia_addr_len - DHCPv6OptionIAADDR_MIN_LENGTH;
630    STRING_APPEND(str, " IAADDR %s Preferred %d Valid=%d",
631		  inet_ntop(AF_INET6,
632			    DHCPv6OptionIAADDRGetAddress(ia_addr),
633			    ntopbuf, sizeof(ntopbuf)),
634		  DHCPv6OptionIAADDRGetPreferredLifetime(ia_addr),
635		  DHCPv6OptionIAADDRGetValidLifetime(ia_addr));
636    if (option_len == 0) {
637	STRING_APPEND(str, "\n");
638    }
639    else {
640	DHCPv6OptionErrorString 	err;
641	DHCPv6OptionListRef		options;
642
643	options = DHCPv6OptionListCreate(ia_addr->options, option_len, &err);
644	if (options == NULL) {
645	    STRING_APPEND(str, " options invalid:\n\t%s\n",
646			  err.str);
647	}
648	else {
649	    STRING_APPEND(str, " ");
650	    /* possibly recurse */
651	    DHCPv6OptionListPrintLevelToString(str, options, level + 1);
652	}
653	DHCPv6OptionListRelease(&options);
654    }
655    return;
656}
657
658PRIVATE_EXTERN void
659DHCPv6OptionIAADDRPrintToString(CFMutableStringRef str,
660				DHCPv6OptionIAADDRRef ia_addr,
661				int ia_addr_len)
662{
663    DHCPv6OptionIAADDRPrintLevelToString(str, ia_addr, ia_addr_len, 0);
664    return;
665}
666
667PRIVATE_EXTERN void
668DHCPv6OptionIAADDRFPrint(FILE * file, DHCPv6OptionIAADDRRef ia_addr,
669			 int ia_addr_len)
670{
671    CFMutableStringRef	str;
672
673    str = CFStringCreateMutable(NULL, 0);
674    DHCPv6OptionIAADDRPrintToString(str, ia_addr, ia_addr_len);
675    SCPrint(TRUE, file, CFSTR("%@"), str);
676    CFRelease(str);
677    return;
678}
679
680/**
681 ** DHCPv6StatusCode
682 **/
683PRIVATE_EXTERN const char *
684DHCPv6StatusCodeGetName(int code)
685{
686    const char *	str;
687
688    switch (code) {
689    case kDHCPv6StatusCodeSuccess:
690	str = "Success";
691	break;
692    case kDHCPv6StatusCodeFailure:
693	str = "Failure";
694	break;
695    case kDHCPv6StatusCodeNoAddrsAvail:
696	str = "NoAddrsAvail";
697	break;
698    case kDHCPv6StatusCodeNoBinding:
699	str = "NoBinding";
700	break;
701    case kDHCPv6StatusCodeNotOnLink:
702	str = "NotOnLink";
703	break;
704    case kDHCPv6StatusCodeUseMulticast:
705	str = "UseMulticast";
706	break;
707    default:
708	str = "<unknown>";
709	break;
710    }
711    return (str);
712}
713
714STATIC void
715DHCPv6OptionSTATUS_CODEPrintToString(CFMutableStringRef str,
716				     DHCPv6OptionSTATUS_CODERef status_p,
717				     int status_len)
718{
719    uint16_t		code;
720    int			message_len;
721
722    if (status_len < DHCPv6OptionSTATUS_CODE_MIN_LENGTH) {
723	STRING_APPEND(str, " STATUS_CODE option is too short %d < %d\n",
724		      status_len, DHCPv6OptionSTATUS_CODE_MIN_LENGTH);
725	return;
726    }
727    code = DHCPv6OptionSTATUS_CODEGetCode(status_p);
728    message_len = status_len - DHCPv6OptionSTATUS_CODE_MIN_LENGTH;
729    if (message_len) {
730	STRING_APPEND(str, " STATUS_CODE %s (%d) '%.*s'\n",
731		      DHCPv6StatusCodeGetName(code), code,
732		      message_len, status_p->message);
733    }
734    else {
735	STRING_APPEND(str, " STATUS_CODE %s (%d)\n",
736		      DHCPv6StatusCodeGetName(code), code);
737    }
738    return;
739}
740
741#if TEST_DHCPV6_OPTIONS
742/* type = 1, hw = 1, time = aabbccdd, linklayer_address = 0x000102030405 */
743#define DUID_LLT_1	0x00, 0x01, 0x00, 0x01, 0xaa, 0xbb, 0xcc, 0xdd, \
744	0x00, 0x01, 0x02, 0x03, 0x04, 0x05
745
746#define DUID_EN_1	0x00, 0x02, 0x01, 0x02, 0x03, 0x04, 0xde, 0xad, \
747	0xbe, 0xef
748
749#define DUID_LL_1	0x00, 0x03, 0x00, 0x01, \
750	0x00, 0x01, 0x02, 0x03, 0x04, 0x05
751
752#define DUID_LLT_UNDEF6_1 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, \
753	0x00, 0x01, 0x02, 0x03, 0x04, 0x05
754
755#define ORO_1 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04
756
757#define DNS_SERVER_1 0x20, 0x02, 0x45, 0xb5, 0xea, 0x61, 0x00, 0x00, 0x2, 0x1f, 0xf3, 0xff, 0xfe, 0x43, 0x1a, 0xbf
758
759#define DNS_NAME_SEARCH_1						\
760    4, 'e', 'u', 'r', 'o', 5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, \
761	9, 'm', 'a', 'r', 'k', 'e', 't', 'i', 'n', 'g', 5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, \
762	11, 'e', 'n', 'g', 'i', 'n', 'e', 'e', 'r', 'i', 'n', 'g', 5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0
763
764
765const uint8_t test_buf1[] = {
766    0x00, 0x03, 0x00, 0x02, 0x01, 0x02,
767    0x00, 0x01, 0x00, 0x0e, DUID_LLT_1 ,
768    0x00, 0x01, 0x00, 0xa, DUID_EN_1 ,
769    0x00, 0x02, 0x00, 0xa, DUID_LL_1 ,
770    0x00, 0x06, 0x00, 0x08, ORO_1 ,
771    0x00, 0x18, 0x00, 0x3c, DNS_NAME_SEARCH_1 ,
772    0x00, 0x18, 0x00, 0x03, 0x1, 'a', 0,
773    0x00, 0x02, 0x00, 0x0e, DUID_LLT_UNDEF6_1 ,
774    0x00, 0x17, 0x00, 0x10, DNS_SERVER_1 ,
775};
776
777const uint8_t test_bad_buf1[] = {
778    0x00, 0x01, 0x01, 0x00, 0x01, 0x02,
779};
780
781struct testbuf {
782    const uint8_t *	buf;
783    int			size;
784    bool		good;
785};
786struct testbuf tests[] = {
787    { test_buf1, sizeof(test_buf1), TRUE },
788    { test_bad_buf1, sizeof(test_bad_buf1), FALSE },
789    { NULL, 0 }
790};
791
792STATIC bool
793run_tests(struct testbuf * list, bool verbose)
794{
795    int				i;
796    struct testbuf *		scan;
797
798    for (i = 0, scan = list; scan->buf != NULL; scan++, i++) {
799	DHCPv6OptionErrorString 	err;
800	DHCPv6OptionListRef		options;
801
802	options = DHCPv6OptionListCreate(scan->buf, scan->size, &err);
803	if (options != NULL) {
804	    if (scan->good == FALSE) {
805		fprintf(stderr, "test %d succeeded unexpectedly\n", i);
806		return (FALSE);
807	    }
808	    printf("test %d SUCCESS\n", i);
809	    if (verbose) {
810		DHCPv6OptionListFPrint(stdout, options);
811	    }
812	    DHCPv6OptionListRelease(&options);
813	}
814	else {
815	    if (scan->good == TRUE) {
816		printf("test %d FAILURE: %s\n", i,
817		       err.str);
818		return (FALSE);
819	    }
820	    printf("test %d SUCCESS\n", i);
821	    if (verbose) {
822		fprintf(stderr, "\tParse failed, %s\n", err.str);
823	    }
824
825	}
826    }
827    return (TRUE);
828}
829
830int
831main(int argc, char * argv[])
832{
833    if (run_tests(tests, argc > 1) == FALSE) {
834	fprintf(stderr, "TEST FAILED\n");
835	exit(1);
836    }
837    exit(0);
838    return (0);
839}
840#endif /* TEST_DHCPV6_OPTIONS */
841