/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * DNSRegistrationThread.cpp * DSNSLPlugins * * Created by Kevin Arnold on Tue Mar 19 2002. * Copyright (c) 2002 Apple Computer. All rights reserved. * */ #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 ) // : DSLThread() { 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 ) { /* if ( mRunLoopRef ) CFRunLoopStop( mRunLoopRef ); */ mCanceled = true; } void DNSRegistrationThread::Initialize( CFRunLoopRef idleRunLoopRef ) { // use these for the reftable dictionary CFDictionaryKeyCallBacks keyCallBack; CFDictionaryValueCallBacks valueCallBack; keyCallBack.version = 0; keyCallBack.retain = NULL; keyCallBack.release = NULL; keyCallBack.copyDescription = NULL; keyCallBack.equal = NULL; keyCallBack.hash = NULL; // this is fine 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); // name changes DBGLOG( "RegisterForNetworkChange for SCDynamicStoreKeyCreateComputerName:\n" ); key = SCDynamicStoreKeyCreateComputerName(NULL); CFArrayAppendValue(notifyKeys, key); CFRelease(key); /* DBGLOG( "RegisterForNetworkChange for SCDynamicStoreKeyCreateHostNames:\n" ); key = SCDynamicStoreKeyCreateHostNames(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" ); } // we want to set this up so that we will keep some special services registered (e.g. MacManager's workstation // and we want to also keep track of the machine name since we'll want to keep this changing its registration when // the machine changes. 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" ); // we need to pass fully qualified domains (i.e. local. not local) modDomainRef = CFStringCreateMutableCopy( NULL, 0, domainRef ); CFStringAppendCString( (CFMutableStringRef)modDomainRef, ".", kCFStringEncodingUTF8 ); } if ( !CFStringHasSuffix( typeRef, kDotUnderscoreTCPSAFE_CFSTR ) ) { // need to convert this to the appropriate DNS style. I.E. _afp._tcp. not afp 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 ) { // we are just going to make the key be the name.type.port.location 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++; // we will just bump the counter, if we don't do this, then multiple registrations be ignored // and the first deregistration will deregister all previous registrations 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; // use default 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; /* Service's name (must be unique per domain (should be UTF8) */ CFStringRef typeOfService = NULL; /* Service Type (i.e. afp, lpr etc) */ nameOfService = (CFStringRef)::CFDictionaryGetValue( service, kDSNAttrRecordNameSAFE_CFSTR ); if ( !nameOfService ) nameOfService = kEmptySAFE_CFSTR; // just deregister Copmuter Name domainRef = (CFStringRef)::CFDictionaryGetValue( service, kDS1AttrLocationSAFE_CFSTR ); if ( !domainRef ) domainRef = kEmptySAFE_CFSTR; // just deregister local else if ( CFStringCompare( domainRef, CFSTR("local"), 0 ) == kCFCompareEqualTo ) { DBGLOG( "DNSRegistrationThread::PerformDeregistration, deregistering in local, use \"\"\n" ); domainRef = kEmptySAFE_CFSTR; // just deregister local } 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 ) ) { // we need to pass fully qualified domains (i.e. local. not local) modDomainRef = CFStringCreateMutableCopy( NULL, 0, domainRef ); CFStringAppendCString( (CFMutableStringRef)modDomainRef, ".", kCFStringEncodingUTF8 ); } if ( !CFStringHasSuffix( typeOfService, kDotUnderscoreTCPSAFE_CFSTR ) ) { // need to convert this to the appropriate DNS style. I.E. _afp._tcp. not afp modTypeRef = CFStringCreateMutableCopy( NULL, 0, kUnderscoreSAFE_CFSTR ); CFStringAppend( (CFMutableStringRef)modTypeRef, typeOfService ); CFStringAppend( (CFMutableStringRef)modTypeRef, kDotUnderscoreTCPSAFE_CFSTR ); } CFMutableStringRef serviceKey = CFStringCreateMutable( NULL, 0 ); if ( serviceKey ) { // we are just going to make the key be the name.type.port.location 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 ); // need to unschedule from run loop 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--; // decrement this 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() ); // deregister old service, since the name isn't valid regThread->RegisterHostedServices(); // register with current name 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]; // we need to test the actual string 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; }