2670 lines
76 KiB
C
2670 lines
76 KiB
C
/* -*- 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;
|
|
}
|
|
|