DNSRegistrationThread.cpp [plain text]
#include "DNSRegistrationThread.h"
#include "mDNSPlugin.h"
#include "LinkAddresses.h"
#include "CNSLTimingUtils.h"
#define kOurSpecialRegRef -1
typedef struct DNSRegData {
CFNetServiceRef fCFNetServiceRef;
UInt32 fCount;
} DNSRegData;
const CFStringRef kDNSSCDynamicStoreKeySAFE_CFSTR = CFSTR("com.apple.DirectoryServices.DNS");
const CFStringRef kWorkstationTypeSAFE_CFSTR = CFSTR("_workstation._tcp.");
const CFStringRef kWorkstationPortSAFE_CFSTR = CFSTR("9");
const CFStringRef kSpaceLeftBracketSAFE_CFSTR = CFSTR(" [");
const CFStringRef kRightBracketSAFE_CFSTR = CFSTR("]");
const CFStringRef kZeroedMACAddressSAFE_CFSTR = CFSTR("0:0:0:0:0:0");
static void RegisterEntityCallBack(CFNetServiceRef theEntity, CFStreamError* error, void* info);
CFStringRef CopyCancelRegDescription( const void* info );
CFStringRef CopyRegistrationDescription( const void* info );
boolean_t SystemConfigurationNameChangedCallBack(SCDynamicStoreRef session, void *callback_argument);
DNSRegistrationThread::DNSRegistrationThread( mDNSPlugin* parentPlugin )
{
mParentPlugin = parentPlugin;
mRunLoopRef = 0;
mSCRef = NULL;
mRegisteredServicesTable = NULL;
mMachineService = NULL;
mCanceled = false;
mOurSpecialRegKey = NULL;
}
DNSRegistrationThread::~DNSRegistrationThread()
{
mParentPlugin = NULL;
mRunLoopRef = 0;
if ( mRegisteredServicesTable )
{
::CFDictionaryRemoveAllValues( mRegisteredServicesTable );
::CFRelease( mRegisteredServicesTable );
mRegisteredServicesTable = NULL;
}
if ( mSCRef )
CFRelease( mSCRef );
mSCRef = NULL;
if ( mOurSpecialRegKey )
CFRelease( mOurSpecialRegKey );
mOurSpecialRegKey = NULL;
}
void DNSRegistrationThread::Cancel( void )
{
mCanceled = true;
}
void DNSRegistrationThread::Initialize( CFRunLoopRef idleRunLoopRef )
{
CFDictionaryKeyCallBacks keyCallBack;
CFDictionaryValueCallBacks valueCallBack;
keyCallBack.version = 0;
keyCallBack.retain = NULL;
keyCallBack.release = NULL;
keyCallBack.copyDescription = NULL;
keyCallBack.equal = NULL;
keyCallBack.hash = NULL;
valueCallBack.version = 0;
valueCallBack.retain = NULL;
valueCallBack.release = NULL;
valueCallBack.copyDescription = NULL;
valueCallBack.equal = NULL;
mRegisteredServicesTable = ::CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &valueCallBack );
mRunLoopRef = idleRunLoopRef;
if ( !mSCRef )
mSCRef = ::SCDynamicStoreCreate(NULL, kDNSSCDynamicStoreKeySAFE_CFSTR, NULL, NULL);
SInt32 scdStatus = 0;
CFStringRef key = 0;
Boolean setStatus = FALSE;
CFMutableArrayRef notifyKeys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFMutableArrayRef notifyPatterns = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
DBGLOG( "RegisterForNetworkChange for SCDynamicStoreKeyCreateComputerName:\n" );
key = SCDynamicStoreKeyCreateComputerName(NULL);
CFArrayAppendValue(notifyKeys, key);
CFRelease(key);
setStatus = SCDynamicStoreSetNotificationKeys(mSCRef, notifyKeys, notifyPatterns);
CFRelease(notifyKeys);
CFRelease(notifyPatterns);
if ( mRunLoopRef )
{
::CFRunLoopAddCommonMode( mRunLoopRef, kCFRunLoopDefaultMode );
scdStatus = ::SCDynamicStoreNotifyCallback( mSCRef, mRunLoopRef, SystemConfigurationNameChangedCallBack, this );
DBGLOG( "NSLRequestMgrThread::RegisterForNetworkChanges, SCDynamicStoreNotifyCallback returned %ld\n", scdStatus );
}
else
DBGLOG( "NSLRequestMgrThread::RegisterForNetworkChanges, No Current Run Loop, couldn't store Notify callback\n" );
}
tDirStatus DNSRegistrationThread::RegisterHostedServices( void )
{
tDirStatus registrationStatus = eDSNoErr;
CFStringEncoding encoding;
CFStringRef computerName = SCDynamicStoreCopyComputerName(NULL, &encoding);
if ( computerName )
{
CFStringRef ethernetAddress = CreateComputerNameEthernetString(computerName);
if ( ethernetAddress )
{
char ethernetAddressStr[1024] = {0,};
CFStringGetCString( ethernetAddress, ethernetAddressStr, sizeof(ethernetAddressStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::RegisterHostedServices, registering %s\n", ethernetAddressStr );
registrationStatus = PerformRegistration( ethernetAddress, kWorkstationTypeSAFE_CFSTR, kEmptySAFE_CFSTR, NULL, kWorkstationPortSAFE_CFSTR, &mOurSpecialRegKey );
if ( mOurSpecialRegKey )
{
CFStringGetCString( mOurSpecialRegKey, ethernetAddressStr, sizeof(ethernetAddressStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::RegisterHostedServices set mOurSpecialRegKey to %s\n", ethernetAddressStr );
}
::CFRelease( ethernetAddress );
}
else
DBGLOG("DNSRegistrationThread::RegisterHostedServices Could't get Ethernet Address!\n" );
::CFRelease( computerName );
}
else
DBGLOG("DNSRegistrationThread::RegisterHostedServices Could't get computer name!\n" );
return registrationStatus;
}
tDirStatus DNSRegistrationThread::PerformRegistration( CFStringRef nameRef,
CFStringRef typeRef,
CFStringRef domainRef,
CFStringRef protocolSpecificData,
CFStringRef portRef,
CFStringRef* serviceKeyRef )
{
Boolean useOldAPICalls = false;
*serviceKeyRef = NULL;
while (!mRunLoopRef)
{
DBGLOG("DNSRegistrationThread::PerformRegistration, waiting for mRunLoopRef\n");
SmartSleep(500000);
}
CFStringRef modDomainRef = NULL;
CFStringRef modTypeRef = NULL;
if ( CFStringCompare( domainRef, kEmptySAFE_CFSTR, 0 ) != kCFCompareEqualTo && !CFStringHasSuffix( domainRef, kDotSAFE_CFSTR ) )
{
DBGLOG( "DNSRegistrationThread::PerformRegistration appending \".\" to domain\n" );
modDomainRef = CFStringCreateMutableCopy( NULL, 0, domainRef );
CFStringAppendCString( (CFMutableStringRef)modDomainRef, ".", kCFStringEncodingUTF8 );
}
if ( !CFStringHasSuffix( typeRef, kDotUnderscoreTCPSAFE_CFSTR ) )
{
modTypeRef = CFStringCreateMutableCopy( NULL, 0, kUnderscoreSAFE_CFSTR );
CFStringAppend( (CFMutableStringRef)modTypeRef, typeRef );
CFStringAppend( (CFMutableStringRef)modTypeRef, kDotUnderscoreTCPSAFE_CFSTR );
}
CFStreamError error = {(CFStreamErrorDomain)0, 0};
CFNetServiceRef entity = NULL;
UInt32 port = CFStringGetIntValue( portRef );
CFMutableStringRef serviceKey = CFStringCreateMutable( NULL, 0 );
if ( serviceKey )
{
CFStringAppend( serviceKey, nameRef );
CFStringAppend( serviceKey, kDotSAFE_CFSTR );
CFStringAppend( serviceKey, (modTypeRef)?modTypeRef:typeRef );
CFStringAppend( serviceKey, (modDomainRef)?modDomainRef:domainRef );
}
DNSRegData* regData = NULL;
if ( (regData = (DNSRegData*)::CFDictionaryGetValue( mRegisteredServicesTable, serviceKey )) != NULL )
{
#define USE_REF_COUNT_FOR_DUP_REGISTRATIONS
#ifdef USE_REF_COUNT_FOR_DUP_REGISTRATIONS
regData->fCount++; char serviceKeyStr[1024] = {0,};
CFStringGetCString( serviceKey, serviceKeyStr, sizeof(serviceKeyStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::PerformRegistration, service: %s (0x%x) is now registered %ld times\n", serviceKeyStr, regData->fCFNetServiceRef, regData->fCount );
#endif
}
else
{
if ( DEBUGGING_NSL )
{
char* name = (char*)malloc( CFStringGetMaximumSizeForEncoding( CFStringGetLength(nameRef), kCFStringEncodingUTF8 ) + 1 );
char* location = (char*)malloc( CFStringGetMaximumSizeForEncoding( CFStringGetLength((modDomainRef)?modDomainRef:domainRef), kCFStringEncodingUTF8 ) + 1 );
char* service = (char*)malloc( CFStringGetMaximumSizeForEncoding( CFStringGetLength(typeRef), kCFStringEncodingUTF8 ) + 1 );
if ( name && location && service )
{
CFStringGetCString( nameRef, name, CFStringGetMaximumSizeForEncoding( CFStringGetLength(nameRef), kCFStringEncodingUTF8 ) + 1, kCFStringEncodingUTF8 );
CFStringGetCString( (modDomainRef)?modDomainRef:domainRef, location, CFStringGetMaximumSizeForEncoding( CFStringGetLength((modDomainRef)?modDomainRef:domainRef), kCFStringEncodingUTF8 ) + 1, kCFStringEncodingUTF8 );
CFStringGetCString( typeRef, service, CFStringGetMaximumSizeForEncoding( CFStringGetLength(typeRef), kCFStringEncodingUTF8 ) + 1, kCFStringEncodingUTF8 );
DBGLOG("DNSRegistrationThread::PerformRegistration for [%s] for service [%s] in domain [%s] using port %ld\n", name, service, location, port );
}
if ( name )
free( name );
if ( location )
free( location );
if ( service )
free( service );
}
if ( CFStringCompare( nameRef, CFSTR(kUseMachineName), 0 ) == kCFCompareEqualTo || GetParentPlugin()->GetComputerNameString() && CFStringCompare( GetParentPlugin()->GetComputerNameString(), nameRef, 0 ) == kCFCompareEqualTo )
nameRef = kEmptySAFE_CFSTR;
entity = CFNetServiceCreate(NULL, (modDomainRef)?modDomainRef:domainRef, (modTypeRef)?modTypeRef:typeRef, nameRef, port);
if ( protocolSpecificData && CFGetTypeID(protocolSpecificData) == CFStringGetTypeID() )
{
CFDictionaryRef txtDataAsDictionary = CreateMutableDictionaryFromXMLString( (CFStringRef)protocolSpecificData );
if ( txtDataAsDictionary )
{
CFDataRef txtRecord = NULL;
DBGLOG("DNSRegistrationThread::PerformRegistration adding TXT data dictionary type data to registration\n" );
txtRecord = CFNetServiceCreateTXTDataWithDictionary( NULL, txtDataAsDictionary );
CFNetServiceSetTXTData( entity, txtRecord );
CFRelease( txtRecord );
}
else
{
CFNetServiceSetProtocolSpecificInformation( entity, protocolSpecificData );
DBGLOG("DNSRegistrationThread::PerformRegistration adding TXT data text type data to registration\n" );
useOldAPICalls = true;
}
}
CFNetServiceClientContext c = {0, NULL, NULL, NULL, CopyRegistrationDescription};
if ( !CFNetServiceSetClient( entity, RegisterEntityCallBack, &c) )
syslog( LOG_ERR, "DS Bonjour was unable to register a service with CFNetService!\n" );
CFNetServiceScheduleWithRunLoop(entity, mRunLoopRef, kCFRunLoopDefaultMode);
if ( useOldAPICalls )
{
if (CFNetServiceRegister(entity, &error))
{
CFRunLoopWakeUp( mRunLoopRef );
DBGLOG("CFNetServiceRegister returned TRUE!\n");
}
else
DBGLOG("CFNetServiceRegister returned FALSE (%d, %ld).\n", error.domain, error.error);
}
else
{
if (CFNetServiceRegisterWithOptions(entity, 0, &error))
{
DBGLOG("CFNetServiceRegister started\n");
}
else
DBGLOG("CFNetServiceRegister returned FALSE (%d, %ld).\n", error.domain, error.error);
}
if ( !error.error && serviceKey )
{
*serviceKeyRef = serviceKey;
CFRetain( *serviceKeyRef );
regData = (DNSRegData*)malloc(sizeof(DNSRegData));
regData->fCount = 1;
regData->fCFNetServiceRef = entity;
::CFDictionaryAddValue( mRegisteredServicesTable, serviceKey, (const void*)regData );
char serviceKeyStr[1024] = {0,};
CFStringGetCString( serviceKey, serviceKeyStr, sizeof(serviceKeyStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::PerformRegistration, registering with CFNetService: %s (0x%x)\n", serviceKeyStr, regData->fCFNetServiceRef );
}
}
if ( serviceKey )
CFRelease( serviceKey );
if ( modDomainRef )
CFRelease( modDomainRef );
if ( modTypeRef )
CFRelease( modTypeRef );
return (tDirStatus)error.error;
}
tDirStatus DNSRegistrationThread::PerformDeregistration( CFDictionaryRef service )
{
tDirStatus status = eDSRecordNotFound;
if ( !service )
return status;
CFStringRef modDomainRef = NULL;
CFStringRef modTypeRef = NULL;
CFStringRef domainRef = NULL;
CFStringRef nameOfService = NULL;
CFStringRef typeOfService = NULL;
nameOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordNameSAFE_CFSTR );
if ( !nameOfService )
nameOfService = kEmptySAFE_CFSTR;
domainRef = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrLocationSAFE_CFSTR );
if ( !domainRef )
domainRef = kEmptySAFE_CFSTR; else
if ( CFStringCompare( domainRef, CFSTR("local"), 0 ) == kCFCompareEqualTo )
{
DBGLOG( "DNSRegistrationThread::PerformDeregistration, deregistering in local, use \"\"\n" );
domainRef = kEmptySAFE_CFSTR; }
typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrServiceTypeSAFE_CFSTR );
if ( !typeOfService )
typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordTypeSAFE_CFSTR );
if ( CFStringCompare( domainRef, kEmptySAFE_CFSTR, 0 ) != kCFCompareEqualTo && !CFStringHasSuffix( domainRef, kDotSAFE_CFSTR ) )
{
modDomainRef = CFStringCreateMutableCopy( NULL, 0, domainRef );
CFStringAppendCString( (CFMutableStringRef)modDomainRef, ".", kCFStringEncodingUTF8 );
}
if ( !CFStringHasSuffix( typeOfService, kDotUnderscoreTCPSAFE_CFSTR ) )
{
modTypeRef = CFStringCreateMutableCopy( NULL, 0, kUnderscoreSAFE_CFSTR );
CFStringAppend( (CFMutableStringRef)modTypeRef, typeOfService );
CFStringAppend( (CFMutableStringRef)modTypeRef, kDotUnderscoreTCPSAFE_CFSTR );
}
CFMutableStringRef serviceKey = CFStringCreateMutable( NULL, 0 );
if ( serviceKey )
{
CFStringAppend( serviceKey, nameOfService );
CFStringAppend( serviceKey, kDotSAFE_CFSTR );
CFStringAppend( serviceKey, (modTypeRef)?modTypeRef:typeOfService );
CFStringAppend( serviceKey, (modDomainRef)?modDomainRef:domainRef );
status = PerformDeregistration( serviceKey );
CFRelease( serviceKey );
}
if ( modTypeRef != NULL )
{
CFRelease( modTypeRef );
modTypeRef = NULL;
}
if ( modDomainRef != NULL )
{
CFRelease( modDomainRef );
modDomainRef = NULL;
}
return status;
}
tDirStatus DNSRegistrationThread::PerformDeregistration( CFStringRef serviceKey )
{
DNSRegData* regData = NULL;
CFNetServiceRef entity = NULL;
tDirStatus status = eDSRecordNotFound;
if ( serviceKey && (regData = (DNSRegData*)::CFDictionaryGetValue( mRegisteredServicesTable, serviceKey )) != NULL )
{
{
char serviceKeyStr[1024] = {0,};
CFStringGetCString( serviceKey, serviceKeyStr, sizeof(serviceKeyStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::PerformDeregistration, deregistering CFNetService: %s (0x%x)\n", serviceKeyStr, regData->fCFNetServiceRef );
}
entity = regData->fCFNetServiceRef;
if ( entity && regData->fCount == 1 )
{
::CFDictionaryRemoveValue( mRegisteredServicesTable, serviceKey );
CFNetServiceUnscheduleFromRunLoop( entity, mRunLoopRef, kCFRunLoopDefaultMode ); if ( !CFNetServiceSetClient( entity, NULL, NULL ) )
syslog( LOG_ERR, "DS Bonjour was unable to unregister a service with CFNetService!\n" );
CFNetServiceCancel( entity );
CFRelease( entity );
free( regData );
status = eDSNoErr;
}
else
{
regData->fCount--; DBGLOG( "DNSRegistrationThread::PerformDeregistration, service is now registered %ld times\n", regData->fCount );
}
}
else if ( serviceKey )
{
char serviceKeyStr[1024] = {0,};
CFStringGetCString( serviceKey, serviceKeyStr, sizeof(serviceKeyStr), kCFStringEncodingUTF8 );
DBGLOG( "DNSRegistrationThread::PerformDeregistration, unable to deregister CFNetService: %s as there no match in our registered services table!\n", serviceKeyStr );
}
return status;
}
boolean_t SystemConfigurationNameChangedCallBack(SCDynamicStoreRef session, void *callback_argument)
{
DNSRegistrationThread* regThread = (DNSRegistrationThread*)callback_argument;
DBGLOG( "SystemConfigurationNameChangedCallBack called\n" );
regThread->PerformDeregistration( regThread->GetOurSpecialRegKey() ); regThread->RegisterHostedServices();
return true;
}
static void RegisterEntityCallBack(CFNetServiceRef theEntity, CFStreamError* error, void* info)
{
if ( error->error )
DBGLOG( "Registration is finished error: (%d, %ld).\n", error->domain, error->error );
else
DBGLOG( "Registration has started\n" );
}
CFStringRef CopyCancelRegDescription( const void* info )
{
DBGLOG( "CopyCancelRegDescription called\n" );
CFNetServiceRef theEntity = (CFNetServiceRef)info;
CFStringRef description = CFNetServiceGetName(theEntity);
CFRetain( description );
return description;
}
CFStringRef CopyRegistrationDescription( const void* info )
{
CFStringRef description = kDNSSCDynamicStoreKeySAFE_CFSTR;
DBGLOG( "CopyRegistrationDescription called\n" );
CFRetain( description );
return description;
}
#define kMaxLengthOfNamePortionOfString 64-sizeof(" [00:00:00:00:00:00]")
CFStringRef CreateComputerNameEthernetString( CFStringRef computerName )
{
CFMutableStringRef modString = NULL;
if ( computerName )
{
char testDNSStringBuf[kMaxLengthOfNamePortionOfString]; CFStringRef macAddress = CreateMacAddressString();
modString = CFStringCreateMutableCopy( NULL, 0, computerName );
while ( CFStringGetLength(modString)>0 && !CFStringGetCString( modString, testDNSStringBuf, sizeof(testDNSStringBuf), kCFStringEncodingUTF8 ) )
{
DBGLOG( "DNSRegistrationThread::CreateComputerNameEthernetString name is too long, need to try trimming...\n" );
CFStringDelete( modString, CFRangeMake(CFStringGetLength(modString)-1, 1) );
}
CFStringAppend( modString, kSpaceLeftBracketSAFE_CFSTR );
if ( macAddress )
CFStringAppend( modString, macAddress );
else
CFStringAppend( modString, kZeroedMACAddressSAFE_CFSTR );
CFStringAppend( modString, kRightBracketSAFE_CFSTR );
if ( macAddress )
CFRelease( macAddress );
if ( getenv( "NSLDEBUG" ) )
{
DBGLOG( "DNSRegistrationThread::CreateComputerNameEthernetString created new composite name\n" );
CFShow( modString );
}
}
return modString;
}
CFStringRef CreateMacAddressString( void )
{
LinkAddresses_t * link_addrs;
char* macAddrCString = NULL;
CFStringRef macAddrStringRef = NULL;
link_addrs = LinkAddresses_create();
if (link_addrs)
{
int i;
for (i = 0; i < link_addrs->count; i++)
{
struct sockaddr_dl * sdl = link_addrs->list[i];
macAddrCString = sockaddr_dl_create_macaddr_string( sdl, "en0" );
if ( macAddrCString )
break;
}
LinkAddresses_free(&link_addrs);
}
if ( macAddrCString )
{
macAddrStringRef = CFStringCreateWithCString( NULL, macAddrCString, kCFStringEncodingUTF8 );
free( macAddrCString );
}
return macAddrStringRef;
}
CFMutableDictionaryRef CreateMutableDictionaryFromXMLString( CFStringRef xmlplist )
{
if ( !xmlplist )
return NULL;
CFDataRef xmlData = NULL;
CFMutableDictionaryRef newDataRef = NULL;
CFStringRef errorString = NULL;
char* rawDataPtr = NULL;
bool rawDataAlloced = false;
rawDataPtr = (char*)CFStringGetCStringPtr( xmlplist, kCFStringEncodingUTF8 );
if ( !rawDataPtr )
{
int bufLen = CFStringGetMaximumSizeForEncoding( CFStringGetLength(xmlplist), kCFStringEncodingUTF8 ) + 1;
rawDataPtr = (char*)malloc( bufLen );
CFStringGetCString( xmlplist, rawDataPtr, bufLen, kCFStringEncodingUTF8 );
rawDataAlloced = true;
}
DBGLOG( "CreateMutableDictionaryFromXMLString called on\n%s\n", rawDataPtr );
xmlData = CFDataCreate(NULL,(UInt8 *)rawDataPtr, strlen(rawDataPtr));
if ( xmlData )
{
newDataRef = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData( NULL,
xmlData,
kCFPropertyListMutableContainersAndLeaves,
&errorString);
if ( errorString )
{
char* errorStr = (char*)malloc(CFStringGetLength( errorString ) + 1);
if ( errorStr )
{
CFStringGetCString( errorString, errorStr, CFStringGetLength( errorString ) + 1, kCFStringEncodingUTF8 );
DBGLOG( "CreateMutableDictionaryFromXMLString got the error [%s] trying to parse the XML: [%s]\n", errorStr, rawDataPtr );
free( errorStr );
}
CFRelease( errorString );
}
else if ( newDataRef && CFGetTypeID(newDataRef) == CFDictionaryGetTypeID() && CFPropertyListIsValid( newDataRef, kCFPropertyListXMLFormat_v1_0 ) )
{
DBGLOG( "CreateMutableDictionaryFromXMLString successfully created newDataRef: 0x%x\n", newDataRef );
}
else
{
DBGLOG( "CreateMutableDictionaryFromXMLString: newDataRef is not a valid dictionary, returning NULL\n" );
CFRelease( newDataRef );
newDataRef = NULL;
}
CFRelease( xmlData );
}
if ( rawDataAlloced )
free( rawDataPtr );
return newDataRef;
}