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