]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/smb-configuration.c
configd-395.7.tar.gz
[apple/configd.git] / Plugins / IPMonitor / smb-configuration.c
1 /*
2 * Copyright (c) 2006-2010 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 #ifdef ADD_NETBIOS_SCOPE
365 // NetBIOS scope
366 str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSScope);
367 str = isA_CFString(str);
368 update_pref(prefs, CFSTR(kSMBPrefNetBIOSScope), str, &changed);
369 #endif // ADD_NETBIOS_SCOPE
370
371 // WINS addresses
372 array = CFDictionaryGetValue(dict, kSCPropNetSMBWINSAddresses);
373 array = isA_CFArray(array);
374 update_pref(prefs, CFSTR(kSMBPrefWINSServerAddressList), array, &changed);
375
376 // Workgroup (or domain)
377 str = CFDictionaryGetValue(dict, kSCPropNetSMBWorkgroup);
378 str = isA_CFString(str);
379 update_pref(prefs, CFSTR(kSMBPrefWorkgroup), str, &changed);
380
381 if (changed) {
382 ok = SCPreferencesCommitChanges(prefs);
383 if (!ok) {
384 SCLog((SCError() != EROFS), LOG_ERR,
385 CFSTR("smb_set_configuration: SCPreferencesCommitChanges() failed: %s"),
386 SCErrorString(SCError()));
387 goto done;
388 }
389
390 ok = SCPreferencesApplyChanges(prefs);
391 if (!ok) {
392 SCLog(TRUE, LOG_ERR,
393 CFSTR("smb_set_configuration: SCPreferencesApplyChanges() failed: %s"),
394 SCErrorString(SCError()));
395 goto done;
396 }
397 }
398
399 done :
400
401 (void) SCPreferencesUnlock(prefs);
402 CFRelease(prefs);
403 return;
404 }
405
406
407 static CFStringRef
408 copy_primary_service(SCDynamicStoreRef store)
409 {
410 CFDictionaryRef dict;
411 CFStringRef key;
412 CFStringRef serviceID = NULL;
413
414 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
415 kSCDynamicStoreDomainState,
416 kSCEntNetIPv4);
417 dict = SCDynamicStoreCopyValue(store, key);
418 CFRelease(key);
419
420 if (dict != NULL) {
421 if (isA_CFDictionary(dict)) {
422 serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService);
423 if (isA_CFString(serviceID)) {
424 CFRetain(serviceID);
425 } else {
426 serviceID = NULL;
427 }
428 }
429 CFRelease(dict);
430 }
431
432 return serviceID;
433 }
434
435
436 static CFStringRef
437 copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID)
438 {
439 CFStringRef address = NULL;
440 CFDictionaryRef dict;
441 CFStringRef key;
442
443 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
444 kSCDynamicStoreDomainState,
445 serviceID,
446 kSCEntNetIPv4);
447 dict = SCDynamicStoreCopyValue(store, key);
448 CFRelease(key);
449
450 if (dict != NULL) {
451 if (isA_CFDictionary(dict)) {
452 CFArrayRef addresses;
453
454 addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses);
455 if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) {
456 address = CFArrayGetValueAtIndex(addresses, 0);
457 if (isA_CFString(address)) {
458 CFRetain(address);
459 } else {
460 address = NULL;
461 }
462 }
463 }
464 CFRelease(dict);
465 }
466
467 return address;
468 }
469
470
471 static void
472 reverseDNSComplete(int32_t status, char *host, char *serv, void *context)
473 {
474 CFDictionaryRef dict;
475 struct timeval dnsQueryComplete;
476 struct timeval dnsQueryElapsed;
477 CFStringRef name;
478 SCDynamicStoreRef store = (SCDynamicStoreRef)context;
479
480 (void) gettimeofday(&dnsQueryComplete, NULL);
481 timersub(&dnsQueryComplete, &dnsQueryStart, &dnsQueryElapsed);
482 SCLog(_verbose, LOG_INFO,
483 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
484 ((status == 0) && (host != NULL)) ? "" : ", host not found",
485 dnsQueryElapsed.tv_sec,
486 dnsQueryElapsed.tv_usec / 1000);
487
488 // get network configuration
489 dict = smb_copy_global_configuration(store);
490
491 // use NetBIOS name from network configuration (if available)
492 name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
493 if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) {
494 SCLog(TRUE, LOG_INFO, CFSTR("NetBIOS name (network configuration) = %@"), name);
495 goto set;
496 }
497
498 // use reverse DNS name, if available
499 switch (status) {
500 case 0 :
501 /*
502 * if [reverse] DNS query was successful
503 */
504 if (host != NULL) {
505 char *dot;
506
507 dot = strchr(host, '.');
508 name = CFStringCreateWithBytes(NULL,
509 (UInt8 *)host,
510 (dot != NULL) ? dot - host : strlen(host),
511 kCFStringEncodingUTF8,
512 FALSE);
513 if (name != NULL) {
514 if (_SC_CFStringIsValidNetBIOSName(name)) {
515 CFMutableDictionaryRef newDict;
516
517 SCLog(TRUE, LOG_INFO, CFSTR("NetBIOS name (reverse DNS query) = %@"), name);
518 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
519 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
520 CFRelease(dict);
521 dict = newDict;
522 CFRelease(name);
523 goto set;
524 }
525
526 CFRelease(name);
527 }
528 }
529 break;
530
531 case EAI_NONAME :
532 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
533 case EAI_NODATA:
534 #endif
535 /*
536 * if no name available
537 */
538 break;
539
540 default :
541 /*
542 * Hmmmm...
543 */
544 SCLog(TRUE, LOG_ERR, CFSTR("getnameinfo() failed: %s"), gai_strerror(status));
545 }
546
547 // try local (multicast DNS) name, if available
548 name = SCDynamicStoreCopyLocalHostName(store);
549 if (name != NULL) {
550 if (_SC_CFStringIsValidNetBIOSName(name)) {
551 CFMutableDictionaryRef newDict;
552
553 SCLog(TRUE, LOG_INFO, CFSTR("NetBIOS name (multicast DNS) = %@"), name);
554 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
555 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
556 CFRelease(dict);
557 dict = newDict;
558 CFRelease(name);
559 goto set;
560 }
561 CFRelease(name);
562 }
563
564 // use "default" name
565 name = copy_default_name();
566 if (name != NULL) {
567 CFMutableDictionaryRef newDict;
568
569 SCLog(TRUE, LOG_INFO, CFSTR("NetBIOS name (default) = %@"), name);
570 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
571 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
572 CFRelease(dict);
573 dict = newDict;
574 CFRelease(name);
575 }
576
577 set :
578
579 // update SMB configuration
580 smb_set_configuration(store, dict);
581
582 if (host != NULL) free(host);
583 if (dict != NULL) CFRelease(dict);
584 if (serv != NULL) free(serv);
585 dnsActive = FALSE;
586 return;
587 }
588
589
590 static CFStringRef
591 replyMPCopyDescription(const void *info)
592 {
593 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
594
595 return CFStringCreateWithFormat(NULL,
596 NULL,
597 CFSTR("<getnameinfo_async_start reply MP> {store = %p}"),
598 store);
599 }
600
601
602 static void
603 getnameinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
604 {
605 mach_port_t mp = MACH_PORT_NULL;
606 int32_t status;
607
608 if (port != dnsPort) {
609 // we've received a callback on the async DNS port but since the
610 // associated CFMachPort doesn't match than the request must have
611 // already been cancelled.
612 SCLog(TRUE, LOG_ERR, CFSTR("getnameinfo_async_handleCFReply(): port != dnsPort"));
613 return;
614 }
615
616 mp = CFMachPortGetPort(port);
617 CFMachPortInvalidate(dnsPort);
618 CFRelease(dnsPort);
619 dnsPort = NULL;
620
621 status = getnameinfo_async_handle_reply(msg);
622 if ((status == 0) && dnsActive && (mp != MACH_PORT_NULL)) {
623 CFMachPortContext context = { 0
624 , (void *)store
625 , CFRetain
626 , CFRelease
627 , replyMPCopyDescription
628 };
629 CFRunLoopSourceRef rls;
630
631 // if request has been re-queued
632 dnsPort = _SC_CFMachPortCreateWithPort("IPMonitor/smb-configuration/re-queue",
633 mp,
634 getnameinfo_async_handleCFReply,
635 &context);
636 rls = CFMachPortCreateRunLoopSource(NULL, dnsPort, 0);
637 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
638 CFRelease(rls);
639 }
640
641 return;
642 }
643
644
645 static Boolean
646 start_dns_query(SCDynamicStoreRef store, CFStringRef address)
647 {
648 char addr[64];
649 SCNetworkReachabilityFlags flags;
650 Boolean haveDNS;
651 Boolean ok = FALSE;
652 struct sockaddr *sa;
653 struct sockaddr_in sin;
654 struct sockaddr_in6 sin6;
655
656 if (_SC_cfstring_to_cstring(address, addr, sizeof(addr), kCFStringEncodingASCII) == NULL) {
657 SCLog(TRUE, LOG_ERR, CFSTR("could not convert [primary] address"));
658 return FALSE;
659 }
660
661 bzero(&sin, sizeof(sin));
662 sin.sin_len = sizeof(sin);
663 sin.sin_family = AF_INET;
664
665 bzero(&sin6, sizeof(sin6));
666 sin6.sin6_len = sizeof(sin6);
667 sin6.sin6_family = AF_INET6;
668
669 if (inet_aton(addr, &sin.sin_addr) == 1) {
670 /*
671 * if IPv4 address
672 */
673 sa = (struct sockaddr *)&sin;
674 } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr) == 1) {
675 /*
676 * if IPv6 address
677 */
678 char *p;
679
680 p = strchr(addr, '%');
681 if (p != NULL) {
682 sin6.sin6_scope_id = if_nametoindex(p + 1);
683 }
684
685 sa = (struct sockaddr *)&sin6;
686 } else {
687 goto done;
688 }
689
690 ok = _SC_checkResolverReachabilityByAddress(&store, &flags, &haveDNS, sa);
691 if (ok) {
692 if (!(flags & kSCNetworkReachabilityFlagsReachable) ||
693 (flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
694 // if not reachable *OR* connection required
695 ok = FALSE;
696 }
697 }
698
699 if (ok) {
700 CFMachPortContext context = { 0
701 , (void *)store
702 , CFRetain
703 , CFRelease
704 , replyMPCopyDescription
705 };
706 int32_t error;
707 mach_port_t mp;
708 CFRunLoopSourceRef rls;
709
710 (void) gettimeofday(&dnsQueryStart, NULL);
711
712 error = getnameinfo_async_start(&mp,
713 sa,
714 sa->sa_len,
715 NI_NAMEREQD, // flags
716 reverseDNSComplete,
717 (void *)store);
718 if (error != 0) {
719 ok = FALSE;
720 goto done;
721 }
722
723 dnsActive = TRUE;
724 dnsPort = _SC_CFMachPortCreateWithPort("IPMonitor/smb-configuration",
725 mp,
726 getnameinfo_async_handleCFReply,
727 &context);
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 */