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 )
{
char mode = 'A';
*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 ) )
{
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
{
DBGLOG("DNSRegistrationThread::PerformRegistration, port is %ld\n", port );
if ( GetParentPlugin()->GetComputerNameString() && CFStringCompare( GetParentPlugin()->GetComputerNameString(), nameRef, 0 ) == kCFCompareEqualTo )
nameRef = kEmptySAFE_CFSTR;
entity = CFNetServiceCreate(NULL, (modDomainRef)?modDomainRef:domainRef, (modTypeRef)?modTypeRef:typeRef, nameRef, port);
if ( protocolSpecificData )
{
CFNetServiceSetProtocolSpecificInformation( entity, protocolSpecificData );
}
mode = 'A';
{
CFNetServiceClientContext c = {0, NULL, NULL, NULL, CopyRegistrationDescription};
if ( !CFNetServiceSetClient( entity, RegisterEntityCallBack, &c) )
syslog( LOG_ERR, "DS Rendezvous was unable to register a service with CFNetService!\n" );
CFNetServiceScheduleWithRunLoop(entity, mRunLoopRef, kCFRunLoopDefaultMode);
}
if (CFNetServiceRegister(entity, &error))
{
CFRunLoopWakeUp( mRunLoopRef );
DBGLOG("CFNetServiceRegister returned TRUE!\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;
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 Rendezvous 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 );
}
}
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)
{
DBGLOG( "Registration is finished error: (%d, %ld).\n", error->domain, error->error);
}
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;
}
CFStringRef CreateComputerNameEthernetString( CFStringRef computerName )
{
CFMutableStringRef modString = NULL;
if ( computerName )
{
CFStringRef macAddress = CreateMacAddressString();
modString = CFStringCreateMutableCopy( NULL, 0, computerName );
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;
}