#if( defined( _MSC_VER ) )
#pragma warning( disable:4127 ) #endif
#if( !defined( WIN32_LEAN_AND_MEAN ) )
#define WIN32_LEAN_AND_MEAN #endif
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h.
#include <process.h>
#endif
#if( DEBUG )
#define mDNSlocal
#endif
#include "mDNSClientAPI.h"
#include "mDNSPlatformFunctions.h"
#include "mDNSWin32.h"
#if 0
#pragma mark == Constants ==
#endif
#define DEBUG_NAME "[mDNS] "
#if( !defined( MDNS_DEBUG_SIGNATURE ) )
#define MDNS_DEBUG_SIGNATURE "mDNS"
#endif
#define kMDNSDefaultName "My Computer"
#define kWinSockMajorMin 2
#define kWinSockMinorMin 2
#define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 )
#define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 )
#define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 )
#define kWaitListFixedItemCount 3
#if 0
#pragma mark == Macros - Debug ==
#endif
#define MDNS_UNUSED( X ) (void)( X )
#define kDebugLevelMask 0x0000FFFFL
#define kDebugLevelChatty 100L
#define kDebugLevelVerbose 500L
#define kDebugLevelTrace 800L
#define kDebugLevelInfo 1000L
#define kDebugLevelRareInfo 2000L
#define kDebugLevelNotice 3000L
#define kDebugLevelWarning 4000L
#define kDebugLevelAllowedError 5000L
#define kDebugLevelAssert 6000L
#define kDebugLevelRequire 7000L
#define kDebugLevelError 8000L
#define kDebugLevelCritical 9000L
#define kDebugLevelCriticalError kDebugLevelCritical // DEPRECATED
#define kDebugLevelAlert 10000L
#define kDebugLevelEmergency 11000L
#define kDebugLevelTragic 12000L
#define kDebugLevelAny 0x0000FFFFL
#if( defined( __MWERKS__ ) || defined( __GNUC__ ) )
#define __ROUTINE__ __FUNCTION__
#else
#define __ROUTINE__ NULL
#endif
#if( MDNS_DEBUGMSGS )
#define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \
mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, 0, ( ASSERT_STRING ), NULL, ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) )
#define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \
mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, ( ERR ), ( ASSERT_STRING ), ( ERROR_STRING ), \
( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) )
#define dlog mDNSPlatformDebugLog
#else
#define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION )
#define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION )
#define dlog while( 0 )
#endif
#define check( X ) \
do { \
if( !( X ) ) { \
debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
} \
} while( 0 )
#define check_noerr( ERR ) \
do { \
if( ( ERR ) != 0 ) { \
debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
} \
} while( 0 )
#define check_errno( ERR, ERRNO ) \
do { \
int localErr; \
\
localErr = (int)( ERR ); \
if( localErr < 0 ) { \
int localErrno; \
\
localErrno = ( ERRNO ); \
localErr = ( localErrno != 0 ) ? localErrno : localErr; \
debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
} \
} while( 0 )
#define require( X, LABEL ) \
do { \
if( !( X ) ) { \
debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
goto LABEL; \
} \
} while( 0 )
#define require_quiet( X, LABEL ) \
do { \
if( !( X ) ) { \
goto LABEL; \
} \
} while( 0 )
#define require_action( X, LABEL, ACTION ) \
do { \
if( !( X ) ) { \
debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
{ ACTION; } \
goto LABEL; \
} \
} while( 0 )
#define require_action_quiet( X, LABEL, ACTION ) \
do { \
if( !( X ) ) { \
{ ACTION; } \
goto LABEL; \
} \
} while( 0 )
#define require_noerr( ERR, LABEL ) \
do { \
if( ( ERR ) != 0 ) { \
debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
goto LABEL; \
} \
} while( 0 )
#define require_noerr_quiet( ERR, LABEL ) \
do { \
if( ( ERR ) != 0 ) { \
goto LABEL; \
} \
} while( 0 )
#define require_errno( ERR, ERRNO, LABEL ) \
do { \
int localErr; \
\
localErr = (int)( ERR ); \
if( localErr < 0 ) { \
int localErrno; \
\
localErrno = ( ERRNO ); \
localErr = ( localErrno != 0 ) ? localErrno : localErr; \
debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
goto LABEL; \
} \
} while( 0 )
#define require_errno_action( ERR, ERRNO, LABEL, ACTION ) \
do { \
int localErr; \
\
localErr = (int)( ERR ); \
if( localErr < 0 ) { \
int localErrno; \
\
localErrno = ( ERRNO ); \
localErr = ( localErrno != 0 ) ? localErrno : localErr; \
debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
{ ACTION; } \
goto LABEL; \
} \
} while( 0 )
#if 0
#pragma mark == Macros - General ==
#endif
#define kInvalidSocketRef INVALID_SOCKET
#define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET )
#define close_compat( X ) closesocket( X )
#define errno_compat() WSAGetLastError()
#if( defined( _WIN32_WCE ) )
#define _beginthreadex( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \
CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \
(LPDWORD) THREAD_ID_PTR )
#define _endthreadex( RESULT )
#endif
#if 0
#pragma mark == Prototypes ==
#endif
#if( MDNS_DEBUGMSGS )
mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... );
mDNSlocal void
mDNSPlatformPrintAssert(
const char * inSignature,
long inError,
const char * inAssertionString,
const char * inErrorString,
const char * inFileName,
unsigned long inLineNumber,
const char * inFunction );
#endif
mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS );
mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS );
mDNSlocal mStatus SetupName( mDNS * const inMDNS );
mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS );
mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS );
mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD );
mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD );
mDNSlocal mStatus SetupSocket( mDNS * const inMDNS,
const struct sockaddr_in * inAddress,
mDNSIPPort inPort,
SocketRef * outSocketRef );
mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS );
mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS );
mDNSlocal mStatus SetupThread( mDNS * const inMDNS );
mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS );
mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam );
mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS );
mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount );
mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef );
mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS );
#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
mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
mDNSs32 mDNSPlatformOneSecond = 0;
#if( MDNS_DEBUGMSGS )
mDNSlocal unsigned long gDebugLevel = kDebugLevelInfo + 1;
#endif
#if 0
#pragma mark -
#pragma mark == Platform Support APIs ==
#endif
mStatus mDNSPlatformInit( mDNS * const inMDNS )
{
mStatus err;
WSADATA wsaData;
int supported;
dlog( kDebugLevelVerbose, DEBUG_NAME "platform init\n" );
memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) );
inMDNS->p = &gMDNSPlatformSupport;
inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef;
mDNSPlatformOneSecond = 1000;
err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData );
require_noerr( err, exit );
supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) );
require_action( supported, exit, err = mStatus_UnsupportedErr );
err = SetupSynchronizationObjects( inMDNS );
require_noerr( err, exit );
err = SetupThread( inMDNS );
require_noerr( err, exit );
mDNSCoreInitComplete( inMDNS, err );
exit:
if( err )
{
mDNSPlatformClose( inMDNS );
}
dlog( kDebugLevelVerbose, DEBUG_NAME "platform init done (err=%ld)\n", err );
return( err );
}
void mDNSPlatformClose( mDNS * const inMDNS )
{
mStatus err;
dlog( kDebugLevelVerbose, DEBUG_NAME "platform close\n" );
check( inMDNS );
err = TearDownThread( inMDNS );
check_noerr( err );
err = TearDownInterfaceList( inMDNS );
check_noerr( err );
err = TearDownSynchronizationObjects( inMDNS );
check_noerr( err );
WSACleanup();
dlog( kDebugLevelVerbose, DEBUG_NAME "platform close done (err=%ld)\n", err );
}
mStatus
mDNSPlatformSendUDP(
const mDNS * const inMDNS,
const DNSMessage * const inMsg,
const mDNSu8 * const inMsgEnd,
mDNSInterfaceID inInterfaceID,
mDNSIPPort inSrcPort,
const mDNSAddr * inDstIP,
mDNSIPPort inDstPort )
{
mStatus err;
mDNSInterfaceData * ifd;
struct sockaddr_in addr;
int n;
MDNS_UNUSED( inSrcPort );
dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" );
check( inMDNS );
check( inMsg );
check( inMsgEnd );
check( inInterfaceID );
check( inDstIP );
if( inDstIP->type != mDNSAddrType_IPv4 )
{
err = mStatus_BadParamErr;
goto exit;
}
ifd = (mDNSInterfaceData *) inInterfaceID;
check( IsValidSocket( ifd->multicastSocketRef ) );
addr.sin_family = AF_INET;
addr.sin_port = inDstPort.NotAnInteger;
addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger;
n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) );
n = sendto( ifd->multicastSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
check_errno( n, errno_compat() );
ifd->sendErrorCounter += ( n < 0 );
ifd->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger );
ifd->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger );
err = mStatus_NoError;
exit:
dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" );
return( err );
}
void mDNSPlatformLock( const mDNS * const inMDNS )
{
EnterCriticalSection( &inMDNS->p->lock );
}
void mDNSPlatformUnlock( const mDNS * const inMDNS )
{
check( inMDNS );
check( inMDNS->p );
check( inMDNS->p->threadID );
if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 )
{
if( GetCurrentThreadId() != inMDNS->p->threadID )
{
BOOL wasSet;
wasSet = SetEvent( inMDNS->p->wakeupEvent );
check( wasSet );
}
}
LeaveCriticalSection( &inMDNS->p->lock );
}
mDNSu32 mDNSPlatformStrLen( const void *inSrc )
{
check( inSrc );
return( (mDNSu32) strlen( (const char *) inSrc ) );
}
void mDNSPlatformStrCopy( const void *inSrc, void *inDst )
{
check( inSrc );
check( inDst );
strcpy( (char *) inDst, (const char*) inSrc );
}
void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize )
{
check( inSrc );
check( inDst );
memcpy( inDst, inSrc, inSize );
}
mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize )
{
check( inSrc );
check( inDst );
return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) );
}
void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
{
check( inDst );
memset( inDst, 0, inSize );
}
mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize )
{
void * mem;
check( inSize > 0 );
mem = malloc( inSize );
check( mem );
return( mem );
}
mDNSexport void mDNSPlatformMemFree( void *inMem )
{
check( inMem );
free( inMem );
}
mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow )
{
check( outTimeNow );
*outTimeNow = mDNSPlatformTimeNow();
return( mStatus_NoError );
}
mDNSs32 mDNSPlatformTimeNow( void )
{
return( (mDNSs32) GetTickCount() );
}
mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
{
mStatus err;
mDNSInterfaceData * ifd;
check( inMDNS );
check( inMDNS->p );
check( inName );
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( strcmp( ifd->name, inName ) == 0 )
{
break;
}
}
require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
if( outID )
{
*outID = (mDNSInterfaceID) ifd;
}
err = mStatus_NoError;
exit:
return( err );
}
mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
{
mStatus err;
mDNSInterfaceData * ifd;
check( inMDNS );
check( inID );
check( outInfo );
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
if( ifd == (mDNSInterfaceData *) inID )
{
break;
}
}
require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
outInfo->name = ifd->name;
outInfo->ip = ifd->hostSet.ip;
err = mStatus_NoError;
exit:
return( err );
}
#if 0
#pragma mark -
#endif
#if( MDNS_DEBUGMSGS )
void debugf_( 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( kDebugLevelInfo, "%s\n", buffer );
}
#endif
#if( MDNS_DEBUGMSGS > 1 )
void verbosedebugf_( 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( kDebugLevelVerbose, "%s\n", buffer );
}
#endif
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( MDNS_DEBUGMSGS )
mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... )
{
if( inLevel >= gDebugLevel )
{
va_list args;
va_start( args, inFormat );
vfprintf( stderr, inFormat, args );
fflush( stderr );
va_end( args );
}
}
mDNSlocal void
mDNSPlatformPrintAssert(
const char * inSignature,
long inError,
const char * inAssertionString,
const char * inErrorString,
const char * inFileName,
unsigned long inLineNumber,
const char * inFunction )
{
char * dataPtr;
char buffer[ 512 ];
char tempSignatureChar;
if( !inSignature )
{
tempSignatureChar = '\0';
inSignature = &tempSignatureChar;
}
dataPtr = buffer;
dataPtr += sprintf( dataPtr, "\n" );
if( inError != 0 )
{
dataPtr += sprintf( dataPtr, "[%s] Error: %ld\n", inSignature, inError );
}
else
{
dataPtr += sprintf( dataPtr, "[%s] Assertion failed", inSignature );
if( inAssertionString )
{
dataPtr += sprintf( dataPtr, ": %s", inAssertionString );
}
dataPtr += sprintf( dataPtr, "\n" );
}
if( inErrorString )
{
dataPtr += sprintf( dataPtr, "[%s] %s\n", inSignature, inErrorString );
}
if( inFileName )
{
dataPtr += sprintf( dataPtr, "[%s] file: \"%s\"\n", inSignature, inFileName );
}
if( inLineNumber )
{
dataPtr += sprintf( dataPtr, "[%s] line: %ld\n", inSignature, inLineNumber );
}
if( inFunction )
{
dataPtr += sprintf( dataPtr, "[%s] function: \"%s\"\n", inSignature, inFunction );
}
dataPtr += sprintf( dataPtr, "\n" );
fprintf( stderr, "%s", buffer );
fflush( stderr );
}
#endif // MDNS_DEBUGMSGS
#if 0
#pragma mark -
#pragma mark == Platform Internals ==
#endif
mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS )
{
mStatus err;
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects\n" );
InitializeCriticalSection( &inMDNS->p->lock );
inMDNS->p->lockInitialized = mDNStrue;
inMDNS->p->cancelEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( inMDNS->p->cancelEvent, exit, err = mStatus_NoMemoryErr );
inMDNS->p->quitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr );
inMDNS->p->interfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( inMDNS->p->interfaceListChangedEvent, exit, err = mStatus_NoMemoryErr );
inMDNS->p->wakeupEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( inMDNS->p->wakeupEvent, exit, err = mStatus_NoMemoryErr );
err = mStatus_NoError;
exit:
if( err )
{
TearDownSynchronizationObjects( inMDNS );
}
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects done (err=%ld)\n", err );
return( err );
}
mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS )
{
mStatus err;
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects\n" );
if( inMDNS->p->quitEvent )
{
CloseHandle( inMDNS->p->quitEvent );
inMDNS->p->quitEvent = 0;
}
if( inMDNS->p->cancelEvent )
{
CloseHandle( inMDNS->p->cancelEvent );
inMDNS->p->cancelEvent = 0;
}
if( inMDNS->p->interfaceListChangedEvent )
{
CloseHandle( inMDNS->p->interfaceListChangedEvent );
inMDNS->p->interfaceListChangedEvent = 0;
}
if( inMDNS->p->wakeupEvent )
{
CloseHandle( inMDNS->p->wakeupEvent );
inMDNS->p->wakeupEvent = 0;
}
if( inMDNS->p->lockInitialized )
{
DeleteCriticalSection( &inMDNS->p->lock );
inMDNS->p->lockInitialized = mDNSfalse;
}
err = mStatus_NoError;
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects done (err=%ld)\n", err );
return( err );
}
mDNSlocal mStatus SetupName( mDNS * const inMDNS )
{
mStatus err;
char tempString[ 256 ];
check( inMDNS );
tempString[ 0 ] = '\0';
err = gethostname( tempString, sizeof( tempString ) - 1 );
check_errno( err, errno_compat() );
if( err || ( tempString[ 0 ] == '\0' ) )
{
strcpy( tempString, kMDNSDefaultName );
}
tempString[ sizeof( tempString ) - 1 ] = '\0';
inMDNS->nicelabel.c[ 0 ] = (mDNSu8) strlen( tempString );
memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] );
ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.c, &inMDNS->hostlabel );
if( inMDNS->hostlabel.c[ 0 ] == 0 )
{
MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
}
check( inMDNS->nicelabel.c[ 0 ] != 0 );
check( inMDNS->hostlabel.c[ 0 ] != 0 );
mDNS_GenerateFQDN( 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 ] );
return( err );
}
mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS )
{
mStatus err;
mDNSInterfaceData ** next;
mDNSInterfaceData * ifd;
struct ifaddrs * addrs;
struct ifaddrs * p;
struct ifaddrs * loopback;
u_int flagMask;
u_int flagTest;
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" );
check( inMDNS );
check( inMDNS->p );
addrs = NULL;
TearDownInterfaceList( inMDNS );
err = SetupName( inMDNS );
check_noerr( err );
err = SetupNotifications( inMDNS );
check_noerr( err );
flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTTOPOINT;
flagTest = IFF_UP | IFF_MULTICAST;
loopback = NULL;
next = &inMDNS->p->interfaceList;
err = getifaddrs( &addrs );
require_noerr( err, exit );
for( p = addrs; p; p = p->ifa_next )
{
if( ( p->ifa_flags & flagMask ) == flagTest )
{
if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) ) {
continue;
}
err = SetupInterface( inMDNS, (struct sockaddr_in *) p->ifa_addr, &ifd );
require_noerr( err, exit );
strcpy( ifd->name, p->ifa_name );
*next = ifd;
next = &ifd->next;
++inMDNS->p->interfaceCount;
}
}
exit:
if( err )
{
TearDownInterfaceList( inMDNS );
}
if( addrs )
{
freeifaddrs( addrs );
}
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err );
return( err );
}
mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS )
{
mStatus err;
mDNSInterfaceData * ifd;
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" );
check( inMDNS );
check( inMDNS->p );
err = TearDownNotifications( inMDNS );
check_noerr( err );
while( inMDNS->p->interfaceList )
{
ifd = inMDNS->p->interfaceList;
inMDNS->p->interfaceList = ifd->next;
TearDownInterface( inMDNS, ifd );
}
inMDNS->p->interfaceCount = 0;
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" );
return( mStatus_NoError );
}
mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD )
{
mStatus err;
mDNSInterfaceData * ifd;
SocketRef socketRef;
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface\n" );
check( inMDNS );
check( inMDNS->p );
check( inAddress );
check( outIFD );
ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
require_action( ifd, exit, err = mStatus_NoMemoryErr );
ifd->multicastSocketRef = kInvalidSocketRef;
ifd->unicastSocketRef = kInvalidSocketRef;
err = SetupSocket( inMDNS, inAddress, MulticastDNSPort, &socketRef );
require_noerr( err, exit );
ifd->multicastSocketRef = socketRef;
ifd->multicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( ifd->multicastReadPendingEvent, exit, err = mStatus_NoMemoryErr );
err = WSAEventSelect( ifd->multicastSocketRef, ifd->multicastReadPendingEvent, FD_READ );
require_noerr( err, exit );
err = SetupSocket( inMDNS, inAddress, UnicastDNSPort, &socketRef );
require_noerr( err, exit );
ifd->unicastSocketRef = socketRef;
ifd->unicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( ifd->unicastReadPendingEvent, exit, err = mStatus_NoMemoryErr );
err = WSAEventSelect( ifd->unicastSocketRef, ifd->unicastReadPendingEvent, FD_READ );
require_noerr( err, exit );
ifd->hostSet.InterfaceID = (mDNSInterfaceID) ifd;
ifd->hostSet.ip.type = mDNSAddrType_IPv4;
ifd->hostSet.ip.ip.v4.NotAnInteger = inAddress->sin_addr.s_addr;
ifd->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses;
err = mDNS_RegisterInterface( inMDNS, &ifd->hostSet );
require_noerr( err, exit );
ifd->hostRegistered = mDNStrue;
dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n",
ifd->hostSet.ip.ip.v4.b[ 0 ],
ifd->hostSet.ip.ip.v4.b[ 1 ],
ifd->hostSet.ip.ip.v4.b[ 2 ],
ifd->hostSet.ip.ip.v4.b[ 3 ] );
*outIFD = ifd;
ifd = NULL;
exit:
if( ifd )
{
TearDownInterface( inMDNS, ifd );
}
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (err=%ld)\n", err );
return( err );
}
mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD )
{
SocketRef socketRef;
check( inMDNS );
check( inIFD );
dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n",
inIFD->hostSet.ip.ip.v4.b[ 0 ],
inIFD->hostSet.ip.ip.v4.b[ 1 ],
inIFD->hostSet.ip.ip.v4.b[ 2 ],
inIFD->hostSet.ip.ip.v4.b[ 3 ] );
if( inIFD->hostRegistered )
{
inIFD->hostRegistered = mDNSfalse;
mDNS_DeregisterInterface( inMDNS, &inIFD->hostSet );
}
if( inIFD->multicastReadPendingEvent )
{
CloseHandle( inIFD->multicastReadPendingEvent );
inIFD->multicastReadPendingEvent = 0;
}
socketRef = inIFD->multicastSocketRef;
inIFD->multicastSocketRef = kInvalidSocketRef;
if( IsValidSocket( socketRef ) )
{
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef );
close_compat( socketRef );
}
if( inIFD->unicastReadPendingEvent )
{
CloseHandle( inIFD->unicastReadPendingEvent );
inIFD->unicastReadPendingEvent = 0;
}
socketRef = inIFD->unicastSocketRef;
inIFD->unicastSocketRef = kInvalidSocketRef;
if( IsValidSocket( socketRef ) )
{
dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef );
close_compat( socketRef );
}
free( inIFD );
return( mStatus_NoError );
}
mDNSlocal mStatus
SetupSocket(
mDNS * const inMDNS,
const struct sockaddr_in * inAddress,
mDNSIPPort inPort,
SocketRef * outSocketRef )
{
mStatus err;
SocketRef socketRef;
int option;
struct ip_mreq mreq;
struct sockaddr_in addr;
mDNSv4Addr ip;
MDNS_UNUSED( inMDNS );
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" );
check( inMDNS );
check( outSocketRef );
socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr );
option = 1;
err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) );
check_errno( err, errno_compat() );
ip.NotAnInteger = inAddress->sin_addr.s_addr;
memset( &addr, 0, sizeof( addr ) );
addr.sin_family = AF_INET;
addr.sin_port = inPort.NotAnInteger;
addr.sin_addr.s_addr = ip.NotAnInteger;
err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) );
if( err && ( inPort.NotAnInteger == UnicastDNSPort.NotAnInteger ) )
{
err = 0;
}
check_errno( err, errno_compat() );
if( inPort.NotAnInteger == MulticastDNSPort.NotAnInteger )
{
mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
mreq.imr_interface.s_addr = ip.NotAnInteger;
err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) );
check_errno( err, errno_compat() );
}
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_compat() );
option = 255;
err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) );
check_errno( err, errno_compat() );
option = 255;
err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) );
check_errno( err, errno_compat() );
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%u.%u.%u.%u:%u, %d)\n",
ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef );
*outSocketRef = socketRef;
socketRef = kInvalidSocketRef;
err = mStatus_NoError;
exit:
if( IsValidSocket( socketRef ) )
{
close_compat( socketRef );
}
return( err );
}
mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS )
{
mStatus err;
SocketRef socketRef;
unsigned long param;
int inBuffer;
int outBuffer;
DWORD outSize;
socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr );
inMDNS->p->interfaceListChangedSocketRef = socketRef;
param = 1;
err = ioctlsocket( socketRef, FIONBIO, ¶m );
require_errno( err, errno_compat(), exit );
inBuffer = 0;
outBuffer = 0;
err = WSAIoctl( socketRef, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
if( err < 0 )
{
check( errno_compat() == WSAEWOULDBLOCK );
}
err = WSAEventSelect( socketRef, inMDNS->p->interfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE );
require_errno( err, errno_compat(), exit );
exit:
if( err )
{
TearDownNotifications( inMDNS );
}
return( err );
}
mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS )
{
SocketRef socketRef;
socketRef = inMDNS->p->interfaceListChangedSocketRef;
inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef;
if( IsValidSocket( socketRef ) )
{
close_compat( socketRef );
}
return( mStatus_NoError );
}
#if 0
#pragma mark -
#endif
mDNSlocal mStatus SetupThread( mDNS * const inMDNS )
{
mStatus err;
HANDLE threadHandle;
unsigned threadID;
DWORD result;
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" );
inMDNS->p->initEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr );
inMDNS->p->initStatus = mStatus_Invalid;
threadHandle = (HANDLE) _beginthreadex( NULL, 0, ProcessingThread, inMDNS, 0, &threadID );
require_action( threadHandle, exit, err = mStatus_NoMemoryErr );
result = WaitForSingleObject( inMDNS->p->initEvent, INFINITE );
require_action( result == WAIT_OBJECT_0, exit, err = mStatus_UnknownErr );
err = inMDNS->p->initStatus;
require_noerr( err, exit );
exit:
if( inMDNS->p->initEvent )
{
CloseHandle( inMDNS->p->initEvent );
inMDNS->p->initEvent = 0;
}
dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld)\n", err );
return( err );
}
mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS )
{
DWORD result;
if( inMDNS->p->cancelEvent )
{
BOOL wasSet;
wasSet = SetEvent( inMDNS->p->cancelEvent );
check( wasSet );
if( inMDNS->p->quitEvent )
{
result = WaitForSingleObject( inMDNS->p->quitEvent, 5 * 1000 );
check( result == WAIT_OBJECT_0 );
}
}
return( mStatus_NoError );
}
mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam )
{
mDNS * m;
int done;
mStatus err;
HANDLE * waitList;
int waitListCount;
DWORD result;
BOOL wasSet;
check( inParam );
m = (mDNS *) inParam;
err = ProcessingThreadInitialize( m );
require_noerr( err, exit );
done = 0;
while( !done )
{
waitList = NULL;
waitListCount = 0;
err = ProcessingThreadSetupWaitList( m, &waitList, &waitListCount );
require_noerr( err, exit );
for( ;; )
{
mDNSs32 interval = mDNS_Execute(m) - mDNSPlatformTimeNow();
if (interval < 0) interval = 0;
else if (interval > (0x7FFFFFFF / 1000)) interval = 0x7FFFFFFF / mDNSPlatformOneSecond;
else interval = (interval * 1000) / mDNSPlatformOneSecond;
result = WaitForMultipleObjects( (DWORD) waitListCount, waitList, FALSE, (DWORD) interval );
if( result == WAIT_TIMEOUT )
{
dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" );
continue;
}
else if( result == kWaitListCancelEvent )
{
dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" );
done = 1;
break;
}
else if( result == kWaitListInterfaceListChangedEvent )
{
ProcessingThreadInterfaceListChanged( m );
break;
}
else if( result == kWaitListWakeupEvent )
{
dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup\n" );
continue;
}
else
{
int waitItemIndex;
waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 );
dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex );
check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) );
if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) )
{
HANDLE signaledObject;
int n;
mDNSInterfaceData * ifd;
signaledObject = waitList[ waitItemIndex ];
n = 0;
for( ifd = m->p->interfaceList; ifd; ifd = ifd->next )
{
if( ifd->multicastReadPendingEvent == signaledObject )
{
ProcessingThreadProcessPacket( m, ifd, ifd->multicastSocketRef );
++n;
}
if( ifd->unicastReadPendingEvent == signaledObject )
{
ProcessingThreadProcessPacket( m, ifd, ifd->unicastSocketRef );
++n;
}
}
check( n > 0 );
}
else
{
dlog( kDebugLevelAllowedError, DEBUG_NAME "unexpected wait result (result=0x%08X)\n", result );
}
}
}
if( waitList )
{
free( waitList );
waitList = NULL;
waitListCount = 0;
}
}
exit:
wasSet = SetEvent( m->p->quitEvent );
check( wasSet );
_endthreadex( 0 );
return( 0 );
}
mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS )
{
mStatus err;
BOOL wasSet;
inMDNS->p->threadID = GetCurrentThreadId();
err = SetupInterfaceList( inMDNS );
require_noerr( err, exit );
exit:
if( err )
{
TearDownInterfaceList( inMDNS );
}
inMDNS->p->initStatus = err;
wasSet = SetEvent( inMDNS->p->initEvent );
check( wasSet );
return( err );
}
mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount )
{
mStatus err;
int waitListCount;
HANDLE * waitList;
HANDLE * waitItemPtr;
mDNSInterfaceData * ifd;
dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list\n" );
check( inMDNS );
check( inMDNS->p );
check( outWaitList );
check( outWaitListCount );
waitListCount = kWaitListFixedItemCount + ( 2 * inMDNS->p->interfaceCount );
waitList = (HANDLE *) malloc( waitListCount * sizeof( *waitList ) );
require_action( waitList, exit, err = mStatus_NoMemoryErr );
waitItemPtr = waitList;
*waitItemPtr++ = inMDNS->p->cancelEvent;
*waitItemPtr++ = inMDNS->p->interfaceListChangedEvent;
*waitItemPtr++ = inMDNS->p->wakeupEvent;
for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
{
*waitItemPtr++ = ifd->multicastReadPendingEvent;
*waitItemPtr++ = ifd->unicastReadPendingEvent;
}
*outWaitList = waitList;
*outWaitListCount = waitListCount;
waitList = NULL;
err = mStatus_NoError;
exit:
if( waitList )
{
free( waitList );
}
dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list done (err=%ld)\n", err );
return( err );
}
mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef )
{
int n;
mDNSBool isMulticast;
DNSMessage packet;
struct sockaddr_in addr;
int addrSize;
mDNSu8 * packetEndPtr;
mDNSAddr srcAddr;
mDNSIPPort srcPort;
mDNSAddr dstAddr;
mDNSIPPort dstPort;
isMulticast = (mDNSBool)( inSocketRef == inIFD->multicastSocketRef );
addrSize = sizeof( addr );
n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize );
check( n >= 0 );
if( n >= 0 )
{
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 = isMulticast ? AllDNSLinkGroup : inIFD->hostSet.ip.ip.v4;
dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort;
dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n );
dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%u\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:%u\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 = %u.%u.%u.%u\n",
inIFD->hostSet.ip.ip.v4.b[ 0 ], inIFD->hostSet.ip.ip.v4.b[ 1 ],
inIFD->hostSet.ip.ip.v4.b[ 2 ], inIFD->hostSet.ip.ip.v4.b[ 3 ] );
dlog( kDebugLevelChatty, DEBUG_NAME "--\n" );
packetEndPtr = ( (mDNSu8 *) &packet ) + n;
mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->hostSet.InterfaceID, 255 );
}
inIFD->recvMulticastCounter += isMulticast;
inIFD->recvUnicastCounter += !isMulticast;
inIFD->recvErrorCounter += ( n < 0 );
}
mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS )
{
mStatus err;
dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed event\n" );
check( inMDNS );
mDNSPlatformLock( inMDNS );
err = TearDownInterfaceList( inMDNS );
check_noerr( err );
err = SetupInterfaceList( inMDNS );
check_noerr( err );
mDNSPlatformUnlock( inMDNS );
if( inMDNS->MainCallback )
{
inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged );
}
mDNSCoreMachineSleep( inMDNS, mDNSfalse );
}
#if 0
#pragma mark -
#pragma mark == Utilities ==
#endif
#if( defined( _WIN32_WCE ) )
int getifaddrs( struct ifaddrs **outAddrs )
{
int err;
SocketRef sock;
DWORD size;
void * buffer;
SOCKET_ADDRESS_LIST * addressList;
struct ifaddrs * head;
struct ifaddrs ** next;
struct ifaddrs * ifa;
int n;
int i;
sock = kInvalidSocketRef;
buffer = NULL;
head = NULL;
next = &head;
sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
require_action( IsValidSocket( sock ), exit, err = mStatus_NoMemoryErr );
size = 0;
WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &size, NULL, NULL );
require_action( size > 0, exit, err = -1 );
size *= 2;
buffer = malloc( size );
require_action( buffer, exit, err = -1 );
err = WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, size, &size, NULL, NULL );
require_noerr( err, exit );
addressList = (SOCKET_ADDRESS_LIST *) buffer;
n = addressList->iAddressCount;
if( n == 0 )
{
n = 1;
}
for( i = 0; i < n; ++i )
{
ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
require_action( ifa, exit, err = WSAENOBUFS );
*next = ifa;
next = &ifa->ifa_next;
ifa->ifa_name = (char *) malloc( 16 );
require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
sprintf( ifa->ifa_name, "%d", i + 1 );
ifa->ifa_flags = IFF_UP | IFF_MULTICAST;
switch( addressList->Address[ i ].lpSockaddr->sa_family )
{
case AF_INET:
{
struct sockaddr_in * sinptr4;
sinptr4 = (struct sockaddr_in *) addressList->Address[ i ].lpSockaddr;
ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) );
break;
}
default:
break;
}
}
if( outAddrs )
{
*outAddrs = head;
head = NULL;
}
err = 0;
exit:
if( head )
{
freeifaddrs( head );
}
if( buffer )
{
free( buffer );
}
if( sock != INVALID_SOCKET )
{
closesocket( sock );
}
return( err );
}
#endif // defined( _WIN32_WCE ) )
#if( !defined( _WIN32_WCE ) )
int getifaddrs( struct ifaddrs **outAddrs )
{
int err;
SOCKET sock;
DWORD size;
DWORD actualSize;
INTERFACE_INFO * buffer;
INTERFACE_INFO * tempBuffer;
INTERFACE_INFO * ifInfo;
int n;
int i;
struct ifaddrs * head;
struct ifaddrs ** next;
struct ifaddrs * ifa;
struct sockaddr_in * sinptr4;
struct sockaddr_in6_old * sinptr6;
struct sockaddr * sa;
sock = INVALID_SOCKET;
buffer = NULL;
head = NULL;
next = &head;
sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
require_action( sock != INVALID_SOCKET, exit, err = WSAEMFILE );
n = 0;
size = 16 * sizeof( INTERFACE_INFO );
for( ;; )
{
tempBuffer = (INTERFACE_INFO *) realloc( buffer, size );
require_action( tempBuffer, exit, err = WSAENOBUFS );
buffer = tempBuffer;
err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL );
if( err == 0 )
{
break;
}
++n;
require_action( n < 100, exit, err = WSAEADDRNOTAVAIL );
size += ( 16 * sizeof( INTERFACE_INFO ) );
}
check( actualSize <= size );
check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 );
n = (int)( actualSize / sizeof( INTERFACE_INFO ) );
for( i = 0; i < n; ++i )
{
ifInfo = &buffer[ i ];
ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
require_action( ifa, exit, err = WSAENOBUFS );
*next = ifa;
next = &ifa->ifa_next;
ifa->ifa_name = (char *) malloc( 16 );
require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
sprintf( ifa->ifa_name, "%d", i + 1 );
ifa->ifa_flags = (u_int) ifInfo->iiFlags;
switch( ifInfo->iiAddress.Address.sa_family )
{
case AF_INET:
sinptr4 = &ifInfo->iiAddress.AddressIn;
ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) );
if( ifInfo->iiNetmask.Address.sa_family == AF_INET )
{
sinptr4 = &ifInfo->iiNetmask.AddressIn;
ifa->ifa_netmask = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_netmask, sinptr4, sizeof( *sinptr4 ) );
}
if( ifInfo->iiBroadcastAddress.Address.sa_family == AF_INET )
{
sinptr4 = &ifInfo->iiBroadcastAddress.AddressIn;
ifa->ifa_broadaddr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
require_action( ifa->ifa_broadaddr, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_broadaddr, sinptr4, sizeof( *sinptr4 ) );
}
break;
case AF_INET6:
sinptr6 = &ifInfo->iiAddress.AddressIn6;
ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr6 ) );
require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_addr, sinptr6, sizeof( *sinptr6 ) );
break;
default:
sa = &ifInfo->iiAddress.Address;
ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa ) );
require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
memcpy( ifa->ifa_addr, sa, sizeof( *sa ) );
break;
}
}
if( outAddrs )
{
*outAddrs = head;
head = NULL;
}
err = 0;
exit:
if( head )
{
freeifaddrs( head );
}
if( buffer )
{
free( buffer );
}
if( sock != INVALID_SOCKET )
{
closesocket( sock );
}
return( err );
}
#endif // !defined( _WIN32_WCE ) )
void freeifaddrs( struct ifaddrs *inAddrs )
{
struct ifaddrs * p;
struct ifaddrs * q;
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_broadaddr )
{
free( p->ifa_broadaddr );
p->ifa_broadaddr = 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 );
}
}
#if( !defined( _WIN32_WCE ) )
int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize )
{
int err;
int size;
if( inAddrSize == 0 )
{
if( inFamily == AF_INET )
{
inAddrSize = sizeof( struct sockaddr_in );
}
else if( inFamily == AF_INET6 )
{
inAddrSize = sizeof( struct sockaddr_in6 );
}
else
{
err = WSAEAFNOSUPPORT;
goto exit;
}
}
size = (int) inAddrSize;
err = WSAStringToAddressA( (char *) inString, inFamily, NULL, (LPSOCKADDR) outAddr, &size );
if( err != 0 ) goto exit;
if( outAddrSize )
{
*outAddrSize = (size_t) size;
}
exit:
return( err );
}
char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize )
{
DWORD size;
int err;
DWORD stringSize;
if( inAddrSize == 0 )
{
const struct sockaddr * addr;
addr = (const struct sockaddr *) inAddr;
if( addr->sa_family == AF_INET )
{
size = sizeof( struct sockaddr_in );
}
else if( addr->sa_family == AF_INET6 )
{
size = sizeof( struct sockaddr_in6 );
}
else
{
WSASetLastError( WSAEAFNOSUPPORT );
inBuffer = NULL;
goto exit;
}
}
else
{
size = (DWORD) inAddrSize;
}
stringSize = (DWORD) inBufferSize;
err = WSAAddressToStringA( (LPSOCKADDR) inAddr, size, NULL, inBuffer, &stringSize );
if( err )
{
inBuffer = NULL;
}
exit:
return( inBuffer );
}
#endif // !defined( _WIN32_WCE )