]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/smb-configuration.c
configd-888.20.5.tar.gz
[apple/configd.git] / Plugins / IPMonitor / smb-configuration.c
1 /*
2 * Copyright (c) 2006-2016 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
67 static SCDynamicStoreRef store = NULL;
68 static CFRunLoopRef rl = NULL;
69 static CFRunLoopSourceRef rls = 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 size_t 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, 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 CFDictionaryRef dict;
469 CFStringRef name;
470 CFMutableDictionaryRef newDict;
471 struct timeval ptrQueryComplete;
472 struct timeval ptrQueryElapsed;
473
474 (void) gettimeofday(&ptrQueryComplete, NULL);
475 timersub(&ptrQueryComplete, &ptrQueryStart, &ptrQueryElapsed);
476 my_log(LOG_INFO, "NetBIOS name: ptr query complete%s (query time = %ld.%3.3d)",
477 (flags & kSCNetworkReachabilityFlagsReachable) ? "" : ", host not found",
478 ptrQueryElapsed.tv_sec,
479 ptrQueryElapsed.tv_usec / 1000);
480
481 // get network configuration
482 dict = smb_copy_global_configuration(store);
483
484 // use NetBIOS name from network configuration (if available)
485 name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
486 if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) {
487 my_log(LOG_INFO, "NetBIOS name (network configuration) = %@", name);
488 goto setDict;
489 }
490
491 // use reverse DNS name, if available
492
493 name = NULL;
494 if (flags & kSCNetworkReachabilityFlagsReachable) {
495 int error_num;
496 CFArrayRef hosts;
497
498 /*
499 * if [reverse] DNS query was successful
500 */
501 hosts = SCNetworkReachabilityCopyResolvedAddress(target, &error_num);
502 if (hosts != NULL) {
503 if (CFArrayGetCount(hosts) > 0) {
504 CFIndex ptrLen;
505 CFMutableStringRef ptrName;
506 CFRange range;
507
508 name = CFArrayGetValueAtIndex(hosts, 0);
509 ptrName = CFStringCreateMutableCopy(NULL, 0, name);
510 ptrLen = CFStringGetLength(ptrName);
511 if (CFStringFindWithOptions(ptrName,
512 CFSTR("."),
513 CFRangeMake(0, ptrLen),
514 0,
515 &range)) {
516 CFStringDelete(ptrName,
517 CFRangeMake(range.location, ptrLen - range.location));
518 }
519 name = ptrName;
520 }
521 CFRelease(hosts);
522 }
523 }
524 if (name != NULL) {
525 if (_SC_CFStringIsValidNetBIOSName(name)) {
526 my_log(LOG_INFO, "NetBIOS name (reverse DNS query) = %@", name);
527 goto setName;
528 }
529 CFRelease(name);
530 }
531
532 // try local (multicast DNS) name, if available
533 name = SCDynamicStoreCopyLocalHostName(store);
534 if (name != NULL) {
535 if (_SC_CFStringIsValidNetBIOSName(name)) {
536 my_log(LOG_INFO, "NetBIOS name (multicast DNS) = %@", name);
537 goto setName;
538 }
539 CFRelease(name);
540 }
541
542 // use "default" name
543 name = copy_default_name();
544 if (name != NULL) {
545 my_log(LOG_INFO, "NetBIOS name (default) = %@", name);
546 goto setName;
547 }
548
549 goto setDict;
550
551 setName :
552
553 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
554 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
555 CFRelease(dict);
556 dict = newDict;
557 CFRelease(name);
558
559 setDict :
560
561 // update SMB configuration
562 smb_set_configuration(store, dict);
563 CFRelease(dict);
564
565 ptr_query_stop();
566
567 #ifdef MAIN
568 CFRunLoopStop(rl);
569 #endif // MAIN
570
571 return;
572 }
573
574
575 static Boolean
576 ptr_query_start(CFStringRef address)
577 {
578 union {
579 struct sockaddr sa;
580 struct sockaddr_in sin;
581 struct sockaddr_in6 sin6;
582 } addr;
583 char buf[64];
584 CFDataRef data;
585 CFMutableDictionaryRef options;
586
587 if (_SC_cfstring_to_cstring(address, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
588 my_log(LOG_ERR, "could not convert [primary] address string");
589 return FALSE;
590 }
591
592 if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
593 my_log(LOG_ERR, "could not convert [primary] address");
594 return FALSE;
595 }
596
597 options = CFDictionaryCreateMutable(NULL,
598 0,
599 &kCFTypeDictionaryKeyCallBacks,
600 &kCFTypeDictionaryValueCallBacks);
601 data = CFDataCreate(NULL, (const UInt8 *)&addr.sa, addr.sa.sa_len);
602 CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data);
603 CFRelease(data);
604 ptrTarget = SCNetworkReachabilityCreateWithOptions(NULL, options);
605 CFRelease(options);
606 if (ptrTarget == NULL) {
607 my_log(LOG_ERR, "could not resolve [primary] address");
608 return FALSE;
609 }
610
611 my_log(LOG_INFO, "NetBIOS name: ptr query start");
612
613 (void) SCNetworkReachabilitySetCallback(ptrTarget, ptr_query_callback, NULL);
614 (void) SCNetworkReachabilityScheduleWithRunLoop(ptrTarget, rl, kCFRunLoopDefaultMode);
615
616 return TRUE;
617 }
618
619
620 static void
621 smb_update_configuration(__unused CFRunLoopTimerRef _timer, void *info)
622 {
623 CFStringRef address = NULL;
624 CFDictionaryRef dict;
625 CFStringRef name;
626 CFStringRef serviceID = NULL;
627 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
628
629 // get network configuration
630 dict = smb_copy_global_configuration(store);
631
632 // use NetBIOS name from network configuration (if available)
633 name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
634 if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) {
635 my_log(LOG_INFO, "NetBIOS name (network configuration) = %@", name);
636 goto set;
637 }
638
639 // get primary service ID
640 serviceID = copy_primary_service(store);
641 if (serviceID == NULL) {
642 // if no primary service
643 goto mDNS;
644 }
645
646 // get DNS name associated with primary IP, if available
647 address = copy_primary_ip(store, serviceID);
648 if (address != NULL) {
649 Boolean ok;
650
651 // start reverse DNS query using primary IP address
652 ok = ptr_query_start(address);
653 if (ok) {
654 // if query started
655 goto done;
656 }
657 }
658
659 mDNS :
660
661 // get local (multicast DNS) name, if available
662
663 name = SCDynamicStoreCopyLocalHostName(store);
664 if (name != NULL) {
665 if (_SC_CFStringIsValidNetBIOSName(name)) {
666 CFMutableDictionaryRef newDict;
667
668 my_log(LOG_INFO, "NetBIOS name (multicast DNS) = %@", name);
669 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
670 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
671 CFRelease(dict);
672 dict = newDict;
673 CFRelease(name);
674 goto set;
675 }
676 CFRelease(name);
677 }
678
679 // get "default" name
680 name = copy_default_name();
681 if (name != NULL) {
682 CFMutableDictionaryRef newDict;
683
684 my_log(LOG_INFO, "NetBIOS name (default) = %@", name);
685 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
686 CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
687 CFRelease(dict);
688 dict = newDict;
689 CFRelease(name);
690 }
691
692 set :
693
694 // update SMB configuration
695 smb_set_configuration(store, dict);
696
697 done :
698
699 if (address != NULL) CFRelease(address);
700 if (dict != NULL) CFRelease(dict);
701 if (serviceID != NULL) CFRelease(serviceID);
702
703 if (timer != NULL) {
704 CFRunLoopTimerInvalidate(timer);
705 CFRelease(timer);
706 timer = NULL;
707 }
708
709 return;
710 }
711
712
713 static void
714 configuration_changed(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
715 {
716 CFRunLoopTimerContext context = { 0, (void *)store, CFRetain, CFRelease, NULL };
717 CFAbsoluteTime time_boot;
718 CFAbsoluteTime time_now ;
719
720 // if active, cancel any in-progress attempt to resolve the primary IP address
721
722 if (ptrTarget != NULL) {
723 ptr_query_stop();
724 }
725
726 // if active, cancel any queued configuration change
727 if (timer != NULL) {
728 CFRunLoopTimerInvalidate(timer);
729 CFRelease(timer);
730 timer = NULL;
731 }
732
733 // queue configuration change
734 time_boot = boottime() + SMB_STARTUP_DELAY;
735 time_now = CFAbsoluteTimeGetCurrent() + SMB_DEBOUNCE_DELAY;
736
737 timer = CFRunLoopTimerCreate(NULL,
738 time_now > time_boot ? time_now : time_boot,
739 0,
740 0,
741 0,
742 smb_update_configuration,
743 &context);
744 CFRunLoopAddTimer(rl, timer, kCFRunLoopDefaultMode);
745
746 return;
747 }
748
749
750 __private_extern__
751 void
752 load_smb_configuration(Boolean verbose)
753 {
754 CFStringRef key;
755 CFMutableArrayRef keys = NULL;
756 dispatch_block_t notify_block;
757 Boolean ok;
758 CFMutableArrayRef patterns = NULL;
759 uint32_t status;
760
761 /* initialize a few globals */
762
763 store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), configuration_changed, NULL);
764 if (store == NULL) {
765 my_log(LOG_ERR,
766 "SCDynamicStoreCreate() failed: %s",
767 SCErrorString(SCError()));
768 goto error;
769 }
770
771 /* establish notification keys and patterns */
772
773 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
774 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
775
776 /* ...watch for SMB configuration changes */
777 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
778 kSCDynamicStoreDomainState,
779 kSCEntNetSMB);
780 CFArrayAppendValue(keys, key);
781 CFRelease(key);
782
783 /* ...watch for ComputerName changes */
784 key = SCDynamicStoreKeyCreateComputerName(NULL);
785 CFArrayAppendValue(keys, key);
786 CFRelease(key);
787
788 /* ...watch for local (multicast DNS) hostname changes */
789 key = SCDynamicStoreKeyCreateHostNames(NULL);
790 CFArrayAppendValue(keys, key);
791 CFRelease(key);
792
793 /* register the keys/patterns */
794 ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns);
795 CFRelease(keys);
796 CFRelease(patterns);
797 if (!ok) {
798 my_log(LOG_ERR,
799 "SCDynamicStoreSetNotificationKeys() failed: %s",
800 SCErrorString(SCError()));
801 goto error;
802 }
803
804 rl = CFRunLoopGetCurrent();
805 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
806 if (rls == NULL) {
807 my_log(LOG_ERR,
808 "SCDynamicStoreCreateRunLoopSource() failed: %s",
809 SCErrorString(SCError()));
810 goto error;
811 }
812 CFRunLoopAddSource(rl, rls, kCFRunLoopDefaultMode);
813
814 /* ...watch for primary service/interface and DNS configuration changes */
815 notify_block = ^{
816 CFArrayRef changes;
817 CFStringRef key;
818
819 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
820 kSCDynamicStoreDomainState,
821 kSCEntNetDNS);
822 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
823 (*configuration_changed)(store, changes, NULL);
824 CFRelease(changes);
825 CFRelease(key);
826
827 return;
828 };
829 status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE,
830 &notify_token,
831 dispatch_get_main_queue(),
832 ^(int token){
833 CFRunLoopPerformBlock(rl,
834 kCFRunLoopDefaultMode,
835 notify_block);
836 CFRunLoopWakeUp(rl);
837 });
838 if (status != NOTIFY_STATUS_OK) {
839 my_log(LOG_ERR, "notify_register_dispatch() failed: %u", status);
840 goto error;
841 }
842
843 return;
844
845 error :
846
847 if (rls != NULL) {
848 CFRunLoopRemoveSource(rl, rls, kCFRunLoopDefaultMode);
849 CFRelease(rls);
850 rls = NULL;
851 }
852 if (store != NULL) {
853 CFRelease(store);
854 store = NULL;
855 }
856
857 return;
858 }
859
860
861 #ifdef MAIN
862 int
863 main(int argc, char **argv)
864 {
865
866 #ifdef DEBUG
867 CFStringRef address;
868 CFStringRef name;
869 CFStringRef serviceID;
870 SCDynamicStoreRef store;
871
872 _sc_log = FALSE;
873 if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) {
874 _sc_verbose = TRUE;
875 argv++;
876 argc--;
877 }
878
879 store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), NULL, NULL);
880 if (store == NULL) {
881 SCPrint(TRUE, stdout,
882 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
883 SCErrorString(SCError()));
884 exit(1);
885 }
886
887 // get "default" name
888 name = copy_default_name();
889 if (name != NULL) {
890 SCPrint(TRUE, stdout, CFSTR("default name = %@\n"), name);
891 CFRelease(name);
892 }
893
894 // get primary service
895 serviceID = copy_primary_service(store);
896 if (serviceID != NULL) {
897 SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID);
898 } else {
899 SCPrint(TRUE, stdout, CFSTR("No primary service\n"));
900 goto done;
901 }
902
903 if ((argc == (2+1)) && (argv[1][0] == 's')) {
904 if (serviceID != NULL) CFRelease(serviceID);
905 serviceID = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
906 SCPrint(TRUE, stdout, CFSTR("alternate service ID = %@\n"), serviceID);
907 }
908
909 // get primary IP address
910 address = copy_primary_ip(store, serviceID);
911 CFRelease(serviceID);
912 if (address != NULL) {
913 SCPrint(TRUE, stdout, CFSTR("primary address = %@\n"), address);
914
915 if ((argc == (2+1)) && (argv[1][0] == 'a')) {
916 if (address != NULL) CFRelease(address);
917 address = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
918 SCPrint(TRUE, stdout, CFSTR("alternate primary address = %@\n"), address);
919 }
920
921 // start reverse DNS query using primary IP address
922 (void) ptr_query_start(address);
923 CFRelease(address);
924 }
925
926 done :
927
928 smb_update_configuration(NULL, (void *)store);
929
930 CFRelease(store);
931
932 CFRunLoopRun();
933
934 #else /* DEBUG */
935
936 _sc_log = FALSE;
937 _sc_verbose = (argc > 1) ? TRUE : FALSE;
938
939 load_smb_configuration((argc > 1) ? TRUE : FALSE);
940 CFRunLoopRun();
941 /* not reached */
942
943 #endif /* DEBUG */
944
945 exit(0);
946 return 0;
947 }
948 #endif /* MAIN */