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