-/*
+/* -*- 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
+<rdar://problem/4071610> NSP should handle IPv6 AAAA queries for dot-local names
+
+Revision 1.16 2005/09/16 22:22:48 herscher
+<rdar://problem/4261460> No longer set the PATH variable when NSP is loaded.
+
+Revision 1.15 2005/07/14 22:12:00 shersche
+<rdar://problem/4178448> 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
+<rdar://problem/4053899> 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
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.11 2005/03/16 03:04:51 shersche
+<rdar://problem/4050633> 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
+<rdar://problem/3936771> Don't try and resolve .local hostnames that are referenced in the hosts file
+
+Revision 1.7 2005/01/28 23:50:13 shersche
+<rdar://problem/3942551> Implement DllRegisterServer,DllUnregisterServer so mdnsNSP.dll can self-register
+Bug #: 3942551
+
Revision 1.6 2004/12/06 01:56:53 shersche
<rdar://problem/3789425> Use the DNS types and classes defined in dns_sd.h
Bug #: 3789425
*/
+
#include <stdio.h>
#include <stdlib.h>
#include <string.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
+
#if 0
#pragma mark == Structures ==
#endif
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
// 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 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,
#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
//===========================================================================================================================
// {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 -
{
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;
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
//
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:
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() );
( ( 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 );
+
+ // <rdar://problem/4050633>
+
+ // 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 );
+
+ // <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, .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();
LPDWORD ioSize,
LPWSAQUERYSETW outResults )
{
+ BOOL data4;
+ BOOL data6;
OSStatus err;
QueryRef obj;
DWORD waitResult;
dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+ data4 = FALSE;
+ data6 = FALSE;
obj = NULL;
NSPLock();
err = QueryRetain( (QueryRef) inLookup );
// 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 );
QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults );
outResults->dwOutputFlags = RESULT_IS_ADDED;
- obj->addrValid = false;
-
+ obj->addr4Valid = false;
+ obj->addr6Valid = false;
+
exit:
if( obj )
{
char name[ kDNSServiceMaxDomainName ];
int n;
QueryRef * p;
-
+ SOCKET s4;
+ SOCKET s6;
+
obj = NULL;
check( inQuerySet );
check( inQuerySet->lpszServiceInstanceName );
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.
// 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.
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 )
}
//===========================================================================================================================
-// QueryRecordCallback
+// QueryRecordCallback4
//===========================================================================================================================
DEBUG_LOCAL void CALLBACK_COMPAT
- QueryRecordCallback(
+ QueryRecordCallback4(
DNSServiceRef inRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
// 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();
#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 );
return( err );
}
+
+
//===========================================================================================================================
// QueryCopyQuerySetTo
//
}
n = inQuerySet->dwNumberOfProtocols;
+
if( n > 0 )
{
check( inQuerySet->lpafpProtocols );
// 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
{
// 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 );
*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 );
}
// 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
}
}
#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;
+}