X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/7f0064bd55e3fa98568d2c359429ff8a38b23a6c..030b743d04071f8345b376fca3677ecaddb166c6:/mDNSWindows/mdnsNSP/mdnsNSP.c diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.c b/mDNSWindows/mdnsNSP/mdnsNSP.c index 34311f1..db0d802 100644 --- a/mDNSWindows/mdnsNSP/mdnsNSP.c +++ b/mDNSWindows/mdnsNSP/mdnsNSP.c @@ -1,28 +1,66 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * 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 * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * http://www.apache.org/licenses/LICENSE-2.0 * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * 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. - * - * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: mdnsNSP.c,v $ +Revision 1.20 2006/08/14 23:26:10 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.19 2006/06/08 23:09:37 cheshire +Updated comment: Correct IPv6LL reverse-mapping domains are '{8,9,A,B}.E.F.ip6.arpa.', +not only '0.8.E.F.ip6.arpa.' (Actual code still needs to be fixed.) + +Revision 1.18 2005/10/17 05:45:36 herscher +Fix typo in previous checkin + +Revision 1.17 2005/10/17 05:30:00 herscher + NSP should handle IPv6 AAAA queries for dot-local names + +Revision 1.16 2005/09/16 22:22:48 herscher + No longer set the PATH variable when NSP is loaded. + +Revision 1.15 2005/07/14 22:12:00 shersche + Delay load dnssd.dll so that it gracefully handles library loading problems immediately after installing Bonjour + +Revision 1.14 2005/03/29 20:35:28 shersche + Remove reverse lookup implementation due to NSP framework limitation + +Revision 1.13 2005/03/29 19:42:47 shersche +Do label check before checking etc/hosts file + +Revision 1.12 2005/03/21 00:42:45 shersche + Fix build warnings on Win32 platform + +Revision 1.11 2005/03/16 03:04:51 shersche + Don't issue multicast query multilabel dot-local names + +Revision 1.10 2005/02/23 22:16:07 shersche +Unregister the NSP before registering to workaround an installer problem during upgrade installs + +Revision 1.9 2005/02/01 01:45:55 shersche +Change mdnsNSP timeout to 2 seconds + +Revision 1.8 2005/01/31 23:27:25 shersche + Don't try and resolve .local hostnames that are referenced in the hosts file + +Revision 1.7 2005/01/28 23:50:13 shersche + Implement DllRegisterServer,DllUnregisterServer so mdnsNSP.dll can self-register +Bug #: 3942551 + Revision 1.6 2004/12/06 01:56:53 shersche Use the DNS types and classes defined in dns_sd.h Bug #: 3789425 @@ -53,6 +91,7 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to */ + #include #include #include @@ -60,11 +99,22 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to #include "CommonServices.h" #include "DebugServices.h" +#include #include #include +#include + + #include "dns_sd.h" +#pragma comment(lib, "DelayImp.lib") + +#ifdef _MSC_VER +#define swprintf _snwprintf +#define snprintf _snprintf +#endif + #if 0 #pragma mark == Structures == #endif @@ -82,17 +132,41 @@ struct Query DWORD querySetFlags; WSAQUERYSETW * querySet; size_t querySetSize; - HANDLE dataEvent; + HANDLE data4Event; + HANDLE data6Event; HANDLE cancelEvent; - HANDLE waitHandles[ 2 ]; + HANDLE waitHandles[ 3 ]; DWORD waitCount; - DNSServiceRef resolver; + DNSServiceRef resolver4; + DNSServiceRef resolver6; char name[ kDNSServiceMaxDomainName ]; size_t nameSize; - uint32_t addr; - bool addrValid; + 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 @@ -104,7 +178,10 @@ struct Query // DLL Exports BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); +STDAPI DllRegisterServer( void ); +STDAPI DllRegisterServer( void ); + // NSP SPIs int WSPAPI NSPCleanup( LPGUID inProviderID ); @@ -148,7 +225,21 @@ DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ); DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ); DEBUG_LOCAL void CALLBACK_COMPAT - QueryRecordCallback( + 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, @@ -186,6 +277,21 @@ DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *in #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 const char * GetNextLabel( const char *cstr, char label[64] ); +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 @@ -195,13 +301,26 @@ DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *in //=========================================================================================================================== // {B600E6E9-553B-4a19-8696-335E5C896153} -// GUID kmdnsNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; - +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 bool gDNSSDInitialized = 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 - @@ -215,17 +334,22 @@ 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, kDebugLevelInfo ); + 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; @@ -241,9 +365,72 @@ BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) 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, sizeof( 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 // @@ -284,6 +471,18 @@ int WSPAPI NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines ) 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: @@ -333,17 +532,20 @@ int WSPAPI NSPCleanup( LPGUID inProviderID ) NSPUnlock(); } - // Shut down DNS-SD and release our resources. - - if( gDNSSDInitialized ) - { - gDNSSDInitialized = false; - } 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() ); @@ -434,66 +636,55 @@ DEBUG_LOCAL int WSPAPI ( ( p[ 4 ] != 'A' ) && ( p[ 4 ] != 'a' ) ) || ( ( p[ 5 ] != 'L' ) && ( p[ 5 ] != 'l' ) ) ) ) { - 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' ) ) ) ) +#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[128]; + char text[64]; + + n = WideCharToMultiByte( CP_UTF8, 0, name, -1, translated, sizeof( translated ), NULL, NULL ); + require_action( n > 0, exit, err = WSASERVICE_NOT_FOUND ); + + // + + // Don't resolve multi-label name + + replyDomain = translated; + + while ( *replyDomain ) { - 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 ); + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); } + + require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND ); + + // + // + // 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, .0.8.e.f.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed. + // The name ends in .local ( and isn't in the hosts table ), {8,9,A,B}.E.F.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed. NSPLock(); - if( !gDNSSDInitialized ) - { - gDNSSDInitialized = true; - } err = QueryCreate( inQuerySet, inFlags, &obj ); NSPUnlock(); @@ -526,6 +717,8 @@ DEBUG_LOCAL int WSPAPI LPDWORD ioSize, LPWSAQUERYSETW outResults ) { + BOOL data4; + BOOL data6; OSStatus err; QueryRef obj; DWORD waitResult; @@ -535,6 +728,8 @@ DEBUG_LOCAL int WSPAPI dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + data4 = FALSE; + data6 = FALSE; obj = NULL; NSPLock(); err = QueryRetain( (QueryRef) inLookup ); @@ -548,14 +743,58 @@ DEBUG_LOCAL int WSPAPI // 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, 5 * 1000 ); + waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 2 * 1000 ); NSPLock(); - require_action_quiet( waitResult != ( WAIT_OBJECT_0 + 1 ), exit, err = WSA_E_CANCELLED ); - err = translate_errno( waitResult == WAIT_OBJECT_0, (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); + 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 ); - DNSServiceProcessResult(obj->resolver); - require_action_quiet( obj->addrValid, exit, err = WSA_E_NO_MORE ); - + + // 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 ); @@ -563,8 +802,9 @@ DEBUG_LOCAL int WSPAPI QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults ); outResults->dwOutputFlags = RESULT_IS_ADDED; - obj->addrValid = false; - + obj->addr4Valid = false; + obj->addr6Valid = false; + exit: if( obj ) { @@ -726,7 +966,9 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS char name[ kDNSServiceMaxDomainName ]; int n; QueryRef * p; - + SOCKET s4; + SOCKET s6; + obj = NULL; check( inQuerySet ); check( inQuerySet->lpszServiceInstanceName ); @@ -748,27 +990,84 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS for( p = &gQueryList; *p; p = &( *p )->next ) {} // Find the end of the list. *p = obj; - // Set up events to signal when data is ready and when cancelling. - - obj->dataEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); - require_action( obj->dataEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); - + // 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. + // 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; + } - err = DNSServiceQueryRecord( &obj->resolver, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, - QueryRecordCallback, obj ); require_noerr( err, exit ); // Attach the socket to the event - WSAEventSelect(DNSServiceRefSockFD(obj->resolver), obj->dataEvent, FD_READ|FD_CLOSE); + __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->dataEvent; 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. @@ -852,10 +1151,30 @@ DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) // Stop the query. - if( inRef->resolver ) + if( inRef->resolver4 ) + { + __try + { + DNSServiceRefDeallocate( inRef->resolver4 ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + inRef->resolver4 = NULL; + } + + if ( inRef->resolver6 ) { - DNSServiceRefDeallocate( inRef->resolver ); - inRef->resolver = NULL; + __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. @@ -874,9 +1193,14 @@ DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) ok = CloseHandle( inRef->cancelEvent ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); } - if( inRef->dataEvent ) + if( inRef->data4Event ) + { + ok = CloseHandle( inRef->data4Event ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->data6Event ) { - ok = CloseHandle( inRef->dataEvent ); + ok = CloseHandle( inRef->data6Event ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); } if( inRef->querySet ) @@ -891,11 +1215,11 @@ exit: } //=========================================================================================================================== -// QueryRecordCallback +// QueryRecordCallback4 //=========================================================================================================================== DEBUG_LOCAL void CALLBACK_COMPAT - QueryRecordCallback( + QueryRecordCallback4( DNSServiceRef inRef, DNSServiceFlags inFlags, uint32_t inInterfaceIndex, @@ -946,19 +1270,27 @@ DEBUG_LOCAL void CALLBACK_COMPAT // Copy the data. - memcpy( &obj->addr, inRData, inRDataSize ); - obj->addrValid = true; + memcpy( &obj->addr4, inRData, inRDataSize ); + obj->addr4Valid = true; + obj->numValidAddrs++; // Signal that a result is ready. - check( obj->dataEvent ); - ok = SetEvent( obj->dataEvent ); + check( obj->data4Event ); + ok = SetEvent( obj->data4Event ); check_translated_errno( ok, GetLastError(), WSAEINVAL ); // Stop the resolver after the first response. - DNSServiceRefDeallocate( inRef ); - obj->resolver = NULL; + __try + { + DNSServiceRefDeallocate( inRef ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + obj->resolver4 = NULL; exit: NSPUnlock(); @@ -968,26 +1300,116 @@ exit: #pragma mark - #endif + //=========================================================================================================================== -// QueryCopyQuerySet -// -// Warning: Assumes the NSP lock is held. +// QueryRecordCallback6 //=========================================================================================================================== -DEBUG_LOCAL OSStatus - QueryCopyQuerySet( - QueryRef inRef, - const WSAQUERYSETW * inQuerySet, - DWORD inQuerySetFlags, - WSAQUERYSETW ** outQuerySet, - size_t * outSize ) +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 ) { - OSStatus err; - size_t size; - WSAQUERYSETW * qs; + QueryRef obj; + const char * src; + char * dst; + BOOL ok; - check( inQuerySet ); - check( outQuerySet ); + 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 ); @@ -1011,6 +1433,8 @@ exit: return( err ); } + + //=========================================================================================================================== // QueryCopyQuerySetTo // @@ -1102,6 +1526,7 @@ DEBUG_LOCAL void } n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) { check( inQuerySet->lpafpProtocols ); @@ -1125,28 +1550,55 @@ DEBUG_LOCAL void // Copy the address(es). - if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && ( inRef->numValidAddrs > 0 ) ) { - struct sockaddr_in * addr; + struct sockaddr_in * addr4; + struct sockaddr_in6 * addr6; + int index; - outQuerySet->dwNumberOfCsAddrs = 1; - outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; - dst += sizeof( *outQuerySet->lpcsaBuffer ); + outQuerySet->dwNumberOfCsAddrs = inRef->numValidAddrs; + outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; + dst += ( sizeof( *outQuerySet->lpcsaBuffer ) ) * ( inRef->numValidAddrs ) ; + index = 0; - outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.lpSockaddr = NULL; - outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.iSockaddrLength = 0; + if ( inRef->addr4Valid ) + { + outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength = 0; - outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; - outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); - addr = (struct sockaddr_in *) dst; - memset( addr, 0, sizeof( *addr ) ); - addr->sin_family = AF_INET; - memcpy( &addr->sin_addr, &inRef->addr, 4 ); - dst += sizeof( *addr ); + 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[ 0 ].iSocketType = AF_INET; // Emulate Tcpip NSP - outQuerySet->lpcsaBuffer[ 0 ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + 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 { @@ -1156,12 +1608,12 @@ DEBUG_LOCAL void // Copy the hostent blob. - if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) { uint8_t * base; struct hostent * he; uintptr_t * p; - + outQuerySet->lpBlob = (LPBLOB) dst; dst += sizeof( *outQuerySet->lpBlob ); @@ -1187,14 +1639,14 @@ DEBUG_LOCAL void *p++ = (uintptr_t)( dst - base ); *p++ = 0; p = (uintptr_t *) dst; - *p++ = (uintptr_t) inRef->addr; + *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 ); } @@ -1267,15 +1719,21 @@ DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *in // Calculate the size of the address(es). - if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + 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->addrValid ) + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid ) { size += sizeof( *inQuerySet->lpBlob ); // Blob ptr/size structure size += sizeof( struct hostent ); // Old-style hostent structure @@ -1438,3 +1896,636 @@ void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ) } } #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; +} + + +//=========================================================================================================================== +// GetNextLabel +//=========================================================================================================================== +DEBUG_LOCAL const char* +GetNextLabel(const char *cstr, char label[64]) +{ + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\') + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; + if (ptr >= label+64) return(NULL); + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + *ptr++ = 0; + return(cstr); +} + + +#ifdef ENABLE_REVERSE_LOOKUP +//=========================================================================================================================== +// IsReverseLookup +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus +IsReverseLookup( LPCWSTR name, size_t size ) +{ + LPCWSTR p; + OSStatus err = kNoErr; + + 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; +}