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