]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSWindows/mdnsNSP/mdnsNSP.c
mDNSResponder-170.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / mdnsNSP / mdnsNSP.c
index 34311f119f4e9d271e0375c78c9396162071fbf5..db0d802f9188411cb586f0329514c80b62ed895e 100644 (file)
@@ -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
+<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
@@ -53,6 +91,7 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to
 
 */
 
+
 #include       <stdio.h>
 #include       <stdlib.h>
 #include       <string.h>
@@ -60,11 +99,22 @@ mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to
 #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
@@ -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 );
+
+               // <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();
@@ -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;
+}