You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2431 lines
67 KiB
2431 lines
67 KiB
/* -*- 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;
|
|
}
|