| /* -*- 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 <stdarg.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include "vxWorks.h" |
| #include "config.h" |
| |
| #include <sys/types.h> |
| |
| #include <arpa/inet.h> |
| #include <net/if.h> |
| #include <net/if_dl.h> |
| #include <net/if_types.h> |
| #include <net/ifaddrs.h> |
| #include <netinet6/in6_var.h> |
| #include <netinet/if_ether.h> |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| |
| #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 ); |
| 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, mDNSfalse ); |
| 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 : "<any>", |
| 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 : "<any>", |
| i ? i->scopeID : 0, |
| ARDisplayString( gMDNSPtr, r ) ); |
| } |
| ++num; |
| } |
| dmsg( kDebugLevelMax, "\n"); |
| } |
| #endif // DEBUG && MDNS_DEBUG_SHOW |