/* -*- 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 #include #include #include #include #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 #include #include #include #include #include #include #include #include #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, " 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 ]; vsnprintf( s, sizeof( 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; // 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, ¶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 = 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 ); // // Don't call SetLLRoute on loopback // 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; // // // // 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; }