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