1/* 2 * Copyright (c) 2006-2014 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 * Modification History 26 * 27 * June 26, 2006 Allan Nathanson <ajn@apple.com> 28 * - initial revision 29 */ 30 31#include <ctype.h> 32#include <stdio.h> 33#include <unistd.h> 34#include <sys/param.h> 35#include <sys/types.h> 36#include <sys/socket.h> 37#include <sys/stat.h> 38#include <sys/sysctl.h> 39#include <sys/time.h> 40#include <net/if.h> 41#include <net/if_dl.h> 42#include <netinet/in.h> 43#include <arpa/inet.h> 44#include <netdb_async.h> 45#include <notify.h> 46#include <smb_server_prefs.h> 47 48#include <CoreFoundation/CoreFoundation.h> 49#include <CoreFoundation/CFStringDefaultEncoding.h> // for __CFStringGetInstallationEncodingAndRegion() 50#include <SystemConfiguration/SystemConfiguration.h> 51#include <SystemConfiguration/SCValidation.h> 52#include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint() 53 54#ifdef MAIN 55#define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__) 56#else // MAIN 57#include "ip_plugin.h" 58#endif // MAIN 59 60#define HW_MODEL_LEN 64 // Note: must be >= NETBIOS_NAME_LEN (below) 61 62#define NETBIOS_NAME_LEN 16 63 64#define SMB_STARTUP_DELAY 60.0 65#define SMB_DEBOUNCE_DELAY 5.0 66 67static SCDynamicStoreRef store = NULL; 68static CFRunLoopSourceRef rls = NULL; 69 70static struct timeval ptrQueryStart; 71static SCNetworkReachabilityRef ptrTarget = NULL; 72 73static CFRunLoopTimerRef timer = NULL; 74 75static Boolean _verbose = FALSE; 76 77 78static CFAbsoluteTime 79boottime(void) 80{ 81 static CFAbsoluteTime bt = 0; 82 83 if (bt == 0) { 84 int mib[2] = { CTL_KERN, KERN_BOOTTIME }; 85 struct timeval tv; 86 size_t tv_len = sizeof(tv); 87 88 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tv, &tv_len, NULL, 0) == -1) { 89 my_log(LOG_ERR, "sysctl() CTL_KERN/KERN_BOOTTIME failed: %s", strerror(errno)); 90 return kCFAbsoluteTimeIntervalSince1970; 91 } 92 93 // Note: we need to convert from Unix time to CF time. 94 bt = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970; 95 bt += (1.0E-6 * (CFTimeInterval)tv.tv_usec); 96 } 97 98 return bt; 99} 100 101 102static CFStringRef 103copy_default_name(void) 104{ 105 CFStringRef model; 106 size_t n; 107 CFMutableStringRef str; 108 109 // get HW model name 110 model = _SC_hw_model(TRUE); 111 if (model == NULL) { 112 return NULL; 113 } 114 115 // start off with the [trunated] HW model 116 str = CFStringCreateMutable(NULL, 0); 117 CFStringAppend(str, model); 118 119 // truncate as needed 120 n = CFStringGetLength(str); 121 if (n > (NETBIOS_NAME_LEN - 1)) { 122 CFStringReplace(str, 123 CFRangeMake(NETBIOS_NAME_LEN, n - (NETBIOS_NAME_LEN - 1)), 124 CFSTR("")); 125 n = NETBIOS_NAME_LEN - 1; 126 } 127 128 // 129 // if there is room for at least one byte (two hex characters) 130 // of the MAC address than append that to the NetBIOS name. 131 // 132 // NETBIOS_NAME_LEN max length 133 // -1 the last byte is reserved 134 // -3 "-XX" 135 // 136 if (n < (NETBIOS_NAME_LEN - 1 - 3)) { 137 SCNetworkInterfaceRef interface; 138 139 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, CFSTR("en0"), 140 kIncludeNoVirtualInterfaces); 141 if (interface != NULL) { 142 CFMutableStringRef en0_MAC; 143 144 en0_MAC = (CFMutableStringRef)SCNetworkInterfaceGetHardwareAddressString(interface); 145 if (en0_MAC != NULL) { 146 CFIndex en0_MAC_len; 147 148 // remove ":" characters from MAC address string 149 en0_MAC = CFStringCreateMutableCopy(NULL, 0, en0_MAC); 150 CFStringFindAndReplace(en0_MAC, 151 CFSTR(":"), 152 CFSTR(""), 153 CFRangeMake(0, CFStringGetLength(en0_MAC)), 154 0); 155 156 // 157 // compute how may bytes (characters) to append 158 // ... and limit that number to 6 159 // 160 // NETBIOS_NAME_LEN max length 161 // -1 the last byte is reserved 162 // -n "iMac" 163 // -1 "-" 164 // 165 n = ((NETBIOS_NAME_LEN - 1 - n - 1) / 2) * 2; 166 if (n > 6) { 167 n = 6; 168 } 169 170 // remove what we don't want 171 en0_MAC_len = CFStringGetLength(en0_MAC); 172 if (en0_MAC_len > n) { 173 CFStringDelete(en0_MAC, CFRangeMake(0, en0_MAC_len - n)); 174 } 175 176 // append 177 CFStringAppendFormat(str, NULL, CFSTR("-%@"), en0_MAC); 178 CFRelease(en0_MAC); 179 } 180 181 CFRelease(interface); 182 } 183 } 184 185 CFStringUppercase(str, NULL); 186 return str; 187} 188 189 190static CFDictionaryRef 191smb_copy_global_configuration(SCDynamicStoreRef store) 192{ 193 CFDictionaryRef dict; 194 CFStringRef key; 195 196 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 197 kSCDynamicStoreDomainState, 198 kSCEntNetSMB); 199 dict = SCDynamicStoreCopyValue(store, key); 200 CFRelease(key); 201 202 if (dict != NULL) { 203 if (isA_CFDictionary(dict)) { 204 return dict; 205 } 206 207 CFRelease(dict); 208 } 209 210 dict = CFDictionaryCreate(NULL, // allocator 211 NULL, // keys 212 NULL, // values 213 0, // numValues 214 &kCFTypeDictionaryKeyCallBacks, 215 &kCFTypeDictionaryValueCallBacks); 216 return dict; 217} 218 219 220static void 221update_pref(SCPreferencesRef prefs, CFStringRef key, CFTypeRef newVal, Boolean *changed) 222{ 223 CFTypeRef curVal; 224 225 curVal = SCPreferencesGetValue(prefs, key); 226 if (!_SC_CFEqual(curVal, newVal)) { 227 if (newVal != NULL) { 228 SCPreferencesSetValue(prefs, key, newVal); 229 } else { 230 SCPreferencesRemoveValue(prefs, key); 231 } 232 233 *changed = TRUE; 234 } 235 236 return; 237} 238 239 240static void 241smb_set_configuration(SCDynamicStoreRef store, CFDictionaryRef dict) 242{ 243 CFArrayRef array; 244 Boolean changed = FALSE; 245 UInt32 dosCodepage = 0; 246 CFStringEncoding dosEncoding = 0; 247 CFStringEncoding macEncoding = kCFStringEncodingMacRoman; 248 uint32_t macRegion = 0; 249 Boolean ok; 250 SCPreferencesRef prefs; 251 CFStringRef str; 252 253 prefs = SCPreferencesCreate(NULL, CFSTR("smb-configuration"), CFSTR(kSMBPreferencesAppID)); 254 if (prefs == NULL) { 255 my_log(LOG_ERR, 256 "smb_set_configuration: SCPreferencesCreate() failed: %s", 257 SCErrorString(SCError())); 258 return; 259 } 260 261 ok = SCPreferencesLock(prefs, TRUE); 262 if (!ok) { 263 my_log(LOG_ERR, 264 "smb_set_configuration: SCPreferencesLock() failed: %s", 265 SCErrorString(SCError())); 266 goto done; 267 } 268 269 // Server description 270 str = SCDynamicStoreCopyComputerName(store, &macEncoding); 271 update_pref(prefs, CFSTR(kSMBPrefServerDescription), str, &changed); 272 273 // DOS code page 274 if (str != NULL) { 275 if (macEncoding == kCFStringEncodingMacRoman) { 276 CFStringRef key; 277 CFDictionaryRef dict; 278 279 // get region 280 key = SCDynamicStoreKeyCreateComputerName(NULL); 281 dict = SCDynamicStoreCopyValue(store, key); 282 CFRelease(key); 283 if (dict != NULL) { 284 if (isA_CFDictionary(dict)) { 285 CFNumberRef num; 286 SInt32 val; 287 288 num = CFDictionaryGetValue(dict, kSCPropSystemComputerNameRegion); 289 if (isA_CFNumber(num) && 290 CFNumberGetValue(num, kCFNumberSInt32Type, &val)) { 291 macRegion = (uint32_t)val; 292 } 293 } 294 295 CFRelease(dict); 296 } 297 } 298 299 CFRelease(str); 300 } else { 301 // Important: must have root acccess (eUID==0) to access the config file! 302 __CFStringGetInstallationEncodingAndRegion((uint32_t *)&macEncoding, &macRegion); 303 } 304 _SC_dos_encoding_and_codepage(macEncoding, macRegion, &dosEncoding, &dosCodepage); 305 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (unsigned int)dosCodepage); 306 assert(str != NULL); 307 update_pref(prefs, CFSTR(kSMBPrefDOSCodePage), str, &changed); 308 CFRelease(str); 309 310 // NetBIOS name 311 str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName); 312 str = isA_CFString(str); 313 update_pref(prefs, CFSTR(kSMBPrefNetBIOSName), str, &changed); 314 315 // NetBIOS node type 316 str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSNodeType); 317 str = isA_CFString(str); 318 if (str != NULL) { 319 if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeBroadcast)) { 320 // B-node 321 str = CFSTR(kSMBPrefNetBIOSNodeBroadcast); 322 } else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypePeer)) { 323 // P-node 324 str = CFSTR(kSMBPrefNetBIOSNodePeer); 325 } else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeMixed)) { 326 // M-node 327 str = CFSTR(kSMBPrefNetBIOSNodeMixed); 328 } else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeHybrid)) { 329 // H-node 330 str = CFSTR(kSMBPrefNetBIOSNodeHybrid); 331 } else { 332 str = NULL; 333 } 334 } 335 update_pref(prefs, CFSTR(kSMBPrefNetBIOSNodeType), str, &changed); 336 337#ifdef ADD_NETBIOS_SCOPE 338 // NetBIOS scope 339 str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSScope); 340 str = isA_CFString(str); 341 update_pref(prefs, CFSTR(kSMBPrefNetBIOSScope), str, &changed); 342#endif // ADD_NETBIOS_SCOPE 343 344 // WINS addresses 345 array = CFDictionaryGetValue(dict, kSCPropNetSMBWINSAddresses); 346 array = isA_CFArray(array); 347 update_pref(prefs, CFSTR(kSMBPrefWINSServerAddressList), array, &changed); 348 349 // Workgroup (or domain) 350 str = CFDictionaryGetValue(dict, kSCPropNetSMBWorkgroup); 351 str = isA_CFString(str); 352 update_pref(prefs, CFSTR(kSMBPrefWorkgroup), str, &changed); 353 354 if (changed) { 355 ok = SCPreferencesCommitChanges(prefs); 356 if (!ok) { 357 if ((SCError() != EROFS)) { 358 my_log(LOG_ERR, 359 "smb_set_configuration: SCPreferencesCommitChanges() failed: %s", 360 SCErrorString(SCError())); 361 } 362 goto done; 363 } 364 365 ok = SCPreferencesApplyChanges(prefs); 366 if (!ok) { 367 my_log(LOG_ERR, 368 "smb_set_configuration: SCPreferencesApplyChanges() failed: %s", 369 SCErrorString(SCError())); 370 goto done; 371 } 372 } 373 374 done : 375 376 (void) SCPreferencesUnlock(prefs); 377 CFRelease(prefs); 378 return; 379} 380 381 382static CFStringRef 383copy_primary_service(SCDynamicStoreRef store) 384{ 385 CFDictionaryRef dict; 386 CFStringRef key; 387 CFStringRef serviceID = NULL; 388 389 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 390 kSCDynamicStoreDomainState, 391 kSCEntNetIPv4); 392 dict = SCDynamicStoreCopyValue(store, key); 393 CFRelease(key); 394 395 if (dict != NULL) { 396 if (isA_CFDictionary(dict)) { 397 serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService); 398 if (isA_CFString(serviceID)) { 399 CFRetain(serviceID); 400 } else { 401 serviceID = NULL; 402 } 403 } 404 CFRelease(dict); 405 } 406 407 return serviceID; 408} 409 410 411static CFStringRef 412copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID) 413{ 414 CFStringRef address = NULL; 415 CFDictionaryRef dict; 416 CFStringRef key; 417 418 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 419 kSCDynamicStoreDomainState, 420 serviceID, 421 kSCEntNetIPv4); 422 dict = SCDynamicStoreCopyValue(store, key); 423 CFRelease(key); 424 425 if (dict != NULL) { 426 if (isA_CFDictionary(dict)) { 427 CFArrayRef addresses; 428 429 addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses); 430 if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) { 431 address = CFArrayGetValueAtIndex(addresses, 0); 432 if (isA_CFString(address)) { 433 CFRetain(address); 434 } else { 435 address = NULL; 436 } 437 } 438 } 439 CFRelease(dict); 440 } 441 442 return address; 443} 444 445 446static void 447ptr_query_stop() 448{ 449 if (ptrTarget == NULL) { 450 return; 451 } 452 453 SCNetworkReachabilitySetCallback(ptrTarget, NULL, NULL); 454 SCNetworkReachabilityUnscheduleFromRunLoop(ptrTarget, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 455 CFRelease(ptrTarget); 456 ptrTarget = NULL; 457 458 return; 459} 460 461 462static void 463ptr_query_callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) 464{ 465 CFDictionaryRef dict; 466 CFStringRef name; 467 CFMutableDictionaryRef newDict; 468 struct timeval ptrQueryComplete; 469 struct timeval ptrQueryElapsed; 470 471 (void) gettimeofday(&ptrQueryComplete, NULL); 472 timersub(&ptrQueryComplete, &ptrQueryStart, &ptrQueryElapsed); 473 if (_verbose) { 474 my_log(LOG_DEBUG, "ptr query complete%s (query time = %ld.%3.3d)", 475 (flags & kSCNetworkReachabilityFlagsReachable) ? "" : ", host not found", 476 ptrQueryElapsed.tv_sec, 477 ptrQueryElapsed.tv_usec / 1000); 478 } 479 480 // get network configuration 481 dict = smb_copy_global_configuration(store); 482 483 // use NetBIOS name from network configuration (if available) 484 name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName); 485 if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) { 486 my_log(LOG_DEBUG, "NetBIOS name (network configuration) = %@", name); 487 goto setDict; 488 } 489 490 // use reverse DNS name, if available 491 492 name = NULL; 493 if (flags & kSCNetworkReachabilityFlagsReachable) { 494 int error_num; 495 CFArrayRef hosts; 496 497 /* 498 * if [reverse] DNS query was successful 499 */ 500 hosts = SCNetworkReachabilityCopyResolvedAddress(target, &error_num); 501 if (hosts != NULL) { 502 if (CFArrayGetCount(hosts) > 0) { 503 CFIndex ptrLen; 504 CFMutableStringRef ptrName; 505 CFRange range; 506 507 name = CFArrayGetValueAtIndex(hosts, 0); 508 ptrName = CFStringCreateMutableCopy(NULL, 0, name); 509 ptrLen = CFStringGetLength(ptrName); 510 if (CFStringFindWithOptions(ptrName, 511 CFSTR("."), 512 CFRangeMake(0, ptrLen), 513 0, 514 &range)) { 515 CFStringDelete(ptrName, 516 CFRangeMake(range.location, ptrLen - range.location)); 517 } 518 name = ptrName; 519 } 520 CFRelease(hosts); 521 } 522 } 523 if (name != NULL) { 524 if (_SC_CFStringIsValidNetBIOSName(name)) { 525 my_log(LOG_DEBUG, "NetBIOS name (reverse DNS query) = %@", name); 526 goto setName; 527 } 528 CFRelease(name); 529 } 530 531 // try local (multicast DNS) name, if available 532 name = SCDynamicStoreCopyLocalHostName(store); 533 if (name != NULL) { 534 if (_SC_CFStringIsValidNetBIOSName(name)) { 535 my_log(LOG_DEBUG, "NetBIOS name (multicast DNS) = %@", name); 536 goto setName; 537 } 538 CFRelease(name); 539 } 540 541 // use "default" name 542 name = copy_default_name(); 543 if (name != NULL) { 544 my_log(LOG_DEBUG, "NetBIOS name (default) = %@", name); 545 goto setName; 546 } 547 548 goto setDict; 549 550 setName : 551 552 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 553 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name); 554 CFRelease(dict); 555 dict = newDict; 556 CFRelease(name); 557 558 setDict : 559 560 // update SMB configuration 561 smb_set_configuration(store, dict); 562 CFRelease(dict); 563 564 ptr_query_stop(); 565 566#ifdef MAIN 567 CFRunLoopStop(CFRunLoopGetCurrent()); 568#endif // MAIN 569 570 return; 571} 572 573 574static Boolean 575ptr_query_start(CFStringRef address) 576{ 577 union { 578 struct sockaddr sa; 579 struct sockaddr_in sin; 580 struct sockaddr_in6 sin6; 581 } addr; 582 char buf[64]; 583 CFDataRef data; 584 CFMutableDictionaryRef options; 585 586 if (_SC_cfstring_to_cstring(address, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) { 587 my_log(LOG_ERR, "could not convert [primary] address string"); 588 return FALSE; 589 } 590 591 if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) { 592 my_log(LOG_ERR, "could not convert [primary] address"); 593 return FALSE; 594 } 595 596 options = CFDictionaryCreateMutable(NULL, 597 0, 598 &kCFTypeDictionaryKeyCallBacks, 599 &kCFTypeDictionaryValueCallBacks); 600 data = CFDataCreate(NULL, (const UInt8 *)&addr.sa, addr.sa.sa_len); 601 CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data); 602 CFRelease(data); 603 ptrTarget = SCNetworkReachabilityCreateWithOptions(NULL, options); 604 CFRelease(options); 605 if (ptrTarget == NULL) { 606 my_log(LOG_ERR, "could not resolve [primary] address"); 607 return FALSE; 608 } 609 610 (void) SCNetworkReachabilitySetCallback(ptrTarget, ptr_query_callback, NULL); 611 (void) SCNetworkReachabilityScheduleWithRunLoop(ptrTarget, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 612 613 return TRUE; 614} 615 616 617static void 618smb_update_configuration(__unused CFRunLoopTimerRef _timer, void *info) 619{ 620 CFStringRef address = NULL; 621 CFDictionaryRef dict; 622 CFStringRef name; 623 CFStringRef serviceID = NULL; 624 SCDynamicStoreRef store = (SCDynamicStoreRef)info; 625 626 // get network configuration 627 dict = smb_copy_global_configuration(store); 628 629 // use NetBIOS name from network configuration (if available) 630 name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName); 631 if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) { 632 my_log(LOG_DEBUG, "NetBIOS name (network configuration) = %@", name); 633 goto set; 634 } 635 636 // get primary service ID 637 serviceID = copy_primary_service(store); 638 if (serviceID == NULL) { 639 // if no primary service 640 goto mDNS; 641 } 642 643 // get DNS name associated with primary IP, if available 644 address = copy_primary_ip(store, serviceID); 645 if (address != NULL) { 646 Boolean ok; 647 648 // start reverse DNS query using primary IP address 649 ok = ptr_query_start(address); 650 if (ok) { 651 // if query started 652 goto done; 653 } 654 } 655 656 mDNS : 657 658 // get local (multicast DNS) name, if available 659 660 name = SCDynamicStoreCopyLocalHostName(store); 661 if (name != NULL) { 662 if (_SC_CFStringIsValidNetBIOSName(name)) { 663 CFMutableDictionaryRef newDict; 664 665 my_log(LOG_DEBUG, "NetBIOS name (multicast DNS) = %@", name); 666 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 667 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name); 668 CFRelease(dict); 669 dict = newDict; 670 CFRelease(name); 671 goto set; 672 } 673 CFRelease(name); 674 } 675 676 // get "default" name 677 name = copy_default_name(); 678 if (name != NULL) { 679 CFMutableDictionaryRef newDict; 680 681 my_log(LOG_DEBUG, "NetBIOS name (default) = %@", name); 682 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 683 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name); 684 CFRelease(dict); 685 dict = newDict; 686 CFRelease(name); 687 } 688 689 set : 690 691 // update SMB configuration 692 smb_set_configuration(store, dict); 693 694 done : 695 696 if (address != NULL) CFRelease(address); 697 if (dict != NULL) CFRelease(dict); 698 if (serviceID != NULL) CFRelease(serviceID); 699 700 if (timer != NULL) { 701 CFRunLoopTimerInvalidate(timer); 702 CFRelease(timer); 703 timer = NULL; 704 } 705 706 return; 707} 708 709 710static void 711configuration_changed(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 712{ 713 CFRunLoopTimerContext context = { 0, (void *)store, CFRetain, CFRelease, NULL }; 714 CFAbsoluteTime time_boot; 715 CFAbsoluteTime time_now ; 716 717 // if active, cancel any in-progress attempt to resolve the primary IP address 718 719 if (ptrTarget != NULL) { 720 ptr_query_stop(); 721 } 722 723 // if active, cancel any queued configuration change 724 if (timer != NULL) { 725 CFRunLoopTimerInvalidate(timer); 726 CFRelease(timer); 727 timer = NULL; 728 } 729 730 // queue configuration change 731 time_boot = boottime() + SMB_STARTUP_DELAY; 732 time_now = CFAbsoluteTimeGetCurrent() + SMB_DEBOUNCE_DELAY; 733 734 timer = CFRunLoopTimerCreate(NULL, 735 time_now > time_boot ? time_now : time_boot, 736 0, 737 0, 738 0, 739 smb_update_configuration, 740 &context); 741 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); 742 743 return; 744} 745 746 747__private_extern__ 748void 749load_smb_configuration(Boolean verbose) 750{ 751 CFStringRef key; 752 CFMutableArrayRef keys = NULL; 753 CFMutableArrayRef patterns = NULL; 754 755 if (verbose) { 756 _verbose = TRUE; 757 } 758 759 /* initialize a few globals */ 760 761 store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), configuration_changed, NULL); 762 if (store == NULL) { 763 my_log(LOG_ERR, 764 "SCDynamicStoreCreate() failed: %s", 765 SCErrorString(SCError())); 766 goto error; 767 } 768 769 /* establish notification keys and patterns */ 770 771 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 772 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 773 774 /* ...watch for primary service / interface changes */ 775 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 776 kSCDynamicStoreDomainState, 777 kSCEntNetIPv4); 778 CFArrayAppendValue(keys, key); 779 CFRelease(key); 780 781 /* ...watch for DNS configuration changes */ 782 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 783 kSCDynamicStoreDomainState, 784 kSCEntNetDNS); 785 CFArrayAppendValue(keys, key); 786 CFRelease(key); 787 788 /* ...watch for SMB configuration changes */ 789 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 790 kSCDynamicStoreDomainState, 791 kSCEntNetSMB); 792 CFArrayAppendValue(keys, key); 793 CFRelease(key); 794 795 /* ...watch for ComputerName changes */ 796 key = SCDynamicStoreKeyCreateComputerName(NULL); 797 CFArrayAppendValue(keys, key); 798 CFRelease(key); 799 800 /* ...watch for local (multicast DNS) hostname changes */ 801 key = SCDynamicStoreKeyCreateHostNames(NULL); 802 CFArrayAppendValue(keys, key); 803 CFRelease(key); 804 805 /* register the keys/patterns */ 806 if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) { 807 my_log(LOG_ERR, 808 "SCDynamicStoreSetNotificationKeys() failed: %s", 809 SCErrorString(SCError())); 810 goto error; 811 } 812 813 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 814 if (!rls) { 815 my_log(LOG_ERR, 816 "SCDynamicStoreCreateRunLoopSource() failed: %s", 817 SCErrorString(SCError())); 818 goto error; 819 } 820 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 821 822 CFRelease(keys); 823 CFRelease(patterns); 824 return; 825 826 error : 827 828 if (keys != NULL) CFRelease(keys); 829 if (patterns != NULL) CFRelease(patterns); 830 if (store != NULL) CFRelease(store); 831 return; 832} 833 834 835#ifdef MAIN 836int 837main(int argc, char **argv) 838{ 839 840#ifdef DEBUG 841 CFStringRef address; 842 CFStringRef name; 843 CFStringRef serviceID; 844 SCDynamicStoreRef store; 845 846 _sc_log = FALSE; 847 if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) { 848 _sc_verbose = TRUE; 849 _verbose = TRUE; 850 argv++; 851 argc--; 852 } 853 854 store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), NULL, NULL); 855 if (store == NULL) { 856 SCPrint(TRUE, stdout, 857 CFSTR("SCDynamicStoreCreate() failed: %s\n"), 858 SCErrorString(SCError())); 859 exit(1); 860 } 861 862 // get "default" name 863 name = copy_default_name(); 864 if (name != NULL) { 865 SCPrint(TRUE, stdout, CFSTR("default name = %@\n"), name); 866 CFRelease(name); 867 } 868 869 // get primary service 870 serviceID = copy_primary_service(store); 871 if (serviceID != NULL) { 872 SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID); 873 } else { 874 SCPrint(TRUE, stdout, CFSTR("No primary service\n")); 875 goto done; 876 } 877 878 if ((argc == (2+1)) && (argv[1][0] == 's')) { 879 if (serviceID != NULL) CFRelease(serviceID); 880 serviceID = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8); 881 SCPrint(TRUE, stdout, CFSTR("alternate service ID = %@\n"), serviceID); 882 } 883 884 // get primary IP address 885 address = copy_primary_ip(store, serviceID); 886 CFRelease(serviceID); 887 if (address != NULL) { 888 SCPrint(TRUE, stdout, CFSTR("primary address = %@\n"), address); 889 890 if ((argc == (2+1)) && (argv[1][0] == 'a')) { 891 if (address != NULL) CFRelease(address); 892 address = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8); 893 SCPrint(TRUE, stdout, CFSTR("alternate primary address = %@\n"), address); 894 } 895 896 // start reverse DNS query using primary IP address 897 (void) ptr_query_start(address); 898 CFRelease(address); 899 } 900 901 done : 902 903 smb_update_configuration(NULL, (void *)store); 904 905 CFRelease(store); 906 907 CFRunLoopRun(); 908 909#else /* DEBUG */ 910 911 _sc_log = FALSE; 912 _sc_verbose = (argc > 1) ? TRUE : FALSE; 913 914 load_smb_configuration((argc > 1) ? TRUE : FALSE); 915 CFRunLoopRun(); 916 /* not reached */ 917 918#endif /* DEBUG */ 919 920 exit(0); 921 return 0; 922} 923#endif /* MAIN */ 924