/* -*- 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 "CommonServices.h" #include "DebugServices.h" #include "RegNames.h" #include "uds_daemon.h" #include "GenLinkedList.h" #include "Service.h" #include "mDNSWindows/SystemService/EventLog.h" #include "Resource.h" #include "mDNSEmbeddedAPI.h" #include "uDNS.h" #include "mDNSWin32.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 RR_CACHE_SIZE 500 static CacheEntity gRRCache[RR_CACHE_SIZE]; #if 0 #pragma mark == Structures == #endif //=========================================================================================================================== // Structures //=========================================================================================================================== typedef struct EventSource { HANDLE event; void * context; RegisterWaitableEventHandler handler; struct EventSource * next; } EventSource; static BOOL gEventSourceListChanged = FALSE; static EventSource * gEventSourceList = NULL; static EventSource * gCurrentSource = NULL; static int gEventSources = 0; #define kWaitListStopEvent ( WAIT_OBJECT_0 + 0 ) #define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 ) #define kWaitListComputerDescriptionEvent ( WAIT_OBJECT_0 + 2 ) #define kWaitListTCPIPEvent ( WAIT_OBJECT_0 + 3 ) #define kWaitListDynDNSEvent ( WAIT_OBJECT_0 + 4 ) #define kWaitListFileShareEvent ( WAIT_OBJECT_0 + 5 ) #define kWaitListFirewallEvent ( WAIT_OBJECT_0 + 6 ) #define kWaitListAdvertisedServicesEvent ( WAIT_OBJECT_0 + 7 ) #define kWaitListSPSWakeupEvent ( WAIT_OBJECT_0 + 8 ) #define kWaitListSPSSleepEvent ( WAIT_OBJECT_0 + 9 ) #define kWaitListFixedItemCount 10 #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 SetupNotifications(); static mStatus TearDownNotifications(); static mStatus RegisterWaitableEvent( mDNS * const inMDNS, HANDLE event, void * context, RegisterWaitableEventHandler handler ); static void UnregisterWaitableEvent( mDNS * const inMDNS, HANDLE event ); static mStatus SetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ); static void UDSCanAccept( mDNS * const inMDNS, HANDLE event, void * context ); static void UDSCanRead( TCPSocket * sock ); static void HandlePowerSuspend( void * v ); static void HandlePowerResumeSuspend( void * v ); 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 SOCKET gInterfaceListChangedSocket = INVALID_SOCKET; DEBUG_LOCAL HANDLE gInterfaceListChangedEvent = NULL; 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 int gWaitCount = 0; DEBUG_LOCAL HANDLE * gWaitList = NULL; DEBUG_LOCAL HANDLE gStopEvent = NULL; DEBUG_LOCAL HANDLE gSPSWakeupEvent = NULL; DEBUG_LOCAL HANDLE gSPSSleepEvent = NULL; DEBUG_LOCAL HANDLE gUDSEvent = NULL; DEBUG_LOCAL SocketRef gUDSSocket = 0; DEBUG_LOCAL udsEventCallback gUDSCallback = NULL; DEBUG_LOCAL BOOL gRetryFirewall = FALSE; DEBUG_LOCAL DWORD gOSMajorVersion; DEBUG_LOCAL DWORD gOSMinorVersion; 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; // 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 ); } return( err ); } #if 0 #pragma mark - #endif //=========================================================================================================================== // ServiceMain //=========================================================================================================================== static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] ) { OSStatus err; BOOL ok; 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; } } //=========================================================================================================================== // 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 ); } //=========================================================================================================================== // HandlePowerSuspend //=========================================================================================================================== static void HandlePowerSuspend( void * v ) { LARGE_INTEGER timeout; BOOL ok; ( void ) v; dlog( kDebugLevelInfo, DEBUG_NAME "HandlePowerSuspend\n" ); gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout ); if ( gMDNSRecord.SystemWakeOnLANEnabled ) { ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE ); check( ok ); } mDNSCoreMachineSleep(&gMDNSRecord, TRUE); } //=========================================================================================================================== // HandlePowerResumeSuspend //=========================================================================================================================== static void HandlePowerResumeSuspend( void * v ) { ( void ) v; dlog( kDebugLevelInfo, DEBUG_NAME "HandlePowerResumeSuspend\n" ); if ( gSPSWakeupEvent ) { CancelWaitableTimer( gSPSWakeupEvent ); } if ( gSPSSleepEvent ) { CancelWaitableTimer( gSPSSleepEvent ); } mDNSCoreMachineSleep(&gMDNSRecord, FALSE); } //=========================================================================================================================== // ServiceControlHandler //=========================================================================================================================== static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext ) { BOOL setStatus; 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" ); QueueUserAPC( ( PAPCFUNC ) HandlePowerSuspend, gMDNSRecord.p->mainThread, ( ULONG_PTR ) NULL ); } else if (inEventType == PBT_APMRESUMESUSPEND) { dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" ); QueueUserAPC( ( PAPCFUNC ) HandlePowerResumeSuspend, gMDNSRecord.p->mainThread, ( ULONG_PTR ) NULL ); } 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 err = ServiceSpecificInitialize( argc, argv ); require_noerr( err, 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 ); ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err ); require_noerr( err, exit ); // Service stopped. Clean up and we're done. exit: 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[] ) { OSVERSIONINFO osInfo; OSStatus err; BOOL ok; DEBUG_UNUSED( argc ); DEBUG_UNUSED( argv ); mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord); mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage); gPlatformStorage.registerWaitableEventFunc = RegisterWaitableEvent; gPlatformStorage.unregisterWaitableEventFunc = UnregisterWaitableEvent; 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); // // Get the version of Windows that we're running on // osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); ok = GetVersionEx( &osInfo ); err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); gOSMajorVersion = osInfo.dwMajorVersion; gOSMinorVersion = osInfo.dwMinorVersion; SetLLRoute( &gMDNSRecord ); exit: if( err != kNoErr ) { ServiceSpecificFinalize( argc, argv ); } return( err ); } //=========================================================================================================================== // ServiceSpecificRun //=========================================================================================================================== static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] ) { HANDLE * waitList; int waitListCount; DWORD timeout; DWORD result; BOOL done; mStatus err; DEBUG_UNUSED( argc ); DEBUG_UNUSED( argv ); timeout = ( gRetryFirewall ) ? kRetryFirewallPeriod : INFINITE; err = SetupInterfaceList( &gMDNSRecord ); check( !err ); err = uDNS_SetupDNSConfig( &gMDNSRecord ); check( !err ); done = FALSE; // Main event loop. while( !done ) { waitList = NULL; waitListCount = 0; err = SetupWaitList( &gMDNSRecord, &waitList, &waitListCount ); require_noerr( err, exit ); gEventSourceListChanged = FALSE; while ( !gEventSourceListChanged ) { static mDNSs32 RepeatedBusy = 0; mDNSs32 nextTimerEvent; // 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; } } // Wait until something occurs (e.g. cancel, incoming packet, or timeout). // // Note: There seems to be a bug in WinSock with respect to Alertable I/O. According // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O // callbacks will only be invoked during the following calls (when the caller sets // the appropriate flag): // // - SleepEx // - WaitForSingleObjectEx // - WaitForMultipleObjectsEx // - SignalObjectAndWait // - MsgWaitForMultipleObjectsEx // // However, we have seen callbacks be invoked during calls to bind() (and maybe others). If there // weren't a bug, then socket events would only be queued during the call to WaitForMultipleObjects() and // we'd only have to check them once afterwards. However since that doesn't seem to be the case, we'll // check the queue both before we call WaitForMultipleObjects() and after. DispatchSocketEvents( &gMDNSRecord ); result = WaitForMultipleObjectsEx( ( DWORD ) waitListCount, waitList, FALSE, (DWORD) nextTimerEvent, TRUE ); check( result != WAIT_FAILED ); DispatchSocketEvents( &gMDNSRecord ); if ( result != WAIT_FAILED ) { if ( result == WAIT_TIMEOUT ) { // Next task timeout occurred. Loop back up to give mDNS core a chance to work. dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" ); continue; } else if ( result == WAIT_IO_COMPLETION ) { dlog( kDebugLevelChatty - 1, DEBUG_NAME "i/o completion\n" ); continue; } else if ( result == kWaitListStopEvent ) { // Stop event. Set the done flag and break to exit. dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" ); udsserver_exit(); mDNS_StartExit( &gMDNSRecord ); break; } else if( result == kWaitListInterfaceListChangedEvent ) { int inBuffer; int outBuffer; DWORD outSize; // 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 ); } } else if ( result == kWaitListComputerDescriptionEvent ) { // The computer description might have changed ComputerDescriptionDidChange( &gMDNSRecord ); udsserver_handle_configchange( &gMDNSRecord ); // and reset the event handler if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) ) { err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE); check_noerr( err ); } } else if ( result == kWaitListTCPIPEvent ) { // The TCP/IP might have changed TCPIPConfigDidChange( &gMDNSRecord ); udsserver_handle_configchange( &gMDNSRecord ); // and reset the event handler if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) ) { err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE ); check_noerr( err ); } } else if ( result == kWaitListDynDNSEvent ) { // The DynDNS config might have changed DynDNSConfigDidChange( &gMDNSRecord ); udsserver_handle_configchange( &gMDNSRecord ); // and reset the event handler if ((gDdnsKey != NULL) && (gDdnsChangedEvent)) { err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE); check_noerr( err ); } } else if ( result == kWaitListFileShareEvent ) { // File sharing changed FileSharingDidChange( &gMDNSRecord ); // and reset the event handler if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent)) { err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE); check_noerr( err ); } } else if ( result == kWaitListFirewallEvent ) { // Firewall configuration changed FirewallDidChange( &gMDNSRecord ); // and reset the event handler if ((gFirewallKey != NULL) && (gFirewallChangedEvent)) { err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE); check_noerr( err ); } } else if ( result == kWaitListAdvertisedServicesEvent ) { // Ultimately we'll want to manage multiple services, but right now the only service // we'll be managing is SMB. FileSharingDidChange( &gMDNSRecord ); // and reset the event handler if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) ) { err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE); check_noerr( err ); } } else if ( result == kWaitListSPSWakeupEvent ) { LARGE_INTEGER timeout; ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" ); timeout.QuadPart = kSPSMaintenanceWakePeriod; timeout.QuadPart *= kSecondsTo100NSUnits; SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE ); } else if ( result == kWaitListSPSSleepEvent ) { 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. HandlePowerSuspend( NULL ); SetSuspendState( FALSE, FALSE, FALSE ); } else { int waitItemIndex; waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 ); dlog( kDebugLevelChatty, DEBUG_NAME "waitable event on %d\n", waitItemIndex ); check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ); if ( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ) { HANDLE signaledEvent; int n = 0; signaledEvent = waitList[ waitItemIndex ]; // If gCurrentSource is not NULL, then this routine has been called // re-entrantly which should never happen. check( !gCurrentSource ); for ( gCurrentSource = gEventSourceList; gCurrentSource; ) { EventSource * current = gCurrentSource; if ( gCurrentSource->event == signaledEvent ) { gCurrentSource->handler( &gMDNSRecord, gCurrentSource->event, gCurrentSource->context ); ++n; break; } // If the current node was removed as a result of calling // the handler, then gCurrentSource was already incremented to // the next node. If it wasn't removed, then increment it // ourselves if ( gCurrentSource == current ) { gCurrentSource = gCurrentSource->next; } } gCurrentSource = NULL; check( n > 0 ); } else { dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); } } } else { Sleep( 3 * 1000 ); err = SetupInterfaceList( &gMDNSRecord ); check( !err ); err = uDNS_SetupDNSConfig( &gMDNSRecord ); check( !err ); break; } } if ( waitList ) { free( waitList ); waitList = NULL; waitListCount = 0; } } exit: return( 0 ); } //=========================================================================================================================== // 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 any open sessions // while ( gEventSourceList ) { UnregisterWaitableEvent( &gMDNSRecord, gEventSourceList->event ); } // // clean up the notifications // TearDownNotifications(); // // clean up loaded library // if( gIPHelperLibraryInstance ) { gGetIpInterfaceEntryFunctionPtr = NULL; FreeLibrary( gIPHelperLibraryInstance ); gIPHelperLibraryInstance = NULL; } } //=========================================================================================================================== // SetupNotifications //=========================================================================================================================== mDNSlocal mStatus SetupNotifications() { mStatus err; SocketRef sock; unsigned long param; int inBuffer; int outBuffer; DWORD outSize; gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); err = translate_errno( gStopEvent, errno_compat(), kNoResourcesErr ); require_noerr( err, exit ); // Register to listen for address list changes. gInterfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); err = translate_errno( gInterfaceListChangedEvent, (mStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); 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, ¶m ); 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 = WSAEventSelect( sock, gInterfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE ); err = translate_errno( err == 0, errno_compat(), kUnknownErr ); 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 ); } // 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 ); // 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 ); // 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 ); } 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 ); } 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 ); gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL ); err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL ); err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); gUDSEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); err = translate_errno( gUDSEvent, ( mStatus ) GetLastError(), kUnknownErr ); require_noerr( err, exit ); exit: if( err ) { TearDownNotifications(); } return( err ); } //=========================================================================================================================== // TearDownNotifications //=========================================================================================================================== mDNSlocal mStatus TearDownNotifications() { if ( gStopEvent ) { CloseHandle( gStopEvent ); gStopEvent = NULL; } if( IsValidSocket( gInterfaceListChangedSocket ) ) { close_compat( gInterfaceListChangedSocket ); gInterfaceListChangedSocket = kInvalidSocketRef; } if( gInterfaceListChangedEvent ) { CloseHandle( gInterfaceListChangedEvent ); gInterfaceListChangedEvent = 0; } if ( gDescChangedEvent != NULL ) { CloseHandle( gDescChangedEvent ); gDescChangedEvent = NULL; } if ( gDescKey != NULL ) { RegCloseKey( gDescKey ); gDescKey = NULL; } if ( gTcpipChangedEvent != NULL ) { CloseHandle( gTcpipChangedEvent ); gTcpipChangedEvent = NULL; } if ( gDdnsChangedEvent != NULL ) { CloseHandle( gDdnsChangedEvent ); gDdnsChangedEvent = NULL; } if ( gDdnsKey != NULL ) { RegCloseKey( gDdnsKey ); gDdnsKey = NULL; } if ( gFileSharingChangedEvent != NULL ) { CloseHandle( gFileSharingChangedEvent ); gFileSharingChangedEvent = NULL; } if ( gFileSharingKey != NULL ) { RegCloseKey( gFileSharingKey ); gFileSharingKey = NULL; } if ( gFirewallChangedEvent != NULL ) { CloseHandle( gFirewallChangedEvent ); gFirewallChangedEvent = NULL; } if ( gFirewallKey != NULL ) { RegCloseKey( gFirewallKey ); gFirewallKey = NULL; } if ( gAdvertisedServicesChangedEvent != NULL ) { CloseHandle( gAdvertisedServicesChangedEvent ); gAdvertisedServicesChangedEvent = NULL; } if ( gAdvertisedServicesKey != NULL ) { RegCloseKey( gAdvertisedServicesKey ); gAdvertisedServicesKey = NULL; } if ( gSPSWakeupEvent ) { CloseHandle( gSPSWakeupEvent ); gSPSWakeupEvent = NULL; } if ( gSPSSleepEvent ) { CloseHandle( gSPSSleepEvent ); gSPSSleepEvent = NULL; } return( mStatus_NoError ); } //=========================================================================================================================== // RegisterWaitableEvent //=========================================================================================================================== static mStatus RegisterWaitableEvent( mDNS * const inMDNS, HANDLE event, void * context, RegisterWaitableEventHandler handler ) { EventSource * source; mStatus err = mStatus_NoError; ( void ) inMDNS; check( event ); check( handler ); source = ( EventSource* ) malloc( sizeof( EventSource ) ); require_action( source, exit, err = mStatus_NoMemoryErr ); mDNSPlatformMemZero( source, sizeof( EventSource ) ); source->event = event; source->context = context; source->handler = handler; source->next = gEventSourceList; gEventSourceList = source; gEventSourceListChanged = TRUE; gEventSources++; exit: return err; } //=========================================================================================================================== // UnregisterWaitableEvent //=========================================================================================================================== static void UnregisterWaitableEvent( mDNS * const inMDNS, HANDLE event ) { EventSource * current = gEventSourceList; EventSource * last = NULL; ( void ) inMDNS; check( event ); while ( current ) { if ( current->event == event ) { if ( last == NULL ) { gEventSourceList = current->next; } else { last->next = current->next; } gEventSourceListChanged = TRUE; // Protect against removing the node that we happen // to be looking at as we iterate through the event // source list in ServiceSpecificRun() if ( current == gCurrentSource ) { gCurrentSource = current->next; } gEventSources--; free( current ); break; } last = current; current = current->next; } } //=========================================================================================================================== // SetupWaitList //=========================================================================================================================== mDNSlocal mStatus SetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ) { int waitListCount; HANDLE * waitList; HANDLE * waitItemPtr; EventSource * source; mStatus err; dlog( kDebugLevelTrace, DEBUG_NAME "setting up wait list\n" ); ( void ) inMDNS; check( inMDNS->p ); check( outWaitList ); check( outWaitListCount ); // Allocate an array to hold all the objects to wait on. waitListCount = kWaitListFixedItemCount + gEventSources; waitList = ( HANDLE* ) malloc( waitListCount * sizeof( *waitList ) ); require_action( waitList, exit, err = mStatus_NoMemoryErr ); waitItemPtr = waitList; // Add the fixed wait items to the beginning of the list. *waitItemPtr++ = gStopEvent; *waitItemPtr++ = gInterfaceListChangedEvent; *waitItemPtr++ = gDescChangedEvent; *waitItemPtr++ = gTcpipChangedEvent; *waitItemPtr++ = gDdnsChangedEvent; *waitItemPtr++ = gFileSharingChangedEvent; *waitItemPtr++ = gFirewallChangedEvent; *waitItemPtr++ = gAdvertisedServicesChangedEvent; *waitItemPtr++ = gSPSWakeupEvent; *waitItemPtr++ = gSPSSleepEvent; for ( source = gEventSourceList; source; source = source->next ) { *waitItemPtr++ = source->event; } check( ( int )( waitItemPtr - waitList ) == waitListCount ); *outWaitList = waitList; *outWaitListCount = waitListCount; waitList = NULL; err = mStatus_NoError; exit: if( waitList ) { free( waitList ); } dlog( kDebugLevelTrace, DEBUG_NAME "setting up wait list done (err=%d %m)\n", err, err ); return( err ); } //=========================================================================================================================== // CoreCallback //=========================================================================================================================== static void CoreCallback(mDNS * const inMDNS, mStatus status) { if (status == mStatus_ConfigChanged) { SetLLRoute( inMDNS ); } } //=========================================================================================================================== // UDSCanAccept //=========================================================================================================================== mDNSlocal void UDSCanAccept( mDNS * const inMDNS, HANDLE event, void * context ) { ( void ) inMDNS; ( void ) event; if ( gUDSCallback ) { gUDSCallback( ( int ) gUDSSocket, 0, context ); } } //=========================================================================================================================== // UDSCanRead //=========================================================================================================================== mDNSlocal void UDSCanRead( TCPSocket * sock ) { udsEventCallback callback = ( udsEventCallback ) sock->userCallback; if ( callback ) { callback( (int) sock->fd, 0, sock->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 "callback" parameter is NULL. If it is an actual read/write socket, then the "callback" // parameter is not null. This is important because we use waitable events for the listen socket // and alertable I/O for the read/write sockets. if ( context ) { TCPSocket * sock; sock = malloc( sizeof( TCPSocket ) ); require_action( sock, exit, err = mStatus_NoMemoryErr ); mDNSPlatformMemZero( sock, sizeof( TCPSocket ) ); sock->fd = (SOCKET) fd; sock->readEventHandler = UDSCanRead; sock->userCallback = callback; sock->userContext = context; sock->m = &gMDNSRecord; err = TCPAddSocket( sock->m, sock ); require_noerr( err, exit ); *platform_data = sock; } else { gUDSSocket = fd; gUDSCallback = callback; gUDSEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); err = translate_errno( gUDSEvent, (mStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); err = WSAEventSelect( fd, gUDSEvent, FD_ACCEPT | FD_CLOSE ); err = translate_errno( err == 0, WSAGetLastError(), kNoResourcesErr ); require_noerr( err, exit ); err = RegisterWaitableEvent( &gMDNSRecord, gUDSEvent, context, UDSCanAccept ); 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; } exit: return ret; } mStatus udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data) // Note: This also CLOSES the socket { mStatus err = kNoErr; if ( platform_data != NULL ) { TCPSocket * sock; dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" ); sock = ( TCPSocket* ) platform_data; check( sock->fd == fd ); mDNSPlatformTCPCloseConnection( sock ); } else if ( gUDSEvent != NULL ) { UnregisterWaitableEvent( &gMDNSRecord, gUDSEvent ); WSAEventSelect( fd, gUDSEvent, 0 ); CloseHandle( gUDSEvent ); gUDSEvent = NULL; } 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 ( ( gOSMajorVersion < 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; }