text: /* -*- Mode: C; tab-width: 4 -*-
   *
   * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *     http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include	<stdio.h>
  #include	<stdlib.h>
  #include	<crtdbg.h>
  #include	<stdarg.h>
  #include	<stddef.h>
  
  #include	"Poll.h"
  #include	"CommonServices.h"
  #include	"DebugServices.h"
  #include	"RegNames.h"
  
  #include	"uds_daemon.h"
  #include	"GenLinkedList.h"
  #include	"Service.h"
  #include	"EventLog.h"
  
  #include	"Resource.h"
  
  #include	"mDNSEmbeddedAPI.h"
  #include	"uDNS.h"
  #include	"mDNSWin32.h"
  #include	"mDNSDebug.h"
  
  #include	"Firewall.h"
  
  #if( !TARGET_OS_WINDOWS_CE )
  	#include	<mswsock.h>
  	#include	<process.h>
  	#include	<ipExport.h>
  	#include	<ws2def.h>
  	#include	<ws2ipdef.h>
  	#include	<iphlpapi.h>
  	#include	<netioapi.h>
  	#include	<iptypes.h>
  	#include	<powrprof.h>
  #endif
  
  #ifndef HeapEnableTerminationOnCorruption
  #	define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
  #endif
  
  #if 0
  #pragma mark == Constants ==
  #endif
  
  //===========================================================================================================================
  //	Constants
  //===========================================================================================================================
  
  #define	DEBUG_NAME							"[mDNSWin32] "
  #define kServiceFirewallName				L"Bonjour"
  #define	kServiceDependencies				TEXT("Tcpip\0\0")
  #define	kDNSServiceCacheEntryCountDefault	512
  #define kRetryFirewallPeriod				30 * 1000
  #define kDefValueSize						MAX_PATH + 1
  #define kZeroIndex							0
  #define kDefaultRouteMetric					399
  #define kSecondsTo100NSUnits				( 10 * 1000 * 1000 )
  #define kSPSMaintenanceWakePeriod			-30
  #define kWaitToRetry						(60 * 5)
  
  #define RR_CACHE_SIZE 500
  static CacheEntity gRRCache[RR_CACHE_SIZE];
  #if 0
  #pragma mark == Structures ==
  #endif
  
  #if 0
  #pragma mark == Prototypes ==
  #endif
  
  //===========================================================================================================================
  //	Prototypes
  //===========================================================================================================================
  static void				Usage( void );
  static BOOL WINAPI		ConsoleControlHandler( DWORD inControlEvent );
  static OSStatus			InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath );
  static OSStatus			RemoveService( LPCTSTR inName );
  static OSStatus			SetServiceParameters();
  static OSStatus			GetServiceParameters();
  static OSStatus			CheckFirewall();
  static OSStatus			SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription );
  static void				ReportStatus( int inType, const char *inFormat, ... );
  
  static void WINAPI		ServiceMain( DWORD argc, LPTSTR argv[] );
  static OSStatus			ServiceSetupEventLogging( void );
  static DWORD WINAPI		ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext );
  
  static OSStatus			ServiceRun( int argc, LPTSTR argv[] );
  static void				ServiceStop( void );
  
  static OSStatus			ServiceSpecificInitialize( int argc, LPTSTR  argv[] );
  static OSStatus			ServiceSpecificRun( int argc, LPTSTR argv[] );
  static OSStatus			ServiceSpecificStop( void );
  static void				ServiceSpecificFinalize( int argc, LPTSTR argv[] );
  static mStatus			SetupServiceEvents();
  static mStatus			TearDownServiceEvents();
  static mStatus			SetupNotifications();
  static mStatus			TearDownNotifications();
  static void CALLBACK	StopNotification( HANDLE event, void * context );
  static void CALLBACK	PowerSuspendNotification( HANDLE event, void * context );
  static void	CALLBACK	PowerResumeNotification( HANDLE event, void * context );
  static void CALLBACK	InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context );
  static void CALLBACK	ComputerDescriptionNotification( HANDLE event, void *context );
  static void CALLBACK	TCPChangedNotification( HANDLE event, void *context );
  static void CALLBACK	DDNSChangedNotification( HANDLE event, void *context );
  static void CALLBACK	FileSharingChangedNotification( HANDLE event, void *context );
  static void CALLBACK	FirewallChangedNotification( HANDLE event, void *context );
  static void CALLBACK	AdvertisedServicesChangedNotification( HANDLE event, void *context );
  static void CALLBACK	SPSWakeupNotification( HANDLE event, void *context );
  static void	CALLBACK	SPSSleepNotification( HANDLE event, void *context );
  static void CALLBACK	UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
  static void CALLBACK	UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context );
  static void				CoreCallback(mDNS * const inMDNS, mStatus result);
  static mDNSu8			SystemWakeForNetworkAccess( LARGE_INTEGER * timeout );
  static OSStatus			GetRouteDestination(DWORD * ifIndex, DWORD * address);
  static OSStatus			SetLLRoute( mDNS * const inMDNS );
  static bool				HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric );
  static bool				IsValidAddress( const char * addr );
  static bool				IsNortelVPN( IP_ADAPTER_INFO * pAdapter );
  static bool				IsJuniperVPN( IP_ADAPTER_INFO * pAdapter );
  static bool				IsCiscoVPN( IP_ADAPTER_INFO * pAdapter );
  static const char *		strnistr( const char * string, const char * subString, size_t max );
  
  #if defined(UNICODE)
  #	define StrLen(X)	wcslen(X)
  #	define StrCmp(X,Y)	wcscmp(X,Y)
  #else
  #	define StrLen(X)	strlen(X)
  #	define StrCmp(X,Y)	strcmp(X,Y)
  #endif
  
  
  #define kLLNetworkAddr      "169.254.0.0"
  #define kLLNetworkAddrMask  "255.255.0.0"
  
  
  #include	"mDNSEmbeddedAPI.h"
  
  #if 0
  #pragma mark == Globals ==
  #endif
  
  //===========================================================================================================================
  //	Globals
  //===========================================================================================================================
  #define gMDNSRecord mDNSStorage
  DEBUG_LOCAL	mDNS_PlatformSupport		gPlatformStorage;
  DEBUG_LOCAL BOOL						gServiceQuietMode		= FALSE;
  DEBUG_LOCAL SERVICE_TABLE_ENTRY			gServiceDispatchTable[] = 
  {
  	{ kServiceName,	ServiceMain }, 
  	{ NULL, 		NULL }
  };
  DEBUG_LOCAL HANDLE						gStopEvent					= NULL;
  DEBUG_LOCAL HANDLE						gPowerSuspendEvent			= NULL;
  DEBUG_LOCAL HANDLE						gPowerSuspendAckEvent		= NULL;
  DEBUG_LOCAL HANDLE						gPowerResumeEvent			= NULL;
  DEBUG_LOCAL SOCKET						gInterfaceListChangedSocket	= INVALID_SOCKET;
  DEBUG_LOCAL HKEY						gDescKey					= NULL;
  DEBUG_LOCAL HANDLE						gDescChangedEvent			= NULL;	// Computer description changed event
  DEBUG_LOCAL HKEY						gTcpipKey					= NULL;
  DEBUG_LOCAL HANDLE						gTcpipChangedEvent			= NULL;	// TCP/IP config changed
  DEBUG_LOCAL HKEY						gDdnsKey					= NULL;
  DEBUG_LOCAL HANDLE						gDdnsChangedEvent			= NULL;	// DynDNS config changed
  DEBUG_LOCAL HKEY						gFileSharingKey				= NULL;
  DEBUG_LOCAL HANDLE						gFileSharingChangedEvent	= NULL;	// File Sharing changed
  DEBUG_LOCAL HKEY						gFirewallKey				= NULL;
  DEBUG_LOCAL HANDLE						gFirewallChangedEvent		= NULL;	// Firewall changed
  DEBUG_LOCAL HKEY						gAdvertisedServicesKey		= NULL;
  DEBUG_LOCAL HANDLE						gAdvertisedServicesChangedEvent	= NULL; // Advertised services changed
  DEBUG_LOCAL SERVICE_STATUS				gServiceStatus;
  DEBUG_LOCAL SERVICE_STATUS_HANDLE		gServiceStatusHandle 	= NULL;
  DEBUG_LOCAL HANDLE						gServiceEventSource		= NULL;
  DEBUG_LOCAL bool						gServiceAllowRemote		= false;
  DEBUG_LOCAL int							gServiceCacheEntryCount	= 0;	// 0 means to use the DNS-SD default.
  DEBUG_LOCAL bool						gServiceManageLLRouting = true;
  DEBUG_LOCAL HANDLE						gSPSWakeupEvent			= NULL;
  DEBUG_LOCAL HANDLE						gSPSSleepEvent			= NULL;
  DEBUG_LOCAL SocketRef					gUDSSocket				= 0;
  DEBUG_LOCAL udsEventCallback			gUDSCallback			= NULL;
  DEBUG_LOCAL BOOL						gRetryFirewall			= FALSE;
  
  typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW );
  mDNSlocal HMODULE								gIPHelperLibraryInstance		= NULL;
  mDNSlocal GetIpInterfaceEntryFunctionPtr		gGetIpInterfaceEntryFunctionPtr	= NULL;
  
  
  #if 0
  #pragma mark -
  #endif
  
  //===========================================================================================================================
  //	Main
  //===========================================================================================================================
  int	Main( int argc, LPTSTR argv[] )
  {
  	OSStatus		err;
  	BOOL			ok;
  	BOOL			start;
  	int				i;
  
  	HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
  
  	debug_initialize( kDebugOutputTypeMetaConsole );
  	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
  
  	// Default to automatically starting the service dispatcher if no extra arguments are specified.
  	
  	start = ( argc <= 1 );
  	
  	// Parse arguments.
  	
  	for( i = 1; i < argc; ++i )
  	{
  		if( StrCmp( argv[ i ], TEXT("-install") ) == 0 )			// Install
  		{
  			TCHAR desc[ 256 ];
  			
  			desc[ 0 ] = 0;
  			LoadString( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) );
  			err = InstallService( kServiceName, kServiceName, desc, argv[0] );
  			if( err )
  			{
  				ReportStatus( EVENTLOG_ERROR_TYPE, "install service failed (%d)\n", err );
  				goto exit;
  			}
  		}
  		else if( StrCmp( argv[ i ], TEXT("-remove") ) == 0 )		// Remove
  		{
  			err = RemoveService( kServiceName );
  			if( err )
  			{
  				ReportStatus( EVENTLOG_ERROR_TYPE, "remove service failed (%d)\n", err );
  				goto exit;
  			}
  		}
  		else if( StrCmp( argv[ i ], TEXT("-start") ) == 0 )		// Start
  		{
  			start = TRUE;
  		}
  		else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 )		// Server
  		{
  			err = RunDirect( argc, argv );
  			if( err )
  			{
  				ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err );
  			}
  			goto exit;
  		}
  		else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 )			// Quiet Mode (toggle)
  		{
  			gServiceQuietMode = !gServiceQuietMode;
  		}
  		else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || 	// Help
  				 ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) )
  		{
  			Usage();
  			err = 0;
  			break;
  		}
  		else
  		{
  			Usage();
  			err = kParamErr;
  			break;
  		}
  	}
  	
  	// Start the service dispatcher if requested. This does not return until all services have terminated. If any 
  	// global initialization is needed, it should be done before starting the service dispatcher, but only if it 
  	// will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
  	
  	if( start )
  	{
  		ok = StartServiceCtrlDispatcher( gServiceDispatchTable );
  		err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
  		if( err != kNoErr )
  		{
  			ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err );
  			goto exit;
  		}
  	}
  	err = 0;
  	
  exit:
  	dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err );
  	_CrtDumpMemoryLeaks();
  	return( (int) err );
  }
  
  //===========================================================================================================================
  //	Usage
  //===========================================================================================================================
  
  static void	Usage( void )
  {
  	fprintf( stderr, "\n" );
  	fprintf( stderr, "mDNSResponder 1.0d1\n" );
  	fprintf( stderr, "\n" );
  	fprintf( stderr, "    <no args>    Runs the service normally\n" );
  	fprintf( stderr, "    -install     Creates the service and starts it\n" );
  	fprintf( stderr, "    -remove      Stops the service and deletes it\n" );
  	fprintf( stderr, "    -start       Starts the service dispatcher after processing all other arguments\n" );
  	fprintf( stderr, "    -server      Runs the service directly as a server (for debugging)\n" );
  	fprintf( stderr, "    -q           Toggles Quiet Mode (no events or output)\n" );
  	fprintf( stderr, "    -remote      Allow remote connections\n" );
  	fprintf( stderr, "    -cache n     Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault );
  	fprintf( stderr, "    -h[elp]      Display Help/Usage\n" );
  	fprintf( stderr, "\n" );
  }
  
  //===========================================================================================================================
  //	ConsoleControlHandler
  //===========================================================================================================================
  
  static BOOL WINAPI	ConsoleControlHandler( DWORD inControlEvent )
  {
  	BOOL			handled;
  	OSStatus		err;
  	
  	handled = FALSE;
  	switch( inControlEvent )
  	{
  		case CTRL_C_EVENT:
  		case CTRL_BREAK_EVENT:
  		case CTRL_CLOSE_EVENT:
  		case CTRL_LOGOFF_EVENT:
  		case CTRL_SHUTDOWN_EVENT:
  			err = ServiceSpecificStop();
  			require_noerr( err, exit );
  			
  			handled = TRUE;
  			break;
  		
  		default:
  			break;
  	}
  	
  exit:
  	return( handled );
  }
  
  //===========================================================================================================================
  //	InstallService
  //===========================================================================================================================
  
  static OSStatus	InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath )
  {
  	OSStatus		err;
  	SC_HANDLE		scm;
  	SC_HANDLE		service;
  	BOOL			ok;
  	TCHAR			fullPath[ MAX_PATH ];
  	TCHAR *			namePtr;
  	DWORD			size;
  	
  	scm		= NULL;
  	service = NULL;
  	
  	// Get a full path to the executable since a relative path may have been specified.
  	
  	size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr );
  	err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
  	require_noerr( err, exit );
  	
  	// Create the service and start it.
  	
  	scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
  	require_noerr( err, exit );
  	
  	service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, 
  							 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies, 
  							 NULL, NULL );
  	err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr );
  	require_noerr( err, exit );
  
  	err = SetServiceParameters();
  	check_noerr( err );
  	
  	if( inDescription )
  	{
  		err = SetServiceInfo( scm, inName, inDescription );
  		check_noerr( err );
  	}
  
  	ok = StartService( service, 0, NULL );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
  	require_noerr( err, exit );
  	
  	ReportStatus( EVENTLOG_SUCCESS, "installed service\n" );
  	err = kNoErr;
  	
  exit:
  	if( service )
  	{
  		CloseServiceHandle( service );
  	}
  	if( scm )
  	{
  		CloseServiceHandle( scm );
  	}
  	return( err );
  }
  
  //===========================================================================================================================
  //	RemoveService
  //===========================================================================================================================
  
  static OSStatus	RemoveService( LPCTSTR inName )
  {
  	OSStatus			err;
  	SC_HANDLE			scm;
  	SC_HANDLE			service;
  	BOOL				ok;
  	SERVICE_STATUS		status;
  	
  	scm		= NULL;
  	service = NULL;
  	
  	// Open a connection to the service.
  	
  	scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS );
  	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
  	require_noerr( err, exit );
  	
  	service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE );
  	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
  	require_noerr( err, exit );
  	
  	// Stop the service, if it is not already stopped, then delete it.
  	
  	ok = QueryServiceStatus( service, &status );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
  	require_noerr( err, exit );
  	
  	if( status.dwCurrentState != SERVICE_STOPPED )
  	{
  		ok = ControlService( service, SERVICE_CONTROL_STOP, &status );
  		check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
  	}
  	
  	ok = DeleteService( service );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr );
  	require_noerr( err, exit );
  		
  	ReportStatus( EVENTLOG_SUCCESS, "Removed service\n" );
  	err = ERROR_SUCCESS;
  	
  exit:
  	if( service )
  	{
  		CloseServiceHandle( service );
  	}
  	if( scm )
  	{
  		CloseServiceHandle( scm );
  	}
  	return( err );
  }
  
  
  
  //===========================================================================================================================
  //	SetServiceParameters
  //===========================================================================================================================
  
  static OSStatus SetServiceParameters()
  {
  	DWORD 			value;
  	DWORD			valueLen = sizeof(DWORD);
  	DWORD			type;
  	OSStatus		err;
  	HKEY			key;
  
  	key = NULL;
  
  	//
  	// Add/Open Parameters section under service entry in registry
  	//
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
  	require_noerr( err, exit );
  	
  	//
  	// If the value isn't already there, then we create it
  	//
  	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
  
  	if (err != ERROR_SUCCESS)
  	{
  		value = 1;
  
  		err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) );
  		require_noerr( err, exit );
  	}
  
  exit:
  
  	if ( key )
  	{
  		RegCloseKey( key );
  	}
  
  	return( err );
  }
  
  
  
  //===========================================================================================================================
  //	GetServiceParameters
  //===========================================================================================================================
  
  static OSStatus GetServiceParameters()
  {
  	DWORD 			value;
  	DWORD			valueLen;
  	DWORD			type;
  	OSStatus		err;
  	HKEY			key;
  
  	key = NULL;
  
  	//
  	// Add/Open Parameters section under service entry in registry
  	//
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
  	require_noerr( err, exit );
  	
  	valueLen = sizeof(DWORD);
  	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
  	if (err == ERROR_SUCCESS)
  	{
  		gServiceManageLLRouting = (value) ? true : false;
  	}
  
  	valueLen = sizeof(DWORD);
  	err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen);
  	if (err == ERROR_SUCCESS)
  	{
  		gServiceCacheEntryCount = value;
  	}
  
  exit:
  
  	if ( key )
  	{
  		RegCloseKey( key );
  	}
  
  	return( err );
  }
  
  
  //===========================================================================================================================
  //	CheckFirewall
  //===========================================================================================================================
  
  static OSStatus CheckFirewall()
  {
  	DWORD 					value;
  	DWORD					valueLen;
  	DWORD					type;
  	ENUM_SERVICE_STATUS	*	lpService = NULL;
  	SC_HANDLE				sc = NULL;
  	HKEY					key = NULL;
  	BOOL					ok;
  	DWORD					bytesNeeded = 0;
  	DWORD					srvCount;
  	DWORD					resumeHandle = 0;
  	DWORD					srvType;
  	DWORD					srvState;
  	DWORD					dwBytes = 0;
  	DWORD					i;
  	BOOL					isRunning = FALSE;
  	OSStatus				err = kUnknownErr;
  	
  	// Check to see if the firewall service is running.  If it isn't, then
  	// we want to return immediately
  
  	sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
  	err = translate_errno( sc, GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	srvType		=	SERVICE_WIN32;
  	srvState	=	SERVICE_STATE_ALL;
  
  	for ( ;; )
  	{
  		// Call EnumServicesStatus using the handle returned by OpenSCManager
  
  		ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
  
  		if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
  		{
  			break;
  		}
  
  		if ( lpService )
  		{
  			free( lpService );
  		}
  
  		dwBytes = bytesNeeded;
  
  		lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
  		require_action( lpService, exit, err = mStatus_NoMemoryErr );
  	}
  
  	err = translate_errno( ok, GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	for ( i = 0; i < srvCount; i++ )
  	{
  		if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 )
  		{
  			if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING )
  			{
  				isRunning = TRUE;
  			}
  
  			break;
  		}
  	}
  
  	require_action( isRunning, exit, err = kUnknownErr );
  
  	// Check to see if we've managed the firewall.
  	// This package might have been installed, then
  	// the OS was upgraded to SP2 or above.  If that's
  	// the case, then we need to manipulate the firewall
  	// so networking works correctly.
  
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
  	require_noerr( err, exit );
  
  	valueLen = sizeof(DWORD);
  	err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen);
  	
  	if ((err != ERROR_SUCCESS) || (value == 0))
  	{
  		wchar_t	fullPath[ MAX_PATH ];
  		DWORD	size;
  
  		// Get a full path to the executable
  
  		size = GetModuleFileNameW( NULL, fullPath, MAX_PATH );
  		err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
  		require_noerr( err, exit );
  
  		err = mDNSAddToFirewall(fullPath, kServiceFirewallName);
  		require_noerr( err, exit );
  
  		value = 1;
  		err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) );
  		require_noerr( err, exit );
  	}
  	
  exit:
  
  	if ( key )
  	{
  		RegCloseKey( key );
  	}
  	
  	if ( lpService )
  	{
  		free( lpService );
  	}
  
  	if ( sc )
  	{
  		CloseServiceHandle ( sc );
  	}
  
  	return( err );
  }
  
  
  
  //===========================================================================================================================
  //	SetServiceInfo
  //===========================================================================================================================
  
  static OSStatus	SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription )
  {
  	OSStatus				err;
  	SC_LOCK					lock;
  	SC_HANDLE				service;
  	SERVICE_DESCRIPTION		description;
  	SERVICE_FAILURE_ACTIONS	actions;
  	SC_ACTION				action;
  	BOOL					ok;
  	
  	check( inServiceName );
  	check( inDescription );
  	
  	lock 	= NULL;
  	service	= NULL;
  	
  	// Open the database (if not provided) and lock it to prevent other access while re-configuring.
  	
  	if( !inSCM )
  	{
  		inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  		err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr );
  		require_noerr( err, exit );
  	}
  	
  	lock = LockServiceDatabase( inSCM );
  	err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr );
  	require_noerr( err, exit );
  	
  	// Open a handle to the service. 
  
  	service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START );
  	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
  	require_noerr( err, exit );
  	
  	// Change the description.
  	
  	description.lpDescription = (LPTSTR) inDescription;
  	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
  	require_noerr( err, exit );
  	
  	actions.dwResetPeriod	=	INFINITE;
  	actions.lpRebootMsg		=	NULL;
  	actions.lpCommand		=	NULL;
  	actions.cActions		=	1;
  	actions.lpsaActions		=	&action;
  	action.Delay			=	500;
  	action.Type				=	SC_ACTION_RESTART;
  
  	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
  	require_noerr( err, exit );
  	
  	err = ERROR_SUCCESS;
  	
  exit:
  	// Close the service and release the lock.
  	
  	if( service )
  	{
  		CloseServiceHandle( service );
  	}
  	if( lock )
  	{
  		UnlockServiceDatabase( lock ); 
  	}
  	return( err );
  }
  
  //===========================================================================================================================
  //	ReportStatus
  //===========================================================================================================================
  
  static void	ReportStatus( int inType, const char *inFormat, ... )
  {
  	if( !gServiceQuietMode )
  	{
  		va_list		args;
  		
  		va_start( args, inFormat );
  		if( gServiceEventSource )
  		{
  			char				s[ 1024 ];
  			BOOL				ok;
  			const char *		array[ 1 ];
  			
  			vsprintf( s, inFormat, args );
  			array[ 0 ] = s;
  			ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL );
  			check_translated_errno( ok, GetLastError(), kUnknownErr );
  		}
  		else
  		{
  			int		n;
  			
  			n = vfprintf( stderr, inFormat, args );
  			check( n >= 0 );
  		}
  		va_end( args );
  	}
  }
  
  //===========================================================================================================================
  //	RunDirect
  //===========================================================================================================================
  
  int	RunDirect( int argc, LPTSTR argv[] )
  {
  	OSStatus		err;
  	BOOL			initialized;
     BOOL        ok;
  	
  	initialized = FALSE;
  
  	err = SetupServiceEvents();
  	require_noerr( err, exit );
  
  	// Install a Console Control Handler to handle things like control-c signals.
  	
  	ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
  	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	
  	err = ServiceSpecificInitialize( argc, argv );
  	require_noerr( err, exit );
  	initialized = TRUE;
  	
  	// Run the service. This does not return until the service quits or is stopped.
  	
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Running service directly\n" );
  	
  	err = ServiceSpecificRun( argc, argv );
  	require_noerr( err, exit );
  	
  	// Clean up.
  	
  exit:
  	if( initialized )
  	{
  		ServiceSpecificFinalize( argc, argv );
  	}
  
  	TearDownServiceEvents();
  
  	return( err );
  }
  
  #if 0
  #pragma mark -
  #endif
  
  //===========================================================================================================================
  //	ServiceMain
  //===========================================================================================================================
  
  static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] )
  {
  	OSStatus		err;
  	BOOL			ok;
  
  	err = SetupServiceEvents();
  	require_noerr( err, exit );
  	
  	err = ServiceSetupEventLogging();
  	check_noerr( err );
  
  	err = GetServiceParameters();
  	check_noerr( err );
  	
  	// Initialize the service status and register the service control handler with the name of the service.
  	
  	gServiceStatus.dwServiceType 				= SERVICE_WIN32_SHARE_PROCESS;
  	gServiceStatus.dwCurrentState 				= 0;
  	gServiceStatus.dwControlsAccepted 			= SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_POWEREVENT;
  	gServiceStatus.dwWin32ExitCode 				= NO_ERROR;
  	gServiceStatus.dwServiceSpecificExitCode 	= NO_ERROR;
  	gServiceStatus.dwCheckPoint 				= 0;
  	gServiceStatus.dwWaitHint 					= 0;
  	
  	gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL );
  	err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr );
  	require_noerr( err, exit );
  	
  	// Mark the service as starting.
  
  	gServiceStatus.dwCurrentState 	= SERVICE_START_PENDING;
  	gServiceStatus.dwCheckPoint	 	= 0;
  	gServiceStatus.dwWaitHint 		= 5000;	// 5 seconds
  	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
  	check_translated_errno( ok, GetLastError(), kParamErr );
  	
  	// Run the service. This does not return until the service quits or is stopped.
  	
  	err = ServiceRun( (int) argc, argv );
  	if( err != kNoErr )
  	{
  		gServiceStatus.dwWin32ExitCode				= ERROR_SERVICE_SPECIFIC_ERROR;
  		gServiceStatus.dwServiceSpecificExitCode 	= (DWORD) err;
  	}
  	
  	// Service-specific work is done so mark the service as stopped.
  	
  	gServiceStatus.dwCurrentState = SERVICE_STOPPED;
  	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
  	check_translated_errno( ok, GetLastError(), kParamErr );
  	
  	// Note: The service status handle should not be closed according to Microsoft documentation.
  	
  exit:
  
  	if( gServiceEventSource )
  	{
  		ok = DeregisterEventSource( gServiceEventSource );
  		check_translated_errno( ok, GetLastError(), kUnknownErr );
  		gServiceEventSource = NULL;
  	}
  
  	TearDownServiceEvents();
  }
  
  //===========================================================================================================================
  //	ServiceSetupEventLogging
  //===========================================================================================================================
  
  static OSStatus	ServiceSetupEventLogging( void )
  {
  	OSStatus			err;
  	HKEY				key;
  	LPCTSTR				s;
  	DWORD				typesSupported;
  	TCHAR				path[ MAX_PATH ];
  	DWORD 				n;
  	
  	key = NULL;
  	
  	// Add/Open source name as a sub-key under the Application key in the EventLog registry key.
  
  	s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName;
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key );
  	require_noerr( err, exit );
  	
  	// Add the name to the EventMessageFile subkey.
  
  	path[ 0 ] = '\0';
  	GetModuleFileName( NULL, path, MAX_PATH );
  	n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) );
  	err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
  	require_noerr( err, exit );
  	
  	// Set the supported event types in the TypesSupported subkey.
  	
  	typesSupported = 0 
  					 | EVENTLOG_SUCCESS
  					 | EVENTLOG_ERROR_TYPE
  					 | EVENTLOG_WARNING_TYPE
  					 | EVENTLOG_INFORMATION_TYPE
  					 | EVENTLOG_AUDIT_SUCCESS
  					 | EVENTLOG_AUDIT_FAILURE; 
  	err = RegSetValueEx( key, TEXT("TypesSupported"), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
  	require_noerr( err, exit );
  	
  	// Set up the event source.
  	
  	gServiceEventSource = RegisterEventSource( NULL, kServiceName );
  	err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr );
  	require_noerr( err, exit );
  		
  exit:
  	if( key )
  	{
  		RegCloseKey( key );
  	}
  	return( err );
  }
  
  
  //===========================================================================================================================
  //	ServiceControlHandler
  //===========================================================================================================================
  
  static DWORD WINAPI	ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext )
  {
  	BOOL		setStatus;
  	OSStatus	err;
  	BOOL		ok;
  
  	DEBUG_UNUSED( inEventData );
  	DEBUG_UNUSED( inContext );
  	
  	setStatus = TRUE;
  	switch( inControl )
  	{
  		case SERVICE_CONTROL_STOP:
  		case SERVICE_CONTROL_SHUTDOWN:
  			
  			dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP|SERVICE_CONTROL_SHUTDOWN\n" );
  			
  			ServiceStop();
  			setStatus = FALSE;
  			break;
  		
  		case SERVICE_CONTROL_POWEREVENT:
  
  			if (inEventType == PBT_APMSUSPEND)
  			{
  				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMSUSPEND\n" );
  
  				if ( gPowerSuspendEvent )
  				{
  					ok = SetEvent( gPowerSuspendEvent );
  					err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
  					check_noerr( err );
  
  					switch ( WaitForSingleObject( gPowerSuspendAckEvent, 5 * 1000 ) )
  					{
  						case WAIT_OBJECT_0:
  						{
  							// No error
  						}
  						break;
  
  						case WAIT_TIMEOUT:
  						{
  							dlog( kDebugLevelError, DEBUG_NAME "Timed out waiting for acknowledgement of machine sleep\n" );
  							ReportStatus( EVENTLOG_ERROR_TYPE, "Timed out waiting for acknowledgement of machine sleep" );
  						}
  						break;
  
  						default:
  						{
  							dlog( kDebugLevelError, DEBUG_NAME "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
  							ReportStatus( EVENTLOG_ERROR_TYPE, "Error waiting for acknowledgement of machine sleep: %d", GetLastError() );
  						}
  						break;
  					}
  				}
  			}
  			else if (inEventType == PBT_APMRESUMESUSPEND)
  			{
  				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" );
  
  				if ( gPowerResumeEvent )
  				{
  					ok = SetEvent( gPowerResumeEvent );
  					err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
  					check_noerr( err );
  				}
  			}
  		
  			break;
  
  		default:
  			dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl );
  			break;
  	}
  	
  	if( setStatus && gServiceStatusHandle )
  	{
  		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
  		check_translated_errno( ok, GetLastError(), kUnknownErr );
  	}
  
  	return NO_ERROR;
  }
  
  //===========================================================================================================================
  //	ServiceRun
  //===========================================================================================================================
  
  static OSStatus	ServiceRun( int argc, LPTSTR argv[] )
  {
  	OSStatus		err;
  	BOOL			initialized;
  	BOOL			ok;
  	
  	DEBUG_UNUSED( argc );
  	DEBUG_UNUSED( argv );
  	
  	initialized = FALSE;
  	
  	// <rdar://problem/5727548> Make the service as running before we call ServiceSpecificInitialize. We've
  	// had reports that some machines with McAfee firewall installed cause a problem with iTunes installation.
  	// We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a
  	// simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock
  	// any installers that are waiting for our state to change.
  
  	gServiceStatus.dwCurrentState = SERVICE_RUNNING;
  	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
  	check_translated_errno( ok, GetLastError(), kParamErr );
  
  	// Initialize the service-specific stuff
  	
  	while ( 1 )
  	{
  		DWORD ret;
  
  		ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initializing" );
  
  		err = ServiceSpecificInitialize( argc, argv );
  
  		if ( !err )
  		{
  			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialized" );
  			break;
  		}
  
  		ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service initialization failed with err %d. Waiting %d seconds to retry...", err, kWaitToRetry );
  
  		ret = WaitForSingleObject( gStopEvent, 1000 * kWaitToRetry );
  
  		if ( ret == WAIT_OBJECT_0 )
  		{
  			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a stop event" );
  			goto exit;
  		}
  		else if ( ret == WAIT_OBJECT_0 + 1 )
  		{
  			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power suspend event" );
  		}
  		else if ( ret == WAIT_OBJECT_0 + 2 )
  		{
  			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received a power resume event" );
  		}
  		else if ( ret != WAIT_TIMEOUT )
  		{
  			ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service received an error in WaitForSingleObject() : %d, %d", ret, GetLastError() );
  			goto exit;
  		}
  	}
  
  	initialized = TRUE;
  	
  	err = CheckFirewall();
  	check_noerr( err );
  
  	if ( err )
  	{
  		gRetryFirewall = TRUE;
  	}
  	
  	// Run the service-specific stuff. This does not return until the service quits or is stopped.
  
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service started\n" );
  
  	err = ServiceSpecificRun( argc, argv );
  	require_noerr( err, exit );
  
  exit:
  
  	// Service stopped. Clean up and we're done.
  	
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err );
  	
  	if( initialized )
  	{
  		ServiceSpecificFinalize( argc, argv );
  	}
  	
  	return( err );
  }
  
  //===========================================================================================================================
  //	ServiceStop
  //===========================================================================================================================
  
  static void	ServiceStop( void )
  {
  	BOOL			ok;
  	OSStatus		err;
  	
  	// Signal the event to cause the service to exit.
  	
  	if( gServiceStatusHandle )
  	{
  		gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
  		check_translated_errno( ok, GetLastError(), kParamErr );
  	}
  		
  	err = ServiceSpecificStop();
  	check_noerr( err );
  }
  
  
  #if 0
  #pragma mark -
  #pragma mark == Service Specific ==
  #endif
  
  //===========================================================================================================================
  //	ServiceSpecificInitialize
  //===========================================================================================================================
  
  static OSStatus	ServiceSpecificInitialize( int argc, LPTSTR argv[] )
  {
  	OSStatus err;
  	
  	DEBUG_UNUSED( argc );
  	DEBUG_UNUSED( argv );
  	
  	mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord);
  	mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage);
  
  	gPlatformStorage.reportStatusFunc = ReportStatus;
  
  	err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext); 
  	require_noerr( err, exit);
  
  	err = SetupNotifications();
  	check_noerr( err );
  
  	err = udsserver_init(mDNSNULL, 0);
  	require_noerr( err, exit);
  
  	SetLLRoute( &gMDNSRecord );
  
  exit:
  	if( err != kNoErr )
  	{
  		ServiceSpecificFinalize( argc, argv );
  	}
  	return( err );
  }
  
  //===========================================================================================================================
  //	ServiceSpecificRun
  //===========================================================================================================================
  
  static OSStatus	ServiceSpecificRun( int argc, LPTSTR argv[] )
  {
  	mDNSBool done = mDNSfalse;
  	mStatus err = mStatus_NoError;
  	
  	DEBUG_UNUSED( argc );
  	DEBUG_UNUSED( argv );
  
  	err = SetupInterfaceList( &gMDNSRecord );
  	check( !err );
  
  	err = uDNS_SetupDNSConfig( &gMDNSRecord );
  	check( !err );
  
  	while( !done )
  	{
  		static mDNSs32 RepeatedBusy = 0;	
  		mDNSs32 nextTimerEvent;
  		mStatus err;
  
  		// Give the mDNS core a chance to do its work and determine next event time.
  
  		nextTimerEvent = udsserver_idle( mDNS_Execute( &gMDNSRecord ) - mDNS_TimeNow( &gMDNSRecord ) );
  
  		if      ( nextTimerEvent < 0)					nextTimerEvent = 0;
  		else if ( nextTimerEvent > (0x7FFFFFFF / 1000))	nextTimerEvent = 0x7FFFFFFF / mDNSPlatformOneSecond;
  		else											nextTimerEvent = ( nextTimerEvent * 1000) / mDNSPlatformOneSecond;
  
  		// Debugging sanity check, to guard against CPU spins
  			
  		if ( nextTimerEvent > 0 )
  		{
  			RepeatedBusy = 0;
  		}
  		else
  		{
  			nextTimerEvent = 1;
  
  			if ( ++RepeatedBusy >= mDNSPlatformOneSecond )
  			{
  				ShowTaskSchedulingError( &gMDNSRecord );
  				RepeatedBusy = 0;
  			}
  		}
  
  		if ( gMDNSRecord.ShutdownTime )
  		{
  			mDNSs32 now = mDNS_TimeNow( &gMDNSRecord );
  
  			if ( mDNS_ExitNow( &gMDNSRecord, now ) )
  			{
  				mDNS_FinalExit( &gMDNSRecord );
  				done = TRUE;
  				break;
  			}
  
  			if ( nextTimerEvent - gMDNSRecord.ShutdownTime >= 0 )
  			{
  				nextTimerEvent = gMDNSRecord.ShutdownTime;
  			}
  		}
  
  		err = mDNSPoll( nextTimerEvent );
  
  		if ( err )
  		{
  			Sleep( 3 * 1000 );
  				
  			err = SetupInterfaceList( &gMDNSRecord );
  			check( !err );
  
  			err = uDNS_SetupDNSConfig( &gMDNSRecord );
  			check( !err );
  				
  			break;
  		}
  	}
  
  	return ( err );
  }
  
  
  //===========================================================================================================================
  //	ServiceSpecificStop
  //===========================================================================================================================
  
  static OSStatus	ServiceSpecificStop( void )
  {
  	OSStatus    err;
  	BOOL        ok;
  
  	ok = SetEvent(gStopEvent);
  	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  exit:
  
  	return( err );
  }
  
  //===========================================================================================================================
  //	ServiceSpecificFinalize
  //===========================================================================================================================
  
  static void	ServiceSpecificFinalize( int argc, LPTSTR argv[] )
  {
  	DEBUG_UNUSED( argc );
  	DEBUG_UNUSED( argv );
  	
  	//
  	// clean up the notifications
  	//
  	TearDownNotifications();
  
  	//
  	// clean up loaded library
  	//
  
  	if( gIPHelperLibraryInstance )
  	{
  		gGetIpInterfaceEntryFunctionPtr = NULL;
  		
  		FreeLibrary( gIPHelperLibraryInstance );
  		gIPHelperLibraryInstance = NULL;
  	}
  }
  
  
  //===========================================================================================================================
  //	SetupServiceEvents
  //===========================================================================================================================
  
  mDNSlocal mStatus	SetupServiceEvents()
  {
  	mStatus err;
  	
  	// Stop Event
  
  	gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gStopEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  exit:
  
  	if ( err )
  	{
  		TearDownServiceEvents();
  	}
  
  	return err;
  }
  
  
  //===========================================================================================================================
  //	TearDownServiceNotifications
  //===========================================================================================================================
  
  mDNSlocal mStatus	TearDownServiceEvents()
  {
  	if ( gStopEvent )
  	{
  		CloseHandle( gStopEvent );
  		gStopEvent = NULL;
  	}
  
  	return mStatus_NoError;
  }
  
  
  //===========================================================================================================================
  //	SetupNotifications
  //===========================================================================================================================
  
  mDNSlocal mStatus	SetupNotifications()
  {
  	mStatus				err;
  	SocketRef			sock;
  	unsigned long		param;
  	int					inBuffer;
  	int					outBuffer;
  	DWORD				outSize;
  
  	require_action( gStopEvent, exit, err = kUnknownErr );
  	err = mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL );
  	require_noerr( err, exit );
  
  	// Power Suspend
  
  	gPowerSuspendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gPowerSuspendEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gPowerSuspendEvent, PowerSuspendNotification, NULL );
  	require_noerr( err, exit );
  
  	// Power Suspend Ack
  
  	gPowerSuspendAckEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  	err = translate_errno( gPowerSuspendAckEvent, ( mStatus ) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	// Power Resume
  
  	gPowerResumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gPowerResumeEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gPowerResumeEvent, PowerResumeNotification, NULL );
  	require_noerr( err, exit );
  
  	// Register to listen for address list changes.
  	
  	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
  	err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
  	require_noerr( err, exit );
  	gInterfaceListChangedSocket = sock;
  	
  	// Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event 
  	// when a change to the interface list is detected.
  	
  	param = 1;
  	err = ioctlsocket( sock, FIONBIO, &param );
  	err = translate_errno( err == 0, errno_compat(), kUnknownErr );
  	require_noerr( err, exit );
  	
  	inBuffer	= 0;
  	outBuffer	= 0;
  	err = WSAIoctl( sock, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
  	if( err < 0 )
  	{
  		check( errno_compat() == WSAEWOULDBLOCK );
  	}
  
  	err = mDNSPollRegisterSocket( sock, FD_ADDRESS_LIST_CHANGE, InterfaceListNotification, NULL );
  	require_noerr( err, exit );
  	
  	gDescChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  	err = translate_errno( gDescChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"), 0, KEY_READ, &gDescKey);
  	check_translated_errno( err == 0, errno_compat(), kNameErr );
  
  	if ( gDescKey != NULL )
  	{
  		err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
  		require_noerr( err, exit );
  	}
  
  	err = mDNSPollRegisterEvent( gDescChangedEvent, ComputerDescriptionNotification, NULL );
  	require_noerr( err, exit );
  
  	// This will catch all changes to tcp/ip networking, including changes to the domain search list
  
  	gTcpipChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gTcpipChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &gTcpipKey );
  	require_noerr( err, exit );
  	err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE);
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gTcpipChangedEvent, TCPChangedNotification, NULL );
  	require_noerr( err, exit );
  
  	// This will catch all changes to ddns configuration
  
  	gDdnsChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gDdnsChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &gDdnsKey );
  	require_noerr( err, exit );
  	err = RegNotifyChangeKeyValue( gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gDdnsChangedEvent, DDNSChangedNotification, NULL );
  	require_noerr( err, exit );
  
  	// This will catch all changes to file sharing
  
  	gFileSharingChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  	err = translate_errno( gFileSharingChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\Shares"), &gFileSharingKey );
  	
  	// Just to make sure that initialization doesn't fail on some old OS
  	// that doesn't have this key, we'll only add the notification if
  	// the key exists.
  
  	if ( !err )
  	{
  		err = RegNotifyChangeKeyValue( gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
  		require_noerr( err, exit );
  		err = mDNSPollRegisterEvent( gFileSharingChangedEvent, FileSharingChangedNotification, NULL );
  		require_noerr( err, exit );
  	}
  	else
  	{
  		err = mStatus_NoError;
  	}
  
  	// This will catch changes to the Windows firewall
  
  	gFirewallChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  	err = translate_errno( gFirewallChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  
  	// Just to make sure that initialization doesn't fail on some old OS
  	// that doesn't have this key, we'll only add the notification if
  	// the key exists.
  
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"), &gFirewallKey );
  	
  	if ( !err )
  	{
  		err = RegNotifyChangeKeyValue( gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
  		require_noerr( err, exit );
  		err = mDNSPollRegisterEvent( gFirewallChangedEvent, FirewallChangedNotification, NULL );
  		require_noerr( err, exit );
  	}
  	else
  	{
  		err = mStatus_NoError;
  	}
  
  	// This will catch all changes to advertised services configuration
  
  	gAdvertisedServicesChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  	err = translate_errno( gAdvertisedServicesChangedEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\Services"), &gAdvertisedServicesKey );
  	require_noerr( err, exit );
  	err = RegNotifyChangeKeyValue( gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gAdvertisedServicesChangedEvent, AdvertisedServicesChangedNotification, NULL );
  	require_noerr( err, exit );
  
  	// SPSWakeup timer
  
  	gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL );
  	err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gSPSWakeupEvent, SPSWakeupNotification, NULL );
  	require_noerr( err, exit );
  
  	// SPSSleep timer
  
  	gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL );
  	err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr );
  	require_noerr( err, exit );
  	err = mDNSPollRegisterEvent( gSPSSleepEvent, SPSSleepNotification, NULL );
  	require_noerr( err, exit );
  
  exit:
  	if( err )
  	{
  		TearDownNotifications();
  	}
  	return( err );
  }
  
  //===========================================================================================================================
  //	TearDownNotifications
  //===========================================================================================================================
  
  mDNSlocal mStatus	TearDownNotifications()
  {
  	if( IsValidSocket( gInterfaceListChangedSocket ) )
  	{
  		mDNSPollUnregisterSocket( gInterfaceListChangedSocket );
  
  		close_compat( gInterfaceListChangedSocket );
  		gInterfaceListChangedSocket = kInvalidSocketRef;
  	}
  
  	if ( gDescChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gDescChangedEvent );
  		CloseHandle( gDescChangedEvent );
  		gDescChangedEvent = NULL;
  	}
  
  	if ( gDescKey != NULL )
  	{
  		RegCloseKey( gDescKey );
  		gDescKey = NULL;
  	}
  
  	if ( gTcpipChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gTcpipChangedEvent );
  		CloseHandle( gTcpipChangedEvent );
  		gTcpipChangedEvent = NULL;
  	}
  
  	if ( gDdnsChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gDdnsChangedEvent );
  		CloseHandle( gDdnsChangedEvent );
  		gDdnsChangedEvent = NULL;
  	}
  
  	if ( gDdnsKey != NULL )
  	{
  		RegCloseKey( gDdnsKey );
  		gDdnsKey = NULL;
  	}
  
  	if ( gFileSharingChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gFileSharingChangedEvent );
  		CloseHandle( gFileSharingChangedEvent );
  		gFileSharingChangedEvent = NULL;
  	}
  
  	if ( gFileSharingKey != NULL )
  	{
  		RegCloseKey( gFileSharingKey );
  		gFileSharingKey = NULL;
  	}
  
  	if ( gFirewallChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gFirewallChangedEvent );
  		CloseHandle( gFirewallChangedEvent );
  		gFirewallChangedEvent = NULL;
  	}
  
  	if ( gFirewallKey != NULL )
  	{
  		RegCloseKey( gFirewallKey );
  		gFirewallKey = NULL;
  	}
  
  	if ( gAdvertisedServicesChangedEvent != NULL )
  	{
  		mDNSPollUnregisterEvent( gAdvertisedServicesChangedEvent );
  		CloseHandle( gAdvertisedServicesChangedEvent );
  		gAdvertisedServicesChangedEvent = NULL;
  	}
  
  	if ( gAdvertisedServicesKey != NULL )
  	{
  		RegCloseKey( gAdvertisedServicesKey );
  		gAdvertisedServicesKey = NULL;
  	}
  
  	if ( gSPSWakeupEvent )
  	{
  		mDNSPollUnregisterEvent( gSPSWakeupEvent );
  		CloseHandle( gSPSWakeupEvent );
  		gSPSWakeupEvent = NULL;
  	}
  
  	if ( gSPSSleepEvent )
  	{
  		mDNSPollUnregisterEvent( gSPSSleepEvent );
  		CloseHandle( gSPSSleepEvent );
  		gSPSSleepEvent = NULL;
  	}
  
  	if ( gPowerResumeEvent )
  	{
  		mDNSPollUnregisterEvent( gPowerResumeEvent );
  		CloseHandle( gPowerResumeEvent );
  		gPowerResumeEvent = NULL;
  	}
  
  	if ( gPowerSuspendAckEvent )
  	{
  		CloseHandle( gPowerSuspendAckEvent );
  		gPowerSuspendAckEvent = NULL;
  	}
  
  	if ( gPowerSuspendEvent )
  	{
  		mDNSPollUnregisterEvent( gPowerSuspendEvent );
  		CloseHandle( gPowerSuspendEvent );
  		gPowerSuspendEvent = NULL;
  	}
  
  	if ( gStopEvent )
  	{
  		mDNSPollUnregisterEvent( gStopEvent );
  	}
  
  	return( mStatus_NoError );
  }
  
  
  mDNSlocal void CALLBACK
  StopNotification( HANDLE event, void *context )
  {
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" );
  	udsserver_exit();
  	mDNS_StartExit( &gMDNSRecord );
  }
  
  
  mDNSlocal void CALLBACK
  PowerSuspendNotification( HANDLE event, void * context )
  {
  	LARGE_INTEGER	timeout;
  	BOOL			ok;
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	dlog( kDebugLevelInfo, DEBUG_NAME "PowerSuspendNotification\n" );
  
  	gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout );
  				
  	if ( gMDNSRecord.SystemWakeOnLANEnabled )
  	{
  		ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE );
  		check( ok );
  	}
  
  	mDNSCoreMachineSleep(&gMDNSRecord, TRUE);
  
  	ok = SetEvent( gPowerSuspendAckEvent );
  
  	if ( !ok )
  	{
  		dlog( kDebugLevelError, DEBUG_NAME "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
  		ReportStatus( EVENTLOG_ERROR_TYPE, "PowerSuspendNotification: error while setting acknowledgement: %d", GetLastError() );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  PowerResumeNotification( HANDLE event, void * context )
  {
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	dlog( kDebugLevelInfo, DEBUG_NAME "PowerResumeNotification\n" );
  
  	if ( gSPSWakeupEvent )
  	{
  		CancelWaitableTimer( gSPSWakeupEvent );
  	}
  
  	if ( gSPSSleepEvent )
  	{
  		CancelWaitableTimer( gSPSSleepEvent );
  	}
  
  	mDNSCoreMachineSleep(&gMDNSRecord, FALSE);
  }
  
  
  
  mDNSlocal void CALLBACK
  InterfaceListNotification( SOCKET socket, LPWSANETWORKEVENTS event, void *context )
  {
  	int		inBuffer;
  	int		outBuffer;
  	DWORD	outSize;
  	int		err;
  
  	DEBUG_UNUSED( socket );
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	// It would be nice to come up with a more elegant solution to this, but it seems that
  	// GetAdaptersAddresses doesn't always stay in sync after network changed events.  So as
  	// as a simple workaround, we'll pause for a couple of seconds before processing the change.
  
  	// We arrived at 2 secs by trial and error. We could reproduce the problem after sleeping
  	// for 500 msec and 750 msec, but couldn't after sleeping for 1 sec.  We added another
  	// second on top of that to account for machine load or some other exigency.
  
  	Sleep( 2000 );
  
  	// Interface list changed event. Break out of the inner loop to re-setup the wait list.
  					
  	InterfaceListDidChange( &gMDNSRecord );
  
  	// reset the event handler
  	inBuffer	= 0;
  	outBuffer	= 0;
  	err = WSAIoctl( gInterfaceListChangedSocket, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
  	if( err < 0 )
  	{
  		check( errno_compat() == WSAEWOULDBLOCK );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  ComputerDescriptionNotification( HANDLE event, void *context )
  {
  	// The computer description might have changed
  					
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	ComputerDescriptionDidChange( &gMDNSRecord );
  	udsserver_handle_configchange( &gMDNSRecord );
  
  	// and reset the event handler
  	if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) )
  	{
  		int err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  TCPChangedNotification( HANDLE event, void *context )
  {
  	// The TCP/IP might have changed
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	TCPIPConfigDidChange( &gMDNSRecord );
  	udsserver_handle_configchange( &gMDNSRecord );
  
  	// and reset the event handler
  
  	if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) )
  	{
  		int err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE );
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  DDNSChangedNotification( HANDLE event, void *context )
  {
  	// The DynDNS config might have changed
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	DynDNSConfigDidChange( &gMDNSRecord );
  	udsserver_handle_configchange( &gMDNSRecord );
  
  	// and reset the event handler
  
  	if ((gDdnsKey != NULL) && (gDdnsChangedEvent))
  	{
  		int err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  FileSharingChangedNotification( HANDLE event, void *context )
  {
  	// File sharing changed
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	FileSharingDidChange( &gMDNSRecord );
  
  	// and reset the event handler
  
  	if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent))
  	{
  		int err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  FirewallChangedNotification( HANDLE event, void *context )
  {
  	// Firewall configuration changed
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	FirewallDidChange( &gMDNSRecord );
  
  	// and reset the event handler
  
  	if ((gFirewallKey != NULL) && (gFirewallChangedEvent))
  	{
  		int err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  AdvertisedServicesChangedNotification( HANDLE event, void *context )
  {
  	// Ultimately we'll want to manage multiple services, but right now the only service
  	// we'll be managing is SMB.
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	FileSharingDidChange( &gMDNSRecord );
  
  	// and reset the event handler
  
  	if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) )
  	{
  		int err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
  		check_noerr( err );
  	}
  }
  
  
  mDNSlocal void CALLBACK
  SPSWakeupNotification( HANDLE event, void *context )
  {
  	LARGE_INTEGER timeout;
  
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" );
  
  	timeout.QuadPart  = kSPSMaintenanceWakePeriod;
  	timeout.QuadPart *= kSecondsTo100NSUnits;
  
  	SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE );
  }
  
  
  mDNSlocal void CALLBACK
  SPSSleepNotification( HANDLE event, void *context )
  {
  	DEBUG_UNUSED( event );
  	DEBUG_UNUSED( context );
  
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Returning to sleep after maintenance wake" );
  
  	// Calling SetSuspendState() doesn't invoke our sleep handlers, so we'll
  	// call HandlePowerSuspend() explicity.  This will reset the 
  	// maintenance wake timers.
  
  	PowerSuspendNotification( gPowerSuspendEvent, NULL );
  	SetSuspendState( FALSE, FALSE, FALSE );
  }
  
  
  //===========================================================================================================================
  //	CoreCallback
  //===========================================================================================================================
  
  static void
  CoreCallback(mDNS * const inMDNS, mStatus status)
  {
  	if (status == mStatus_ConfigChanged)
  	{
  		SetLLRoute( inMDNS );
  	}
  }
  
  
  //===========================================================================================================================
  //	UDSAcceptNotification
  //===========================================================================================================================
  
  mDNSlocal void CALLBACK
  UDSAcceptNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
  {
  	( void ) sock;
  	( void ) event;
  	( void ) context;
  	
  	if ( gUDSCallback )
  	{
  		gUDSCallback( ( int ) gUDSSocket, 0, context );
  	}
  }
  
  
  //===========================================================================================================================
  //	UDSReadNotification
  //===========================================================================================================================
  
  mDNSlocal void CALLBACK
  UDSReadNotification( SOCKET sock, LPWSANETWORKEVENTS event, void *context )
  {
  	TCPSocket *tcpSock = ( TCPSocket* ) context;
  
  	( void ) sock;
  	( void ) event;
  
  	if ( tcpSock )
  	{
  		tcpSock->userCallback( ( int ) tcpSock->fd, 0, tcpSock->userContext );
  	}
  }
  
  
  //===========================================================================================================================
  //	udsSupportAddFDToEventLoop
  //===========================================================================================================================
  
  mStatus
  udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
  {
  	mStatus err = mStatus_NoError;
  
  	// We are using some knowledge of what is being passed to us here.  If the fd is a listen socket,
  	// then the "context" parameter is NULL.  If it is an actual read/write socket, then the "context"
  	// parameter is not null.
  
  	if ( context )
  	{
  		TCPSocket * sock;
  
  		sock = malloc( sizeof( TCPSocket ) );
  		require_action( sock, exit, err = mStatus_NoMemoryErr );
  		mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
  
  		sock->fd				= (SOCKET) fd;
  		sock->userCallback		= callback;
  		sock->userContext		= context;
  		sock->m					= &gMDNSRecord;
  
  		*platform_data = sock;
  
  		err = mDNSPollRegisterSocket( sock->fd, FD_READ | FD_CLOSE, UDSReadNotification, sock );
  		require_noerr( err, exit );
  	}
  	else
  	{
  		gUDSSocket		= fd;
  		gUDSCallback	= callback;
  
  		err = mDNSPollRegisterSocket( gUDSSocket, FD_ACCEPT | FD_CLOSE, UDSAcceptNotification, NULL );
  		require_noerr( err, exit );
  	}
  
  exit:
  
  	return err;
  }
  
  
  int
  udsSupportReadFD( SocketRef fd, char *buf, int len, int flags, void *platform_data )
  {
  	TCPSocket	*	sock;
  	mDNSBool		closed;
  	int				ret;
  
  	( void ) flags;
  
  	sock = ( TCPSocket* ) platform_data;
  	require_action( sock, exit, ret = -1 );
  	require_action( sock->fd == fd, exit, ret = -1 );
  
  	ret = mDNSPlatformReadTCP( sock, buf, len, &closed );
  
  	if ( closed )
  	{
  		ret = 0;
  	}
  	else if ( !ret && ( WSAGetLastError() == WSAEWOULDBLOCK ) )
  	{
  		// mDNSPlatformReadTCP will return 0 if it gets WSAEWOULDBLOCK, but
  		// that caller of this routine interprets that as close connection.
  		// We'll fix that by returning -1 in that case.
  
  		ret = -1;
  	}
  
  exit:
  
  	return ret;
  }
  
  
  mStatus
  udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data)		// Note: This also CLOSES the socket
  {
  	mStatus err = kNoErr;
  
  	mDNSPollUnregisterSocket( fd );
  
  	if ( platform_data != NULL )
  	{
  		TCPSocket * sock;
  
  		dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" );
  		sock = ( TCPSocket* ) platform_data;
  		check( sock->fd == fd );
  		mDNSPlatformTCPCloseConnection( sock );
  	}
  
  	return err;
  }
  
  
  mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
  	{
  	(void)m;
  	(void)delay;
  	// No-op, for now
  	}
  
  
  //===========================================================================================================================
  //	SystemWakeForNetworkAccess
  //===========================================================================================================================
  
  mDNSu8
  SystemWakeForNetworkAccess( LARGE_INTEGER * timeout )
  {
  	HKEY					key = NULL;
  	DWORD					dwSize;
  	DWORD					enabled;
  	mDNSu8					ok;
  	SYSTEM_POWER_STATUS		powerStatus;
  	time_t					startTime;
  	time_t					nextWakeupTime;
  	int						delta;
  	DWORD					err;
  
  	dlog( kDebugLevelInfo, DEBUG_NAME "SystemWakeForNetworkAccess\n" );
  
  	// Make sure we have a timer
  
  	require_action( gSPSWakeupEvent != NULL, exit, ok = FALSE );
  	require_action( gSPSSleepEvent != NULL, exit, ok = FALSE );
  
  	// Make sure the user enabled bonjour sleep proxy client 
  	
  	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", &key );
  	require_action( !err, exit, ok = FALSE );
  	dwSize = sizeof( DWORD );
  	err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
  	require_action( !err, exit, ok = FALSE );
  	require_action( enabled, exit, ok = FALSE );
  	
  	// Make sure machine is on AC power
  	
  	ok = ( mDNSu8 ) GetSystemPowerStatus( &powerStatus );
  	require_action( ok, exit, ok = FALSE );
  	require_action( powerStatus.ACLineStatus == AC_LINE_ONLINE, exit, ok = FALSE );
  
  	// Now make sure we have a network interface that does wake-on-lan
  
  	ok = ( mDNSu8 ) IsWOMPEnabled( &gMDNSRecord );
  	require_action( ok, exit, ok = FALSE );
  
  	// Now make sure we have advertised services. Doesn't make sense to
  	// enable sleep proxy if we have no multicast services that could
  	// potentially wake us up.
  
  	ok = ( mDNSu8 ) mDNSCoreHaveAdvertisedMulticastServices( &gMDNSRecord );
  	require_action( ok, exit, ok = FALSE );
  
  	// Calculate next wake up time
  
  	startTime		= time( NULL );					// Seconds since midnight January 1, 1970
  	nextWakeupTime	= startTime + ( 120 * 60 );		// 2 hours later
  	
  	if ( gMDNSRecord.p->nextDHCPLeaseExpires < nextWakeupTime )
  	{
  		nextWakeupTime = gMDNSRecord.p->nextDHCPLeaseExpires;
  	}
  
  	// Finally calculate the next relative wakeup time
  
  	delta = ( int )( ( ( double )( nextWakeupTime - startTime ) ) * 0.9 );
  	ReportStatus( EVENTLOG_INFORMATION_TYPE, "enabling sleep proxy client with next maintenance wake in %d seconds", delta );
  
  	// Convert seconds to 100 nanosecond units expected by SetWaitableTimer
  
  	timeout->QuadPart  = -delta;
  	timeout->QuadPart *= kSecondsTo100NSUnits;
  
  	ok = TRUE;
  
  exit:
  
  	if ( key )
  	{
  		RegCloseKey( key );
  	}
  
  	return ok;
  }
  
  
  //===========================================================================================================================
  //	HaveRoute
  //===========================================================================================================================
  
  static bool
  HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric )
  {
  	PMIB_IPFORWARDTABLE	pIpForwardTable	= NULL;
  	DWORD				dwSize			= 0;
  	BOOL				bOrder			= FALSE;
  	OSStatus			err;
  	bool				found			= false;
  	unsigned long int	i;
  
  	//
  	// Find out how big our buffer needs to be.
  	//
  	err = GetIpForwardTable(NULL, &dwSize, bOrder);
  	require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
  
  	//
  	// Allocate the memory for the table
  	//
  	pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
  	require_action( pIpForwardTable, exit, err = kNoMemoryErr );
    
  	//
  	// Now get the table.
  	//
  	err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
  	require_noerr( err, exit );
  
  	//
  	// Search for the row in the table we want.
  	//
  	for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
  	{
  		if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) )
  		{
  			memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) );
  			found = true;
  			break;
  		}
  	}
  
  exit:
  
  	if ( pIpForwardTable != NULL ) 
  	{
  		free(pIpForwardTable);
  	}
      
  	return found;
  }
  
  
  //===========================================================================================================================
  //	IsValidAddress
  //===========================================================================================================================
  
  static bool
  IsValidAddress( const char * addr )
  {
  	return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false;
  }	
  
  
  //===========================================================================================================================
  //	GetAdditionalMetric
  //===========================================================================================================================
  
  static ULONG
  GetAdditionalMetric( DWORD ifIndex )
  {
  	ULONG metric = 0;
  
  	if( !gIPHelperLibraryInstance )
  	{
  		gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
  
  		gGetIpInterfaceEntryFunctionPtr = 
  				(GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" );
  
  		if( !gGetIpInterfaceEntryFunctionPtr )
  		{		
  			BOOL ok;
  				
  			ok = FreeLibrary( gIPHelperLibraryInstance );
  			check_translated_errno( ok, GetLastError(), kUnknownErr );
  			gIPHelperLibraryInstance = NULL;
  		}
  	}
  
  	if ( gGetIpInterfaceEntryFunctionPtr )
  	{
  		MIB_IPINTERFACE_ROW row;
  		DWORD err;
  
  		ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) );
  		row.Family = AF_INET;
  		row.InterfaceIndex = ifIndex;
  		err = gGetIpInterfaceEntryFunctionPtr( &row );
  		require_noerr( err, exit );
  		metric = row.Metric + 256;
  	}
  
  exit:
  
  	return metric;
  }
  
  
  //===========================================================================================================================
  //	SetLLRoute
  //===========================================================================================================================
  
  static OSStatus
  SetLLRoute( mDNS * const inMDNS )
  {
  	OSStatus err = kNoErr;
  
  	DEBUG_UNUSED( inMDNS );
  
  	//
  	// <rdar://problem/4096464> Don't call SetLLRoute on loopback
  	// <rdar://problem/6885843> Default route on Windows 7 breaks network connectivity
  	// 
  	// Don't mess w/ the routing table on Vista and later OSes, as 
  	// they have a permanent route to link-local addresses. Otherwise,
  	// set a route to link local addresses (169.254.0.0)
  	//
  	if ( ( inMDNS->p->osMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 )
  	{
  		DWORD				ifIndex;
  		MIB_IPFORWARDROW	rowExtant;
  		bool				addRoute;
  		MIB_IPFORWARDROW	row;
  
  		ZeroMemory(&row, sizeof(row));
  
  		err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop);
  		require_noerr( err, exit );
  		row.dwForwardDest		= inet_addr(kLLNetworkAddr);
  		row.dwForwardIfIndex	= ifIndex;
  		row.dwForwardMask		= inet_addr(kLLNetworkAddrMask);
  		row.dwForwardType		= 3;
  		row.dwForwardProto		= MIB_IPPROTO_NETMGMT;
  		row.dwForwardAge		= 0;
  		row.dwForwardPolicy		= 0;
  		row.dwForwardMetric1	= 20 + GetAdditionalMetric( ifIndex );
  		row.dwForwardMetric2	= (DWORD) - 1;
  		row.dwForwardMetric3	= (DWORD) - 1;
  		row.dwForwardMetric4	= (DWORD) - 1;
  		row.dwForwardMetric5	= (DWORD) - 1;
  
  		addRoute = true;
  
  		//
  		// check to make sure we don't already have a route
  		//
  		if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) )
  		{
  			//
  			// set the age to 0 so that we can do a memcmp.
  			//
  			rowExtant.dwForwardAge = 0;
  
  			//
  			// check to see if this route is the same as our route
  			//
  			if (memcmp(&row, &rowExtant, sizeof(row)) != 0)
  			{
  				//
  				// if it isn't then delete this entry
  				//
  				DeleteIpForwardEntry(&rowExtant);
  			}
  			else
  			{
  				//
  				// else it is, so we don't want to create another route
  				//
  				addRoute = false;
  			}
  		}
  
  		if (addRoute && row.dwForwardNextHop)
  		{
  			err = CreateIpForwardEntry(&row);
  			check_noerr( err );
  		}
  	}
  
  exit:
  
  	return ( err );
  }
  
  
  //===========================================================================================================================
  //	GetRouteDestination
  //===========================================================================================================================
  
  static OSStatus
  GetRouteDestination(DWORD * ifIndex, DWORD * address)
  {
  	struct in_addr		ia;
  	IP_ADAPTER_INFO	*	pAdapterInfo	=	NULL;
  	IP_ADAPTER_INFO	*	pAdapter		=	NULL;
  	ULONG				bufLen;
  	mDNSBool			done			=	mDNSfalse;
  	OSStatus			err;
  
  	//
  	// GetBestInterface will fail if there is no default gateway
  	// configured.  If that happens, we will just take the first
  	// interface in the list. MSDN support says there is no surefire
  	// way to manually determine what the best interface might
  	// be for a particular network address.
  	//
  	ia.s_addr	=	inet_addr(kLLNetworkAddr);
  	err			=	GetBestInterface(*(IPAddr*) &ia, ifIndex);
  
  	if (err)
  	{
  		*ifIndex = 0;
  	}
  
  	//
  	// Make an initial call to GetAdaptersInfo to get
  	// the necessary size into the bufLen variable
  	//
  	err = GetAdaptersInfo( NULL, &bufLen);
  	require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr );
  
  	pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen );
  	require_action( pAdapterInfo, exit, err = kNoMemoryErr );
  	
  	err = GetAdaptersInfo( pAdapterInfo, &bufLen);
  	require_noerr( err, exit );
  	
  	pAdapter	=	pAdapterInfo;
  	err			=	kUnknownErr;
  			
  	// <rdar://problem/3718122>
  	// <rdar://problem/5652098>
  	//
  	// Look for the Nortel VPN virtual interface, along with Juniper virtual interface.
  	//
  	// If these interfaces are active (i.e., has a non-zero IP Address),
  	// then we want to disable routing table modifications.
  
  	while (pAdapter)
  	{
  		if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) &&
  			 ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) )
  		{
  			dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" );
  			goto exit;
  		}
  
  		pAdapter = pAdapter->Next;
  	}
  
  	while ( !done )
  	{
  		pAdapter	=	pAdapterInfo;
  		err			=	kUnknownErr;
  
  		while (pAdapter)
  		{
  			// If we don't have an interface selected, choose the first one that is of type ethernet and
  			// has a valid IP Address
  
  			if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex))))
  			{
  				*address =	inet_addr( pAdapter->IpAddressList.IpAddress.String );
  				*ifIndex =  pAdapter->Index;
  				err		 =	kNoErr;
  				break;
  			}
  		
  			pAdapter = pAdapter->Next;
  		}
  
  		// If we found the right interface, or we weren't trying to find a specific interface then we're done
  
  		if ( !err || !( *ifIndex) )
  		{
  			done = mDNStrue;
  		}
  
  		// Otherwise, try again by wildcarding the interface
  
  		else
  		{
  			*ifIndex = 0;
  		}
  	} 
  
  exit:
  
  	if ( pAdapterInfo != NULL )
  	{
  		free( pAdapterInfo );
  	}
  
  	return( err );
  }
  
  
  static bool
  IsNortelVPN( IP_ADAPTER_INFO * pAdapter )
  {
  	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
  		    (pAdapter->AddressLength == 6) &&
  		    (pAdapter->Address[0] == 0x44) &&
  		    (pAdapter->Address[1] == 0x45) &&
  		    (pAdapter->Address[2] == 0x53) &&
  		    (pAdapter->Address[3] == 0x54) &&
  		    (pAdapter->Address[4] == 0x42) &&
  			(pAdapter->Address[5] == 0x00)) ? true : false;
  }
  
  
  static bool
  IsJuniperVPN( IP_ADAPTER_INFO * pAdapter )
  {	
  	return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description  ) ) != NULL ) ? true : false;
  }
  
  
  static bool
  IsCiscoVPN( IP_ADAPTER_INFO * pAdapter )
  {
  	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
  		    (pAdapter->AddressLength == 6) &&
  		    (pAdapter->Address[0] == 0x00) &&
  		    (pAdapter->Address[1] == 0x05) &&
  		    (pAdapter->Address[2] == 0x9a) &&
  		    (pAdapter->Address[3] == 0x3c) &&
  		    (pAdapter->Address[4] == 0x7a) &&
  			(pAdapter->Address[5] == 0x00)) ? true : false;
  }
  
  
  static const char *
  strnistr( const char * string, const char * subString, size_t max )
  {
  	size_t       subStringLen;
  	size_t       offset;
  	size_t       maxOffset;
  	size_t       stringLen;
  	const char * pPos;
  
  	if ( ( string == NULL ) || ( subString == NULL ) )
  	{
  		return string;
  	}
  
  	stringLen = ( max > strlen( string ) ) ? strlen( string ) : max;
  
  	if ( stringLen == 0 )
  	{
  		return NULL;
  	}
  	
  	subStringLen = strlen( subString );
  
  	if ( subStringLen == 0 )
  	{
  		return string;
  	}
  
  	if ( subStringLen > stringLen )
  	{
  		return NULL;
  	}
  
  	maxOffset = stringLen - subStringLen;
  	pPos      = string;
  
  	for ( offset = 0; offset <= maxOffset; offset++ )
  	{
  		if ( _strnicmp( pPos, subString, subStringLen ) == 0 )
  		{
  			return pPos;
  		}
  
  		pPos++;
  	}
  
  	return NULL;
  }
  
  