summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSWindows/SystemService/Service.c
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSWindows/SystemService/Service.c')
-rw-r--r--mDNSResponder/mDNSWindows/SystemService/Service.c2616
1 files changed, 2616 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSWindows/SystemService/Service.c b/mDNSResponder/mDNSWindows/SystemService/Service.c
new file mode 100644
index 00000000..d175326f
--- /dev/null
+++ b/mDNSResponder/mDNSWindows/SystemService/Service.c
@@ -0,0 +1,2616 @@
+/* -*- 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;
+}
+