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