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