1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include	<stdio.h>
19#include	<stdlib.h>
20#include	<crtdbg.h>
21#include	<stdarg.h>
22#include	<stddef.h>
23
24#include	"Poll.h"
25#include	"CommonServices.h"
26#include	"DebugServices.h"
27#include	"RegNames.h"
28
29#include	"uds_daemon.h"
30#include	"GenLinkedList.h"
31#include	"Service.h"
32#include	"EventLog.h"
33
34#include	"Resource.h"
35
36#include	"mDNSEmbeddedAPI.h"
37#include	"uDNS.h"
38#include	"mDNSWin32.h"
39#include	"mDNSDebug.h"
40
41#include	"Firewall.h"
42
43#if( !TARGET_OS_WINDOWS_CE )
44	#include	<mswsock.h>
45	#include	<process.h>
46	#include	<ipExport.h>
47	#include	<ws2def.h>
48	#include	<ws2ipdef.h>
49	#include	<iphlpapi.h>
50	#include	<netioapi.h>
51	#include	<iptypes.h>
52	#include	<powrprof.h>
53#endif
54
55#ifndef HeapEnableTerminationOnCorruption
56#	define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
57#endif
58
59#if 0
60#pragma mark == Constants ==
61#endif
62
63//===========================================================================================================================
64//	Constants
65//===========================================================================================================================
66
67#define	DEBUG_NAME							"[mDNSWin32] "
68#define kServiceFirewallName				L"Bonjour"
69#define	kServiceDependencies				TEXT("Tcpip\0\0")
70#define	kDNSServiceCacheEntryCountDefault	512
71#define kRetryFirewallPeriod				30 * 1000
72#define kDefValueSize						MAX_PATH + 1
73#define kZeroIndex							0
74#define kDefaultRouteMetric					399
75#define kSecondsTo100NSUnits				( 10 * 1000 * 1000 )
76#define kSPSMaintenanceWakePeriod			-30
77#define kWaitToRetry						(60 * 5)
78
79#define RR_CACHE_SIZE 500
80static CacheEntity gRRCache[RR_CACHE_SIZE];
81#if 0
82#pragma mark == Structures ==
83#endif
84
85#if 0
86#pragma mark == Prototypes ==
87#endif
88
89//===========================================================================================================================
90//	Prototypes
91//===========================================================================================================================
92static void				Usage( void );
93static BOOL WINAPI		ConsoleControlHandler( DWORD inControlEvent );
94static OSStatus			InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath );
95static OSStatus			RemoveService( LPCTSTR inName );
96static OSStatus			SetServiceParameters();
97static OSStatus			GetServiceParameters();
98static OSStatus			CheckFirewall();
99static OSStatus			SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription );
100static void				ReportStatus( int inType, const char *inFormat, ... );
101
102static void WINAPI		ServiceMain( DWORD argc, LPTSTR argv[] );
103static OSStatus			ServiceSetupEventLogging( void );
104static DWORD WINAPI		ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext );
105
106static OSStatus			ServiceRun( int argc, LPTSTR argv[] );
107static void				ServiceStop( void );
108
109static OSStatus			ServiceSpecificInitialize( int argc, LPTSTR  argv[] );
110static OSStatus			ServiceSpecificRun( int argc, LPTSTR argv[] );
111static OSStatus			ServiceSpecificStop( void );
112static void				ServiceSpecificFinalize( int argc, LPTSTR argv[] );
113static mStatus			SetupServiceEvents();
114static mStatus			TearDownServiceEvents();
115static mStatus			SetupNotifications();
116static mStatus			TearDownNotifications();
117static void CALLBACK	StopNotification( HANDLE event, void * context );
118static void CALLBACK	PowerSuspendNotification( HANDLE event, void * context );
119static void	CALLBACK	PowerResumeNotification( HANDLE event, void * context );
120static void CALLBACK	InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context );
121static void CALLBACK	ComputerDescriptionNotification( HANDLE event, void *context );
122static void CALLBACK	TCPChangedNotification( HANDLE event, void *context );
123static void CALLBACK	DDNSChangedNotification( HANDLE event, void *context );
124static void CALLBACK	FileSharingChangedNotification( HANDLE event, void *context );
125static void CALLBACK	FirewallChangedNotification( HANDLE event, void *context );
126static void CALLBACK	AdvertisedServicesChangedNotification( HANDLE event, void *context );
127static void CALLBACK	SPSWakeupNotification( HANDLE event, void *context );
128static void	CALLBACK	SPSSleepNotification( HANDLE event, void *context );
129static void CALLBACK	UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
130static void CALLBACK	UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
131static void				CoreCallback(mDNS * const inMDNS, mStatus result);
132static mDNSu8			SystemWakeForNetworkAccess( LARGE_INTEGER * timeout );
133static OSStatus			GetRouteDestination(DWORD * ifIndex, DWORD * address);
134static OSStatus			SetLLRoute( mDNS * const inMDNS );
135static bool				HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric );
136static bool				IsValidAddress( const char * addr );
137static bool				IsNortelVPN( IP_ADAPTER_INFO * pAdapter );
138static bool				IsJuniperVPN( IP_ADAPTER_INFO * pAdapter );
139static bool				IsCiscoVPN( IP_ADAPTER_INFO * pAdapter );
140static const char *		strnistr( const char * string, const char * subString, size_t max );
141
142#if defined(UNICODE)
143#	define StrLen(X)	wcslen(X)
144#	define StrCmp(X,Y)	wcscmp(X,Y)
145#else
146#	define StrLen(X)	strlen(X)
147#	define StrCmp(X,Y)	strcmp(X,Y)
148#endif
149
150
151#define kLLNetworkAddr      "169.254.0.0"
152#define kLLNetworkAddrMask  "255.255.0.0"
153
154
155#include	"mDNSEmbeddedAPI.h"
156
157#if 0
158#pragma mark == Globals ==
159#endif
160
161//===========================================================================================================================
162//	Globals
163//===========================================================================================================================
164#define gMDNSRecord mDNSStorage
165DEBUG_LOCAL	mDNS_PlatformSupport		gPlatformStorage;
166DEBUG_LOCAL BOOL						gServiceQuietMode		= FALSE;
167DEBUG_LOCAL SERVICE_TABLE_ENTRY			gServiceDispatchTable[] =
168{
169	{ kServiceName,	ServiceMain },
170	{ NULL, 		NULL }
171};
172DEBUG_LOCAL HANDLE						gStopEvent					= NULL;
173DEBUG_LOCAL HANDLE						gPowerSuspendEvent			= NULL;
174DEBUG_LOCAL HANDLE						gPowerSuspendAckEvent		= NULL;
175DEBUG_LOCAL HANDLE						gPowerResumeEvent			= NULL;
176DEBUG_LOCAL SOCKET						gInterfaceListChangedSocket	= INVALID_SOCKET;
177DEBUG_LOCAL HKEY						gDescKey					= NULL;
178DEBUG_LOCAL HANDLE						gDescChangedEvent			= NULL;	// Computer description changed event
179DEBUG_LOCAL HKEY						gTcpipKey					= NULL;
180DEBUG_LOCAL HANDLE						gTcpipChangedEvent			= NULL;	// TCP/IP config changed
181DEBUG_LOCAL HKEY						gDdnsKey					= NULL;
182DEBUG_LOCAL HANDLE						gDdnsChangedEvent			= NULL;	// DynDNS config changed
183DEBUG_LOCAL HKEY						gFileSharingKey				= NULL;
184DEBUG_LOCAL HANDLE						gFileSharingChangedEvent	= NULL;	// File Sharing changed
185DEBUG_LOCAL HKEY						gFirewallKey				= NULL;
186DEBUG_LOCAL HANDLE						gFirewallChangedEvent		= NULL;	// Firewall changed
187DEBUG_LOCAL HKEY						gAdvertisedServicesKey		= NULL;
188DEBUG_LOCAL HANDLE						gAdvertisedServicesChangedEvent	= NULL; // Advertised services changed
189DEBUG_LOCAL SERVICE_STATUS				gServiceStatus;
190DEBUG_LOCAL SERVICE_STATUS_HANDLE		gServiceStatusHandle 	= NULL;
191DEBUG_LOCAL HANDLE						gServiceEventSource		= NULL;
192DEBUG_LOCAL bool						gServiceAllowRemote		= false;
193DEBUG_LOCAL int							gServiceCacheEntryCount	= 0;	// 0 means to use the DNS-SD default.
194DEBUG_LOCAL bool						gServiceManageLLRouting = true;
195DEBUG_LOCAL HANDLE						gSPSWakeupEvent			= NULL;
196DEBUG_LOCAL HANDLE						gSPSSleepEvent			= NULL;
197DEBUG_LOCAL SocketRef					gUDSSocket				= 0;
198DEBUG_LOCAL udsEventCallback			gUDSCallback			= NULL;
199DEBUG_LOCAL BOOL						gRetryFirewall			= FALSE;
200
201typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW );
202mDNSlocal HMODULE								gIPHelperLibraryInstance		= NULL;
203mDNSlocal GetIpInterfaceEntryFunctionPtr		gGetIpInterfaceEntryFunctionPtr	= NULL;
204
205
206#if 0
207#pragma mark -
208#endif
209
210//===========================================================================================================================
211//	Main
212//===========================================================================================================================
213int	Main( int argc, LPTSTR argv[] )
214{
215	OSStatus		err;
216	BOOL			ok;
217	BOOL			start;
218	int				i;
219
220	HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
221
222	debug_initialize( kDebugOutputTypeMetaConsole );
223	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
224
225	// Default to automatically starting the service dispatcher if no extra arguments are specified.
226
227	start = ( argc <= 1 );
228
229	// Parse arguments.
230
231	for( i = 1; i < argc; ++i )
232	{
233		if( StrCmp( argv[ i ], TEXT("-install") ) == 0 )			// Install
234		{
235			TCHAR desc[ 256 ];
236
237			desc[ 0 ] = 0;
238			LoadString( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) );
239			err = InstallService( kServiceName, kServiceName, desc, argv[0] );
240			if( err )
241			{
242				ReportStatus( EVENTLOG_ERROR_TYPE, "install service failed (%d)\n", err );
243				goto exit;
244			}
245		}
246		else if( StrCmp( argv[ i ], TEXT("-remove") ) == 0 )		// Remove
247		{
248			err = RemoveService( kServiceName );
249			if( err )
250			{
251				ReportStatus( EVENTLOG_ERROR_TYPE, "remove service failed (%d)\n", err );
252				goto exit;
253			}
254		}
255		else if( StrCmp( argv[ i ], TEXT("-start") ) == 0 )		// Start
256		{
257			start = TRUE;
258		}
259		else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 )		// Server
260		{
261			err = RunDirect( argc, argv );
262			if( err )
263			{
264				ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err );
265			}
266			goto exit;
267		}
268		else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 )			// Quiet Mode (toggle)
269		{
270			gServiceQuietMode = !gServiceQuietMode;
271		}
272		else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || 	// Help
273				 ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) )
274		{
275			Usage();
276			err = 0;
277			break;
278		}
279		else
280		{
281			Usage();
282			err = kParamErr;
283			break;
284		}
285	}
286
287	// Start the service dispatcher if requested. This does not return until all services have terminated. If any
288	// global initialization is needed, it should be done before starting the service dispatcher, but only if it
289	// will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
290
291	if( start )
292	{
293		ok = StartServiceCtrlDispatcher( gServiceDispatchTable );
294		err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
295		if( err != kNoErr )
296		{
297			ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err );
298			goto exit;
299		}
300	}
301	err = 0;
302
303exit:
304	dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err );
305	_CrtDumpMemoryLeaks();
306	return( (int) err );
307}
308
309//===========================================================================================================================
310//	Usage
311//===========================================================================================================================
312
313static void	Usage( void )
314{
315	fprintf( stderr, "\n" );
316	fprintf( stderr, "mDNSResponder 1.0d1\n" );
317	fprintf( stderr, "\n" );
318	fprintf( stderr, "    <no args>    Runs the service normally\n" );
319	fprintf( stderr, "    -install     Creates the service and starts it\n" );
320	fprintf( stderr, "    -remove      Stops the service and deletes it\n" );
321	fprintf( stderr, "    -start       Starts the service dispatcher after processing all other arguments\n" );
322	fprintf( stderr, "    -server      Runs the service directly as a server (for debugging)\n" );
323	fprintf( stderr, "    -q           Toggles Quiet Mode (no events or output)\n" );
324	fprintf( stderr, "    -remote      Allow remote connections\n" );
325	fprintf( stderr, "    -cache n     Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault );
326	fprintf( stderr, "    -h[elp]      Display Help/Usage\n" );
327	fprintf( stderr, "\n" );
328}
329
330//===========================================================================================================================
331//	ConsoleControlHandler
332//===========================================================================================================================
333
334static BOOL WINAPI	ConsoleControlHandler( DWORD inControlEvent )
335{
336	BOOL			handled;
337	OSStatus		err;
338
339	handled = FALSE;
340	switch( inControlEvent )
341	{
342		case CTRL_C_EVENT:
343		case CTRL_BREAK_EVENT:
344		case CTRL_CLOSE_EVENT:
345		case CTRL_LOGOFF_EVENT:
346		case CTRL_SHUTDOWN_EVENT:
347			err = ServiceSpecificStop();
348			require_noerr( err, exit );
349
350			handled = TRUE;
351			break;
352
353		default:
354			break;
355	}
356
357exit:
358	return( handled );
359}
360
361//===========================================================================================================================
362//	InstallService
363//===========================================================================================================================
364
365static OSStatus	InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath )
366{
367	OSStatus		err;
368	SC_HANDLE		scm;
369	SC_HANDLE		service;
370	BOOL			ok;
371	TCHAR			fullPath[ MAX_PATH ];
372	TCHAR *			namePtr;
373	DWORD			size;
374
375	scm		= NULL;
376	service = NULL;
377
378	// Get a full path to the executable since a relative path may have been specified.
379
380	size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr );
381	err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
382	require_noerr( err, exit );
383
384	// Create the service and start it.
385
386	scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
387	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
388	require_noerr( err, exit );
389
390	service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS,
391							 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies,
392							 NULL, NULL );
393	err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr );
394	require_noerr( err, exit );
395
396	err = SetServiceParameters();
397	check_noerr( err );
398
399	if( inDescription )
400	{
401		err = SetServiceInfo( scm, inName, inDescription );
402		check_noerr( err );
403	}
404
405	ok = StartService( service, 0, NULL );
406	err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
407	require_noerr( err, exit );
408
409	ReportStatus( EVENTLOG_SUCCESS, "installed service\n" );
410	err = kNoErr;
411
412exit:
413	if( service )
414	{
415		CloseServiceHandle( service );
416	}
417	if( scm )
418	{
419		CloseServiceHandle( scm );
420	}
421	return( err );
422}
423
424//===========================================================================================================================
425//	RemoveService
426//===========================================================================================================================
427
428static OSStatus	RemoveService( LPCTSTR inName )
429{
430	OSStatus			err;
431	SC_HANDLE			scm;
432	SC_HANDLE			service;
433	BOOL				ok;
434	SERVICE_STATUS		status;
435
436	scm		= NULL;
437	service = NULL;
438
439	// Open a connection to the service.
440
441	scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS );
442	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
443	require_noerr( err, exit );
444
445	service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE );
446	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
447	require_noerr( err, exit );
448
449	// Stop the service, if it is not already stopped, then delete it.
450
451	ok = QueryServiceStatus( service, &status );
452	err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
453	require_noerr( err, exit );
454
455	if( status.dwCurrentState != SERVICE_STOPPED )
456	{
457		ok = ControlService( service, SERVICE_CONTROL_STOP, &status );
458		check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
459	}
460
461	ok = DeleteService( service );
462	err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr );
463	require_noerr( err, exit );
464
465	ReportStatus( EVENTLOG_SUCCESS, "Removed service\n" );
466	err = ERROR_SUCCESS;
467
468exit:
469	if( service )
470	{
471		CloseServiceHandle( service );
472	}
473	if( scm )
474	{
475		CloseServiceHandle( scm );
476	}
477	return( err );
478}
479
480
481
482//===========================================================================================================================
483//	SetServiceParameters
484//===========================================================================================================================
485
486static OSStatus SetServiceParameters()
487{
488	DWORD 			value;
489	DWORD			valueLen = sizeof(DWORD);
490	DWORD			type;
491	OSStatus		err;
492	HKEY			key;
493
494	key = NULL;
495
496	//
497	// Add/Open Parameters section under service entry in registry
498	//
499	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
500	require_noerr( err, exit );
501
502	//
503	// If the value isn't already there, then we create it
504	//
505	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
506
507	if (err != ERROR_SUCCESS)
508	{
509		value = 1;
510
511		err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) );
512		require_noerr( err, exit );
513	}
514
515exit:
516
517	if ( key )
518	{
519		RegCloseKey( key );
520	}
521
522	return( err );
523}
524
525
526
527//===========================================================================================================================
528//	GetServiceParameters
529//===========================================================================================================================
530
531static OSStatus GetServiceParameters()
532{
533	DWORD 			value;
534	DWORD			valueLen;
535	DWORD			type;
536	OSStatus		err;
537	HKEY			key;
538
539	key = NULL;
540
541	//
542	// Add/Open Parameters section under service entry in registry
543	//
544	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
545	require_noerr( err, exit );
546
547	valueLen = sizeof(DWORD);
548	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
549	if (err == ERROR_SUCCESS)
550	{
551		gServiceManageLLRouting = (value) ? true : false;
552	}
553
554	valueLen = sizeof(DWORD);
555	err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen);
556	if (err == ERROR_SUCCESS)
557	{
558		gServiceCacheEntryCount = value;
559	}
560
561exit:
562
563	if ( key )
564	{
565		RegCloseKey( key );
566	}
567
568	return( err );
569}
570
571
572//===========================================================================================================================
573//	CheckFirewall
574//===========================================================================================================================
575
576static OSStatus CheckFirewall()
577{
578	DWORD 					value;
579	DWORD					valueLen;
580	DWORD					type;
581	ENUM_SERVICE_STATUS	*	lpService = NULL;
582	SC_HANDLE				sc = NULL;
583	HKEY					key = NULL;
584	BOOL					ok;
585	DWORD					bytesNeeded = 0;
586	DWORD					srvCount;
587	DWORD					resumeHandle = 0;
588	DWORD					srvType;
589	DWORD					srvState;
590	DWORD					dwBytes = 0;
591	DWORD					i;
592	BOOL					isRunning = FALSE;
593	OSStatus				err = kUnknownErr;
594
595	// Check to see if the firewall service is running.  If it isn't, then
596	// we want to return immediately
597
598	sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
599	err = translate_errno( sc, GetLastError(), kUnknownErr );
600	require_noerr( err, exit );
601
602	srvType		=	SERVICE_WIN32;
603	srvState	=	SERVICE_STATE_ALL;
604
605	for ( ;; )
606	{
607		// Call EnumServicesStatus using the handle returned by OpenSCManager
608
609		ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
610
611		if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
612		{
613			break;
614		}
615
616		if ( lpService )
617		{
618			free( lpService );
619		}
620
621		dwBytes = bytesNeeded;
622
623		lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
624		require_action( lpService, exit, err = mStatus_NoMemoryErr );
625	}
626
627	err = translate_errno( ok, GetLastError(), kUnknownErr );
628	require_noerr( err, exit );
629
630	for ( i = 0; i < srvCount; i++ )
631	{
632		if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 )
633		{
634			if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING )
635			{
636				isRunning = TRUE;
637			}
638
639			break;
640		}
641	}
642
643	require_action( isRunning, exit, err = kUnknownErr );
644
645	// Check to see if we've managed the firewall.
646	// This package might have been installed, then
647	// the OS was upgraded to SP2 or above.  If that's
648	// the case, then we need to manipulate the firewall
649	// so networking works correctly.
650
651	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
652	require_noerr( err, exit );
653
654	valueLen = sizeof(DWORD);
655	err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen);
656
657	if ((err != ERROR_SUCCESS) || (value == 0))
658	{
659		wchar_t	fullPath[ MAX_PATH ];
660		DWORD	size;
661
662		// Get a full path to the executable
663
664		size = GetModuleFileNameW( NULL, fullPath, MAX_PATH );
665		err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
666		require_noerr( err, exit );
667
668		err = mDNSAddToFirewall(fullPath, kServiceFirewallName);
669		require_noerr( err, exit );
670
671		value = 1;
672		err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) );
673		require_noerr( err, exit );
674	}
675
676exit:
677
678	if ( key )
679	{
680		RegCloseKey( key );
681	}
682
683	if ( lpService )
684	{
685		free( lpService );
686	}
687
688	if ( sc )
689	{
690		CloseServiceHandle ( sc );
691	}
692
693	return( err );
694}
695
696
697
698//===========================================================================================================================
699//	SetServiceInfo
700//===========================================================================================================================
701
702static OSStatus	SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription )
703{
704	OSStatus				err;
705	SC_LOCK					lock;
706	SC_HANDLE				service;
707	SERVICE_DESCRIPTION		description;
708	SERVICE_FAILURE_ACTIONS	actions;
709	SC_ACTION				action;
710	BOOL					ok;
711
712	check( inServiceName );
713	check( inDescription );
714
715	lock 	= NULL;
716	service	= NULL;
717
718	// Open the database (if not provided) and lock it to prevent other access while re-configuring.
719
720	if( !inSCM )
721	{
722		inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
723		err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr );
724		require_noerr( err, exit );
725	}
726
727	lock = LockServiceDatabase( inSCM );
728	err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr );
729	require_noerr( err, exit );
730
731	// Open a handle to the service.
732
733	service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START );
734	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
735	require_noerr( err, exit );
736
737	// Change the description.
738
739	description.lpDescription = (LPTSTR) inDescription;
740	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description );
741	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
742	require_noerr( err, exit );
743
744	actions.dwResetPeriod	=	INFINITE;
745	actions.lpRebootMsg		=	NULL;
746	actions.lpCommand		=	NULL;
747	actions.cActions		=	1;
748	actions.lpsaActions		=	&action;
749	action.Delay			=	500;
750	action.Type				=	SC_ACTION_RESTART;
751
752	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions );
753	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
754	require_noerr( err, exit );
755
756	err = ERROR_SUCCESS;
757
758exit:
759	// Close the service and release the lock.
760
761	if( service )
762	{
763		CloseServiceHandle( service );
764	}
765	if( lock )
766	{
767		UnlockServiceDatabase( lock );
768	}
769	return( err );
770}
771
772//===========================================================================================================================
773//	ReportStatus
774//===========================================================================================================================
775
776static void	ReportStatus( int inType, const char *inFormat, ... )
777{
778	if( !gServiceQuietMode )
779	{
780		va_list		args;
781
782		va_start( args, inFormat );
783		if( gServiceEventSource )
784		{
785			char				s[ 1024 ];
786			BOOL				ok;
787			const char *		array[ 1 ];
788
789			vsprintf( s, inFormat, args );
790			array[ 0 ] = s;
791			ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL );
792			check_translated_errno( ok, GetLastError(), kUnknownErr );
793		}
794		else
795		{
796			int		n;
797
798			n = vfprintf( stderr, inFormat, args );
799			check( n >= 0 );
800		}
801		va_end( args );
802	}
803}
804
805//===========================================================================================================================
806//	RunDirect
807//===========================================================================================================================
808
809int	RunDirect( int argc, LPTSTR argv[] )
810{
811	OSStatus		err;
812	BOOL			initialized;
813   BOOL        ok;
814
815	initialized = FALSE;
816
817	err = SetupServiceEvents();
818	require_noerr( err, exit );
819
820	// Install a Console Control Handler to handle things like control-c signals.
821
822	ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
823	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
824	require_noerr( err, exit );
825
826	err = ServiceSpecificInitialize( argc, argv );
827	require_noerr( err, exit );
828	initialized = TRUE;
829
830	// Run the service. This does not return until the service quits or is stopped.
831
832	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Running service directly\n" );
833
834	err = ServiceSpecificRun( argc, argv );
835	require_noerr( err, exit );
836
837	// Clean up.
838
839exit:
840	if( initialized )
841	{
842		ServiceSpecificFinalize( argc, argv );
843	}
844
845	TearDownServiceEvents();
846
847	return( err );
848}
849
850#if 0
851#pragma mark -
852#endif
853
854//===========================================================================================================================
855//	ServiceMain
856//===========================================================================================================================
857
858static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] )
859{
860	OSStatus		err;
861	BOOL			ok;
862
863	err = SetupServiceEvents();
864	require_noerr( err, exit );
865
866	err = ServiceSetupEventLogging();
867	check_noerr( err );
868
869	err = GetServiceParameters();
870	check_noerr( err );
871
872	// Initialize the service status and register the service control handler with the name of the service.
873
874	gServiceStatus.dwServiceType 				= SERVICE_WIN32_SHARE_PROCESS;
875	gServiceStatus.dwCurrentState 				= 0;
876	gServiceStatus.dwControlsAccepted 			= SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_POWEREVENT;
877	gServiceStatus.dwWin32ExitCode 				= NO_ERROR;
878	gServiceStatus.dwServiceSpecificExitCode 	= NO_ERROR;
879	gServiceStatus.dwCheckPoint 				= 0;
880	gServiceStatus.dwWaitHint 					= 0;
881
882	gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL );
883	err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr );
884	require_noerr( err, exit );
885
886	// Mark the service as starting.
887
888	gServiceStatus.dwCurrentState 	= SERVICE_START_PENDING;
889	gServiceStatus.dwCheckPoint	 	= 0;
890	gServiceStatus.dwWaitHint 		= 5000;	// 5 seconds
891	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
892	check_translated_errno( ok, GetLastError(), kParamErr );
893
894	// Run the service. This does not return until the service quits or is stopped.
895
896	err = ServiceRun( (int) argc, argv );
897	if( err != kNoErr )
898	{
899		gServiceStatus.dwWin32ExitCode				= ERROR_SERVICE_SPECIFIC_ERROR;
900		gServiceStatus.dwServiceSpecificExitCode 	= (DWORD) err;
901	}
902
903	// Service-specific work is done so mark the service as stopped.
904
905	gServiceStatus.dwCurrentState = SERVICE_STOPPED;
906	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
907	check_translated_errno( ok, GetLastError(), kParamErr );
908
909	// Note: The service status handle should not be closed according to Microsoft documentation.
910
911exit:
912
913	if( gServiceEventSource )
914	{
915		ok = DeregisterEventSource( gServiceEventSource );
916		check_translated_errno( ok, GetLastError(), kUnknownErr );
917		gServiceEventSource = NULL;
918	}
919
920	TearDownServiceEvents();
921}
922
923//===========================================================================================================================
924//	ServiceSetupEventLogging
925//===========================================================================================================================
926
927static OSStatus	ServiceSetupEventLogging( void )
928{
929	OSStatus			err;
930	HKEY				key;
931	LPCTSTR				s;
932	DWORD				typesSupported;
933	TCHAR				path[ MAX_PATH ];
934	DWORD 				n;
935
936	key = NULL;
937
938	// Add/Open source name as a sub-key under the Application key in the EventLog registry key.
939
940	s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName;
941	err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key );
942	require_noerr( err, exit );
943
944	// Add the name to the EventMessageFile subkey.
945
946	path[ 0 ] = '\0';
947	GetModuleFileName( NULL, path, MAX_PATH );
948	n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) );
949	err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
950	require_noerr( err, exit );
951
952	// Set the supported event types in the TypesSupported subkey.
953
954	typesSupported = 0
955					 | EVENTLOG_SUCCESS
956					 | EVENTLOG_ERROR_TYPE
957					 | EVENTLOG_WARNING_TYPE
958					 | EVENTLOG_INFORMATION_TYPE
959					 | EVENTLOG_AUDIT_SUCCESS
960					 | EVENTLOG_AUDIT_FAILURE;
961	err = RegSetValueEx( key, TEXT("TypesSupported"), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
962	require_noerr( err, exit );
963
964	// Set up the event source.
965
966	gServiceEventSource = RegisterEventSource( NULL, kServiceName );
967	err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr );
968	require_noerr( err, exit );
969
970exit:
971	if( key )
972	{
973		RegCloseKey( key );
974	}
975	return( err );
976}
977
978
979//===========================================================================================================================
980//	ServiceControlHandler
981//===========================================================================================================================
982
983static DWORD WINAPI	ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext )
984{
985	BOOL		setStatus;
986	OSStatus	err;
987	BOOL		ok;
988
989	DEBUG_UNUSED( inEventData );
990	DEBUG_UNUSED( inContext );
991
992	setStatus = TRUE;
993	switch( inControl )
994	{
995		case SERVICE_CONTROL_STOP:
996		case SERVICE_CONTROL_SHUTDOWN:
997
998			dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP|SERVICE_CONTROL_SHUTDOWN\n" );
999
1000			ServiceStop();
1001			setStatus = FALSE;
1002			break;
1003
1004		case SERVICE_CONTROL_POWEREVENT:
1005
1006			if (inEventType == PBT_APMSUSPEND)
1007			{
1008				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMSUSPEND\n" );
1009
1010				if ( gPowerSuspendEvent )
1011				{
1012					ok = SetEvent( gPowerSuspendEvent );
1013					err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1014					check_noerr( err );
1015
1016					switch ( WaitForSingleObject( gPowerSuspendAckEvent, 5 * 1000 ) )
1017					{
1018						case WAIT_OBJECT_0:
1019						{
1020							// No error
1021						}
1022						break;
1023
1024						case WAIT_TIMEOUT:
1025						{
1026							dlog( kDebugLevelError, DEBUG_NAME "Timed out waiting for acknowledgement of machine sleep\n" );
1027							ReportStatus( EVENTLOG_ERROR_TYPE, "Timed out waiting for acknowledgement of machine sleep" );
1028						}
1029						break;
1030
1031						default:
1032						{
1033							dlog( kDebugLevelError, DEBUG_NAME "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
1034							ReportStatus( EVENTLOG_ERROR_TYPE, "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
1035						}
1036						break;
1037					}
1038				}
1039			}
1040			else if (inEventType == PBT_APMRESUMESUSPEND)
1041			{
1042				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" );
1043
1044				if ( gPowerResumeEvent )
1045				{
1046					ok = SetEvent( gPowerResumeEvent );
1047					err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1048					check_noerr( err );
1049				}
1050			}
1051
1052			break;
1053
1054		default:
1055			dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl );
1056			break;
1057	}
1058
1059	if( setStatus && gServiceStatusHandle )
1060	{
1061		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1062		check_translated_errno( ok, GetLastError(), kUnknownErr );
1063	}
1064
1065	return NO_ERROR;
1066}
1067
1068//===========================================================================================================================
1069//	ServiceRun
1070//===========================================================================================================================
1071
1072static OSStatus	ServiceRun( int argc, LPTSTR argv[] )
1073{
1074	OSStatus		err;
1075	BOOL			initialized;
1076	BOOL			ok;
1077
1078	DEBUG_UNUSED( argc );
1079	DEBUG_UNUSED( argv );
1080
1081	initialized = FALSE;
1082
1083	// <rdar://problem/5727548> Make the service as running before we call ServiceSpecificInitialize. We've
1084	// had reports that some machines with McAfee firewall installed cause a problem with iTunes installation.
1085	// We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a
1086	// simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock
1087	// any installers that are waiting for our state to change.
1088
1089	gServiceStatus.dwCurrentState = SERVICE_RUNNING;
1090	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1091	check_translated_errno( ok, GetLastError(), kParamErr );
1092
1093	// Initialize the service-specific stuff
1094
1095	while ( 1 )
1096	{
1097		DWORD ret;
1098
1099		ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initializing" );
1100
1101		err = ServiceSpecificInitialize( argc, argv );
1102
1103		if ( !err )
1104		{
1105			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialized" );
1106			break;
1107		}
1108
1109		ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialization failed with err %d. Waiting %d seconds to retry...", err, kWaitToRetry );
1110
1111		ret = WaitForSingleObject( gStopEvent, 1000 * kWaitToRetry );
1112
1113		if ( ret == WAIT_OBJECT_0 )
1114		{
1115			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a stop event" );
1116			goto exit;
1117		}
1118		else if ( ret == WAIT_OBJECT_0 + 1 )
1119		{
1120			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power suspend event" );
1121		}
1122		else if ( ret == WAIT_OBJECT_0 + 2 )
1123		{
1124			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power resume event" );
1125		}
1126		else if ( ret != WAIT_TIMEOUT )
1127		{
1128			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received an error in WaitForSingleObject() : %d, %d", ret, GetLastError() );
1129			goto exit;
1130		}
1131	}
1132
1133	initialized = TRUE;
1134
1135	err = CheckFirewall();
1136	check_noerr( err );
1137
1138	if ( err )
1139	{
1140		gRetryFirewall = TRUE;
1141	}
1142
1143	// Run the service-specific stuff. This does not return until the service quits or is stopped.
1144
1145	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service started\n" );
1146
1147	err = ServiceSpecificRun( argc, argv );
1148	require_noerr( err, exit );
1149
1150exit:
1151
1152	// Service stopped. Clean up and we're done.
1153
1154	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err );
1155
1156	if( initialized )
1157	{
1158		ServiceSpecificFinalize( argc, argv );
1159	}
1160
1161	return( err );
1162}
1163
1164//===========================================================================================================================
1165//	ServiceStop
1166//===========================================================================================================================
1167
1168static void	ServiceStop( void )
1169{
1170	BOOL			ok;
1171	OSStatus		err;
1172
1173	// Signal the event to cause the service to exit.
1174
1175	if( gServiceStatusHandle )
1176	{
1177		gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
1178		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1179		check_translated_errno( ok, GetLastError(), kParamErr );
1180	}
1181
1182	err = ServiceSpecificStop();
1183	check_noerr( err );
1184}
1185
1186
1187#if 0
1188#pragma mark -
1189#pragma mark == Service Specific ==
1190#endif
1191
1192//===========================================================================================================================
1193//	ServiceSpecificInitialize
1194//===========================================================================================================================
1195
1196static OSStatus	ServiceSpecificInitialize( int argc, LPTSTR argv[] )
1197{
1198	OSStatus err;
1199
1200	DEBUG_UNUSED( argc );
1201	DEBUG_UNUSED( argv );
1202
1203	mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord);
1204	mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage);
1205
1206	gPlatformStorage.reportStatusFunc = ReportStatus;
1207
1208	err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext);
1209	require_noerr( err, exit);
1210
1211	err = SetupNotifications();
1212	check_noerr( err );
1213
1214	err = udsserver_init(mDNSNULL, 0);
1215	require_noerr( err, exit);
1216
1217	SetLLRoute( &gMDNSRecord );
1218
1219exit:
1220	if( err != kNoErr )
1221	{
1222		ServiceSpecificFinalize( argc, argv );
1223	}
1224	return( err );
1225}
1226
1227//===========================================================================================================================
1228//	ServiceSpecificRun
1229//===========================================================================================================================
1230
1231static OSStatus	ServiceSpecificRun( int argc, LPTSTR argv[] )
1232{
1233	mDNSBool done = mDNSfalse;
1234	mStatus err = mStatus_NoError;
1235
1236	DEBUG_UNUSED( argc );
1237	DEBUG_UNUSED( argv );
1238
1239	err = SetupInterfaceList( &gMDNSRecord );
1240	check( !err );
1241
1242	err = uDNS_SetupDNSConfig( &gMDNSRecord );
1243	check( !err );
1244
1245	while( !done )
1246	{
1247		static mDNSs32 RepeatedBusy = 0;
1248		mDNSs32 nextTimerEvent;
1249		mStatus err;
1250
1251		// Give the mDNS core a chance to do its work and determine next event time.
1252
1253		nextTimerEvent = udsserver_idle( mDNS_Execute( &gMDNSRecord ) - mDNS_TimeNow( &gMDNSRecord ) );
1254
1255		if      ( nextTimerEvent < 0)					nextTimerEvent = 0;
1256		else if ( nextTimerEvent > (0x7FFFFFFF / 1000))	nextTimerEvent = 0x7FFFFFFF / mDNSPlatformOneSecond;
1257		else											nextTimerEvent = ( nextTimerEvent * 1000) / mDNSPlatformOneSecond;
1258
1259		// Debugging sanity check, to guard against CPU spins
1260
1261		if ( nextTimerEvent > 0 )
1262		{
1263			RepeatedBusy = 0;
1264		}
1265		else
1266		{
1267			nextTimerEvent = 1;
1268
1269			if ( ++RepeatedBusy >= mDNSPlatformOneSecond )
1270			{
1271				ShowTaskSchedulingError( &gMDNSRecord );
1272				RepeatedBusy = 0;
1273			}
1274		}
1275
1276		if ( gMDNSRecord.ShutdownTime )
1277		{
1278			mDNSs32 now = mDNS_TimeNow( &gMDNSRecord );
1279
1280			if ( mDNS_ExitNow( &gMDNSRecord, now ) )
1281			{
1282				mDNS_FinalExit( &gMDNSRecord );
1283				done = TRUE;
1284				break;
1285			}
1286
1287			if ( nextTimerEvent - gMDNSRecord.ShutdownTime >= 0 )
1288			{
1289				nextTimerEvent = gMDNSRecord.ShutdownTime;
1290			}
1291		}
1292
1293		err = mDNSPoll( nextTimerEvent );
1294
1295		if ( err )
1296		{
1297			Sleep( 3 * 1000 );
1298
1299			err = SetupInterfaceList( &gMDNSRecord );
1300			check( !err );
1301
1302			err = uDNS_SetupDNSConfig( &gMDNSRecord );
1303			check( !err );
1304
1305			break;
1306		}
1307	}
1308
1309	return ( err );
1310}
1311
1312
1313//===========================================================================================================================
1314//	ServiceSpecificStop
1315//===========================================================================================================================
1316
1317static OSStatus	ServiceSpecificStop( void )
1318{
1319	OSStatus    err;
1320	BOOL        ok;
1321
1322	ok = SetEvent(gStopEvent);
1323	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1324	require_noerr( err, exit );
1325
1326exit:
1327
1328	return( err );
1329}
1330
1331//===========================================================================================================================
1332//	ServiceSpecificFinalize
1333//===========================================================================================================================
1334
1335static void	ServiceSpecificFinalize( int argc, LPTSTR argv[] )
1336{
1337	DEBUG_UNUSED( argc );
1338	DEBUG_UNUSED( argv );
1339
1340	//
1341	// clean up the notifications
1342	//
1343	TearDownNotifications();
1344
1345	//
1346	// clean up loaded library
1347	//
1348
1349	if( gIPHelperLibraryInstance )
1350	{
1351		gGetIpInterfaceEntryFunctionPtr = NULL;
1352
1353		FreeLibrary( gIPHelperLibraryInstance );
1354		gIPHelperLibraryInstance = NULL;
1355	}
1356}
1357
1358
1359//===========================================================================================================================
1360//	SetupServiceEvents
1361//===========================================================================================================================
1362
1363mDNSlocal mStatus	SetupServiceEvents()
1364{
1365	mStatus err;
1366
1367	// Stop Event
1368
1369	gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1370	err = translate_errno( gStopEvent, (mStatus) GetLastError(), kUnknownErr );
1371	require_noerr( err, exit );
1372
1373exit:
1374
1375	if ( err )
1376	{
1377		TearDownServiceEvents();
1378	}
1379
1380	return err;
1381}
1382
1383
1384//===========================================================================================================================
1385//	TearDownServiceNotifications
1386//===========================================================================================================================
1387
1388mDNSlocal mStatus	TearDownServiceEvents()
1389{
1390	if ( gStopEvent )
1391	{
1392		CloseHandle( gStopEvent );
1393		gStopEvent = NULL;
1394	}
1395
1396	return mStatus_NoError;
1397}
1398
1399
1400//===========================================================================================================================
1401//	SetupNotifications
1402//===========================================================================================================================
1403
1404mDNSlocal mStatus	SetupNotifications()
1405{
1406	mStatus				err;
1407	SocketRef			sock;
1408	unsigned long		param;
1409	int					inBuffer;
1410	int					outBuffer;
1411	DWORD				outSize;
1412
1413	require_action( gStopEvent, exit, err = kUnknownErr );
1414	err = mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL );
1415	require_noerr( err, exit );
1416
1417	// Power Suspend
1418
1419	gPowerSuspendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1420	err = translate_errno( gPowerSuspendEvent, (mStatus) GetLastError(), kUnknownErr );
1421	require_noerr( err, exit );
1422	err = mDNSPollRegisterEvent( gPowerSuspendEvent, PowerSuspendNotification, NULL );
1423	require_noerr( err, exit );
1424
1425	// Power Suspend Ack
1426
1427	gPowerSuspendAckEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1428	err = translate_errno( gPowerSuspendAckEvent, ( mStatus ) GetLastError(), kUnknownErr );
1429	require_noerr( err, exit );
1430
1431	// Power Resume
1432
1433	gPowerResumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1434	err = translate_errno( gPowerResumeEvent, (mStatus) GetLastError(), kUnknownErr );
1435	require_noerr( err, exit );
1436	err = mDNSPollRegisterEvent( gPowerResumeEvent, PowerResumeNotification, NULL );
1437	require_noerr( err, exit );
1438
1439	// Register to listen for address list changes.
1440
1441	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
1442	err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
1443	require_noerr( err, exit );
1444	gInterfaceListChangedSocket = sock;
1445
1446	// Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event
1447	// when a change to the interface list is detected.
1448
1449	param = 1;
1450	err = ioctlsocket( sock, FIONBIO, &param );
1451	err = translate_errno( err == 0, errno_compat(), kUnknownErr );
1452	require_noerr( err, exit );
1453
1454	inBuffer	= 0;
1455	outBuffer	= 0;
1456	err = WSAIoctl( sock, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
1457	if( err < 0 )
1458	{
1459		check( errno_compat() == WSAEWOULDBLOCK );
1460	}
1461
1462	err = mDNSPollRegisterSocket( sock, FD_ADDRESS_LIST_CHANGE, InterfaceListNotification, NULL );
1463	require_noerr( err, exit );
1464
1465	gDescChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1466	err = translate_errno( gDescChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1467	require_noerr( err, exit );
1468
1469	err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"), 0, KEY_READ, &gDescKey);
1470	check_translated_errno( err == 0, errno_compat(), kNameErr );
1471
1472	if ( gDescKey != NULL )
1473	{
1474		err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
1475		require_noerr( err, exit );
1476	}
1477
1478	err = mDNSPollRegisterEvent( gDescChangedEvent, ComputerDescriptionNotification, NULL );
1479	require_noerr( err, exit );
1480
1481	// This will catch all changes to tcp/ip networking, including changes to the domain search list
1482
1483	gTcpipChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1484	err = translate_errno( gTcpipChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1485	require_noerr( err, exit );
1486	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &gTcpipKey );
1487	require_noerr( err, exit );
1488	err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE);
1489	require_noerr( err, exit );
1490	err = mDNSPollRegisterEvent( gTcpipChangedEvent, TCPChangedNotification, NULL );
1491	require_noerr( err, exit );
1492
1493	// This will catch all changes to ddns configuration
1494
1495	gDdnsChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1496	err = translate_errno( gDdnsChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1497	require_noerr( err, exit );
1498	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &gDdnsKey );
1499	require_noerr( err, exit );
1500	err = RegNotifyChangeKeyValue( gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
1501	require_noerr( err, exit );
1502	err = mDNSPollRegisterEvent( gDdnsChangedEvent, DDNSChangedNotification, NULL );
1503	require_noerr( err, exit );
1504
1505	// This will catch all changes to file sharing
1506
1507	gFileSharingChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1508	err = translate_errno( gFileSharingChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1509	require_noerr( err, exit );
1510
1511	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\Shares"), &gFileSharingKey );
1512
1513	// Just to make sure that initialization doesn't fail on some old OS
1514	// that doesn't have this key, we'll only add the notification if
1515	// the key exists.
1516
1517	if ( !err )
1518	{
1519		err = RegNotifyChangeKeyValue( gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
1520		require_noerr( err, exit );
1521		err = mDNSPollRegisterEvent( gFileSharingChangedEvent, FileSharingChangedNotification, NULL );
1522		require_noerr( err, exit );
1523	}
1524	else
1525	{
1526		err = mStatus_NoError;
1527	}
1528
1529	// This will catch changes to the Windows firewall
1530
1531	gFirewallChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1532	err = translate_errno( gFirewallChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1533	require_noerr( err, exit );
1534
1535	// Just to make sure that initialization doesn't fail on some old OS
1536	// that doesn't have this key, we'll only add the notification if
1537	// the key exists.
1538
1539	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"), &gFirewallKey );
1540
1541	if ( !err )
1542	{
1543		err = RegNotifyChangeKeyValue( gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
1544		require_noerr( err, exit );
1545		err = mDNSPollRegisterEvent( gFirewallChangedEvent, FirewallChangedNotification, NULL );
1546		require_noerr( err, exit );
1547	}
1548	else
1549	{
1550		err = mStatus_NoError;
1551	}
1552
1553	// This will catch all changes to advertised services configuration
1554
1555	gAdvertisedServicesChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1556	err = translate_errno( gAdvertisedServicesChangedEvent, (mStatus) GetLastError(), kUnknownErr );
1557	require_noerr( err, exit );
1558	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\Services"), &gAdvertisedServicesKey );
1559	require_noerr( err, exit );
1560	err = RegNotifyChangeKeyValue( gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
1561	require_noerr( err, exit );
1562	err = mDNSPollRegisterEvent( gAdvertisedServicesChangedEvent, AdvertisedServicesChangedNotification, NULL );
1563	require_noerr( err, exit );
1564
1565	// SPSWakeup timer
1566
1567	gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL );
1568	err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr );
1569	require_noerr( err, exit );
1570	err = mDNSPollRegisterEvent( gSPSWakeupEvent, SPSWakeupNotification, NULL );
1571	require_noerr( err, exit );
1572
1573	// SPSSleep timer
1574
1575	gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL );
1576	err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr );
1577	require_noerr( err, exit );
1578	err = mDNSPollRegisterEvent( gSPSSleepEvent, SPSSleepNotification, NULL );
1579	require_noerr( err, exit );
1580
1581exit:
1582	if( err )
1583	{
1584		TearDownNotifications();
1585	}
1586	return( err );
1587}
1588
1589//===========================================================================================================================
1590//	TearDownNotifications
1591//===========================================================================================================================
1592
1593mDNSlocal mStatus	TearDownNotifications()
1594{
1595	if( IsValidSocket( gInterfaceListChangedSocket ) )
1596	{
1597		mDNSPollUnregisterSocket( gInterfaceListChangedSocket );
1598
1599		close_compat( gInterfaceListChangedSocket );
1600		gInterfaceListChangedSocket = kInvalidSocketRef;
1601	}
1602
1603	if ( gDescChangedEvent != NULL )
1604	{
1605		mDNSPollUnregisterEvent( gDescChangedEvent );
1606		CloseHandle( gDescChangedEvent );
1607		gDescChangedEvent = NULL;
1608	}
1609
1610	if ( gDescKey != NULL )
1611	{
1612		RegCloseKey( gDescKey );
1613		gDescKey = NULL;
1614	}
1615
1616	if ( gTcpipChangedEvent != NULL )
1617	{
1618		mDNSPollUnregisterEvent( gTcpipChangedEvent );
1619		CloseHandle( gTcpipChangedEvent );
1620		gTcpipChangedEvent = NULL;
1621	}
1622
1623	if ( gDdnsChangedEvent != NULL )
1624	{
1625		mDNSPollUnregisterEvent( gDdnsChangedEvent );
1626		CloseHandle( gDdnsChangedEvent );
1627		gDdnsChangedEvent = NULL;
1628	}
1629
1630	if ( gDdnsKey != NULL )
1631	{
1632		RegCloseKey( gDdnsKey );
1633		gDdnsKey = NULL;
1634	}
1635
1636	if ( gFileSharingChangedEvent != NULL )
1637	{
1638		mDNSPollUnregisterEvent( gFileSharingChangedEvent );
1639		CloseHandle( gFileSharingChangedEvent );
1640		gFileSharingChangedEvent = NULL;
1641	}
1642
1643	if ( gFileSharingKey != NULL )
1644	{
1645		RegCloseKey( gFileSharingKey );
1646		gFileSharingKey = NULL;
1647	}
1648
1649	if ( gFirewallChangedEvent != NULL )
1650	{
1651		mDNSPollUnregisterEvent( gFirewallChangedEvent );
1652		CloseHandle( gFirewallChangedEvent );
1653		gFirewallChangedEvent = NULL;
1654	}
1655
1656	if ( gFirewallKey != NULL )
1657	{
1658		RegCloseKey( gFirewallKey );
1659		gFirewallKey = NULL;
1660	}
1661
1662	if ( gAdvertisedServicesChangedEvent != NULL )
1663	{
1664		mDNSPollUnregisterEvent( gAdvertisedServicesChangedEvent );
1665		CloseHandle( gAdvertisedServicesChangedEvent );
1666		gAdvertisedServicesChangedEvent = NULL;
1667	}
1668
1669	if ( gAdvertisedServicesKey != NULL )
1670	{
1671		RegCloseKey( gAdvertisedServicesKey );
1672		gAdvertisedServicesKey = NULL;
1673	}
1674
1675	if ( gSPSWakeupEvent )
1676	{
1677		mDNSPollUnregisterEvent( gSPSWakeupEvent );
1678		CloseHandle( gSPSWakeupEvent );
1679		gSPSWakeupEvent = NULL;
1680	}
1681
1682	if ( gSPSSleepEvent )
1683	{
1684		mDNSPollUnregisterEvent( gSPSSleepEvent );
1685		CloseHandle( gSPSSleepEvent );
1686		gSPSSleepEvent = NULL;
1687	}
1688
1689	if ( gPowerResumeEvent )
1690	{
1691		mDNSPollUnregisterEvent( gPowerResumeEvent );
1692		CloseHandle( gPowerResumeEvent );
1693		gPowerResumeEvent = NULL;
1694	}
1695
1696	if ( gPowerSuspendAckEvent )
1697	{
1698		CloseHandle( gPowerSuspendAckEvent );
1699		gPowerSuspendAckEvent = NULL;
1700	}
1701
1702	if ( gPowerSuspendEvent )
1703	{
1704		mDNSPollUnregisterEvent( gPowerSuspendEvent );
1705		CloseHandle( gPowerSuspendEvent );
1706		gPowerSuspendEvent = NULL;
1707	}
1708
1709	if ( gStopEvent )
1710	{
1711		mDNSPollUnregisterEvent( gStopEvent );
1712	}
1713
1714	return( mStatus_NoError );
1715}
1716
1717
1718mDNSlocal void CALLBACK
1719StopNotification( HANDLE event, void *context )
1720{
1721	DEBUG_UNUSED( event );
1722	DEBUG_UNUSED( context );
1723
1724	dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" );
1725	udsserver_exit();
1726	mDNS_StartExit( &gMDNSRecord );
1727}
1728
1729
1730mDNSlocal void CALLBACK
1731PowerSuspendNotification( HANDLE event, void * context )
1732{
1733	LARGE_INTEGER	timeout;
1734	BOOL			ok;
1735
1736	DEBUG_UNUSED( event );
1737	DEBUG_UNUSED( context );
1738
1739	dlog( kDebugLevelInfo, DEBUG_NAME "PowerSuspendNotification\n" );
1740
1741	gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout );
1742
1743	if ( gMDNSRecord.SystemWakeOnLANEnabled )
1744	{
1745		ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE );
1746		check( ok );
1747	}
1748
1749	mDNSCoreMachineSleep(&gMDNSRecord, TRUE);
1750
1751	ok = SetEvent( gPowerSuspendAckEvent );
1752
1753	if ( !ok )
1754	{
1755		dlog( kDebugLevelError, DEBUG_NAME "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
1756		ReportStatus( EVENTLOG_ERROR_TYPE, "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
1757	}
1758}
1759
1760
1761mDNSlocal void CALLBACK
1762PowerResumeNotification( HANDLE event, void * context )
1763{
1764	DEBUG_UNUSED( event );
1765	DEBUG_UNUSED( context );
1766
1767	dlog( kDebugLevelInfo, DEBUG_NAME "PowerResumeNotification\n" );
1768
1769	if ( gSPSWakeupEvent )
1770	{
1771		CancelWaitableTimer( gSPSWakeupEvent );
1772	}
1773
1774	if ( gSPSSleepEvent )
1775	{
1776		CancelWaitableTimer( gSPSSleepEvent );
1777	}
1778
1779	mDNSCoreMachineSleep(&gMDNSRecord, FALSE);
1780}
1781
1782
1783
1784mDNSlocal void CALLBACK
1785InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context )
1786{
1787	int		inBuffer;
1788	int		outBuffer;
1789	DWORD	outSize;
1790	int		err;
1791
1792	DEBUG_UNUSED( socket );
1793	DEBUG_UNUSED( event );
1794	DEBUG_UNUSED( context );
1795
1796	// It would be nice to come up with a more elegant solution to this, but it seems that
1797	// GetAdaptersAddresses doesn't always stay in sync after network changed events.  So as
1798	// as a simple workaround, we'll pause for a couple of seconds before processing the change.
1799
1800	// We arrived at 2 secs by trial and error. We could reproduce the problem after sleeping
1801	// for 500 msec and 750 msec, but couldn't after sleeping for 1 sec.  We added another
1802	// second on top of that to account for machine load or some other exigency.
1803
1804	Sleep( 2000 );
1805
1806	// Interface list changed event. Break out of the inner loop to re-setup the wait list.
1807
1808	InterfaceListDidChange( &gMDNSRecord );
1809
1810	// reset the event handler
1811	inBuffer	= 0;
1812	outBuffer	= 0;
1813	err = WSAIoctl( gInterfaceListChangedSocket, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
1814	if( err < 0 )
1815	{
1816		check( errno_compat() == WSAEWOULDBLOCK );
1817	}
1818}
1819
1820
1821mDNSlocal void CALLBACK
1822ComputerDescriptionNotification( HANDLE event, void *context )
1823{
1824	// The computer description might have changed
1825
1826	DEBUG_UNUSED( event );
1827	DEBUG_UNUSED( context );
1828
1829	ComputerDescriptionDidChange( &gMDNSRecord );
1830	udsserver_handle_configchange( &gMDNSRecord );
1831
1832	// and reset the event handler
1833	if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) )
1834	{
1835		int err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
1836		check_noerr( err );
1837	}
1838}
1839
1840
1841mDNSlocal void CALLBACK
1842TCPChangedNotification( HANDLE event, void *context )
1843{
1844	// The TCP/IP might have changed
1845
1846	DEBUG_UNUSED( event );
1847	DEBUG_UNUSED( context );
1848
1849	TCPIPConfigDidChange( &gMDNSRecord );
1850	udsserver_handle_configchange( &gMDNSRecord );
1851
1852	// and reset the event handler
1853
1854	if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) )
1855	{
1856		int err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE );
1857		check_noerr( err );
1858	}
1859}
1860
1861
1862mDNSlocal void CALLBACK
1863DDNSChangedNotification( HANDLE event, void *context )
1864{
1865	// The DynDNS config might have changed
1866
1867	DEBUG_UNUSED( event );
1868	DEBUG_UNUSED( context );
1869
1870	DynDNSConfigDidChange( &gMDNSRecord );
1871	udsserver_handle_configchange( &gMDNSRecord );
1872
1873	// and reset the event handler
1874
1875	if ((gDdnsKey != NULL) && (gDdnsChangedEvent))
1876	{
1877		int err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
1878		check_noerr( err );
1879	}
1880}
1881
1882
1883mDNSlocal void CALLBACK
1884FileSharingChangedNotification( HANDLE event, void *context )
1885{
1886	// File sharing changed
1887
1888	DEBUG_UNUSED( event );
1889	DEBUG_UNUSED( context );
1890
1891	FileSharingDidChange( &gMDNSRecord );
1892
1893	// and reset the event handler
1894
1895	if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent))
1896	{
1897		int err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
1898		check_noerr( err );
1899	}
1900}
1901
1902
1903mDNSlocal void CALLBACK
1904FirewallChangedNotification( HANDLE event, void *context )
1905{
1906	// Firewall configuration changed
1907
1908	DEBUG_UNUSED( event );
1909	DEBUG_UNUSED( context );
1910
1911	FirewallDidChange( &gMDNSRecord );
1912
1913	// and reset the event handler
1914
1915	if ((gFirewallKey != NULL) && (gFirewallChangedEvent))
1916	{
1917		int err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
1918		check_noerr( err );
1919	}
1920}
1921
1922
1923mDNSlocal void CALLBACK
1924AdvertisedServicesChangedNotification( HANDLE event, void *context )
1925{
1926	// Ultimately we'll want to manage multiple services, but right now the only service
1927	// we'll be managing is SMB.
1928
1929	DEBUG_UNUSED( event );
1930	DEBUG_UNUSED( context );
1931
1932	FileSharingDidChange( &gMDNSRecord );
1933
1934	// and reset the event handler
1935
1936	if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) )
1937	{
1938		int err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
1939		check_noerr( err );
1940	}
1941}
1942
1943
1944mDNSlocal void CALLBACK
1945SPSWakeupNotification( HANDLE event, void *context )
1946{
1947	LARGE_INTEGER timeout;
1948
1949	DEBUG_UNUSED( event );
1950	DEBUG_UNUSED( context );
1951
1952	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" );
1953
1954	timeout.QuadPart  = kSPSMaintenanceWakePeriod;
1955	timeout.QuadPart *= kSecondsTo100NSUnits;
1956
1957	SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE );
1958}
1959
1960
1961mDNSlocal void CALLBACK
1962SPSSleepNotification( HANDLE event, void *context )
1963{
1964	DEBUG_UNUSED( event );
1965	DEBUG_UNUSED( context );
1966
1967	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Returning to sleep after maintenance wake" );
1968
1969	// Calling SetSuspendState() doesn't invoke our sleep handlers, so we'll
1970	// call HandlePowerSuspend() explicity.  This will reset the
1971	// maintenance wake timers.
1972
1973	PowerSuspendNotification( gPowerSuspendEvent, NULL );
1974	SetSuspendState( FALSE, FALSE, FALSE );
1975}
1976
1977
1978//===========================================================================================================================
1979//	CoreCallback
1980//===========================================================================================================================
1981
1982static void
1983CoreCallback(mDNS * const inMDNS, mStatus status)
1984{
1985	if (status == mStatus_ConfigChanged)
1986	{
1987		SetLLRoute( inMDNS );
1988	}
1989}
1990
1991
1992//===========================================================================================================================
1993//	UDSAcceptNotification
1994//===========================================================================================================================
1995
1996mDNSlocal void CALLBACK
1997UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
1998{
1999	( void ) sock;
2000	( void ) event;
2001	( void ) context;
2002
2003	if ( gUDSCallback )
2004	{
2005		gUDSCallback( ( int ) gUDSSocket, 0, context );
2006	}
2007}
2008
2009
2010//===========================================================================================================================
2011//	UDSReadNotification
2012//===========================================================================================================================
2013
2014mDNSlocal void CALLBACK
2015UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
2016{
2017	TCPSocket *tcpSock = ( TCPSocket* ) context;
2018
2019	( void ) sock;
2020	( void ) event;
2021
2022	if ( tcpSock )
2023	{
2024		tcpSock->userCallback( ( int ) tcpSock->fd, 0, tcpSock->userContext );
2025	}
2026}
2027
2028
2029//===========================================================================================================================
2030//	udsSupportAddFDToEventLoop
2031//===========================================================================================================================
2032
2033mStatus
2034udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
2035{
2036	mStatus err = mStatus_NoError;
2037
2038	// We are using some knowledge of what is being passed to us here.  If the fd is a listen socket,
2039	// then the "context" parameter is NULL.  If it is an actual read/write socket, then the "context"
2040	// parameter is not null.
2041
2042	if ( context )
2043	{
2044		TCPSocket * sock;
2045
2046		sock = malloc( sizeof( TCPSocket ) );
2047		require_action( sock, exit, err = mStatus_NoMemoryErr );
2048		mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
2049
2050		sock->fd				= (SOCKET) fd;
2051		sock->userCallback		= callback;
2052		sock->userContext		= context;
2053		sock->m					= &gMDNSRecord;
2054
2055		*platform_data = sock;
2056
2057		err = mDNSPollRegisterSocket( sock->fd, FD_READ | FD_CLOSE, UDSReadNotification, sock );
2058		require_noerr( err, exit );
2059	}
2060	else
2061	{
2062		gUDSSocket		= fd;
2063		gUDSCallback	= callback;
2064
2065		err = mDNSPollRegisterSocket( gUDSSocket, FD_ACCEPT | FD_CLOSE, UDSAcceptNotification, NULL );
2066		require_noerr( err, exit );
2067	}
2068
2069exit:
2070
2071	return err;
2072}
2073
2074
2075int
2076udsSupportReadFD( SocketRef fd, char *buf, int len, int flags, void *platform_data )
2077{
2078	TCPSocket	*	sock;
2079	mDNSBool		closed;
2080	int				ret;
2081
2082	( void ) flags;
2083
2084	sock = ( TCPSocket* ) platform_data;
2085	require_action( sock, exit, ret = -1 );
2086	require_action( sock->fd == fd, exit, ret = -1 );
2087
2088	ret = mDNSPlatformReadTCP( sock, buf, len, &closed );
2089
2090	if ( closed )
2091	{
2092		ret = 0;
2093	}
2094	else if ( !ret && ( WSAGetLastError() == WSAEWOULDBLOCK ) )
2095	{
2096		// mDNSPlatformReadTCP will return 0 if it gets WSAEWOULDBLOCK, but
2097		// that caller of this routine interprets that as close connection.
2098		// We'll fix that by returning -1 in that case.
2099
2100		ret = -1;
2101	}
2102
2103exit:
2104
2105	return ret;
2106}
2107
2108
2109mStatus
2110udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data)		// Note: This also CLOSES the socket
2111{
2112	mStatus err = kNoErr;
2113
2114	mDNSPollUnregisterSocket( fd );
2115
2116	if ( platform_data != NULL )
2117	{
2118		TCPSocket * sock;
2119
2120		dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" );
2121		sock = ( TCPSocket* ) platform_data;
2122		check( sock->fd == fd );
2123		mDNSPlatformTCPCloseConnection( sock );
2124	}
2125
2126	return err;
2127}
2128
2129
2130mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
2131	{
2132	(void)m;
2133	(void)delay;
2134	// No-op, for now
2135	}
2136
2137
2138//===========================================================================================================================
2139//	SystemWakeForNetworkAccess
2140//===========================================================================================================================
2141
2142mDNSu8
2143SystemWakeForNetworkAccess( LARGE_INTEGER * timeout )
2144{
2145	HKEY					key = NULL;
2146	DWORD					dwSize;
2147	DWORD					enabled;
2148	mDNSu8					ok;
2149	SYSTEM_POWER_STATUS		powerStatus;
2150	time_t					startTime;
2151	time_t					nextWakeupTime;
2152	int						delta;
2153	DWORD					err;
2154
2155	dlog( kDebugLevelInfo, DEBUG_NAME "SystemWakeForNetworkAccess\n" );
2156
2157	// Make sure we have a timer
2158
2159	require_action( gSPSWakeupEvent != NULL, exit, ok = FALSE );
2160	require_action( gSPSSleepEvent != NULL, exit, ok = FALSE );
2161
2162	// Make sure the user enabled bonjour sleep proxy client
2163
2164	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", &key );
2165	require_action( !err, exit, ok = FALSE );
2166	dwSize = sizeof( DWORD );
2167	err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
2168	require_action( !err, exit, ok = FALSE );
2169	require_action( enabled, exit, ok = FALSE );
2170
2171	// Make sure machine is on AC power
2172
2173	ok = ( mDNSu8 ) GetSystemPowerStatus( &powerStatus );
2174	require_action( ok, exit, ok = FALSE );
2175	require_action( powerStatus.ACLineStatus == AC_LINE_ONLINE, exit, ok = FALSE );
2176
2177	// Now make sure we have a network interface that does wake-on-lan
2178
2179	ok = ( mDNSu8 ) IsWOMPEnabled( &gMDNSRecord );
2180	require_action( ok, exit, ok = FALSE );
2181
2182	// Now make sure we have advertised services. Doesn't make sense to
2183	// enable sleep proxy if we have no multicast services that could
2184	// potentially wake us up.
2185
2186	ok = ( mDNSu8 ) mDNSCoreHaveAdvertisedMulticastServices( &gMDNSRecord );
2187	require_action( ok, exit, ok = FALSE );
2188
2189	// Calculate next wake up time
2190
2191	startTime		= time( NULL );					// Seconds since midnight January 1, 1970
2192	nextWakeupTime	= startTime + ( 120 * 60 );		// 2 hours later
2193
2194	if ( gMDNSRecord.p->nextDHCPLeaseExpires < nextWakeupTime )
2195	{
2196		nextWakeupTime = gMDNSRecord.p->nextDHCPLeaseExpires;
2197	}
2198
2199	// Finally calculate the next relative wakeup time
2200
2201	delta = ( int )( ( ( double )( nextWakeupTime - startTime ) ) * 0.9 );
2202	ReportStatus( EVENTLOG_INFORMATION_TYPE, "enabling sleep proxy client with next maintenance wake in %d seconds", delta );
2203
2204	// Convert seconds to 100 nanosecond units expected by SetWaitableTimer
2205
2206	timeout->QuadPart  = -delta;
2207	timeout->QuadPart *= kSecondsTo100NSUnits;
2208
2209	ok = TRUE;
2210
2211exit:
2212
2213	if ( key )
2214	{
2215		RegCloseKey( key );
2216	}
2217
2218	return ok;
2219}
2220
2221
2222//===========================================================================================================================
2223//	HaveRoute
2224//===========================================================================================================================
2225
2226static bool
2227HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric )
2228{
2229	PMIB_IPFORWARDTABLE	pIpForwardTable	= NULL;
2230	DWORD				dwSize			= 0;
2231	BOOL				bOrder			= FALSE;
2232	OSStatus			err;
2233	bool				found			= false;
2234	unsigned long int	i;
2235
2236	//
2237	// Find out how big our buffer needs to be.
2238	//
2239	err = GetIpForwardTable(NULL, &dwSize, bOrder);
2240	require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
2241
2242	//
2243	// Allocate the memory for the table
2244	//
2245	pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
2246	require_action( pIpForwardTable, exit, err = kNoMemoryErr );
2247
2248	//
2249	// Now get the table.
2250	//
2251	err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
2252	require_noerr( err, exit );
2253
2254	//
2255	// Search for the row in the table we want.
2256	//
2257	for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
2258	{
2259		if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) )
2260		{
2261			memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) );
2262			found = true;
2263			break;
2264		}
2265	}
2266
2267exit:
2268
2269	if ( pIpForwardTable != NULL )
2270	{
2271		free(pIpForwardTable);
2272	}
2273
2274	return found;
2275}
2276
2277
2278//===========================================================================================================================
2279//	IsValidAddress
2280//===========================================================================================================================
2281
2282static bool
2283IsValidAddress( const char * addr )
2284{
2285	return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false;
2286}
2287
2288
2289//===========================================================================================================================
2290//	GetAdditionalMetric
2291//===========================================================================================================================
2292
2293static ULONG
2294GetAdditionalMetric( DWORD ifIndex )
2295{
2296	ULONG metric = 0;
2297
2298	if( !gIPHelperLibraryInstance )
2299	{
2300		gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
2301
2302		gGetIpInterfaceEntryFunctionPtr =
2303				(GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" );
2304
2305		if( !gGetIpInterfaceEntryFunctionPtr )
2306		{
2307			BOOL ok;
2308
2309			ok = FreeLibrary( gIPHelperLibraryInstance );
2310			check_translated_errno( ok, GetLastError(), kUnknownErr );
2311			gIPHelperLibraryInstance = NULL;
2312		}
2313	}
2314
2315	if ( gGetIpInterfaceEntryFunctionPtr )
2316	{
2317		MIB_IPINTERFACE_ROW row;
2318		DWORD err;
2319
2320		ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) );
2321		row.Family = AF_INET;
2322		row.InterfaceIndex = ifIndex;
2323		err = gGetIpInterfaceEntryFunctionPtr( &row );
2324		require_noerr( err, exit );
2325		metric = row.Metric + 256;
2326	}
2327
2328exit:
2329
2330	return metric;
2331}
2332
2333
2334//===========================================================================================================================
2335//	SetLLRoute
2336//===========================================================================================================================
2337
2338static OSStatus
2339SetLLRoute( mDNS * const inMDNS )
2340{
2341	OSStatus err = kNoErr;
2342
2343	DEBUG_UNUSED( inMDNS );
2344
2345	//
2346	// <rdar://problem/4096464> Don't call SetLLRoute on loopback
2347	// <rdar://problem/6885843> Default route on Windows 7 breaks network connectivity
2348	//
2349	// Don't mess w/ the routing table on Vista and later OSes, as
2350	// they have a permanent route to link-local addresses. Otherwise,
2351	// set a route to link local addresses (169.254.0.0)
2352	//
2353	if ( ( inMDNS->p->osMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 )
2354	{
2355		DWORD				ifIndex;
2356		MIB_IPFORWARDROW	rowExtant;
2357		bool				addRoute;
2358		MIB_IPFORWARDROW	row;
2359
2360		ZeroMemory(&row, sizeof(row));
2361
2362		err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop);
2363		require_noerr( err, exit );
2364		row.dwForwardDest		= inet_addr(kLLNetworkAddr);
2365		row.dwForwardIfIndex	= ifIndex;
2366		row.dwForwardMask		= inet_addr(kLLNetworkAddrMask);
2367		row.dwForwardType		= 3;
2368		row.dwForwardProto		= MIB_IPPROTO_NETMGMT;
2369		row.dwForwardAge		= 0;
2370		row.dwForwardPolicy		= 0;
2371		row.dwForwardMetric1	= 20 + GetAdditionalMetric( ifIndex );
2372		row.dwForwardMetric2	= (DWORD) - 1;
2373		row.dwForwardMetric3	= (DWORD) - 1;
2374		row.dwForwardMetric4	= (DWORD) - 1;
2375		row.dwForwardMetric5	= (DWORD) - 1;
2376
2377		addRoute = true;
2378
2379		//
2380		// check to make sure we don't already have a route
2381		//
2382		if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) )
2383		{
2384			//
2385			// set the age to 0 so that we can do a memcmp.
2386			//
2387			rowExtant.dwForwardAge = 0;
2388
2389			//
2390			// check to see if this route is the same as our route
2391			//
2392			if (memcmp(&row, &rowExtant, sizeof(row)) != 0)
2393			{
2394				//
2395				// if it isn't then delete this entry
2396				//
2397				DeleteIpForwardEntry(&rowExtant);
2398			}
2399			else
2400			{
2401				//
2402				// else it is, so we don't want to create another route
2403				//
2404				addRoute = false;
2405			}
2406		}
2407
2408		if (addRoute && row.dwForwardNextHop)
2409		{
2410			err = CreateIpForwardEntry(&row);
2411			check_noerr( err );
2412		}
2413	}
2414
2415exit:
2416
2417	return ( err );
2418}
2419
2420
2421//===========================================================================================================================
2422//	GetRouteDestination
2423//===========================================================================================================================
2424
2425static OSStatus
2426GetRouteDestination(DWORD * ifIndex, DWORD * address)
2427{
2428	struct in_addr		ia;
2429	IP_ADAPTER_INFO	*	pAdapterInfo	=	NULL;
2430	IP_ADAPTER_INFO	*	pAdapter		=	NULL;
2431	ULONG				bufLen;
2432	mDNSBool			done			=	mDNSfalse;
2433	OSStatus			err;
2434
2435	//
2436	// GetBestInterface will fail if there is no default gateway
2437	// configured.  If that happens, we will just take the first
2438	// interface in the list. MSDN support says there is no surefire
2439	// way to manually determine what the best interface might
2440	// be for a particular network address.
2441	//
2442	ia.s_addr	=	inet_addr(kLLNetworkAddr);
2443	err			=	GetBestInterface(*(IPAddr*) &ia, ifIndex);
2444
2445	if (err)
2446	{
2447		*ifIndex = 0;
2448	}
2449
2450	//
2451	// Make an initial call to GetAdaptersInfo to get
2452	// the necessary size into the bufLen variable
2453	//
2454	err = GetAdaptersInfo( NULL, &bufLen);
2455	require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr );
2456
2457	pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen );
2458	require_action( pAdapterInfo, exit, err = kNoMemoryErr );
2459
2460	err = GetAdaptersInfo( pAdapterInfo, &bufLen);
2461	require_noerr( err, exit );
2462
2463	pAdapter	=	pAdapterInfo;
2464	err			=	kUnknownErr;
2465
2466	// <rdar://problem/3718122>
2467	// <rdar://problem/5652098>
2468	//
2469	// Look for the Nortel VPN virtual interface, along with Juniper virtual interface.
2470	//
2471	// If these interfaces are active (i.e., has a non-zero IP Address),
2472	// then we want to disable routing table modifications.
2473
2474	while (pAdapter)
2475	{
2476		if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) &&
2477			 ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) )
2478		{
2479			dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" );
2480			goto exit;
2481		}
2482
2483		pAdapter = pAdapter->Next;
2484	}
2485
2486	while ( !done )
2487	{
2488		pAdapter	=	pAdapterInfo;
2489		err			=	kUnknownErr;
2490
2491		while (pAdapter)
2492		{
2493			// If we don't have an interface selected, choose the first one that is of type ethernet and
2494			// has a valid IP Address
2495
2496			if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex))))
2497			{
2498				*address =	inet_addr( pAdapter->IpAddressList.IpAddress.String );
2499				*ifIndex =  pAdapter->Index;
2500				err		 =	kNoErr;
2501				break;
2502			}
2503
2504			pAdapter = pAdapter->Next;
2505		}
2506
2507		// If we found the right interface, or we weren't trying to find a specific interface then we're done
2508
2509		if ( !err || !( *ifIndex) )
2510		{
2511			done = mDNStrue;
2512		}
2513
2514		// Otherwise, try again by wildcarding the interface
2515
2516		else
2517		{
2518			*ifIndex = 0;
2519		}
2520	}
2521
2522exit:
2523
2524	if ( pAdapterInfo != NULL )
2525	{
2526		free( pAdapterInfo );
2527	}
2528
2529	return( err );
2530}
2531
2532
2533static bool
2534IsNortelVPN( IP_ADAPTER_INFO * pAdapter )
2535{
2536	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
2537		    (pAdapter->AddressLength == 6) &&
2538		    (pAdapter->Address[0] == 0x44) &&
2539		    (pAdapter->Address[1] == 0x45) &&
2540		    (pAdapter->Address[2] == 0x53) &&
2541		    (pAdapter->Address[3] == 0x54) &&
2542		    (pAdapter->Address[4] == 0x42) &&
2543			(pAdapter->Address[5] == 0x00)) ? true : false;
2544}
2545
2546
2547static bool
2548IsJuniperVPN( IP_ADAPTER_INFO * pAdapter )
2549{
2550	return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description  ) ) != NULL ) ? true : false;
2551}
2552
2553
2554static bool
2555IsCiscoVPN( IP_ADAPTER_INFO * pAdapter )
2556{
2557	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
2558		    (pAdapter->AddressLength == 6) &&
2559		    (pAdapter->Address[0] == 0x00) &&
2560		    (pAdapter->Address[1] == 0x05) &&
2561		    (pAdapter->Address[2] == 0x9a) &&
2562		    (pAdapter->Address[3] == 0x3c) &&
2563		    (pAdapter->Address[4] == 0x7a) &&
2564			(pAdapter->Address[5] == 0x00)) ? true : false;
2565}
2566
2567
2568static const char *
2569strnistr( const char * string, const char * subString, size_t max )
2570{
2571	size_t       subStringLen;
2572	size_t       offset;
2573	size_t       maxOffset;
2574	size_t       stringLen;
2575	const char * pPos;
2576
2577	if ( ( string == NULL ) || ( subString == NULL ) )
2578	{
2579		return string;
2580	}
2581
2582	stringLen = ( max > strlen( string ) ) ? strlen( string ) : max;
2583
2584	if ( stringLen == 0 )
2585	{
2586		return NULL;
2587	}
2588
2589	subStringLen = strlen( subString );
2590
2591	if ( subStringLen == 0 )
2592	{
2593		return string;
2594	}
2595
2596	if ( subStringLen > stringLen )
2597	{
2598		return NULL;
2599	}
2600
2601	maxOffset = stringLen - subStringLen;
2602	pPos      = string;
2603
2604	for ( offset = 0; offset <= maxOffset; offset++ )
2605	{
2606		if ( _strnicmp( pPos, subString, subStringLen ) == 0 )
2607		{
2608			return pPos;
2609		}
2610
2611		pPos++;
2612	}
2613
2614	return NULL;
2615}
2616
2617