blob: 0dcc1217a562007188ce4ad84e2496b73fc98e4f [file] [log] [blame]
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002-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.
To Do:
- Get unicode name of machine for nice name instead of just the host name.
- Use the IPv6 Internet Connection Firewall API to allow IPv6 mDNS without manually changing the firewall.
- Get DNS server address(es) from Windows and provide them to the uDNS layer.
- Implement TCP support for truncated packets (only stubs now).
*/
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <crtdbg.h>
#include <string.h>
#include "CommonServices.h"
#include "DebugServices.h"
#include "Firewall.h"
#include "RegNames.h"
#include "Secret.h"
#include <dns_sd.h>
#include <Iphlpapi.h>
#include <mswsock.h>
#include <process.h>
#include <ntsecapi.h>
#include <lm.h>
#include <winioctl.h>
#include <ntddndis.h> // This defines the IOCTL constants.
#include "mDNSEmbeddedAPI.h"
#include "GenLinkedList.h"
#include "DNSCommon.h"
#include "mDNSWin32.h"
#if 0
#pragma mark == Constants ==
#endif
//===========================================================================================================================
// Constants
//===========================================================================================================================
#define DEBUG_NAME "[mDNSWin32] "
#define MDNS_WINDOWS_USE_IPV6_IF_ADDRS 1
#define MDNS_WINDOWS_ENABLE_IPV4 1
#define MDNS_WINDOWS_ENABLE_IPV6 1
#define MDNS_FIX_IPHLPAPI_PREFIX_BUG 1
#define MDNS_SET_HINFO_STRINGS 0
#define kMDNSDefaultName "My Computer"
#define kWinSockMajorMin 2
#define kWinSockMinorMin 2
#define kRegistryMaxKeyLength 255
#define kRegistryMaxValueName 16383
static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG;
#define kIPv6IfIndexBase (10000000L)
#define SMBPortAsNumber 445
#define DEVICE_PREFIX "\\\\.\\"
#if 0
#pragma mark == Prototypes ==
#endif
//===========================================================================================================================
// Prototypes
//===========================================================================================================================
mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS );
mDNSlocal mStatus SetupHostName( mDNS * const inMDNS );
mDNSlocal mStatus SetupName( mDNS * const inMDNS );
mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD );
mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD );
mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD );
mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef );
mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort );
mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize );
mDNSlocal int getifaddrs( struct ifaddrs **outAddrs );
mDNSlocal void freeifaddrs( struct ifaddrs *inAddrs );
// Platform Accessors
#ifdef __cplusplus
extern "C" {
#endif
typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
struct mDNSPlatformInterfaceInfo
{
const char * name;
mDNSAddr ip;
};
mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
// Utilities
#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs );
#endif
mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs );
mDNSlocal DWORD GetPrimaryInterface();
mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask );
mDNSlocal mDNSBool CanReceiveUnicast( void );
mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr );
mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string );
mDNSlocal mStatus RegQueryString( HKEY key, LPCSTR param, LPSTR * string, DWORD * stringLen, DWORD * enabled );
mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh);
mDNSlocal OSStatus TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize );
mDNSlocal OSStatus WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize );
mDNSlocal void TCPDidConnect( mDNS * const inMDNS, HANDLE event, void * context );
mDNSlocal void TCPCanRead( TCPSocket * sock );
mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock );
mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
mDNSlocal void TCPCloseSocket( TCPSocket * socket );
mDNSlocal void CALLBACK TCPFreeSocket( TCPSocket *sock );
mDNSlocal OSStatus UDPBeginRecv( UDPSocket * socket );
mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
mDNSlocal void UDPCloseSocket( UDPSocket * sock );
mDNSlocal void CALLBACK UDPFreeSocket( UDPSocket * sock );
mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa);
mDNSlocal void GetDDNSFQDN( domainname *const fqdn );
#ifdef UNICODE
mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey );
#else
mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCSTR lpSubKey );
#endif
mDNSlocal void SetDomainSecrets( mDNS * const inMDNS );
mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain );
mDNSlocal VOID CALLBACK CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue );
mDNSlocal void CheckFileShares( mDNS * const inMDNS );
mDNSlocal void SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName );
mDNSlocal void DispatchUDPEvent( mDNS * const m, UDPSocket * sock );
mDNSlocal void DispatchTCPEvent( mDNS * const m, TCPSocket * sock );
#ifdef __cplusplus
}
#endif
#if 0
#pragma mark == Globals ==
#endif
//===========================================================================================================================
// Globals
//===========================================================================================================================
mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
mDNSs32 mDNSPlatformOneSecond = 0;
mDNSlocal UDPSocket * gUDPSockets = NULL;
mDNSlocal int gUDPNumSockets = 0;
mDNSlocal GenLinkedList gUDPDispatchableSockets;
mDNSlocal GenLinkedList gTCPDispatchableSockets;
#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
typedef DWORD
( WINAPI * GetAdaptersAddressesFunctionPtr )(
ULONG inFamily,
DWORD inFlags,
PVOID inReserved,
PIP_ADAPTER_ADDRESSES inAdapter,
PULONG outBufferSize );
mDNSlocal HMODULE gIPHelperLibraryInstance = NULL;
mDNSlocal GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL;
#endif
#ifndef HCRYPTPROV
typedef ULONG_PTR HCRYPTPROV; // WinCrypt.h, line 249
#endif
#ifndef CRYPT_MACHINE_KEYSET
# define CRYPT_MACHINE_KEYSET 0x00000020
#endif
#ifndef CRYPT_NEWKEYSET
# define CRYPT_NEWKEYSET 0x00000008
#endif
#ifndef PROV_RSA_FULL
# define PROV_RSA_FULL 1
#endif
typedef BOOL (__stdcall *fnCryptGenRandom)( HCRYPTPROV, DWORD, BYTE* );
typedef BOOL (__stdcall *fnCryptAcquireContext)( HCRYPTPROV*, LPCTSTR, LPCTSTR, DWORD, DWORD);
typedef BOOL (__stdcall *fnCryptReleaseContext)(HCRYPTPROV, DWORD);
static fnCryptAcquireContext g_lpCryptAcquireContext = NULL;
static fnCryptReleaseContext g_lpCryptReleaseContext = NULL;
static fnCryptGenRandom g_lpCryptGenRandom = NULL;
static HINSTANCE g_hAAPI32 = NULL;
static HCRYPTPROV g_hProvider = ( ULONG_PTR ) NULL;
typedef DNSServiceErrorType ( DNSSD_API *DNSServiceRegisterFunc )
(
DNSServiceRef *sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
const char *name, /* may be NULL */
const char *regtype,
const char *domain, /* may be NULL */
const char *host, /* may be NULL */
uint16_t port,
uint16_t txtLen,
const void *txtRecord, /* may be NULL */
DNSServiceRegisterReply callBack, /* may be NULL */
void *context /* may be NULL */
);
typedef void ( DNSSD_API *DNSServiceRefDeallocateFunc )( DNSServiceRef sdRef );
mDNSlocal HMODULE gDNSSDLibrary = NULL;
mDNSlocal DNSServiceRegisterFunc gDNSServiceRegister = NULL;
mDNSlocal DNSServiceRefDeallocateFunc gDNSServiceRefDeallocate = NULL;
mDNSlocal HANDLE gSMBThread = NULL;
mDNSlocal HANDLE gSMBThreadRegisterEvent = NULL;
mDNSlocal HANDLE gSMBThreadDeregisterEvent = NULL;
mDNSlocal HANDLE gSMBThreadStopEvent = NULL;
mDNSlocal HANDLE gSMBThreadQuitEvent = NULL;
#define kSMBStopEvent ( WAIT_OBJECT_0 + 0 )
#define kSMBRegisterEvent ( WAIT_OBJECT_0 + 1 )
#define kSMBDeregisterEvent ( WAIT_OBJECT_0 + 2 )
#if 0
#pragma mark -
#pragma mark == Platform Support ==
#endif
//===========================================================================================================================
// mDNSPlatformInit
//===========================================================================================================================
mDNSexport mStatus mDNSPlatformInit( mDNS * const inMDNS )
{
mStatus err;
WSADATA wsaData;
int supported;
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
int sa4len;
int sa6len;
DWORD size;
DWORD val;
dlog( kDebugLevelTrace, DEBUG_NAME "platform init\n" );
// Initialize variables. If the PlatformSupport pointer is not null then just assume that a non-Apple client is
// calling mDNS_Init and wants to provide its own storage for the platform-specific data so do not overwrite it.
mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) );
if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport;
inMDNS->p->mainThread = OpenThread( THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId() );
require_action( inMDNS->p->mainThread, exit, err = mStatus_UnknownErr );
inMDNS->p->checkFileSharesTimer = CreateWaitableTimer( NULL, FALSE, NULL );
require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr );
inMDNS->p->checkFileSharesTimeout = 10; // Retry time for CheckFileShares() in seconds
mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time
InitLinkedList( &gTCPDispatchableSockets, offsetof( TCPSocket, nextDispatchable ) );
InitLinkedList( &gUDPDispatchableSockets, offsetof( UDPSocket, nextDispatchable ) );
// Startup WinSock 2.2 or later.
err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData );
require_noerr( err, exit );
supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) );
require_action( supported, exit, err = mStatus_UnsupportedErr );
inMDNS->CanReceiveUnicastOn5353 = CanReceiveUnicast();
// Setup the HINFO HW strings.
//<rdar://problem/7245119> device-info should have model=Windows
strcpy_s( ( char* ) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2, "Windows" );
inMDNS->HIHardware.c[ 0 ] = ( mDNSu8 ) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] );
dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c );
// Setup the HINFO SW strings.
#if ( MDNS_SET_HINFO_STRINGS )
mDNS_snprintf( (char *) &inMDNS->HISoftware.c[ 1 ], sizeof( inMDNS->HISoftware.c ) - 2,
"mDNSResponder (%s %s)", __DATE__, __TIME__ );
inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] );
dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c );
#endif
// Set the thread global overlapped flag
val = 0;
err = setsockopt( INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, ( char* ) &val, sizeof( val ) );
err = translate_errno( err != SOCKET_ERROR, WSAGetLastError(), kUnknownErr );
require_noerr( err, exit );
// Set up the IPv4 unicast socket
inMDNS->p->unicastSock4.fd = INVALID_SOCKET;
inMDNS->p->unicastSock4.recvMsgPtr = NULL;
inMDNS->p->unicastSock4.ifd = NULL;
inMDNS->p->unicastSock4.overlapped.pending = FALSE;
inMDNS->p->unicastSock4.next = NULL;
inMDNS->p->unicastSock4.m = inMDNS;
#if ( MDNS_WINDOWS_ENABLE_IPV4 )
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = INADDR_ANY;
err = SetupSocket( inMDNS, (const struct sockaddr*) &sa4, zeroIPPort, &inMDNS->p->unicastSock4.fd );
check_noerr( err );
sa4len = sizeof( sa4 );
err = getsockname( inMDNS->p->unicastSock4.fd, (struct sockaddr*) &sa4, &sa4len );
require_noerr( err, exit );
inMDNS->p->unicastSock4.port.NotAnInteger = sa4.sin_port;
inMDNS->UnicastPort4 = inMDNS->p->unicastSock4.port;
err = WSAIoctl( inMDNS->p->unicastSock4.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4.recvMsgPtr, sizeof( inMDNS->p->unicastSock4.recvMsgPtr ), &size, NULL, NULL );
if ( err )
{
inMDNS->p->unicastSock4.recvMsgPtr = NULL;
}
err = UDPBeginRecv( &inMDNS->p->unicastSock4 );
require_noerr( err, exit );
#endif
// Set up the IPv6 unicast socket
inMDNS->p->unicastSock6.fd = INVALID_SOCKET;
inMDNS->p->unicastSock6.recvMsgPtr = NULL;
inMDNS->p->unicastSock6.ifd = NULL;
inMDNS->p->unicastSock6.overlapped.pending = FALSE;
inMDNS->p->unicastSock6.next = NULL;
inMDNS->p->unicastSock6.m = inMDNS;
#if ( MDNS_WINDOWS_ENABLE_IPV6 )
sa6.sin6_family = AF_INET6;
sa6.sin6_addr = in6addr_any;
sa6.sin6_scope_id = 0;
// This call will fail if the machine hasn't installed IPv6. In that case,
// the error will be WSAEAFNOSUPPORT.
err = SetupSocket( inMDNS, (const struct sockaddr*) &sa6, zeroIPPort, &inMDNS->p->unicastSock6.fd );
require_action( !err || ( err == WSAEAFNOSUPPORT ), exit, err = (mStatus) WSAGetLastError() );
err = kNoErr;
// If we weren't able to create the socket (because IPv6 hasn't been installed) don't do this
if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET )
{
sa6len = sizeof( sa6 );
err = getsockname( inMDNS->p->unicastSock6.fd, (struct sockaddr*) &sa6, &sa6len );
require_noerr( err, exit );
inMDNS->p->unicastSock6.port.NotAnInteger = sa6.sin6_port;
inMDNS->UnicastPort6 = inMDNS->p->unicastSock6.port;
err = WSAIoctl( inMDNS->p->unicastSock6.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock6.recvMsgPtr, sizeof( inMDNS->p->unicastSock6.recvMsgPtr ), &size, NULL, NULL );
if ( err != 0 )
{
inMDNS->p->unicastSock6.recvMsgPtr = NULL;
}
err = UDPBeginRecv( &inMDNS->p->unicastSock6 );
require_noerr( err, exit );
}
#endif
// Notify core of domain secret keys
SetDomainSecrets( inMDNS );
// Success!
mDNSCoreInitComplete( inMDNS, err );
exit:
if ( err )
{
mDNSPlatformClose( inMDNS );
}
dlog( kDebugLevelTrace, DEBUG_NAME "platform init done (err=%d %m)\n", err, err );
return( err );
}
//===========================================================================================================================
// mDNSPlatformClose
//===========================================================================================================================
mDNSexport void mDNSPlatformClose( mDNS * const inMDNS )
{
mStatus err;
dlog( kDebugLevelTrace, DEBUG_NAME "platform close\n" );
check( inMDNS );
if ( gSMBThread != NULL )
{
dlog( kDebugLevelTrace, DEBUG_NAME "tearing down smb registration thread\n" );
SetEvent( gSMBThreadStopEvent );
if ( WaitForSingleObject( gSMBThreadQuitEvent, 5 * 1000 ) == WAIT_OBJECT_0 )
{
if ( gSMBThreadQuitEvent )
{
CloseHandle( gSMBThreadQuitEvent );
gSMBThreadQuitEvent = NULL;
}
if ( gSMBThreadStopEvent )
{
CloseHandle( gSMBThreadStopEvent );
gSMBThreadStopEvent = NULL;
}
if ( gSMBThreadDeregisterEvent )
{
CloseHandle( gSMBThreadDeregisterEvent );
gSMBThreadDeregisterEvent = NULL;
}
if ( gSMBThreadRegisterEvent )
{
CloseHandle( gSMBThreadRegisterEvent );
gSMBThreadRegisterEvent = NULL;
}
if ( gDNSSDLibrary )
{
FreeLibrary( gDNSSDLibrary );
gDNSSDLibrary = NULL;
}
}
else
{
LogMsg( "Unable to stop SMBThread" );
}
inMDNS->p->smbFileSharing = mDNSfalse;
inMDNS->p->smbPrintSharing = mDNSfalse;
}
// Tear everything down in reverse order to how it was set up.
err = TearDownInterfaceList( inMDNS );
check_noerr( err );
check( !inMDNS->p->inactiveInterfaceList );
#if ( MDNS_WINDOWS_ENABLE_IPV4 )
UDPCloseSocket( &inMDNS->p->unicastSock4 );
#endif
#if ( MDNS_WINDOWS_ENABLE_IPV6 )
UDPCloseSocket( &inMDNS->p->unicastSock6 );
#endif
// Free the DLL needed for IPv6 support.
#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
if( gIPHelperLibraryInstance )
{
gGetAdaptersAddressesFunctionPtr = NULL;
FreeLibrary( gIPHelperLibraryInstance );
gIPHelperLibraryInstance = NULL;
}
#endif
if ( g_hAAPI32 )
{
// Release any resources
if ( g_hProvider && g_lpCryptReleaseContext )
{
( g_lpCryptReleaseContext )( g_hProvider, 0 );
}
// Free the AdvApi32.dll
FreeLibrary( g_hAAPI32 );
// And reset all the data
g_lpCryptAcquireContext = NULL;
g_lpCryptReleaseContext = NULL;
g_lpCryptGenRandom = NULL;
g_hProvider = ( ULONG_PTR ) NULL;
g_hAAPI32 = NULL;
}
// Clear out the APC queue
while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION )
{
DispatchSocketEvents( inMDNS );
}
WSACleanup();
dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" );
}
//===========================================================================================================================
// mDNSPlatformLock
//===========================================================================================================================
mDNSexport void mDNSPlatformLock( const mDNS * const inMDNS )
{
( void ) inMDNS;
}
//===========================================================================================================================
// mDNSPlatformUnlock
//===========================================================================================================================
mDNSexport void mDNSPlatformUnlock( const mDNS * const inMDNS )
{
( void ) inMDNS;
}
//===========================================================================================================================
// mDNSPlatformStrCopy
//===========================================================================================================================
mDNSexport void mDNSPlatformStrCopy( void *inDst, const void *inSrc )
{
check( inSrc );
check( inDst );
strcpy( (char *) inDst, (const char*) inSrc );
}
//===========================================================================================================================
// mDNSPlatformStrLCopy
//===========================================================================================================================
mDNSexport mDNSu32 mDNSPlatformStrLCopy(void *inDst, const void *inSrc, mDNSu32 inSize)
{
const char * src = (const char *) inSrc;
if( inSize > 0 )
{
size_t n;
char * dst = (char *) inDst;
for( n = inSize - 1; n > 0; --n )
{
if( ( *dst++ = *src++ ) == '\0' )
{
// Null terminator encountered, so exit.
goto exit;
}
}
*dst = '\0';
}
while( *src++ != '\0' )
{
// Stop at null terminator.
}
exit:
return( (mDNSu32)( src - (const char *) inSrc ) - 1 );
}
//===========================================================================================================================
// mDNSPlatformStrLen
//===========================================================================================================================
mDNSexport mDNSu32 mDNSPlatformStrLen( const void *inSrc )
{
check( inSrc );
return( (mDNSu32) strlen( (const char *) inSrc ) );
}
//===========================================================================================================================
// mDNSPlatformMemCopy
//===========================================================================================================================
mDNSexport void mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize )
{
check( inSrc );
check( inDst );
memcpy( inDst, inSrc, inSize );
}
//===========================================================================================================================
// mDNSPlatformMemSame
//===========================================================================================================================
mDNSexport mDNSBool mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize )
{
check( inSrc );
check( inDst );
return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) );
}
//===========================================================================================================================
// mDNSPlatformMemZero
//===========================================================================================================================
mDNSexport void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
{
check( inDst );
memset( inDst, 0, inSize );
}
//===========================================================================================================================
// mDNSPlatformMemAllocate
//===========================================================================================================================
mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize )
{
void * mem;
check( inSize > 0 );
mem = malloc( inSize );
check( mem );
return( mem );
}
//===========================================================================================================================
// mDNSPlatformMemFree
//===========================================================================================================================
mDNSexport void mDNSPlatformMemFree( void *inMem )
{
check( inMem );
free( inMem );
}
//===========================================================================================================================
// mDNSPlatformRandomNumber
//===========================================================================================================================
mDNSexport mDNSu32 mDNSPlatformRandomNumber(void)
{
mDNSu32 randomNumber = 0;
BOOL bResult;
OSStatus err = 0;
if ( !g_hAAPI32 )
{
g_hAAPI32 = LoadLibrary( TEXT("AdvAPI32.dll") );
err = translate_errno( g_hAAPI32 != NULL, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
}
// Function Pointer: CryptAcquireContext
if ( !g_lpCryptAcquireContext )
{
g_lpCryptAcquireContext = ( fnCryptAcquireContext )
#ifdef UNICODE
( GetProcAddress( g_hAAPI32, "CryptAcquireContextW" ) );
#else
( GetProcAddress( g_hAAPI32, "CryptAcquireContextA" ) );
#endif
err = translate_errno( g_lpCryptAcquireContext != NULL, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
}
// Function Pointer: CryptReleaseContext
if ( !g_lpCryptReleaseContext )
{
g_lpCryptReleaseContext = ( fnCryptReleaseContext )
( GetProcAddress( g_hAAPI32, "CryptReleaseContext" ) );
err = translate_errno( g_lpCryptReleaseContext != NULL, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
}
// Function Pointer: CryptGenRandom
if ( !g_lpCryptGenRandom )
{
g_lpCryptGenRandom = ( fnCryptGenRandom )
( GetProcAddress( g_hAAPI32, "CryptGenRandom" ) );
err = translate_errno( g_lpCryptGenRandom != NULL, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
}
// Setup
if ( !g_hProvider )
{
bResult = (*g_lpCryptAcquireContext)( &g_hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET );
if ( !bResult )
{
bResult = ( *g_lpCryptAcquireContext)( &g_hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET );
err = translate_errno( bResult, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
}
}
bResult = (*g_lpCryptGenRandom)( g_hProvider, sizeof( randomNumber ), ( BYTE* ) &randomNumber );
err = translate_errno( bResult, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
exit:
if ( err )
{
randomNumber = rand();
}
return randomNumber;
}
//===========================================================================================================================
// mDNSPlatformTimeInit
//===========================================================================================================================
mDNSexport mStatus mDNSPlatformTimeInit( void )
{
// No special setup is required on Windows -- we just use GetTickCount().
return( mStatus_NoError );
}
//===========================================================================================================================
// mDNSPlatformRawTime
//===========================================================================================================================
mDNSexport mDNSs32 mDNSPlatformRawTime( void )
{
return( (mDNSs32) GetTickCount() );
}
//===========================================================================================================================
// mDNSPlatformUTC
//===========================================================================================================================
mDNSexport mDNSs32 mDNSPlatformUTC( void )
{
return ( mDNSs32 ) time( NULL );
}
//===========================================================================================================================
// mDNSPlatformInterfaceNameToID
//===========================================================================================================================
mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
{
mStatus err;
mDNSInterfaceData * ifd;
check( inMDNS );
check( inMDNS->p );
check( inName );
// Search for an interface with the specified name,
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( strcmp( ifd->name, inName ) == 0 )
{
break;
}
}
require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
// Success!
if( outID )
{
*outID = (mDNSInterfaceID) ifd;
}
err = mStatus_NoError;
exit:
return( err );
}
//===========================================================================================================================
// mDNSPlatformInterfaceIDToInfo
//===========================================================================================================================
mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
{
mStatus err;
mDNSInterfaceData * ifd;
check( inMDNS );
check( inID );
check( outInfo );
// Search for an interface with the specified ID,
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( ifd == (mDNSInterfaceData *) inID )
{
break;
}
}
require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
// Success!
outInfo->name = ifd->name;
outInfo->ip = ifd->interfaceInfo.ip;
err = mStatus_NoError;
exit:
return( err );
}
//===========================================================================================================================
// mDNSPlatformInterfaceIDfromInterfaceIndex
//===========================================================================================================================
mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * const inMDNS, mDNSu32 inIndex )
{
mDNSInterfaceID id;
id = mDNSNULL;
if( inIndex == kDNSServiceInterfaceIndexLocalOnly )
{
id = mDNSInterface_LocalOnly;
}
/* uncomment if Windows ever supports P2P
else if( inIndex == kDNSServiceInterfaceIndexP2P )
{
id = mDNSInterface_P2P;
}
*/
else if( inIndex != 0 )
{
mDNSInterfaceData * ifd;
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( ( ifd->scopeID == inIndex ) && ifd->interfaceInfo.InterfaceActive )
{
id = ifd->interfaceInfo.InterfaceID;
break;
}
}
check( ifd );
}
return( id );
}
//===========================================================================================================================
// mDNSPlatformInterfaceIndexfromInterfaceID
//===========================================================================================================================
mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange )
{
mDNSu32 index;
(void) suppressNetworkChange; // Unused
index = 0;
if( inID == mDNSInterface_LocalOnly )
{
index = (mDNSu32) kDNSServiceInterfaceIndexLocalOnly;
}
/* uncomment if Windows ever supports P2P
else if( inID == mDNSInterface_P2P )
{
index = (mDNSu32) kDNSServiceInterfaceIndexP2P;
}
*/
else if( inID )
{
mDNSInterfaceData * ifd;
// Search active interfaces.
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( (mDNSInterfaceID) ifd == inID )
{
index = ifd->scopeID;
break;
}
}
// Search inactive interfaces too so remove events for inactive interfaces report the old interface index.
if( !ifd )
{
for( ifd = inMDNS->p->inactiveInterfaceList; ifd; ifd = ifd->next )
{
if( (mDNSInterfaceID) ifd == inID )
{
index = ifd->scopeID;
break;
}
}
}
check( ifd );
}
return( index );
}
//===========================================================================================================================
// mDNSPlatformTCPSocket
//===========================================================================================================================
TCPSocket *
mDNSPlatformTCPSocket
(
mDNS * const m,
TCPSocketFlags flags,
mDNSIPPort * port
)
{
TCPSocket * sock = NULL;
u_long on = 1; // "on" for setsockopt
struct sockaddr_in saddr;
int len;
mStatus err = mStatus_NoError;
DEBUG_UNUSED( m );
require_action( flags == 0, exit, err = mStatus_UnsupportedErr );
// Setup connection data object
sock = (TCPSocket *) malloc( sizeof( TCPSocket ) );
require_action( sock, exit, err = mStatus_NoMemoryErr );
mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
sock->fd = INVALID_SOCKET;
sock->flags = flags;
sock->m = m;
mDNSPlatformMemZero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl( INADDR_ANY );
saddr.sin_port = port->NotAnInteger;
// Create the socket
sock->fd = socket(AF_INET, SOCK_STREAM, 0);
err = translate_errno( sock->fd != INVALID_SOCKET, WSAGetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
// bind
err = bind( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) );
err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
// Set it to be non-blocking
err = ioctlsocket( sock->fd, FIONBIO, &on );
err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
// Get port number
mDNSPlatformMemZero( &saddr, sizeof( saddr ) );
len = sizeof( saddr );
err = getsockname( sock->fd, ( struct sockaddr* ) &saddr, &len );
err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
port->NotAnInteger = saddr.sin_port;
exit:
if ( err && sock )
{
TCPFreeSocket( sock );
sock = mDNSNULL;
}
return sock;
}
//===========================================================================================================================
// mDNSPlatformTCPConnect
//===========================================================================================================================
mStatus
mDNSPlatformTCPConnect
(
TCPSocket * sock,
const mDNSAddr * inDstIP,
mDNSOpaque16 inDstPort,
domainname * hostname,
mDNSInterfaceID inInterfaceID,
TCPConnectionCallback inCallback,
void * inContext
)
{
struct sockaddr_in saddr;
mStatus err = mStatus_NoError;
DEBUG_UNUSED( inInterfaceID );
( void ) hostname;
if ( inDstIP->type != mDNSAddrType_IPv4 )
{
LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: operation not supported");
return mStatus_UnknownErr;
}
// Setup connection data object
sock->readEventHandler = TCPCanRead;
sock->userCallback = inCallback;
sock->userContext = inContext;
mDNSPlatformMemZero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = inDstPort.NotAnInteger;
memcpy(&saddr.sin_addr, &inDstIP->ip.v4.NotAnInteger, sizeof(saddr.sin_addr));
// Try and do connect
err = connect( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) );
require_action( !err || ( WSAGetLastError() == WSAEWOULDBLOCK ), exit, err = mStatus_ConnFailed );
sock->connected = !err ? TRUE : FALSE;
if ( sock->connected )
{
err = TCPAddSocket( sock->m, sock );
require_noerr( err, exit );
}
else
{
require_action( sock->m->p->registerWaitableEventFunc != NULL, exit, err = mStatus_ConnFailed );
sock->connectEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
err = translate_errno( sock->connectEvent, GetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
err = WSAEventSelect( sock->fd, sock->connectEvent, FD_CONNECT );
require_noerr( err, exit );
err = sock->m->p->registerWaitableEventFunc( sock->m, sock->connectEvent, sock, TCPDidConnect );
require_noerr( err, exit );
}
exit:
if ( !err )
{
err = sock->connected ? mStatus_ConnEstablished : mStatus_ConnPending;
}
return err;
}
//===========================================================================================================================
// mDNSPlatformTCPAccept
//===========================================================================================================================
mDNSexport
mDNSexport TCPSocket *mDNSPlatformTCPAccept( TCPSocketFlags flags, int fd )
{
TCPSocket * sock = NULL;
mStatus err = mStatus_NoError;
require_action( !flags, exit, err = mStatus_UnsupportedErr );
sock = malloc( sizeof( TCPSocket ) );
require_action( sock, exit, err = mStatus_NoMemoryErr );
mDNSPlatformMemZero( sock, sizeof( *sock ) );
sock->fd = fd;
sock->flags = flags;
exit:
if ( err && sock )
{
free( sock );
sock = NULL;
}
return sock;
}
//===========================================================================================================================
// mDNSPlatformTCPCloseConnection
//===========================================================================================================================
mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock )
{
check( sock );
if ( sock->connectEvent && sock->m->p->unregisterWaitableEventFunc )
{
sock->m->p->unregisterWaitableEventFunc( sock->m, sock->connectEvent );
}
if ( sock->fd != INVALID_SOCKET )
{
TCPCloseSocket( sock );
QueueUserAPC( ( PAPCFUNC ) TCPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
}
}
//===========================================================================================================================
// mDNSPlatformReadTCP
//===========================================================================================================================
mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned long inBufferSize, mDNSBool * closed )
{
unsigned long bytesLeft;
int wsaError;
long ret;
*closed = sock->closed;
wsaError = sock->lastError;
ret = -1;
if ( *closed )
{
ret = 0;
}
else if ( sock->lastError == 0 )
{
// First check to see if we have any data left in our buffer
bytesLeft = ( DWORD ) ( sock->eptr - sock->bptr );
if ( bytesLeft )
{
unsigned long bytesToCopy = ( bytesLeft < inBufferSize ) ? bytesLeft : inBufferSize;
memcpy( inBuffer, sock->bptr, bytesToCopy );
sock->bptr += bytesToCopy;
if ( !sock->overlapped.pending && ( sock->bptr == sock->eptr ) )
{
sock->bptr = sock->bbuf;
sock->eptr = sock->bbuf;
}
ret = bytesToCopy;
}
else
{
wsaError = WSAEWOULDBLOCK;
}
}
// Always set the last winsock error, so that we don't inadvertently use a previous one
WSASetLastError( wsaError );
return ret;
}
//===========================================================================================================================
// mDNSPlatformWriteTCP
//===========================================================================================================================
mDNSexport long mDNSPlatformWriteTCP( TCPSocket *sock, const char *inMsg, unsigned long inMsgSize )
{
int nsent;
OSStatus err;
nsent = send( sock->fd, inMsg, inMsgSize, 0 );
err = translate_errno( ( nsent >= 0 ) || ( WSAGetLastError() == WSAEWOULDBLOCK ), WSAGetLastError(), mStatus_UnknownErr );
require_noerr( err, exit );
if ( nsent < 0)
{
nsent = 0;
}
exit:
return nsent;
}
//===========================================================================================================================
// mDNSPlatformTCPGetFD
//===========================================================================================================================
mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock )
{
return ( int ) sock->fd;
}
//===========================================================================================================================
// TCPAddConnection
//===========================================================================================================================
mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock )
{
mStatus err;
( void ) inMDNS;
sock->bptr = sock->bbuf;
sock->eptr = sock->bbuf;
sock->ebuf = sock->bbuf + sizeof( sock->bbuf );
dlog( kDebugLevelChatty, DEBUG_NAME "adding TCPSocket 0x%x:%d\n", sock, sock->fd );
err = TCPBeginRecv( sock );
require_noerr( err, exit );
exit:
return err;
}
//===========================================================================================================================
// TCPDidConnect
//===========================================================================================================================
mDNSlocal void TCPDidConnect( mDNS * const inMDNS, HANDLE event, void * context )
{
TCPSocket * sock = ( TCPSocket* ) context;
TCPConnectionCallback callback = NULL;
WSANETWORKEVENTS sockEvent;
int err = kNoErr;
if ( inMDNS->p->unregisterWaitableEventFunc )
{
inMDNS->p->unregisterWaitableEventFunc( inMDNS, event );
}
if ( sock )
{
callback = ( TCPConnectionCallback ) sock->userCallback;
err = WSAEnumNetworkEvents( sock->fd, sock->connectEvent, &sockEvent );
require_noerr( err, exit );
require_action( sockEvent.lNetworkEvents & FD_CONNECT, exit, err = mStatus_UnknownErr );
require_action( sockEvent.iErrorCode[ FD_CONNECT_BIT ] == 0, exit, err = sockEvent.iErrorCode[ FD_CONNECT_BIT ] );
sock->connected = mDNStrue;
if ( sock->fd != INVALID_SOCKET )
{
err = TCPAddSocket( sock->m, sock );
require_noerr( err, exit );
}
if ( callback )
{
callback( sock, sock->userContext, TRUE, 0 );
}
}
exit:
if ( err && callback )
{
callback( sock, sock->userContext, TRUE, err );
}
}
//===========================================================================================================================
// TCPCanRead
//===========================================================================================================================
mDNSlocal void TCPCanRead( TCPSocket * sock )
{
TCPConnectionCallback callback = ( TCPConnectionCallback ) sock->userCallback;
if ( callback )
{
callback( sock, sock->userContext, mDNSfalse, sock->lastError );
}
}
//===========================================================================================================================
// TCPBeginRecv
//===========================================================================================================================
mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock )
{
DWORD bytesReceived = 0;
DWORD flags = 0;
mStatus err;
dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
check( !sock->overlapped.pending );
ZeroMemory( &sock->overlapped.data, sizeof( sock->overlapped.data ) );
sock->overlapped.data.hEvent = sock;
sock->overlapped.wbuf.buf = ( char* ) sock->eptr;
sock->overlapped.wbuf.len = ( ULONG) ( sock->ebuf - sock->eptr );
err = WSARecv( sock->fd, &sock->overlapped.wbuf, 1, &bytesReceived, &flags, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), WSAGetLastError(), kUnknownErr );
require_noerr( err, exit );
sock->overlapped.pending = TRUE;
exit:
return err;
}
//===========================================================================================================================
// TCPEndRecv
//===========================================================================================================================
mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags )
{
TCPSocket * sock;
( void ) flags;
dlog( kDebugLevelChatty, DEBUG_NAME "%s: error = %d, bytesTransferred = %d\n", __ROUTINE__, error, bytesTransferred );
sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
require_action( sock, exit, error = ( DWORD ) mStatus_BadStateErr );
dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
sock->overlapped.error = error;
sock->overlapped.bytesTransferred = bytesTransferred;
check( sock->overlapped.pending );
sock->overlapped.pending = FALSE;
// Queue this socket
AddToTail( &gTCPDispatchableSockets, sock );
exit:
return;
}
//===========================================================================================================================
// mDNSPlatformUDPSocket
//===========================================================================================================================
mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport)
{
UDPSocket* sock = NULL;
mDNSIPPort port = requestedport;
mStatus err = mStatus_NoError;
unsigned i;
// Setup connection data object
sock = ( UDPSocket* ) malloc(sizeof( UDPSocket ) );
require_action( sock, exit, err = mStatus_NoMemoryErr );
memset( sock, 0, sizeof( UDPSocket ) );
// Create the socket
sock->fd = INVALID_SOCKET;
sock->recvMsgPtr = m->p->unicastSock4.recvMsgPtr;
sock->addr = m->p->unicastSock4.addr;
sock->ifd = NULL;
sock->overlapped.pending = FALSE;
sock->m = m;
// Try at most 10000 times to get a unique random port
for (i=0; i<10000; i++)
{
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = 0;
// The kernel doesn't do cryptographically strong random port
// allocation, so we do it ourselves here
if (mDNSIPPortIsZero(requestedport))
{
port = mDNSOpaque16fromIntVal( ( mDNSu16 ) ( 0xC000 + mDNSRandom(0x3FFF) ) );
}
saddr.sin_port = port.NotAnInteger;
err = SetupSocket(m, ( struct sockaddr* ) &saddr, port, &sock->fd );
if (!err) break;
}
require_noerr( err, exit );
// Set the port
sock->port = port;
// Arm the completion routine
err = UDPBeginRecv( sock );
require_noerr( err, exit );
// Bookkeeping
sock->next = gUDPSockets;
gUDPSockets = sock;
gUDPNumSockets++;
exit:
if ( err && sock )
{
UDPFreeSocket( sock );
sock = NULL;
}
return sock;
}
//===========================================================================================================================
// mDNSPlatformUDPClose
//===========================================================================================================================
mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
{
UDPSocket * current = gUDPSockets;
UDPSocket * last = NULL;
while ( current )
{
if ( current == sock )
{
if ( last == NULL )
{
gUDPSockets = sock->next;
}
else
{
last->next = sock->next;
}
// Alertable I/O is great, except not so much when it comes to closing
// the socket. Anything that has been previously queued for this socket
// will stay in the queue after you close the socket. This is problematic
// for obvious reasons. So we'll attempt to workaround this by closing
// the socket which will prevent any further queued packets and then not calling
// UDPFreeSocket directly, but by queueing it using QueueUserAPC. The queues
// are FIFO, so that will execute *after* any other previous items in the queue
//
// UDPEndRecv will check if the socket is valid, and if not, it will ignore
// the packet
UDPCloseSocket( sock );
QueueUserAPC( ( PAPCFUNC ) UDPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
gUDPNumSockets--;
break;
}
last = current;
current = current->next;
}
}
//===========================================================================================================================
// mDNSPlatformSendUDP
//===========================================================================================================================
mDNSexport mStatus
mDNSPlatformSendUDP(
const mDNS * const inMDNS,
const void * const inMsg,
const mDNSu8 * const inMsgEnd,
mDNSInterfaceID inInterfaceID,
UDPSocket * inSrcSocket,
const mDNSAddr * inDstIP,
mDNSIPPort inDstPort )
{
SOCKET sendingsocket = INVALID_SOCKET;
mStatus err = mStatus_NoError;
mDNSInterfaceData * ifd = (mDNSInterfaceData*) inInterfaceID;
struct sockaddr_storage addr;
int n;
DEBUG_USE_ONLY( inMDNS );
n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) );
check( inMDNS );
check( inMsg );
check( inMsgEnd );
check( inDstIP );
dlog( kDebugLevelChatty, DEBUG_NAME "platform send %d bytes to %#a:%u\n", n, inDstIP, ntohs( inDstPort.NotAnInteger ) );
if( inDstIP->type == mDNSAddrType_IPv4 )
{
struct sockaddr_in * sa4;
sa4 = (struct sockaddr_in *) &addr;
sa4->sin_family = AF_INET;
sa4->sin_port = inDstPort.NotAnInteger;
sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger;
sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock4.fd;
if (inSrcSocket) { sendingsocket = inSrcSocket->fd; debugf("mDNSPlatformSendUDP using port %d, static port %d, sock %d", mDNSVal16(inSrcSocket->port), inMDNS->p->unicastSock4.fd, sendingsocket); }
}
else if( inDstIP->type == mDNSAddrType_IPv6 )
{
struct sockaddr_in6 * sa6;
sa6 = (struct sockaddr_in6 *) &addr;
sa6->sin6_family = AF_INET6;
sa6->sin6_port = inDstPort.NotAnInteger;
sa6->sin6_flowinfo = 0;
sa6->sin6_addr = *( (struct in6_addr *) &inDstIP->ip.v6 );
sa6->sin6_scope_id = 0; // Windows requires the scope ID to be zero. IPV6_MULTICAST_IF specifies interface.
sendingsocket = ifd ? ifd->sock.fd : inMDNS->p->unicastSock6.fd;
}
else
{
dlog( kDebugLevelError, DEBUG_NAME "%s: dst is not an IPv4 or IPv6 address (type=%d)\n", __ROUTINE__, inDstIP->type );
err = mStatus_BadParamErr;
goto exit;
}
if (IsValidSocket(sendingsocket))
{
n = sendto( sendingsocket, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
err = translate_errno( n > 0, errno_compat(), kWriteErr );
if ( err )
{
// Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations
if ( !mDNSAddressIsAllDNSLinkGroup( inDstIP ) && ( WSAGetLastError() == WSAEHOSTDOWN || WSAGetLastError() == WSAENETDOWN || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAENETUNREACH ) )
{
err = mStatus_TransientErr;
}
else
{
require_noerr( err, exit );
}
}
}
exit:
return( err );
}
mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
{
DEBUG_UNUSED( m );
DEBUG_UNUSED( InterfaceID );
}
//===========================================================================================================================
// mDNSPlatformSendRawPacket
//===========================================================================================================================
mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
{
DEBUG_UNUSED( m );
DEBUG_UNUSED( allowSleep );
DEBUG_UNUSED( reason );
}
mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
{
DEBUG_UNUSED( msg );
DEBUG_UNUSED( end );
DEBUG_UNUSED( InterfaceID );
}
mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
{
DEBUG_UNUSED( msg );
DEBUG_UNUSED( end );
DEBUG_UNUSED( InterfaceID );
}
mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
{
DEBUG_UNUSED( m );
DEBUG_UNUSED( tpa );
DEBUG_UNUSED( tha );
DEBUG_UNUSED( InterfaceID );
}
mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
{
dlog( kDebugLevelInfo, "%s\n", msg );
}
mDNSexport void mDNSPlatformWriteLogMsg( const char * ident, const char * msg, mDNSLogLevel_t loglevel )
{
extern mDNS mDNSStorage;
int type;
DEBUG_UNUSED( ident );
type = EVENTLOG_ERROR_TYPE;
switch (loglevel)
{
case MDNS_LOG_MSG: type = EVENTLOG_ERROR_TYPE; break;
case MDNS_LOG_OPERATION: type = EVENTLOG_WARNING_TYPE; break;
case MDNS_LOG_SPS: type = EVENTLOG_INFORMATION_TYPE; break;
case MDNS_LOG_INFO: type = EVENTLOG_INFORMATION_TYPE; break;
case MDNS_LOG_DEBUG: type = EVENTLOG_INFORMATION_TYPE; break;
default:
fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel);
fflush(stderr);
}
mDNSStorage.p->reportStatusFunc( type, msg );
dlog( kDebugLevelInfo, "%s\n", msg );
}
mDNSexport void mDNSPlatformSourceAddrForDest( mDNSAddr * const src, const mDNSAddr * const dst )
{
DEBUG_UNUSED( src );
DEBUG_UNUSED( dst );
}
//===========================================================================================================================
// mDNSPlatformTLSSetupCerts
//===========================================================================================================================
mDNSexport mStatus
mDNSPlatformTLSSetupCerts(void)
{
return mStatus_UnsupportedErr;
}
//===========================================================================================================================
// mDNSPlatformTLSTearDownCerts
//===========================================================================================================================
mDNSexport void
mDNSPlatformTLSTearDownCerts(void)
{
}
//===========================================================================================================================
// mDNSPlatformSetDNSConfig
//===========================================================================================================================
mDNSlocal void SetDNSServers( mDNS *const m );
mDNSlocal void SetSearchDomainList( void );
mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains)
{
if (setservers) SetDNSServers(m);
if (setsearch) SetSearchDomainList();
if ( fqdn )
{
GetDDNSFQDN( fqdn );
}
if ( browseDomains )
{
GetDDNSDomains( browseDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains );
}
if ( regDomains )
{
GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
}
}
//===========================================================================================================================
// mDNSPlatformDynDNSHostNameStatusChanged
//===========================================================================================================================
mDNSexport void
mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
{
char uname[MAX_ESCAPED_DOMAIN_NAME];
BYTE bStatus;
LPCTSTR name;
HKEY key = NULL;
mStatus err;
char * p;
ConvertDomainNameToCString(dname, uname);
p = uname;
while (*p)
{
*p = (char) tolower(*p);
if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot
p++;
}
check( strlen( p ) <= MAX_ESCAPED_DOMAIN_NAME );
name = kServiceParametersNode TEXT("\\DynDNS\\State\\HostNames");
err = RegCreateKey( HKEY_LOCAL_MACHINE, name, &key );
require_noerr( err, exit );
bStatus = ( status ) ? 0 : 1;
err = RegSetValueEx( key, kServiceDynDNSStatus, 0, REG_DWORD, (const LPBYTE) &bStatus, sizeof(DWORD) );
require_noerr( err, exit );
exit:
if ( key )
{
RegCloseKey( key );
}
return;
}
mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; // unused
(void)rr;
(void)result;
}
//===========================================================================================================================
// SetDomainSecrets
//===========================================================================================================================
// This routine needs to be called whenever the system secrets database changes.
// We call it from DynDNSConfigDidChange and mDNSPlatformInit
void
SetDomainSecrets( mDNS * const m )
{
DomainAuthInfo *ptr;
domainname fqdn;
DNameListElem * regDomains = NULL;
// Rather than immediately deleting all keys now, we mark them for deletion in ten seconds.
// In the case where the user simultaneously removes their DDNS host name and the key
// for it, this gives mDNSResponder ten seconds to gracefully delete the name from the
// server before it loses access to the necessary key. Otherwise, we'd leave orphaned
// address records behind that we no longer have permission to delete.
for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10);
GetDDNSFQDN( &fqdn );
if ( fqdn.c[ 0 ] )
{
SetDomainSecret( m, &fqdn );
}
GetDDNSDomains( &regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
while ( regDomains )
{
DNameListElem * current = regDomains;
SetDomainSecret( m, &current->name );
regDomains = regDomains->next;
free( current );
}
}
//===========================================================================================================================
// SetSearchDomainList
//===========================================================================================================================
mDNSlocal void SetDomainFromDHCP( void );
mDNSlocal void SetReverseMapSearchDomainList( void );
mDNSlocal void
SetSearchDomainList( void )
{
char * searchList = NULL;
DWORD searchListLen;
//DNameListElem * head = NULL;
//DNameListElem * current = NULL;
char * tok;
HKEY key;
mStatus err;
err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &key );
require_noerr( err, exit );
err = RegQueryString( key, "SearchList", &searchList, &searchListLen, NULL );
require_noerr( err, exit );
// Windows separates the search domains with ','
tok = strtok( searchList, "," );
while ( tok )
{
if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) )
mDNS_AddSearchDomain_CString(tok, mDNSNULL);
tok = strtok( NULL, "," );
}
exit:
if ( searchList )
{
free( searchList );
}
if ( key )
{
RegCloseKey( key );
}
SetDomainFromDHCP();
SetReverseMapSearchDomainList();
}
//===========================================================================================================================
// SetReverseMapSearchDomainList
//===========================================================================================================================
mDNSlocal void
SetReverseMapSearchDomainList( void )
{
struct ifaddrs * ifa;
ifa = myGetIfAddrs( 1 );
while (ifa)
{
mDNSAddr addr;
if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask)
{
mDNSAddr netmask;
char buffer[256];
if (!SetupAddr(&netmask, ifa->ifa_netmask))
{
sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3],
addr.ip.v4.b[2] & netmask.ip.v4.b[2],
addr.ip.v4.b[1] & netmask.ip.v4.b[1],
addr.ip.v4.b[0] & netmask.ip.v4.b[0]);
mDNS_AddSearchDomain_CString(buffer, mDNSNULL);
}
}
ifa = ifa->ifa_next;
}
return;
}
//===========================================================================================================================
// SetDNSServers
//===========================================================================================================================
mDNSlocal void
SetDNSServers( mDNS *const m )
{
PIP_PER_ADAPTER_INFO pAdapterInfo = NULL;
FIXED_INFO * fixedInfo = NULL;
ULONG bufLen = 0;
IP_ADDR_STRING * dnsServerList;
IP_ADDR_STRING * ipAddr;
DWORD index;
int i = 0;
mStatus err = kUnknownErr;
// Get the primary interface.
index = GetPrimaryInterface();
// This should have the interface index of the primary index. Fall back in cases where
// it can't be determined.
if ( index )
{
bufLen = 0;
for ( i = 0; i < 100; i++ )
{
err = GetPerAdapterInfo( index, pAdapterInfo, &bufLen );
if ( err != ERROR_BUFFER_OVERFLOW )
{
break;
}
pAdapterInfo = (PIP_PER_ADAPTER_INFO) realloc( pAdapterInfo, bufLen );
require_action( pAdapterInfo, exit, err = mStatus_NoMemoryErr );
}
require_noerr( err, exit );
dnsServerList = &pAdapterInfo->DnsServerList;
}
else
{
bufLen = sizeof( FIXED_INFO );
for ( i = 0; i < 100; i++ )
{
if ( fixedInfo )
{
GlobalFree( fixedInfo );
fixedInfo = NULL;
}
fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen );
require_action( fixedInfo, exit, err = mStatus_NoMemoryErr );
err = GetNetworkParams( fixedInfo, &bufLen );
if ( err != ERROR_BUFFER_OVERFLOW )
{
break;
}
}
require_noerr( err, exit );
dnsServerList = &fixedInfo->DnsServerList;
}
for ( ipAddr = dnsServerList; ipAddr; ipAddr = ipAddr->Next )
{
mDNSAddr addr;
err = StringToAddress( &addr, ipAddr->IpAddress.String );
if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
}
exit:
if ( pAdapterInfo )
{
free( pAdapterInfo );
}
if ( fixedInfo )
{
GlobalFree( fixedInfo );
}
}
//===========================================================================================================================
// SetDomainFromDHCP
//===========================================================================================================================
mDNSlocal void
SetDomainFromDHCP( void )
{
int i = 0;
IP_ADAPTER_INFO * pAdapterInfo;
IP_ADAPTER_INFO * pAdapter;
DWORD bufLen;
DWORD index;
HKEY key = NULL;
LPSTR domain = NULL;
DWORD dwSize;
mStatus err = mStatus_NoError;
pAdapterInfo = NULL;
for ( i = 0; i < 100; i++ )
{
err = GetAdaptersInfo( pAdapterInfo, &bufLen);
if ( err != ERROR_BUFFER_OVERFLOW )
{
break;
}
pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
require_action( pAdapterInfo, exit, err = kNoMemoryErr );
}
require_noerr( err, exit );
index = GetPrimaryInterface();
for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
{
if ( pAdapter->IpAddressList.IpAddress.String &&
pAdapter->IpAddressList.IpAddress.String[0] &&
pAdapter->GatewayList.IpAddress.String &&
pAdapter->GatewayList.IpAddress.String[0] &&
( !index || ( pAdapter->Index == index ) ) )
{
// Found one that will work
char keyName[1024];
_snprintf( keyName, 1024, "%s%s", "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\", pAdapter->AdapterName );
err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyName, &key );
require_noerr( err, exit );
err = RegQueryString( key, "Domain", &domain, &dwSize, NULL );
check_noerr( err );
if ( !domain || !domain[0] )
{
if ( domain )
{
free( domain );
domain = NULL;
}
err = RegQueryString( key, "DhcpDomain", &domain, &dwSize, NULL );
check_noerr( err );
}
if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL);
break;
}
}
exit:
if ( pAdapterInfo )
{
free( pAdapterInfo );
}
if ( domain )
{
free( domain );
}
if ( key )
{
RegCloseKey( key );
}
}
//===========================================================================================================================
// mDNSPlatformGetPrimaryInterface
//===========================================================================================================================
mDNSexport mStatus
mDNSPlatformGetPrimaryInterface( mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router )
{
IP_ADAPTER_INFO * pAdapterInfo;
IP_ADAPTER_INFO * pAdapter;
DWORD bufLen;
int i;
BOOL found;
DWORD index;
mStatus err = mStatus_NoError;
DEBUG_UNUSED( m );
*v6 = zeroAddr;
pAdapterInfo = NULL;
bufLen = 0;
found = FALSE;
for ( i = 0; i < 100; i++ )
{
err = GetAdaptersInfo( pAdapterInfo, &bufLen);
if ( err != ERROR_BUFFER_OVERFLOW )
{
break;
}
pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
require_action( pAdapterInfo, exit, err = kNoMemoryErr );
}
require_noerr( err, exit );
index = GetPrimaryInterface();
for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
{
if ( pAdapter->IpAddressList.IpAddress.String &&
pAdapter->IpAddressList.IpAddress.String[0] &&
pAdapter->GatewayList.IpAddress.String &&
pAdapter->GatewayList.IpAddress.String[0] &&
( StringToAddress( v4, pAdapter->IpAddressList.IpAddress.String ) == mStatus_NoError ) &&
( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) &&
( !index || ( pAdapter->Index == index ) ) )
{
// Found one that will work
if ( pAdapter->AddressLength == sizeof( m->PrimaryMAC ) )
{
memcpy( &m->PrimaryMAC, pAdapter->Address, pAdapter->AddressLength );
}
found = TRUE;
break;
}
}
exit:
if ( pAdapterInfo )
{
free( pAdapterInfo );
}
return err;
}
mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
{
(void) m;
(void) InterfaceID;
(void) EthAddr;
(void) IPAddr;
(void) iteration;
}
mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
{
(void) rr;
(void) intf;
return 1;
}
#if 0
#pragma mark -
#endif
//===========================================================================================================================
// debugf_
//===========================================================================================================================
#if( MDNS_DEBUGMSGS )
mDNSexport void debugf_( const char *inFormat, ... )
{
char buffer[ 512 ];
va_list args;
mDNSu32 length;
va_start( args, inFormat );
length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
va_end( args );
dlog( kDebugLevelInfo, "%s\n", buffer );
}
#endif
//===========================================================================================================================
// verbosedebugf_
//===========================================================================================================================
#if( MDNS_DEBUGMSGS > 1 )
mDNSexport void verbosedebugf_( const char *inFormat, ... )
{
char buffer[ 512 ];
va_list args;
mDNSu32 length;
va_start( args, inFormat );
length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
va_end( args );
dlog( kDebugLevelVerbose, "%s\n", buffer );
}
#endif
#if 0
#pragma mark -
#pragma mark == Platform Internals ==
#endif
//===========================================================================================================================
// SetupNiceName
//===========================================================================================================================
mStatus SetupNiceName( mDNS * const inMDNS )
{
HKEY descKey = NULL;
char utf8[ 256 ];
LPCTSTR s;
LPWSTR joinName;
NETSETUP_JOIN_STATUS joinStatus;
mStatus err = 0;
DWORD namelen;
BOOL ok;
check( inMDNS );
// Set up the nice name.
utf8[0] = '\0';
// First try and open the registry key that contains the computer description value
s = TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters");
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s, 0, KEY_READ, &descKey);
check_translated_errno( err == 0, errno_compat(), kNameErr );
if ( !err )
{
TCHAR desc[256];
DWORD descSize = sizeof( desc );
// look for the computer description
err = RegQueryValueEx( descKey, TEXT("srvcomment"), 0, NULL, (LPBYTE) &desc, &descSize);
if ( !err )
{
err = TCHARtoUTF8( desc, utf8, sizeof( utf8 ) );
}
if ( err )
{
utf8[ 0 ] = '\0';
}
}
// if we can't find it in the registry, then use the hostname of the machine
if ( err || ( utf8[ 0 ] == '\0' ) )
{
TCHAR hostname[256];
namelen = sizeof( hostname ) / sizeof( TCHAR );
ok = GetComputerNameExW( ComputerNamePhysicalDnsHostname, hostname, &namelen );
err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
check_noerr( err );
if( !err )
{
err = TCHARtoUTF8( hostname, utf8, sizeof( utf8 ) );
}
if ( err )
{
utf8[ 0 ] = '\0';
}
}
// if we can't get the hostname
if ( err || ( utf8[ 0 ] == '\0' ) )
{
// Invalidate name so fall back to a default name.
strcpy( utf8, kMDNSDefaultName );
}
utf8[ sizeof( utf8 ) - 1 ] = '\0';
inMDNS->nicelabel.c[ 0 ] = (mDNSu8) (strlen( utf8 ) < MAX_DOMAIN_LABEL ? strlen( utf8 ) : MAX_DOMAIN_LABEL);
memcpy( &inMDNS->nicelabel.c[ 1 ], utf8, inMDNS->nicelabel.c[ 0 ] );
dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] );
if ( descKey )
{
RegCloseKey( descKey );
}
ZeroMemory( inMDNS->p->nbname, sizeof( inMDNS->p->nbname ) );
ZeroMemory( inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
namelen = sizeof( inMDNS->p->nbname );
ok = GetComputerNameExA( ComputerNamePhysicalNetBIOS, inMDNS->p->nbname, &namelen );
check( ok );
if ( ok ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios name \"%s\"\n", inMDNS->p->nbname );
err = NetGetJoinInformation( NULL, &joinName, &joinStatus );
check ( err == NERR_Success );
if ( err == NERR_Success )
{
if ( ( joinStatus == NetSetupWorkgroupName ) || ( joinStatus == NetSetupDomainName ) )
{
err = TCHARtoUTF8( joinName, inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
check( !err );
if ( !err ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios domain/workgroup \"%s\"\n", inMDNS->p->nbdomain );
}
NetApiBufferFree( joinName );
joinName = NULL;
}
err = 0;
return( err );
}
//===========================================================================================================================
// SetupHostName
//===========================================================================================================================
mDNSlocal mStatus SetupHostName( mDNS * const inMDNS )
{
mStatus err = 0;
char tempString[ 256 ];
DWORD tempStringLen;
domainlabel tempLabel;
BOOL ok;
check( inMDNS );
// Set up the nice name.
tempString[ 0 ] = '\0';
// use the hostname of the machine
tempStringLen = sizeof( tempString );
ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen );
err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
check_noerr( err );
// if we can't get the hostname
if( err || ( tempString[ 0 ] == '\0' ) )
{
// Invalidate name so fall back to a default name.
strcpy( tempString, kMDNSDefaultName );
}
tempString[ sizeof( tempString ) - 1 ] = '\0';
tempLabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL );
memcpy( &tempLabel.c[ 1 ], tempString, tempLabel.c[ 0 ] );
// Set up the host name.
ConvertUTF8PstringToRFC1034HostLabel( tempLabel.c, &inMDNS->hostlabel );
if( inMDNS->hostlabel.c[ 0 ] == 0 )
{
// Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default.
MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
}
check( inMDNS->hostlabel.c[ 0 ] != 0 );
mDNS_SetFQDN( inMDNS );
dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] );
return( err );
}
//===========================================================================================================================
// SetupName
//===========================================================================================================================
mDNSlocal mStatus SetupName( mDNS * const inMDNS )
{
mStatus err = 0;
check( inMDNS );
err = SetupNiceName( inMDNS );
check_noerr( err );
err = SetupHostName( inMDNS );
check_noerr( err );
return err;
}
//===========================================================================================================================
// SetupInterfaceList
//===========================================================================================================================
mStatus SetupInterfaceList( mDNS * const inMDNS )
{
mStatus err;
mDNSInterfaceData ** next;
mDNSInterfaceData * ifd;
struct ifaddrs * addrs;
struct ifaddrs * p;
struct ifaddrs * loopbackv4;
struct ifaddrs * loopbackv6;
u_int flagMask;
u_int flagTest;
mDNSBool foundv4;
mDNSBool foundv6;
mDNSBool foundUnicastSock4DestAddr;
mDNSBool foundUnicastSock6DestAddr;
dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" );
check( inMDNS );
check( inMDNS->p );
inMDNS->p->registeredLoopback4 = mDNSfalse;
inMDNS->p->nextDHCPLeaseExpires = 0x7FFFFFFF;
addrs = NULL;
foundv4 = mDNSfalse;
foundv6 = mDNSfalse;
foundUnicastSock4DestAddr = mDNSfalse;
foundUnicastSock6DestAddr = mDNSfalse;
// Tear down any existing interfaces that may be set up.
TearDownInterfaceList( inMDNS );
// Set up the name of this machine.
err = SetupName( inMDNS );
check_noerr( err );
// Set up IPv4 interface(s). We have to set up IPv4 first so any IPv6 interface with an IPv4-routable address
// can refer to the IPv4 interface when it registers to allow DNS AAAA records over the IPv4 interface.
err = getifaddrs( &addrs );
require_noerr( err, exit );
loopbackv4 = NULL;
loopbackv6 = NULL;
next = &inMDNS->p->interfaceList;
flagMask = IFF_UP | IFF_MULTICAST;
flagTest = IFF_UP | IFF_MULTICAST;
#if( MDNS_WINDOWS_ENABLE_IPV4 )
for( p = addrs; p; p = p->ifa_next )
{
if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
{
continue;
}
if( p->ifa_flags & IFF_LOOPBACK )
{
if( !loopbackv4 )
{
loopbackv4 = p;
}
continue;
}
dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
err = SetupInterface( inMDNS, p, &ifd );
require_noerr( err, exit );
// If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
// register him, but we also want to note that we haven't found a v4 interface
// so that we register loopback so same host operations work
if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
{
foundv4 = mDNStrue;
}
if ( p->ifa_dhcpEnabled && ( p->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) )
{
inMDNS->p->nextDHCPLeaseExpires = p->ifa_dhcpLeaseExpires;
}
// If we're on a platform that doesn't have WSARecvMsg(), there's no way
// of determing the destination address of a packet that is sent to us.
// For multicast packets, that's easy to determine. But for the unicast
// sockets, we'll fake it by taking the address of the first interface
// that is successfully setup.
if ( !foundUnicastSock4DestAddr )
{
inMDNS->p->unicastSock4.addr = ifd->interfaceInfo.ip;
foundUnicastSock4DestAddr = TRUE;
}
*next = ifd;
next = &ifd->next;
++inMDNS->p->interfaceCount;
}
#endif
// Set up IPv6 interface(s) after IPv4 is set up (see IPv4 notes above for reasoning).
#if( MDNS_WINDOWS_ENABLE_IPV6 )
for( p = addrs; p; p = p->ifa_next )
{
if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET6 ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
{
continue;
}
if( p->ifa_flags & IFF_LOOPBACK )
{
if( !loopbackv6 )
{
loopbackv6 = p;
}
continue;
}
dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
err = SetupInterface( inMDNS, p, &ifd );
require_noerr( err, exit );
// If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
// register him, but we also want to note that we haven't found a v4 interface
// so that we register loopback so same host operations work
if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
{
foundv6 = mDNStrue;
}
// If we're on a platform that doesn't have WSARecvMsg(), there's no way
// of determing the destination address of a packet that is sent to us.
// For multicast packets, that's easy to determine. But for the unicast
// sockets, we'll fake it by taking the address of the first interface
// that is successfully setup.
if ( !foundUnicastSock6DestAddr )
{
inMDNS->p->unicastSock6.addr = ifd->interfaceInfo.ip;
foundUnicastSock6DestAddr = TRUE;
}
*next = ifd;
next = &ifd->next;
++inMDNS->p->interfaceCount;
}
#endif
// If there are no real interfaces, but there is a loopback interface, use that so same-machine operations work.
#if( !MDNS_WINDOWS_ENABLE_IPV4 && !MDNS_WINDOWS_ENABLE_IPV6 )
flagMask |= IFF_LOOPBACK;
flagTest |= IFF_LOOPBACK;
for( p = addrs; p; p = p->ifa_next )
{
if( !p->ifa_addr || ( ( p->ifa_flags & flagMask ) != flagTest ) )
{
continue;
}
if( ( p->ifa_addr->sa_family != AF_INET ) && ( p->ifa_addr->sa_family != AF_INET6 ) )
{
continue;
}
v4loopback = p;
break;
}
#endif
if ( !foundv4 && loopbackv4 )
{
dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
loopbackv4->ifa_name ? loopbackv4->ifa_name : "<null>", loopbackv4->ifa_extra.index, loopbackv4->ifa_addr );
err = SetupInterface( inMDNS, loopbackv4, &ifd );
require_noerr( err, exit );
inMDNS->p->registeredLoopback4 = mDNStrue;
#if( MDNS_WINDOWS_ENABLE_IPV4 )
// If we're on a platform that doesn't have WSARecvMsg(), there's no way
// of determing the destination address of a packet that is sent to us.
// For multicast packets, that's easy to determine. But for the unicast
// sockets, we'll fake it by taking the address of the first interface
// that is successfully setup.
if ( !foundUnicastSock4DestAddr )
{
inMDNS->p->unicastSock4.addr = ifd->sock.addr;
foundUnicastSock4DestAddr = TRUE;
}
#endif
*next = ifd;
next = &ifd->next;
++inMDNS->p->interfaceCount;
}
if ( !foundv6 && loopbackv6 )
{
dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n",
loopbackv6->ifa_name ? loopbackv6->ifa_name : "<null>", loopbackv6->ifa_extra.index, loopbackv6->ifa_addr );
err = SetupInterface( inMDNS, loopbackv6, &ifd );
require_noerr( err, exit );
#if( MDNS_WINDOWS_ENABLE_IPV6 )
// If we're on a platform that doesn't have WSARecvMsg(), there's no way
// of determing the destination address of a packet that is sent to us.
// For multicast packets, that's easy to determine. But for the unicast
// sockets, we'll fake it by taking the address of the first interface
// that is successfully setup.
if ( !foundUnicastSock6DestAddr )
{
inMDNS->p->unicastSock6.addr = ifd->sock.addr;
foundUnicastSock6DestAddr = TRUE;
}
#endif
*next = ifd;
next = &ifd->next;
++inMDNS->p->interfaceCount;
}
CheckFileShares( inMDNS );
exit:
if( err )
{
TearDownInterfaceList( inMDNS );
}
if( addrs )
{
freeifaddrs( addrs );
}
dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list done (err=%d %m)\n", err, err );
return( err );
}
//===========================================================================================================================
// TearDownInterfaceList
//===========================================================================================================================
mStatus TearDownInterfaceList( mDNS * const inMDNS )
{
mDNSInterfaceData ** p;
mDNSInterfaceData * ifd;
dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" );
check( inMDNS );
check( inMDNS->p );
// Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache.
// Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache
// so that remove events that occur after an interface goes away can still report the correct interface.
p = &inMDNS->p->inactiveInterfaceList;
while( *p )
{
ifd = *p;
if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) ifd ) > 0 )
{
p = &ifd->next;
continue;
}
dlog( kDebugLevelInfo, DEBUG_NAME "freeing unreferenced, inactive interface %#p %#a\n", ifd, &ifd->interfaceInfo.ip );
*p = ifd->next;
QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) ifd );
}
// Tear down all the interfaces.
while( inMDNS->p->interfaceList )
{
ifd = inMDNS->p->interfaceList;
inMDNS->p->interfaceList = ifd->next;
TearDownInterface( inMDNS, ifd );
}
inMDNS->p->interfaceCount = 0;
dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list done\n" );
return( mStatus_NoError );
}
//===========================================================================================================================
// SetupInterface
//===========================================================================================================================
mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD )
{
mDNSInterfaceData * ifd;
mDNSInterfaceData * p;
mStatus err;
ifd = NULL;
dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface\n" );
check( inMDNS );
check( inMDNS->p );
check( inIFA );
check( inIFA->ifa_addr );
check( outIFD );
// Allocate memory for the interface and initialize it.
ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
require_action( ifd, exit, err = mStatus_NoMemoryErr );
ifd->sock.fd = kInvalidSocketRef;
ifd->sock.overlapped.pending = FALSE;
ifd->sock.ifd = ifd;
ifd->sock.next = NULL;
ifd->sock.m = inMDNS;
ifd->index = inIFA->ifa_extra.index;
ifd->scopeID = inIFA->ifa_extra.index;
check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) );
strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 );
ifd->name[ sizeof( ifd->name ) - 1 ] = '\0';
strncpy(ifd->interfaceInfo.ifname, inIFA->ifa_name, sizeof(ifd->interfaceInfo.ifname));
ifd->interfaceInfo.ifname[sizeof(ifd->interfaceInfo.ifname)-1] = 0;
// We always send and receive using IPv4, but to reduce traffic, we send and receive using IPv6 only on interfaces
// that have no routable IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being
// on a large configured network, which means there's a good chance that most or all the other devices on that
// network should also have v4. By doing this we lose the ability to talk to true v6-only devices on that link,
// but we cut the packet rate in half. At this time, reducing the packet rate is more important than v6-only
// devices on a large configured network, so we are willing to make that sacrifice.
ifd->interfaceInfo.McastTxRx = ( ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTTOPOINT ) ) ? mDNStrue : mDNSfalse;
ifd->interfaceInfo.InterfaceID = NULL;
for( p = inMDNS->p->interfaceList; p; p = p->next )
{
if ( strcmp( p->name, ifd->name ) == 0 )
{
if (!ifd->interfaceInfo.InterfaceID)
{
ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) p;
}
if ( ( inIFA->ifa_addr->sa_family != AF_INET ) &&
( p->interfaceInfo.ip.type == mDNSAddrType_IPv4 ) &&
( p->interfaceInfo.ip.ip.v4.b[ 0 ] != 169 || p->interfaceInfo.ip.ip.v4.b[ 1 ] != 254 ) )
{
ifd->interfaceInfo.McastTxRx = mDNSfalse;
}
break;
}
}
if ( !ifd->interfaceInfo.InterfaceID )
{
ifd->interfaceInfo