#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "CNSLHeaders.h"
#include <CoreFoundation/CoreFoundation.h>
#include "mDNSPlugin.h"
#include "DNSBrowserThread.h"
#include "DNSRegistrationThread.h"
#include "mDNSNodeLookupThread.h"
#include "mDNSServiceLookupThread.h"
const CFStringRef gBundleIdentifier = CFSTR("com.apple.DirectoryService.Bonjour");
const char* gProtocolPrefixString = "Bonjour";
#pragma warning "Need to get our default Node String from our resource"
extern "C" {
CFUUIDRef ModuleFactoryUUID = CFUUIDGetConstantUUIDWithBytes ( NULL, \
0x36, 0x33, 0xF4, 0x12, 0xD4, 0x83, 0x11, 0xD5, \
0x88, 0xC9, 0x00, 0x03, 0x93, 0x4F, 0xB0, 0x10 );
}
static CDSServerModule* _Creator ( void )
{
DBGLOG( "Creating new DNS Plugin\n" );
return( new mDNSPlugin );
}
CDSServerModule::tCreator CDSServerModule::sCreator = _Creator;
mDNSPlugin::mDNSPlugin( void )
: CNSLPlugin()
{
DBGLOG( "mDNSPlugin::mDNSPlugin\n" );
mLocalNodeString = NULL;
mTopLevelContainerName = "NULL";
mServiceTypeString = "NULL";
mWorkgroupLookupString = "NULL";
mListClassPath = "NULL";
mLookupThread = NULL;
mRegistrationThread = NULL;
mListOfServicesToRegisterManually = NULL;
mStartedLocalNodeLookups = false;
mStartedNodeLookups = false;
mRegisteredHostedServices = false;
mComputerNameRef = NULL;
mComputerMACAddressNameRef = NULL;
}
mDNSPlugin::~mDNSPlugin( void )
{
DBGLOG( "mDNSPlugin::~mDNSPlugin\n" );
if ( mLocalNodeString )
free( mLocalNodeString );
mLocalNodeString = NULL;
if ( mRegistrationThread )
mRegistrationThread->Cancel();
mRegistrationThread = NULL;
if ( mListOfServicesToRegisterManually )
CFRelease( mListOfServicesToRegisterManually );
mListOfServicesToRegisterManually = NULL;
if ( mComputerNameRef )
CFRelease( mComputerNameRef );
mComputerNameRef = NULL;
if ( mComputerMACAddressNameRef )
CFRelease( mComputerMACAddressNameRef );
mComputerMACAddressNameRef = NULL;
}
sInt32 mDNSPlugin::InitPlugin( void )
{
sInt32 siResult = eDSNoErr;
DBGLOG( "mDNSPlugin::InitPlugin\n" );
return siResult;
}
sInt32 mDNSPlugin::SetServerIdleRunLoopRef( CFRunLoopRef idleRunLoopRef )
{
sInt32 siResult = 0;
CNSLPlugin::SetServerIdleRunLoopRef( idleRunLoopRef );
if ( !mLookupThread )
{
mLookupThread = new DNSBrowserThread( this );
if ( mLookupThread )
{
mLookupThread->Initialize( idleRunLoopRef );
}
}
if ( !mRegistrationThread )
{
mRegistrationThread= new DNSRegistrationThread( this );
if ( mRegistrationThread )
{
mRegistrationThread->Initialize( idleRunLoopRef );
}
}
return siResult;
}
void mDNSPlugin::ActivateSelf( void )
{
CNSLPlugin::ActivateSelf();
AddNode( kLocalSAFE_CFSTR, true );
CFStringEncoding encoding;
mComputerNameRef = SCDynamicStoreCopyComputerName(NULL, &encoding);
mComputerMACAddressNameRef = CreateComputerNameEthernetString(mComputerNameRef);
sHeader header = {kHandleNetworkTransition, 0, NULL};
DBGLOG( "mDNSPlugin::ActivateSelf, calling HandleRequest with a kHandleNetworkTransition event\n" );
HandleRequest( &header );
}
CFStringRef mDNSPlugin::GetBundleIdentifier( void )
{
return gBundleIdentifier;
}
const char* mDNSPlugin::GetProtocolPrefixString( void )
{
return gProtocolPrefixString;
}
const char* mDNSPlugin::GetLocalNodeString( void )
{
if ( !mLocalNodeString )
DBGLOG( "mDNSPlugin::GetLocalNodeString, mLocalNodeString not found yet\n" );
return mLocalNodeString;
}
Boolean mDNSPlugin::IsLocalNode( const char *inNode )
{
Boolean result = false;
DBGLOG( "mDNSPlugin::IsLocalNode called checking %s\n", inNode );
if ( mLocalNodeString )
{
result = ( strcmp( inNode, mLocalNodeString ) == 0 );
DBGLOG( "mDNSPlugin::IsLocalNode result:%d (strcmp(%s,%s))\n", result, inNode, mLocalNodeString );
}
else
result = ( strcmp( inNode, "local" ) == 0 );
return result;
}
void mDNSPlugin::NewNodeLookup( void )
{
if ( (!mStartedNodeLookups || !mStartedLocalNodeLookups) && mLookupThread ) {
DBGLOG( "mDNSPlugin::NewNodeLookup - start a new node lookup\n" );
sHeader header = {kHandleNetworkTransition, 0, NULL};
DBGLOG( "mDNSPlugin::NewNodeLookup, calling HandleRequest with a kHandleNetworkTransition event\n" );
HandleRequest( &header );
}
else
DBGLOG( "mDNSPlugin::NewNodeLookup - ignore new node lookup, Bonjour will notifiy us of any changes\n" );
}
Boolean mDNSPlugin::OKToOpenUnPublishedNode ( const char* parentNodeName )
{
return true;
}
void mDNSPlugin::NewSubNodeLookup( char* parentNodeName )
{
DBGLOG( "mDNSPlugin::NewSubNodeLookup in %s\n", parentNodeName );
}
void mDNSPlugin::NewServiceLookup( char* serviceType, CNSLDirNodeRep* nodeDirRep )
{
DBGLOG( "mDNSPlugin::NewServicesLookup\n" );
mDNSServiceLookupThread* newLookup = new mDNSServiceLookupThread( this, serviceType, nodeDirRep );
if ( OKToStartNewSearch() )
newLookup->Resume();
else
QueueNewSearch( newLookup );
}
sInt32 mDNSPlugin::HandleNetworkTransition( sHeader *inData )
{
sInt32 siResult = eDSNoErr;
DBGLOG( "mDNSPlugin::HandleNetworkTransition called\n" );
if ( IsActive() )
{
if ( mActivatedByNSL && !mStartedLocalNodeLookups && mLookupThread ) {
DBGLOG( "mDNSPlugin::HandleNetworkTransition calling StartNodeLookups for default nodes\n" );
mStartedLocalNodeLookups = true;
if ( mLookupThread->StartNodeLookups( true ) != eDSNoErr ) {
DBGLOG( "mDNSPlugin::HandleNetworkTransition StartNodeLookups failed, reset to try again later\n" );
mStartedLocalNodeLookups = false;
ResetNodeLookupTimer( 5 ); }
}
if ( mActivatedByNSL && !mStartedNodeLookups && mLookupThread ) {
DBGLOG( "mDNSPlugin::HandleNetworkTransition calling StartNodeLookups for non-default nodes\n" );
mStartedNodeLookups = true;
if ( mLookupThread->StartNodeLookups( false ) != eDSNoErr ) {
DBGLOG( "mDNSPlugin::HandleNetworkTransition StartNodeLookups failed, reset to try again later\n" );
mStartedNodeLookups = false;
ResetNodeLookupTimer( 5 ); }
}
if ( !mRegisteredHostedServices && mRegistrationThread )
{
DBGLOG( "mDNSPlugin::HandleNetworkTransition calling RegisterHostedServices\n" );
mRegisteredHostedServices = true;
if ( mRegistrationThread->RegisterHostedServices() != eDSNoErr )
{
DBGLOG( "mDNSPlugin::HandleNetworkTransition RegisterHostedServices failed, reset to try again later\n" );
mRegisteredHostedServices = false; ResetNodeLookupTimer( 5 ); }
}
}
else
DBGLOG( "mDNSPlugin::HandleNetworkTransition IsActive is false, ignoring notification\n" );
return ( siResult );
}
Boolean mDNSPlugin::IsClientAuthorizedToCreateRecords ( sCreateRecord *inData )
{
const void* dictionaryResult = NULL;
Boolean okToCreate = false;
if( ::CFDictionaryGetValueIfPresent( mOpenRefTable, (const void*)inData->fInNodeRef, &dictionaryResult ) )
{
if ( ::CFStringCompare( ((CNSLDirNodeRep*)dictionaryResult)->GetNodeName(), kEmptySAFE_CFSTR, 0 ) == kCFCompareEqualTo || ::CFStringCompare( ((CNSLDirNodeRep*)dictionaryResult)->GetNodeName(), kLocalSAFE_CFSTR, 0 ) == kCFCompareEqualTo || ::CFStringCompare( ((CNSLDirNodeRep*)dictionaryResult)->GetNodeName(), kLocalDotSAFE_CFSTR, 0 ) == kCFCompareEqualTo )
{
okToCreate = true; DBGLOG( "mDNSPlugin::IsClientAuthorizedToCreateRecords returning true because client is registering in local or default.\n" );
}
else if ( ((CNSLDirNodeRep*)dictionaryResult)->GetUID() == 0 )
{
okToCreate = true; DBGLOG( "mDNSPlugin::IsClientAuthorizedToCreateRecords returning true because client is root\n" );
}
}
else
DBGLOG( "mDNSPlugin::IsClientAuthorizedToCreateRecords called but we couldn't find the nodeDirRep!\n" );
return okToCreate;
}
char* mDNSPlugin::CreateNSLTypeFromRecType ( char *inRecType, Boolean* needToFreeRecType )
{
char *outResult = nil;
uInt32 uiStrLen = 0;
uInt32 uiStdLen = ::strlen( kDSStdRecordTypePrefix );
DBGLOG( "mDNSPlugin::CreateNSLTypeFromRecType called on %s\n", inRecType );
*needToFreeRecType = false;
if ( ( inRecType != nil ) )
{
uiStrLen = ::strlen( inRecType );
if ( ::strncmp( inRecType, kDSStdRecordTypePrefix, uiStdLen ) == 0 )
{
DBGLOG( "mDNSPlugin::CreateNSLTypeFromRecType kDSStdRecordTypePrefix, uiStrLen:%ld uiStdLen:%ld\n", uiStrLen, uiStdLen );
if ( strcmp( inRecType, kDSStdRecordTypeAFPServer ) == 0 )
{
outResult = kAFPoverTCPServiceType;
DBGLOG( "mDNSPlugin::CreateNSLTypeFromRecType mapping %s to %s\n", inRecType, outResult );
}
}
}
if ( !outResult )
outResult = CNSLPlugin::CreateNSLTypeFromRecType( inRecType, needToFreeRecType );
return( outResult );
}
CFStringRef mDNSPlugin::CreateRecTypeFromNativeType ( char *inNativeType )
{
CFMutableStringRef outResultRef = NULL;
DBGLOG( "mDNSPlugin::CreateRecTypeFromNativeType called on %s\n", inNativeType );
if ( ( inNativeType != nil ) )
{
if ( ::strcmp( inNativeType, kAFPoverTCPServiceType ) == 0 )
{
outResultRef = CFStringCreateMutable( NULL, 0 );
CFStringAppend( outResultRef, kDSStdRecordTypeAFPServerSAFE_CFSTR );
}
}
if ( !outResultRef )
outResultRef = (CFMutableStringRef)CNSLPlugin::CreateRecTypeFromNativeType( inNativeType );
return( outResultRef );
}
#pragma mark -
sInt32 mDNSPlugin::RegisterService( tRecordReference recordRef, CFDictionaryRef service )
{
sInt32 status = eDSNoErr;
CFStringRef nameOfService = NULL;
CFStringRef typeOfService = NULL;
CFStringRef locationOfService = NULL;
CFStringRef protocolSpecificData = NULL;
CFStringRef portOfService = NULL;
if ( service )
{
nameOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordNameSAFE_CFSTR );
if ( !nameOfService )
nameOfService = kEmptySAFE_CFSTR;
locationOfService = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrLocationSAFE_CFSTR );
if ( !locationOfService )
{
DBGLOG( "mDNSPlugin::RegisterService, no location specified, use \"\"\n" );
locationOfService = kEmptySAFE_CFSTR; }
else
if ( CFStringCompare( locationOfService, CFSTR("local"), 0 ) == kCFCompareEqualTo )
{
DBGLOG( "mDNSPlugin::RegisterService, registering in local, use \"\"\n" );
locationOfService = kEmptySAFE_CFSTR; }
else
if ( DEBUGGING_NSL )
{
char* location = (char*)malloc( CFStringGetMaximumSizeForEncoding( CFStringGetLength(locationOfService), kCFStringEncodingUTF8 ) + 1 );
if ( location )
{
CFStringGetCString( locationOfService, location, CFStringGetMaximumSizeForEncoding( CFStringGetLength(locationOfService), kCFStringEncodingUTF8 ) + 1, kCFStringEncodingUTF8 );
DBGLOG("mDNSPlugin::RegisterService using specified location [%s]\n", location );
}
}
typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrServiceTypeSAFE_CFSTR );
if ( !typeOfService )
typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordTypeSAFE_CFSTR );
protocolSpecificData = (CFStringRef)::CFDictionaryGetValue( service, kDNSTextRecordSAFE_CFSTR );
portOfService = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrPtrSAFE_CFSTR );
if ( nameOfService && locationOfService && typeOfService && portOfService )
{
if ( !mRegistrationThread )
{
char name[1024];
char type[256];
CFStringGetCString( nameOfService, name, sizeof(name), kCFStringEncodingUTF8 );
CFStringGetCString( typeOfService, type, sizeof(type), kCFStringEncodingUTF8 );
syslog( LOG_ERR, "DS Bonjour couldn't register %s (%s) since our Registration Thread hasn't been created!\n", name, type );
status = ePlugInError;
}
if ( mRegistrationThread )
{
CFStringRef serviceKey = NULL;
status = mRegistrationThread->PerformRegistration( nameOfService, typeOfService, locationOfService, protocolSpecificData, portOfService, &serviceKey );
if ( serviceKey )
CFRelease( serviceKey );
}
}
else
{
DBGLOG( "mDNSPlugin::RegisterService, we didn't receive all the valid registration parameters\n" );
status = eDSNullAttribute;
}
}
else
status = eDSNullAttribute;
return status;
}
sInt32 mDNSPlugin::DeregisterService( tRecordReference recordRef, CFDictionaryRef service )
{
sInt32 status = eDSNoErr;
if ( mRegistrationThread )
{
DBGLOG( "mDNSPlugin::DeregisterService, calling PerformDeregistraton with the mRegistrationThread\n" );
status = mRegistrationThread->PerformDeregistration( service );
}
else
{
char name[1024] = {0,};
char type[256] = {0,};
CFStringRef nameOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordNameSAFE_CFSTR );
if ( nameOfService )
CFStringGetCString( nameOfService, name, sizeof(name), kCFStringEncodingUTF8 );
CFStringRef typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrServiceTypeSAFE_CFSTR );
if ( !typeOfService )
typeOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordTypeSAFE_CFSTR );
if ( typeOfService )
CFStringGetCString( typeOfService, type, sizeof(type), kCFStringEncodingUTF8 );
syslog( LOG_ERR, "DS Bonjour couldn't deregister %s (%s) since our Registration Thread hasn't been created!\n", name, type );
status = ePlugInError;
}
return status;
}