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