/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2002-2005 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. */ #if 0 #pragma mark == Configuration == #endif //=========================================================================================================================== // Configuration //=========================================================================================================================== #define DEBUG_NAME "[mDNS] " #define MDNS_AAAA_OVER_IPV4 1 // 1=Send AAAA & A records over IPv4 & IPv6, 0=Send AAAA over IPv6, A over IPv4. #define MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6 1 // 1=Don't use IPv6 socket if non-link-local IPv4 available on same interface. #define MDNS_ENABLE_PPP 0 // 1=Enable Unicast DNS over PPP interfaces. 0=Don't enable it. #define MDNS_DEBUG_PACKETS 1 // 1=Enable debug output for packet send/recv if debug level high enough. #define MDNS_DEBUG_SHOW 1 // 1=Enable console show routines. #define DEBUG_USE_DEFAULT_CATEGORY 1 // Set up to use the default category (see DebugServices.h for details). #include #include #include #include #include #include #include "vxWorks.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifLib.h" #include "inetLib.h" #include "pipeDrv.h" #include "selectLib.h" #include "semLib.h" #include "sockLib.h" #include "sysLib.h" #include "taskLib.h" #include "tickLib.h" #include "CommonServices.h" #include "DebugServices.h" #include "DNSCommon.h" #include "mDNSEmbeddedAPI.h" #include "mDNSVxWorks.h" #if 0 #pragma mark == Constants == #endif //=========================================================================================================================== // Constants //=========================================================================================================================== typedef uint8_t MDNSPipeCommandCode; #define kMDNSPipeCommandCodeInvalid 0 #define kMDNSPipeCommandCodeReschedule 1 #define kMDNSPipeCommandCodeReconfigure 2 #define kMDNSPipeCommandCodeQuit 3 #if 0 #pragma mark == Prototypes == #endif //=========================================================================================================================== // Prototypes //=========================================================================================================================== #if ( DEBUG ) mDNSlocal void DebugMsg( DebugLevel inLevel, const char *inFormat, ... ); #define dmsg( LEVEL, ARGS... ) DebugMsg( LEVEL, ## ARGS ) #else #define dmsg( LEVEL, ARGS... ) #endif #if ( DEBUG && MDNS_DEBUG_PACKETS ) #define dpkt( LEVEL, ARGS... ) DebugMsg( LEVEL, ## ARGS ) #else #define dpkt( LEVEL, ARGS... ) #endif #define ForgetSem( X ) do { if( *( X ) ) { semDelete( ( *X ) ); *( X ) = 0; } } while( 0 ) #define ForgetSocket( X ) do { if( IsValidSocket( *( X ) ) ) { close_compat( *( X ) ); *( X ) = kInvalidSocketRef; } } while( 0 ) // Interfaces mDNSlocal mStatus UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC ); mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC ); mDNSlocal int SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC ); mDNSlocal void MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC ); mDNSlocal int ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing ); mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID ); mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex ); mDNSlocal mStatus SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS ); mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP ); // Commands mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ); mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ); mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ); mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ); // Threads mDNSlocal void Task( mDNS *inMDNS ); mDNSlocal mStatus TaskInit( mDNS *inMDNS ); mDNSlocal void TaskTerm( mDNS *inMDNS ); mDNSlocal void TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout ); mDNSlocal void TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock ); mDNSlocal ssize_t mDNSRecvMsg( SocketRef inSock, void * inBuffer, size_t inBufferSize, void * outFrom, size_t inFromSize, size_t * outFromSize, mDNSAddr * outDstAddr, uint32_t * outIndex ); // DNSServices compatibility. When all clients move to DNS-SD, this section can be removed. #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 //=========================================================================================================================== debug_log_new_default_category( mdns ); mDNSexport mDNSs32 mDNSPlatformOneSecond; mDNSlocal mDNSs32 gMDNSTicksToMicro = 0; mDNSlocal mDNS * gMDNSPtr = NULL; mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; mDNSlocal mDNSBool gMDNSDeferIPv4 = mDNSfalse; #if ( DEBUG ) DebugLevel gMDNSDebugOverrideLevel = kDebugLevelMax; #endif #if 0 #pragma mark - #endif //=========================================================================================================================== // mDNSReconfigure //=========================================================================================================================== void mDNSReconfigure( void ) { if( gMDNSPtr ) SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure ); } //=========================================================================================================================== // mDNSDeferIPv4 //=========================================================================================================================== void mDNSDeferIPv4( mDNSBool inDefer ) { gMDNSDeferIPv4 = inDefer; } #if 0 #pragma mark - #endif //=========================================================================================================================== // mDNSPlatformInit //=========================================================================================================================== mStatus mDNSPlatformInit( mDNS * const inMDNS ) { mStatus err; int id; mDNSPlatformOneSecond = sysClkRateGet(); gMDNSTicksToMicro = ( 1000000L / mDNSPlatformOneSecond ); // Do minimal initialization to get the task started and so we can cleanup safely if an error occurs. mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) ); if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport; inMDNS->p->unicastSS.info = NULL; inMDNS->p->unicastSS.sockV4 = kInvalidSocketRef; inMDNS->p->unicastSS.sockV6 = kInvalidSocketRef; inMDNS->p->initErr = mStatus_NotInitializedErr; inMDNS->p->commandPipe = ERROR; inMDNS->p->taskID = ERROR; inMDNS->p->lock = semMCreate( SEM_Q_FIFO ); require_action( inMDNS->p->lock, exit, err = mStatus_NoMemoryErr ); inMDNS->p->initEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr ); inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); // Start the task and wait for it to initialize. The task does the full initialization from its own context // to avoid potential issues with stack space and APIs that key off the current task (e.g. watchdog timers). // We wait here until the init is complete because it needs to be ready to use as soon as this function returns. id = taskSpawn( "tMDNS", 102, 0, 16384, (FUNCPTR) Task, (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); err = translate_errno( id != ERROR, errno_compat(), mStatus_NoMemoryErr ); require_noerr( err, exit ); err = semTake( inMDNS->p->initEvent, WAIT_FOREVER ); if( err == OK ) err = inMDNS->p->initErr; require_noerr( err, exit ); gMDNSPtr = inMDNS; mDNSCoreInitComplete( inMDNS, err ); exit: if( err ) mDNSPlatformClose( inMDNS ); return( err ); } //=========================================================================================================================== // mDNSPlatformClose //=========================================================================================================================== void mDNSPlatformClose( mDNS * const inMDNS ) { mStatus err; check( inMDNS ); gMDNSPtr = NULL; // Signal the task to quit and wait for it to signal back that it exited. Timeout in 10 seconds to handle a hung thread. if( inMDNS->p->taskID != ERROR ) { SendCommand( inMDNS, kMDNSPipeCommandCodeQuit ); if( inMDNS->p->quitEvent ) { err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 ); check_noerr( err ); } inMDNS->p->taskID = ERROR; } // Clean up resources set up in mDNSPlatformInit. All other resources should have been cleaned up already by TaskTerm. ForgetSem( &inMDNS->p->quitEvent ); ForgetSem( &inMDNS->p->initEvent ); ForgetSem( &inMDNS->p->lock ); dmsg( kDebugLevelNotice, DEBUG_NAME "CLOSED\n" ); } //=========================================================================================================================== // mDNSPlatformSendUDP //=========================================================================================================================== mStatus mDNSPlatformSendUDP( const mDNS * const inMDNS, const void * const inMsg, const mDNSu8 * const inEnd, mDNSInterfaceID inInterfaceID, const mDNSAddr * inDstIP, mDNSIPPort inDstPort ) { mStatus err; NetworkInterfaceInfoVxWorks * info; SocketRef sock; struct sockaddr_storage to; int n; // Set up the sockaddr to sent to and the socket to send on. info = (NetworkInterfaceInfoVxWorks *) inInterfaceID; if( inDstIP->type == mDNSAddrType_IPv4 ) { struct sockaddr_in * sa4; sa4 = (struct sockaddr_in *) &to; sa4->sin_len = sizeof( *sa4 ); sa4->sin_family = AF_INET; sa4->sin_port = inDstPort.NotAnInteger; sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; sock = info ? info->ss.sockV4 : inMDNS->p->unicastSS.sockV4; } else if( inDstIP->type == mDNSAddrType_IPv6 ) { struct sockaddr_in6 * sa6; sa6 = (struct sockaddr_in6 *) &to; sa6->sin6_len = sizeof( *sa6 ); 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 = info ? info->scopeID : 0; sock = info ? info->ss.sockV6 : inMDNS->p->unicastSS.sockV6; } else { dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! destination is not an IPv4 or IPv6 address\n", __ROUTINE__ ); err = mStatus_BadParamErr; goto exit; } // Send the packet if we've got a valid socket of this type. Note: mDNSCore may ask us to send an IPv4 packet and then // an IPv6 multicast packet. If we don't have the corresponding type of socket available, quietly return an error. n = (int)( (mDNSu8 *) inEnd - (mDNSu8 *) inMsg ); if( !IsValidSocket( sock ) ) { dpkt( kDebugLevelChatty - 1, DEBUG_NAME "DROP: %4d bytes, DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n", n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info ); err = mStatus_Invalid; goto exit; } dpkt( kDebugLevelChatty, DEBUG_NAME "SEND %4d bytes, DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n", n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info ); n = sendto( sock, (mDNSu8 *) inMsg, n, 0, (struct sockaddr *) &to, to.ss_len ); if( n < 0 ) { // Don't warn about ARP failures or no route to host for unicast destinations. err = errno_compat(); if( ( ( err == EHOSTDOWN ) || ( err == ENETDOWN ) || ( err == EHOSTUNREACH ) ) && !mDNSAddressIsAllDNSLinkGroup( inDstIP ) ) { goto exit; } dmsg( kDebugLevelError, "%s: ERROR! sendto failed on %8s(%u) to %#a:%d, sock %d, err %d, time %u\n", __ROUTINE__, info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, inDstIP, mDNSVal16( inDstPort ), sock, err, (unsigned int) inMDNS->timenow ); if( err == 0 ) err = mStatus_UnknownErr; goto exit; } err = mStatus_NoError; exit: return( err ); } //=========================================================================================================================== // Connection-oriented (TCP) functions //=========================================================================================================================== mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context, int *descriptor) { (void)dst; // Unused (void)dstport; // Unused (void)InterfaceID; // Unused (void)callback; // Unused (void)context; // Unused (void)descriptor; // Unused return(mStatus_UnsupportedErr); } mDNSexport void mDNSPlatformTCPCloseConnection(int sd) { (void)sd; // Unused } mDNSexport long mDNSPlatformReadTCP(int sd, void *buf, unsigned long buflen) { (void)sd; // Unused (void)buf; // Unused (void)buflen; // Unused return(0); } mDNSexport long mDNSPlatformWriteTCP(int sd, const char *msg, unsigned long len) { (void)sd; // Unused (void)msg; // Unused (void)len; // Unused return(0); } //=========================================================================================================================== // mDNSPlatformLock //=========================================================================================================================== void mDNSPlatformLock( const mDNS * const inMDNS ) { check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" ); #if ( DEBUG ) if( semTake( inMDNS->p->lock, 60 * sysClkRateGet() ) != OK ) { dmsg( kDebugLevelTragic, "\n### DEADLOCK DETECTED ### (sem=%#p, task=%#p)\n\n", inMDNS->p->lock, taskIdSelf() ); debug_stack_trace(); // 1) Print Stack Trace. semShow( inMDNS->p->lock, 1 ); // 2) Print semaphore info, including which tasks are pending on it. taskSuspend( 0 ); // 3) Suspend task. Can be resumed from the console for debugging. } #else semTake( inMDNS->p->lock, WAIT_FOREVER ); #endif } //=========================================================================================================================== // mDNSPlatformUnlock //=========================================================================================================================== void mDNSPlatformUnlock( const mDNS * const inMDNS ) { check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" ); // Wake up the mDNS task to handle any work initiated by an API call and to calculate the next event time. // We only need to wake up if we're not already inside the task. This avoids filling up the command queue. if( taskIdSelf() != inMDNS->p->taskID ) { SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule ); } semGive( inMDNS->p->lock ); } //=========================================================================================================================== // mDNSPlatformStrLen //=========================================================================================================================== mDNSu32 mDNSPlatformStrLen( const void *inSrc ) { check( inSrc ); return( (mDNSu32) strlen( (const char *) inSrc ) ); } //=========================================================================================================================== // mDNSPlatformStrCopy //=========================================================================================================================== void mDNSPlatformStrCopy( void *inDst, const void *inSrc ) { check( inSrc ); check( inDst ); strcpy( (char *) inDst, (const char*) inSrc ); } //=========================================================================================================================== // mDNSPlatformMemCopy //=========================================================================================================================== void mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize ) { check( inSrc ); check( inDst ); memcpy( inDst, inSrc, inSize ); } //=========================================================================================================================== // mDNSPlatformMemSame //=========================================================================================================================== mDNSBool mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize ) { check( inSrc ); check( inDst ); return( 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 ); if( inMem ) free( inMem ); } //=========================================================================================================================== // mDNSPlatformRandomSeed //=========================================================================================================================== mDNSexport mDNSu32 mDNSPlatformRandomSeed( void ) { return( tickGet() ); } //=========================================================================================================================== // mDNSPlatformTimeInit //=========================================================================================================================== mDNSexport mStatus mDNSPlatformTimeInit( void ) { // No special setup is required on VxWorks -- we just use tickGet(). return( mStatus_NoError ); } //=========================================================================================================================== // mDNSPlatformRawTime //=========================================================================================================================== mDNSs32 mDNSPlatformRawTime( void ) { return( (mDNSs32) tickGet() ); } //=========================================================================================================================== // mDNSPlatformUTC //=========================================================================================================================== mDNSexport mDNSs32 mDNSPlatformUTC( void ) { return( (mDNSs32) time( NULL ) ); } //=========================================================================================================================== // mDNSPlatformInterfaceIDfromInterfaceIndex //=========================================================================================================================== mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS *const inMDNS, mDNSu32 inIndex ) { NetworkInterfaceInfoVxWorks * i; if( inIndex == (mDNSu32) -1 ) return( mDNSInterface_LocalOnly ); if( inIndex != 0 ) { for( i = inMDNS->p->interfaceList; i; i = i->next ) { // Don't get tricked by inactive interfaces with no InterfaceID set. if( i->ifinfo.InterfaceID && ( i->scopeID == inIndex ) ) return( i->ifinfo.InterfaceID ); } } return( NULL ); } //=========================================================================================================================== // mDNSPlatformInterfaceIndexfromInterfaceID //=========================================================================================================================== mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS *const inMDNS, mDNSInterfaceID inID ) { NetworkInterfaceInfoVxWorks * i; if( inID == mDNSInterface_LocalOnly ) return( (mDNSu32) -1 ); if( inID ) { // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces. for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {} if( i ) return( i->scopeID ); } return( 0 ); } //=========================================================================================================================== // mDNSPlatformInterfaceNameToID //=========================================================================================================================== mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) { NetworkInterfaceInfoVxWorks * i; for( i = inMDNS->p->interfaceList; i; i = i->next ) { // Don't get tricked by inactive interfaces with no InterfaceID set. if( i->ifinfo.InterfaceID && ( strcmp( i->ifinfo.ifname, inName ) == 0 ) ) { *outID = (mDNSInterfaceID) i; return( mStatus_NoError ); } } return( mStatus_NoSuchNameErr ); } //=========================================================================================================================== // mDNSPlatformInterfaceIDToInfo //=========================================================================================================================== mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) { NetworkInterfaceInfoVxWorks * i; // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces. for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {} if( !i ) return( mStatus_NoSuchNameErr ); outInfo->name = i->ifinfo.ifname; outInfo->ip = i->ifinfo.ip; return( mStatus_NoError ); } //=========================================================================================================================== // debugf_ //=========================================================================================================================== #if ( MDNS_DEBUGMSGS > 0 ) mDNSexport void debugf_( const char *inFormat, ... ) { char buffer[ 512 ]; va_list args; va_start( args, inFormat ); 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; va_start( args, inFormat ); mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); dlog( kDebugLevelVerbose, "%s\n", buffer ); } #endif //=========================================================================================================================== // LogMsg //=========================================================================================================================== mDNSexport void LogMsg( const char *inFormat, ... ) { #if ( DEBUG ) char buffer[ 512 ]; va_list args; va_start( args, inFormat ); mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); dlog( kDebugLevelWarning, "%s\n", buffer ); #else DEBUG_UNUSED( inFormat ); #endif } #if ( DEBUG ) //=========================================================================================================================== // DebugMsg //=========================================================================================================================== mDNSlocal void DebugMsg( DebugLevel inLevel, const char *inFormat, ... ) { char buffer[ 512 ]; va_list args; va_start( args, inFormat ); mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); va_end( args ); if( inLevel >= gMDNSDebugOverrideLevel ) inLevel = kDebugLevelMax; dlog( inLevel, "%s", buffer ); } #endif #if 0 #pragma mark - #pragma mark == Interfaces == #endif //=========================================================================================================================== // UpdateInterfaceList //=========================================================================================================================== #if ( MDNS_ENABLE_PPP ) // Note: This includes PPP dial-in interfaces (pppXYZ), but not PPP dial-out interface (pppdXYZ). #define IsCompatibleInterface( IFA ) \ ( ( ( IFA )->ifa_flags & IFF_UP ) && \ ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) ) && \ ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) ) && \ ( !( ( IFA )->ifa_flags & IFF_POINTOPOINT ) || ( strncmp( ( IFA )->ifa_name, "pppd", 4 ) != 0 ) ) ) #else #define IsCompatibleInterface( IFA ) \ ( ( ( ( IFA )->ifa_flags & ( IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT ) ) == ( IFF_UP | IFF_MULTICAST ) ) && \ ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) ) && \ ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) ) ) #endif #define IsLinkLocalSockAddr( SA ) \ ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ : IN6_IS_ADDR_LINKLOCAL( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) #define FamilyToString( X ) \ ( ( ( X ) == AF_INET ) ? "AF_INET" : \ ( ( ( X ) == AF_INET6 ) ? "AF_INET6" : \ ( ( ( X ) == AF_LINK ) ? "AF_LINK" : \ "UNKNOWN" ) ) ) mDNSlocal mStatus UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC ) { mStatus err; struct ifaddrs * ifaList; struct ifaddrs * ifa; int family; mDNSBool foundV4; mDNSBool foundV6; struct ifaddrs * loopbackV4; struct ifaddrs * loopbackV6; mDNSEthAddr primaryMAC; SocketRef infoSock; char defaultName[ 64 ]; NetworkInterfaceInfoVxWorks * i; domainlabel nicelabel; domainlabel hostlabel; domainlabel tmp; ifaList = NULL; foundV4 = mDNSfalse; foundV6 = mDNSfalse; loopbackV4 = NULL; loopbackV6 = NULL; primaryMAC = zeroEthAddr; // Set up an IPv6 socket so we can check the state of interfaces using SIOCGIFAFLAG_IN6. infoSock = socket( AF_INET6, SOCK_DGRAM, 0 ); check_translated_errno( IsValidSocket( infoSock ), errno_compat(), kUnknownErr ); // Run through the entire list of interfaces. err = getifaddrs( &ifaList ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); for( ifa = ifaList; ifa; ifa = ifa->ifa_next ) { int flags; family = ifa->ifa_addr->sa_family; dmsg( kDebugLevelVerbose, DEBUG_NAME "%s: %8s(%d), Flags 0x%08X, Family %8s(%2d)\n", __ROUTINE__, ifa->ifa_name, if_nametoindex( ifa->ifa_name ), ifa->ifa_flags, FamilyToString( family ), family ); // Save off the MAC address of the first Ethernet-ish interface. if( family == AF_LINK ) { struct sockaddr_dl * sdl; sdl = (struct sockaddr_dl *) ifa->ifa_addr; if( ( sdl->sdl_type == IFT_ETHER ) && ( sdl->sdl_alen == sizeof( primaryMAC ) && mDNSSameEthAddress( &primaryMAC, &zeroEthAddr ) ) ) { memcpy( primaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6 ); } } if( !IsCompatibleInterface( ifa ) ) continue; // If this is a link-local address and there's a non-link-local address on this interface, skip this alias. if( IsLinkLocalSockAddr( ifa->ifa_addr ) ) { struct ifaddrs * ifaLL; for( ifaLL = ifaList; ifaLL; ifaLL = ifaLL->ifa_next ) { if( ifaLL->ifa_addr->sa_family != family ) continue; if( !IsCompatibleInterface( ifaLL ) ) continue; if( strcmp( ifaLL->ifa_name, ifa->ifa_name ) != 0 ) continue; if( !IsLinkLocalSockAddr( ifaLL->ifa_addr ) ) break; } if( ifaLL ) { dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %8s(%d) skipping link-local alias\n", __ROUTINE__, ifa->ifa_name, if_nametoindex( ifa->ifa_name ) ); continue; } } // If this is an IPv6 interface, perform additional checks to make sure it is really ready for use. // If this is a loopback interface, save it off since we may add it later if there are no other interfaces. // Otherwise, add the interface to the list. flags = 0; if( ( family == AF_INET6 ) && IsValidSocket( infoSock ) ) { struct sockaddr_in6 * sa6; struct in6_ifreq ifr6; sa6 = (struct sockaddr_in6 *) ifa->ifa_addr; mDNSPlatformMemZero( &ifr6, sizeof( ifr6 ) ); strcpy( ifr6.ifr_name, ifa->ifa_name ); ifr6.ifr_addr = *sa6; if( ioctl( infoSock, SIOCGIFAFLAG_IN6, (int) &ifr6 ) != -1 ) { flags = ifr6.ifr_ifru.ifru_flags6; } } // HACK: This excludes interfaces with IN6_IFF_DUPLICATED set instead of using IN6_IFF_NOTREADY (which is // HACK: IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED) because we currently do not get a notification when an // HACK: interface goes from the tentative state to the fully ready state. So as a short-term workaround, // HACK: this allows tentative interfaces to be registered. We should revisit if we get notification hooks. if( flags & ( IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY ) ) { dmsg( kDebugLevelNotice, DEBUG_NAME "%s: %8s(%d), SIOCGIFAFLAG_IN6 not ready yet (0x%X)\n", __ROUTINE__, ifa->ifa_name, if_nametoindex( ifa->ifa_name ), flags ); continue; } if( ifa->ifa_flags & IFF_LOOPBACK ) { if( family == AF_INET ) loopbackV4 = ifa; else loopbackV6 = ifa; } else { if( ( family == AF_INET ) && gMDNSDeferIPv4 && IsLinkLocalSockAddr( ifa->ifa_addr ) ) continue; i = AddInterfaceToList( inMDNS, ifa, inUTC ); if( i && i->multicast ) { if( family == AF_INET ) foundV4 = mDNStrue; else foundV6 = mDNStrue; } } } // For efficiency, we don't register a loopback interface when other interfaces of that family are available. if( !foundV4 && loopbackV4 ) AddInterfaceToList( inMDNS, loopbackV4, inUTC ); if( !foundV6 && loopbackV6 ) AddInterfaceToList( inMDNS, loopbackV6, inUTC ); freeifaddrs( ifaList ); if( IsValidSocket( infoSock ) ) close_compat( infoSock ); // The list is complete. Set the McastTxRx setting for each interface. We always send and receive using IPv4. // 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. for( i = inMDNS->p->interfaceList; i; i = i->next ) { if( i->exists ) { mDNSBool txrx; txrx = i->multicast && ( ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) || !FindRoutableIPv4( inMDNS, i->scopeID ) ); if( i->ifinfo.McastTxRx != txrx ) { i->ifinfo.McastTxRx = txrx; i->exists = 2; // 2=state change; need to de-register and re-register this interface. } } } // Set up the user-specified, friendly name, which is allowed to be full UTF-8. mDNS_snprintf( defaultName, sizeof( defaultName ), "Device-%02X:%02X:%02X:%02X:%02X:%02X", primaryMAC.b[ 0 ], primaryMAC.b[ 1 ], primaryMAC.b[ 2 ], primaryMAC.b[ 3 ], primaryMAC.b[ 4 ], primaryMAC.b[ 5 ] ); MakeDomainLabelFromLiteralString( &nicelabel, "Put Nice Name Here" ); // $$$ Implementers: Fill in nice name of device. if( nicelabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &nicelabel, defaultName ); // Set up the RFC 1034-compliant label. If not set or it is not RFC 1034 compliant, try the user-specified nice name. MakeDomainLabelFromLiteralString( &tmp, "Put-DNS-Name-Here" ); // $$$ Implementers: Fill in DNS name of device. ConvertUTF8PstringToRFC1034HostLabel( tmp.c, &hostlabel ); if( hostlabel.c[ 0 ] == 0 ) ConvertUTF8PstringToRFC1034HostLabel( nicelabel.c, &hostlabel ); if( hostlabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &hostlabel, defaultName ); // Update our globals and mDNS with the new labels. if( !SameDomainLabelCS( inMDNS->p->userNiceLabel.c, nicelabel.c ) ) { dmsg( kDebugLevelInfo, DEBUG_NAME "Updating nicelabel to \"%#s\"\n", nicelabel.c ); inMDNS->p->userNiceLabel = nicelabel; inMDNS->nicelabel = nicelabel; } if( !SameDomainLabelCS( inMDNS->p->userHostLabel.c, hostlabel.c ) ) { dmsg( kDebugLevelInfo, DEBUG_NAME "Updating hostlabel to \"%#s\"\n", hostlabel.c ); inMDNS->p->userHostLabel = hostlabel; inMDNS->hostlabel = hostlabel; mDNS_SetFQDN( inMDNS ); } return( mStatus_NoError ); } //=========================================================================================================================== // AddInterfaceToList //=========================================================================================================================== mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC ) { mStatus err; mDNSAddr ip; mDNSAddr mask; mDNSu32 scopeID; NetworkInterfaceInfoVxWorks ** p; NetworkInterfaceInfoVxWorks * i; i = NULL; err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ip ); require_noerr( err, exit ); err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &mask ); require_noerr( err, exit ); // Search for an existing interface with the same info. If found, just return that one. scopeID = if_nametoindex( inIFA->ifa_name ); check( scopeID ); for( p = &inMDNS->p->interfaceList; *p; p = &( *p )->next ) { if( ( scopeID == ( *p )->scopeID ) && mDNSSameAddress( &ip, &( *p )->ifinfo.ip ) ) { dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Found existing interface %u with address %#a at %#p\n", __ROUTINE__, scopeID, &ip, *p ); ( *p )->exists = mDNStrue; i = *p; goto exit; } } // Allocate the new interface info and fill it out. i = (NetworkInterfaceInfoVxWorks *) calloc( 1, sizeof( *i ) ); require( i, exit ); dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Making new interface %u with address %#a at %#p\n", __ROUTINE__, scopeID, &ip, i ); strncpy( i->ifinfo.ifname, inIFA->ifa_name, sizeof( i->ifinfo.ifname ) ); i->ifinfo.ifname[ sizeof( i->ifinfo.ifname ) - 1 ] = '\0'; i->ifinfo.InterfaceID = NULL; i->ifinfo.ip = ip; i->ifinfo.mask = mask; i->ifinfo.Advertise = inMDNS->AdvertiseLocalAddresses; i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList. i->next = NULL; i->exists = mDNStrue; i->lastSeen = inUTC; i->scopeID = scopeID; i->family = inIFA->ifa_addr->sa_family; i->multicast = ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTOPOINT ); i->ss.info = i; i->ss.sockV4 = kInvalidSocketRef; i->ss.sockV6 = kInvalidSocketRef; *p = i; exit: return( i ); } //=========================================================================================================================== // SetupActiveInterfaces // // Returns a count of non-link local IPv4 addresses registered. //=========================================================================================================================== #define mDNSAddressIsNonLinkLocalIPv4( X ) \ ( ( ( X )->type == mDNSAddrType_IPv4 ) && ( ( ( X )->ip.v4.b[ 0 ] != 169 ) || ( ( X )->ip.v4.b[ 1 ] != 254 ) ) ) mDNSlocal int SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC ) { int count; NetworkInterfaceInfoVxWorks * i; count = 0; for( i = inMDNS->p->interfaceList; i; i = i->next ) { NetworkInterfaceInfo * n; NetworkInterfaceInfoVxWorks * primary; if( !i->exists ) continue; // Search for the primary interface and sanity check it. n = &i->ifinfo; primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID ); if( !primary ) { dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! didn't find %s(%u)\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID ); continue; } if( n->InterfaceID && ( n->InterfaceID != (mDNSInterfaceID) primary ) ) { dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! n->InterfaceID %#p != primary %#p\n", __ROUTINE__, n->InterfaceID, primary ); n->InterfaceID = NULL; } // If n->InterfaceID is set, it means we've already called mDNS_RegisterInterface() for this interface. // so we don't need to call it again. Otherwise, register the interface with mDNS. if( !n->InterfaceID ) { mDNSBool flapping; n->InterfaceID = (mDNSInterfaceID) primary; // If lastSeen == inUTC, then this is a brand-new interface, or an interface that never went away. // If lastSeen != inUTC, then this is an old interface, that went away for (inUTC - lastSeen) seconds. // If it's is an old one that went away and came back in less than a minute, we're in a flapping scenario. flapping = ( ( inUTC - i->lastSeen ) > 0 ) && ( ( inUTC - i->lastSeen ) < 60 ); mDNS_RegisterInterface( inMDNS, n, (flapping ? SlowActivation : NormalActivation)); if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count; dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Registered %8s(%u) InterfaceID %#p %#a%s%s\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID, primary, &n->ip, flapping ? " (Flapping)" : "", n->InterfaceActive ? " (Primary)" : "" ); } // Set up a socket if it's not already set up. If multicast is not enabled on this interface then we // don't need a socket since unicast traffic will be handled on the unicast socket. if( n->McastTxRx ) { mStatus err; if( ( ( i->family == AF_INET ) && !IsValidSocket( primary->ss.sockV4 ) ) || ( ( i->family == AF_INET6 ) && !IsValidSocket( primary->ss.sockV6 ) ) ) { err = SetupSocket( inMDNS, &i->ifinfo.ip, mDNStrue, i->family, &primary->ss ); check_noerr( err ); } } else { dmsg( kDebugLevelInfo, DEBUG_NAME "%s: No Tx/Rx on %8s(%u) InterfaceID %#p %#a\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID, primary, &n->ip ); } } return( count ); } //=========================================================================================================================== // MarkAllInterfacesInactive //=========================================================================================================================== mDNSlocal void MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC ) { NetworkInterfaceInfoVxWorks * i; for( i = inMDNS->p->interfaceList; i; i = i->next ) { if( !i->exists ) continue; i->lastSeen = inUTC; i->exists = mDNSfalse; } } //=========================================================================================================================== // ClearInactiveInterfaces // // Returns count of non-link local IPv4 addresses de-registered. //=========================================================================================================================== mDNSlocal int ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing ) { int count; NetworkInterfaceInfoVxWorks * i; NetworkInterfaceInfoVxWorks ** p; // First pass: // If an interface is going away, then de-register it from mDNSCore. // We also have to de-register it if the primary interface that it's using for its InterfaceID is going away. // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory // it refers to has gone away, we'll crash. Don't actually close the sockets or free the memory yet though: // When the last representative of an interface goes away mDNSCore may want to send goodbye packets on that // interface. (Not yet implemented, but a good idea anyway.). count = 0; for( i = inMDNS->p->interfaceList; i; i = i->next ) { NetworkInterfaceInfoVxWorks * primary; // 1. If this interface is no longer active, or its InterfaceID is changing, de-register it. if( !i->ifinfo.InterfaceID ) continue; primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID ); if( ( i->exists == 0 ) || ( i->exists == 2 ) || ( i->ifinfo.InterfaceID != (mDNSInterfaceID) primary ) ) { dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Deregistering %8s(%u) InterfaceID %#p %#a%s\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : "" ); mDNS_DeregisterInterface( inMDNS, &i->ifinfo, NormalActivation ); i->ifinfo.InterfaceID = NULL; if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count; } } // Second pass: // Now that everything that's going to de-register has done so, we can close sockets and free the memory. p = &inMDNS->p->interfaceList; while( *p ) { i = *p; // 2. Close all our sockets. We'll recreate them later as needed. // (We may have previously had both v4 and v6, and we may not need both any more.). ForgetSocket( &i->ss.sockV4 ); ForgetSocket( &i->ss.sockV6 ); // 3. If no longer active, remove the interface from the list and free its memory. if( !i->exists ) { mDNSBool deleteIt; if( inClosing ) { check_string( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0, "closing with in-use records!" ); deleteIt = mDNStrue; } else { if( i->lastSeen == inUTC ) i->lastSeen = inUTC - 1; deleteIt = ( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0 ) && ( ( inUTC - i->lastSeen ) >= 60 ); } dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %-13s %8s(%u) InterfaceID %#p %#a Age %d%s\n", __ROUTINE__, deleteIt ? "Deleting" : "Holding", i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip, inUTC - i->lastSeen, i->ifinfo.InterfaceActive ? " (Primary)" : "" ); if( deleteIt ) { *p = i->next; free( i ); continue; } } p = &i->next; } return( count ); } //=========================================================================================================================== // FindRoutableIPv4 //=========================================================================================================================== mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID ) { #if ( MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6 ) NetworkInterfaceInfoVxWorks * i; for( i = inMDNS->p->interfaceList; i; i = i->next ) { if( i->exists && ( i->scopeID == inScopeID ) && mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) { break; } } return( i ); #else DEBUG_UNUSED( inMDNS ); DEBUG_UNUSED( inScopeID ); return( NULL ); #endif } //=========================================================================================================================== // FindInterfaceByIndex //=========================================================================================================================== mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex ) { NetworkInterfaceInfoVxWorks * i; check( inIndex != 0 ); for( i = inMDNS->p->interfaceList; i; i = i->next ) { if( i->exists && ( i->scopeID == inIndex ) && ( MDNS_AAAA_OVER_IPV4 || ( ( inFamily == AF_INET ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) ) || ( ( inFamily == AF_INET6 ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv6 ) ) ) ) { return( i ); } } return( NULL ); } //=========================================================================================================================== // SetupSocket //=========================================================================================================================== mDNSlocal mStatus SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS ) { mStatus err; SocketRef * sockPtr; mDNSIPPort port; SocketRef sock; const int on = 1; check( inAddr ); check( inSS ); sockPtr = ( inFamily == AF_INET ) ? &inSS->sockV4 : &inSS->sockV6; port = ( inMcast || inMDNS->CanReceiveUnicastOn5353 ) ? MulticastDNSPort : zeroIPPort; sock = socket( inFamily, SOCK_DGRAM, IPPROTO_UDP ); err = translate_errno( IsValidSocket( sock ), errno_compat(), mStatus_UnknownErr ); require_noerr( err, exit ); // Allow multiple listeners if this is a multicast port. if( port.NotAnInteger ) { err = setsockopt( sock, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // Set up the socket based on the family (IPv4 or IPv6). if( inFamily == AF_INET ) { const int ttlV4 = 255; const u_char ttlV4Mcast = 255; struct sockaddr_in sa4; // Receive destination addresses so we know which address the packet was sent to. err = setsockopt( sock, IPPROTO_IP, IP_RECVDSTADDR, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Receive interface indexes so we know which interface received the packet. err = setsockopt( sock, IPPROTO_IP, IP_RECVIF, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving. if( inMcast ) { struct in_addr addrV4; struct ip_mreq mreqV4; addrV4.s_addr = inAddr->ip.v4.NotAnInteger; mreqV4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; mreqV4.imr_interface = addrV4; err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqV4, sizeof( mreqV4 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addrV4, sizeof( addrV4 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // Send unicast packets with TTL 255 (helps against spoofing). err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &ttlV4, sizeof( ttlV4 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Send multicast packets with TTL 255 (helps against spoofing). err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttlV4Mcast, sizeof( ttlV4Mcast ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Start listening for packets. mDNSPlatformMemZero( &sa4, sizeof( sa4 ) ); sa4.sin_len = sizeof( sa4 ); sa4.sin_family = AF_INET; sa4.sin_port = port.NotAnInteger; sa4.sin_addr.s_addr = htonl( INADDR_ANY ); // We want to receive multicasts AND unicasts on this socket. err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } else if( inFamily == AF_INET6 ) { struct sockaddr_in6 sa6; const int ttlV6 = 255; // Receive destination addresses and interface index so we know where the packet was received and intended. err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Receive only IPv6 packets because otherwise, we may get IPv4 addresses as IPv4-mapped IPv6 addresses. err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving. if( inMcast ) { u_int ifindex; struct ipv6_mreq mreqV6; ifindex = inSS->info->scopeID; mreqV6.ipv6mr_interface = ifindex; mreqV6.ipv6mr_multiaddr = *( (struct in6_addr * ) &AllDNSLinkGroup_v6.ip.v6 ); err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqV6, sizeof( mreqV6 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &ifindex, sizeof( ifindex ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } // Send unicast packets with TTL 255 (helps against spoofing). err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Send multicast packets with TTL 255 (helps against spoofing). err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Receive our own packets for same-machine operation. err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &on, sizeof( on ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); // Start listening for packets. mDNSPlatformMemZero( &sa6, sizeof( sa6 ) ); sa6.sin6_len = sizeof( sa6 ); sa6.sin6_family = AF_INET6; sa6.sin6_port = port.NotAnInteger; sa6.sin6_flowinfo = 0; sa6.sin6_addr = in6addr_any; // We want to receive multicasts AND unicasts on this socket. sa6.sin6_scope_id = 0; err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); } else { dmsg( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inFamily ); err = kUnsupportedErr; goto exit; } // Make the socket non-blocking so we can potentially get multiple packets per select call. err = ioctl( sock, FIONBIO, (int) &on ); check_translated_errno( err == 0, errno_compat(), kOptionErr ); *sockPtr = sock; sock = kInvalidSocketRef; err = mStatus_NoError; exit: if( IsValidSocket( sock ) ) close_compat( sock ); return( err ); } //=========================================================================================================================== // SockAddrToMDNSAddr //=========================================================================================================================== mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP ) { 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; 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; err = mStatus_NoError; } else { dmsg( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family (%d)\n", __ROUTINE__, inSA->sa_family ); err = mStatus_BadParamErr; } return( err ); } #if 0 #pragma mark - #pragma mark == Commands == #endif //=========================================================================================================================== // SetupCommandPipe //=========================================================================================================================== mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ) { mStatus err; err = pipeDevCreate( "/pipe/mDNS", 32, 1 ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); inMDNS->p->commandPipe = open( "/pipe/mDNS", O_RDWR, 0 ); err = translate_errno( inMDNS->p->commandPipe != ERROR, errno_compat(), mStatus_UnsupportedErr ); require_noerr( err, exit ); exit: return( err ); } //=========================================================================================================================== // TearDownCommandPipe //=========================================================================================================================== mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ) { mStatus err; if( inMDNS->p->commandPipe != ERROR ) { err = close( inMDNS->p->commandPipe ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); inMDNS->p->commandPipe = ERROR; err = pipeDevDelete( "/pipe/mDNS", FALSE ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); } return( mStatus_NoError ); } //=========================================================================================================================== // SendCommand //=========================================================================================================================== mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ) { mStatus err; require_action_quiet( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) ); err = translate_errno( err >= 0, errno_compat(), kWriteErr ); require_noerr( err, exit ); exit: return( err ); } //=========================================================================================================================== // ProcessCommand //=========================================================================================================================== mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ) { mStatus err; MDNSPipeCommandCode cmd; mDNSs32 utc; err = read( inMDNS->p->commandPipe, &cmd, sizeof( cmd ) ); err = translate_errno( err >= 0, errno_compat(), kReadErr ); require_noerr( err, exit ); switch( cmd ) { case kMDNSPipeCommandCodeReschedule: // Reschedule: just break out to re-run mDNS_Execute. break; case kMDNSPipeCommandCodeReconfigure: // Reconfigure: rebuild the interface list after a config change. dmsg( kDebugLevelInfo, DEBUG_NAME "*** NETWORK CONFIGURATION CHANGE ***\n" ); mDNSPlatformLock( inMDNS ); utc = mDNSPlatformUTC(); MarkAllInterfacesInactive( inMDNS, utc ); UpdateInterfaceList( inMDNS, utc ); ClearInactiveInterfaces( inMDNS, utc, mDNSfalse ); SetupActiveInterfaces( inMDNS, utc ); mDNSPlatformUnlock( inMDNS ); mDNS_ConfigChanged(inMDNS); break; case kMDNSPipeCommandCodeQuit: // Quit: just set a flag so the task exits cleanly. inMDNS->p->quit = mDNStrue; break; default: dmsg( kDebugLevelError, DEBUG_NAME "unknown pipe command (%d)\n", cmd ); err = mStatus_BadParamErr; goto exit; } exit: return( err ); } #if 0 #pragma mark - #pragma mark == Threads == #endif //=========================================================================================================================== // Task //=========================================================================================================================== mDNSlocal void Task( mDNS *inMDNS ) { mStatus err; mDNSs32 nextEvent; fd_set readSet; int maxFd; struct timeval timeout; NetworkInterfaceInfoVxWorks * i; int fd; int n; check( inMDNS ); err = TaskInit( inMDNS ); require_noerr( err, exit ); while( !inMDNS->p->quit ) { // Let mDNSCore do its work then wait for an event. On idle timeouts (n == 0), just loop back to mDNS_Exceute. nextEvent = mDNS_Execute( inMDNS ); TaskSetupSelect( inMDNS, &readSet, &maxFd, nextEvent, &timeout ); n = select( maxFd + 1, &readSet, NULL, NULL, &timeout ); check_translated_errno( n >= 0, errno_compat(), kUnknownErr ); if( n == 0 ) continue; // Process interface-specific sockets with pending data. n = 0; for( i = inMDNS->p->interfaceList; i; i = i->next ) { fd = i->ss.sockV4; if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { TaskProcessPackets( inMDNS, &i->ss, fd ); ++n; } fd = i->ss.sockV6; if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { TaskProcessPackets( inMDNS, &i->ss, fd ); ++n; } } // Process unicast sockets with pending data. fd = inMDNS->p->unicastSS.sockV4; if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd ); ++n; } fd = inMDNS->p->unicastSS.sockV6; if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) ) { TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd ); ++n; } // Processing pending commands. fd = inMDNS->p->commandPipe; check( fd >= 0 ); if( FD_ISSET( fd, &readSet ) ) { ProcessCommand( inMDNS ); ++n; } check_string( n > 0, "select said something was readable, but nothing was" ); } exit: TaskTerm( inMDNS ); } //=========================================================================================================================== // TaskInit //=========================================================================================================================== mDNSlocal mStatus TaskInit( mDNS *inMDNS ) { mStatus err; mDNSs32 utc; socklen_t len; inMDNS->p->taskID = taskIdSelf(); err = SetupCommandPipe( inMDNS ); require_noerr( err, exit ); inMDNS->CanReceiveUnicastOn5353 = mDNStrue; // Set up the HINFO string using the description property (e.g. "Device V1.0"). inMDNS->HIHardware.c[ 0 ] = 11; memcpy( &inMDNS->HIHardware.c[ 1 ], "Device V1.0", inMDNS->HIHardware.c[ 0 ] ); // $$$ Implementers: Fill in real info. // Set up the unicast sockets. err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET, &inMDNS->p->unicastSS ); check_noerr( err ); if( err == mStatus_NoError ) { struct sockaddr_in sa4; len = sizeof( sa4 ); err = getsockname( inMDNS->p->unicastSS.sockV4, (struct sockaddr *) &sa4, &len ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); if( err == 0 ) inMDNS->UnicastPort4.NotAnInteger = sa4.sin_port; } err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET6, &inMDNS->p->unicastSS ); check_noerr( err ); if( err == mStatus_NoError ) { struct sockaddr_in6 sa6; len = sizeof( sa6 ); err = getsockname( inMDNS->p->unicastSS.sockV6, (struct sockaddr *) &sa6, &len ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); if( err == 0 ) inMDNS->UnicastPort6.NotAnInteger = sa6.sin6_port; } // Set up the interfaces. utc = mDNSPlatformUTC(); UpdateInterfaceList( inMDNS, utc ); SetupActiveInterfaces( inMDNS, utc ); err = mStatus_NoError; exit: // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not). inMDNS->p->initErr = err; semGive( inMDNS->p->initEvent ); return( err ); } //=========================================================================================================================== // TaskTerm //=========================================================================================================================== mDNSlocal void TaskTerm( mDNS *inMDNS ) { mStatus err; mDNSs32 utc; // Tear down all interfaces. utc = mDNSPlatformUTC(); MarkAllInterfacesInactive( inMDNS, utc ); ClearInactiveInterfaces( inMDNS, utc, mDNStrue ); check_string( !inMDNS->p->interfaceList, "LEAK: closing without deleting all interfaces" ); // Close unicast sockets. ForgetSocket( &inMDNS->p->unicastSS.sockV4); ForgetSocket( &inMDNS->p->unicastSS.sockV6 ); // Tear down everything else that was set up in TaskInit then signal back that we're done. err = TearDownCommandPipe( inMDNS ); check_noerr( err ); err = semGive( inMDNS->p->quitEvent ); check_translated_errno( err == 0, errno_compat(), kUnknownErr ); } //=========================================================================================================================== // TaskSetupSelect //=========================================================================================================================== mDNSlocal void TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout ) { NetworkInterfaceInfoVxWorks * i; int maxFd; int fd; mDNSs32 delta; FD_ZERO( outSet ); maxFd = -1; // Add the interface-specific sockets. for( i = inMDNS->p->interfaceList; i; i = i->next ) { fd = i->ss.sockV4; if( IsValidSocket( fd ) ) { FD_SET( fd, outSet ); if( fd > maxFd ) maxFd = fd; } fd = i->ss.sockV6; if( IsValidSocket( fd ) ) { FD_SET( fd, outSet ); if( fd > maxFd ) maxFd = fd; } } // Add the unicast sockets. fd = inMDNS->p->unicastSS.sockV4; if( IsValidSocket( fd ) ) { FD_SET( fd, outSet ); if( fd > maxFd ) maxFd = fd; } fd = inMDNS->p->unicastSS.sockV6; if( IsValidSocket( fd ) ) { FD_SET( fd, outSet ); if( fd > maxFd ) maxFd = fd; } // Add the command pipe. fd = inMDNS->p->commandPipe; check( fd >= 0 ); FD_SET( fd, outSet ); if( fd > maxFd ) maxFd = fd; check( maxFd > 0 ); *outMaxFd = maxFd; // Calculate how long to wait before performing idle processing. delta = inNextEvent - mDNS_TimeNow( inMDNS ); if( delta <= 0 ) { // The next task time is now or in the past. Set the timeout to fire immediately. outTimeout->tv_sec = 0; outTimeout->tv_usec = 0; } else { // Calculate the seconds and microseconds until the timeout should occur. Add one to the ticks remainder // before multiplying to account for integer rounding error and avoid firing the timeout too early. outTimeout->tv_sec = delta / mDNSPlatformOneSecond; outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicro; if( outTimeout->tv_usec >= 1000000L ) { outTimeout->tv_sec += 1; outTimeout->tv_usec = 0; } } } //=========================================================================================================================== // TaskProcessPackets //=========================================================================================================================== mDNSlocal void TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock ) { mDNSu32 ifindex; ssize_t n; mDNSu8 * buf; size_t size; struct sockaddr_storage from; size_t fromSize; mDNSAddr destAddr; mDNSAddr senderAddr; mDNSIPPort senderPort; mDNSInterfaceID id; buf = (mDNSu8 *) &inMDNS->imsg; size = sizeof( inMDNS->imsg ); for( ;; ) { ifindex = 0; n = mDNSRecvMsg( inSock, buf, size, &from, sizeof( from ), &fromSize, &destAddr, &ifindex ); if( n < 0 ) break; if( from.ss_family == AF_INET ) { struct sockaddr_in * sa4; sa4 = (struct sockaddr_in *) &from; senderAddr.type = mDNSAddrType_IPv4; senderAddr.ip.v4.NotAnInteger = sa4->sin_addr.s_addr; senderPort.NotAnInteger = sa4->sin_port; } else if( from.ss_family == AF_INET6 ) { struct sockaddr_in6 * sa6; sa6 = (struct sockaddr_in6 *) &from; senderAddr.type = mDNSAddrType_IPv6; senderAddr.ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); senderPort.NotAnInteger = sa6->sin6_port; } else { dmsg( kDebugLevelWarning, DEBUG_NAME "%s: WARNING! from addr unknown family %d\n", __ROUTINE__, from.ss_family ); continue; } // Even though we indicated a specific interface when joining the multicast group, a weirdness of the // sockets API means that even though this socket has only officially joined the multicast group // on one specific interface, the kernel will still deliver multicast packets to it no matter which // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. // To work around this weirdness, we use the IP_RECVIF/IPV6_PKTINFO options to find the interface // on which the packet arrived, and ignore the packet if it really arrived on some other interface. if( mDNSAddrIsDNSMulticast( &destAddr ) ) { if( !inSS->info || !inSS->info->exists ) { dpkt( kDebugLevelChatty - 3, DEBUG_NAME " ignored mcast, src=[%#39a], dst=[%#39a], if= unicast socket %d\n", &senderAddr, &destAddr, inSock ); continue; } if( ifindex != inSS->info->scopeID ) { #if ( DEBUG && MDNS_DEBUG_PACKETS ) char ifname[ IF_NAMESIZE ]; #endif dpkt( kDebugLevelChatty - 3, DEBUG_NAME " ignored mcast, src=[%#39a] dst=[%#39a], if=%8s(%u) -- really for %8s(%u)\n", &senderAddr, &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID, if_indextoname( ifindex, ifname ), ifindex ); continue; } id = inSS->info->ifinfo.InterfaceID; dpkt( kDebugLevelChatty - 2, DEBUG_NAME "recv %4d bytes, src=[%#39a]:%5hu, dst=[%#39a], if=%8s(%u) %#p\n", n, &senderAddr, mDNSVal16( senderPort ), &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID, id ); } else { NetworkInterfaceInfoVxWorks * i; // For unicast packets, try to find the matching interface. for( i = inMDNS->p->interfaceList; i && ( i->scopeID != ifindex ); i = i->next ) {} if( i ) id = i->ifinfo.InterfaceID; else id = NULL; } mDNSCoreReceive( inMDNS, buf, buf + n, &senderAddr, senderPort, &destAddr, MulticastDNSPort, id ); } } //=========================================================================================================================== // mDNSRecvMsg //=========================================================================================================================== mDNSlocal ssize_t mDNSRecvMsg( SocketRef inSock, void * inBuffer, size_t inBufferSize, void * outFrom, size_t inFromSize, size_t * outFromSize, mDNSAddr * outDstAddr, uint32_t * outIndex ) { struct msghdr msg; struct iovec iov; ssize_t n; char ancillary[ 1024 ]; struct cmsghdr * cmPtr; int err; // Read a packet and any ancillary data. Note: EWOULDBLOCK errors are expected when we've read all pending packets. iov.iov_base = (char *) inBuffer; iov.iov_len = inBufferSize; msg.msg_name = (caddr_t) outFrom; msg.msg_namelen = inFromSize; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (caddr_t) &ancillary; msg.msg_controllen = sizeof( ancillary ); msg.msg_flags = 0; n = recvmsg( inSock, &msg, 0 ); if( n < 0 ) { err = errno_compat(); if( err != EWOULDBLOCK ) dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) returned %d, errno %d\n", __ROUTINE__, inSock, n, err ); goto exit; } if( msg.msg_controllen < sizeof( struct cmsghdr ) ) { dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) msg_controllen %d < sizeof( struct cmsghdr ) %u\n", __ROUTINE__, inSock, msg.msg_controllen, sizeof( struct cmsghdr ) ); n = mStatus_UnknownErr; goto exit; } if( msg.msg_flags & MSG_CTRUNC ) { dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) MSG_CTRUNC (%d recv'd)\n", __ROUTINE__, inSock, n ); n = mStatus_BadFlagsErr; goto exit; } *outFromSize = msg.msg_namelen; // Parse each option out of the ancillary data. for( cmPtr = CMSG_FIRSTHDR( &msg ); cmPtr; cmPtr = CMSG_NXTHDR( &msg, cmPtr ) ) { if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVDSTADDR ) ) { outDstAddr->type = mDNSAddrType_IPv4; outDstAddr->ip.v4.NotAnInteger = *( (mDNSu32 *) CMSG_DATA( cmPtr ) ); } else if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVIF ) ) { struct sockaddr_dl * sdl; sdl = (struct sockaddr_dl *) CMSG_DATA( cmPtr ); *outIndex = sdl->sdl_index; } else if( ( cmPtr->cmsg_level == IPPROTO_IPV6 ) && ( cmPtr->cmsg_type == IPV6_PKTINFO ) ) { struct in6_pktinfo * pi6; pi6 = (struct in6_pktinfo *) CMSG_DATA( cmPtr ); outDstAddr->type = mDNSAddrType_IPv6; outDstAddr->ip.v6 = *( (mDNSv6Addr *) &pi6->ipi6_addr ); *outIndex = pi6->ipi6_ifindex; } } exit: return( n ); } #if 0 #pragma mark - #pragma mark == Debugging == #endif #if ( DEBUG && MDNS_DEBUG_SHOW ) //=========================================================================================================================== // mDNSShow //=========================================================================================================================== void mDNSShow( void ); void mDNSShow( void ) { NetworkInterfaceInfoVxWorks * i; int num; AuthRecord * r; mDNSs32 utc; // Globals dmsg( kDebugLevelMax, "\n-- mDNS globals --\n" ); dmsg( kDebugLevelMax, " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) ); dmsg( kDebugLevelMax, " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) ); dmsg( kDebugLevelMax, " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) ); dmsg( kDebugLevelMax, " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) ); dmsg( kDebugLevelMax, " mDNSPlatformOneSecond = %ld\n", mDNSPlatformOneSecond ); dmsg( kDebugLevelMax, " gMDNSTicksToMicro = %ld\n", gMDNSTicksToMicro ); dmsg( kDebugLevelMax, " gMDNSPtr = %#p\n", gMDNSPtr ); if( !gMDNSPtr ) { dmsg( kDebugLevelMax, "### mDNS not initialized\n" ); return; } dmsg( kDebugLevelMax, " nicelabel = \"%#s\"\n", gMDNSPtr->nicelabel.c ); dmsg( kDebugLevelMax, " hostLabel = \"%#s\"\n", gMDNSPtr->hostlabel.c ); dmsg( kDebugLevelMax, " MulticastHostname = \"%##s\"\n", gMDNSPtr->MulticastHostname.c ); dmsg( kDebugLevelMax, " HIHardware = \"%#s\"\n", gMDNSPtr->HIHardware.c ); dmsg( kDebugLevelMax, " HISoftware = \"%#s\"\n", gMDNSPtr->HISoftware.c ); dmsg( kDebugLevelMax, " UnicastPort4/6 = %d/%d\n", mDNSVal16( gMDNSPtr->UnicastPort4 ), mDNSVal16( gMDNSPtr->UnicastPort6 ) ); dmsg( kDebugLevelMax, " unicastSS.sockV4/V6 = %d/%d\n", gMDNSPtr->p->unicastSS.sockV4, gMDNSPtr->p->unicastSS.sockV6 ); dmsg( kDebugLevelMax, " lock = %#p\n", gMDNSPtr->p->lock ); dmsg( kDebugLevelMax, " initEvent = %#p\n", gMDNSPtr->p->initEvent ); dmsg( kDebugLevelMax, " initErr = %ld\n", gMDNSPtr->p->initErr ); dmsg( kDebugLevelMax, " quitEvent = %#p\n", gMDNSPtr->p->quitEvent ); dmsg( kDebugLevelMax, " commandPipe = %d\n", gMDNSPtr->p->commandPipe ); dmsg( kDebugLevelMax, " taskID = %#p\n", gMDNSPtr->p->taskID ); dmsg( kDebugLevelMax, "\n" ); // Interfaces utc = mDNSPlatformUTC(); dmsg( kDebugLevelMax, "-- mDNS interfaces --\n" ); num = 0; for( i = gMDNSPtr->p->interfaceList; i; i = i->next ) { dmsg( kDebugLevelMax, " interface %2d %8s(%u) [%#39a] %s, sockV4 %2d, sockV6 %2d, Age %d\n", num, i->ifinfo.ifname, i->scopeID, &i->ifinfo.ip, i->ifinfo.InterfaceID ? " REGISTERED" : "*NOT* registered", i->ss.sockV4, i->ss.sockV6, utc - i->lastSeen ); ++num; } dmsg( kDebugLevelMax, "\n" ); // Resource Records dmsg( kDebugLevelMax, "-- mDNS resource records --\n" ); num = 0; for( r = gMDNSPtr->ResourceRecords; r; r = r->next ) { i = (NetworkInterfaceInfoVxWorks *) r->resrec.InterfaceID; if( r->resrec.rrtype == kDNSType_TXT ) { RDataBody * rd; const mDNSu8 * txt; const mDNSu8 * end; mDNSu8 size; int nEntries; rd = &r->resrec.rdata->u; dmsg( kDebugLevelMax, " record %2d: %#p %8s(%u): %4d %##s %s:\n", num, i, i ? i->ifinfo.ifname : "", i ? i->scopeID : 0, r->resrec.rdlength, r->resrec.name->c, DNSTypeName( r->resrec.rrtype ) ); nEntries = 0; txt = rd->txt.c; end = txt + r->resrec.rdlength; while( txt < end ) { size = *txt++; if( ( txt + size ) > end ) { dmsg( kDebugLevelMax, " ### ERROR! txt length byte too big (%u, %u max)\n", size, end - txt ); break; } dmsg( kDebugLevelMax, " string %2d (%3d bytes): \"%.*s\"\n", nEntries, size, size, txt ); txt += size; ++nEntries; } } else { dmsg( kDebugLevelMax, " record %2d: %#p %8s(%u): %s\n", num, i, i ? i->ifinfo.ifname : "", i ? i->scopeID : 0, ARDisplayString( gMDNSPtr, r ) ); } ++num; } dmsg( kDebugLevelMax, "\n"); } #endif // DEBUG && MDNS_DEBUG_SHOW