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