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