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