| /* -*- 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; |
| } |
| |