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