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
67static SCDynamicStoreRef	store		= NULL;
68static CFRunLoopSourceRef	rls		= NULL;
69
70static struct timeval		ptrQueryStart;
71static SCNetworkReachabilityRef	ptrTarget	= NULL;
72
73static CFRunLoopTimerRef	timer		= NULL;
74
75static Boolean			_verbose	= FALSE;
76
77
78static CFAbsoluteTime
79boottime(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
102static CFStringRef
103copy_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
190static CFDictionaryRef
191smb_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
220static void
221update_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
240static void
241smb_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
382static CFStringRef
383copy_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
411static CFStringRef
412copy_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
446static void
447ptr_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
462static void
463ptr_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
574static Boolean
575ptr_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
617static void
618smb_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
710static void
711configuration_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__
748void
749load_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
836int
837main(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 */
924