From 9449f151d0ccf3ac755d5f2bd9b4057ae2b03157 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 30 Jan 2014 13:52:13 +0100 Subject: mDNS: Import The sources can be obtained via: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz --- mDNSResponder/mDNSVxWorks/README.txt | 8 + mDNSResponder/mDNSVxWorks/mDNSVxWorks.c | 2147 +++++++++++++++++++++++ mDNSResponder/mDNSVxWorks/mDNSVxWorks.h | 122 ++ mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.c | 2088 ++++++++++++++++++++++ mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.h | 129 ++ 5 files changed, 4494 insertions(+) create mode 100644 mDNSResponder/mDNSVxWorks/README.txt create mode 100644 mDNSResponder/mDNSVxWorks/mDNSVxWorks.c create mode 100644 mDNSResponder/mDNSVxWorks/mDNSVxWorks.h create mode 100644 mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.c create mode 100644 mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.h (limited to 'mDNSResponder/mDNSVxWorks') diff --git a/mDNSResponder/mDNSVxWorks/README.txt b/mDNSResponder/mDNSVxWorks/README.txt new file mode 100644 index 00000000..f0ea01ef --- /dev/null +++ b/mDNSResponder/mDNSVxWorks/README.txt @@ -0,0 +1,8 @@ +These are the platform support files for running mDNSCore on VxWorks. + +Please note, most of the developers working on the mDNSResponder code do +not have access to a VxWorks development environment, so they are not able +to personally verify that the VxWorks compiles and runs successfully after +every single change to the mDNSCore code. We do try to take care not to +make careless changes that would break the VxWorks build, but if you do +find that something is broken, let us know and we'll fix it. diff --git a/mDNSResponder/mDNSVxWorks/mDNSVxWorks.c b/mDNSResponder/mDNSVxWorks/mDNSVxWorks.c new file mode 100644 index 00000000..685dbf76 --- /dev/null +++ b/mDNSResponder/mDNSVxWorks/mDNSVxWorks.c @@ -0,0 +1,2147 @@ +/* -*- 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 ); + 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 : "", + 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 diff --git a/mDNSResponder/mDNSVxWorks/mDNSVxWorks.h b/mDNSResponder/mDNSVxWorks/mDNSVxWorks.h new file mode 100644 index 00000000..afd41e36 --- /dev/null +++ b/mDNSResponder/mDNSVxWorks/mDNSVxWorks.h @@ -0,0 +1,122 @@ +/* -*- 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. + */ + +#ifndef __MDNS_VXWORKS_H__ +#define __MDNS_VXWORKS_H__ + +#include "vxWorks.h" +#include "config.h" + +#include "semLib.h" + +#include "CommonServices.h" +#include "DebugServices.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward Declarations + +typedef struct NetworkInterfaceInfoVxWorks NetworkInterfaceInfoVxWorks; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct SocketSet + + @abstract Data for IPv4 and IPv6 sockets. + */ + +typedef struct SocketSet SocketSet; +struct SocketSet +{ + NetworkInterfaceInfoVxWorks * info; + SocketRef sockV4; + SocketRef sockV6; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct NetworkInterfaceInfoVxWorks + + @abstract Interface info for VxWorks. + */ + +struct NetworkInterfaceInfoVxWorks +{ + NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure. + NetworkInterfaceInfoVxWorks * next; + mDNSu32 exists; // 1 = currently exists in getifaddrs list; 0 = doesn't. + // 2 = exists, but McastTxRx state changed. + mDNSs32 lastSeen; // If exists == 0, last time this interface appeared in getifaddrs list. + mDNSu32 scopeID; // Interface index / IPv6 scope ID. + int family; // Socket address family of the primary socket. + mDNSBool multicast; + SocketSet ss; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNS_PlatformSupport_struct + + @abstract Data for mDNS platform plugin. + */ + +struct mDNS_PlatformSupport_struct +{ + NetworkInterfaceInfoVxWorks * interfaceList; + SocketSet unicastSS; + domainlabel userNiceLabel; + domainlabel userHostLabel; + + SEM_ID lock; + SEM_ID initEvent; + mStatus initErr; + SEM_ID quitEvent; + int commandPipe; + int taskID; + mDNSBool quit; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function mDNSReconfigure + + @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc. + + @discussion + + VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines + provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces. + */ + +void mDNSReconfigure( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function mDNSDeferIPv4 + + @abstract Tells mDNS whether to defer advertising of IPv4 interfaces. + + @discussion + + To workaround problems with clients getting a link-local IPv4 address before a DHCP address is acquired, this allows + external code to defer advertising of IPv4 addresses until a DHCP lease has been acquired (or it times out). + */ + +void mDNSDeferIPv4( mDNSBool inDefer ); + +#ifdef __cplusplus +} +#endif + +#endif // __MDNS_VXWORKS_H__ diff --git a/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.c b/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.c new file mode 100644 index 00000000..efb1f7a3 --- /dev/null +++ b/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.c @@ -0,0 +1,2088 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 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. + + Contains: mDNS platform plugin for VxWorks. + + Copyright: Copyright (C) 2002-2004 Apple Computer, Inc., All Rights Reserved. + + Notes for non-Apple platforms: + + TARGET_NON_APPLE should be defined to 1 to avoid relying on Apple-only header files, macros, or functions. + + To Do: + + - Add support for IPv6 (needs VxWorks IPv6 support). + */ + +// Set up the debug library to use the default category (see DebugServicesLite.h for details). + +#if ( !TARGET_NON_APPLE ) + #define DEBUG_USE_DEFAULT_CATEGORY 1 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vxWorks.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 "config.h" + +#if ( !TARGET_NON_APPLE ) + #include "ACP/ACPUtilities.h" + #include "Support/DebugServicesLite.h" + #include "Support/MiscUtilities.h" +#endif + +#include "mDNSEmbeddedAPI.h" + +#include "mDNSVxWorks.h" + +#if 0 +#pragma mark == Preprocessor == +#endif + +//=========================================================================================================================== +// Preprocessor +//=========================================================================================================================== + +#if ( !TARGET_NON_APPLE ) +debug_log_new_default_category( mdns ); +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNS] " + +#define kMDNSDefaultName "My-Device" + +#define kMDNSTaskName "tMDNS" +#define kMDNSTaskPriority 102 +#define kMDNSTaskStackSize 49152 + +#define kMDNSPipeName "/pipe/mDNS" +#define kMDNSPipeMessageQueueSize 32 +#define kMDNSPipeMessageSize 1 + +#define kInvalidSocketRef -1 + +typedef uint8_t MDNSPipeCommandCode; +enum +{ + kMDNSPipeCommandCodeInvalid = 0, + kMDNSPipeCommandCodeReschedule = 1, + kMDNSPipeCommandCodeReconfigure = 2, + kMDNSPipeCommandCodeQuit = 3 +}; + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef int MDNSSocketRef; + +struct MDNSInterfaceItem +{ + MDNSInterfaceItem * next; + char name[ 32 ]; + MDNSSocketRef multicastSocketRef; + MDNSSocketRef sendingSocketRef; + NetworkInterfaceInfo hostSet; + mDNSBool hostRegistered; + + int sendMulticastCounter; + int sendUnicastCounter; + int sendErrorCounter; + + int recvCounter; + int recvErrorCounter; + int recvLoopCounter; +}; + +#if 0 +#pragma mark == Macros == +#endif + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +#if ( TARGET_NON_APPLE ) + +// Do-nothing versions of the debugging macros for non-Apple platforms. + + #define check(assertion) + #define check_string( assertion, cstring ) + #define check_noerr(err) + #define check_noerr_string( error, cstring ) + #define check_errno( assertion, errno_value ) + #define debug_string( cstring ) + #define require( assertion, label ) do { if( !(assertion) ) goto label;} while(0) + #define require_string( assertion, label, string ) require(assertion, label) + #define require_quiet( assertion, label ) require( assertion, label ) + #define require_noerr( error, label ) do { if( (error) != 0 ) goto label;} while(0) + #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label ) + #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0) + #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action ) + #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action ) + #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_errno( assertion, errno_value, label ) do { if( !(assertion) ) goto label;} while(0) + #define require_errno_action( assertion, errno_value, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + + #define dlog( ARGS... ) + + #define DEBUG_UNUSED( X ) (void)( X ) +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// ifIndexToIfp is in net/if.c, but not exported by net/if.h so provide it here. + +extern struct ifnet * ifIndexToIfp(int ifIndex); + +// Platform Internals + +mDNSlocal void SetupNames( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ); +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ); +mDNSlocal mStatus +SetupSocket( + mDNS * const inMDNS, + const struct ifaddrs * inAddr, + mDNSIPPort inPort, + MDNSSocketRef * outSocketRef ); + +// 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 ); +mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ); + +// Threads + +mDNSlocal mStatus SetupTask( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ); +mDNSlocal void Task( mDNS *inMDNS ); +mDNSlocal mStatus TaskInit( mDNS *inMDNS ); +mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ); +mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ); +mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ); + +// Utilities + +#if ( TARGET_NON_APPLE ) +mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ); +mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ); +#endif + +// Platform Accessors + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo; +struct mDNSPlatformInterfaceInfo +{ + const char * name; + mDNSAddr ip; +}; + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); + +#ifdef __cplusplus +} +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNSlocal mDNS * gMDNSPtr = NULL; +mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; +mDNSlocal mDNSs32 gMDNSTicksToMicrosecondsMultiplier = 0; + +// Platform support + +mDNSs32 mDNSPlatformOneSecond; + +#if 0 +#pragma mark - +#pragma mark == Public APIs == +#endif + +//=========================================================================================================================== +// mDNSReconfigure +//=========================================================================================================================== + +void mDNSReconfigure( void ) +{ + // Send a "reconfigure" command to the MDNS task. + + if( gMDNSPtr ) + { + SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure ); + } +} + +#if 0 +#pragma mark - +#pragma mark == Platform Support == +#endif + +//=========================================================================================================================== +// mDNSPlatformInit +//=========================================================================================================================== + +mStatus mDNSPlatformInit( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "platform init\n" ); + + // Initialize variables. + + mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) ); + inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->commandPipe = ERROR; + inMDNS->p->task = ERROR; + inMDNS->p->rescheduled = 1; // Default to rescheduled until fully initialized. + mDNSPlatformOneSecond = sysClkRateGet(); + gMDNSTicksToMicrosecondsMultiplier = ( 1000000L / mDNSPlatformOneSecond ); + + // Allocate semaphores. + + inMDNS->p->lockID = semMCreate( SEM_Q_FIFO ); + require_action( inMDNS->p->lockID, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->readyEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); + require_action( inMDNS->p->readyEvent, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); + require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); + + gMDNSPtr = inMDNS; + + // Set up the task and wait for it to initialize. Initialization is done from the task instead of here to avoid + // stack space issues. Some of the initialization may require a larger stack than the current task supports. + + err = SetupTask( inMDNS ); + require_noerr( err, exit ); + + err = semTake( inMDNS->p->readyEvent, WAIT_FOREVER ); + require_noerr( err, exit ); + err = inMDNS->p->taskInitErr; + require_noerr( err, exit ); + + mDNSCoreInitComplete( inMDNS, err ); + +exit: + if( err ) + { + mDNSPlatformClose( inMDNS ); + } + dlog( kDebugLevelInfo, DEBUG_NAME "platform init done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformClose +//=========================================================================================================================== + +void mDNSPlatformClose( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "platform close\n" ); + check( inMDNS ); + + // Tear everything down. + + err = TearDownTask( inMDNS ); + check_noerr( err ); + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = TearDownCommandPipe( inMDNS ); + check_noerr( err ); + + gMDNSPtr = NULL; + + // Release semaphores. + + if( inMDNS->p->quitEvent ) + { + semDelete( inMDNS->p->quitEvent ); + inMDNS->p->quitEvent = 0; + } + if( inMDNS->p->readyEvent ) + { + semDelete( inMDNS->p->readyEvent ); + inMDNS->p->readyEvent = 0; + } + if( inMDNS->p->lockID ) + { + semDelete( inMDNS->p->lockID ); + inMDNS->p->lockID = 0; + } + + dlog( kDebugLevelInfo, DEBUG_NAME "platform close done\n" ); +} + +//=========================================================================================================================== +// mDNSPlatformSendUDP +//=========================================================================================================================== + +mStatus +mDNSPlatformSendUDP( + const mDNS * const inMDNS, + const void * const inMsg, + const mDNSu8 * const inMsgEnd, + mDNSInterfaceID inInterfaceID, + const mDNSAddr * inDstIP, + mDNSIPPort inDstPort ) +{ + mStatus err; + MDNSInterfaceItem * item; + struct sockaddr_in addr; + int n; + + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); + + // Check parameters. + + check( inMDNS ); + check( inMsg ); + check( inMsgEnd ); + check( inInterfaceID ); + check( inDstIP ); + if( inDstIP->type != mDNSAddrType_IPv4 ) + { + err = mStatus_BadParamErr; + goto exit; + } + +#if ( DEBUG ) + // Make sure the InterfaceID is valid. + + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + if( item == (MDNSInterfaceItem *) inInterfaceID ) + { + break; + } + } + require_action( item, exit, err = mStatus_NoSuchNameErr ); +#endif + + // Send the packet. + + item = (MDNSInterfaceItem *) inInterfaceID; + check( item->sendingSocketRef != kInvalidSocketRef ); + + mDNSPlatformMemZero( &addr, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inDstPort.NotAnInteger; + addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + + n = inMsgEnd - ( (const mDNSu8 * const) inMsg ); + n = sendto( item->sendingSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( n, errno ); + + item->sendErrorCounter += ( n < 0 ); + item->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger ); + item->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger ); + + dlog( kDebugLevelChatty, DEBUG_NAME "sent (to=%u.%u.%u.%u:%hu)\n", + inDstIP->ip.v4.b[ 0 ], inDstIP->ip.v4.b[ 1 ], inDstIP->ip.v4.b[ 2 ], inDstIP->ip.v4.b[ 3 ], + htons( inDstPort.NotAnInteger ) ); + err = mStatus_NoError; + +exit: + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" ); + 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( inMDNS->p->lockID ); + + if( inMDNS->p->lockID ) + { + #if ( TARGET_NON_APPLE ) + semTake( inMDNS->p->lockID, WAIT_FOREVER ); + #else + semTakeDeadlockDetect( inMDNS->p->lockID, WAIT_FOREVER ); + #endif + } +} + +//=========================================================================================================================== +// mDNSPlatformUnlock +//=========================================================================================================================== + +void mDNSPlatformUnlock( const mDNS * const inMDNS ) +{ + check( inMDNS ); + check( inMDNS->p ); + check( inMDNS->p->lockID ); + check_string( inMDNS->p->task != ERROR, "mDNS task not started" ); + + // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock() + // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to: + // (a) handle immediate work (if any) resulting from this API call + // (b) calculate the next sleep time between now and the next interesting event + + if( ( mDNS_TimeNow(inMDNS) - inMDNS->NextScheduledEvent ) >= 0 ) + { + // We only need to send the reschedule event when called from a task other than the mDNS task since if we are + // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. + + if( ( inMDNS->p->rescheduled++ == 0 ) && ( taskIdSelf() != inMDNS->p->task ) ) + { + SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule ); + } + } + + if( inMDNS->p->lockID ) + { + semGive( inMDNS->p->lockID ); + } +} + +//=========================================================================================================================== +// 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 ); + + 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( -1 ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceNameToID +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) +{ + mStatus err; + MDNSInterfaceItem * ifd; + + check( inMDNS ); + check( inMDNS->p ); + check( inName ); + + // Search for an interface with the specified name, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( strcmp( ifd->name, inName ) == 0 ) + { + break; + } + } + if( !ifd ) + { + err = mStatus_NoSuchNameErr; + goto exit; + } + + // Success! + + if( outID ) + { + *outID = (mDNSInterfaceID) ifd; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDToInfo +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +{ + mStatus err; + MDNSInterfaceItem * ifd; + + check( inMDNS ); + check( inID ); + check( outInfo ); + + // Search for an interface with the specified ID, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ifd == (MDNSInterfaceItem *) inID ) + { + break; + } + } + if( !ifd ) + { + err = mStatus_NoSuchNameErr; + goto exit; + } + + // Success! + + outInfo->name = ifd->name; + outInfo->ip = ifd->hostSet.ip; + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// debugf_ +//=========================================================================================================================== + +#if ( MDNS_DEBUGMSGS ) +mDNSexport void debugf_( const char *format, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, format ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_end( args ); + + dlog( kDebugLevelInfo, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// verbosedebugf_ +//=========================================================================================================================== + +#if ( MDNS_DEBUGMSGS > 1 ) +mDNSexport void verbosedebugf_( const char *format, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, format ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_end( args ); + + dlog( kDebugLevelVerbose, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// LogMsg +//=========================================================================================================================== + +void LogMsg( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelWarning, "%s\n", buffer ); +} + +#if 0 +#pragma mark - +#pragma mark == Platform Internals == +#endif + +//=========================================================================================================================== +// SetupNames +//=========================================================================================================================== + +mDNSlocal void SetupNames( mDNS * const inMDNS ) +{ + char tempCString[ 128 ]; + mDNSu8 tempPString[ 128 ]; + mDNSu8 * namePtr; + + // Set up the host name. + + tempCString[ 0 ] = '\0'; + GenerateUniqueHostName( tempCString, NULL ); + check( tempCString[ 0 ] != '\0' ); + if( tempCString[ 0 ] == '\0' ) + { + // No name so use the default. + + strcpy( tempCString, kMDNSDefaultName ); + } + inMDNS->nicelabel.c[ 0 ] = strlen( tempCString ); + memcpy( &inMDNS->nicelabel.c[ 1 ], tempCString, inMDNS->nicelabel.c[ 0 ] ); + check( inMDNS->nicelabel.c[ 0 ] > 0 ); + + // Set up the DNS name. + + tempCString[ 0 ] = '\0'; + GenerateUniqueDNSName( tempCString, NULL ); + if( tempCString[ 0 ] != '\0' ) + { + tempPString[ 0 ] = strlen( tempCString ); + memcpy( &tempPString[ 1 ], tempCString, tempPString[ 0 ] ); + namePtr = tempPString; + } + else + { + // No DNS name so use the host name. + + namePtr = inMDNS->nicelabel.c; + } + ConvertUTF8PstringToRFC1034HostLabel( namePtr, &inMDNS->hostlabel ); + if( inMDNS->hostlabel.c[ 0 ] == 0 ) + { + // Nice name has no characters that are representable as an RFC 1034 name (e.g. Japanese) so use the default. + + MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); + } + check( inMDNS->hostlabel.c[ 0 ] > 0 ); + + mDNS_SetFQDN( inMDNS ); + + dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); +} + +//=========================================================================================================================== +// SetupInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) +{ + mStatus err; + struct ifaddrs * addrs; + struct ifaddrs * p; + uint32_t flagMask; + uint32_t flagTest; + MDNSInterfaceItem ** next; + MDNSInterfaceItem * item; + + addrs = NULL; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" ); + check( inMDNS ); + + // Tear down any existing interfaces that may be set up. + + TearDownInterfaceList( inMDNS ); + inMDNS->p->interfaceList = NULL; + next = &inMDNS->p->interfaceList; + + // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. + + flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTOPOINT; + flagTest = IFF_UP | IFF_MULTICAST; + + err = getifaddrs( &addrs ); + require_noerr( err, exit ); + + for( p = addrs; p; p = p->ifa_next ) + { + if( ( p->ifa_flags & flagMask ) == flagTest ) + { + err = SetupInterface( inMDNS, p, &item ); + require_noerr( err, exit ); + + *next = item; + next = &item->next; + } + } + err = mStatus_NoError; + +exit: + if( addrs ) + { + freeifaddrs( addrs ); + } + if( err ) + { + TearDownInterfaceList( inMDNS ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) +{ + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); + check( inMDNS ); + + // Tear down all the interfaces. + + while( inMDNS->p->interfaceList ) + { + MDNSInterfaceItem * item; + + item = inMDNS->p->interfaceList; + inMDNS->p->interfaceList = item->next; + + TearDownInterface( inMDNS, item ); + } + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupInterface +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ) +{ + mStatus err; + MDNSInterfaceItem * item; + MDNSSocketRef socketRef; + const struct sockaddr_in * ipv4, *mask; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface (name=%s)\n", inAddr->ifa_name ); + check( inMDNS ); + check( inAddr ); + check( inAddr->ifa_addr ); + ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; + mask = (const struct sockaddr_in *) inAddr->ifa_netmask; + check( outItem ); + + // Allocate memory for the info item. + + item = (MDNSInterfaceItem *) calloc( 1, sizeof( *item ) ); + require_action( item, exit, err = mStatus_NoMemoryErr ); + strcpy( item->name, inAddr->ifa_name ); + item->multicastSocketRef = kInvalidSocketRef; + item->sendingSocketRef = kInvalidSocketRef; + + // Set up the multicast DNS (port 5353) socket for this interface. + + err = SetupSocket( inMDNS, inAddr, MulticastDNSPort, &socketRef ); + require_noerr( err, exit ); + item->multicastSocketRef = socketRef; + + // Set up the sending socket for this interface. + + err = SetupSocket( inMDNS, inAddr, zeroIPPort, &socketRef ); + require_noerr( err, exit ); + item->sendingSocketRef = socketRef; + + // Register this interface with mDNS. + + item->hostSet.InterfaceID = (mDNSInterfaceID) item; + item->hostSet.ip.type = mDNSAddrType_IPv4; + item->hostSet.ip.ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; + item->hostSet.mask.type = mDNSAddrType_IPv4; + item->hostSet.mask.ip.v4.NotAnInteger = mask->sin_addr.s_addr; + item->hostSet.ifname[0] = 0; + item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; + item->hostSet.McastTxRx = mDNStrue; + + err = mDNS_RegisterInterface( inMDNS, &item->hostSet, mDNSfalse ); + require_noerr( err, exit ); + item->hostRegistered = mDNStrue; + + dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n", + item->hostSet.ip.ip.v4.b[ 0 ], item->hostSet.ip.ip.v4.b[ 1 ], + item->hostSet.ip.ip.v4.b[ 2 ], item->hostSet.ip.ip.v4.b[ 3 ] ); + + // Success! + + *outItem = item; + item = NULL; + +exit: + if( item ) + { + TearDownInterface( inMDNS, item ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (name=%s, err=%ld)\n", inAddr->ifa_name, err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterface +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ) +{ + MDNSSocketRef socketRef; + + check( inMDNS ); + check( inItem ); + + // Deregister this interface with mDNS. + + dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n", + inItem->hostSet.ip.ip.v4.b[ 0 ], inItem->hostSet.ip.ip.v4.b[ 1 ], + inItem->hostSet.ip.ip.v4.b[ 2 ], inItem->hostSet.ip.ip.v4.b[ 3 ] ); + + if( inItem->hostRegistered ) + { + inItem->hostRegistered = mDNSfalse; + mDNS_DeregisterInterface( inMDNS, &inItem->hostSet, mDNSfalse ); + } + + // Close the multicast socket. + + socketRef = inItem->multicastSocketRef; + inItem->multicastSocketRef = kInvalidSocketRef; + if( socketRef != kInvalidSocketRef ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef ); + close( socketRef ); + } + + // Close the sending socket. + + socketRef = inItem->sendingSocketRef; + inItem->sendingSocketRef = kInvalidSocketRef; + if( socketRef != kInvalidSocketRef ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down sending socket %d\n", socketRef ); + close( socketRef ); + } + + // Free the memory used by the interface info. + + free( inItem ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus +SetupSocket( + mDNS * const inMDNS, + const struct ifaddrs * inAddr, + mDNSIPPort inPort, + MDNSSocketRef * outSocketRef ) +{ + mStatus err; + MDNSSocketRef socketRef; + int option; + unsigned char optionByte; + struct ip_mreq mreq; + const struct sockaddr_in * ipv4; + struct sockaddr_in addr; + mDNSv4Addr ip; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); + check( inMDNS ); + check( inAddr ); + check( inAddr->ifa_addr ); + ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; + check( outSocketRef ); + + // Set up a UDP socket for multicast DNS. + + socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_errno_action( socketRef, errno, exit, err = mStatus_UnknownErr ); + + // A port of zero means this socket is for sending and should be set up for sending. Otherwise, it is for receiving + // and should be set up for receiving. The reason for separate sending vs receiving sockets is to workaround problems + // with VxWorks IP stack when using dynamic IP configuration such as DHCP (problems binding to wildcard IP when the + // IP address later changes). Since we have to bind the Multicast DNS address to workaround these issues we have to + // use a separate sending socket since it is illegal to send a packet with a multicast source address (RFC 1122). + + if( inPort.NotAnInteger != zeroIPPort.NotAnInteger ) + { + // Turn on reuse port option so multiple servers can listen for Multicast DNS packets. + + option = 1; + err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); + + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + ip.NotAnInteger = ipv4->sin_addr.s_addr; + mreq.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + mreq.imr_interface.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); + check_errno( err, errno ); + + // Bind to the multicast DNS address and port 5353. + + mDNSPlatformMemZero( &addr, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inPort.NotAnInteger; + addr.sin_addr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( err, errno ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%s, %u.%u.%u.%u:%u, %d)\n", + inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef ); + } + else + { + // Bind to the interface address and multicast DNS port. + + ip.NotAnInteger = ipv4->sin_addr.s_addr; + mDNSPlatformMemZero( &addr, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = MulticastDNSPort.NotAnInteger; + addr.sin_addr.s_addr = ip.NotAnInteger; + err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( err, errno ); + + // Direct multicast packets to the specified interface. + + addr.sin_addr.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) ); + check_errno( err, errno ); + + // Set the TTL of outgoing unicast packets to 255 (helps against spoofing). + + option = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); + + // Set the TTL of outgoing multicast packets to 255 (helps against spoofing). + + optionByte = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &optionByte, sizeof( optionByte ) ); + check_errno( err, errno ); + + // WARNING: Setting this option causes unicast responses to be routed to the wrong interface so they are + // WARNING: disabled. These options were only hints to improve 802.11 performance (and not implemented) anyway. + +#if 0 + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to maximize 802.11 multicast rate. + + option = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; + err = setsockopt( socketRef, IPPROTO_IP, IP_TOS, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); +#endif + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up sending socket done (%s, %u.%u.%u.%u, %d)\n", + inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], socketRef ); + } + + // Success! + + *outSocketRef = socketRef; + socketRef = kInvalidSocketRef; + err = mStatus_NoError; + +exit: + if( socketRef != kInvalidSocketRef ) + { + close( socketRef ); + } + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Commands == +#endif + +//=========================================================================================================================== +// SetupCommandPipe +//=========================================================================================================================== + +mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ) +{ + mStatus err; + + // Clean up any leftover command pipe. + + TearDownCommandPipe( inMDNS ); + + // Create the pipe device and open it. + + pipeDevCreate( kMDNSPipeName, kMDNSPipeMessageQueueSize, kMDNSPipeMessageSize ); + + inMDNS->p->commandPipe = open( kMDNSPipeName, O_RDWR, 0 ); + require_errno_action( inMDNS->p->commandPipe, errno, exit, err = mStatus_UnsupportedErr ); + + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TearDownCommandPipe +//=========================================================================================================================== + +mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ) +{ + if( inMDNS->p->commandPipe != ERROR ) + { + close( inMDNS->p->commandPipe ); +#ifdef _WRS_VXWORKS_5_X + // pipeDevDelete is not defined in older versions of VxWorks + pipeDevDelete( kMDNSPipeName, FALSE ); +#endif + inMDNS->p->commandPipe = ERROR; + } + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SendCommand +//=========================================================================================================================== + +mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ) +{ + mStatus err; + + require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); + + err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) ); + require_errno( err, errno, exit ); + + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// ProcessCommand +//=========================================================================================================================== + +mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ) +{ + mStatus err; + MDNSPipeCommandCode commandCode; + + require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); + + // Read the command code from the pipe and dispatch it. + + err = read( inMDNS->p->commandPipe, &commandCode, sizeof( commandCode ) ); + require_errno( err, errno, exit ); + + switch( commandCode ) + { + case kMDNSPipeCommandCodeReschedule: + + // Reschedule event. Do nothing here, but this will cause mDNS_Execute to run before waiting again. + + dlog( kDebugLevelChatty, DEBUG_NAME "reschedule\n" ); + break; + + case kMDNSPipeCommandCodeReconfigure: + ProcessCommandReconfigure( inMDNS ); + break; + + case kMDNSPipeCommandCodeQuit: + + // Quit requested. Set quit flag and bump the config ID to let the thread exit normally. + + dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe quit command\n" ); + inMDNS->p->quit = mDNStrue; + ++inMDNS->p->configID; + break; + + default: + dlog( kDebugLevelError, DEBUG_NAME "unknown pipe command code (code=0x%08X)\n", commandCode ); + err = mStatus_BadParamErr; + goto exit; + break; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// ProcessCommandReconfigure +//=========================================================================================================================== + +mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe reconfigure command\n" ); + + // Tear down the existing interfaces and set up new ones using the new IP info. + + mDNSPlatformLock( inMDNS ); + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = SetupInterfaceList( inMDNS ); + check_noerr( err ); + + mDNSPlatformUnlock( inMDNS ); + + // Inform clients of the change. + + mDNS_ConfigChanged(m); + + // Force mDNS to update. + + mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this + + // Bump the config ID so the main processing loop detects the configuration change. + + ++inMDNS->p->configID; +} + +#if 0 +#pragma mark - +#pragma mark == Threads == +#endif + +//=========================================================================================================================== +// SetupTask +//=========================================================================================================================== + +mDNSlocal mStatus SetupTask( mDNS * const inMDNS ) +{ + mStatus err; + int task; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); + check( inMDNS ); + + // Create our main thread. Note: The task will save off its ID in the globals. We cannot do it here because the + // task invokes code that needs it and the task may begin execution before taskSpawn returns the task ID. + // This also means code in this thread context cannot rely on the task ID until the task has fully initialized. + + task = taskSpawn( kMDNSTaskName, kMDNSTaskPriority, 0, kMDNSTaskStackSize, (FUNCPTR) Task, + (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + require_action( task != ERROR, exit, err = mStatus_NoMemoryErr ); + + err = mStatus_NoError; + +exit: + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld, id=%d)\n", err, task ); + return( err ); +} + +//=========================================================================================================================== +// TearDownTask +//=========================================================================================================================== + +mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread\n" ); + check( inMDNS ); + + // Send a quit command to cause the thread to exit. + + SendCommand( inMDNS, kMDNSPipeCommandCodeQuit ); + + // Wait for the thread to signal it has exited. Timeout in 10 seconds to handle a hung thread. + + if( inMDNS->p->quitEvent ) + { + err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 ); + check_noerr( err ); + } + err = mStatus_NoError; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// Task +//=========================================================================================================================== + +mDNSlocal void Task( mDNS *inMDNS ) +{ + mStatus err; + fd_set allReadSet; + MDNSInterfaceItem * item; + int maxSocket; + long configID; + struct timeval timeout; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task starting\n" ); + check( inMDNS ); + + // Set up everything up. + + err = TaskInit( inMDNS ); + require_noerr( err, exit ); + + // Main Processing Loop. + + while( !inMDNS->p->quit ) + { + // Set up the read set here to avoid the overhead of setting it up each iteration of the main processing loop. + // If the configuration changes, the server ID will be bumped, causing this code to set up the read set again. + + TaskSetupReadSet( inMDNS, &allReadSet, &maxSocket ); + configID = inMDNS->p->configID; + dlog( kDebugLevelVerbose, DEBUG_NAME "task starting processing loop (configID=%ld)\n", configID ); + + while( configID == inMDNS->p->configID ) + { + mDNSs32 nextTaskTime; + fd_set readSet; + int n; + + // Give the mDNS core a chance to do its work. Reset the rescheduled flag before calling mDNS_Execute + // so anything that needs processing during or after causes a re-schedule to wake up the thread. The + // reschedule flag is set to 1 after processing a waking up to prevent redundant reschedules while + // processing packets. This introduces a window for a race condition because the thread wake-up and + // reschedule set are not atomic, but this would be benign. Even if the reschedule flag is "corrupted" + // like this, it would only result in a redundant reschedule since it will loop back to mDNS_Execute. + + inMDNS->p->rescheduled = 0; + nextTaskTime = mDNS_Execute( inMDNS ); + TaskSetupTimeout( inMDNS, nextTaskTime, &timeout ); + + // Wait until something occurs (e.g. command, incoming packet, or timeout). + + readSet = allReadSet; + n = select( maxSocket + 1, &readSet, NULL, NULL, &timeout ); + inMDNS->p->rescheduled = 1; + check_errno( n, errno ); + dlog( kDebugLevelChatty - 1, DEBUG_NAME "task select result = %d\n", n ); + if( n == 0 ) + { + // Next task timeout occurred. Loop back up to give mDNS core a chance to work. + + dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNS_TimeNow(inMDNS) ); + continue; + } + + // Scan the read set to determine if any sockets have something pending and process them. + + n = 0; + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + if( FD_ISSET( item->multicastSocketRef, &readSet ) ) + { + TaskProcessPacket( inMDNS, item, item->multicastSocketRef ); + ++n; + } + } + + // Check for a pending command and process it. + + if( FD_ISSET( inMDNS->p->commandPipe, &readSet ) ) + { + ProcessCommand( inMDNS ); + ++n; + } + check( n > 0 ); + } + } + +exit: + // Signal we've quit. + + check( inMDNS->p->quitEvent ); + semGive( inMDNS->p->quitEvent ); + + dlog( kDebugLevelInfo, DEBUG_NAME "task ended\n" ); +} + +//=========================================================================================================================== +// TaskInit +//=========================================================================================================================== + +mDNSlocal mStatus TaskInit( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task init\n" ); + check( inMDNS->p->readyEvent ); + + inMDNS->p->task = taskIdSelf(); + + err = SetupCommandPipe( inMDNS ); + require_noerr( err, exit ); + + SetupNames( inMDNS ); + + err = SetupInterfaceList( inMDNS ); + require_noerr( err, exit ); + +exit: + // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not). + + inMDNS->p->taskInitErr = err; + semGive( inMDNS->p->readyEvent ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "task init done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TaskSetupReadSet +//=========================================================================================================================== + +mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ) +{ + MDNSInterfaceItem * item; + int maxSocket; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set\n" ); + check( inMDNS ); + check( outReadSet ); + check( outMaxSocket ); + + // Initialize the read set. Default the max socket to -1 so "maxSocket + 1" (as needed by select) is zero. This + // should never happen since we should always have at least one interface, but it's just to be safe. + + FD_ZERO( outReadSet ); + maxSocket = -1; + + // Add all the receiving sockets to the read set. + + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + FD_SET( item->multicastSocketRef, outReadSet ); + if( item->multicastSocketRef > maxSocket ) + { + maxSocket = item->multicastSocketRef; + } + } + + // Add the command pipe to the read set. + + FD_SET( inMDNS->p->commandPipe, outReadSet ); + if( inMDNS->p->commandPipe > maxSocket ) + { + maxSocket = inMDNS->p->commandPipe; + } + check( maxSocket > 0 ); + *outMaxSocket = maxSocket; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set done (maxSocket=%d)\n", maxSocket ); +} + +//=========================================================================================================================== +// TaskSetupTimeout +//=========================================================================================================================== + +mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ) +{ + mDNSs32 delta; + + // Calculate how long to wait before performing idle processing. + + delta = inNextTaskTime - 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 ) * gMDNSTicksToMicrosecondsMultiplier; + + // Check if the microseconds is more than 1 second. If so, bump the seconds instead. + + if( outTimeout->tv_usec >= 1000000L ) + { + outTimeout->tv_sec += 1; + outTimeout->tv_usec = 0; + } + } + + dlog( kDebugLevelChatty, DEBUG_NAME "next task in %ld:%ld seconds (%ld)\n", + outTimeout->tv_sec, outTimeout->tv_usec, inNextTaskTime ); +} +//=========================================================================================================================== +// TaskProcessPacket +//=========================================================================================================================== + +mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ) +{ + int n; + DNSMessage packet; + struct sockaddr_in addr; + int addrSize; + mDNSu8 * packetEndPtr; + mDNSAddr srcAddr; + mDNSIPPort srcPort; + mDNSAddr dstAddr; + mDNSIPPort dstPort; + + // Receive the packet. + + addrSize = sizeof( addr ); + n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); + check( n >= 0 ); + if( n >= 0 ) + { + // Set up the src/dst/interface info. + + srcAddr.type = mDNSAddrType_IPv4; + srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; + srcPort.NotAnInteger = addr.sin_port; + dstAddr.type = mDNSAddrType_IPv4; + dstAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; + dstPort = MulticastDNSPort; + + dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); + dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); + dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%hu\n", + srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ], + ntohs( srcPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%hu\n", + dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ], + ntohs( dstPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " interface = 0x%08X\n", (int) inItem->hostSet.InterfaceID ); + dlog( kDebugLevelChatty, DEBUG_NAME "--\n" ); + + // Dispatch the packet to mDNS. + + packetEndPtr = ( (mDNSu8 *) &packet ) + n; + mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID ); + } + + // Update counters. + + inItem->recvCounter += 1; + inItem->recvErrorCounter += ( n < 0 ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +#if ( TARGET_NON_APPLE ) +//=========================================================================================================================== +// GenerateUniqueHostName +// +// Non-Apple platform stub routine to generate a unique name for the device. Should be implemented to return a unique name. +//=========================================================================================================================== + +mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ) +{ + DEBUG_UNUSED( ioSeed ); + + // $$$ Non-Apple Platforms: Fill in appropriate name for device. + + mDNSPlatformStrCopy( outName, kMDNSDefaultName ); +} + +//=========================================================================================================================== +// GenerateUniqueDNSName +// +// Non-Apple platform stub routine to generate a unique RFC 1034-compatible DNS name for the device. Should be +// implemented to return a unique name. +//=========================================================================================================================== + +mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ) +{ + DEBUG_UNUSED( ioSeed ); + + // $$$ Non-Apple Platforms: Fill in appropriate DNS name for device. + + mDNSPlatformStrCopy( outName, kMDNSDefaultName ); +} +#endif + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// getifaddrs +//=========================================================================================================================== + +int getifaddrs( struct ifaddrs **outAddrs ) +{ + int err; + struct ifaddrs * head; + struct ifaddrs ** next; + struct ifaddrs * ifa; + int i; + struct ifnet * ifp; + char ipString[ INET_ADDR_LEN ]; + int n; + + head = NULL; + next = &head; + + i = 1; + for( ;; ) + { + ifp = ifIndexToIfp( i ); + if( !ifp ) + { + break; + } + ++i; + + // Allocate and initialize the ifaddrs structure and attach it to the linked list. + + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = ENOMEM ); + + *next = ifa; + next = &ifa->ifa_next; + + // Fetch the name. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = ENOMEM ); + + n = sprintf( ifa->ifa_name, "%s%d", ifp->if_name, ifp->if_unit ); + require_action( n < 16, exit, err = ENOBUFS ); + + // Fetch the address. + + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( struct sockaddr_in ) ); + require_action( ifa->ifa_addr, exit, err = ENOMEM ); + + ipString[ 0 ] = '\0'; + #if ( TARGET_NON_APPLE ) + err = ifAddrGet( ifa->ifa_name, ipString ); + require_noerr( err, exit ); + #else + err = ifAddrGetNonAlias( ifa->ifa_name, ipString ); + require_noerr( err, exit ); + #endif + + err = sock_pton( ipString, AF_INET, ifa->ifa_addr, 0, NULL ); + require_noerr( err, exit ); + + // Fetch flags. + + ifa->ifa_flags = ifp->if_flags; + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = 0; + +exit: + if( head ) + { + freeifaddrs( head ); + } + return( err ); +} + +//=========================================================================================================================== +// freeifaddrs +//=========================================================================================================================== + +void freeifaddrs( struct ifaddrs *inAddrs ) +{ + struct ifaddrs * p; + struct ifaddrs * q; + + // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. + + for( p = inAddrs; p; p = q ) + { + q = p->ifa_next; + + if( p->ifa_name ) + { + free( p->ifa_name ); + p->ifa_name = NULL; + } + if( p->ifa_addr ) + { + free( p->ifa_addr ); + p->ifa_addr = NULL; + } + if( p->ifa_netmask ) + { + free( p->ifa_netmask ); + p->ifa_netmask = NULL; + } + if( p->ifa_dstaddr ) + { + free( p->ifa_dstaddr ); + p->ifa_dstaddr = NULL; + } + if( p->ifa_data ) + { + free( p->ifa_data ); + p->ifa_data = NULL; + } + free( p ); + } +} + +//=========================================================================================================================== +// sock_pton +//=========================================================================================================================== + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) +{ + int err; + + if( inFamily == AF_INET ) + { + struct sockaddr_in * ipv4; + + if( inAddrSize == 0 ) + { + inAddrSize = sizeof( struct sockaddr_in ); + } + if( inAddrSize < sizeof( struct sockaddr_in ) ) + { + err = EINVAL; + goto exit; + } + + ipv4 = (struct sockaddr_in *) outAddr; + err = inet_aton( (char *) inString, &ipv4->sin_addr ); + if( err == 0 ) + { + ipv4->sin_family = AF_INET; + if( outAddrSize ) + { + *outAddrSize = sizeof( struct sockaddr_in ); + } + } + } +#if ( defined( AF_INET6 ) ) + else if( inFamily == AF_INET6 ) // $$$ TO DO: Add IPv6 support. + { + err = EAFNOSUPPORT; + goto exit; + } +#endif + else + { + err = EAFNOSUPPORT; + goto exit; + } + +exit: + return( err ); +} + +//=========================================================================================================================== +// sock_ntop +//=========================================================================================================================== + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) +{ + const struct sockaddr * addr; + + addr = (const struct sockaddr *) inAddr; + if( addr->sa_family == AF_INET ) + { + struct sockaddr_in * ipv4; + + if( inAddrSize == 0 ) + { + inAddrSize = sizeof( struct sockaddr_in ); + } + if( inAddrSize < sizeof( struct sockaddr_in ) ) + { + errno = EINVAL; + inBuffer = NULL; + goto exit; + } + if( inBufferSize < 16 ) + { + errno = ENOBUFS; + inBuffer = NULL; + goto exit; + } + + ipv4 = (struct sockaddr_in *) addr; + inet_ntoa_b( ipv4->sin_addr, inBuffer ); + } +#if ( defined( AF_INET6 ) ) + else if( addr->sa_family == AF_INET6 ) // $$$ TO DO: Add IPv6 support. + { + errno = EAFNOSUPPORT; + inBuffer = NULL; + goto exit; + } +#endif + else + { + errno = EAFNOSUPPORT; + inBuffer = NULL; + goto exit; + } + +exit: + return( inBuffer ); +} + +#if 0 +#pragma mark - +#pragma mark == Debugging == +#endif + +#if ( DEBUG ) + +void mDNSShow( BOOL inShowRecords ); +void mDNSShowRecords( void ); +void mDNSShowTXT( const void *inTXT, size_t inTXTSize ); + +//=========================================================================================================================== +// mDNSShow +//=========================================================================================================================== + +void mDNSShow( BOOL inShowRecords ) +{ + MDNSInterfaceItem * item; + mDNSAddr ip; + int n; + + if( !gMDNSPtr ) + { + printf( "### mDNS not initialized\n" ); + return; + } + + // Globals + + printf( "\n-- mDNS globals --\n" ); + printf( " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) ); + printf( " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) ); + printf( " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) ); + printf( " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) ); + printf( " gMDNSPtr = 0x%08lX\n", (unsigned long) gMDNSPtr ); + printf( " gMDNSTicksToMicrosecondsMultiplier = %ld\n", gMDNSTicksToMicrosecondsMultiplier ); + printf( " lockID = 0x%08lX\n", (unsigned long) gMDNSPtr->p->lockID ); + printf( " readyEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->readyEvent ); + printf( " taskInitErr = %ld\n", gMDNSPtr->p->taskInitErr ); + printf( " quitEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->quitEvent ); + printf( " commandPipe = %d\n", gMDNSPtr->p->commandPipe ); + printf( " task = 0x%08lX\n", (unsigned long) gMDNSPtr->p->task ); + printf( " quit = %d\n", gMDNSPtr->p->quit ); + printf( " configID = %ld\n", gMDNSPtr->p->configID ); + printf( " rescheduled = %d\n", gMDNSPtr->p->rescheduled ); + printf( " nicelabel = \"%.*s\"\n", gMDNSPtr->nicelabel.c[ 0 ], (char *) &gMDNSPtr->nicelabel.c[ 1 ] ); + printf( " hostLabel = \"%.*s\"\n", gMDNSPtr->hostlabel.c[ 0 ], (char *) &gMDNSPtr->hostlabel.c[ 1 ] ); + printf( "\n"); + + // Interfaces + + printf( "\n-- mDNS interfaces --\n" ); + n = 1; + for( item = gMDNSPtr->p->interfaceList; item; item = item->next ) + { + printf( " -- interface %u --\n", n ); + printf( " name = \"%s\"\n", item->name ); + printf( " multicastSocketRef = %d\n", item->multicastSocketRef ); + printf( " sendingSocketRef = %d\n", item->sendingSocketRef ); + ip = item->hostSet.ip; + printf( " hostSet.ip = %u.%u.%u.%u\n", ip.ip.v4.b[ 0 ], ip.ip.v4.b[ 1 ], + ip.ip.v4.b[ 2 ], ip.ip.v4.b[ 3 ] ); + printf( " hostSet.advertise = %s\n", item->hostSet.Advertise ? "YES" : "NO" ); + printf( " hostRegistered = %s\n", item->hostRegistered ? "YES" : "NO" ); + printf( " --\n" ); + printf( " sendMulticastCounter = %d\n", item->sendMulticastCounter ); + printf( " sendUnicastCounter = %d\n", item->sendUnicastCounter ); + printf( " sendErrorCounter = %d\n", item->sendErrorCounter ); + printf( " recvCounter = %d\n", item->recvCounter ); + printf( " recvErrorCounter = %d\n", item->recvErrorCounter ); + printf( " recvLoopCounter = %d\n", item->recvLoopCounter ); + printf( "\n" ); + ++n; + } + + // Resource Records + + if( inShowRecords ) + { + mDNSShowRecords(); + } +} + +//=========================================================================================================================== +// mDNSShowRecords +//=========================================================================================================================== + +void mDNSShowRecords( void ) +{ + MDNSInterfaceItem * item; + int n; + AuthRecord * record; + char name[ MAX_ESCAPED_DOMAIN_NAME ]; + + printf( "\n-- mDNS resource records --\n" ); + n = 1; + for( record = gMDNSPtr->ResourceRecords; record; record = record->next ) + { + item = (MDNSInterfaceItem *) record->resrec.InterfaceID; + ConvertDomainNameToCString( &record->resrec.name, name ); + printf( " -- record %d --\n", n ); + printf( " interface = 0x%08X (%s)\n", (int) item, item ? item->name : "" ); + printf( " name = \"%s\"\n", name ); + printf( "\n" ); + ++n; + } + printf( "\n"); +} + +//=========================================================================================================================== +// mDNSShowTXT +//=========================================================================================================================== + +void mDNSShowTXT( const void *inTXT, size_t inTXTSize ) +{ + const mDNSu8 * p; + const mDNSu8 * end; + int i; + mDNSu8 size; + + printf( "\nTXT record (%u bytes):\n\n", inTXTSize ); + + p = (const mDNSu8 *) inTXT; + end = p + inTXTSize; + i = 0; + + while( p < end ) + { + size = *p++; + if( ( p + size ) > end ) + { + printf( "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" ); + break; + } + printf( "%2d (%3d bytes): \"%.*s\"\n", i, size, size, p ); + p += size; + ++i; + } + printf( "\n" ); +} +#endif // DEBUG diff --git a/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.h b/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.h new file mode 100644 index 00000000..ecde894a --- /dev/null +++ b/mDNSResponder/mDNSVxWorks/mDNSVxWorksIPv4Only.h @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 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. + + Contains: mDNS platform plugin for VxWorks. + + Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved. + + */ + +#ifndef __MDNS_VXWORKS__ +#define __MDNS_VXWORKS__ + +#include "vxWorks.h" +#include "semLib.h" + +#include "mDNSEmbeddedAPI.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Forward Declarations + +typedef struct MDNSInterfaceItem MDNSInterfaceItem; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNS_PlatformSupport_struct + + @abstract Structure containing platform-specific data. + */ + +struct mDNS_PlatformSupport_struct +{ + SEM_ID lockID; + SEM_ID readyEvent; + mStatus taskInitErr; + SEM_ID quitEvent; + MDNSInterfaceItem * interfaceList; + int commandPipe; + int task; + mDNSBool quit; + long configID; + int rescheduled; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function mDNSReconfigure + + @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc. + + @discussion + + VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines + provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces. + */ + +void mDNSReconfigure( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct ifaddrs + + @abstract Interface information + */ + +struct ifaddrs +{ + struct ifaddrs * ifa_next; + char * ifa_name; + u_int ifa_flags; + struct sockaddr * ifa_addr; + struct sockaddr * ifa_netmask; + struct sockaddr * ifa_dstaddr; + void * ifa_data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function getifaddrs + + @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful. + */ + +int getifaddrs( struct ifaddrs **outAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function freeifaddrs + + @abstract Frees a linked list of interfaces built with getifaddrs. + */ + +void freeifaddrs( struct ifaddrs *inAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_pton + + @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure. + + @result 0 if successful or an error code on failure. + */ + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_ntop + + @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string. + + @result Ptr to 'p'resentation address string buffer if successful or NULL on failure. + */ + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ); + +#ifdef __cplusplus +} +#endif + +#endif // __MDNS_VXWORKS__ -- cgit v1.2.3