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
63static SCDynamicStoreRef	store		= NULL;
64static CFRunLoopSourceRef	rls		= NULL;
65
66static Boolean			dnsActive	= FALSE;
67static CFMachPortRef		dnsPort		= NULL;
68static struct timeval		dnsQueryStart;
69
70static CFRunLoopTimerRef	timer		= NULL;
71
72static Boolean			_verbose	= FALSE;
73
74
75static Boolean
76isMacOSXServer()
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
92static CFAbsoluteTime
93boottime(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
116static CFStringRef
117copy_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
218static CFDictionaryRef
219smb_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
248static void
249update_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
268static void
269smb_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
412static CFStringRef
413copy_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
441static CFStringRef
442copy_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
476static void
477reverseDNSComplete(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
597static CFStringRef
598replyMPCopyDescription(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
609static void
610getnameinfo_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
652static Boolean
653start_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
725static void
726smb_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
818static void
819configuration_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__
862void
863load_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
950int
951main(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 */
1037