| /* -*- 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 |