/* -*- 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( ®Domains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains ); while ( regDomains ) { DNameListElem * current = regDomains; SetDomainSecret( m, ¤t->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.InterfaceID = (mDNSInterfaceID) ifd; } // Set up a socket for this interface (if needed). if( ifd->interfaceInfo.McastTxRx ) { DWORD size; err = SetupSocket( inMDNS, inIFA->ifa_addr, MulticastDNSPort, &ifd->sock.fd ); require_noerr( err, exit ); ifd->sock.addr = ( inIFA->ifa_addr->sa_family == AF_INET6 ) ? AllDNSLinkGroup_v6 : AllDNSLinkGroup_v4; ifd->sock.port = MulticastDNSPort; // Get a ptr to the WSARecvMsg function, if supported. Otherwise, we'll fallback to recvfrom. err = WSAIoctl( ifd->sock.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &ifd->sock.recvMsgPtr, sizeof( ifd->sock.recvMsgPtr ), &size, NULL, NULL ); if ( err ) { ifd->sock.recvMsgPtr = NULL; } } if ( inIFA->ifa_dhcpEnabled && ( inIFA->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) ) { inMDNS->p->nextDHCPLeaseExpires = inIFA->ifa_dhcpLeaseExpires; } ifd->interfaceInfo.NetWake = inIFA->ifa_womp; // Register this interface with mDNS. err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL ); require_noerr( err, exit ); err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &ifd->interfaceInfo.mask, NULL ); require_noerr( err, exit ); memcpy( ifd->interfaceInfo.MAC.b, inIFA->ifa_physaddr, sizeof( ifd->interfaceInfo.MAC.b ) ); ifd->interfaceInfo.Advertise = ( mDNSu8 ) inMDNS->AdvertiseLocalAddresses; if ( ifd->sock.fd != kInvalidSocketRef ) { err = UDPBeginRecv( &ifd->sock ); require_noerr( err, exit ); } err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo, mDNSfalse ); require_noerr( err, exit ); ifd->hostRegistered = mDNStrue; dlog( kDebugLevelInfo, DEBUG_NAME "Registered interface %##a with mDNS\n", inIFA->ifa_addr ); // Success! *outIFD = ifd; ifd = NULL; exit: if( ifd ) { TearDownInterface( inMDNS, ifd ); } dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface done (err=%d %m)\n", err, err ); return( err ); } //=========================================================================================================================== // TearDownInterface //=========================================================================================================================== mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ) { check( inMDNS ); check( inIFD ); // Deregister this interface with mDNS. dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering interface %#a with mDNS\n", &inIFD->interfaceInfo.ip ); if( inIFD->hostRegistered ) { inIFD->hostRegistered = mDNSfalse; mDNS_DeregisterInterface( inMDNS, &inIFD->interfaceInfo, mDNSfalse ); } // Tear down the multicast socket. UDPCloseSocket( &inIFD->sock ); // If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps // the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it. if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) inIFD ) > 0 ) { inIFD->next = inMDNS->p->inactiveInterfaceList; inMDNS->p->inactiveInterfaceList = inIFD; dlog( kDebugLevelInfo, DEBUG_NAME "deferring free of interface %#p %#a\n", inIFD, &inIFD->interfaceInfo.ip ); } else { dlog( kDebugLevelInfo, DEBUG_NAME "freeing interface %#p %#a immediately\n", inIFD, &inIFD->interfaceInfo.ip ); QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) inIFD ); } return( mStatus_NoError ); } mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD ) { free( inIFD ); } //=========================================================================================================================== // SetupSocket //=========================================================================================================================== mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef ) { mStatus err; SocketRef sock; int option; DWORD bytesReturned = 0; BOOL behavior = FALSE; DEBUG_UNUSED( inMDNS ); dlog( kDebugLevelTrace, DEBUG_NAME "setting up socket %##a\n", inAddr ); check( inMDNS ); check( outSocketRef ); // Set up an IPv4 or IPv6 UDP socket. sock = socket( inAddr->sa_family, SOCK_DGRAM, IPPROTO_UDP ); err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); require_noerr( err, exit ); // Turn on reuse address option so multiple servers can listen for Multicast DNS packets, // if we're creating a multicast socket if ( port.NotAnInteger ) { option = 1; err = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // <rdar://problem/7894393> Bonjour for Windows broken on Windows XP // // Not sure why, but the default behavior for sockets is to behave incorrectly // when using them in Overlapped I/O mode on XP. According to MSDN: // // SIO_UDP_CONNRESET (opcode setting: I, T==3) // Windows XP: Controls whether UDP PORT_UNREACHABLE messages are reported. Set to TRUE to enable reporting. // Set to FALSE to disable reporting. // // Packet traces from misbehaving Bonjour installations showed that ICMP port unreachable // messages were being sent to us after we sent out packets to a multicast address. This is clearly // incorrect behavior, but should be harmless. However, after receiving a port unreachable error, WinSock // will no longer receive any packets from that socket, which is not harmless. This behavior is only // seen on XP. // // So we turn off port unreachable reporting to make sure our sockets that are reading // multicast packets function correctly under all circumstances. err = WSAIoctl( sock, SIO_UDP_CONNRESET, &behavior, sizeof(behavior), NULL, 0, &bytesReturned, NULL, NULL ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); if( inAddr->sa_family == AF_INET ) { mDNSv4Addr ipv4; struct sockaddr_in sa4; struct ip_mreq mreqv4; // Bind the socket to the desired port ipv4.NotAnInteger = ( (const struct sockaddr_in *) inAddr )->sin_addr.s_addr; mDNSPlatformMemZero( &sa4, sizeof( sa4 ) ); sa4.sin_family = AF_INET; sa4.sin_port = port.NotAnInteger; sa4.sin_addr.s_addr = ipv4.NotAnInteger; err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); // Turn on option to receive destination addresses and receiving interface. option = 1; err = setsockopt( sock, IPPROTO_IP, IP_PKTINFO, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); if (port.NotAnInteger) { // Join the all-DNS multicast group so we receive Multicast DNS packets mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; mreqv4.imr_interface.s_addr = ipv4.NotAnInteger; err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqv4, sizeof( mreqv4 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Specify the interface to send multicast packets on this socket. sa4.sin_addr.s_addr = ipv4.NotAnInteger; err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &sa4.sin_addr, sizeof( sa4.sin_addr ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). option = 1; err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // Send unicast packets with TTL 255 (helps against spoofing). option = 255; err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Send multicast packets with TTL 255 (helps against spoofing). option = 255; err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } else if( inAddr->sa_family == AF_INET6 ) { struct sockaddr_in6 * sa6p; struct sockaddr_in6 sa6; struct ipv6_mreq mreqv6; sa6p = (struct sockaddr_in6 *) inAddr; // Bind the socket to the desired port mDNSPlatformMemZero( &sa6, sizeof( sa6 ) ); sa6.sin6_family = AF_INET6; sa6.sin6_port = port.NotAnInteger; sa6.sin6_flowinfo = 0; sa6.sin6_addr = sa6p->sin6_addr; sa6.sin6_scope_id = sa6p->sin6_scope_id; err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); // Turn on option to receive destination addresses and receiving interface. option = 1; err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // We only want to receive IPv6 packets (not IPv4-mapped IPv6 addresses) because we have a separate socket // for IPv4, but the IPv6 stack in Windows currently doesn't support IPv4-mapped IPv6 addresses and doesn't // support the IPV6_V6ONLY socket option so the following code would typically not be executed (or needed). #if( defined( IPV6_V6ONLY ) ) option = 1; err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); #endif if ( port.NotAnInteger ) { // Join the all-DNS multicast group so we receive Multicast DNS packets. mreqv6.ipv6mr_multiaddr = *( (struct in6_addr *) &AllDNSLinkGroup_v6.ip.v6 ); mreqv6.ipv6mr_interface = sa6p->sin6_scope_id; err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqv6, sizeof( mreqv6 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Specify the interface to send multicast packets on this socket. option = (int) sa6p->sin6_scope_id; err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). option = 1; err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // Send unicast packets with TTL 255 (helps against spoofing). option = 255; err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Send multicast packets with TTL 255 (helps against spoofing). option = 255; err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &option, sizeof( option ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } else { dlog( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inAddr->sa_family ); err = kUnsupportedErr; goto exit; } // Success! *outSocketRef = sock; sock = kInvalidSocketRef; err = mStatus_NoError; exit: if( IsValidSocket( sock ) ) { close_compat( sock ); } return( err ); } //=========================================================================================================================== // SetupSocket //=========================================================================================================================== mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort ) { mStatus err; check( inSA ); check( outIP ); if( inSA->sa_family == AF_INET ) { struct sockaddr_in * sa4; sa4 = (struct sockaddr_in *) inSA; outIP->type = mDNSAddrType_IPv4; outIP->ip.v4.NotAnInteger = sa4->sin_addr.s_addr; if( outPort ) { outPort->NotAnInteger = sa4->sin_port; } err = mStatus_NoError; } else if( inSA->sa_family == AF_INET6 ) { struct sockaddr_in6 * sa6; sa6 = (struct sockaddr_in6 *) inSA; outIP->type = mDNSAddrType_IPv6; outIP->ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) ) { outIP->ip.v6.w[ 1 ] = 0; } if( outPort ) { outPort->NotAnInteger = sa6->sin6_port; } err = mStatus_NoError; } else { dlog( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family %d", __ROUTINE__, inSA->sa_family ); err = mStatus_BadParamErr; } return( err ); } #if 0 #pragma mark - #endif //=========================================================================================================================== // UDPBeginRecv //=========================================================================================================================== mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock ) { DWORD size; DWORD numTries; mStatus err; dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd ); require_action( sock != NULL, exit, err = mStatus_BadStateErr ); check( !sock->overlapped.pending ); // Initialize the buffer structure sock->overlapped.wbuf.buf = (char *) &sock->packet; sock->overlapped.wbuf.len = (u_long) sizeof( sock->packet ); sock->srcAddrLen = sizeof( sock->srcAddr ); // Initialize the overlapped structure ZeroMemory( &sock->overlapped.data, sizeof( OVERLAPPED ) ); sock->overlapped.data.hEvent = sock; numTries = 0; do { if ( sock->recvMsgPtr ) { sock->wmsg.name = ( LPSOCKADDR ) &sock->srcAddr; sock->wmsg.namelen = sock->srcAddrLen; sock->wmsg.lpBuffers = &sock->overlapped.wbuf; sock->wmsg.dwBufferCount = 1; sock->wmsg.Control.buf = ( CHAR* ) sock->controlBuffer; sock->wmsg.Control.len = sizeof( sock->controlBuffer ); sock->wmsg.dwFlags = 0; err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv ); err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), (OSStatus) WSAGetLastError(), kUnknownErr ); // <rdar://problem/7824093> iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate // // There seems to be a bug in some network device drivers that involves calling WSARecvMsg() in // overlapped i/o mode. Although all the parameters to WSARecvMsg() are correct, it returns a // WSAEFAULT error code when there is no actual error. We have found experientially that falling // back to using WSARecvFrom() when this happens will work correctly. if ( err == WSAEFAULT ) sock->recvMsgPtr = NULL; } else { DWORD flags = 0; err = WSARecvFrom( sock->fd, &sock->overlapped.wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv ); err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), ( OSStatus ) WSAGetLastError(), kUnknownErr ); } // According to MSDN <http://msdn.microsoft.com/en-us/library/ms741687(VS.85).aspx>: // // "WSAECONNRESET: For a UDP datagram socket, this error would indicate that a previous // send operation resulted in an ICMP "Port Unreachable" message." // // Because this is the case, we want to ignore this error and try again. Just in case // this is some kind of pathological condition, we'll break out of the retry loop // after 100 iterations require_action( !err || ( err == WSAECONNRESET ) || ( err == WSAEFAULT ), exit, err = WSAGetLastError() ); } while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) ); sock->overlapped.pending = TRUE; exit: if ( err ) { LogMsg( "WSARecvMsg failed (%d)\n", err ); } return err; } //=========================================================================================================================== // UDPEndRecv //=========================================================================================================================== mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags ) { UDPSocket * sock = NULL; ( void ) flags; dlog( kDebugLevelChatty, DEBUG_NAME "%s: err = %d, bytesTransferred = %d\n", __ROUTINE__, err, bytesTransferred ); require_action_quiet( err != WSA_OPERATION_ABORTED, exit, err = ( DWORD ) kUnknownErr ); require_noerr( err, exit ); sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL; require_action( sock != NULL, exit, err = ( DWORD ) kUnknownErr ); dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd ); sock->overlapped.error = err; sock->overlapped.bytesTransferred = bytesTransferred; check( sock->overlapped.pending ); sock->overlapped.pending = FALSE; // Translate the source of this packet into mDNS data types SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &sock->overlapped.srcAddr, &sock->overlapped.srcPort ); // Initialize the destination of this packet. Just in case // we can't determine this info because we couldn't call // WSARecvMsg (recvMsgPtr) sock->overlapped.dstAddr = sock->addr; sock->overlapped.dstPort = sock->port; if ( sock->recvMsgPtr ) { LPWSACMSGHDR header; LPWSACMSGHDR last = NULL; int count = 0; // Parse the control information. Reject packets received on the wrong interface. // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop // // There seems to be an interaction between Bullguard and this next bit of code. // When a user's machine is running Bullguard, the control information that is // returned is corrupted, and the code would go into an infinite loop. We'll add // two bits of defensive coding here. The first will check that each pointer to // the LPWSACMSGHDR that is returned in the for loop is different than the last. // This fixes the problem with Bullguard. The second will break out of this loop // after 100 iterations, just in case the corruption isn't caught by the first // check. for ( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) ) { if ( ( header != last ) && ( ++count < 100 ) ) { last = header; if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) ) { IN_PKTINFO * ipv4PacketInfo; ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header ); if ( sock->ifd != NULL ) { require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr ); } sock->overlapped.dstAddr.type = mDNSAddrType_IPv4; sock->overlapped.dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr; } else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) ) { IN6_PKTINFO * ipv6PacketInfo; ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header ); if ( sock->ifd != NULL ) { require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr ); } sock->overlapped.dstAddr.type = mDNSAddrType_IPv6; sock->overlapped.dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr ); } } else { static BOOL loggedMessage = FALSE; if ( !loggedMessage ) { LogMsg( "UDPEndRecv: WSARecvMsg control information error." ); loggedMessage = TRUE; } break; } } } dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", bytesTransferred ); dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &sock->overlapped.srcAddr, ntohs( sock->overlapped.srcPort.NotAnInteger ) ); dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &sock->overlapped.dstAddr, ntohs( sock->overlapped.dstPort.NotAnInteger ) ); if ( sock->ifd != NULL ) { dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index ); } dlog( kDebugLevelChatty, DEBUG_NAME "\n" ); // Queue this socket AddToTail( &gUDPDispatchableSockets, sock ); exit: return; } //=========================================================================================================================== // InterfaceListDidChange //=========================================================================================================================== void InterfaceListDidChange( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" ); check( inMDNS ); // Tear down the existing interfaces and set up new ones using the new IP info. err = TearDownInterfaceList( inMDNS ); check_noerr( err ); err = SetupInterfaceList( inMDNS ); check_noerr( err ); err = uDNS_SetupDNSConfig( inMDNS ); check_noerr( err ); // Inform clients of the change. mDNS_ConfigChanged(inMDNS); // Force mDNS to update. mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this } //=========================================================================================================================== // ComputerDescriptionDidChange //=========================================================================================================================== void ComputerDescriptionDidChange( mDNS * const inMDNS ) { dlog( kDebugLevelInfo, DEBUG_NAME "computer description has changed\n" ); check( inMDNS ); // redo the names SetupNiceName( inMDNS ); } //=========================================================================================================================== // TCPIPConfigDidChange //=========================================================================================================================== void TCPIPConfigDidChange( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelInfo, DEBUG_NAME "TCP/IP config has changed\n" ); check( inMDNS ); err = uDNS_SetupDNSConfig( inMDNS ); check_noerr( err ); } //=========================================================================================================================== // DynDNSConfigDidChange //=========================================================================================================================== void DynDNSConfigDidChange( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelInfo, DEBUG_NAME "DynDNS config has changed\n" ); check( inMDNS ); SetDomainSecrets( inMDNS ); err = uDNS_SetupDNSConfig( inMDNS ); check_noerr( err ); } //=========================================================================================================================== // FileSharingDidChange //=========================================================================================================================== void FileSharingDidChange( mDNS * const inMDNS ) { dlog( kDebugLevelInfo, DEBUG_NAME "File shares has changed\n" ); check( inMDNS ); CheckFileShares( inMDNS ); } //=========================================================================================================================== // FilewallDidChange //=========================================================================================================================== void FirewallDidChange( mDNS * const inMDNS ) { dlog( kDebugLevelInfo, DEBUG_NAME "Firewall has changed\n" ); check( inMDNS ); CheckFileShares( inMDNS ); } #if 0 #pragma mark - #pragma mark == Utilities == #endif //=========================================================================================================================== // getifaddrs //=========================================================================================================================== mDNSlocal int getifaddrs( struct ifaddrs **outAddrs ) { int err; #if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) // Try to the load the GetAdaptersAddresses function from the IP Helpers DLL. This API is only available on Windows // XP or later. Looking up the symbol at runtime allows the code to still work on older systems without that API. if( !gIPHelperLibraryInstance ) { gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); if( gIPHelperLibraryInstance ) { gGetAdaptersAddressesFunctionPtr = (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" ); if( !gGetAdaptersAddressesFunctionPtr ) { BOOL ok; ok = FreeLibrary( gIPHelperLibraryInstance ); check_translated_errno( ok, GetLastError(), kUnknownErr ); gIPHelperLibraryInstance = NULL; } } } // Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code. // <rdar://problem/4278934> Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 fails // <rdar://problem/6145913> Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 returns no addrs if( !gGetAdaptersAddressesFunctionPtr || ( ( ( err = getifaddrs_ipv6( outAddrs ) ) != mStatus_NoError ) || ( ( outAddrs != NULL ) && ( *outAddrs == NULL ) ) ) ) { err = getifaddrs_ipv4( outAddrs ); require_noerr( err, exit ); } #else err = getifaddrs_ipv4( outAddrs ); require_noerr( err, exit ); #endif exit: return( err ); } #if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) //=========================================================================================================================== // getifaddrs_ipv6 //=========================================================================================================================== mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) { DWORD err; int i; DWORD flags; struct ifaddrs * head; struct ifaddrs ** next; IP_ADAPTER_ADDRESSES * iaaList; ULONG iaaListSize; IP_ADAPTER_ADDRESSES * iaa; size_t size; struct ifaddrs * ifa; check( gGetAdaptersAddressesFunctionPtr ); head = NULL; next = &head; iaaList = NULL; // 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_INCLUDE_PREFIX | 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 ) { int addrIndex; IP_ADAPTER_UNICAST_ADDRESS * addr; DWORD ipv6IfIndex; IP_ADAPTER_PREFIX * firstPrefix; if( iaa->IfIndex > 0xFFFFFF ) { dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv4 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->IfIndex ); } if( iaa->Ipv6IfIndex > 0xFF ) { dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv6 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->Ipv6IfIndex ); } // 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; firstPrefix = iaa->FirstPrefix; } else { ipv6IfIndex = 0; firstPrefix = NULL; } // Skip pseudo and tunnel interfaces. if( ( ( ipv6IfIndex == 1 ) && ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) ) || ( iaa->IfType == IF_TYPE_TUNNEL ) ) { continue; } // Add each address as a separate interface to emulate the way getifaddrs works. for( addrIndex = 0, addr = iaa->FirstUnicastAddress; addr; ++addrIndex, addr = addr->Next ) { int family; int prefixIndex; IP_ADAPTER_PREFIX * prefix; ULONG prefixLength; uint32_t ipv4Index; struct sockaddr_in ipv4Netmask; family = addr->Address.lpSockaddr->sa_family; if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue; // <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8 // Seems as if the problem here is a buggy implementation of some network interface // driver. It is reporting that is has a link-local address when it is actually // disconnected. This was causing a problem in AddressToIndexAndMask. // The solution is to call AddressToIndexAndMask first, and if unable to lookup // the address, to ignore that address. ipv4Index = 0; memset( &ipv4Netmask, 0, sizeof( ipv4Netmask ) ); if ( family == AF_INET ) { err = AddressToIndexAndMask( addr->Address.lpSockaddr, &ipv4Index, ( struct sockaddr* ) &ipv4Netmask ); if ( err ) { err = 0; continue; } } ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); require_action( ifa, exit, err = WSAENOBUFS ); *next = ifa; next = &ifa->ifa_next; // Get the name. size = strlen( iaa->AdapterName ) + 1; ifa->ifa_name = (char *) malloc( size ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_name, iaa->AdapterName, size ); // Get interface flags. ifa->ifa_flags = 0; if( iaa->OperStatus == IfOperStatusUp ) ifa->ifa_flags |= IFF_UP; if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK ) ifa->ifa_flags |= IFF_LOOPBACK; else if ( IsPointToPoint( addr ) ) ifa->ifa_flags |= IFF_POINTTOPOINT; if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) ) ifa->ifa_flags |= IFF_MULTICAST; // <rdar://problem/4045657> Interface index being returned is 512 // // Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes. // This code used to shift the IPv4 index up to ensure uniqueness between // it and IPv6 indexes. Although this worked, it was somewhat confusing to developers, who // then see interface indexes passed back that don't correspond to anything // that is seen in Win32 APIs or command line tools like "route". As a relatively // small percentage of developers are actively using IPv6, it seems to // make sense to make our use of IPv4 as confusion free as possible. // So now, IPv6 interface indexes will be shifted up by a // constant value which will serve to uniquely identify them, and we will // leave IPv4 interface indexes unmodified. switch( family ) { case AF_INET: ifa->ifa_extra.index = iaa->IfIndex; break; case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex + kIPv6IfIndexBase; break; default: break; } // Get lease lifetime if ( ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) && ( addr->LeaseLifetime != 0 ) && ( addr->ValidLifetime != 0xFFFFFFFF ) ) { ifa->ifa_dhcpEnabled = TRUE; ifa->ifa_dhcpLeaseExpires = time( NULL ) + addr->ValidLifetime; } else { ifa->ifa_dhcpEnabled = FALSE; ifa->ifa_dhcpLeaseExpires = 0; } if ( iaa->PhysicalAddressLength == sizeof( ifa->ifa_physaddr ) ) { memcpy( ifa->ifa_physaddr, iaa->PhysicalAddress, iaa->PhysicalAddressLength ); } // Because we don't get notified of womp changes, we're going to just assume // that all wired interfaces have it enabled. Before we go to sleep, we'll check // if the interface actually supports it, and update mDNS->SystemWakeOnLANEnabled // accordingly ifa->ifa_womp = ( iaa->IfType == IF_TYPE_ETHERNET_CSMACD ) ? mDNStrue : mDNSfalse; // Get address. switch( family ) { case AF_INET: case AF_INET6: ifa->ifa_addr = (struct sockaddr *) calloc( 1, (size_t) addr->Address.iSockaddrLength ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, addr->Address.lpSockaddr, (size_t) addr->Address.iSockaddrLength ); break; default: break; } check( ifa->ifa_addr ); // Get subnet mask (IPv4)/link prefix (IPv6). It is specified as a bit length (e.g. 24 for 255.255.255.0). prefixLength = 0; for( prefixIndex = 0, prefix = firstPrefix; prefix; ++prefixIndex, prefix = prefix->Next ) { if( ( prefix->Address.lpSockaddr->sa_family == family ) && ( prefixIndex == addrIndex ) ) { check_string( prefix->Address.lpSockaddr->sa_family == family, "addr family != netmask family" ); prefixLength = prefix->PrefixLength; break; } } switch( family ) { case AF_INET: { struct sockaddr_in * sa4; sa4 = (struct sockaddr_in *) calloc( 1, sizeof( *sa4 ) ); require_action( sa4, exit, err = WSAENOBUFS ); sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = ipv4Netmask.sin_addr.s_addr; dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv4 mask = %s\n", __ROUTINE__, inet_ntoa( sa4->sin_addr ) ); ifa->ifa_netmask = (struct sockaddr *) sa4; break; } case AF_INET6: { struct sockaddr_in6 * sa6; int len; int maskIndex; uint8_t maskByte; require_action( prefixLength <= 128, exit, err = ERROR_INVALID_DATA ); sa6 = (struct sockaddr_in6 *) calloc( 1, sizeof( *sa6 ) ); require_action( sa6, exit, err = WSAENOBUFS ); sa6->sin6_family = AF_INET6; if( prefixLength == 0 ) { dlog( kDebugLevelWarning, DEBUG_NAME "%s: IPv6 link prefix 0, defaulting to /128\n", __ROUTINE__ ); prefixLength = 128; } maskIndex = 0; for( len = (int) prefixLength; len > 0; len -= 8 ) { if( len >= 8 ) maskByte = 0xFF; else maskByte = (uint8_t)( ( 0xFFU << ( 8 - len ) ) & 0xFFU ); sa6->sin6_addr.s6_addr[ maskIndex++ ] = maskByte; } ifa->ifa_netmask = (struct sockaddr *) sa6; break; } default: break; } } } // Success! if( outAddrs ) { *outAddrs = head; head = NULL; } err = ERROR_SUCCESS; exit: if( head ) { freeifaddrs( head ); } if( iaaList ) { free( iaaList ); } return( (int) err ); } #endif // MDNS_WINDOWS_USE_IPV6_IF_ADDRS //=========================================================================================================================== // getifaddrs_ipv4 //=========================================================================================================================== mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ) { int err; SOCKET sock; DWORD size; DWORD actualSize; INTERFACE_INFO * buffer; INTERFACE_INFO * tempBuffer; INTERFACE_INFO * ifInfo; int n; int i; struct ifaddrs * head; struct ifaddrs ** next; struct ifaddrs * ifa; sock = INVALID_SOCKET; buffer = NULL; head = NULL; next = &head; // Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a // way to determine the size of the interface list beforehand, we have to start with an initial size guess and // call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); require_noerr( err, exit ); n = 0; size = 16 * sizeof( INTERFACE_INFO ); for( ;; ) { tempBuffer = (INTERFACE_INFO *) realloc( buffer, size ); require_action( tempBuffer, exit, err = WSAENOBUFS ); buffer = tempBuffer; err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL ); if( err == 0 ) { break; } ++n; require_action( n < 100, exit, err = WSAEADDRNOTAVAIL ); size += ( 16 * sizeof( INTERFACE_INFO ) ); } check( actualSize <= size ); check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 ); n = (int)( actualSize / sizeof( INTERFACE_INFO ) ); // Process the raw interface list and build a linked list of IPv4 interfaces. for( i = 0; i < n; ++i ) { uint32_t ifIndex; struct sockaddr_in netmask; ifInfo = &buffer[ i ]; if( ifInfo->iiAddress.Address.sa_family != AF_INET ) { continue; } // <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8 // See comment in getifaddrs_ipv6 ifIndex = 0; memset( &netmask, 0, sizeof( netmask ) ); err = AddressToIndexAndMask( ( struct sockaddr* ) &ifInfo->iiAddress.AddressIn, &ifIndex, ( struct sockaddr* ) &netmask ); if ( err ) { continue; } ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); require_action( ifa, exit, err = WSAENOBUFS ); *next = ifa; next = &ifa->ifa_next; // Get the name. ifa->ifa_name = (char *) malloc( 16 ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); sprintf( ifa->ifa_name, "%d", i + 1 ); // Get interface flags. ifa->ifa_flags = (u_int) ifInfo->iiFlags; // Get addresses. if ( ifInfo->iiAddress.Address.sa_family == AF_INET ) { struct sockaddr_in * sa4; sa4 = &ifInfo->iiAddress.AddressIn; ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) ); require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); // <rdar://problem/4076478> Service won't start on Win2K. The address // family field was not being initialized. ifa->ifa_netmask->sa_family = AF_INET; ( ( struct sockaddr_in* ) ifa->ifa_netmask )->sin_addr = netmask.sin_addr; ifa->ifa_extra.index = ifIndex; } else { // Emulate an interface index. ifa->ifa_extra.index = (uint32_t)( i + 1 ); } } // Success! if( outAddrs ) { *outAddrs = head; head = NULL; } err = 0; exit: if( head ) { freeifaddrs( head ); } if( buffer ) { free( buffer ); } if( sock != INVALID_SOCKET ) { closesocket( sock ); } return( err ); } //=========================================================================================================================== // freeifaddrs //=========================================================================================================================== mDNSlocal void freeifaddrs( struct ifaddrs *inIFAs ) { struct ifaddrs * p; struct ifaddrs * q; // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. for( p = inIFAs; p; p = q ) { q = p->ifa_next; if( p->ifa_name ) { free( p->ifa_name ); p->ifa_name = NULL; } if( p->ifa_addr ) { free( p->ifa_addr ); p->ifa_addr = NULL; } if( p->ifa_netmask ) { free( p->ifa_netmask ); p->ifa_netmask = NULL; } if( p->ifa_broadaddr ) { free( p->ifa_broadaddr ); p->ifa_broadaddr = NULL; } if( p->ifa_dstaddr ) { free( p->ifa_dstaddr ); p->ifa_dstaddr = NULL; } if( p->ifa_data ) { free( p->ifa_data ); p->ifa_data = NULL; } free( p ); } } //=========================================================================================================================== // GetPrimaryInterface //=========================================================================================================================== mDNSlocal DWORD GetPrimaryInterface() { PMIB_IPFORWARDTABLE pIpForwardTable = NULL; DWORD dwSize = 0; BOOL bOrder = FALSE; OSStatus err; DWORD index = 0; DWORD metric = 0; unsigned long int i; // Find out how big our buffer needs to be. err = GetIpForwardTable(NULL, &dwSize, bOrder); require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr ); // Allocate the memory for the table pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize ); require_action( pIpForwardTable, exit, err = kNoMemoryErr ); // Now get the table. err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); require_noerr( err, exit ); // Search for the row in the table we want. for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) { // Look for a default route if ( pIpForwardTable->table[i].dwForwardDest == 0 ) { if ( index && ( pIpForwardTable->table[i].dwForwardMetric1 >= metric ) ) { continue; } index = pIpForwardTable->table[i].dwForwardIfIndex; metric = pIpForwardTable->table[i].dwForwardMetric1; } } exit: if ( pIpForwardTable != NULL ) { free( pIpForwardTable ); } return index; } //=========================================================================================================================== // AddressToIndexAndMask //=========================================================================================================================== mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * addr, uint32_t * ifIndex, struct sockaddr * mask ) { // Before calling AddIPAddress we use GetIpAddrTable to get // an adapter to which we can add the IP. PMIB_IPADDRTABLE pIPAddrTable = NULL; DWORD dwSize = 0; mStatus err = mStatus_UnknownErr; DWORD i; // For now, this is only for IPv4 addresses. That is why we can safely cast // addr's to sockaddr_in. require_action( addr->sa_family == AF_INET, exit, err = mStatus_UnknownErr ); // Make an initial call to GetIpAddrTable to get the // necessary size into the dwSize variable for ( i = 0; i < 100; i++ ) { err = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); if ( err != ERROR_INSUFFICIENT_BUFFER ) { break; } pIPAddrTable = (MIB_IPADDRTABLE *) realloc( pIPAddrTable, dwSize ); require_action( pIPAddrTable, exit, err = WSAENOBUFS ); } require_noerr( err, exit ); err = mStatus_UnknownErr; for ( i = 0; i < pIPAddrTable->dwNumEntries; i++ ) { if ( ( ( struct sockaddr_in* ) addr )->sin_addr.s_addr == pIPAddrTable->table[i].dwAddr ) { *ifIndex = pIPAddrTable->table[i].dwIndex; ( ( struct sockaddr_in*) mask )->sin_addr.s_addr = pIPAddrTable->table[i].dwMask; err = mStatus_NoError; break; } } exit: if ( pIPAddrTable ) { free( pIPAddrTable ); } return err; } //=========================================================================================================================== // CanReceiveUnicast //=========================================================================================================================== mDNSlocal mDNSBool CanReceiveUnicast( void ) { mDNSBool ok; SocketRef sock; struct sockaddr_in addr; // Try to bind to the port without the SO_REUSEADDR option to test if someone else has already bound to it. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); check_translated_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); ok = IsValidSocket( sock ); if( ok ) { mDNSPlatformMemZero( &addr, sizeof( addr ) ); addr.sin_family = AF_INET; addr.sin_port = MulticastDNSPort.NotAnInteger; addr.sin_addr.s_addr = htonl( INADDR_ANY ); ok = ( bind( sock, (struct sockaddr *) &addr, sizeof( addr ) ) == 0 ); close_compat( sock ); } dlog( kDebugLevelInfo, DEBUG_NAME "Unicast UDP responses %s\n", ok ? "okay" : "*not allowed*" ); return( ok ); } //=========================================================================================================================== // IsPointToPoint //=========================================================================================================================== mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr ) { struct ifaddrs * addrs = NULL; struct ifaddrs * p = NULL; OSStatus err; mDNSBool ret = mDNSfalse; // For now, only works for IPv4 interfaces if ( addr->Address.lpSockaddr->sa_family == AF_INET ) { // The getifaddrs_ipv4 call will give us correct information regarding IFF_POINTTOPOINT flags. err = getifaddrs_ipv4( &addrs ); require_noerr( err, exit ); for ( p = addrs; p; p = p->ifa_next ) { if ( ( addr->Address.lpSockaddr->sa_family == p->ifa_addr->sa_family ) && ( ( ( struct sockaddr_in* ) addr->Address.lpSockaddr )->sin_addr.s_addr == ( ( struct sockaddr_in* ) p->ifa_addr )->sin_addr.s_addr ) ) { ret = ( p->ifa_flags & IFF_POINTTOPOINT ) ? mDNStrue : mDNSfalse; break; } } } exit: if ( addrs ) { freeifaddrs( addrs ); } return ret; } //=========================================================================================================================== // GetWindowsVersionString //=========================================================================================================================== mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ) { #if( !defined( VER_PLATFORM_WIN32_CE ) ) #define VER_PLATFORM_WIN32_CE 3 #endif OSStatus err; OSVERSIONINFO osInfo; BOOL ok; const char * versionString; DWORD platformID; DWORD majorVersion; DWORD minorVersion; DWORD buildNumber; versionString = "unknown Windows version"; osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); ok = GetVersionEx( &osInfo ); err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); require_noerr( err, exit ); platformID = osInfo.dwPlatformId; majorVersion = osInfo.dwMajorVersion; minorVersion = osInfo.dwMinorVersion; buildNumber = osInfo.dwBuildNumber & 0xFFFF; if( ( platformID == VER_PLATFORM_WIN32_WINDOWS ) && ( majorVersion == 4 ) ) { if( ( minorVersion < 10 ) && ( buildNumber == 950 ) ) { versionString = "Windows 95"; } else if( ( minorVersion < 10 ) && ( ( buildNumber > 950 ) && ( buildNumber <= 1080 ) ) ) { versionString = "Windows 95 SP1"; } else if( ( minorVersion < 10 ) && ( buildNumber > 1080 ) ) { versionString = "Windows 95 OSR2"; } else if( ( minorVersion == 10 ) && ( buildNumber == 1998 ) ) { versionString = "Windows 98"; } else if( ( minorVersion == 10 ) && ( ( buildNumber > 1998 ) && ( buildNumber < 2183 ) ) ) { versionString = "Windows 98 SP1"; } else if( ( minorVersion == 10 ) && ( buildNumber >= 2183 ) ) { versionString = "Windows 98 SE"; } else if( minorVersion == 90 ) { versionString = "Windows ME"; } } else if( platformID == VER_PLATFORM_WIN32_NT ) { if( ( majorVersion == 3 ) && ( minorVersion == 51 ) ) { versionString = "Windows NT 3.51"; } else if( ( majorVersion == 4 ) && ( minorVersion == 0 ) ) { versionString = "Windows NT 4"; } else if( ( majorVersion == 5 ) && ( minorVersion == 0 ) ) { versionString = "Windows 2000"; } else if( ( majorVersion == 5 ) && ( minorVersion == 1 ) ) { versionString = "Windows XP"; } else if( ( majorVersion == 5 ) && ( minorVersion == 2 ) ) { versionString = "Windows Server 2003"; } } else if( platformID == VER_PLATFORM_WIN32_CE ) { versionString = "Windows CE"; } exit: if( inBuffer && ( inBufferSize > 0 ) ) { inBufferSize -= 1; strncpy( inBuffer, versionString, inBufferSize ); inBuffer[ inBufferSize ] = '\0'; } return( err ); } //=========================================================================================================================== // RegQueryString //=========================================================================================================================== mDNSlocal mStatus RegQueryString( HKEY key, LPCSTR valueName, LPSTR * string, DWORD * stringLen, DWORD * enabled ) { DWORD type; int i; mStatus err; *stringLen = MAX_ESCAPED_DOMAIN_NAME; *string = NULL; i = 0; do { if ( *string ) { free( *string ); } *string = (char*) malloc( *stringLen ); require_action( *string, exit, err = mStatus_NoMemoryErr ); err = RegQueryValueExA( key, valueName, 0, &type, (LPBYTE) *string, stringLen ); i++; } while ( ( err == ERROR_MORE_DATA ) && ( i < 100 ) ); require_noerr_quiet( err, exit ); if ( enabled ) { DWORD dwSize = sizeof( DWORD ); err = RegQueryValueEx( key, TEXT("Enabled"), NULL, NULL, (LPBYTE) enabled, &dwSize ); check_noerr( err ); err = kNoErr; } exit: return err; } //=========================================================================================================================== // StringToAddress //=========================================================================================================================== mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string ) { struct sockaddr_in6 sa6; struct sockaddr_in sa4; INT dwSize; mStatus err; sa6.sin6_family = AF_INET6; dwSize = sizeof( sa6 ); err = WSAStringToAddressA( string, AF_INET6, NULL, (struct sockaddr*) &sa6, &dwSize ); if ( err == mStatus_NoError ) { err = SetupAddr( ip, (struct sockaddr*) &sa6 ); require_noerr( err, exit ); } else { sa4.sin_family = AF_INET; dwSize = sizeof( sa4 ); err = WSAStringToAddressA( string, AF_INET, NULL, (struct sockaddr*) &sa4, &dwSize ); err = translate_errno( err == 0, WSAGetLastError(), kUnknownErr ); require_noerr( err, exit ); err = SetupAddr( ip, (struct sockaddr*) &sa4 ); require_noerr( err, exit ); } exit: return err; } //=========================================================================================================================== // myGetIfAddrs //=========================================================================================================================== mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) { static struct ifaddrs *ifa = NULL; if (refresh && ifa) { freeifaddrs(ifa); ifa = NULL; } if (ifa == NULL) { getifaddrs(&ifa); } return ifa; } //=========================================================================================================================== // TCHARtoUTF8 //=========================================================================================================================== mDNSlocal OSStatus TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize ) { #if( defined( UNICODE ) || defined( _UNICODE ) ) OSStatus err; int len; len = WideCharToMultiByte( CP_UTF8, 0, inString, -1, inBuffer, (int) inBufferSize, NULL, NULL ); err = translate_errno( len > 0, errno_compat(), kUnknownErr ); require_noerr( err, exit ); exit: return( err ); #else return( WindowsLatin1toUTF8( inString, inBuffer, inBufferSize ) ); #endif } //=========================================================================================================================== // WindowsLatin1toUTF8 //=========================================================================================================================== mDNSlocal OSStatus WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize ) { OSStatus err; WCHAR * utf16; int len; utf16 = NULL; // Windows doesn't support going directly from Latin-1 to UTF-8 so we have to go from Latin-1 to UTF-16 first. len = MultiByteToWideChar( CP_ACP, 0, inString, -1, NULL, 0 ); err = translate_errno( len > 0, errno_compat(), kUnknownErr ); require_noerr( err, exit ); utf16 = (WCHAR *) malloc( len * sizeof( *utf16 ) ); require_action( utf16, exit, err = kNoMemoryErr ); len = MultiByteToWideChar( CP_ACP, 0, inString, -1, utf16, len ); err = translate_errno( len > 0, errno_compat(), kUnknownErr ); require_noerr( err, exit ); // Now convert the temporary UTF-16 to UTF-8. len = WideCharToMultiByte( CP_UTF8, 0, utf16, -1, inBuffer, (int) inBufferSize, NULL, NULL ); err = translate_errno( len > 0, errno_compat(), kUnknownErr ); require_noerr( err, exit ); exit: if( utf16 ) free( utf16 ); return( err ); } //=========================================================================================================================== // TCPCloseSocket //=========================================================================================================================== mDNSlocal void TCPCloseSocket( TCPSocket * sock ) { dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd ); RemoveFromList( &gTCPDispatchableSockets, sock ); if ( sock->fd != INVALID_SOCKET ) { closesocket( sock->fd ); sock->fd = INVALID_SOCKET; } } //=========================================================================================================================== // TCPFreeSocket //=========================================================================================================================== mDNSlocal void CALLBACK TCPFreeSocket( TCPSocket *sock ) { check( sock ); dlog( kDebugLevelChatty, DEBUG_NAME "freeing TCPSocket 0x%x:%d\n", sock, sock->fd ); if ( sock->connectEvent ) { CloseHandle( sock->connectEvent ); sock->connectEvent = NULL; } if ( sock->fd != INVALID_SOCKET ) { closesocket( sock->fd ); sock->fd = INVALID_SOCKET; } free( sock ); } //=========================================================================================================================== // UDPCloseSocket //=========================================================================================================================== mDNSlocal void UDPCloseSocket( UDPSocket * sock ) { dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd ); RemoveFromList( &gUDPDispatchableSockets, sock ); if ( sock->fd != INVALID_SOCKET ) { closesocket( sock->fd ); sock->fd = INVALID_SOCKET; } } //=========================================================================================================================== // UDPFreeSocket //=========================================================================================================================== mDNSlocal void CALLBACK UDPFreeSocket( UDPSocket * sock ) { check( sock ); dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d\n", sock->fd ); if ( sock->fd != INVALID_SOCKET ) { closesocket( sock->fd ); sock->fd = INVALID_SOCKET; } free( sock ); } //=========================================================================================================================== // SetupAddr //=========================================================================================================================== mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) { if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } if (sa->sa_family == AF_INET) { struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; ip->type = mDNSAddrType_IPv4; ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; return(mStatus_NoError); } if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; ip->type = mDNSAddrType_IPv6; if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.u.Word[1] = 0; ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; return(mStatus_NoError); } LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); return(mStatus_Invalid); } mDNSlocal void GetDDNSFQDN( domainname *const fqdn ) { LPSTR name = NULL; DWORD dwSize; DWORD enabled; HKEY key = NULL; OSStatus err; check( fqdn ); // Initialize fqdn->c[0] = '\0'; // Get info from Bonjour registry key err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key ); require_noerr( err, exit ); err = RegQueryString( key, "", &name, &dwSize, &enabled ); if ( !err && ( name[0] != '\0' ) && enabled ) { if ( !MakeDomainNameFromDNSNameString( fqdn, name ) || !fqdn->c[0] ) { dlog( kDebugLevelError, "bad DDNS host name in registry: %s", name[0] ? name : "(unknown)"); } } exit: if ( key ) { RegCloseKey( key ); key = NULL; } if ( name ) { free( name ); name = NULL; } } #ifdef UNICODE mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey ) #else mDNSlocal void GetDDNSConfig( DNameListElem ** domains, LPCSTR lpSubKey ) #endif { char subKeyName[kRegistryMaxKeyLength + 1]; DWORD cSubKeys = 0; DWORD cbMaxSubKey; DWORD cchMaxClass; DWORD dwSize; HKEY key = NULL; HKEY subKey = NULL; domainname dname; DWORD i; OSStatus err; check( domains ); // Initialize *domains = NULL; err = RegCreateKey( HKEY_LOCAL_MACHINE, lpSubKey, &key ); require_noerr( err, exit ); // Get information about this node err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL ); require_noerr( err, exit ); for ( i = 0; i < cSubKeys; i++) { DWORD enabled; dwSize = kRegistryMaxKeyLength; err = RegEnumKeyExA( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL ); if ( !err ) { err = RegOpenKeyExA( key, subKeyName, 0, KEY_READ, &subKey ); require_noerr( err, exit ); dwSize = sizeof( DWORD ); err = RegQueryValueExA( subKey, "Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize ); if ( !err && ( subKeyName[0] != '\0' ) && enabled ) { if ( !MakeDomainNameFromDNSNameString( &dname, subKeyName ) || !dname.c[0] ) { dlog( kDebugLevelError, "bad DDNS domain in registry: %s", subKeyName[0] ? subKeyName : "(unknown)"); } else { DNameListElem * domain = (DNameListElem*) malloc( sizeof( DNameListElem ) ); require_action( domain, exit, err = mStatus_NoMemoryErr ); AssignDomainName(&domain->name, &dname); domain->next = *domains; *domains = domain; } } RegCloseKey( subKey ); subKey = NULL; } } exit: if ( subKey ) { RegCloseKey( subKey ); } if ( key ) { RegCloseKey( key ); } } mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain ) { char domainUTF8[ 256 ]; DomainAuthInfo *foundInList; DomainAuthInfo *ptr; char outDomain[ 256 ]; char outKey[ 256 ]; char outSecret[ 256 ]; OSStatus err; ConvertDomainNameToCString( inDomain, domainUTF8 ); // If we're able to find a secret for this domain if ( LsaGetSecret( domainUTF8, outDomain, sizeof( outDomain ), outKey, sizeof( outKey ), outSecret, sizeof( outSecret ) ) ) { domainname domain; domainname key; // Tell the core about this secret MakeDomainNameFromDNSNameString( &domain, outDomain ); MakeDomainNameFromDNSNameString( &key, outKey ); for (foundInList = m->AuthInfoList; foundInList; foundInList = foundInList->next) if (SameDomainName(&foundInList->domain, &domain ) ) break; ptr = foundInList; if (!ptr) { ptr = (DomainAuthInfo*)malloc(sizeof(DomainAuthInfo)); require_action( ptr, exit, err = mStatus_NoMemoryErr ); } err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL, 0, NULL ); require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) ); debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c); } exit: return; } mDNSlocal VOID CALLBACK CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue ) { mDNS * const m = ( mDNS * const ) arg; ( void ) dwTimerLowValue; ( void ) dwTimerHighValue; CheckFileShares( m ); } mDNSlocal unsigned __stdcall SMBRegistrationThread( void * arg ) { mDNS * const m = ( mDNS * const ) arg; DNSServiceRef sref = NULL; HANDLE handles[ 3 ]; mDNSu8 txtBuf[ 256 ]; mDNSu8 * txtPtr; size_t keyLen; size_t valLen; mDNSIPPort port = { { SMBPortAsNumber >> 8, SMBPortAsNumber & 0xFF } }; DNSServiceErrorType err; DEBUG_UNUSED( arg ); handles[ 0 ] = gSMBThreadStopEvent; handles[ 1 ] = gSMBThreadRegisterEvent; handles[ 2 ] = gSMBThreadDeregisterEvent; memset( txtBuf, 0, sizeof( txtBuf ) ); txtPtr = txtBuf; keyLen = strlen( "netbios=" ); valLen = strlen( m->p->nbname ); require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption *txtPtr++ = ( mDNSu8 ) ( keyLen + valLen ); memcpy( txtPtr, "netbios=", keyLen ); txtPtr += keyLen; if ( valLen ) { memcpy( txtPtr, m->p->nbname, valLen ); txtPtr += ( mDNSu8 ) valLen; } keyLen = strlen( "domain=" ); valLen = strlen( m->p->nbdomain ); require_action( valLen < 32, exit, err = kUnknownErr ); // This should never happen, but check to avoid further memory corruption *txtPtr++ = ( mDNSu8 )( keyLen + valLen ); memcpy( txtPtr, "domain=", keyLen ); txtPtr += keyLen; if ( valLen ) { memcpy( txtPtr, m->p->nbdomain, valLen ); txtPtr += valLen; } for ( ;; ) { DWORD ret; ret = WaitForMultipleObjects( 3, handles, FALSE, INFINITE ); if ( ret != WAIT_FAILED ) { if ( ret == kSMBStopEvent ) { break; } else if ( ret == kSMBRegisterEvent ) { err = gDNSServiceRegister( &sref, 0, 0, NULL, "_smb._tcp,_file", NULL, NULL, ( uint16_t ) port.NotAnInteger, ( mDNSu16 )( txtPtr - txtBuf ), txtBuf, NULL, NULL ); if ( err ) { LogMsg( "SMBRegistrationThread: DNSServiceRegister returned %d\n", err ); sref = NULL; break; } } else if ( ret == kSMBDeregisterEvent ) { if ( sref ) { gDNSServiceRefDeallocate( sref ); sref = NULL; } } } else { LogMsg( "SMBRegistrationThread: WaitForMultipleObjects returned %d\n", GetLastError() ); break; } } exit: if ( sref != NULL ) { gDNSServiceRefDeallocate( sref ); sref = NULL; } SetEvent( gSMBThreadQuitEvent ); _endthreadex( 0 ); return 0; } mDNSlocal void CheckFileShares( mDNS * const m ) { PSHARE_INFO_1 bufPtr = ( PSHARE_INFO_1 ) NULL; DWORD entriesRead = 0; DWORD totalEntries = 0; DWORD resume = 0; mDNSBool advertise = mDNSfalse; mDNSBool fileSharing = mDNSfalse; mDNSBool printSharing = mDNSfalse; HKEY key = NULL; BOOL retry = FALSE; NET_API_STATUS res; mStatus err; check( m ); // Only do this if we're not shutting down require_action_quiet( m->AdvertiseLocalAddresses && !m->ShutdownTime, exit, err = kNoErr ); err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", &key ); if ( !err ) { DWORD dwSize = sizeof( DWORD ); RegQueryValueEx( key, L"Advertise", NULL, NULL, (LPBYTE) &advertise, &dwSize ); } if ( advertise && mDNSIsFileAndPrintSharingEnabled( &retry ) ) { dlog( kDebugLevelTrace, DEBUG_NAME "Sharing is enabled\n" ); res = NetShareEnum( NULL, 1, ( LPBYTE* )&bufPtr, MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, &resume ); if ( ( res == ERROR_SUCCESS ) || ( res == ERROR_MORE_DATA ) ) { PSHARE_INFO_1 p = bufPtr; DWORD i; for( i = 0; i < entriesRead; i++ ) { // We are only interested if the user is sharing anything other // than the built-in "print$" source if ( ( p->shi1_type == STYPE_DISKTREE ) && ( wcscmp( p->shi1_netname, TEXT( "print$" ) ) != 0 ) ) { fileSharing = mDNStrue; } else if ( p->shi1_type == STYPE_PRINTQ ) { printSharing = mDNStrue; } p++; } NetApiBufferFree( bufPtr ); bufPtr = NULL; retry = FALSE; } else if ( res == NERR_ServerNotStarted ) { retry = TRUE; } } if ( retry ) { __int64 qwTimeout; LARGE_INTEGER liTimeout; qwTimeout = -m->p->checkFileSharesTimeout * 10000000; liTimeout.LowPart = ( DWORD )( qwTimeout & 0xFFFFFFFF ); liTimeout.HighPart = ( LONG )( qwTimeout >> 32 ); SetWaitableTimer( m->p->checkFileSharesTimer, &liTimeout, 0, CheckFileSharesProc, m, FALSE ); } if ( !m->p->smbFileSharing && fileSharing ) { if ( !gSMBThread ) { if ( !gDNSSDLibrary ) { gDNSSDLibrary = LoadLibrary( TEXT( "dnssd.dll" ) ); require_action( gDNSSDLibrary, exit, err = GetLastError() ); } if ( !gDNSServiceRegister ) { gDNSServiceRegister = ( DNSServiceRegisterFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRegister" ); require_action( gDNSServiceRegister, exit, err = GetLastError() ); } if ( !gDNSServiceRefDeallocate ) { gDNSServiceRefDeallocate = ( DNSServiceRefDeallocateFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRefDeallocate" ); require_action( gDNSServiceRefDeallocate, exit, err = GetLastError() ); } if ( !gSMBThreadRegisterEvent ) { gSMBThreadRegisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( gSMBThreadRegisterEvent != NULL, exit, err = GetLastError() ); } if ( !gSMBThreadDeregisterEvent ) { gSMBThreadDeregisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( gSMBThreadDeregisterEvent != NULL, exit, err = GetLastError() ); } if ( !gSMBThreadStopEvent ) { gSMBThreadStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( gSMBThreadStopEvent != NULL, exit, err = GetLastError() ); } if ( !gSMBThreadQuitEvent ) { gSMBThreadQuitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( gSMBThreadQuitEvent != NULL, exit, err = GetLastError() ); } gSMBThread = ( HANDLE ) _beginthreadex( NULL, 0, SMBRegistrationThread, m, 0, NULL ); require_action( gSMBThread != NULL, exit, err = GetLastError() ); } SetEvent( gSMBThreadRegisterEvent ); m->p->smbFileSharing = mDNStrue; } else if ( m->p->smbFileSharing && !fileSharing ) { dlog( kDebugLevelTrace, DEBUG_NAME "deregistering smb type\n" ); if ( gSMBThreadDeregisterEvent != NULL ) { SetEvent( gSMBThreadDeregisterEvent ); } m->p->smbFileSharing = mDNSfalse; } exit: if ( key ) { RegCloseKey( key ); } } BOOL IsWOMPEnabled( mDNS * const m ) { BOOL enabled; mDNSInterfaceData * ifd; enabled = FALSE; for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) { if ( IsWOMPEnabledForAdapter( ifd->name ) ) { enabled = TRUE; break; } } return enabled; } mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName ) { char fileName[80]; NDIS_OID oid; DWORD count; HANDLE handle = INVALID_HANDLE_VALUE; NDIS_PNP_CAPABILITIES * pNPC = NULL; int err; mDNSu8 ok = TRUE; require_action( adapterName != NULL, exit, ok = FALSE ); dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter: %s\n", adapterName ); // Construct a device name to pass to CreateFile strncpy_s( fileName, sizeof( fileName ), DEVICE_PREFIX, strlen( DEVICE_PREFIX ) ); strcat_s( fileName, sizeof( fileName ), adapterName ); handle = CreateFileA( fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, INVALID_HANDLE_VALUE ); require_action ( handle != INVALID_HANDLE_VALUE, exit, ok = FALSE ); // We successfully opened the driver, format the IOCTL to pass the driver. oid = OID_PNP_CAPABILITIES; pNPC = ( NDIS_PNP_CAPABILITIES * ) malloc( sizeof( NDIS_PNP_CAPABILITIES ) ); require_action( pNPC != NULL, exit, ok = FALSE ); ok = ( mDNSu8 ) DeviceIoControl( handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof( oid ), pNPC, sizeof( NDIS_PNP_CAPABILITIES ), &count, NULL ); err = translate_errno( ok, GetLastError(), kUnknownErr ); require_action( !err, exit, ok = FALSE ); ok = ( mDNSu8 ) ( ( count == sizeof( NDIS_PNP_CAPABILITIES ) ) && ( pNPC->Flags & NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE ) ); exit: if ( pNPC != NULL ) { free( pNPC ); } if ( handle != INVALID_HANDLE_VALUE ) { CloseHandle( handle ); } dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter returns %s\n", ok ? "true" : "false" ); return ( mDNSu8 ) ok; } void DispatchSocketEvents( mDNS * const inMDNS ) { UDPSocket * udpSock; TCPSocket * tcpSock; while ( ( udpSock = ( UDPSocket* ) gUDPDispatchableSockets.Head ) != NULL ) { dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchUDPEvent on socket %d, error = %d, bytesTransferred = %d\n", __ROUTINE__, udpSock->fd, udpSock->overlapped.error, udpSock->overlapped.bytesTransferred ); RemoveFromList( &gUDPDispatchableSockets, udpSock ); DispatchUDPEvent( inMDNS, udpSock ); } while ( ( tcpSock = ( TCPSocket* ) gTCPDispatchableSockets.Head ) != NULL ) { dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchTCPEvent on socket %d, error = %d, bytesTransferred = %d\n", __ROUTINE__, tcpSock->fd, tcpSock->overlapped.error, tcpSock->overlapped.bytesTransferred ); RemoveFromList( &gTCPDispatchableSockets, tcpSock ); DispatchTCPEvent( inMDNS, tcpSock ); } } mDNSlocal void DispatchUDPEvent( mDNS * const inMDNS, UDPSocket * sock ) { ( void ) inMDNS; // If we've closed the socket, then we want to ignore // this read. The packet might have been queued before // the socket was closed. if ( sock->fd != INVALID_SOCKET ) { const mDNSInterfaceID iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL; mDNSu8 * end = ( (mDNSu8 *) &sock->packet ) + sock->overlapped.bytesTransferred; dlog( kDebugLevelChatty, DEBUG_NAME "calling mDNSCoreReceive on socket: %d\n", sock->fd ); mDNSCoreReceive( sock->m, &sock->packet, end, &sock->overlapped.srcAddr, sock->overlapped.srcPort, &sock->overlapped.dstAddr, sock->overlapped.dstPort, iid ); } // If the socket is still good, then start up another asynchronous read if ( sock->fd != INVALID_SOCKET ) { int err = UDPBeginRecv( sock ); check_noerr( err ); } } mDNSlocal void DispatchTCPEvent( mDNS * const inMDNS, TCPSocket * sock ) { ( void ) inMDNS; if ( sock->fd != INVALID_SOCKET ) { sock->eptr += sock->overlapped.bytesTransferred; sock->lastError = sock->overlapped.error; if ( !sock->overlapped.error && !sock->overlapped.bytesTransferred ) { sock->closed = TRUE; } if ( sock->readEventHandler != NULL ) { dlog( kDebugLevelChatty, DEBUG_NAME "calling TCP read handler on socket: %d\n", sock->fd ); sock->readEventHandler( sock ); } } // If the socket is still good, then start up another asynchronous read if ( !sock->closed && ( sock->fd != INVALID_SOCKET ) ) { int err = TCPBeginRecv( sock ); check_noerr( err ); } }