/* * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: mDNSWin32.c,v $ Revision 1.22 2003/08/20 06:21:25 bradley Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease the addition of IPv6 support in the future; Changed init code to serialize thread initialization to enable ThreadID improvement to wakeup notification; Define platform support structure locally to allow portable mDNS_Init usage; Removed dependence on modified mDNSCore: define interface ID<->name structures/prototypes locally; Changed to use _beginthreadex()/_endthreadex() on non-Windows CE platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Microsoft C runtime; Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc. Revision 1.21 2003/08/18 23:09:57 cheshire mDNSResponder divide by zero in mDNSPlatformTimeNow() Revision 1.20 2003/08/12 19:56:27 cheshire Update to APSL 2.0 Revision 1.19 2003/08/05 23:58:18 cheshire Update code to compile with the new mDNSCoreReceive() function that requires a TTL Right now this platform layer just reports 255 instead of returning the real value -- we should fix this Revision 1.18 2003/07/23 21:16:30 cheshire Removed a couple of debugfs Revision 1.17 2003/07/23 02:23:01 cheshire Updated mDNSPlatformUnlock() to work correctly, now that "ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set Revision 1.16 2003/07/19 03:15:16 cheshire Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, and add the obvious trivial implementations to each platform support layer Revision 1.15 2003/07/02 21:20:04 cheshire Update copyright notices, etc., in source code comments Revision 1.14 2003/05/26 03:21:30 cheshire Tidy up address structure naming: mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 Revision 1.13 2003/05/26 03:01:28 cheshire sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead Revision 1.12 2003/05/06 21:06:05 cheshire mDNSWindows needs a wakeupEvent object to signal the main thread Revision 1.11 2003/05/06 00:00:51 cheshire Rationalize naming of domainname manipulation functions Revision 1.10 2003/04/29 00:06:09 cheshire mDNSWindows needs a wakeupEvent object to signal the main thread Revision 1.9 2003/04/26 02:40:01 cheshire Add void LogMsg( const char *format, ... ) Revision 1.8 2003/03/22 02:57:44 cheshire Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") Revision 1.7 2003/03/15 04:40:38 cheshire Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" Revision 1.6 2003/02/21 01:54:10 cheshire Bug #: 3099194 mDNSResponder needs performance improvements Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") Revision 1.5 2003/02/20 00:59:03 cheshire Brought Windows code up to date so it complies with Josh Graessley's interface changes for IPv6 support. (Actual support for IPv6 on Windows will come later.) Revision 1.4 2002/09/21 20:44:54 zarzycki Added APSL info Revision 1.3 2002/09/20 05:50:45 bradley Multicast DNS platform plugin for Win32 */ #if( defined( _MSC_VER ) ) #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. #endif #if( !defined( WIN32_LEAN_AND_MEAN ) ) #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. #endif #include #include #include #include #include #include #include #include #if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h. #include #endif #if( DEBUG ) #define mDNSlocal #endif #include "mDNSClientAPI.h" #include "mDNSPlatformFunctions.h" #include "mDNSWin32.h" #if 0 #pragma mark == Constants == #endif //=========================================================================================================================== // Constants //=========================================================================================================================== #define DEBUG_NAME "[mDNS] " #if( !defined( MDNS_DEBUG_SIGNATURE ) ) #define MDNS_DEBUG_SIGNATURE "mDNS" #endif #define kMDNSDefaultName "My Computer" #define kWinSockMajorMin 2 #define kWinSockMinorMin 2 #define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 ) #define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 ) #define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 ) #define kWaitListFixedItemCount 3 #if 0 #pragma mark == Macros - Debug == #endif //=========================================================================================================================== // Macros - Debug //=========================================================================================================================== #define MDNS_UNUSED( X ) (void)( X ) #define kDebugLevelMask 0x0000FFFFL #define kDebugLevelChatty 100L #define kDebugLevelVerbose 500L #define kDebugLevelTrace 800L #define kDebugLevelInfo 1000L #define kDebugLevelRareInfo 2000L #define kDebugLevelNotice 3000L #define kDebugLevelWarning 4000L #define kDebugLevelAllowedError 5000L #define kDebugLevelAssert 6000L #define kDebugLevelRequire 7000L #define kDebugLevelError 8000L #define kDebugLevelCritical 9000L #define kDebugLevelCriticalError kDebugLevelCritical // DEPRECATED #define kDebugLevelAlert 10000L #define kDebugLevelEmergency 11000L #define kDebugLevelTragic 12000L #define kDebugLevelAny 0x0000FFFFL #if( defined( __MWERKS__ ) || defined( __GNUC__ ) ) #define __ROUTINE__ __FUNCTION__ #else // Apple and Symantec compilers don't support the C99/GCC extensions yet. #define __ROUTINE__ NULL #endif #if( MDNS_DEBUGMSGS ) #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \ mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, 0, ( ASSERT_STRING ), NULL, ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) ) #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \ mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, ( ERR ), ( ASSERT_STRING ), ( ERROR_STRING ), \ ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) ) #define dlog mDNSPlatformDebugLog #else #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) #define dlog while( 0 ) #endif /// /// The following debugging macros emulate those available on Mac OS in AssertMacros.h/Debugging.h. /// // checks #define check( X ) \ do { \ if( !( X ) ) { \ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ } \ } while( 0 ) #define check_noerr( ERR ) \ do { \ if( ( ERR ) != 0 ) { \ debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ } \ } while( 0 ) #define check_errno( ERR, ERRNO ) \ do { \ int localErr; \ \ localErr = (int)( ERR ); \ if( localErr < 0 ) { \ int localErrno; \ \ localErrno = ( ERRNO ); \ localErr = ( localErrno != 0 ) ? localErrno : localErr; \ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ } \ } while( 0 ) // requires #define require( X, LABEL ) \ do { \ if( !( X ) ) { \ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ goto LABEL; \ } \ } while( 0 ) #define require_quiet( X, LABEL ) \ do { \ if( !( X ) ) { \ goto LABEL; \ } \ } while( 0 ) #define require_action( X, LABEL, ACTION ) \ do { \ if( !( X ) ) { \ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ { ACTION; } \ goto LABEL; \ } \ } while( 0 ) #define require_action_quiet( X, LABEL, ACTION ) \ do { \ if( !( X ) ) { \ { ACTION; } \ goto LABEL; \ } \ } while( 0 ) #define require_noerr( ERR, LABEL ) \ do { \ if( ( ERR ) != 0 ) { \ debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ goto LABEL; \ } \ } while( 0 ) #define require_noerr_quiet( ERR, LABEL ) \ do { \ if( ( ERR ) != 0 ) { \ goto LABEL; \ } \ } while( 0 ) #define require_errno( ERR, ERRNO, LABEL ) \ do { \ int localErr; \ \ localErr = (int)( ERR ); \ if( localErr < 0 ) { \ int localErrno; \ \ localErrno = ( ERRNO ); \ localErr = ( localErrno != 0 ) ? localErrno : localErr; \ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ goto LABEL; \ } \ } while( 0 ) #define require_errno_action( ERR, ERRNO, LABEL, ACTION ) \ do { \ int localErr; \ \ localErr = (int)( ERR ); \ if( localErr < 0 ) { \ int localErrno; \ \ localErrno = ( ERRNO ); \ localErr = ( localErrno != 0 ) ? localErrno : localErr; \ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ { ACTION; } \ goto LABEL; \ } \ } while( 0 ) #if 0 #pragma mark == Macros - General == #endif //=========================================================================================================================== // Macros - General //=========================================================================================================================== #define kInvalidSocketRef INVALID_SOCKET #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) #define close_compat( X ) closesocket( X ) #define errno_compat() WSAGetLastError() // _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking // resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to // CreateThread on Windows CE. #if( defined( _WIN32_WCE ) ) #define _beginthreadex( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ (LPDWORD) THREAD_ID_PTR ) #define _endthreadex( RESULT ) #endif #if 0 #pragma mark == Prototypes == #endif //=========================================================================================================================== // Prototypes //=========================================================================================================================== #if( MDNS_DEBUGMSGS ) mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... ); mDNSlocal void mDNSPlatformPrintAssert( const char * inSignature, long inError, const char * inAssertionString, const char * inErrorString, const char * inFileName, unsigned long inLineNumber, const char * inFunction ); #endif mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS ); mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ); mDNSlocal mStatus SetupName( mDNS * const inMDNS ); mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD ); mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ); mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr_in * inAddress, mDNSIPPort inPort, SocketRef * outSocketRef ); mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ); mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ); mDNSlocal mStatus SetupThread( mDNS * const inMDNS ); mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS ); mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ); mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS ); mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ); mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef ); mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ); // 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 ); #ifdef __cplusplus } #endif #if 0 #pragma mark == Globals == #endif //=========================================================================================================================== // Globals //=========================================================================================================================== mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; mDNSs32 mDNSPlatformOneSecond = 0; #if( MDNS_DEBUGMSGS ) mDNSlocal unsigned long gDebugLevel = kDebugLevelInfo + 1; #endif #if 0 #pragma mark - #pragma mark == Platform Support APIs == #endif //=========================================================================================================================== // mDNSPlatformInit //=========================================================================================================================== mStatus mDNSPlatformInit( mDNS * const inMDNS ) { mStatus err; WSADATA wsaData; int supported; dlog( kDebugLevelVerbose, DEBUG_NAME "platform init\n" ); // Initialize variables. memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); inMDNS->p = &gMDNSPlatformSupport; inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time. // Set everything up. 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 ); err = SetupSynchronizationObjects( inMDNS ); require_noerr( err, exit ); err = SetupThread( inMDNS ); require_noerr( err, exit ); // Success! mDNSCoreInitComplete( inMDNS, err ); exit: if( err ) { mDNSPlatformClose( inMDNS ); } dlog( kDebugLevelVerbose, DEBUG_NAME "platform init done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // mDNSPlatformClose //=========================================================================================================================== void mDNSPlatformClose( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelVerbose, DEBUG_NAME "platform close\n" ); check( inMDNS ); // Tear everything down in reverse order to how it was set up. err = TearDownThread( inMDNS ); check_noerr( err ); err = TearDownInterfaceList( inMDNS ); check_noerr( err ); err = TearDownSynchronizationObjects( inMDNS ); check_noerr( err ); WSACleanup(); dlog( kDebugLevelVerbose, DEBUG_NAME "platform close done (err=%ld)\n", err ); } //=========================================================================================================================== // mDNSPlatformSendUDP //=========================================================================================================================== mStatus mDNSPlatformSendUDP( const mDNS * const inMDNS, const DNSMessage * const inMsg, const mDNSu8 * const inMsgEnd, mDNSInterfaceID inInterfaceID, mDNSIPPort inSrcPort, const mDNSAddr * inDstIP, mDNSIPPort inDstPort ) { mStatus err; mDNSInterfaceData * ifd; struct sockaddr_in addr; int n; MDNS_UNUSED( inSrcPort ); dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); // Check parameters. check( inMDNS ); check( inMsg ); check( inMsgEnd ); check( inInterfaceID ); check( inDstIP ); if( inDstIP->type != mDNSAddrType_IPv4 ) { err = mStatus_BadParamErr; goto exit; } // Send the packet. ifd = (mDNSInterfaceData *) inInterfaceID; check( IsValidSocket( ifd->multicastSocketRef ) ); addr.sin_family = AF_INET; addr.sin_port = inDstPort.NotAnInteger; addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) ); n = sendto( ifd->multicastSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); check_errno( n, errno_compat() ); ifd->sendErrorCounter += ( n < 0 ); ifd->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger ); ifd->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger ); err = mStatus_NoError; exit: dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" ); return( err ); } //=========================================================================================================================== // mDNSPlatformLock //=========================================================================================================================== void mDNSPlatformLock( const mDNS * const inMDNS ) { EnterCriticalSection( &inMDNS->p->lock ); } //=========================================================================================================================== // mDNSPlatformUnlock //=========================================================================================================================== void mDNSPlatformUnlock( const mDNS * const inMDNS ) { check( inMDNS ); check( inMDNS->p ); check( inMDNS->p->threadID ); // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock() // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to: // (a) handle immediate work (if any) resulting from this API call // (b) calculate the next sleep time between now and the next interesting event if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 ) { // We only need to case a wakeup event when called from a task other than the mDNS task since if we are // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. if( GetCurrentThreadId() != inMDNS->p->threadID ) { BOOL wasSet; wasSet = SetEvent( inMDNS->p->wakeupEvent ); check( wasSet ); } } LeaveCriticalSection( &inMDNS->p->lock ); } //=========================================================================================================================== // mDNSPlatformStrLen //=========================================================================================================================== mDNSu32 mDNSPlatformStrLen( const void *inSrc ) { check( inSrc ); return( (mDNSu32) strlen( (const char *) inSrc ) ); } //=========================================================================================================================== // mDNSPlatformStrCopy //=========================================================================================================================== void mDNSPlatformStrCopy( const void *inSrc, void *inDst ) { check( inSrc ); check( inDst ); strcpy( (char *) inDst, (const char*) inSrc ); } //=========================================================================================================================== // mDNSPlatformMemCopy //=========================================================================================================================== void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize ) { check( inSrc ); check( inDst ); memcpy( inDst, inSrc, inSize ); } //=========================================================================================================================== // mDNSPlatformMemSame //=========================================================================================================================== mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize ) { check( inSrc ); check( inDst ); return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) ); } //=========================================================================================================================== // mDNSPlatformMemZero //=========================================================================================================================== 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 ); } //=========================================================================================================================== // mDNSPlatformTimeInit //=========================================================================================================================== mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow ) { check( outTimeNow ); // No special setup is required on Windows -- we just use GetTickCount(). *outTimeNow = mDNSPlatformTimeNow(); return( mStatus_NoError ); } //=========================================================================================================================== // mDNSPlatformTimeNow //=========================================================================================================================== mDNSs32 mDNSPlatformTimeNow( void ) { return( (mDNSs32) GetTickCount() ); } //=========================================================================================================================== // mDNSPlatformInterfaceNameToID //=========================================================================================================================== 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 //=========================================================================================================================== 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->hostSet.ip; err = mStatus_NoError; exit: return( err ); } #if 0 #pragma mark - #endif //=========================================================================================================================== // debugf_ //=========================================================================================================================== #if( MDNS_DEBUGMSGS ) 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 ) 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 //=========================================================================================================================== // LogMsg //=========================================================================================================================== void LogMsg( 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( kDebugLevelWarning, "%s\n", buffer ); } #if( MDNS_DEBUGMSGS ) //=========================================================================================================================== // mDNSPlatformDebugLog //=========================================================================================================================== mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... ) { if( inLevel >= gDebugLevel ) { va_list args; va_start( args, inFormat ); vfprintf( stderr, inFormat, args ); fflush( stderr ); va_end( args ); } } //=========================================================================================================================== // mDNSPlatformPrintAssert //=========================================================================================================================== mDNSlocal void mDNSPlatformPrintAssert( const char * inSignature, long inError, const char * inAssertionString, const char * inErrorString, const char * inFileName, unsigned long inLineNumber, const char * inFunction ) { char * dataPtr; char buffer[ 512 ]; char tempSignatureChar; if( !inSignature ) { tempSignatureChar = '\0'; inSignature = &tempSignatureChar; } dataPtr = buffer; dataPtr += sprintf( dataPtr, "\n" ); if( inError != 0 ) { dataPtr += sprintf( dataPtr, "[%s] Error: %ld\n", inSignature, inError ); } else { dataPtr += sprintf( dataPtr, "[%s] Assertion failed", inSignature ); if( inAssertionString ) { dataPtr += sprintf( dataPtr, ": %s", inAssertionString ); } dataPtr += sprintf( dataPtr, "\n" ); } if( inErrorString ) { dataPtr += sprintf( dataPtr, "[%s] %s\n", inSignature, inErrorString ); } if( inFileName ) { dataPtr += sprintf( dataPtr, "[%s] file: \"%s\"\n", inSignature, inFileName ); } if( inLineNumber ) { dataPtr += sprintf( dataPtr, "[%s] line: %ld\n", inSignature, inLineNumber ); } if( inFunction ) { dataPtr += sprintf( dataPtr, "[%s] function: \"%s\"\n", inSignature, inFunction ); } dataPtr += sprintf( dataPtr, "\n" ); fprintf( stderr, "%s", buffer ); fflush( stderr ); } #endif // MDNS_DEBUGMSGS #if 0 #pragma mark - #pragma mark == Platform Internals == #endif //=========================================================================================================================== // SetupSynchronizationObjects //=========================================================================================================================== mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects\n" ); InitializeCriticalSection( &inMDNS->p->lock ); inMDNS->p->lockInitialized = mDNStrue; inMDNS->p->cancelEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( inMDNS->p->cancelEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->quitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->interfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( inMDNS->p->interfaceListChangedEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->wakeupEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( inMDNS->p->wakeupEvent, exit, err = mStatus_NoMemoryErr ); err = mStatus_NoError; exit: if( err ) { TearDownSynchronizationObjects( inMDNS ); } dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // TearDownSynchronizationObjects //=========================================================================================================================== mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ) { mStatus err; dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects\n" ); if( inMDNS->p->quitEvent ) { CloseHandle( inMDNS->p->quitEvent ); inMDNS->p->quitEvent = 0; } if( inMDNS->p->cancelEvent ) { CloseHandle( inMDNS->p->cancelEvent ); inMDNS->p->cancelEvent = 0; } if( inMDNS->p->interfaceListChangedEvent ) { CloseHandle( inMDNS->p->interfaceListChangedEvent ); inMDNS->p->interfaceListChangedEvent = 0; } if( inMDNS->p->wakeupEvent ) { CloseHandle( inMDNS->p->wakeupEvent ); inMDNS->p->wakeupEvent = 0; } if( inMDNS->p->lockInitialized ) { DeleteCriticalSection( &inMDNS->p->lock ); inMDNS->p->lockInitialized = mDNSfalse; } err = mStatus_NoError; dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // SetupName //=========================================================================================================================== mDNSlocal mStatus SetupName( mDNS * const inMDNS ) { mStatus err; char tempString[ 256 ]; check( inMDNS ); // Get the name of this machine. tempString[ 0 ] = '\0'; err = gethostname( tempString, sizeof( tempString ) - 1 ); check_errno( err, errno_compat() ); if( err || ( tempString[ 0 ] == '\0' ) ) { // Invalidate name so fall back to a default name. strcpy( tempString, kMDNSDefaultName ); } tempString[ sizeof( tempString ) - 1 ] = '\0'; // Set up the host name with mDNS. inMDNS->nicelabel.c[ 0 ] = (mDNSu8) strlen( tempString ); memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] ); ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.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->nicelabel.c[ 0 ] != 0 ); check( inMDNS->hostlabel.c[ 0 ] != 0 ); mDNS_GenerateFQDN( inMDNS ); dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); return( err ); } //=========================================================================================================================== // SetupInterfaceList //=========================================================================================================================== mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) { mStatus err; mDNSInterfaceData ** next; mDNSInterfaceData * ifd; struct ifaddrs * addrs; struct ifaddrs * p; struct ifaddrs * loopback; u_int flagMask; u_int flagTest; dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" ); check( inMDNS ); check( inMDNS->p ); addrs = NULL; // 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 the interface list change notification. err = SetupNotifications( inMDNS ); check_noerr( err ); // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTTOPOINT; flagTest = IFF_UP | IFF_MULTICAST; loopback = NULL; next = &inMDNS->p->interfaceList; err = getifaddrs( &addrs ); require_noerr( err, exit ); for( p = addrs; p; p = p->ifa_next ) { if( ( p->ifa_flags & flagMask ) == flagTest ) { if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) ) // $$$ TO DO: Update for IPv6. { continue; } err = SetupInterface( inMDNS, (struct sockaddr_in *) p->ifa_addr, &ifd ); require_noerr( err, exit ); strcpy( ifd->name, p->ifa_name ); *next = ifd; next = &ifd->next; ++inMDNS->p->interfaceCount; } } exit: if( err ) { TearDownInterfaceList( inMDNS ); } if( addrs ) { freeifaddrs( addrs ); } dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // TearDownInterfaceList //=========================================================================================================================== mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) { mStatus err; mDNSInterfaceData * ifd; dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); check( inMDNS ); check( inMDNS->p ); // Tear down interface list change notifications. err = TearDownNotifications( inMDNS ); check_noerr( err ); // 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( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); return( mStatus_NoError ); } //=========================================================================================================================== // SetupInterface //=========================================================================================================================== mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD ) { mStatus err; mDNSInterfaceData * ifd; SocketRef socketRef; dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface\n" ); check( inMDNS ); check( inMDNS->p ); check( inAddress ); check( outIFD ); // Allocate memory for the info item. ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) ); require_action( ifd, exit, err = mStatus_NoMemoryErr ); ifd->multicastSocketRef = kInvalidSocketRef; ifd->unicastSocketRef = kInvalidSocketRef; /// /// Set up multicast portion of interface. /// // Set up the multicast DNS (port 5353) socket for this interface. err = SetupSocket( inMDNS, inAddress, MulticastDNSPort, &socketRef ); require_noerr( err, exit ); ifd->multicastSocketRef = socketRef; // Set up the read pending event and associate it so we can block until data is available for this socket. ifd->multicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( ifd->multicastReadPendingEvent, exit, err = mStatus_NoMemoryErr ); err = WSAEventSelect( ifd->multicastSocketRef, ifd->multicastReadPendingEvent, FD_READ ); require_noerr( err, exit ); /// /// Set up unicast portion of interface. /// // Set up the unicast DNS (port 53) socket for this interface (to handle normal DNS requests). err = SetupSocket( inMDNS, inAddress, UnicastDNSPort, &socketRef ); require_noerr( err, exit ); ifd->unicastSocketRef = socketRef; // Set up the read pending event and associate it so we can block until data is available for this socket. ifd->unicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( ifd->unicastReadPendingEvent, exit, err = mStatus_NoMemoryErr ); err = WSAEventSelect( ifd->unicastSocketRef, ifd->unicastReadPendingEvent, FD_READ ); require_noerr( err, exit ); // Register this interface with mDNS. ifd->hostSet.InterfaceID = (mDNSInterfaceID) ifd; ifd->hostSet.ip.type = mDNSAddrType_IPv4; ifd->hostSet.ip.ip.v4.NotAnInteger = inAddress->sin_addr.s_addr; ifd->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; err = mDNS_RegisterInterface( inMDNS, &ifd->hostSet ); require_noerr( err, exit ); ifd->hostRegistered = mDNStrue; dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n", ifd->hostSet.ip.ip.v4.b[ 0 ], ifd->hostSet.ip.ip.v4.b[ 1 ], ifd->hostSet.ip.ip.v4.b[ 2 ], ifd->hostSet.ip.ip.v4.b[ 3 ] ); // Success! *outIFD = ifd; ifd = NULL; exit: if( ifd ) { TearDownInterface( inMDNS, ifd ); } dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // TearDownInterface //=========================================================================================================================== mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ) { SocketRef socketRef; check( inMDNS ); check( inIFD ); // Deregister this interface with mDNS. dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n", inIFD->hostSet.ip.ip.v4.b[ 0 ], inIFD->hostSet.ip.ip.v4.b[ 1 ], inIFD->hostSet.ip.ip.v4.b[ 2 ], inIFD->hostSet.ip.ip.v4.b[ 3 ] ); if( inIFD->hostRegistered ) { inIFD->hostRegistered = mDNSfalse; mDNS_DeregisterInterface( inMDNS, &inIFD->hostSet ); } // Tear down the multicast socket. if( inIFD->multicastReadPendingEvent ) { CloseHandle( inIFD->multicastReadPendingEvent ); inIFD->multicastReadPendingEvent = 0; } socketRef = inIFD->multicastSocketRef; inIFD->multicastSocketRef = kInvalidSocketRef; if( IsValidSocket( socketRef ) ) { dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef ); close_compat( socketRef ); } // Tear down the unicast socket. if( inIFD->unicastReadPendingEvent ) { CloseHandle( inIFD->unicastReadPendingEvent ); inIFD->unicastReadPendingEvent = 0; } socketRef = inIFD->unicastSocketRef; inIFD->unicastSocketRef = kInvalidSocketRef; if( IsValidSocket( socketRef ) ) { dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef ); close_compat( socketRef ); } // Free the memory used by the interface info. free( inIFD ); return( mStatus_NoError ); } //=========================================================================================================================== // SetupSocket //=========================================================================================================================== mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr_in * inAddress, mDNSIPPort inPort, SocketRef * outSocketRef ) { mStatus err; SocketRef socketRef; int option; struct ip_mreq mreq; struct sockaddr_in addr; mDNSv4Addr ip; MDNS_UNUSED( inMDNS ); dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); check( inMDNS ); check( outSocketRef ); // Set up a UDP socket. socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr ); // Turn on reuse address option so multiple servers can listen for Multicast DNS packets. option = 1; err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); check_errno( err, errno_compat() ); // Bind to the specified port (53 for unicast or 5353 for multicast). ip.NotAnInteger = inAddress->sin_addr.s_addr; memset( &addr, 0, sizeof( addr ) ); addr.sin_family = AF_INET; addr.sin_port = inPort.NotAnInteger; addr.sin_addr.s_addr = ip.NotAnInteger; err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); if( err && ( inPort.NotAnInteger == UnicastDNSPort.NotAnInteger ) ) { // Some systems prevent code without root permissions from binding to the DNS port so ignore this // error since it is not critical. This should only occur with non-root processes. err = 0; } check_errno( err, errno_compat() ); // Join the all-DNS multicast group so we receive Multicast DNS packets. if( inPort.NotAnInteger == MulticastDNSPort.NotAnInteger ) { mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; mreq.imr_interface.s_addr = ip.NotAnInteger; err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); check_errno( err, errno_compat() ); } // Direct multicast packets to the specified interface. addr.sin_addr.s_addr = ip.NotAnInteger; err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) ); check_errno( err, errno_compat() ); // Set the TTL of outgoing unicast packets to 255 (helps against spoofing). option = 255; err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); check_errno( err, errno_compat() ); // Set the TTL of outgoing multicast packets to 255 (helps against spoofing). option = 255; err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) ); check_errno( err, errno_compat() ); // Success! dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%u.%u.%u.%u:%u, %d)\n", ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef ); *outSocketRef = socketRef; socketRef = kInvalidSocketRef; err = mStatus_NoError; exit: if( IsValidSocket( socketRef ) ) { close_compat( socketRef ); } return( err ); } //=========================================================================================================================== // SetupNotifications //=========================================================================================================================== mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) { mStatus err; SocketRef socketRef; unsigned long param; int inBuffer; int outBuffer; DWORD outSize; // Register to listen for address list changes. socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr ); inMDNS->p->interfaceListChangedSocketRef = socketRef; // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event // when a change to the interface list is detected. param = 1; err = ioctlsocket( socketRef, FIONBIO, ¶m ); require_errno( err, errno_compat(), exit ); inBuffer = 0; outBuffer = 0; err = WSAIoctl( socketRef, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL ); if( err < 0 ) { check( errno_compat() == WSAEWOULDBLOCK ); } err = WSAEventSelect( socketRef, inMDNS->p->interfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE ); require_errno( err, errno_compat(), exit ); exit: if( err ) { TearDownNotifications( inMDNS ); } return( err ); } //=========================================================================================================================== // TearDownNotifications //=========================================================================================================================== mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ) { SocketRef socketRef; socketRef = inMDNS->p->interfaceListChangedSocketRef; inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; if( IsValidSocket( socketRef ) ) { close_compat( socketRef ); } return( mStatus_NoError ); } #if 0 #pragma mark - #endif //=========================================================================================================================== // SetupThread //=========================================================================================================================== mDNSlocal mStatus SetupThread( mDNS * const inMDNS ) { mStatus err; HANDLE threadHandle; unsigned threadID; DWORD result; dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); // To avoid a race condition with the thread ID needed by the unlocking code, we need to make sure the // thread has fully initialized. To do this, we create the thread then wait for it to signal it is ready. inMDNS->p->initEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->initStatus = mStatus_Invalid; // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time // libraries. See . threadHandle = (HANDLE) _beginthreadex( NULL, 0, ProcessingThread, inMDNS, 0, &threadID ); require_action( threadHandle, exit, err = mStatus_NoMemoryErr ); result = WaitForSingleObject( inMDNS->p->initEvent, INFINITE ); require_action( result == WAIT_OBJECT_0, exit, err = mStatus_UnknownErr ); err = inMDNS->p->initStatus; require_noerr( err, exit ); exit: if( inMDNS->p->initEvent ) { CloseHandle( inMDNS->p->initEvent ); inMDNS->p->initEvent = 0; } dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // TearDownThread //=========================================================================================================================== mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS ) { DWORD result; // Signal the cancel event to cause the thread to exit. Then wait for the quit event to be signal indicating it did // exit. If the quit event is not signal in 5 seconds, just give up and close anyway sinec the thread is probably hung. if( inMDNS->p->cancelEvent ) { BOOL wasSet; wasSet = SetEvent( inMDNS->p->cancelEvent ); check( wasSet ); if( inMDNS->p->quitEvent ) { result = WaitForSingleObject( inMDNS->p->quitEvent, 5 * 1000 ); check( result == WAIT_OBJECT_0 ); } } return( mStatus_NoError ); } //=========================================================================================================================== // ProcessingThread //=========================================================================================================================== mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) { mDNS * m; int done; mStatus err; HANDLE * waitList; int waitListCount; DWORD result; BOOL wasSet; check( inParam ); m = (mDNS *) inParam; err = ProcessingThreadInitialize( m ); require_noerr( err, exit ); done = 0; while( !done ) { // Set up the list of objects we'll be waiting on. waitList = NULL; waitListCount = 0; err = ProcessingThreadSetupWaitList( m, &waitList, &waitListCount ); require_noerr( err, exit ); // Main processing loop. for( ;; ) { // Give the mDNS core a chance to do its work and determine next event time. mDNSs32 interval = mDNS_Execute(m) - mDNSPlatformTimeNow(); if (interval < 0) interval = 0; else if (interval > (0x7FFFFFFF / 1000)) interval = 0x7FFFFFFF / mDNSPlatformOneSecond; else interval = (interval * 1000) / mDNSPlatformOneSecond; // Wait until something occurs (e.g. cancel, incoming packet, or timeout). result = WaitForMultipleObjects( (DWORD) waitListCount, waitList, FALSE, (DWORD) interval ); if( result == WAIT_TIMEOUT ) { // Next task timeout occurred. Loop back up to give mDNS core a chance to work. dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" ); continue; } else if( result == kWaitListCancelEvent ) { // Cancel event. Set the done flag and break to exit. dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" ); done = 1; break; } else if( result == kWaitListInterfaceListChangedEvent ) { // Interface list changed event. Break out of the inner loop to re-setup the wait list. ProcessingThreadInterfaceListChanged( m ); break; } else if( result == kWaitListWakeupEvent ) { // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute. dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup\n" ); continue; } else { int waitItemIndex; // Socket data available event. Determine which socket and process the packet. waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 ); dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex ); check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ); if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ) { HANDLE signaledObject; int n; mDNSInterfaceData * ifd; signaledObject = waitList[ waitItemIndex ]; n = 0; for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) { if( ifd->multicastReadPendingEvent == signaledObject ) { ProcessingThreadProcessPacket( m, ifd, ifd->multicastSocketRef ); ++n; } if( ifd->unicastReadPendingEvent == signaledObject ) { ProcessingThreadProcessPacket( m, ifd, ifd->unicastSocketRef ); ++n; } } check( n > 0 ); } else { // Unexpected wait result. dlog( kDebugLevelAllowedError, DEBUG_NAME "unexpected wait result (result=0x%08X)\n", result ); } } } // Release the wait list. if( waitList ) { free( waitList ); waitList = NULL; waitListCount = 0; } } // Signal the quit event to indicate that the thread is finished. exit: wasSet = SetEvent( m->p->quitEvent ); check( wasSet ); // Call _endthreadex() explicitly instead of just exiting normally to avoid memory leaks when using static run-time // libraries. See . _endthreadex( 0 ); return( 0 ); } //=========================================================================================================================== // ProcessingThreadInitialize //=========================================================================================================================== mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS ) { mStatus err; BOOL wasSet; inMDNS->p->threadID = GetCurrentThreadId(); err = SetupInterfaceList( inMDNS ); require_noerr( err, exit ); exit: if( err ) { TearDownInterfaceList( inMDNS ); } inMDNS->p->initStatus = err; wasSet = SetEvent( inMDNS->p->initEvent ); check( wasSet ); return( err ); } //=========================================================================================================================== // ProcessingThreadSetupWaitList //=========================================================================================================================== mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ) { mStatus err; int waitListCount; HANDLE * waitList; HANDLE * waitItemPtr; mDNSInterfaceData * ifd; dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list\n" ); check( inMDNS ); check( inMDNS->p ); check( outWaitList ); check( outWaitListCount ); // Allocate an array to hold all the objects to wait on. waitListCount = kWaitListFixedItemCount + ( 2 * inMDNS->p->interfaceCount ); waitList = (HANDLE *) malloc( waitListCount * sizeof( *waitList ) ); require_action( waitList, exit, err = mStatus_NoMemoryErr ); waitItemPtr = waitList; // Add the fixed wait items to the beginning of the list. *waitItemPtr++ = inMDNS->p->cancelEvent; *waitItemPtr++ = inMDNS->p->interfaceListChangedEvent; *waitItemPtr++ = inMDNS->p->wakeupEvent; // Append all the dynamic wait items to the list. for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) { *waitItemPtr++ = ifd->multicastReadPendingEvent; *waitItemPtr++ = ifd->unicastReadPendingEvent; } *outWaitList = waitList; *outWaitListCount = waitListCount; waitList = NULL; err = mStatus_NoError; exit: if( waitList ) { free( waitList ); } dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list done (err=%ld)\n", err ); return( err ); } //=========================================================================================================================== // ProcessingThreadProcessPacket //=========================================================================================================================== mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef ) { int n; mDNSBool isMulticast; DNSMessage packet; struct sockaddr_in addr; int addrSize; mDNSu8 * packetEndPtr; mDNSAddr srcAddr; mDNSIPPort srcPort; mDNSAddr dstAddr; mDNSIPPort dstPort; isMulticast = (mDNSBool)( inSocketRef == inIFD->multicastSocketRef ); // Receive the packet. addrSize = sizeof( addr ); n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); check( n >= 0 ); if( n >= 0 ) { // Set up the src/dst/interface info. srcAddr.type = mDNSAddrType_IPv4; srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; srcPort.NotAnInteger = addr.sin_port; dstAddr.type = mDNSAddrType_IPv4; dstAddr.ip.v4 = isMulticast ? AllDNSLinkGroup : inIFD->hostSet.ip.ip.v4; dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort; dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%u\n", srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ], ntohs( srcPort.NotAnInteger ) ); dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%u\n", dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ], ntohs( dstPort.NotAnInteger ) ); dlog( kDebugLevelChatty, DEBUG_NAME " interface = %u.%u.%u.%u\n", inIFD->hostSet.ip.ip.v4.b[ 0 ], inIFD->hostSet.ip.ip.v4.b[ 1 ], inIFD->hostSet.ip.ip.v4.b[ 2 ], inIFD->hostSet.ip.ip.v4.b[ 3 ] ); dlog( kDebugLevelChatty, DEBUG_NAME "--\n" ); // Dispatch the packet to mDNS. packetEndPtr = ( (mDNSu8 *) &packet ) + n; mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->hostSet.InterfaceID, 255 ); } // Update counters. inIFD->recvMulticastCounter += isMulticast; inIFD->recvUnicastCounter += !isMulticast; inIFD->recvErrorCounter += ( n < 0 ); } //=========================================================================================================================== // ProcessingThreadInterfaceListChanged //=========================================================================================================================== mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) { mStatus err; dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed event\n" ); check( inMDNS ); mDNSPlatformLock( 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 ); mDNSPlatformUnlock( inMDNS ); // Inform clients of the change. if( inMDNS->MainCallback ) { inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged ); } // Force mDNS to update. mDNSCoreMachineSleep( inMDNS, mDNSfalse ); } #if 0 #pragma mark - #pragma mark == Utilities == #endif #if( defined( _WIN32_WCE ) ) //=========================================================================================================================== // getifaddrs //=========================================================================================================================== int getifaddrs( struct ifaddrs **outAddrs ) { int err; SocketRef sock; DWORD size; void * buffer; SOCKET_ADDRESS_LIST * addressList; struct ifaddrs * head; struct ifaddrs ** next; struct ifaddrs * ifa; int n; int i; sock = kInvalidSocketRef; buffer = NULL; head = NULL; next = &head; // Open a temporary socket because one is needed to use WSAIoctl (we'll close it before exiting this function). sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); require_action( IsValidSocket( sock ), exit, err = mStatus_NoMemoryErr ); // Call WSAIoctl with SIO_ADDRESS_LIST_QUERY and pass a null buffer. This call will fail, but the size needed to // for the request will be filled in. Once we know the size, allocate a buffer to hold the entire list. // // NOTE: Due to a bug in Windows CE, the size returned by WSAIoctl is not enough so double it as a workaround. size = 0; WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &size, NULL, NULL ); require_action( size > 0, exit, err = -1 ); size *= 2; buffer = malloc( size ); require_action( buffer, exit, err = -1 ); // We now know the size of the list and have a buffer to hold so call WSAIoctl again to get it. err = WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, size, &size, NULL, NULL ); require_noerr( err, exit ); addressList = (SOCKET_ADDRESS_LIST *) buffer; // Process the raw interface list and build a linked list of interfaces. // // NOTE: Due to a bug in Windows CE, the iAddressCount field is always 0 so use 1 in that case. n = addressList->iAddressCount; if( n == 0 ) { n = 1; } for( i = 0; i < n; ++i ) { ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); require_action( ifa, exit, err = WSAENOBUFS ); *next = ifa; next = &ifa->ifa_next; // Fetch the name. $$$ TO DO: Get the real name of the interface. ifa->ifa_name = (char *) malloc( 16 ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); sprintf( ifa->ifa_name, "%d", i + 1 ); // Fetch flags. Note: SIO_ADDRESS_LIST_QUERY does not report flags so just fake IFF_UP and IFF_MULTICAST. ifa->ifa_flags = IFF_UP | IFF_MULTICAST; // Fetch addresses. switch( addressList->Address[ i ].lpSockaddr->sa_family ) { case AF_INET: { struct sockaddr_in * sinptr4; sinptr4 = (struct sockaddr_in *) addressList->Address[ i ].lpSockaddr; ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) ); break; } default: break; } } // 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 ); } #endif // defined( _WIN32_WCE ) ) #if( !defined( _WIN32_WCE ) ) //=========================================================================================================================== // getifaddrs //=========================================================================================================================== int getifaddrs( 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; struct sockaddr_in * sinptr4; struct sockaddr_in6_old * sinptr6; struct sockaddr * sa; 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 ); require_action( sock != INVALID_SOCKET, exit, err = WSAEMFILE ); 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 interfaces. for( i = 0; i < n; ++i ) { ifInfo = &buffer[ i ]; ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); require_action( ifa, exit, err = WSAENOBUFS ); *next = ifa; next = &ifa->ifa_next; // Fetch the name. $$$ TO DO: Get the real name of the interface. ifa->ifa_name = (char *) malloc( 16 ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); sprintf( ifa->ifa_name, "%d", i + 1 ); // Fetch interface flags. ifa->ifa_flags = (u_int) ifInfo->iiFlags; // Fetch addresses. switch( ifInfo->iiAddress.Address.sa_family ) { case AF_INET: sinptr4 = &ifInfo->iiAddress.AddressIn; ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) ); if( ifInfo->iiNetmask.Address.sa_family == AF_INET ) { sinptr4 = &ifInfo->iiNetmask.AddressIn; ifa->ifa_netmask = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_netmask, sinptr4, sizeof( *sinptr4 ) ); } if( ifInfo->iiBroadcastAddress.Address.sa_family == AF_INET ) { sinptr4 = &ifInfo->iiBroadcastAddress.AddressIn; ifa->ifa_broadaddr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); require_action( ifa->ifa_broadaddr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_broadaddr, sinptr4, sizeof( *sinptr4 ) ); } break; case AF_INET6: sinptr6 = &ifInfo->iiAddress.AddressIn6; ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr6 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sinptr6, sizeof( *sinptr6 ) ); break; default: sa = &ifInfo->iiAddress.Address; ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sa, sizeof( *sa ) ); break; } } // 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 ); } #endif // !defined( _WIN32_WCE ) ) //=========================================================================================================================== // freeifaddrs //=========================================================================================================================== void freeifaddrs( struct ifaddrs *inAddrs ) { struct ifaddrs * p; struct ifaddrs * q; // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. for( p = inAddrs; 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 ); } } #if( !defined( _WIN32_WCE ) ) //=========================================================================================================================== // sock_pton //=========================================================================================================================== int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) { int err; int size; if( inAddrSize == 0 ) { if( inFamily == AF_INET ) { inAddrSize = sizeof( struct sockaddr_in ); } else if( inFamily == AF_INET6 ) { inAddrSize = sizeof( struct sockaddr_in6 ); } else { err = WSAEAFNOSUPPORT; goto exit; } } size = (int) inAddrSize; err = WSAStringToAddressA( (char *) inString, inFamily, NULL, (LPSOCKADDR) outAddr, &size ); if( err != 0 ) goto exit; if( outAddrSize ) { *outAddrSize = (size_t) size; } exit: return( err ); } //=========================================================================================================================== // sock_ntop //=========================================================================================================================== char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) { DWORD size; int err; DWORD stringSize; if( inAddrSize == 0 ) { const struct sockaddr * addr; addr = (const struct sockaddr *) inAddr; if( addr->sa_family == AF_INET ) { size = sizeof( struct sockaddr_in ); } else if( addr->sa_family == AF_INET6 ) { size = sizeof( struct sockaddr_in6 ); } else { WSASetLastError( WSAEAFNOSUPPORT ); inBuffer = NULL; goto exit; } } else { size = (DWORD) inAddrSize; } stringSize = (DWORD) inBufferSize; err = WSAAddressToStringA( (LPSOCKADDR) inAddr, size, NULL, inBuffer, &stringSize ); if( err ) { inBuffer = NULL; } exit: return( inBuffer ); } #endif // !defined( _WIN32_WCE )