diff options
Diffstat (limited to 'mDNSResponder/mDNSWindows/mdnsNSP/mdnsNSP.c')
-rw-r--r-- | mDNSResponder/mDNSWindows/mdnsNSP/mdnsNSP.c | 2430 |
1 files changed, 2430 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSWindows/mdnsNSP/mdnsNSP.c b/mDNSResponder/mDNSWindows/mdnsNSP/mdnsNSP.c new file mode 100644 index 00000000..2cd01eff --- /dev/null +++ b/mDNSResponder/mDNSWindows/mdnsNSP/mdnsNSP.c @@ -0,0 +1,2430 @@ +/* -*- 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 <string.h> + +#include "ClientCommon.h" +#include "CommonServices.h" +#include "DebugServices.h" + +#include <iphlpapi.h> +#include <guiddef.h> +#include <ws2spi.h> +#include <shlwapi.h> + + + +#include "dns_sd.h" + +#pragma comment(lib, "DelayImp.lib") + +#ifdef _MSC_VER +#define swprintf _snwprintf +#define snprintf _snprintf +#endif + +#define MAX_LABELS 128 + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef struct Query * QueryRef; +typedef struct Query Query; +struct Query +{ + QueryRef next; + int refCount; + DWORD querySetFlags; + WSAQUERYSETW * querySet; + size_t querySetSize; + HANDLE data4Event; + HANDLE data6Event; + HANDLE cancelEvent; + HANDLE waitHandles[ 3 ]; + DWORD waitCount; + DNSServiceRef resolver4; + DNSServiceRef resolver6; + char name[ kDNSServiceMaxDomainName ]; + size_t nameSize; + uint8_t numValidAddrs; + uint32_t addr4; + bool addr4Valid; + uint8_t addr6[16]; + u_long addr6ScopeId; + bool addr6Valid; +}; + +#define BUFFER_INITIAL_SIZE 4192 +#define ALIASES_INITIAL_SIZE 5 + +typedef struct HostsFile +{ + int m_bufferSize; + char * m_buffer; + FILE * m_fp; +} HostsFile; + + +typedef struct HostsFileInfo +{ + struct hostent m_host; + struct HostsFileInfo * m_next; +} HostsFileInfo; + + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// DLL Exports + +BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); +STDAPI DllRegisterServer( void ); +STDAPI DllRegisterServer( void ); + + +// NSP SPIs + +int WSPAPI NSPCleanup( LPGUID inProviderID ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioBufferLength, + LPWSAQUERYSETW outResults ); + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ); + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ); + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ); +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ); +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioBufSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ); + +// Private + +#define NSPLock() EnterCriticalSection( &gLock ); +#define NSPUnlock() LeaveCriticalSection( &gLock ); + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ); +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ); +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ); + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback4( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback6( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ); + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ); + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ); + +#if( DEBUG ) + void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ); + + #define dlog_query_set( LEVEL, SET ) DebugDumpQuerySet( LEVEL, SET ) +#else + #define dlog_query_set( LEVEL, SET ) +#endif + +DEBUG_LOCAL BOOL InHostsTable( const char * name ); +DEBUG_LOCAL BOOL IsLocalName( HostsFileInfo * node ); +DEBUG_LOCAL BOOL IsSameName( HostsFileInfo * node, const char * name ); +DEBUG_LOCAL OSStatus HostsFileOpen( HostsFile ** self, const char * fname ); +DEBUG_LOCAL OSStatus HostsFileClose( HostsFile * self ); +DEBUG_LOCAL void HostsFileInfoFree( HostsFileInfo * info ); +DEBUG_LOCAL OSStatus HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ); +DEBUG_LOCAL DWORD GetScopeId( DWORD ifIndex ); + +#ifdef ENABLE_REVERSE_LOOKUP +DEBUG_LOCAL OSStatus IsReverseLookup( LPCWSTR name, size_t size ); +#endif + + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +// {B600E6E9-553B-4a19-8696-335E5C896153} +DEBUG_LOCAL HINSTANCE gInstance = NULL; +DEBUG_LOCAL wchar_t * gNSPName = L"mdnsNSP"; +DEBUG_LOCAL GUID gNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; +DEBUG_LOCAL LONG gRefCount = 0; +DEBUG_LOCAL CRITICAL_SECTION gLock; +DEBUG_LOCAL bool gLockInitialized = false; +DEBUG_LOCAL QueryRef gQueryList = NULL; +DEBUG_LOCAL HostsFileInfo * gHostsFileInfo = NULL; +typedef DWORD + ( WINAPI * GetAdaptersAddressesFunctionPtr )( + ULONG inFamily, + DWORD inFlags, + PVOID inReserved, + PIP_ADAPTER_ADDRESSES inAdapter, + PULONG outBufferSize ); + +DEBUG_LOCAL HMODULE gIPHelperLibraryInstance = NULL; +DEBUG_LOCAL GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL; + + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DllMain +//=========================================================================================================================== + +BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) +{ + DEBUG_USE_ONLY( inInstance ); + DEBUG_UNUSED( inReserved ); + + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + gInstance = inInstance; + gHostsFileInfo = NULL; + debug_initialize( kDebugOutputTypeWindowsEventLog, "mDNS NSP", inInstance ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelNotice ); + dlog( kDebugLevelTrace, "\n" ); + dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ ); + + break; + + case DLL_PROCESS_DETACH: + HostsFileInfoFree( gHostsFileInfo ); + gHostsFileInfo = NULL; + dlog( kDebugLevelVerbose, "%s: process detach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_ATTACH: + dlog( kDebugLevelVerbose, "%s: thread attach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_DETACH: + dlog( kDebugLevelVerbose, "%s: thread detach\n", __ROUTINE__ ); + break; + + default: + dlog( kDebugLevelNotice, "%s: unknown reason code (%d)\n", __ROUTINE__, inReason ); + break; + } + + return( TRUE ); +} + + +//=========================================================================================================================== +// DllRegisterServer +//=========================================================================================================================== + +STDAPI DllRegisterServer( void ) +{ + WSADATA wsd; + WCHAR path[ MAX_PATH ]; + HRESULT err; + + dlog( kDebugLevelTrace, "DllRegisterServer\n" ); + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + // Unregister before registering to workaround an installer + // problem during upgrade installs. + + WSCUnInstallNameSpace( &gNSPGUID ); + + err = GetModuleFileNameW( gInstance, path, MAX_PATH ); + err = translate_errno( err != 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + err = WSCInstallNameSpace( gNSPName, path, NS_DNS, 1, &gNSPGUID ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + +exit: + + WSACleanup(); + return( err ); +} + +//=========================================================================================================================== +// DllUnregisterServer +//=========================================================================================================================== + +STDAPI DllUnregisterServer( void ) +{ + WSADATA wsd; + HRESULT err; + + dlog( kDebugLevelTrace, "DllUnregisterServer\n" ); + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + err = WSCUnInstallNameSpace( &gNSPGUID ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + +exit: + + WSACleanup(); + return err; +} + + +//=========================================================================================================================== +// NSPStartup +// +// This function is called when our namespace DLL is loaded. It sets up the NSP functions we implement and initializes us. +//=========================================================================================================================== + +int WSPAPI NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedIncrement( &gRefCount ) != 1 ) + { + err = NO_ERROR; + goto exit; + } + + // Initialize our internal state. + + InitializeCriticalSection( &gLock ); + gLockInitialized = true; + + // Set the size to exclude NSPIoctl because we don't implement it. + + outRoutines->cbSize = FIELD_OFFSET( NSP_ROUTINE, NSPIoctl ); + outRoutines->dwMajorVersion = 4; + outRoutines->dwMinorVersion = 4; + outRoutines->NSPCleanup = NSPCleanup; + outRoutines->NSPLookupServiceBegin = NSPLookupServiceBegin; + outRoutines->NSPLookupServiceNext = NSPLookupServiceNext; + outRoutines->NSPLookupServiceEnd = NSPLookupServiceEnd; + outRoutines->NSPSetService = NSPSetService; + outRoutines->NSPInstallServiceClass = NSPInstallServiceClass; + outRoutines->NSPRemoveServiceClass = NSPRemoveServiceClass; + outRoutines->NSPGetServiceClassInfo = NSPGetServiceClassInfo; + + // See if we can get the address for the GetAdaptersAddresses() API. This is only in XP, but we want our + // code to run on older versions of Windows + + if ( !gIPHelperLibraryInstance ) + { + gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" ); + } + } + + err = NO_ERROR; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + NSPCleanup( inProviderID ); + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPCleanup +// +// This function is called when our namespace DLL is unloaded. It cleans up anything we set up in NSPStartup. +//=========================================================================================================================== + +int WSPAPI NSPCleanup( LPGUID inProviderID ) +{ + DEBUG_USE_ONLY( inProviderID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedDecrement( &gRefCount ) != 0 ) + { + goto exit; + } + + // Stop any outstanding queries. + + if( gLockInitialized ) + { + NSPLock(); + } + while( gQueryList ) + { + check_string( gQueryList->refCount == 1, "NSPCleanup with outstanding queries!" ); + QueryRelease( gQueryList ); + } + if( gLockInitialized ) + { + NSPUnlock(); + } + + if( gLockInitialized ) + { + gLockInitialized = false; + DeleteCriticalSection( &gLock ); + } + + if( gIPHelperLibraryInstance ) + { + BOOL ok; + + ok = FreeLibrary( gIPHelperLibraryInstance ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gIPHelperLibraryInstance = NULL; + } + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceBegin +// +// This function maps to the WinSock WSALookupServiceBegin function. It starts the lookup process and returns a HANDLE +// that can be used in subsequent operations. Subsequent calls only need to refer to this query by the handle as +// opposed to specifying the query parameters each time. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ) +{ + OSStatus err; + QueryRef obj; + LPCWSTR name; + size_t size; + LPCWSTR p; + DWORD type; + DWORD n; + DWORD i; + INT family; + INT protocol; + + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + obj = NULL; + require_action( inQuerySet, exit, err = WSAEINVAL ); + name = inQuerySet->lpszServiceInstanceName; + require_action_quiet( name, exit, err = WSAEINVAL ); + require_action( outLookup, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=\"%S\")\n", __ROUTINE__, inFlags, name ); + dlog_query_set( kDebugLevelVerbose, inQuerySet ); + + // Check if we can handle this type of request and if we support any of the protocols being requested. + // We only support the DNS namespace, TCP and UDP protocols, and IPv4. Only blob results are supported. + + require_action_quiet( inFlags & (LUP_RETURN_ADDR|LUP_RETURN_BLOB), exit, err = WSASERVICE_NOT_FOUND ); + + type = inQuerySet->dwNameSpace; + require_action_quiet( ( type == NS_DNS ) || ( type == NS_ALL ), exit, err = WSASERVICE_NOT_FOUND ); + + n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) + { + require_action( inQuerySet->lpafpProtocols, exit, err = WSAEINVAL ); + for( i = 0; i < n; ++i ) + { + family = inQuerySet->lpafpProtocols[ i ].iAddressFamily; + protocol = inQuerySet->lpafpProtocols[ i ].iProtocol; + if( ( family == AF_INET ) && ( ( protocol == IPPROTO_UDP ) || ( protocol == IPPROTO_TCP ) ) ) + { + break; + } + } + require_action_quiet( i < n, exit, err = WSASERVICE_NOT_FOUND ); + } + + // Check if the name ends in ".local" and if not, exit with an error since we only resolve .local names. + // The name may or may not end with a "." (fully qualified) so handle both cases. DNS is also case + // insensitive the check for .local has to be case insensitive (.LoCaL is equivalent to .local). This + // manually does the wchar_t strlen and stricmp to avoid needing any special wchar_t versions of the + // libraries. It is probably faster to do the inline compare than invoke functions to do it anyway. + + for( p = name; *p; ++p ) {} // Find end of string + size = (size_t)( p - name ); + require_action_quiet( size > sizeof_string( ".local" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".local" ) ) : ( ( p - sizeof_string( ".local" ) ) + 1 ); + if ( ( ( p[ 0 ] != '.' ) || + ( ( p[ 1 ] != 'L' ) && ( p[ 1 ] != 'l' ) ) || + ( ( p[ 2 ] != 'O' ) && ( p[ 2 ] != 'o' ) ) || + ( ( p[ 3 ] != 'C' ) && ( p[ 3 ] != 'c' ) ) || + ( ( p[ 4 ] != 'A' ) && ( p[ 4 ] != 'a' ) ) || + ( ( p[ 5 ] != 'L' ) && ( p[ 5 ] != 'l' ) ) ) ) + { +#ifdef ENABLE_REVERSE_LOOKUP + + err = IsReverseLookup( name, size ); + +#else + + err = WSASERVICE_NOT_FOUND; + +#endif + + require_noerr( err, exit ); + } + else + { + const char * replyDomain; + char translated[ kDNSServiceMaxDomainName ]; + int n; + int labels = 0; + const char * label[MAX_LABELS]; + char text[64]; + + n = WideCharToMultiByte( CP_UTF8, 0, name, -1, translated, sizeof( translated ), NULL, NULL ); + require_action( n > 0, exit, err = WSASERVICE_NOT_FOUND ); + + // <rdar://problem/4050633> + + // Don't resolve multi-label name + + // <rdar://problem/5914160> Eliminate use of GetNextLabel in mdnsNSP + // Add checks for GetNextLabel returning NULL, individual labels being greater than + // 64 bytes, and the number of labels being greater than MAX_LABELS + replyDomain = translated; + + while (replyDomain && *replyDomain && labels < MAX_LABELS) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND ); + + // <rdar://problem/3936771> + // + // Check to see if the name of this host is in the hosts table. If so, + // don't try and resolve it + + require_action( InHostsTable( translated ) == FALSE, exit, err = WSASERVICE_NOT_FOUND ); + } + + // The name ends in .local ( and isn't in the hosts table ), .0.8.e.f.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed. + + NSPLock(); + + err = QueryCreate( inQuerySet, inFlags, &obj ); + NSPUnlock(); + require_noerr( err, exit ); + + *outLookup = (HANDLE) obj; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceNext +// +// This function maps to the Winsock call WSALookupServiceNext. This routine takes a handle to a previously defined +// query and attempts to locate a service matching the criteria defined by the query. If so, that instance is returned +// in the lpqsResults parameter. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioSize, + LPWSAQUERYSETW outResults ) +{ + BOOL data4; + BOOL data6; + OSStatus err; + QueryRef obj; + DWORD waitResult; + size_t size; + + DEBUG_USE_ONLY( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + data4 = FALSE; + data6 = FALSE; + obj = NULL; + NSPLock(); + err = QueryRetain( (QueryRef) inLookup ); + require_noerr( err, exit ); + obj = (QueryRef) inLookup; + require_action( ioSize, exit, err = WSAEINVAL ); + require_action( outResults, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p, flags=0x%08X, *ioSize=%d)\n", __ROUTINE__, inLookup, inFlags, *ioSize ); + + // Wait for data or a cancel. Release the lock while waiting. This is safe because we've retained the query. + + NSPUnlock(); + waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 2 * 1000 ); + NSPLock(); + require_action_quiet( waitResult != ( WAIT_OBJECT_0 ), exit, err = WSA_E_CANCELLED ); + err = translate_errno( ( waitResult == WAIT_OBJECT_0 + 1 ) || ( waitResult == WAIT_OBJECT_0 + 2 ), (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); + require_noerr_quiet( err, exit ); + + // If we've received an IPv4 reply, then hang out briefly for an IPv6 reply + + if ( waitResult == WAIT_OBJECT_0 + 1 ) + { + data4 = TRUE; + data6 = WaitForSingleObject( obj->data6Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE; + } + + // Else we've received an IPv6 reply, so hang out briefly for an IPv4 reply + + else if ( waitResult == WAIT_OBJECT_0 + 2 ) + { + data4 = WaitForSingleObject( obj->data4Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE; + data6 = TRUE; + } + + if ( data4 ) + { + __try + { + err = DNSServiceProcessResult(obj->resolver4); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + } + + if ( data6 ) + { + __try + { + err = DNSServiceProcessResult( obj->resolver6 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + } + + require_action_quiet( obj->addr4Valid || obj->addr6Valid, exit, err = WSA_E_NO_MORE ); + + // Copy the externalized query results to the callers buffer (if it fits). + + size = QueryCopyQuerySetSize( obj, obj->querySet, obj->querySetFlags ); + require_action( size <= (size_t) *ioSize, exit, err = WSAEFAULT ); + + QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults ); + outResults->dwOutputFlags = RESULT_IS_ADDED; + obj->addr4Valid = false; + obj->addr6Valid = false; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + NSPUnlock(); + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceEnd +// +// This function maps to the Winsock call WSALookupServiceEnd. Once the user process has finished is query (usually +// indicated when WSALookupServiceNext returns the error WSA_E_NO_MORE) a call to this function is made to release any +// allocated resources associated with the query. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p)\n", __ROUTINE__, inLookup ); + + NSPLock(); + err = QueryRelease( (QueryRef) inLookup ); + NSPUnlock(); + require_noerr( err, exit ); + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPSetService +// +// This function maps to the Winsock call WSASetService. This routine is called when the user wants to register or +// deregister an instance of a server with our service. For registration, the user needs to associate the server with a +// service class. For deregistration the service class is required along with the servicename. The inRegInfo parameter +// contains a WSAQUERYSET structure defining the server (such as protocol and address where it is). +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + DEBUG_UNUSED( inRegInfo ); + DEBUG_UNUSED( inOperation ); + DEBUG_UNUSED( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow services to be registered so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSAEINVAL ); +} + +//=========================================================================================================================== +// NSPInstallServiceClass +// +// This function maps to the Winsock call WSAInstallServiceClass. This routine is used to install a service class which +// is used to define certain characteristics for a group of services. After a service class is registered, an actual +// instance of a server may be registered. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSA_INVALID_PARAMETER ); +} + +//=========================================================================================================================== +// NSPRemoveServiceClass +// +// This function maps to the Winsock call WSARemoveServiceClass. This routine removes a previously registered service +// class. This is accomplished by connecting to the namespace service and writing the GUID which defines the given +// service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +//=========================================================================================================================== +// NSPGetServiceClassInfo +// +// This function maps to the Winsock call WSAGetServiceClassInfo. This routine returns the information associated with +// a given service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( ioSize ); + DEBUG_UNUSED( ioServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// QueryCreate +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ) +{ + OSStatus err; + QueryRef obj; + char name[ kDNSServiceMaxDomainName ]; + int n; + QueryRef * p; + SOCKET s4; + SOCKET s6; + + obj = NULL; + check( inQuerySet ); + check( inQuerySet->lpszServiceInstanceName ); + check( outRef ); + + // Convert the wchar_t name to UTF-8. + + n = WideCharToMultiByte( CP_UTF8, 0, inQuerySet->lpszServiceInstanceName, -1, name, sizeof( name ), NULL, NULL ); + err = translate_errno( n > 0, (OSStatus) GetLastError(), WSAEINVAL ); + require_noerr( err, exit ); + + // Allocate the object and append it to the list. Append immediately so releases of partial objects work. + + obj = (QueryRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->refCount = 1; + + for( p = &gQueryList; *p; p = &( *p )->next ) {} // Find the end of the list. + *p = obj; + + // Set up cancel event + + obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + // Set up events to signal when A record data is ready + + obj->data4Event = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->data4Event, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + // Start the query. Handle delay loaded DLL errors. + + __try + { + err = DNSServiceQueryRecord( &obj->resolver4, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, QueryRecordCallback4, obj ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + + // Attach the socket to the event + + __try + { + s4 = DNSServiceRefSockFD(obj->resolver4); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + s4 = INVALID_SOCKET; + } + + err = translate_errno( s4 != INVALID_SOCKET, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + WSAEventSelect(s4, obj->data4Event, FD_READ|FD_CLOSE); + + // Set up events to signal when AAAA record data is ready + + obj->data6Event = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->data6Event, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + // Start the query. Handle delay loaded DLL errors. + + __try + { + err = DNSServiceQueryRecord( &obj->resolver6, 0, 0, name, kDNSServiceType_AAAA, kDNSServiceClass_IN, QueryRecordCallback6, obj ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + err = kUnknownErr; + } + + require_noerr( err, exit ); + + // Attach the socket to the event + + __try + { + s6 = DNSServiceRefSockFD(obj->resolver6); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + s6 = INVALID_SOCKET; + } + + err = translate_errno( s6 != INVALID_SOCKET, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + WSAEventSelect(s6, obj->data6Event, FD_READ|FD_CLOSE); + + obj->waitCount = 0; + obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent; + obj->waitHandles[ obj->waitCount++ ] = obj->data4Event; + obj->waitHandles[ obj->waitCount++ ] = obj->data6Event; + + check( obj->waitCount == sizeof_array( obj->waitHandles ) ); + + // Copy the QuerySet so it can be returned later. + + obj->querySetFlags = inQuerySetFlags; + inQuerySetFlags = ( inQuerySetFlags & ~( LUP_RETURN_ADDR | LUP_RETURN_BLOB ) ) | LUP_RETURN_NAME; + err = QueryCopyQuerySet( obj, inQuerySet, inQuerySetFlags, &obj->querySet, &obj->querySetSize ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + err = NO_ERROR; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// QueryRetain +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ) +{ + OSStatus err; + QueryRef obj; + + for( obj = gQueryList; obj; obj = obj->next ) + { + if( obj == inRef ) + { + break; + } + } + require_action( obj, exit, err = WSA_INVALID_HANDLE ); + + ++inRef->refCount; + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRelease +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) +{ + OSStatus err; + QueryRef * p; + BOOL ok; + + // Find the item in the list. + + for( p = &gQueryList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + require_action( *p, exit, err = WSA_INVALID_HANDLE ); + + // Signal a cancel to unblock any threads waiting for results. + + if( inRef->cancelEvent ) + { + ok = SetEvent( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + + // Stop the query. + + if( inRef->resolver4 ) + { + __try + { + DNSServiceRefDeallocate( inRef->resolver4 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + inRef->resolver4 = NULL; + } + + if ( inRef->resolver6 ) + { + __try + { + DNSServiceRefDeallocate( inRef->resolver6 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + inRef->resolver6 = NULL; + } + + // Decrement the refCount. Fully release if it drops to 0. If still referenced, just exit. + + if( --inRef->refCount != 0 ) + { + err = NO_ERROR; + goto exit; + } + *p = inRef->next; + + // Release resources. + + if( inRef->cancelEvent ) + { + ok = CloseHandle( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->data4Event ) + { + ok = CloseHandle( inRef->data4Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->data6Event ) + { + ok = CloseHandle( inRef->data6Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->querySet ) + { + free( inRef->querySet ); + } + free( inRef ); + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRecordCallback4 +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback4( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + QueryRef obj; + const char * src; + char * dst; + BOOL ok; + + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inTTL ); + + NSPLock(); + obj = (QueryRef) inContext; + check( obj ); + require_noerr( inErrorCode, exit ); + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require( inRRClass == kDNSServiceClass_IN, exit ); + require( inRRType == kDNSServiceType_A, exit ); + require( inRDataSize == 4, exit ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", + __ROUTINE__, inFlags, inName, inRRType, inRDataSize ); + + // Copy the name if needed. + + if( obj->name[ 0 ] == '\0' ) + { + src = inName; + dst = obj->name; + while( *src != '\0' ) + { + *dst++ = *src++; + } + *dst = '\0'; + obj->nameSize = (size_t)( dst - obj->name ); + check( obj->nameSize < sizeof( obj->name ) ); + } + + // Copy the data. + + memcpy( &obj->addr4, inRData, inRDataSize ); + obj->addr4Valid = true; + obj->numValidAddrs++; + + // Signal that a result is ready. + + check( obj->data4Event ); + ok = SetEvent( obj->data4Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + + // Stop the resolver after the first response. + + __try + { + DNSServiceRefDeallocate( inRef ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + obj->resolver4 = NULL; + +exit: + NSPUnlock(); +} + +#if 0 +#pragma mark - +#endif + + +//=========================================================================================================================== +// QueryRecordCallback6 +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback6( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + QueryRef obj; + const char * src; + char * dst; + BOOL ok; + + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inTTL ); + + NSPLock(); + obj = (QueryRef) inContext; + check( obj ); + require_noerr( inErrorCode, exit ); + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require( inRRClass == kDNSServiceClass_IN, exit ); + require( inRRType == kDNSServiceType_AAAA, exit ); + require( inRDataSize == 16, exit ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", + __ROUTINE__, inFlags, inName, inRRType, inRDataSize ); + + // Copy the name if needed. + + if( obj->name[ 0 ] == '\0' ) + { + src = inName; + dst = obj->name; + while( *src != '\0' ) + { + *dst++ = *src++; + } + *dst = '\0'; + obj->nameSize = (size_t)( dst - obj->name ); + check( obj->nameSize < sizeof( obj->name ) ); + } + + // Copy the data. + + memcpy( &obj->addr6, inRData, inRDataSize ); + + obj->addr6ScopeId = GetScopeId( inInterfaceIndex ); + require( obj->addr6ScopeId, exit ); + obj->addr6Valid = true; + obj->numValidAddrs++; + + // Signal that we're done + + check( obj->data6Event ); + ok = SetEvent( obj->data6Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + + // Stop the resolver after the first response. + + __try + { + DNSServiceRefDeallocate( inRef ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + obj->resolver6 = NULL; + +exit: + + + + NSPUnlock(); +} + + +//=========================================================================================================================== +// QueryCopyQuerySet +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ) +{ + OSStatus err; + size_t size; + WSAQUERYSETW * qs; + + check( inQuerySet ); + check( outQuerySet ); + + size = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); + qs = (WSAQUERYSETW *) calloc( 1, size ); + require_action( qs, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + QueryCopyQuerySetTo( inRef, inQuerySet, inQuerySetFlags, qs ); + + *outQuerySet = qs; + if( outSize ) + { + *outSize = size; + } + qs = NULL; + err = NO_ERROR; + +exit: + if( qs ) + { + free( qs ); + } + return( err ); +} + + + +//=========================================================================================================================== +// QueryCopyQuerySetTo +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ) +{ + uint8_t * dst; + LPCWSTR s; + LPWSTR q; + DWORD n; + DWORD i; + +#if( DEBUG ) + size_t debugSize; + + debugSize = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); +#endif + + check( inQuerySet ); + check( outQuerySet ); + + dst = (uint8_t *) outQuerySet; + + // Copy the static portion of the results. + + *outQuerySet = *inQuerySet; + dst += sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + outQuerySet->lpszServiceInstanceName = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + } + else + { + outQuerySet->lpszServiceInstanceName = NULL; + } + + if( inQuerySet->lpServiceClassId ) + { + outQuerySet->lpServiceClassId = (LPGUID) dst; + *outQuerySet->lpServiceClassId = *inQuerySet->lpServiceClassId; + dst += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + outQuerySet->lpVersion = (LPWSAVERSION) dst; + *outQuerySet->lpVersion = *inQuerySet->lpVersion; + dst += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + outQuerySet->lpszComment = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + if( inQuerySet->lpNSProviderId ) + { + outQuerySet->lpNSProviderId = (LPGUID) dst; + *outQuerySet->lpNSProviderId = *inQuerySet->lpNSProviderId; + dst += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + outQuerySet->lpszContext = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + n = inQuerySet->dwNumberOfProtocols; + + if( n > 0 ) + { + check( inQuerySet->lpafpProtocols ); + + outQuerySet->lpafpProtocols = (LPAFPROTOCOLS) dst; + for( i = 0; i < n; ++i ) + { + outQuerySet->lpafpProtocols[ i ] = inQuerySet->lpafpProtocols[ i ]; + dst += sizeof( *inQuerySet->lpafpProtocols ); + } + } + + s = inQuerySet->lpszQueryString; + if( s ) + { + outQuerySet->lpszQueryString = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + // Copy the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && ( inRef->numValidAddrs > 0 ) ) + { + struct sockaddr_in * addr4; + struct sockaddr_in6 * addr6; + int index; + + outQuerySet->dwNumberOfCsAddrs = inRef->numValidAddrs; + outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; + dst += ( sizeof( *outQuerySet->lpcsaBuffer ) ) * ( inRef->numValidAddrs ) ; + index = 0; + + if ( inRef->addr4Valid ) + { + outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength = 0; + + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); + + addr4 = (struct sockaddr_in *) dst; + memset( addr4, 0, sizeof( *addr4 ) ); + addr4->sin_family = AF_INET; + memcpy( &addr4->sin_addr, &inRef->addr4, 4 ); + dst += sizeof( *addr4 ); + + outQuerySet->lpcsaBuffer[ index ].iSocketType = AF_INET; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ index ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + + index++; + } + + if ( inRef->addr6Valid ) + { + outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength = 0; + + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in6 ); + + addr6 = (struct sockaddr_in6 *) dst; + memset( addr6, 0, sizeof( *addr6 ) ); + addr6->sin6_family = AF_INET6; + addr6->sin6_scope_id = inRef->addr6ScopeId; + memcpy( &addr6->sin6_addr, &inRef->addr6, 16 ); + dst += sizeof( *addr6 ); + + outQuerySet->lpcsaBuffer[ index ].iSocketType = AF_INET6; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ index ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + } + } + else + { + outQuerySet->dwNumberOfCsAddrs = 0; + outQuerySet->lpcsaBuffer = NULL; + } + + // Copy the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) + { + uint8_t * base; + struct hostent * he; + uintptr_t * p; + + outQuerySet->lpBlob = (LPBLOB) dst; + dst += sizeof( *outQuerySet->lpBlob ); + + base = dst; + he = (struct hostent *) dst; + dst += sizeof( *he ); + + he->h_name = (char *)( dst - base ); + memcpy( dst, inRef->name, inRef->nameSize + 1 ); + dst += ( inRef->nameSize + 1 ); + + he->h_aliases = (char **)( dst - base ); + p = (uintptr_t *) dst; + *p++ = 0; + dst = (uint8_t *) p; + + he->h_addrtype = AF_INET; + he->h_length = 4; + + he->h_addr_list = (char **)( dst - base ); + p = (uintptr_t *) dst; + dst += ( 2 * sizeof( *p ) ); + *p++ = (uintptr_t)( dst - base ); + *p++ = 0; + p = (uintptr_t *) dst; + *p++ = (uintptr_t) inRef->addr4; + dst = (uint8_t *) p; + + outQuerySet->lpBlob->cbSize = (ULONG)( dst - base ); + outQuerySet->lpBlob->pBlobData = (BYTE *) base; + } + dlog_query_set( kDebugLevelVerbose, outQuerySet ); + + check( (size_t)( dst - ( (uint8_t *) outQuerySet ) ) == debugSize ); +} + +//=========================================================================================================================== +// QueryCopyQuerySetSize +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ) +{ + size_t size; + LPCWSTR s; + LPCWSTR p; + + check( inRef ); + check( inQuerySet ); + + // Calculate the size of the static portion of the results. + + size = sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + } + + if( inQuerySet->lpServiceClassId ) + { + size += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + size += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + if( inQuerySet->lpNSProviderId ) + { + size += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + size += ( inQuerySet->dwNumberOfProtocols * sizeof( *inQuerySet->lpafpProtocols ) ); + + s = inQuerySet->lpszQueryString; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + // Calculate the size of the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr4Valid ) + { + size += sizeof( *inQuerySet->lpcsaBuffer ); + size += sizeof( struct sockaddr_in ); + } + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr6Valid ) + { + size += sizeof( *inQuerySet->lpcsaBuffer ); + size += sizeof( struct sockaddr_in6 ); + } + + // Calculate the size of the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) + { + size += sizeof( *inQuerySet->lpBlob ); // Blob ptr/size structure + size += sizeof( struct hostent ); // Old-style hostent structure + size += ( inRef->nameSize + 1 ); // Name and null terminator + size += 4; // Alias list terminator (0 offset) + size += 4; // Offset to address. + size += 4; // Address list terminator (0 offset) + size += 4; // IPv4 address + } + return( size ); +} + +#if 0 +#pragma mark - +#endif + +#if( DEBUG ) +//=========================================================================================================================== +// DebugDumpQuerySet +//=========================================================================================================================== + +#define DebugSocketFamilyToString( FAM ) ( ( FAM ) == AF_INET ) ? "AF_INET" : \ + ( ( FAM ) == AF_INET6 ) ? "AF_INET6" : "" + +#define DebugSocketProtocolToString( PROTO ) ( ( PROTO ) == IPPROTO_UDP ) ? "IPPROTO_UDP" : \ + ( ( PROTO ) == IPPROTO_TCP ) ? "IPPROTO_TCP" : "" + +#define DebugNameSpaceToString( NS ) ( ( NS ) == NS_DNS ) ? "NS_DNS" : ( ( NS ) == NS_ALL ) ? "NS_ALL" : "" + +void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ) +{ + DWORD i; + + check( inQuerySet ); + + // Fixed portion of the QuerySet. + + dlog( inLevel, "QuerySet:\n" ); + dlog( inLevel, " dwSize: %d (expected %d)\n", inQuerySet->dwSize, sizeof( *inQuerySet ) ); + if( inQuerySet->lpszServiceInstanceName ) + { + dlog( inLevel, " lpszServiceInstanceName: %S\n", inQuerySet->lpszServiceInstanceName ); + } + else + { + dlog( inLevel, " lpszServiceInstanceName: <null>\n" ); + } + if( inQuerySet->lpServiceClassId ) + { + dlog( inLevel, " lpServiceClassId: %U\n", inQuerySet->lpServiceClassId ); + } + else + { + dlog( inLevel, " lpServiceClassId: <null>\n" ); + } + if( inQuerySet->lpVersion ) + { + dlog( inLevel, " lpVersion:\n" ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->dwVersion ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->ecHow ); + } + else + { + dlog( inLevel, " lpVersion: <null>\n" ); + } + if( inQuerySet->lpszComment ) + { + dlog( inLevel, " lpszComment: %S\n", inQuerySet->lpszComment ); + } + else + { + dlog( inLevel, " lpszComment: <null>\n" ); + } + dlog( inLevel, " dwNameSpace: %d %s\n", inQuerySet->dwNameSpace, + DebugNameSpaceToString( inQuerySet->dwNameSpace ) ); + if( inQuerySet->lpNSProviderId ) + { + dlog( inLevel, " lpNSProviderId: %U\n", inQuerySet->lpNSProviderId ); + } + else + { + dlog( inLevel, " lpNSProviderId: <null>\n" ); + } + if( inQuerySet->lpszContext ) + { + dlog( inLevel, " lpszContext: %S\n", inQuerySet->lpszContext ); + } + else + { + dlog( inLevel, " lpszContext: <null>\n" ); + } + dlog( inLevel, " dwNumberOfProtocols: %d\n", inQuerySet->dwNumberOfProtocols ); + dlog( inLevel, " lpafpProtocols: %s\n", inQuerySet->lpafpProtocols ? "" : "<null>" ); + for( i = 0; i < inQuerySet->dwNumberOfProtocols; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + dlog( inLevel, " iAddressFamily: %d %s\n", inQuerySet->lpafpProtocols[ i ].iAddressFamily, + DebugSocketFamilyToString( inQuerySet->lpafpProtocols[ i ].iAddressFamily ) ); + dlog( inLevel, " iProtocol: %d %s\n", inQuerySet->lpafpProtocols[ i ].iProtocol, + DebugSocketProtocolToString( inQuerySet->lpafpProtocols[ i ].iProtocol ) ); + } + if( inQuerySet->lpszQueryString ) + { + dlog( inLevel, " lpszQueryString: %S\n", inQuerySet->lpszQueryString ); + } + else + { + dlog( inLevel, " lpszQueryString: <null>\n" ); + } + dlog( inLevel, " dwNumberOfCsAddrs: %d\n", inQuerySet->dwNumberOfCsAddrs ); + dlog( inLevel, " lpcsaBuffer: %s\n", inQuerySet->lpcsaBuffer ? "" : "<null>" ); + for( i = 0; i < inQuerySet->dwNumberOfCsAddrs; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].LocalAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " LocalAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " LocalAddr: <null/empty>\n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " RemoteAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " RemoteAddr: <null/empty>\n" ); + } + dlog( inLevel, " iSocketType: %d\n", inQuerySet->lpcsaBuffer[ i ].iSocketType ); + dlog( inLevel, " iProtocol: %d\n", inQuerySet->lpcsaBuffer[ i ].iProtocol ); + } + dlog( inLevel, " dwOutputFlags: %d\n", inQuerySet->dwOutputFlags ); + + // Blob portion of the QuerySet. + + if( inQuerySet->lpBlob ) + { + dlog( inLevel, " lpBlob:\n" ); + dlog( inLevel, " cbSize: %ld\n", inQuerySet->lpBlob->cbSize ); + dlog( inLevel, " pBlobData: %#p\n", inQuerySet->lpBlob->pBlobData ); + dloghex( inLevel, 12, NULL, 0, 0, NULL, 0, + inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->cbSize, + kDebugFlagsNone, NULL, 0 ); + } + else + { + dlog( inLevel, " lpBlob: <null>\n" ); + } +} +#endif + + +//=========================================================================================================================== +// InHostsTable +//=========================================================================================================================== + +DEBUG_LOCAL BOOL +InHostsTable( const char * name ) +{ + HostsFileInfo * node; + BOOL ret = FALSE; + OSStatus err; + + check( name ); + + if ( gHostsFileInfo == NULL ) + { + TCHAR systemDirectory[MAX_PATH]; + TCHAR hFileName[MAX_PATH]; + HostsFile * hFile; + + GetSystemDirectory( systemDirectory, sizeof( systemDirectory ) ); + sprintf( hFileName, "%s\\drivers\\etc\\hosts", systemDirectory ); + err = HostsFileOpen( &hFile, hFileName ); + require_noerr( err, exit ); + + while ( HostsFileNext( hFile, &node ) == 0 ) + { + if ( IsLocalName( node ) ) + { + node->m_next = gHostsFileInfo; + gHostsFileInfo = node; + } + else + { + HostsFileInfoFree( node ); + } + } + + HostsFileClose( hFile ); + } + + for ( node = gHostsFileInfo; node; node = node->m_next ) + { + if ( IsSameName( node, name ) ) + { + ret = TRUE; + break; + } + } + +exit: + + return ret; +} + + +//=========================================================================================================================== +// IsLocalName +//=========================================================================================================================== + +DEBUG_LOCAL BOOL +IsLocalName( HostsFileInfo * node ) +{ + BOOL ret = TRUE; + + check( node ); + + if ( strstr( node->m_host.h_name, ".local" ) == NULL ) + { + int i; + + for ( i = 0; node->m_host.h_aliases[i]; i++ ) + { + if ( strstr( node->m_host.h_aliases[i], ".local" ) ) + { + goto exit; + } + } + + ret = FALSE; + } + +exit: + + return ret; +} + + +//=========================================================================================================================== +// IsSameName +//=========================================================================================================================== + +DEBUG_LOCAL BOOL +IsSameName( HostsFileInfo * node, const char * name ) +{ + BOOL ret = TRUE; + + check( node ); + check( name ); + + if ( strcmp( node->m_host.h_name, name ) != 0 ) + { + int i; + + for ( i = 0; node->m_host.h_aliases[i]; i++ ) + { + if ( strcmp( node->m_host.h_aliases[i], name ) == 0 ) + { + goto exit; + } + } + + ret = FALSE; + } + +exit: + + return ret; +} + + +//=========================================================================================================================== +// HostsFileOpen +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +HostsFileOpen( HostsFile ** self, const char * fname ) +{ + OSStatus err = kNoErr; + + *self = (HostsFile*) malloc( sizeof( HostsFile ) ); + require_action( *self, exit, err = kNoMemoryErr ); + memset( *self, 0, sizeof( HostsFile ) ); + + (*self)->m_bufferSize = BUFFER_INITIAL_SIZE; + (*self)->m_buffer = (char*) malloc( (*self)->m_bufferSize ); + require_action( (*self)->m_buffer, exit, err = kNoMemoryErr ); + + // check malloc + + (*self)->m_fp = fopen( fname, "r" ); + require_action( (*self)->m_fp, exit, err = kUnknownErr ); + +exit: + + if ( err && *self ) + { + HostsFileClose( *self ); + *self = NULL; + } + + return err; +} + + +//=========================================================================================================================== +// HostsFileClose +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +HostsFileClose( HostsFile * self ) +{ + check( self ); + + if ( self->m_buffer ) + { + free( self->m_buffer ); + self->m_buffer = NULL; + } + + if ( self->m_fp ) + { + fclose( self->m_fp ); + self->m_fp = NULL; + } + + free( self ); + + return kNoErr; +} + + +//=========================================================================================================================== +// HostsFileInfoFree +//=========================================================================================================================== + +DEBUG_LOCAL void +HostsFileInfoFree( HostsFileInfo * info ) +{ + while ( info ) + { + HostsFileInfo * next = info->m_next; + + if ( info->m_host.h_addr_list ) + { + if ( info->m_host.h_addr_list[0] ) + { + free( info->m_host.h_addr_list[0] ); + info->m_host.h_addr_list[0] = NULL; + } + + free( info->m_host.h_addr_list ); + info->m_host.h_addr_list = NULL; + } + + if ( info->m_host.h_aliases ) + { + int i; + + for ( i = 0; info->m_host.h_aliases[i]; i++ ) + { + free( info->m_host.h_aliases[i] ); + } + + free( info->m_host.h_aliases ); + } + + if ( info->m_host.h_name ) + { + free( info->m_host.h_name ); + info->m_host.h_name = NULL; + } + + free( info ); + + info = next; + } +} + + +//=========================================================================================================================== +// HostsFileNext +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ) +{ + struct sockaddr_in6 addr_6; + struct sockaddr_in addr_4; + int numAliases = ALIASES_INITIAL_SIZE; + char * line; + char * tok; + int dwSize; + int idx; + int i; + short family; + OSStatus err = kNoErr; + + check( self ); + check( self->m_fp ); + check( hInfo ); + + idx = 0; + + *hInfo = (HostsFileInfo*) malloc( sizeof( HostsFileInfo ) ); + require_action( *hInfo, exit, err = kNoMemoryErr ); + memset( *hInfo, 0, sizeof( HostsFileInfo ) ); + + for ( ; ; ) + { + line = fgets( self->m_buffer + idx, self->m_bufferSize - idx, self->m_fp ); + + if ( line == NULL ) + { + err = 1; + goto exit; + } + + // If there's no eol and no eof, then we didn't get the whole line + + if ( !strchr( line, '\n' ) && !feof( self->m_fp ) ) + { + int bufferSize; + char * buffer; + + /* Try and allocate space for longer line */ + + bufferSize = self->m_bufferSize * 2; + buffer = (char*) realloc( self->m_buffer, bufferSize ); + require_action( buffer, exit, err = kNoMemoryErr ); + self->m_bufferSize = bufferSize; + self->m_buffer = buffer; + idx = (int) strlen( self->m_buffer ); + + continue; + } + + line = self->m_buffer; + idx = 0; + + if (*line == '#') + { + continue; + } + + // Get rid of either comments or eol characters + + if (( tok = strpbrk(line, "#\n")) != NULL ) + { + *tok = '\0'; + } + + // Make sure there is some whitespace on this line + + if (( tok = strpbrk(line, " \t")) == NULL ) + { + continue; + } + + // Create two strings, where p == the IP Address and tok is the name list + + *tok++ = '\0'; + + while ( *tok == ' ' || *tok == '\t') + { + tok++; + } + + // Now we have the name + + (*hInfo)->m_host.h_name = (char*) malloc( strlen( tok ) + 1 ); + require_action( (*hInfo)->m_host.h_name, exit, err = kNoMemoryErr ); + strcpy( (*hInfo)->m_host.h_name, tok ); + + // Now create the address (IPv6/IPv4) + + addr_6.sin6_family = family = AF_INET6; + dwSize = sizeof( addr_6 ); + + if ( WSAStringToAddress( line, AF_INET6, NULL, ( struct sockaddr*) &addr_6, &dwSize ) != 0 ) + { + addr_4.sin_family = family = AF_INET; + dwSize = sizeof( addr_4 ); + + if (WSAStringToAddress( line, AF_INET, NULL, ( struct sockaddr*) &addr_4, &dwSize ) != 0 ) + { + continue; + } + } + + (*hInfo)->m_host.h_addr_list = (char**) malloc( sizeof( char**) * 2 ); + require_action( (*hInfo)->m_host.h_addr_list, exit, err = kNoMemoryErr ); + + if ( family == AF_INET6 ) + { + (*hInfo)->m_host.h_length = (short) sizeof( addr_6.sin6_addr ); + (*hInfo)->m_host.h_addr_list[0] = (char*) malloc( (*hInfo)->m_host.h_length ); + require_action( (*hInfo)->m_host.h_addr_list[0], exit, err = kNoMemoryErr ); + memmove( (*hInfo)->m_host.h_addr_list[0], &addr_6.sin6_addr, sizeof( addr_6.sin6_addr ) ); + + } + else + { + (*hInfo)->m_host.h_length = (short) sizeof( addr_4.sin_addr ); + (*hInfo)->m_host.h_addr_list[0] = (char*) malloc( (*hInfo)->m_host.h_length ); + require_action( (*hInfo)->m_host.h_addr_list[0], exit, err = kNoMemoryErr ); + memmove( (*hInfo)->m_host.h_addr_list[0], &addr_4.sin_addr, sizeof( addr_4.sin_addr ) ); + } + + (*hInfo)->m_host.h_addr_list[1] = NULL; + (*hInfo)->m_host.h_addrtype = family; + + // Now get the aliases + + if ((tok = strpbrk(tok, " \t")) != NULL) + { + *tok++ = '\0'; + } + + i = 0; + + (*hInfo)->m_host.h_aliases = (char**) malloc( sizeof(char**) * numAliases ); + require_action( (*hInfo)->m_host.h_aliases, exit, err = kNoMemoryErr ); + (*hInfo)->m_host.h_aliases[0] = NULL; + + while ( tok && *tok ) + { + // Skip over the whitespace, waiting for the start of the next alias name + + if (*tok == ' ' || *tok == '\t') + { + tok++; + continue; + } + + // Check to make sure we don't exhaust the alias buffer + + if ( i >= ( numAliases - 1 ) ) + { + numAliases = numAliases * 2; + (*hInfo)->m_host.h_aliases = (char**) realloc( (*hInfo)->m_host.h_aliases, numAliases * sizeof( char** ) ); + require_action( (*hInfo)->m_host.h_aliases, exit, err = kNoMemoryErr ); + } + + (*hInfo)->m_host.h_aliases[i] = (char*) malloc( strlen( tok ) + 1 ); + require_action( (*hInfo)->m_host.h_aliases[i], exit, err = kNoMemoryErr ); + + strcpy( (*hInfo)->m_host.h_aliases[i], tok ); + + if (( tok = strpbrk( tok, " \t")) != NULL ) + { + *tok++ = '\0'; + } + + (*hInfo)->m_host.h_aliases[++i] = NULL; + } + + break; + } + +exit: + + if ( err && ( *hInfo ) ) + { + HostsFileInfoFree( *hInfo ); + *hInfo = NULL; + } + + return err; +} + + +#ifdef ENABLE_REVERSE_LOOKUP +//=========================================================================================================================== +// IsReverseLookup +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +IsReverseLookup( LPCWSTR name, size_t size ) +{ + LPCWSTR p; + OSStatus err = kNoErr; + + // IPv6LL Reverse-mapping domains are {8,9,A,B}.E.F.ip6.arpa + require_action_quiet( size > sizeof_string( ".0.8.e.f.ip6.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) : ( ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) + 1 ); + + if ( ( ( p[ 0 ] != '.' ) || + ( ( p[ 1 ] != '0' ) ) || + ( ( p[ 2 ] != '.' ) ) || + ( ( p[ 3 ] != '8' ) ) || + ( ( p[ 4 ] != '.' ) ) || + ( ( p[ 5 ] != 'E' ) && ( p[ 5 ] != 'e' ) ) || + ( ( p[ 6 ] != '.' ) ) || + ( ( p[ 7 ] != 'F' ) && ( p[ 7 ] != 'f' ) ) || + ( ( p[ 8 ] != '.' ) ) || + ( ( p[ 9 ] != 'I' ) && ( p[ 9 ] != 'i' ) ) || + ( ( p[ 10 ] != 'P' ) && ( p[ 10 ] != 'p' ) ) || + ( ( p[ 11 ] != '6' ) ) || + ( ( p[ 12 ] != '.' ) ) || + ( ( p[ 13 ] != 'A' ) && ( p[ 13 ] != 'a' ) ) || + ( ( p[ 14 ] != 'R' ) && ( p[ 14 ] != 'r' ) ) || + ( ( p[ 15 ] != 'P' ) && ( p[ 15 ] != 'p' ) ) || + ( ( p[ 16 ] != 'A' ) && ( p[ 16 ] != 'a' ) ) ) ) + { + require_action_quiet( size > sizeof_string( ".254.169.in-addr.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) : ( ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) + 1 ); + + require_action_quiet( ( ( p[ 0 ] == '.' ) && + ( ( p[ 1 ] == '2' ) ) && + ( ( p[ 2 ] == '5' ) ) && + ( ( p[ 3 ] == '4' ) ) && + ( ( p[ 4 ] == '.' ) ) && + ( ( p[ 5 ] == '1' ) ) && + ( ( p[ 6 ] == '6' ) ) && + ( ( p[ 7 ] == '9' ) ) && + ( ( p[ 8 ] == '.' ) ) && + ( ( p[ 9 ] == 'I' ) || ( p[ 9 ] == 'i' ) ) && + ( ( p[ 10 ] == 'N' ) || ( p[ 10 ] == 'n' ) ) && + ( ( p[ 11 ] == '-' ) ) && + ( ( p[ 12 ] == 'A' ) || ( p[ 12 ] == 'a' ) ) && + ( ( p[ 13 ] == 'D' ) || ( p[ 13 ] == 'd' ) ) && + ( ( p[ 14 ] == 'D' ) || ( p[ 14 ] == 'd' ) ) && + ( ( p[ 15 ] == 'R' ) || ( p[ 15 ] == 'r' ) ) && + ( ( p[ 16 ] == '.' ) ) && + ( ( p[ 17 ] == 'A' ) || ( p[ 17 ] == 'a' ) ) && + ( ( p[ 18 ] == 'R' ) || ( p[ 18 ] == 'r' ) ) && + ( ( p[ 19 ] == 'P' ) || ( p[ 19 ] == 'p' ) ) && + ( ( p[ 20 ] == 'A' ) || ( p[ 20 ] == 'a' ) ) ), + exit, err = WSASERVICE_NOT_FOUND ); + } + + // It's a reverse lookup + + check( err == kNoErr ); + +exit: + + return err; +} +#endif + +//=========================================================================================================================== +// GetScopeId +//=========================================================================================================================== + +DEBUG_LOCAL DWORD +GetScopeId( DWORD ifIndex ) +{ + DWORD err; + int i; + DWORD flags; + struct ifaddrs * head; + struct ifaddrs ** next; + IP_ADAPTER_ADDRESSES * iaaList; + ULONG iaaListSize; + IP_ADAPTER_ADDRESSES * iaa; + DWORD scopeId = 0; + + head = NULL; + next = &head; + iaaList = NULL; + + require( gGetAdaptersAddressesFunctionPtr, exit ); + + // Get the list of interfaces. The first call gets the size and the second call gets the actual data. + // This loops to handle the case where the interface changes in the window after getting the size, but before the + // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong. + + flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + i = 0; + for( ;; ) + { + iaaListSize = 0; + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize ); + check( err == ERROR_BUFFER_OVERFLOW ); + check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) ); + + iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize ); + require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY ); + + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize ); + if( err == ERROR_SUCCESS ) break; + + free( iaaList ); + iaaList = NULL; + ++i; + require( i < 100, exit ); + dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err ); + } + + for( iaa = iaaList; iaa; iaa = iaa->Next ) + { + DWORD ipv6IfIndex; + + if ( iaa->IfIndex > 0xFFFFFF ) + { + continue; + } + if ( iaa->Ipv6IfIndex > 0xFF ) + { + continue; + } + + // For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the + // following code to crash when iterating through the prefix list. This seems + // to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host. + // This shouldn't happen according to Microsoft docs which states: + // + // "Ipv6IfIndex contains 0 if IPv6 is not available on the interface." + // + // So the data structure seems to be corrupted when we return from + // GetAdaptersAddresses(). The bug seems to occur when iaa->Length < + // sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually + // modify iaa to have the correct values. + + if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) ) + { + ipv6IfIndex = iaa->Ipv6IfIndex; + } + else + { + ipv6IfIndex = 0; + } + + // Skip psuedo and tunnel interfaces. + + if( ( ipv6IfIndex == 1 ) || ( iaa->IfType == IF_TYPE_TUNNEL ) ) + { + continue; + } + + if ( iaa->IfIndex == ifIndex ) + { + scopeId = iaa->Ipv6IfIndex; + break; + } + } + +exit: + + if( iaaList ) + { + free( iaaList ); + } + + return scopeId; +} |