1/* 2 * Copyright (c) 2001, 2003-2005, 2011, 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#include <stdio.h> 25#include <unistd.h> 26 27 28#include "SystemConfiguration.h" 29#include "SCValidation.h" 30#include "SCDynamicStoreCopyDHCPInfo.h" 31#include "DHCPClientPreferences.h" 32 33#define DHCPCLIENT_PREFERENCES_ID "DHCPClient.xml" 34#define DHCPCLIENT_APPLICATION_PREF "Application" 35 36#define DHCP_REQUESTED_PARAMETER_LIST "DHCPRequestedParameterList" 37 38/** 39 ** DHCPClientPreferences{Set,Get}ApplicationOptions() 40 **/ 41static UInt8 * 42S_get_char_array(CFArrayRef arr, CFIndex * len) 43{ 44 UInt8 * buf = NULL; 45 CFIndex count = 0; 46 CFIndex i; 47 CFIndex real_count; 48 49 if (arr) { 50 count = CFArrayGetCount(arr); 51 } 52 if (count == 0) { 53 goto done; 54 } 55 buf = malloc(count); 56 if (buf == NULL) { 57 goto done; 58 } 59 for (i = 0, real_count = 0; i < count; i++) { 60 CFNumberRef n = isA_CFNumber(CFArrayGetValueAtIndex(arr, i)); 61 int val; 62 63 if (n && CFNumberGetValue(n, kCFNumberIntType, &val)) { 64 buf[real_count++] = (UInt8) val; 65 } 66 } 67 count = real_count; 68 done: 69 *len = count; 70 if (count == 0 && buf) { 71 free(buf); 72 buf = NULL; 73 } 74 return (buf); 75} 76 77static void 78my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new) 79{ 80 CFIndex i; 81 CFIndex n = CFArrayGetCount(arr); 82 83 for (i = 0; i < n; i++) { 84 CFStringRef element = CFArrayGetValueAtIndex(arr, i); 85 if (CFEqual(element, new)) { 86 return; 87 } 88 } 89 CFArrayAppendValue(arr, new); 90 return; 91} 92 93static __inline__ CF_RETURNS_RETAINED CFStringRef 94S_application_path(CFStringRef applicationID) 95{ 96 return (CFStringCreateWithFormat(NULL, NULL, 97 CFSTR("/" DHCPCLIENT_APPLICATION_PREF 98 "/%@"), 99 applicationID)); 100} 101 102Boolean 103DHCPClientPreferencesSetApplicationOptions(CFStringRef applicationID, 104 UInt8 * options, 105 CFIndex count) 106{ 107 CFMutableDictionaryRef dict = NULL; 108 CFStringRef path = NULL; 109 SCPreferencesRef prefs = NULL; 110 Boolean success = FALSE; 111 112 if (applicationID == NULL) { 113 goto done; 114 } 115 path = S_application_path(applicationID); 116 if (path == NULL) { 117 goto done; 118 } 119 prefs = SCPreferencesCreate(NULL, CFSTR("DHCPClientSetAppReqParams"), 120 CFSTR(DHCPCLIENT_PREFERENCES_ID)); 121 if (prefs == NULL) { 122 goto done; 123 } 124 dict = (CFMutableDictionaryRef)SCPreferencesPathGetValue(prefs, path); 125 if (dict == NULL) { 126 dict = CFDictionaryCreateMutable(NULL, 0, 127 &kCFTypeDictionaryKeyCallBacks, 128 &kCFTypeDictionaryValueCallBacks); 129 } 130 else { 131 dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 132 } 133 if (dict == NULL) { 134 goto done; 135 } 136 if (options && count > 0) { 137 int i; 138 CFMutableArrayRef array; 139 140 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 141 if (array == NULL) { 142 goto done; 143 } 144 for (i = 0; i < count; i++) { 145 int val; 146 CFNumberRef number; 147 148 if (options[i] == 0 || options[i] == 255) { 149 /* ignore pads and end */ 150 continue; 151 } 152 val = options[i]; 153 number = CFNumberCreate(NULL, kCFNumberIntType, &val); 154 if (number == NULL) { 155 CFRelease(array); 156 goto done; 157 } 158 my_CFArrayAppendUniqueValue(array, number); 159 CFRelease(number); 160 } 161 CFDictionarySetValue(dict, CFSTR(DHCP_REQUESTED_PARAMETER_LIST), 162 array); 163 CFRelease(array); 164 } 165 else { 166 CFDictionaryRemoveValue(dict, CFSTR(DHCP_REQUESTED_PARAMETER_LIST)); 167 } 168 if (SCPreferencesLock(prefs, TRUE)) { 169 success = SCPreferencesPathSetValue(prefs, path, dict); 170 if (success) { 171 success = SCPreferencesCommitChanges(prefs); 172 if (success) { 173 (void)SCPreferencesApplyChanges(prefs); 174 } 175 } 176 (void)SCPreferencesUnlock(prefs); 177 } 178 done: 179 if (prefs) { 180 CFRelease(prefs); 181 } 182 if (path) { 183 CFRelease(path); 184 } 185 if (dict) { 186 CFRelease(dict); 187 } 188 return (success); 189} 190 191UInt8 * 192DHCPClientPreferencesCopyApplicationOptions(CFStringRef applicationID, 193 CFIndex * count) 194{ 195 CFDictionaryRef dict = NULL; 196 UInt8 * options = NULL; 197 CFArrayRef parms; 198 CFStringRef path = NULL; 199 SCPreferencesRef prefs = NULL; 200 201 if (applicationID == NULL) { 202 goto done; 203 } 204 path = S_application_path(applicationID); 205 if (path == NULL) { 206 goto done; 207 } 208 prefs = SCPreferencesCreate(NULL, CFSTR("DHCPClientCopyAppReqParams"), 209 CFSTR(DHCPCLIENT_PREFERENCES_ID)); 210 if (prefs == NULL) { 211 goto done; 212 } 213 dict = SCPreferencesPathGetValue(prefs, path); 214 if (dict == NULL) { 215 goto done; 216 } 217 parms = CFDictionaryGetValue(dict, 218 CFSTR(DHCP_REQUESTED_PARAMETER_LIST)); 219 if (isA_CFArray(parms) == NULL) { 220 goto done; 221 } 222 options = S_get_char_array(parms, count); 223 224 done: 225 if (prefs) { 226 CFRelease(prefs); 227 } 228 if (path) { 229 CFRelease(path); 230 } 231 return (options); 232} 233 234/** 235 ** DHCPClientInfo*() 236 **/ 237 238CFDictionaryRef 239SCDynamicStoreCopyDHCPInfo(SCDynamicStoreRef store, CFStringRef serviceID) 240{ 241 CFDictionaryRef dhcp_dict = NULL; 242 CFStringRef key = NULL; 243 CFDictionaryRef primary_dict = NULL; 244 245 if (serviceID == NULL) { 246 /* get the primary service name */ 247 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 248 kSCDynamicStoreDomainState, 249 kSCEntNetIPv4); 250 if (key) { 251 primary_dict = SCDynamicStoreCopyValue(store, key); 252 if (primary_dict) { 253 serviceID = CFDictionaryGetValue(primary_dict, 254 kSCDynamicStorePropNetPrimaryService); 255 } 256 CFRelease(key); 257 } 258 } 259 if (serviceID == NULL) { 260 goto done; 261 } 262 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 263 kSCDynamicStoreDomainState, 264 serviceID, 265 kSCEntNetDHCP); 266 if (key) { 267 dhcp_dict = SCDynamicStoreCopyValue(store, key); 268 if (dhcp_dict != NULL 269 && isA_CFDictionary(dhcp_dict) == NULL) { 270 CFRelease(dhcp_dict); 271 dhcp_dict = NULL; 272 } 273 CFRelease(key); 274 } 275 done: 276 if (primary_dict) { 277 CFRelease(primary_dict); 278 } 279 return (dhcp_dict); 280} 281 282CFDataRef 283DHCPInfoGetOptionData(CFDictionaryRef dhcp_dict, UInt8 code) 284{ 285 CFDataRef data = NULL; 286 CFStringRef option_code_str = NULL; 287 288 option_code_str = CFStringCreateWithFormat(NULL, NULL, 289 CFSTR("Option_%d"), code); 290 if (option_code_str == NULL) { 291 goto done; 292 } 293 294 data = CFDictionaryGetValue(dhcp_dict, option_code_str); 295 data = isA_CFData(data); 296 done: 297 if (option_code_str) 298 CFRelease(option_code_str); 299 return (data); 300} 301 302CFDateRef 303DHCPInfoGetLeaseStartTime(CFDictionaryRef dhcp_dict) 304{ 305 return (CFDictionaryGetValue(dhcp_dict, CFSTR("LeaseStartTime"))); 306} 307 308CFDateRef 309DHCPInfoGetLeaseExpirationTime(CFDictionaryRef dhcp_dict) 310{ 311 return (CFDictionaryGetValue(dhcp_dict, CFSTR("LeaseExpirationTime"))); 312} 313 314#ifdef TEST_DHCPCLIENT_PREFERENCES 315void 316print_data(u_char * data_p, int n_bytes) 317{ 318#define CHARS_PER_LINE 16 319 char line_buf[CHARS_PER_LINE + 1]; 320 int line_pos; 321 int offset; 322 323 for (line_pos = 0, offset = 0; offset < n_bytes; offset++, data_p++) { 324 if (line_pos == 0) 325 printf("%04x ", offset); 326 327 line_buf[line_pos] = isprint(*data_p) ? *data_p : '.'; 328 printf(" %02x", *data_p); 329 line_pos++; 330 if (line_pos == CHARS_PER_LINE) { 331 line_buf[CHARS_PER_LINE] = '\0'; 332 printf(" %s\n", line_buf); 333 line_pos = 0; 334 } 335 else if (line_pos == (CHARS_PER_LINE / 2)) 336 printf(" "); 337 } 338 if (line_pos) { /* need to finish up the line */ 339 for (; line_pos < CHARS_PER_LINE; line_pos++) { 340 printf(" "); 341 line_buf[line_pos] = ' '; 342 } 343 line_buf[CHARS_PER_LINE] = '\0'; 344 printf(" %s\n", line_buf); 345 } 346} 347 348#define CMDSTR_GETOPTION "getoption" 349#define CMDSTR_LEASE "leaseinfo" 350#define CMDSTR_GETPARAMS "getparams" 351#define CMDSTR_SETPARAMS "setparams" 352 353static __inline__ void 354S_print_char_array(UInt8 * params, int n_params) 355{ 356 int i; 357 358 for (i = 0; i < n_params; i++) { 359 if (i == 0) 360 printf("%d", params[i]); 361 else 362 printf(", %d", params[i]); 363 } 364 return; 365} 366 367void 368usage(char * prog) 369{ 370 printf("%s " CMDSTR_GETOPTION " <serviceID> <opt> [ <type> ]\n" 371 "%s " CMDSTR_LEASE " <serviceID>\n" 372 "%s " CMDSTR_GETPARAMS " <app_id>\n" 373 "%s " CMDSTR_SETPARAMS " <app_id> [ <opt> [ <opt> ] ... [ <opt> ] ] ]\n" 374 "where:\n" 375 " <serviceID> : service ID string | \"\"\n" 376 " <opt> : DHCP/BOOTP option code\n" 377 " (e.g. 1 == subnet mask, 3 == router, 6 = dns, 15 = domain)\n" 378 " <type> : type of option: string, ip\n" 379 " <app_id> : application id (e.g. com.apple.ntpd, com.thursby.Dave)\n", 380 prog, prog, prog, prog); 381 exit(0); 382} 383 384static void 385dump_gregorian_date(CFGregorianDate d) 386{ 387 printf("%d/%d/%d %02d:%02d:%02d\n", 388 (int)d.year, d.month, d.day, d.hour, d.minute, (int)d.second); 389 return; 390} 391 392static void 393show_date(CFAbsoluteTime t) 394{ 395 CFGregorianDate d; 396 static CFTimeZoneRef tz = NULL; 397 398 if (tz == NULL) { 399 tz = CFTimeZoneCopySystem(); 400 } 401 402 d = CFAbsoluteTimeGetGregorianDate(t, tz); 403 dump_gregorian_date(d); 404 return; 405} 406 407#define IP_FORMAT "%d.%d.%d.%d" 408#define IP_CH(ip, i) (((u_char *)(ip))[i]) 409#define IP_LIST(ip) IP_CH(ip,0),IP_CH(ip,1),IP_CH(ip,2),IP_CH(ip,3) 410 411typedef enum { 412 command_none_e, 413 command_getoption_e, 414 command_lease_e, 415 command_setparams_e, 416 command_getparams_e, 417} command_t; 418 419int 420main(int argc, char * argv[]) 421{ 422 CFStringRef app_id; 423 command_t command = command_none_e; 424 char * command_str; 425 CFIndex count; 426 CFDictionaryRef info; 427 UInt8 * params; 428 CFStringRef serviceID = NULL; 429 430 command_str = argv[1]; 431 if (argc < 2) 432 usage(argv[0]); 433 if (strcmp(command_str, CMDSTR_GETOPTION) == 0) { 434 if (argc < 4 || argc > 5) { 435 usage(argv[0]); 436 } 437 command = command_getoption_e; 438 } 439 else if (strcmp(command_str, CMDSTR_LEASE) == 0) { 440 if (argc != 3) { 441 usage(argv[0]); 442 } 443 command = command_lease_e; 444 } 445 else if (strcmp(command_str, CMDSTR_SETPARAMS) == 0) { 446 command = command_setparams_e; 447 if (argc < 3) { 448 usage(argv[0]); 449 } 450 } 451 else if (strcmp(command_str, CMDSTR_GETPARAMS) == 0) { 452 command = command_getparams_e; 453 if (argc != 3) { 454 usage(argv[0]); 455 } 456 } 457 else { 458 usage(argv[0]); 459 } 460 461 switch (command) { 462 case command_getoption_e: { 463 UInt8 code; 464 char * code_str; 465 CFDataRef option; 466 boolean_t printed = FALSE; 467 CFIndex len; 468 char * type = NULL; 469 470 if (argv[2][0]) { 471 serviceID = CFStringCreateWithFormat(NULL, NULL, 472 CFSTR("%s"), argv[2]); 473 } 474 475 info = SCDynamicStoreCopyDHCPInfo(NULL, serviceID); 476 if (info == NULL) { 477 exit(1); 478 } 479 480 code_str = argv[3]; 481 if (argc > 4) { 482 type = argv[4]; 483 } 484 code = atoi(code_str); 485 486 option = DHCPInfoGetOptionData(info, code); 487 if (option == NULL) { 488 exit(1); 489 } 490 len = CFDataGetLength(option); 491 if (type) { 492 printed = TRUE; 493 if (strcmp(type, "ip") == 0) { 494 int i = 0; 495 const void * ptr = CFDataGetBytePtr(option); 496 497 while (len >= 4) { 498 if (i == 0) { 499 printf(IP_FORMAT, IP_LIST(ptr)); 500 } 501 else { 502 printf(" " IP_FORMAT, IP_LIST(ptr)); 503 } 504 i++; 505 len -= 4; 506 ptr += 4; 507 } 508 printf("\n"); 509 } 510 else if (strcmp(type, "string") == 0) { 511 printf("%.*s\n", (int)len, (char *)CFDataGetBytePtr(option)); 512 } 513 else { 514 printed = FALSE; 515 } 516 } 517 if (printed == FALSE) { 518 print_data((void *)CFDataGetBytePtr(option), len); 519 } 520 if (serviceID) 521 CFRelease(serviceID); 522 CFRelease(info); 523 break; 524 } 525 case command_lease_e: { 526 CFDateRef start; 527 528 if (argv[2][0]) { 529 serviceID = CFStringCreateWithFormat(NULL, NULL, 530 CFSTR("%s"), argv[2]); 531 } 532 533 info = SCDynamicStoreCopyDHCPInfo(NULL, serviceID); 534 if (info == NULL) { 535 exit(1); 536 } 537 start = DHCPInfoGetLeaseStartTime(info); 538 539 if (start) { 540 CFDataRef option; 541 int32_t lease; 542 543#define OPTION_LEASE_TIME 51 544#define SERVER_ID 54 545 option = DHCPInfoGetOptionData(info, OPTION_LEASE_TIME); 546 if (option == NULL) { 547 fprintf(stderr, "what, no lease time?\n"); 548 exit(1); 549 } 550 printf("Lease start: "); 551 show_date(CFDateGetAbsoluteTime(start)); 552 lease = ntohl(*((int32_t *)CFDataGetBytePtr(option))); 553 if (lease == 0xffffffff) { 554 printf("Lease is infinite\n"); 555 } 556 else { 557 printf("Lease expires: "); 558 show_date(lease + CFDateGetAbsoluteTime(start)); 559 } 560 option = DHCPInfoGetOptionData(info, SERVER_ID); 561 if (option) { 562 printf("Server IP: " IP_FORMAT "\n", 563 IP_LIST(CFDataGetBytePtr(option))); 564 } 565 } 566 else { 567 printf("no lease\n"); 568 } 569 if (serviceID) 570 CFRelease(serviceID); 571 CFRelease(info); 572 break; 573 } 574 case command_getparams_e: { 575 app_id = CFStringCreateWithFormat(NULL, NULL, 576 CFSTR("%s"), argv[2]); 577 params = DHCPClientPreferencesCopyApplicationOptions(app_id, &count); 578 if (params) { 579 printf("%s params = {", argv[2]); 580 S_print_char_array(params, count); 581 printf("}\n"); 582 free(params); 583 } 584 break; 585 } 586 case command_setparams_e: { 587 int count = 0; 588 UInt8 * options = NULL; 589 590 if (argc > 3) { 591 int i; 592 593 count = argc - 3; 594 options = malloc(count); 595 if (options == NULL) { 596 fprintf(stderr, "malloc failed %s\n", 597 strerror(errno)); 598 exit(1); 599 } 600 for (i = 0; i < count; i++) { 601 options[i] = atoi(argv[3 + i]); 602 } 603 } 604 app_id = CFStringCreateWithFormat(NULL, NULL, 605 CFSTR("%s"), argv[2]); 606 if (DHCPClientPreferencesSetApplicationOptions(app_id, options, 607 count) == FALSE) { 608 printf("operation failed\n"); 609 } 610 if (options) { 611 free(options); 612 } 613 break; 614 } 615 default: 616 break; 617 } 618 exit(0); 619 return(0); 620} 621#endif // TEST_DHCPCLIENT_PREFERENCES 622 623