#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "CommonServices.h"
#include "DebugServices.h"
#include "DNSSDDirect.h"
#include "RMxClient.h"
#include "DNSSD.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEBUG_NAME "[DNS-SD] "
DEBUG_LOCAL int DomainEndsInDot( const char *dom );
#if( DNS_SD_CLIENT_ENABLED )
const char * gDNSSDServer = NULL;
DEBUG_LOCAL bool gDNSSDServerCompatible = false;
#endif
#if 0
#pragma mark == General ==
#endif
DNSServiceErrorType DNSServiceInitialize( DNSServiceInitializeFlags inFlags, int inCacheEntryCount )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
err = RMxClientInitialize();
if( err == kNoErr )
{
if( !( inFlags & kDNSServiceInitializeFlagsNoServerCheck ) )
{
err = DNSServiceCheckVersion();
if( err == kNoErr )
{
goto exit;
}
}
else
{
gDNSSDServerCompatible = true;
goto exit;
}
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
#if( DNS_SD_CLIENT_ENABLED )
dlog( kDebugLevelNotice, DEBUG_NAME "server missing or incompatible...falling back to direct implementation\n" );
#endif
err = DNSServiceInitialize_direct( inFlags, inCacheEntryCount );
goto exit;
#else
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inCacheEntryCount );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
void DNSServiceFinalize( void )
{
#if( DNS_SD_CLIENT_ENABLED )
RMxClientFinalize();
#endif
#if( DNS_SD_DIRECT_ENABLED )
DNSServiceFinalize_direct();
#endif
}
DNSServiceErrorType DNSServiceCheckVersion( void )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
err = DNSServiceCheckVersion_client( gDNSSDServer );
if( err == kNoErr )
{
gDNSSDServerCompatible = true;
}
#else
err = kUnsupportedErr;
#endif
return( err );
}
#if 0
#pragma mark -
#pragma mark == Properties ==
#endif
DNSServiceErrorType DNSServiceCopyProperty( DNSPropertyCode inCode, DNSPropertyData *outData )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceCopyProperty_client( gDNSSDServer, inCode, outData );
goto exit;
}
#else
DEBUG_UNUSED( inCode );
DEBUG_UNUSED( outData );
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = kUnsupportedErr;
goto exit;
#else
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType DNSServiceReleaseProperty( DNSPropertyData *inData )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceReleaseProperty_client( inData );
goto exit;
}
#else
DEBUG_UNUSED( inData );
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = kUnsupportedErr;
goto exit;
#else
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
#if 0
#pragma mark -
#pragma mark == Unix Domain Socket Access ==
#endif
int DNSServiceRefSockFD( DNSServiceRef inRef )
{
DEBUG_UNUSED( inRef );
dlog( kDebugLevelError, "DNSServiceRefSockFD is not supported\n" );
return( -1 );
}
DNSServiceErrorType DNSServiceProcessResult( DNSServiceRef inRef )
{
DEBUG_UNUSED( inRef );
dlog( kDebugLevelError, "DNSServiceProcessResult is not supported\n" );
return( kDNSServiceErr_Unsupported );
}
void DNSServiceRefDeallocate( DNSServiceRef inRef )
{
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
DNSServiceRefDeallocate_client( inRef );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
DNSServiceRefDeallocate_direct( inRef );
goto exit;
#else
DEBUG_UNUSED( inRef );
goto exit;
#endif
exit:
return;
}
#if 0
#pragma mark -
#pragma mark == Domain Enumeration ==
#endif
DNSServiceErrorType
DNSServiceEnumerateDomains(
DNSServiceRef * outRef,
const DNSServiceFlags inFlags,
const uint32_t inInterfaceIndex,
const DNSServiceDomainEnumReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceEnumerateDomains_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceEnumerateDomains_direct( outRef, inFlags, inInterfaceIndex, inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( outRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
#if 0
#pragma mark -
#pragma mark == Service Registration ==
#endif
DNSServiceErrorType
DNSServiceRegister(
DNSServiceRef * outRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inName,
const char * inType,
const char * inDomain,
const char * inHost,
uint16_t inPort,
uint16_t inTXTSize,
const void * inTXT,
DNSServiceRegisterReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceRegister_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inType, inDomain,
inHost, inPort, inTXTSize, inTXT, inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceRegister_direct( outRef, inFlags, inInterfaceIndex, inName, inType, inDomain, inHost, inPort,
inTXTSize, inTXT, inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( outRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inType );
DEBUG_UNUSED( inDomain );
DEBUG_UNUSED( inHost );
DEBUG_UNUSED( inPort );
DEBUG_UNUSED( inTXTSize );
DEBUG_UNUSED( inTXT );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType
DNSServiceAddRecord(
DNSServiceRef inRef,
DNSRecordRef * outRecordRef,
DNSServiceFlags inFlags,
uint16_t inRRType,
uint16_t inRDataSize,
const void * inRData,
uint32_t inTTL )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceAddRecord_client( inRef, outRecordRef, inFlags, inRRType, inRDataSize, inRData, inTTL );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceAddRecord_direct( inRef, outRecordRef, inFlags, inRRType, inRDataSize, inRData, inTTL );
goto exit;
#else
DEBUG_UNUSED( inRef );
DEBUG_UNUSED( outRecordRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inRRType );
DEBUG_UNUSED( inRDataSize );
DEBUG_UNUSED( inRData );
DEBUG_UNUSED( inTTL );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType
DNSServiceUpdateRecord(
DNSServiceRef inRef,
DNSRecordRef inRecordRef,
DNSServiceFlags inFlags,
uint16_t inRDataSize,
const void * inRData,
uint32_t inTTL )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceUpdateRecord_client( inRef, inRecordRef, inFlags, inRDataSize, inRData, inTTL );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceUpdateRecord_direct( inRef, inRecordRef, inFlags, inRDataSize, inRData, inTTL );
goto exit;
#else
DEBUG_UNUSED( inRef );
DEBUG_UNUSED( inRecordRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inRDataSize );
DEBUG_UNUSED( inRData );
DEBUG_UNUSED( inTTL );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType DNSServiceRemoveRecord( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceRemoveRecord_client( inRef, inRecordRef, inFlags );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceRemoveRecord_direct( inRef, inRecordRef, inFlags );
goto exit;
#else
DEBUG_UNUSED( inRef );
DEBUG_UNUSED( inRecordRef );
DEBUG_UNUSED( inFlags );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
#if 0
#pragma mark -
#pragma mark == Service Discovery ==
#endif
DNSServiceErrorType
DNSServiceBrowse(
DNSServiceRef * outRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inType,
const char * inDomain,
DNSServiceBrowseReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceBrowse_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inType, inDomain,
inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceBrowse_direct( outRef, inFlags, inInterfaceIndex, inType, inDomain, inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( outRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inType );
DEBUG_UNUSED( inDomain );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType
DNSServiceResolve(
DNSServiceRef * outRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inName,
const char * inType,
const char * inDomain,
DNSServiceResolveReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceResolve_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inType, inDomain,
inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceResolve_direct( outRef, inFlags, inInterfaceIndex, inName, inType, inDomain, inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( outRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inType );
DEBUG_UNUSED( inDomain );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
#if 0
#pragma mark -
#pragma mark == Special Purpose ==
#endif
int DNSServiceConstructFullName( char *fullName, const char *service, const char *regtype, const char *domain )
{
size_t len;
unsigned char c;
char *fn = fullName;
const char *s = service;
const char *r = regtype;
const char *d = domain;
if (service && *service)
{
while(*s)
{
c = (unsigned char) *s++;
if (c == '.' || (c == '\\')) *fn++ = '\\'; else if (c <= ' ') {
*fn++ = '\\';
*fn++ = (char) ('0' + (c / 100));
*fn++ = (char) ('0' + (c / 10) % 10);
c = (unsigned char)('0' + (c % 10));
}
*fn++ = (char) c;
}
*fn++ = '.';
}
if (regtype && *regtype)
{
len = strlen(regtype);
if (DomainEndsInDot(regtype)) len--;
if (len < 4) return -1; if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1;
while(*r)
*fn++ = *r++;
if (!DomainEndsInDot(regtype)) *fn++ = '.';
}
if (!domain || !(*domain))
{
domain = "local.";
d = domain;
}
len = strlen(domain);
if (!len) return -1;
while(*d)
*fn++ = *d++;
if (!DomainEndsInDot(domain)) *fn++ = '.';
*fn = '\0';
return 0;
}
DNSServiceErrorType DNSServiceCreateConnection( DNSServiceRef *outRef )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceCreateConnection_client( outRef, gDNSSDServer );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceCreateConnection_direct( outRef );
goto exit;
#else
DEBUG_UNUSED( outRef );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType
DNSServiceRegisterRecord(
DNSServiceRef inRef,
DNSRecordRef * outRecordRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inName,
uint16_t inRRType,
uint16_t inRRClass,
uint16_t inRDataSize,
const void * inRData,
uint32_t inTTL,
DNSServiceRegisterRecordReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceRegisterRecord_client( inRef, outRecordRef, inFlags, inInterfaceIndex, inName,
inRRType, inRRClass, inRDataSize, inRData, inTTL, inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceRegisterRecord_direct( inRef, outRecordRef, inFlags, inInterfaceIndex, inName,
inRRType, inRRClass, inRDataSize, inRData, inTTL, inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( inRef );
DEBUG_UNUSED( outRecordRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inRRType );
DEBUG_UNUSED( inRRClass );
DEBUG_UNUSED( inRDataSize );
DEBUG_UNUSED( inRData );
DEBUG_UNUSED( inTTL );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
DNSServiceErrorType
DNSServiceQueryRecord(
DNSServiceRef * outRef,
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inName,
uint16_t inRRType,
uint16_t inRRClass,
DNSServiceQueryRecordReply inCallBack,
void * inContext )
{
DNSServiceErrorType err;
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
err = DNSServiceQueryRecord_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inRRType, inRRClass,
inCallBack, inContext );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
err = DNSServiceQueryRecord_direct( outRef, inFlags, inInterfaceIndex, inName, inRRType, inRRClass,
inCallBack, inContext );
goto exit;
#else
DEBUG_UNUSED( outRef );
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inRRType );
DEBUG_UNUSED( inRRClass );
DEBUG_UNUSED( inCallBack );
DEBUG_UNUSED( inContext );
err = kUnsupportedErr;
goto exit;
#endif
exit:
return( err );
}
void
DNSServiceReconfirmRecord(
DNSServiceFlags inFlags,
uint32_t inInterfaceIndex,
const char * inName,
uint16_t inRRType,
uint16_t inRRClass,
uint16_t inRDataSize,
const void * inRData )
{
#if( DNS_SD_CLIENT_ENABLED )
if( gDNSSDServerCompatible )
{
DNSServiceReconfirmRecord_client( gDNSSDServer, inFlags, inInterfaceIndex, inName, inRRType, inRRClass,
inRDataSize, inRData );
goto exit;
}
#endif
#if( DNS_SD_DIRECT_ENABLED )
DNSServiceReconfirmRecord_direct( inFlags, inInterfaceIndex, inName, inRRType, inRRClass, inRDataSize, inRData );
goto exit;
#else
DEBUG_UNUSED( inFlags );
DEBUG_UNUSED( inInterfaceIndex );
DEBUG_UNUSED( inName );
DEBUG_UNUSED( inRRType );
DEBUG_UNUSED( inRRClass );
DEBUG_UNUSED( inRDataSize );
DEBUG_UNUSED( inRData );
goto exit;
#endif
exit:
return;
}
#if 0
#pragma mark -
#pragma mark == Utilities ==
#endif
DEBUG_LOCAL int DomainEndsInDot( const char *dom )
{
check( dom );
while(*dom && *(dom + 1))
{
if (*dom == '\\') {
if (*(dom + 1) >= '0' && *(dom + 1) <= '9') dom += 4;
else dom += 2;
}
else dom++; }
return (*dom == '.');
}
#if 0
#pragma mark -
#pragma mark == TXT Record Building ==
#endif
typedef struct _TXTRecordRef_t _TXTRecordRef_t;
struct _TXTRecordRef_t
{
uint8_t * txt;
uint16_t size;
};
DEBUG_LOCAL DNSServiceErrorType
TXTRecordGetValuePtrInternal(
uint16_t inTXTSize,
const void * inTXTBytes,
const char * inKey,
uint8_t ** outItem,
uint16_t * outItemSize,
const void ** outValue,
uint8_t * outValueSize );
DEBUG_LOCAL DNSServiceErrorType
TXTRecordGetItemAtIndexInternal(
uint16_t inTXTSize,
const void * inTXTBytes,
uint16_t inIndex,
uint8_t ** outItem,
uint16_t * outItemSize,
char * inKeyBuffer,
const void ** outValue,
uint8_t * outValueSize );
DEBUG_LOCAL int TXTRecordMemEqv( const void *inLeft, size_t inLeftSize, const void *inRight, size_t inRightSize );
DNSServiceErrorType TXTRecordCreate( TXTRecordRef *outRef )
{
DNSServiceErrorType err;
TXTRecordRef obj;
require_action_expect( outRef, exit, err = kDNSServiceErr_BadParam );
obj = (TXTRecordRef) calloc( 1, sizeof( *obj ) );
require_action( obj, exit, err = kDNSServiceErr_NoMemory );
*outRef = obj;
err = kDNSServiceErr_NoError;
exit:
return( err );
}
void TXTRecordDeallocate( TXTRecordRef inRef )
{
check( inRef );
if( inRef )
{
if( inRef->txt ) free( inRef->txt );
free( inRef );
}
}
DNSServiceErrorType TXTRecordSetValue( TXTRecordRef inRef, const char *inKey, uint8_t inValueSize, const void *inValue )
{
DNSServiceErrorType err;
const char * p;
size_t keySize;
uint8_t * item;
uint8_t * itemEnd;
uintptr_t itemOffset;
uint16_t oldItemSize;
uint16_t newItemSize;
uint16_t delta;
uint8_t * txt;
require_action_expect( inRef, exit, err = kDNSServiceErr_BadParam );
require_action_expect( inKey, exit, err = kDNSServiceErr_BadParam );
require_action_expect( inValue || ( inValueSize == 0 ), exit, err = kDNSServiceErr_BadParam );
for( p = inKey; *p != '\0'; ++p )
{
if( ( *p < 0x20 ) || ( *p > 0x7E ) || ( *p == 0x3D ) )
{
break;
}
}
keySize = (size_t)( p - inKey );
require_action( ( keySize > 0 ) && ( keySize <= 255 ) && ( *p == '\0' ), exit, err = kDNSServiceErr_Invalid );
newItemSize = (uint16_t) keySize;
if( inValue ) newItemSize += 1; newItemSize = (uint16_t)( newItemSize + inValueSize );
require_action( newItemSize <= 255, exit, err = kDNSServiceErr_Invalid );
err = TXTRecordGetValuePtrInternal( inRef->size, inRef->txt, inKey, &item, &oldItemSize, NULL, NULL );
if( err == kDNSServiceErr_NoError )
{
if( newItemSize > oldItemSize )
{
itemOffset = (uintptr_t)( item - inRef->txt );
delta = (uint16_t)( newItemSize - oldItemSize );
txt = (uint8_t *) realloc( inRef->txt, (size_t)( inRef->size + delta ) );
require_action( txt, exit, err = kDNSServiceErr_NoMemory );
item = txt + itemOffset;
itemEnd = item + ( 1 + oldItemSize );
memmove( item + ( 1 + newItemSize ), itemEnd, (size_t)( ( inRef->txt + inRef->size ) - itemEnd ) );
inRef->txt = txt;
inRef->size = (uint16_t)( inRef->size + delta );
}
else if( newItemSize < oldItemSize )
{
itemEnd = item + ( 1 + oldItemSize );
memmove( item + ( 1 + newItemSize ), itemEnd, (size_t)( ( inRef->txt + inRef->size ) - itemEnd ) );
itemOffset = (uintptr_t)( item - inRef->txt );
inRef->size -= ( oldItemSize - newItemSize );
txt = (uint8_t *) realloc( inRef->txt, inRef->size );
require_action( txt, exit, err = kDNSServiceErr_NoMemory );
item = txt + itemOffset;
inRef->txt = txt;
}
}
else
{
delta = (uint16_t)( 1 + newItemSize );
txt = (uint8_t *) realloc( inRef->txt, (size_t)( inRef->size + delta ) );
require_action( txt, exit, err = kDNSServiceErr_NoMemory );
item = txt + inRef->size;
inRef->txt = txt;
inRef->size = (uint16_t)( inRef->size + delta );
}
*item++ = (uint8_t) newItemSize;
memcpy( item, inKey, keySize );
item += keySize;
if( inValue ) *item++ = '='; memcpy( item, inValue, inValueSize );
err = kDNSServiceErr_NoError;
exit:
return( err );
}
DNSServiceErrorType TXTRecordRemoveValue( TXTRecordRef inRef, const char *inKey )
{
DNSServiceErrorType err;
uint8_t * item;
uint8_t * itemEnd;
uint16_t size;
uint8_t * txt;
uint8_t * txtEnd;
err = TXTRecordGetValuePtrInternal( inRef->size, inRef->txt, inKey, &item, &size, NULL, NULL );
require_noerr_quiet( err, exit );
itemEnd = item + ( 1 + size );
txtEnd = inRef->txt + inRef->size;
if( itemEnd < txtEnd )
{
memmove( item, itemEnd, (size_t)( txtEnd - itemEnd ) );
}
inRef->size = (uint16_t)( inRef->size - ( (uint16_t)( itemEnd - item ) ) );
if( inRef->size > 0 )
{
txt = (uint8_t *) realloc( inRef->txt, inRef->size );
require_action_quiet( txt, exit, err = kDNSServiceErr_Unknown );
}
else
{
free( inRef->txt );
txt = NULL;
}
inRef->txt = txt;
exit:
return( err );
}
uint16_t TXTRecordGetLength( TXTRecordRef inRef )
{
check( inRef );
if( inRef && inRef->txt )
{
return( inRef->size );
}
return( 0 );
}
const void * TXTRecordGetBytesPtr( TXTRecordRef inRef )
{
check( inRef );
if( inRef && inRef->txt )
{
return( inRef->txt );
}
return( NULL );
}
#if 0
#pragma mark -
#pragma mark == TXT Record Parsing ==
#endif
DNSServiceErrorType
TXTRecordGetValuePtr(
uint16_t inTXTSize,
const void * inTXTBytes,
const char * inKey,
const void ** outValue,
uint8_t * outValueSize )
{
return( TXTRecordGetValuePtrInternal( inTXTSize, inTXTBytes, inKey, NULL, NULL, outValue, outValueSize ) );
}
uint16_t TXTRecordGetCount( uint16_t inTXTSize, const void *inTXTBytes )
{
uint16_t n;
const uint8_t * p;
const uint8_t * q;
require_action( inTXTBytes, exit, n = 0 );
n = 0;
p = (const uint8_t *) inTXTBytes;
q = p + inTXTSize;
while( p < q )
{
++n;
p += ( 1 + *p );
require_action( p <= q, exit, n = 0 );
}
exit:
return( n );
}
DNSServiceErrorType
TXTRecordGetItemAtIndex(
uint16_t inTXTSize,
const void * inTXTBytes,
uint16_t inIndex,
char * inKeyBuffer,
const void ** outValue,
uint8_t * outValueSize )
{
return( TXTRecordGetItemAtIndexInternal( inTXTSize, inTXTBytes, inIndex, NULL, NULL, inKeyBuffer, outValue, outValueSize ) );
}
#if 0
#pragma mark -
#pragma mark == TXT Record Internal ==
#endif
DEBUG_LOCAL DNSServiceErrorType
TXTRecordGetValuePtrInternal(
uint16_t inTXTSize,
const void * inTXTBytes,
const char * inKey,
uint8_t ** outItem,
uint16_t * outItemSize,
const void ** outValue,
uint8_t * outValueSize )
{
DNSServiceErrorType err;
size_t keySize;
const uint8_t * p;
const uint8_t * q;
const uint8_t * r;
const uint8_t * key;
const uint8_t * keyEnd;
const uint8_t * value;
const uint8_t * valueEnd;
require_action_quiet( inTXTBytes, exit, err = kDNSServiceErr_NoSuchKey );
require_action_expect( inKey, exit, err = kDNSServiceErr_BadParam );
keySize = strlen( inKey );
keyEnd = NULL;
value = NULL;
valueEnd = NULL;
p = (const uint8_t *) inTXTBytes;
q = p + inTXTSize;
while( p < q )
{
r = p + ( 1 + *p );
require_action( r <= q, exit, err = kDNSServiceErr_Invalid );
key = p + 1;
keyEnd = (const uint8_t *) memchr( key, '=', *p );
if( keyEnd )
{
value = keyEnd + 1;
}
else
{
keyEnd = r;
value = r;
}
valueEnd = r;
if( TXTRecordMemEqv( key, (size_t)( keyEnd - key ), inKey, keySize ) == 0 )
{
break;
}
p = r;
check( p <= q );
}
require_action_quiet( p < q, exit, err = kDNSServiceErr_NoSuchKey );
if( outItem ) *outItem = (uint8_t *) p;
if( outItemSize ) *outItemSize = *p;
if( outValue ) *outValue = ( value == keyEnd ) ? NULL : value; if( outValueSize ) *outValueSize = (uint8_t)( valueEnd - value );
err = kDNSServiceErr_NoError;
exit:
return( err );
}
DEBUG_LOCAL DNSServiceErrorType
TXTRecordGetItemAtIndexInternal(
uint16_t inTXTSize,
const void * inTXTBytes,
uint16_t inIndex,
uint8_t ** outItem,
uint16_t * outItemSize,
char * inKeyBuffer,
const void ** outValue,
uint8_t * outValueSize )
{
DNSServiceErrorType err;
uint16_t n;
const uint8_t * p;
const uint8_t * q;
const uint8_t * r;
const uint8_t * key;
const uint8_t * keyEnd;
const uint8_t * value;
const uint8_t * valueEnd;
require_action_quiet( inTXTBytes, exit, err = kDNSServiceErr_Invalid );
n = 0;
p = (const uint8_t *) inTXTBytes;
q = p + inTXTSize;
while( p < q )
{
if( n == inIndex ) break;
++n;
p += ( 1 + *p );
require_action( p <= q, exit, err = kDNSServiceErr_Invalid );
}
require_action_quiet( p < q, exit, err = kDNSServiceErr_Invalid );
r = p + ( 1 + *p );
require_action( r <= q, exit, err = kDNSServiceErr_Invalid );
key = p + 1;
keyEnd = (const uint8_t *) memchr( key, '=', *p );
if( keyEnd )
{
value = keyEnd + 1;
}
else
{
keyEnd = r;
value = r;
}
valueEnd = r;
if( outItem ) *outItem = (uint8_t *) p;
if( outItemSize ) *outItemSize = *p;
if( inKeyBuffer )
{
n = (uint16_t)( keyEnd - key );
memcpy( inKeyBuffer, key, n );
inKeyBuffer[ n ] = '\0';
}
if( outValue ) *outValue = ( value == keyEnd ) ? NULL : value; if( outValueSize ) *outValueSize = (uint8_t)( valueEnd - value );
err = kDNSServiceErr_NoError;
exit:
return( err );
}
DEBUG_LOCAL int TXTRecordMemEqv( const void *inLeft, size_t inLeftSize, const void *inRight, size_t inRightSize )
{
const uint8_t * p1;
const uint8_t * p2;
const uint8_t * end;
int c1;
int c2;
if( inLeftSize < inRightSize ) return( -1 );
if( inLeftSize > inRightSize ) return( 1 );
p1 = (const uint8_t *) inLeft;
p2 = (const uint8_t *) inRight;
end = p1 + inLeftSize;
while( p1 < end )
{
c1 = tolower( *p1 );
c2 = tolower( *p2 );
if( c1 < c2 ) return( -1 );
if( c1 > c2 ) return( 1 );
++p1;
++p2;
}
return( 0 );
}
#if( DEBUG )
DNSServiceErrorType TXTRecordTest( void );
DNSServiceErrorType TXTRecordTest( void )
{
DNSServiceErrorType err;
const char * s;
TXTRecordRef ref;
const void * txt;
uint16_t size;
uint16_t n;
char key[ 256 ];
const void * value;
uint8_t valueSize;
s = "\014Anon Allowed"
"\010Options="
"\025InstalledPlugins=JPEG";
size = (uint16_t) strlen( s );
n = TXTRecordGetCount( size, s );
require_action( n == 3, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "test", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "Anon", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "Allowed", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "Anon Allowed", &value, &valueSize );
require_noerr( err, exit );
require_action( value == NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "Options", &value, &valueSize );
require_noerr( err, exit );
require_action( value != NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, s, "InstalledPlugins", &value, &valueSize );
require_noerr( err, exit );
require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( value, "JPEG", 4 ) == 0, exit, err = kDNSServiceErr_Unknown );
key[ 0 ] = '\0';
err = TXTRecordGetItemAtIndex( size, s, 0, key, &value, &valueSize );
require_noerr( err, exit );
require_action( strcmp( key, "Anon Allowed" ) == 0, exit, err = kDNSServiceErr_Unknown );
require_action( value == NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
key[ 0 ] = '\0';
err = TXTRecordGetItemAtIndex( size, s, 1, key, &value, &valueSize );
require_noerr( err, exit );
require_action( strcmp( key, "Options" ) == 0, exit, err = kDNSServiceErr_Unknown );
require_action( value != NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
key[ 0 ] = '\0';
err = TXTRecordGetItemAtIndex( size, s, 2, key, &value, &valueSize );
require_noerr( err, exit );
require_action( strcmp( key, "InstalledPlugins" ) == 0, exit, err = kDNSServiceErr_Unknown );
require_action( value != NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( value, "JPEG", valueSize ) == 0, exit, err = kDNSServiceErr_Unknown );
key[ 0 ] = '\0';
err = TXTRecordGetItemAtIndex( size, s, 3, key, &value, &valueSize );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 13, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt, "\014Anon Allowed", size ) == 0, exit, err = kDNSServiceErr_Unknown );
n = TXTRecordGetCount( size, txt );
require_action( n == 1, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "test", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Anon", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Allowed", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Anon Allowed", &value, &valueSize );
require_noerr( err, exit );
require_action( value == NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "aNoN aLlOwEd", &value, &valueSize );
require_noerr( err, exit );
require_action( value == NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Options", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 44, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt,
"\014Anon Allowed"
"\010Options="
"\025InstalledPlugins=JPEG",
size ) == 0, exit, err = kDNSServiceErr_Unknown );
n = TXTRecordGetCount( size, txt );
require_action( n == 3, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "test", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Anon", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Allowed", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "", NULL, NULL );
require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Anon Allowed", &value, &valueSize );
require_noerr( err, exit );
require_action( value == NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "Options", &value, &valueSize );
require_noerr( err, exit );
require_action( value != NULL, exit, err = kDNSServiceErr_Unknown );
require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordGetValuePtr( size, txt, "InstalledPlugins", &value, &valueSize );
require_noerr( err, exit );
require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( value, "JPEG", 4 ) == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordRemoveValue( ref, "Anon Allowed" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Options", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" );
require_noerr( err, exit );
err = TXTRecordRemoveValue( ref, "Options" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 35, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt,
"\014Anon Allowed"
"\025InstalledPlugins=JPEG",
size ) == 0, exit, err = kDNSServiceErr_Unknown );
n = TXTRecordGetCount( size, txt );
require_action( n == 2, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordRemoveValue( ref, "Anon Allowed" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 22, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt,
"\025InstalledPlugins=JPEG",
size ) == 0, exit, err = kDNSServiceErr_Unknown );
n = TXTRecordGetCount( size, txt );
require_action( n == 1, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordRemoveValue( ref, "InstalledPlugins" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
err = TXTRecordCreate( &ref );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Options", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Options", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 44, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt,
"\014Anon Allowed"
"\010Options="
"\025InstalledPlugins=JPEG",
size ) == 0, exit, err = kDNSServiceErr_Unknown );
err = TXTRecordSetValue( ref, "Anon Allowed", 4, "test" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 0, "" );
require_noerr( err, exit );
err = TXTRecordSetValue( ref, "Anon Allowed", 4, "test" );
require_noerr( err, exit );
txt = TXTRecordGetBytesPtr( ref );
require_action( txt, exit, err = kDNSServiceErr_Unknown );
size = TXTRecordGetLength( ref );
require_action( size == 49, exit, err = kDNSServiceErr_Unknown );
require_action( memcmp( txt,
"\021Anon Allowed=test"
"\010Options="
"\025InstalledPlugins=JPEG",
size ) == 0, exit, err = kDNSServiceErr_Unknown );
TXTRecordDeallocate( ref );
exit:
return( err );
}
#endif // DEBUG
#ifdef __cplusplus
}
#endif