CFNetServices.c   [plain text]


/*
 * Copyright (c) 2005 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@
 */
/*
 *  CFNetServices.c
 *  CFNetwork
 *
 *  Created by Jeremy Wyld on Wed Feb 06 2002.
 *  Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 *
 */

#if 0
#pragma mark Includes
#endif
#include <CFNetwork/CFNetwork.h>
#include <CFNetwork/CFNetworkPriv.h>
#include "CFNetworkInternal.h"			/* for __CFSpinLock and __CFSpinUnlock */
#include "CFNetworkSchedule.h"

#include "DeprecatedDNSServiceDiscovery.h"
#include <SystemConfiguration/SystemConfiguration.h>

#include <dns_sd.h>
#include <nameser.h>


#if 0
#pragma mark -
#pragma mark Constants
#endif

/* extern */ const SInt32 kCFStreamErrorDomainNetServices = 10;
/* extern */ const SInt32 kCFStreamErrorDomainMach = 11;

/* Timeout for the short timer */
#define kCFNetServiceShortTimeout ((CFTimeInterval)0.25)

#define _kCFNetServiceDomain 0x00000000UL
#define _kCFNetServiceType 0x00000004UL
#define _kCFNetServiceName 0x00000002UL
#define _kCFNetServiceAddress 0x00000003UL
#define _kCFNetServiceTXT 0x00000001UL
#define _kCFNetServiceTargetHost 0x00000005UL


#if 0
#pragma mark -
#pragma mark Constant Strings
#endif

#ifdef __CONSTANT_CFSTRINGS__
#define _kCFNetServiceBlockingMode			CFSTR("_kCFNetServiceBlockingMode")
#define _kCFNetServiceEmptyString			CFSTR("")
#define _kCFNetServiceDebugFormatString		CFSTR("<CFNetService 0x%x>{domain=%@, type=%@, name=%@, specific=%@, addresses=%@}")
#else
static CONST_STRING_DECL(_kCFNetServiceBlockingMode, "_kCFNetServiceBlockingMode")
static CONST_STRING_DECL(_kCFNetServiceEmptyString, "")
static CONST_STRING_DECL(_kCFNetServiceDebugFormatString, "<CFNetService 0x%x>{domain=%@, type=%@, name=%@, specific=%@, addresses=%@}")
#endif	/* __CONSTANT_CFSTRINGS__ */

static const char _kCFNetServiceClassName[] = "CFNetService";


#if 0
#pragma mark -
#pragma mark Enum Values
#endif

enum {
    /* __CFNetService flags */
	kFlagBitLegacyService   = 0,
	kFlagBitAComplete,
	kFlagBitAAAAComplete,
	kFlagBitAReceived,
	kFlagBitAAAAReceived,
	kFlagBitActiveResolve,
	kFlagBitActiveRegister,
	kFlagBitCancel
};


#if 0
#pragma mark -
#pragma mark CFNetService struct
#endif

typedef struct {

	CFRuntimeBase						_base;
	
	CFSpinLock_t						_lock;
	
	UInt32								_flags;

	CFStreamError						_error;

	CFMutableDictionaryRef				_info;
	UInt32								_port;			/* Saved here for now.  Could be made a CFType and placed in info. */

	CFMutableArrayRef					_sources;		/* List of different things being performed */
	UInt32								_interface;
	
	union {
		dns_service_discovery_ref		_old_service;
		DNSServiceRef					_new_service;
	};
	
	DNSServiceRef						_a;
	DNSServiceRef						_aaaa;
	
	CFMutableDictionaryRef				_records;		/* Published records (DNSRecordRef's) */

	CFMutableArrayRef					_schedules;		/* List of loops and modes */
	CFNetServiceClientCallBack			_callback;
	CFNetServiceClientContext			_client;
} __CFNetService;


#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif

/*
** Exported for CFNetServiceBrowser so that it can create items found on the wire
** without using normalization.
*/
extern CFNetServiceRef _CFNetServiceCreateCommon(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port);
	
extern dns_service_discovery_ref _CFNetServiceGetDNSServiceDiscovery(CFNetServiceRef theService);

/*
** Exported for CFNetServiceMonitor so that it can keep the service up-to-date
** with monitored records.  As it sees the updated records, it will update them
** on the service.  If the service happens to be a registration object, the
** SetInfo call should not force another update on the wire, thus the "no update."
*/
extern Boolean _CFNetServiceSetInfoNoPublish(CFNetServiceRef theService, UInt32 property, CFTypeRef value);


#if 0
#pragma mark -
#pragma mark Static Function Declarations
#endif

static void _CFNetServiceRegisterClass(void);

static void _ServiceDestroy(__CFNetService* service);
static Boolean _ServiceEqual(__CFNetService* s1, __CFNetService* s2);
static CFHashCode _ServiceHash(__CFNetService* service);
static CFStringRef _ServiceDescribe(__CFNetService* service);

static Boolean _ServiceSetInfo(__CFNetService* service, UInt32 property, CFTypeRef value, Boolean publish);
static void _ServiceCancel(__CFNetService* service);
static void _ServiceCancelDNSService_NoLock(__CFNetService* service, DNSServiceRef cancel);
static void _ServiceCreateQuery_NoLock(__CFNetService* service, ns_type rrtype, const char* name,
									   const char* regtype, const char* domain, Boolean schedule);
static Boolean _ServiceBlockUntilComplete(__CFNetService* service);
static void _MachPortCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info);
static void _LegacyRegistrationReply(int error, void* context);
static void _LegacyResolverReply(struct sockaddr* interface, struct sockaddr* address, const char* txtRecord,
								 DNSServiceDiscoveryReplyFlags flags, void* context);
static void _RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
						   const char* name, const char* regtype, const char* domain, void* context);
static void _ResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
						  DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget,
						  uint16_t port, uint16_t txtLen, const char* txtRecord, void* context);
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void _AQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void _AAAAQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void _LongTimerCallBack(CFRunLoopTimerRef timer, void *context);
static void _ShortTimerCallBack(CFRunLoopTimerRef timer, void *context);
static void _AddressQueryRecordReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
									 DNSServiceErrorType errorCode, const char* fullname, uint16_t rrtype,
									 uint16_t rrclass, uint16_t rdlen, const void* rdata, uint32_t ttl, void* context);

static CFDataRef _CFDataCreateWithRecord(CFAllocatorRef allocator, uint16_t rrtype, uint16_t rdlen,
										 const void* rdata, u_short port, uint32_t interfaceIndex);
static void _AddRecords(const void *key, const void *value, void *context);
static void _DictionaryApplier(const void *key, const void *value, void *context);
static void _ScheduleSources(CFArrayRef sources, CFArrayRef schedules);
static void _UnscheduleSources(CFArrayRef sources, CFArrayRef schedules);
static void _InvalidateSources(CFMutableArrayRef sources);

static const void* TXTDictionaryKeyRetain(CFAllocatorRef allocator, CFStringRef key);
static void TXTDictionaryKeyRelease(CFAllocatorRef allocator, CFStringRef key);
static Boolean TXTDictionaryKeyEqual(CFStringRef key1, CFStringRef key2);
static CFHashCode TXTDictionaryKeyHash(CFStringRef key);


#if 0
#pragma mark -
#pragma mark Globals
#endif

static _CFOnceLock _kCFNetServiceRegisterClass = _CFOnceInitializer;
static CFTypeID _kCFNetServiceTypeID = _kCFRuntimeNotATypeID;
static CFRuntimeClass* _kCFNetServiceClass = NULL;


#if 0
#pragma mark -
#pragma mark Static Function Definitions
#endif

/* static */ void
_CFNetServiceRegisterClass(void) {
	
	_kCFNetServiceClass = (CFRuntimeClass*)calloc(1, sizeof(_kCFNetServiceClass[0]));
	
	if (_kCFNetServiceClass) {
		
		_kCFNetServiceClass->version = 0;
		_kCFNetServiceClass->className = _kCFNetServiceClassName;
		_kCFNetServiceClass->finalize = (void(*)(CFTypeRef))_ServiceDestroy;
		_kCFNetServiceClass->equal = (Boolean(*)(CFTypeRef, CFTypeRef))_ServiceEqual;
		_kCFNetServiceClass->hash = (CFHashCode(*)(CFTypeRef))_ServiceHash;
		_kCFNetServiceClass->copyDebugDesc = (CFStringRef(*)(CFTypeRef cf))_ServiceDescribe;
		
		_kCFNetServiceTypeID = _CFRuntimeRegisterClass(_kCFNetServiceClass);
	}
}


#if 0
#pragma mark * Service Methods
#endif



/* static */ void
_ServiceDestroy(__CFNetService* service) {
	
	/* Prevent anything else from taking hold */
	__CFSpinLock(&(service->_lock));
	
	/* Release the user's context info if there is some and a release method */
	if (service->_client.info && service->_client.release)
		service->_client.release(service->_client.info);
	
	/* Cancel the outstanding sources */
	if (service->_sources) {
		
		/* Remove the source from run loops and modes */
		if (service->_schedules)
			_UnscheduleSources(service->_sources, service->_schedules);
		
		/* Go ahead and invalidate the sources */
		_InvalidateSources(service->_sources);
		
		/* Release the sources now. */
		CFRelease(service->_sources);
	}
	
	/* Different way to clean up based upon whether it's legacy or not. */
	if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
		
		/* Need to clean up the service discovery stuff if there is */
		if (service->_old_service) {
			
			/* Release the underlying service discovery reference */
			DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
		}
	}
	else {
		
		/* Need to clean up the service discovery stuff if there is */
		if (service->_new_service) {

			/* Release the underlying service discovery reference */
			DNSServiceRefDeallocate(service->_new_service);
		}
	}
	
	/* Clean up A record lookup if there is one */
	if (service->_a)
		DNSServiceRefDeallocate(service->_a);
	
	/* Clean up AAAA record lookup if there is one */
	if (service->_aaaa)
		DNSServiceRefDeallocate(service->_aaaa);
	
	/* Dump all the records that may have been published */
	if (service->_records)
		CFRelease(service->_records);
	
	/* Release any gathered information */
	if (service->_info)
		CFRelease(service->_info);

	/* Release the list of loops and modes */
	if (service->_schedules)
		CFRelease(service->_schedules);
}


/* static */ Boolean
_ServiceEqual(__CFNetService* s1, __CFNetService* s2) {
	
	Boolean result = FALSE;
	CFStringRef t1, t2;

	/*
    ** Two services which have different address are not caught here, since
    ** performing CFEqual on arrays requires order.  Passing on this now
    ** and only fix if there are bugs with this comparison.  **FIXME**
	*/
	
	/* Lock the services */
	__CFSpinLock(&s1->_lock);
	__CFSpinLock(&s2->_lock);
	
	/* Get the types for comparison */
	t1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceType);
	t2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceType);
	
	/* Can't be equal if the types aren't the same. */
	if (CFEqual(t1, t2)) {
		
		/* Get the domains for comparison */
		CFStringRef d1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceDomain);
		CFStringRef d2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceDomain);
	
		/* If either is the emtpy string need to shove in "local." */
		if (CFEqual(d1, _kCFNetServiceEmptyString))
			d1 = _kCFNetServiceEmptyString;
			
		if (CFEqual(d2, _kCFNetServiceEmptyString))
			d2 = _kCFNetServiceEmptyString;
		
		/* No need to do the name check unless the domains are the same */
		if (CFEqual(d1, d2)) {
		
			/* Get the names for comparison. */
			CFStringRef n1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceName);
			CFStringRef n2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceName);
			
			/* If the names are the same, they are equal. */
			if (CFEqual(n1, n2))
				result = TRUE;
				
			/* If there is an empty string, need to fill in the "default" name for the computer */
			else if (CFEqual(n1, _kCFNetServiceEmptyString) || CFEqual(n2, _kCFNetServiceEmptyString)) {
			
				/* Get the computer name */
				CFStringRef computer_name = SCDynamicStoreCopyLocalHostName(NULL);
				if (computer_name) {
					
					/* Set the default name for any that have no name */
					if (CFEqual(n1, _kCFNetServiceEmptyString))
						n1 = computer_name;
					
					if (CFEqual(n2, _kCFNetServiceEmptyString))
						n2 = computer_name;
						
					/* If the names are the same, they are equal */
					if (CFEqual(n1, n2))
						result = TRUE;
						
					CFRelease(computer_name);
				}
			}
		}
	}
	

	/* Unlock the services */
	__CFSpinUnlock(&s1->_lock);
	__CFSpinUnlock(&s2->_lock);

	return result;
}


/* static */ CFHashCode
_ServiceHash(__CFNetService* service) {
	
	CFHashCode result;
	CFStringRef name;
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* Get the hash for the name on the service */
	name = CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceName);
	result = CFHash(name);
	
	/* If it's an empty name, need to go for the default */
	if (CFEqual(name, _kCFNetServiceEmptyString)) {
		
		/* Get the default name */
		name = SCDynamicStoreCopyLocalHostName(NULL);
		
		/* If got a name, create the hash from it. */
		if (name) {
			CFHash(name);
			CFRelease(name);
		}
	}

	/* Unlock the service */
	__CFSpinUnlock(&service->_lock);
	
	return result;
}


/* static */ CFStringRef
_ServiceDescribe(__CFNetService* service) {

	CFStringRef result = NULL;
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	result = CFStringCreateWithFormat(CFGetAllocator((CFNetServiceRef)service),
									  NULL,
									  _kCFNetServiceDebugFormatString,
									  service,
									  CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceDomain),
									  CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceType),
									  CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceName),
									  CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceTXT),
									  CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress));

	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);

	return result;
}


/* static */ Boolean
_ServiceSetInfo(__CFNetService* service, UInt32 property, CFTypeRef value, Boolean publish) {
	
	Boolean result = FALSE;
	
	__CFSpinLock(&(service->_lock));
	
	/* Don't allow setting on a legacy service or on a resolve. */
	if (!__CFBitIsSet(service->_flags, kFlagBitLegacyService) &&
		!__CFBitIsSet(service->_flags, kFlagBitActiveResolve))
	{
		/* Save the value */
		if (value)
			CFDictionarySetValue(service->_info, (const void*)property, value);
		else
			CFDictionaryRemoveValue(service->_info, (const void*)property);
		
		/* Assume success */
		result = TRUE;
		
		/* Send it to the wire if actively registered */
		if (publish && service->_new_service) {
			
			DNSServiceErrorType err = 0;
			
			/* Just update the primary txt record when given TXT property. */
			if (property == _kCFNetServiceTXT) {
				
				err = DNSServiceUpdateRecord(service->_new_service,
											 NULL,
											 0,
											 value ? CFDataGetLength(value) : 0,
											 value ? CFDataGetBytePtr(value) : NULL,
											 0);
				
				if (err)
					result = FALSE;
			}
			
			/* High word is the class for the record type.  Only support Internet. */
			else if ((0xFFFF0000 & property) == 0x00010000) {
				
				/* Get the existing published record. */
				DNSRecordRef record = (DNSRecordRef)CFDictionaryGetValue(service->_records, (const void*)property);
				
				/* No value indicates to remove the record. */
				if (!value) {
					if (record)
						err = DNSServiceRemoveRecord(service->_new_service, record, 0);
					CFDictionaryRemoveValue(service->_records, (const void*)property);
				}
				
				/* If it exists, only need to update. */
				else if (record) {
					err = DNSServiceUpdateRecord(service->_new_service,
												 record,
												 0,
												 CFDataGetLength(value),
												 CFDataGetBytePtr(value),
												 0);
				}
				
				/* Not an update, but an add. */
				else {
					err = DNSServiceAddRecord(service->_new_service,
											  &record,
											  0,
											  (0x0000FFFF & property),
											  CFDataGetLength(value),
											  CFDataGetBytePtr(value),
											  0);
					
					CFDictionaryAddValue(service->_records, (const void*)property, record);
				}
				
				/* If there was an error, remove the published record. */
				if (err) {
					
					if (record)
						DNSServiceRemoveRecord(service->_new_service, record, 0);
					
					CFDictionaryRemoveValue(service->_records, (const void*)property);
					
					result = FALSE;
				}
			}
		}
	}
	
	__CFSpinUnlock(&(service->_lock));
	
	return result;
}


/* static */ void
_ServiceCancel(__CFNetService* service) {
	
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	 *  Retain here to guarantee safety really after the service release,
	 *  but definitely before the callback.
	 */
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* If canceled, don't need to do any of this. */
	if (CFArrayGetCount(service->_sources)) {
		
		/* Save the callback if there is one at this time. */
		cb = service->_callback;
		
		/* Save the error and client information for the callback */
		memmove(&error, &(service->_error), sizeof(error));
		info = service->_client.info;
		
		/* Remove the sources from run loops and modes */
		_UnscheduleSources(service->_sources, service->_schedules);
		
		/* Invalidate the run loop source that got here */
		_InvalidateSources(service->_sources);
	}

	/* No longer cancel */
	__CFBitClear(service->_flags, kFlagBitCancel);
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client of the finish. */
	if (cb)
		cb((CFNetServiceRef)service, &error, info);
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


/* static */ void
_ServiceCancelDNSService_NoLock(__CFNetService* service, DNSServiceRef cancel) {
	
	CFTypeID t = CFSocketGetTypeID();
	int fd = DNSServiceRefSockFD(cancel);
	int i, count = CFArrayGetCount(service->_sources);
	
	/* Loop through the sources trying to find the socket associated with the cancel request */
	for (i = 0; i < count; i++) {
	
		/* Get the CF object at the current location */
		CFTypeRef sock = CFArrayGetValueAtIndex(service->_sources, i);
		
		/* Make sure it's a socket for continuing */
		if (t == CFGetTypeID(sock)) {
			
			/* If the native socket is the same as the mdns socket, need to kill it */
			if (CFSocketGetNative((CFSocketRef)sock) == fd) {
				
				/* First remove it from the run loops */
				_CFTypeUnscheduleFromMultipleRunLoops(sock, service->_schedules);
				
				/* Invalidate it */
				_CFTypeInvalidate(sock);
				
				/* Kill the mdns portion */
				DNSServiceRefDeallocate(cancel);
				
				/* Remove the sources from the list of sources */
				CFArrayRemoveValueAtIndex(service->_sources, i);
				
				/* Bail now */
				break;
			}
		}
	}
}


/* static */ void
_ServiceCreateQuery_NoLock(__CFNetService* service, ns_type rrtype, const char* name,
						   const char* regtype, const char* domain, Boolean schedule)
{
	DNSServiceRef* which = NULL;
	CFSocketCallBack cb = _SocketCallBack;
	
	/* ns_t_invalid indicates to do a regular resolve */
	if (rrtype == ns_t_invalid) {
		
		/* Which ivar is being used for service creation */
		which = &(service->_new_service);
		
		/* Start on no special interface.  Resolve against all interfaces. */
		service->_interface = 0;
		
		/* Create the resolve */
		service->_error.error = DNSServiceResolve(which,
												  0,
												  service->_interface,
												  name,
												  regtype,
												  domain,
												  _ResolveReply,
												  service);
	}
	
	/* Some other type of query */
	else {
		
		DNSServiceQueryRecordReply reply = NULL;
		
		/* Set up everything else for the query */
		switch (rrtype) {
		
			case ns_t_a:
				which = &(service->_a);				/* Hold in A record lookup */
				reply = _AddressQueryRecordReply;   /* Reply callback is for addresses */
				cb = _AQuerySocketCallBack;
				break;
			
			case ns_t_aaaa:
				which = &(service->_aaaa);			/* Hold in AAAA record lookup */
				reply = _AddressQueryRecordReply;   /* Reply callback is for addresses */
				cb = _AAAAQuerySocketCallBack;
				break;
				
			default:
				break;
		}
		
		/* If got something for lookup, start the query */
		if (which) {
			
			service->_error.error = DNSServiceQueryRecord(which,
														  0,
														  service->_interface,
														  name,
														  rrtype,
														  ns_c_in,
														  reply,
														  service);
		}
	}
	
	/* Set the domain if an error occurred */
	if (service->_error.error) {
		service->_error.error = _DNSServiceErrorToCFNetServiceError(service->_error.error);
		service->_error.domain = kCFStreamErrorDomainNetServices;
	}
	
	/* No error, so wrap the query for run loop integration. */
	else {

		CFSocketContext ctxt = {0, service, CFRetain, CFRelease, NULL};

		/* Create a CFSocket wrapper on the query */
		CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator((CFNetServiceRef)service),
													DNSServiceRefSockFD(*which),
													kCFSocketReadCallBack,
													cb,
													&ctxt);
		
		/* Add the socket to the sources if succeeded. */
		if (sock) {
			
			/* Tell CFSocket not to close the native socket on invalidation. */
			CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate);

			CFArrayAppendValue(service->_sources, sock);
			
			/* Schedule on run loops and modes, as required. */
			if (schedule)
				_CFTypeScheduleOnMultipleRunLoops(sock, service->_schedules);
			
			CFRelease(sock);
		}
		
		/* Need to record the error if it failed */
		else {
			
			/* Set error to whatever happened. */
			service->_error.error = errno;
			if (!service->_error.error)
				service->_error.error = ENOMEM;
			
			service->_error.domain = kCFStreamErrorDomainPOSIX;
			
			/* Cancel the query now if there was an error */
			DNSServiceRefDeallocate(*which);
			*which = NULL;
		}
	}
}


/* static */ Boolean
_ServiceBlockUntilComplete(__CFNetService* service) {
	
	/* Assume success by default */
	Boolean result = TRUE;
	CFRunLoopRef rl = CFRunLoopGetCurrent();
	
	/* Schedule in the blocking mode. */
	CFNetServiceScheduleWithRunLoop((CFNetServiceRef)service, rl, _kCFNetServiceBlockingMode);
	
	/* Lock in order to check for sources */
	__CFSpinLock(&(service->_lock));
	
	/* Check that there are sources. */
	while (CFArrayGetCount(service->_sources)) {
		
		/* Unlock again so the service can continue to be processed. */
		__CFSpinUnlock(&(service->_lock));
		
		/*
		 *  Run the loop in a private mode with it returning whenever a source
		 *  has been handled.
		 */
		CFRunLoopRunInMode(_kCFNetServiceBlockingMode, DBL_MAX, TRUE);
		
		/* Lock again in preparation for sources check */
		__CFSpinLock(&(service->_lock));		
	}
	
	/* Fail if there was an error. */
	if (service->_error.error)
		result = FALSE;
	
	/* Unlock the service again. */
	__CFSpinUnlock(&(service->_lock));
	
	/* Unschedule from the blocking mode */
	CFNetServiceUnscheduleFromRunLoop((CFNetServiceRef)service, rl, _kCFNetServiceBlockingMode);
	
	return result;
}


#if 0
#pragma mark * Service Discovery CallBacks
#endif


/* static */ void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
	
	DNSServiceErrorType err;
	__CFNetService* service = info;
	
	(void)s;		/* unused */
	(void)type;		/* unused */
	(void)address;  /* unused */
	(void)data;		/* unused */
	
	CFRetain(service);
	
	// Dispatch to process the result
	err = DNSServiceProcessResult(service->_new_service);
	
	// If there was an error, need to infor the client.
	if (err) {
		
		// Dispatch based upon search type.
		if (__CFBitIsSet(service->_flags, kFlagBitActiveResolve))
			_ResolveReply(service->_new_service, 0, 0, err, NULL, NULL, 0, 0, NULL, info);
		
		else {
			
			void* info = NULL;
			CFStreamError error = {kCFStreamErrorDomainNetServices, _DNSServiceErrorToCFNetServiceError(err)};
			CFNetServiceClientCallBack cb = NULL;
			
			/* Lock the service */
			__CFSpinLock(&service->_lock);
			
			/* Remove the registration from run loops and modes */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);
			
			/* Kill the registration. */
			DNSServiceRefDeallocate(service->_new_service);
			service->_new_service = NULL;
			
			/* Clear the flags */
			__CFBitClear(service->_flags, kFlagBitActiveRegister);
			
			/* Grab the callback and client info */
			cb = service->_callback;
			info = service->_client.info;
			
			/* Save the error in the client. */
			memmove(&(service->_error), &error, sizeof(error));
			
			/* Unlock the service so the callback can be made safely. */
			__CFSpinUnlock(&service->_lock);
			
			/* If there is a callback, inform the client of the error. */
			if (cb) {
				
				/* Inform the client. */
				cb((CFNetServiceRef)service, &error, info);
			}
		}
	}
	
	CFRelease(service);
}


/* static */ void
_AQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
	
	DNSServiceErrorType err;
	__CFNetService* service = info;
	
	(void)s;		/* unused */
	(void)type;		/* unused */
	(void)address;  /* unused */
	(void)data;		/* unused */
	
	CFRetain(service);
	
	// Dispatch to process the result
	err = DNSServiceProcessResult(service->_a);
	
	// If there was an error, need to inform the client.
	if (err)
		_AddressQueryRecordReply(service->_a, 0, 0, err, NULL, 0, 0, 0, NULL, 0, info);
	
	CFRelease(service);
}


/* static */ void
_AAAAQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
	
	DNSServiceErrorType err;
	__CFNetService* service = info;
	
	(void)s;		/* unused */
	(void)type;		/* unused */
	(void)address;  /* unused */
	(void)data;		/* unused */
	
	CFRetain(service);
	
	// Dispatch to process the result
	err = DNSServiceProcessResult(service->_aaaa);
	
	// If there was an error, need to inform the client.
	if (err)
		_AddressQueryRecordReply(service->_aaaa, 0, 0, err, NULL, 0, 0, 0, NULL, 0, info);
	
	CFRelease(service);
}


/* static */ void
_RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
			   const char* name, const char* regtype, const char* domain, void* context)
{
	
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	** Retain here to guarantee safety really after the source release,
	** but definitely before the callback.
	*/
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* If the register canceled, don't need to do any of this. */
	if (service->_new_service) {
		
		int i;
		CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
		const char* values[] = {name, regtype, domain};
		UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain};
		
		if (errorCode) {
		    service->_error.error = _DNSServiceErrorToCFNetServiceError(errorCode);
		    service->_error.domain = kCFStreamErrorDomainNetServices;
		}
		
		/* Save the registered values */
		for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
			
			/* Only save if there is a value */
			if (values[i]) {
				
				/* Create the CFString to go into the info dictionary */
				CFStringRef string = CFStringCreateWithCString(alloc, values[i], kCFStringEncodingUTF8);
				
				/* Save the value if it was created. */
				if (string) {
					CFDictionarySetValue(service->_info, (const void*)(keys[i]), string);
					CFRelease(string);
				}
			}
		}
		
		/* Grab the callback */
		cb = service->_callback;
		
		/* Save the error and client information for the callback */
		memmove(&error, &(service->_error), sizeof(error));
		info = service->_client.info;
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client of the error. */
	if (cb) {
		
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}



/* static */ void
_ResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
			  DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget,
			  uint16_t port, uint16_t txtLen, const char* txtRecord, void* context)
{
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error = {0, 0};
	void* info = NULL;
	
	/*
	** Retain here to guarantee safety really after the source release,
	** but definitely before the callback.
	*/
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* If the register canceled, don't need to do any of this. */
	if (service->_new_service) {
		
		if (errorCode) {
			
			/* Save the error */
			service->_error.error = _DNSServiceErrorToCFNetServiceError(errorCode);
			service->_error.domain = kCFStreamErrorDomainNetServices;
			
			/* Remove the registration from run loops and modes */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);
			
			/* Kill the A record lookup */
			if (service->_a) {
				DNSServiceRefDeallocate(service->_a);
				service->_a = NULL;
			}
			
			/* Kill the AAAA record lookup */
			if (service->_aaaa) {
				DNSServiceRefDeallocate(service->_aaaa);
				service->_aaaa = NULL;
			}
			
			/* Mark these as being done. */
			__CFBitSet(service->_flags, kFlagBitAComplete);
			__CFBitSet(service->_flags, kFlagBitAAAAComplete);
		}
		
		else {
			
			CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
			CFDataRef txt = txtRecord ? CFDataCreate(alloc, (const UInt8*)txtRecord, txtLen) : NULL;
			CFStringRef tgt = hosttarget ? CFStringCreateWithCString(alloc, hosttarget, kCFStringEncodingUTF8) : NULL;
			
			/* Save the port */
			service->_port = ntohs(port);
			
			/* Remove the saved txt data */
			CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
			
			/* Add the new one back if there is one */
			if (txt) {
				CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTXT, txt);
				CFRelease(txt);
			}
			
			/* Remove the saved target host */
			CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTargetHost);
			
			/* Add the new one back if there is one */
			if (tgt) {
				CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTargetHost, tgt);
				CFRelease(tgt);
			}
			
			/* Save the interface so other queries are on the same interface */
			service->_interface = interfaceIndex;
			
			/* Kick off the address lookups */
			_ServiceCreateQuery_NoLock(service, ns_t_a, hosttarget, NULL, NULL, TRUE);
			_ServiceCreateQuery_NoLock(service, ns_t_aaaa, hosttarget, NULL, NULL, TRUE);
			
			/* If there was an error, need to mark as done */
			if (service->_error.error) {
				
				/* Remove the registration from run loops and modes */
				_UnscheduleSources(service->_sources, service->_schedules);
				
				/* Go ahead and invalidate the sources */
				_InvalidateSources(service->_sources);
				
				/* Kill the A record lookup */
				if (service->_a) {
					DNSServiceRefDeallocate(service->_a);
					service->_a = NULL;
				}
				
				/* Kill the AAAA record lookup */
				if (service->_aaaa) {
					DNSServiceRefDeallocate(service->_aaaa);
					service->_aaaa = NULL;
				}
				
				/* Mark these as being done. */
				__CFBitSet(service->_flags, kFlagBitAComplete);
				__CFBitSet(service->_flags, kFlagBitAAAAComplete);
			}			
		}
		
		/* Kill the mdns service */
		_ServiceCancelDNSService_NoLock(service, service->_new_service);
		service->_new_service = NULL;
		
		/* If all lookups are done, need to perform the callback. */
		if (__CFBitIsSet(service->_flags, kFlagBitAComplete) &&
			__CFBitIsSet(service->_flags, kFlagBitAAAAComplete))
		{
			/* Grab the callback */
			cb = service->_callback;
			
			/* Save the error and client information for the callback */
			memmove(&error, &(service->_error), sizeof(error));
			info = service->_client.info;
		}
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client. */
	if (cb) {
		
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


/* static */ void
_LongTimerCallBack(CFRunLoopTimerRef timer, void *context) {
	
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	 ** Retain here to guarantee safety really after the source release,
	 ** but definitely before the callback.
	 */
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	if (!__CFBitIsSet(service->_flags, kFlagBitAReceived) &&
		!__CFBitIsSet(service->_flags, kFlagBitAAAAReceived) &&
		CFArrayGetCount(service->_sources))
	{
		int i;
		CFArrayRef list = CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress);
		DNSServiceRef* lookups[] = {
			&(service->_new_service),
			&(service->_a),
			&(service->_aaaa)
		};
		
		/* If no results were retrieved, mark as a timeout error. */
		if (service->_new_service || !list || (CFArrayGetCount(list) == 0)) {
			service->_error.error = kCFNetServicesErrorTimeout;
			service->_error.domain = kCFStreamErrorDomainNetServices;
		}

		/* Remove all the sources from run loops and modes */
		_UnscheduleSources(service->_sources, service->_schedules);
		
		/* Invalidate everything */
		_InvalidateSources(service->_sources);
		
		/* Stop and release the underlying mdns queries */
		for (i = 0; i < (sizeof(lookups) / sizeof(lookups[0])); i++) {
			if (*lookups[i]) {
				DNSServiceRefDeallocate(*lookups[i]);
				*lookups[i] = NULL;
			}
		}
		
		/* Grab the callback */
		cb = service->_callback;
		
		/* Save the error and client information for the callback */
		memmove(&error, &(service->_error), sizeof(error));
		info = service->_client.info;
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client. */
	if (cb) {
		
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


/* static */ void
_ShortTimerCallBack(CFRunLoopTimerRef timer, void *context) {
	
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	Boolean a_complete, aaaa_complete;
	
	/*
	 ** Retain here to guarantee safety really after the source release,
	 ** but definitely before the callback.
	 */
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/*
	** START 4031485
	**
	** On occasions, the short timer fires just before the CFSocket for the query
	** fires.  The last, outstanding query has actually completed, but it just
	** hasn't been serviced.  This change will quickly poll the CFSocket in order
	** to pump along the answer before the service resolve is timed out.
	**
	** NOTE that this change is entirely in its own context.
	*/
	{
		int fd = -1;					/* Used to find the matching CFSocketRef. */
		CFSocketCallBack c = NULL;		/* Call out to poll. */
		
		/* Check for the type of the outstanding query. */
		if (service->_a) {
			fd = DNSServiceRefSockFD(service->_a);
			c = _AQuerySocketCallBack;
		}
		else if (service->_aaaa) {
			fd = DNSServiceRefSockFD(service->_aaaa);
			c = _AAAAQuerySocketCallBack;
		}
		
		/* Need to poll the line if there is an fd. */
		if (fd != -1) {
		
			int val;
			fd_set	set;
			fd_set* setptr = &set;
			
			struct timeval timeout = {0, 0};
			
			FD_ZERO(setptr);
			
			if (fd >= FD_SETSIZE) {
				
				val = howmany(fd + 1, NFDBITS) * sizeof(fd_mask);
				
				setptr = (fd_set*)malloc(val);
				bzero(setptr, val);
			}
			
			FD_SET(fd, setptr);
			
			val = select(fd + 1, setptr, NULL, NULL, &timeout);
			
			if (setptr != &set)
				free(setptr);
				
			if (val > 0) {
			
				CFSocketRef s = NULL;
				CFTypeID socket_type = CFSocketGetTypeID();
				CFIndex i, count = CFArrayGetCount(service->_sources);
				
				/* Go trolling through the sources for the corresponding CFSocketRef. */
				for (i = 0; i < count; i++) {
					CFTypeRef obj = (CFTypeRef)CFArrayGetValueAtIndex(service->_sources, i);
					if ((CFGetTypeID(obj) == socket_type) && (CFSocketGetNative((CFSocketRef)obj) == fd)) {
						s = (CFSocketRef)obj;
						break;
					}
				}
				
				/* This should always hit, but late in Tiger, it's needed. */
				if (s) {
				
					/* Unlock the service so the callback can be made safely. */
					__CFSpinUnlock(&service->_lock);
					
					/* Make the callback which could alter the state of the service. */
					c(s, kCFSocketReadCallBack, NULL, NULL, service);
			
					/* Lock the service */
					__CFSpinLock(&service->_lock);
				}
			}
		}
	}
	
	/* As a result of the possible call to the address callback, the timeout may not occur. */
	
	/* END 4031485 */
	
	a_complete = __CFBitIsSet(service->_flags, kFlagBitAComplete);
	aaaa_complete = __CFBitIsSet(service->_flags, kFlagBitAAAAComplete);
	
	if (CFArrayGetCount(service->_sources) &&
		((a_complete && aaaa_complete) ||
		 (a_complete && !__CFBitIsSet(service->_flags, kFlagBitAAAAReceived)) ||
		 (aaaa_complete && !__CFBitIsSet(service->_flags, kFlagBitAReceived))))
	{
		int i;
		DNSServiceRef* lookups[] = {
			&(service->_new_service),
			&(service->_a),
			&(service->_aaaa)
		};
		
		/* Remove all the sources from run loops and modes */
		_UnscheduleSources(service->_sources, service->_schedules);
		
		/* Invalidate everything */
		_InvalidateSources(service->_sources);
		
		/* Stop and release the underlying mdns queries */
		for (i = 0; i < (sizeof(lookups) / sizeof(lookups[0])); i++) {
			if (*lookups[i]) {
				DNSServiceRefDeallocate(*lookups[i]);
				*lookups[i] = NULL;
			}
		}
		
		/* Grab the callback */
		cb = service->_callback;
		
		/* Save the error and client information for the callback */
		memmove(&error, &(service->_error), sizeof(error));
		info = service->_client.info;
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client. */
	if (cb) {
		
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}	


/* static */ void
_AddressQueryRecordReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
						 DNSServiceErrorType errorCode, const char* fullname, uint16_t rrtype,
						 uint16_t rrclass, uint16_t rdlen, const void* rdata, uint32_t ttl, void* context)
{
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	** Retain here to guarantee safety really after the source release,
	** but definitely before the callback.
	*/
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* Only perform the work if there are outstanding queries */
	if (service->_a || service->_aaaa) {
		
		UInt32 service_flags_copy = service->_flags;
		CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
		
		/* Get the list of addresses to add this one */
		CFMutableArrayRef list = (CFMutableArrayRef)CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress);
		
		/* If there is not a list, need to create one */
		if (!list) {
			
			/* Create the list */
			list = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
			
			/* If the list was created, add it back to the list of info */
			if (list) {
				CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceAddress, list);
				CFRelease(list);
			}
		}
		
		/* If there is a list of addresses, create the new address to add */
		if (list) {
			
			/* Create the address from the record data */
			CFDataRef addr = _CFDataCreateWithRecord(alloc,
													 rrtype,
													 rdlen,
													 rdata,
													 (service->_port & 0x0000FFFF),
													 interfaceIndex);
			
			/* If created an address, add it to the list and release it */
			if (addr) {
				CFArrayAppendValue(list, addr);
				CFRelease(addr);
			}
		}
		
		if (rrtype == ns_t_a)
			__CFBitSet(service->_flags, kFlagBitAReceived);
		else if (rrtype == ns_t_aaaa)
			__CFBitSet(service->_flags, kFlagBitAAAAReceived);
		
		/* If done or there was an error, need to close down the query */
		if (!(flags & kDNSServiceFlagsMoreComing) || errorCode) {
			
			/* Kill the mdns service */
			_ServiceCancelDNSService_NoLock(service, sdRef);
			
			/* If it's an A record lookup, clear and mark as done. */
			if (rrtype == ns_t_a) {
				service->_a = NULL;
				__CFBitSet(service->_flags, kFlagBitAComplete);
			}
			
			/* If it's a AAAA record, clear and mark that one. */
			else if (rrtype == ns_t_aaaa) {
				service->_aaaa = NULL;
				__CFBitSet(service->_flags, kFlagBitAAAAComplete);
			}
		}
		
		/* If all lookups are done, need to perform the callback. */
		if (__CFBitIsSet(service->_flags, kFlagBitAComplete) &&
			__CFBitIsSet(service->_flags, kFlagBitAAAAComplete) &&
			!service->_new_service)
		{
			/* Remove the timers */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);

			/* Grab the callback */
			cb = service->_callback;
			
			/* Save the error and client information for the callback */
			memmove(&error, &(service->_error), sizeof(error));
			info = service->_client.info;
		}
		
		/*
		** Not all done, but set the short timer if got the first
		** "no more coming" flag.
		*/
		else if ((!__CFBitIsSet(service_flags_copy, kFlagBitAComplete) &&
				  !__CFBitIsSet(service_flags_copy, kFlagBitAAAAComplete)) &&
				  (__CFBitIsSet(service->_flags, kFlagBitAComplete) ||
				   __CFBitIsSet(service->_flags, kFlagBitAAAAComplete)))
		{
			
			CFRunLoopTimerContext c = {0, service, NULL, NULL, NULL};
			
			/* Create the timer used for the longest amount of time willing to wait. */
			CFRunLoopTimerRef timer = CFRunLoopTimerCreate(alloc,
														   CFAbsoluteTimeGetCurrent() + kCFNetServiceShortTimeout,
														   0.0,
														   0,
														   0,
														   _ShortTimerCallBack,
														   &c);
			
			/* Need to add the timer to the list of sources on success */
			if (timer) {
				
				CFArrayAppendValue(service->_sources, timer);
				
				/* Need to schedule it */
				_CFTypeScheduleOnMultipleRunLoops(timer, service->_schedules);
				
				CFRelease(timer);
			}
		}
	}
	
	/* Unlock the service. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client of the error. */
	if (cb) {
		
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


#if 0
#pragma mark * Utility Functions
#endif


/* static */ CFDataRef
_CFDataCreateWithRecord(CFAllocatorRef allocator, uint16_t rrtype, uint16_t rdlen,
						const void* rdata, u_short port, uint32_t interfaceIndex)
{	
	CFDataRef result = NULL;
	UInt8 buffer[512];
	struct sockaddr* sa = (struct sockaddr*)(&buffer[0]);
	UInt8* addr = NULL;
	
	memset(sa, 0, sizeof(buffer));
	
	/* Need to bundle up the A record into a sockaddr */
	if (rrtype == ns_t_a) {
		
		if (rdlen == sizeof(struct in_addr)) {
			
			sa->sa_len = sizeof(struct sockaddr_in);
			sa->sa_family = AF_INET;
			
			((struct sockaddr_in*)sa)->sin_port = htons(port);
			
			addr = (UInt8*)(&(((struct sockaddr_in*)sa)->sin_addr));
		}
	}
	
	/* Need to bundle up the AAAA record into a sockaddr */
	else if (rrtype == ns_t_aaaa) {
		
		if (rdlen == sizeof(struct in6_addr)) {
			
			sa->sa_len = sizeof(struct sockaddr_in6);
			sa->sa_family = AF_INET6;
			
			((struct sockaddr_in6*)sa)->sin6_port = htons(port);
			((struct sockaddr_in6*)sa)->sin6_scope_id = htonl(interfaceIndex);
			
			addr = (UInt8*)(&(((struct sockaddr_in6*)sa)->sin6_addr));
		}
	}
	
	if (addr) {
		memmove(addr, rdata, rdlen);
		result = CFDataCreate(allocator, (const UInt8*)sa, sa->sa_len);
	}
	
	return result;
}


/* static */ void
_AddRecords(const void *key, const void *value, void *context) {
	
	if (((0xFFFF0000 & (UInt32)key) == 0x00010000) &&
		(((__CFNetService*)context)->_error.error == 0))
	{
		DNSRecordRef record;
		
		((__CFNetService*)context)->_error.error = DNSServiceAddRecord(((__CFNetService*)context)->_new_service,
																	   &record,
																	   0,
																	   (0x0000FFFF & (UInt32)key),
																	   CFDataGetLength((CFDataRef)value),
																	   CFDataGetBytePtr((CFDataRef)value),
																	   0);
		
		if (!((__CFNetService*)context)->_error.error)
			CFDictionaryAddValue(((__CFNetService*)context)->_records, key, record);
	}
}


/* static */ void
_DictionaryApplier(const void *key, const void *value, void *context) {
	
	/* Get the type in order to figure out how to copy */
	CFTypeID t = CFGetTypeID((CFTypeRef)value);
	
	/* Strings get a copy */
	if (t == CFStringGetTypeID()) {
		CFStringRef c = CFStringCreateCopy(CFGetAllocator((CFTypeRef)context), (CFStringRef)value);
		
		/* Only added if copy succeeded. */
		if (c) {
			CFDictionaryAddValue((CFMutableDictionaryRef)context, key, c);
			CFRelease(c);
		}
	}
	
	/* Arrays get a copy */
	else if (t == CFArrayGetTypeID()) {
		CFArrayRef c = CFArrayCreateCopy(CFGetAllocator((CFTypeRef)context), (CFArrayRef)value);
		
		/* Only added if copy succeeded. */
		if (c) {
			CFDictionaryAddValue((CFMutableDictionaryRef)context, key, c);
			CFRelease(c);
		}
	}
	
	else
		CFDictionaryAddValue((CFMutableDictionaryRef)context, key, value);
}


/* static */ void
_ScheduleSources(CFArrayRef sources, CFArrayRef schedules) {
	
	int i, count = CFArrayGetCount(sources);
	for (i = 0; i < count; i++)
		_CFTypeScheduleOnMultipleRunLoops(CFArrayGetValueAtIndex(sources, i), schedules);
}


/* static */ void
_UnscheduleSources(CFArrayRef sources, CFArrayRef schedules) {
	
	int i, count = CFArrayGetCount(sources);
	for (i = 0; i < count; i++)
		_CFTypeUnscheduleFromMultipleRunLoops(CFArrayGetValueAtIndex(sources, i), schedules);
}


/* static */ void
_InvalidateSources(CFMutableArrayRef sources) {
	
	int i, count = CFArrayGetCount(sources);
	for (i = 0; i < count; i++)
		_CFTypeInvalidate(CFArrayGetValueAtIndex(sources, i));
	
	/* Dump all the sources. */
	CFArrayRemoveAllValues(sources);
}


#if 0
#pragma mark * Legacy Support
#endif


/* static */ void
_MachPortCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) {

	/* Call Service Discovery to do the dispatch. */
	DNSServiceDiscovery_handleReply(msg);
}


/* static */ void
_LegacyRegistrationReply(int errorCode, void* context) {

	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	** Retain here to guarantee safety really after the source release,
	** but definitely before the callback.
	*/
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* If the register canceled, don't need to do any of this. */
	if (service->_old_service) {
		
		/* If there is an error, fold the registration. */
		if (errorCode) {
			
			/* Save the error.  3869179 All errors under Jaguar and Panther were collisions. */
			service->_error.error = kCFNetServicesErrorCollision;
			service->_error.domain = kCFStreamErrorDomainNetServices;
			
			/* Remove the registration from run loops and modes */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);

			/* Clean up the underlying service discovery stuff */
			DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
			service->_old_service = NULL;
			
			/* Clear the flags */
			__CFBitClear(service->_flags, kFlagBitLegacyService);
			__CFBitClear(service->_flags, kFlagBitActiveResolve);
			
			/* Grab the callback */
			cb = service->_callback;
			
			/* Save the error and client information for the callback */
			memmove(&error, &(service->_error), sizeof(error));
			info = service->_client.info;
		}
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client of the error. */
	if (cb) {
			
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


/* static */ void
_LegacyResolverReply(struct sockaddr* interface, struct sockaddr* address, const char* txtRecord,
					 DNSServiceDiscoveryReplyFlags flags, void* context)
{
	__CFNetService* service = (__CFNetService*)context;
	CFNetServiceClientCallBack cb = NULL;
	CFStreamError error;
	void* info = NULL;
	
	/*
	** Retain here to guarantee safety really after the source release,
	** but definitely before the callback.
	*/
	CFRetain(service);
	
	/* Lock the service */
	__CFSpinLock(&service->_lock);
	
	/* If the register canceled, don't need to do any of this. */
	if (service->_old_service) {
		
		CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
		
		/* Get the list of addresses to add this one */
		CFMutableArrayRef list = (CFMutableArrayRef)CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress);
		
		/* If there is not a list, need to create one */
		if (!list) {
			
			/* Create the list */
			list = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
			
			/* If the list was created, add it back to the list of info */
			if (list) {
				CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceAddress, list);
				CFRelease(list);
			}
		}
		
		/* If there is a list, need to add the address */
		if (list) {

			int i;
			
			/* Search for this address in the list. */
			for (i = CFArrayGetCount(list) - 1; i >= 0; i--) {
				
				struct sockaddr* saved = (struct sockaddr*)CFDataGetBytePtr((CFDataRef)CFArrayGetValueAtIndex(list, i));
				
				/*
				** The length on an AF_INET address structure is 16 but only the first 8 bytes
				** are used.  The rest are supposed to be zero, but mDNSResponder does not
				** zero them.
				*/
				int compare = (saved->sa_family == AF_INET) ? 8 : saved->sa_len;

				/* Break if found */
				if (!memcmp(saved, address, compare))
					break;
			}
	
			/* If it wasn't found, need to add it. */
			if (i < 0) {
				
				/* Wrap the sockaddr */
				CFDataRef data = CFDataCreate(alloc, (const UInt8*)address, address->sa_len);
				
				/* Add the address to the list if wrapped. */
				if (data) {
					CFArrayAppendValue(list, data);
					CFRelease(data);
				}
			}
		}
		
		/* Remove the old TXT data. */
		CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
		
		/* If there was a TXT record, need to wrap it as a string. */
		if (txtRecord) {
			
			/* Create the string */
			CFStringRef t = CFStringCreateWithCString(alloc, txtRecord, kCFStringEncodingUTF8);
			
			/* If it worked, add it back into the info */
			if (t) {
				CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTXT, t);
				CFRelease(t);
			}
		}
		
		/* Grab the callback */
		cb = service->_callback;
		
		/* If not an asynchronous resolve, need to finish up now */
		if (!cb) {
			
			/* Remove the resolve from run loops and modes */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);
			
			/* Clean up the underlying service discovery stuff */
			DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
			service->_old_service = NULL;
			
			/* Clear the flags */
			__CFBitClear(service->_flags, kFlagBitLegacyService);
			__CFBitClear(service->_flags, kFlagBitActiveRegister);
		}
		
		/* Save the error and client information for the callback */
		memmove(&error, &(service->_error), sizeof(error));
		info = service->_client.info;
	}
	
	/* Unlock the service so the callback can be made safely. */
	__CFSpinUnlock(&service->_lock);
	
	/* If there is a callback, inform the client of the error. */
	if (cb) {
			
		/* Inform the client. */
		cb((CFNetServiceRef)service, &error, info);
	}
	
	/* Go ahead and release now that the callback is done. */
	CFRelease(service);
}


#if 0
#pragma mark * TXT Dictionary Key Callbacks
#endif

/* static */ const void*
TXTDictionaryKeyRetain(CFAllocatorRef allocator, CFStringRef key) {
	(void)allocator;	/* unsused */
	return (const void*)CFRetain(key);
}


/* static */ void
TXTDictionaryKeyRelease(CFAllocatorRef allocator, CFStringRef key) {
	(void)allocator;	/* unused */
	CFRelease(key);
}


/* static */ Boolean
TXTDictionaryKeyEqual(CFStringRef key1, CFStringRef key2) {
	return (CFStringCompare(key1, key2, kCFCompareCaseInsensitive) == kCFCompareEqualTo);
}


/* static */ CFHashCode
TXTDictionaryKeyHash(CFStringRef key) {
	return (CFHashCode)CFStringGetLength(key);
}


#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif


/* extern */ CFTypeID
CFNetServiceGetTypeID(void) {

    _CFDoOnce(&_kCFNetServiceRegisterClass, _CFNetServiceRegisterClass);

    return _kCFNetServiceTypeID;
}


/* extern */ CFNetServiceRef
CFNetServiceCreate(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port) {
	
	CFNetServiceRef result = NULL;
	
	/* Domain, type, and name must be specified */
	if (domain && type && name) {
		
		/* Create copies for normalization */
		CFMutableStringRef d = CFStringCreateMutableCopy(alloc, 0, domain);
		CFMutableStringRef t = CFStringCreateMutableCopy(alloc, 0, type);
		CFMutableStringRef n = CFStringCreateMutableCopy(alloc, 0, name);
		
		if (d && t && n) {
			
			/* Normalization for on-the-wire transfer */
			CFStringNormalize(d, kCFStringNormalizationFormC);
			CFStringNormalize(t, kCFStringNormalizationFormC);
			CFStringNormalize(n, kCFStringNormalizationFormC);
			
			result = _CFNetServiceCreateCommon(alloc, d, t, n, port);
		}
			
		/* Release the copies created for normalization */
		if (d)
			CFRelease(d);
		if (t)
			CFRelease(t);
		if (n)
			CFRelease(n);
	}
	
	return (CFNetServiceRef)result;
}


/* extern */ CFNetServiceRef
CFNetServiceCreateCopy(CFAllocatorRef alloc, CFNetServiceRef service) {
	
	__CFNetService* result = NULL;
	__CFNetService* s = (__CFNetService*)service;
	CFTypeID class_type = CFNetServiceGetTypeID();
	
	__CFSpinLock(&(s->_lock));

	if (class_type != _kCFRuntimeNotATypeID) {
		result = (__CFNetService*)_CFRuntimeCreateInstance(alloc,
														   class_type,
														   sizeof(result[0]) - sizeof(CFRuntimeBase),
														   NULL);
	}
	
	if (result) {
		
		CFDictionaryKeyCallBacks keys = {0, NULL, NULL, NULL, NULL, NULL};
		CFDictionaryValueCallBacks values = {0, NULL, NULL, NULL, NULL};

		/* Save a copy of the base so it's easier to zero the struct */
		CFRuntimeBase copy = result->_base;

		/* Clear everything. */
		memset(result, 0, sizeof(result[0]));

		/* Put back the base */
		memmove(&(result->_base), &copy, sizeof(result->_base));

		/* Create the dictionary of information */
		result->_info = CFDictionaryCreateMutable(alloc, 0, &keys, &kCFTypeDictionaryValueCallBacks);

		/* Create the dictionary for holding the published records */
		result->_records = CFDictionaryCreateMutable(alloc, 0, &keys, &values);
		
		/* Copy all the info from the original */
		if (result->_info)
			CFDictionaryApplyFunction(s->_info, _DictionaryApplier, result->_info);

		/* Create the list of loops and modes */
		result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
		
		/* Create the list of sources */
		result->_sources = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
		
		/* Failure needs to release and return NULL. */
		if (!result->_info ||
			!result->_schedules ||
			!result->_sources ||
			!result->_records ||
			(CFDictionaryGetCount(result->_info) != CFDictionaryGetCount(s->_info)))
		{
			CFRelease((CFTypeRef)result);
			result = NULL;
		}
	}
	
	__CFSpinUnlock(&(s->_lock));

	return (CFNetServiceRef)result;
}


/* extern */ CFStringRef
CFNetServiceGetDomain(CFNetServiceRef theService) {
	
	return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceDomain);
}


/* extern */ CFStringRef
CFNetServiceGetType(CFNetServiceRef theService) {
	
	return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceType);
}


/* extern */ CFStringRef
CFNetServiceGetName(CFNetServiceRef theService) {

	return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceName);
}


/* extern */ CFArrayRef
CFNetServiceGetAddressing(CFNetServiceRef theService) {

	return (CFArrayRef)CFNetServiceGetInfo(theService, _kCFNetServiceAddress);
}

/* extern */ CFStringRef 
CFNetServiceGetTargetHost(CFNetServiceRef theService) {
	
	return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceTargetHost);
}


/* extern */ CFDataRef 
CFNetServiceGetTXTData(CFNetServiceRef theService) {
	
	CFTypeRef result = CFNetServiceGetInfo(theService, _kCFNetServiceTXT);
	
	/*
	** This shouldn't really happen.  This is here in order to protect
	** against using the new TXT calls in conjunction with the old
	** deprecated calls.
	*/
	if (result && (CFGetTypeID(result) != CFDataGetTypeID()))
		result = NULL;
	
	return (CFDataRef)result;
}


/* extern */ Boolean 
CFNetServiceSetTXTData(CFNetServiceRef theService, CFDataRef txtRecord) {
	
	return CFNetServiceSetInfo(theService, _kCFNetServiceTXT, txtRecord);
}


/* extern */ Boolean 
CFNetServiceRegisterWithOptions(CFNetServiceRef theService, CFOptionFlags options, CFStreamError* error) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	CFStreamError extra;
	Boolean result = FALSE;
	
	if (!error)
		error = &extra;
	
	memset(error, 0, sizeof(error[0]));
	
	/*
	** Retain so it doesn't go away underneath in the case of a callout.  This is really
	** no worry for async, but makes the memmove for the error more difficult to place
	** for synchronous without it being here.
	*/
	CFRetain(theService);
	
	/* Lock down the service to start */
	__CFSpinLock(&(service->_lock));
	
	do {
		
		int i;
		char properties[3][1024];
		CFSocketRef sock;
		CFSocketContext ctxt = {0, service, CFRetain, CFRelease, NULL};
		UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain};
		DNSServiceFlags flags = ((options == kCFNetServiceFlagNoAutoRename) ? kDNSServiceFlagsNoAutoRename : 0);
		CFDataRef txt = (CFDataRef)CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceTXT);
		
		/* Check to see if there is an ongoing process already */
		if (CFArrayGetCount(service->_sources)) {
			
			/* If there's already mdns activity, don't allow another. */
			if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
				service->_error.error = kCFNetServicesErrorInProgress;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			/* It's just the cancel that hasn't fired yet, so cancel it. */
			else {
				
				/* Remove the cancel from run loops and modes */
				_UnscheduleSources(service->_sources, service->_schedules);
				
				/* Invalidate the run loop source */
				_InvalidateSources(service->_sources);
			}
		}
		
		/* Get the raw data for the properties to send down to mdns */
		for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
			
			CFStringRef value = (CFStringRef)CFDictionaryGetValue(service->_info, (const void*)(keys[i]));
			if (!value)
				properties[i][0] = '\0';
			else {
				CFIndex used;
				CFStringGetBytes(value,
								 CFRangeMake(0, CFStringGetLength(value)),
								 kCFStringEncodingUTF8,
								 0,
								 FALSE,
								 (UInt8*)properties[i],
								 sizeof(properties[i]) - 1,
								 &used);
				properties[i][used] = '\0';
			}
		}
		
		/* Create the registration */
		service->_error.error = DNSServiceRegister(&(service->_new_service),
												   flags,
												   0,
												   properties[0],
												   properties[1],
												   properties[2],
												   NULL,
												   htons((service->_port & 0x0000FFFF)),
												   txt ? CFDataGetLength(txt) : 0,
												   txt ? CFDataGetBytePtr(txt) : NULL,
												   _RegisterReply,
												   service);
		
		if (service->_error.error) {
			service->_error.error = _DNSServiceErrorToCFNetServiceError(service->_error.error);
			service->_error.domain = kCFStreamErrorDomainNetServices;
			break;
		}
		
		CFDictionaryApplyFunction(service->_info, _AddRecords, service);
		if (service->_error.error) {
			service->_error.error = _DNSServiceErrorToCFNetServiceError(service->_error.error);
			service->_error.domain = kCFStreamErrorDomainNetServices;
			
			/* Stop right away on failure */
			DNSServiceRefDeallocate(service->_new_service);
			service->_new_service = NULL;
			
			/* Dump all the records that may have been published */
			CFDictionaryRemoveAllValues(service->_records);
			
			break;
		}
		
		/* Create a CFSocket wrapper on the register */
		sock = CFSocketCreateWithNative(CFGetAllocator(theService),
										DNSServiceRefSockFD(service->_new_service),
										kCFSocketReadCallBack,
										_SocketCallBack,
										&ctxt);
		
		/* Need to bail if it failed */
		if (!sock) {
			
			/* Set error to whatever happened. */
			service->_error.error = errno;
			if (!service->_error.error)
				service->_error.error = ENOMEM;
			
			service->_error.domain = kCFStreamErrorDomainPOSIX;
			
			/* Stop right away on failure */
			DNSServiceRefDeallocate(service->_new_service);
			service->_new_service = NULL;
			
			/* Dump all the records that may have been published */
			CFDictionaryRemoveAllValues(service->_records);
			
			break;
		}
		
		/* Tell CFSocket not to close the native socket on invalidation. */
		CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate);
		
		/* Add the socket to the list of sources */
		CFArrayAppendValue(service->_sources, sock);
		CFRelease(sock);
		
		/* Start with no error. */
		service->_error.error = 0;
		service->_error.domain = 0;
		
		/* Set the flags indicating a new, Panther-type registration */
		__CFBitClear(service->_flags, kFlagBitLegacyService);
		__CFBitSet(service->_flags, kFlagBitActiveRegister);
		
		/* Async mode is complete at this point */
		if (CFArrayGetCount(service->_schedules)) {
			
			/* Schedule the sources on the run loops and modes. */
			_ScheduleSources(service->_sources, service->_schedules);
			
			/* It's now succeeded. */
			result = TRUE;
		}
		
		/* Go into synchronous mode. */
		else {
			
			/* Unlock the service */
			__CFSpinUnlock(&(service->_lock));
			
			/* Wait for synchronous return */
			result = _ServiceBlockUntilComplete(service);
			
			/* Lock down the service */
			__CFSpinLock(&(service->_lock));
		}
		
	} while (0);
	
	/* Copy the error. */
	memmove(error, &service->_error, sizeof(error[0]));
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
	
	/* Release the earlier retain. */
	CFRelease(theService);
	
	return result;
}


/* extern */ Boolean 
CFNetServiceResolveWithTimeout(CFNetServiceRef theService, CFTimeInterval timeout, CFStreamError* error) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	CFStreamError extra;
	Boolean result = FALSE;
	
	if (!error)
		error = &extra;
	
	memset(error, 0, sizeof(error[0]));
	
	/*
	** Retain so it doesn't go away underneath in the case of a callout.  This is really
	** no worry for async, but makes the memmove for the error more difficult to place
	** for synchronous without it being here.
	*/
	CFRetain(theService);
	
	/* Lock down the service to start */
	__CFSpinLock(&(service->_lock));
	
	do {
		
		int i;
		char properties[3][1024];
		UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain};
		
		/* Check to see if there is an ongoing process already */
		if (CFArrayGetCount(service->_sources)) {
		
			/* If there's already mdns activity, don't allow another. */
			if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
				service->_error.error = kCFNetServicesErrorInProgress;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			/* It's just the cancel that hasn't fired yet, so cancel it. */
			else {
				
				/* Remove the cancel from run loops and modes */
				_UnscheduleSources(service->_sources, service->_schedules);
				
				/* Invalidate the run loop source */
				_InvalidateSources(service->_sources);
			}
		}
		
		/* Get the raw data for the properties to send down to mdns */
		for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
			
			CFStringRef value = (CFStringRef)CFDictionaryGetValue(service->_info, (const void*)(keys[i]));
			if (!value)
				properties[i][0] = '\0';
			else {
				CFIndex used;
				CFStringGetBytes(value,
								 CFRangeMake(0, CFStringGetLength(value)),
								 kCFStringEncodingUTF8,
								 0,
								 FALSE,
								 (UInt8*)properties[i],
								 sizeof(properties[i]) - 1,
								 &used);
				properties[i][used] = '\0';
			}
		}
		
		_ServiceCreateQuery_NoLock(service, ns_t_invalid, properties[0],
								   properties[1],  properties[2], FALSE);
		
		/* If a timeout is set, need to start the timer. */
		if (!service->_error.error && (timeout > 0.0)) {
			
			CFRunLoopTimerContext c = {0, service, NULL, NULL, NULL};
			
			/* Create the timer used for the longest amount of time willing to wait. */
			CFRunLoopTimerRef timer = CFRunLoopTimerCreate(CFGetAllocator(theService),
														   CFAbsoluteTimeGetCurrent() + timeout,
														   0.0,
														   0,
														   0,
														   _LongTimerCallBack,
														   &c);
			
			__CFBitClear(service->_flags, kFlagBitAReceived);
			__CFBitClear(service->_flags, kFlagBitAAAAReceived);
			
			/* Need to add the timer to the list of sources on success */
			if (timer) {
				CFArrayAppendValue(service->_sources, timer);
				CFRelease(timer);
			}
			
			/* Need to clean up if there was a failure. */
			else {
				
				/* Set error to whatever happened. */
				service->_error.error = errno;
				if (!service->_error.error)
					service->_error.error = ENOMEM;
				
				service->_error.domain = kCFStreamErrorDomainPOSIX;
			}
		}
		
		/* Need to bail if it there was an error */
		if (service->_error.error) {
			
			/* Invalidate everything */
			_InvalidateSources(service->_sources);
			
			/* Stop and release the underlying mdns query */
			DNSServiceRefDeallocate(service->_new_service);
			service->_new_service = NULL;
			
			break;
		}
		
		/* Remove any addresses so the resolve fills in new. */
		CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceAddress);
		
		/* Remove any TXT record. */
		CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
		
		/* Remove the target host. */
		CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTargetHost);
		
		/* Don't want a port until it's resolved */
		service->_port = 0;
		
		/* Start with no error. */
		service->_error.error = 0;
		service->_error.domain = 0;
		
		/* Set the flags indicating a new, Panther-type resolve */
		__CFBitClear(service->_flags, kFlagBitLegacyService);
		__CFBitSet(service->_flags, kFlagBitActiveResolve);
		
		/* Clear flags indicating address lookup completion. */
		__CFBitClear(service->_flags, kFlagBitAComplete);
		__CFBitClear(service->_flags, kFlagBitAAAAComplete);

		/* Async mode is complete at this point */
		if (CFArrayGetCount(service->_schedules)) {
			
			/* Schedule the sources on the run loops and modes. */
			_ScheduleSources(service->_sources, service->_schedules);
			
			/* It's now succeeded. */
			result = TRUE;
		}
		
		/* Go into synchronous mode. */
		else {
			
			/* Unlock the service */
			__CFSpinUnlock(&(service->_lock));
			
			/* Wait for synchronous return */
			result = _ServiceBlockUntilComplete(service);
			
			/* Lock down the service */
			__CFSpinLock(&(service->_lock));
		}
		
	} while (0);
	
	/* Copy the error. */
	memmove(error, &service->_error, sizeof(error[0]));
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
	
	/* Release the earlier retain. */
	CFRelease(theService);
	
	return result;
}


/* extern */ void
CFNetServiceCancel(CFNetServiceRef theService) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	/* Lock down the service */
	__CFSpinLock(&(service->_lock));
	
	/* Make sure there is something to cancel. */
	if (CFArrayGetCount(service->_sources)) {
		
		CFRunLoopSourceRef src = NULL;
		CFRunLoopSourceContext ctxt = {
			0,									/* version */
			service,							/* info */
			NULL,								/* retain */
			NULL,								/* release */
			NULL,								/* copyDescription */
			NULL,								/* equal */
			NULL,								/* hash */
			NULL,								/* schedule */
			NULL,								/* cancel */
			(void(*)(void*))(&_ServiceCancel)  	/* perform */
		};
		
		/* Remove the sources from run loops and modes */
		_UnscheduleSources(service->_sources, service->_schedules);
		
		/* Go ahead and invalidate the sources */
		_InvalidateSources(service->_sources);
		
		if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
			
			/* Need to clean up the service discovery stuff if there is */
			if (service->_old_service) {
				
				/* Release the underlying service discovery reference */
				DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
				service->_old_service = NULL;
			}
		}
		else {
			
			int i;
			DNSServiceRef* services[] = {&service->_new_service, &service->_a, &service->_aaaa};
			
			/* Need to clean up the service discovery stuff if there is */
			for (i = 0; i < (sizeof(services) / sizeof(services[0])); i++) {
				
				if (*services[i]) {

					/* Release the underlying service discovery reference */
					DNSServiceRefDeallocate(*services[i]);
					*services[i] = NULL;
				}
			}
			
			/* Dump all the records that may have been published */
			CFDictionaryRemoveAllValues(service->_records);
		}
		
		/* Clear any flags related to the sources */
		__CFBitClear(service->_flags, kFlagBitLegacyService);
		__CFBitClear(service->_flags, kFlagBitActiveResolve);
		__CFBitClear(service->_flags, kFlagBitActiveRegister);
		__CFBitClear(service->_flags, kFlagBitCancel);
		
		/* Mark the service as cancelled */
		service->_error.error = kCFNetServicesErrorCancel;
		service->_error.domain = kCFStreamErrorDomainNetServices;
		
		/* Create the cancel source */
		src = CFRunLoopSourceCreate(CFGetAllocator(theService), 0, &ctxt);
		
		/* If the cancel was created, need to schedule and signal it. */
		if (src) {
			
			CFArrayRef schedules = service->_schedules;
			CFIndex i, count = CFArrayGetCount(schedules);
			
			/* Mark it as being a cancel */
			__CFBitSet(service->_flags, kFlagBitCancel);
			
			/* Add the source to the list of sources */
			CFArrayAppendValue(service->_sources, src);
			
			/* Schedule the new cancel */
			_ScheduleSources(service->_sources, service->_schedules);
			
			/* Signal the cancel for immediate attention. */
			CFRunLoopSourceSignal(src);
			
			/* Make sure the signal can make it through */
			for (i = 0; i < count; i += 2) {
				
				/* Grab the run loop for checking */
				CFRunLoopRef runloop = (CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i);
				
				/* If it's sleeping, need to further check it. */
				if (CFRunLoopIsWaiting(runloop)) {
					
					/* Grab the mode for further check */
					CFStringRef mode = CFRunLoopCopyCurrentMode(runloop);
					
					if (mode) {
						
						/* If the cancel source is in the right mode, need to wake up the run loop. */
						if (CFRunLoopContainsSource(runloop, src, mode)) {
							CFRunLoopWakeUp(runloop);
						}
						
						/* Don't need this anymore. */
						CFRelease(mode);
					}
				}
			}
			
			/* No longer need this */
			CFRelease(src);
		}
	}
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
}


/* extern */ Boolean
CFNetServiceSetClient(CFNetServiceRef theService, CFNetServiceClientCallBack clientCB, CFNetServiceClientContext* clientContext) {

	__CFNetService* service = (__CFNetService*)theService;

	/* Lock down the service */
	__CFSpinLock(&(service->_lock));

	/* Release the user's context info if there is some and a release method */
	if (service->_client.info && service->_client.release)
		service->_client.release(service->_client.info);
	
	/* NULL callback or context signals to remove the client */
	if (!clientCB || !clientContext) {
		
		/* Cancel the sources if any */
		if (CFArrayGetCount(service->_sources)) {
			
			/* Remove the sources from run loops and modes */
			_UnscheduleSources(service->_sources, service->_schedules);
			
			/* Go ahead and invalidate the sources */
			_InvalidateSources(service->_sources);
			
			if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
				
				/* Need to clean up the service discovery stuff if there is */
				if (service->_old_service) {
					
					/* Release the underlying service discovery reference */
					DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
					service->_old_service = NULL;
				}
			}
			
			else {
				
				/* Need to clean up the service discovery stuff if there is */
				if (service->_new_service) {
					
					/* Release the underlying service discovery reference */
					DNSServiceRefDeallocate(service->_new_service);
					service->_new_service = NULL;
					
					/* Dump all the records that may have been published */
					CFDictionaryRemoveAllValues(service->_records);
				}
			}
		}
		
		/* Clear any flags related to the sources */
		__CFBitClear(service->_flags, kFlagBitLegacyService);
		__CFBitClear(service->_flags, kFlagBitActiveResolve);
		__CFBitClear(service->_flags, kFlagBitActiveRegister);
		__CFBitClear(service->_flags, kFlagBitCancel);
		
		/* Zero out the callback and client context. */
		service->_callback = NULL;
		memset(&(service->_client), 0, sizeof(service->_client));
	}

	else {
		
		/*
		** Schedule any sources on the run loops and modes if they haven't been scheduled
		** already.  If there had previously been a callback, the sources will have
		** already been scheduled.
		*/
		if (!service->_callback && CFArrayGetCount(service->_sources))
			_ScheduleSources(service->_sources, service->_schedules);
		
		/* Save the client's new callback */
		service->_callback = clientCB;

		/* Copy the client's context */
		memmove(&(service->_client), clientContext, sizeof(service->_client));

		/* If there is user data and a retain method, call it. */
		if (service->_client.info && service->_client.retain)
			service->_client.info = (void*)(service->_client.retain(service->_client.info));
	}
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));

	return TRUE;
}



/* extern */ void
CFNetServiceScheduleWithRunLoop(CFNetServiceRef theService, CFRunLoopRef runLoop, CFStringRef runLoopMode) {

	__CFNetService* service = (__CFNetService*)theService;
	
	/* Lock down the service before work */
	__CFSpinLock(&(service->_lock));
	
	if (_SchedulesAddRunLoopAndMode(service->_schedules, runLoop, runLoopMode)) {

		int i, count = CFArrayGetCount(service->_sources);
		
		/* If there are current processes, need to schedule them. */
		for (i = 0; i < count; i++)
			_CFTypeScheduleOnRunLoop(CFArrayGetValueAtIndex(service->_sources, i), runLoop, runLoopMode);
	}
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
}


/* extern */ void
CFNetServiceUnscheduleFromRunLoop(CFNetServiceRef theService, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
	
	__CFNetService* service = (__CFNetService*)theService;

	/* Lock down the service before work */
	__CFSpinLock(&(service->_lock));
	
	if (_SchedulesRemoveRunLoopAndMode(service->_schedules, runLoop, runLoopMode)) {
		
		int i, count = CFArrayGetCount(service->_sources);
		
		/* If there are current processes, need to unschedule them. */
		for (i = 0; i < count; i++)
			_CFTypeUnscheduleFromRunLoop(CFArrayGetValueAtIndex(service->_sources, i), runLoop, runLoopMode);
    }

	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
}	



/* extern */ CFDictionaryRef
CFNetServiceCreateDictionaryWithTXTData(CFAllocatorRef alloc, CFDataRef txtRecord) {

	CFMutableDictionaryRef result = NULL;

	CFIndex len = CFDataGetLength(txtRecord);
	const void* txt = CFDataGetBytePtr(txtRecord);
	
	if ((len > 0) && (len < 65536)) {
		
		static const CFDictionaryKeyCallBacks kTXTDictionaryKeyCallBacks = {
			0,
			(CFDictionaryRetainCallBack)TXTDictionaryKeyRetain,
			(CFDictionaryReleaseCallBack)TXTDictionaryKeyRelease,
			CFCopyDescription,
			(CFDictionaryEqualCallBack)TXTDictionaryKeyEqual,
			(CFDictionaryHashCallBack)TXTDictionaryKeyHash
		};
		
		/* Get the number of keys */
		uint16_t i, count = TXTRecordGetCount(len, txt);
		result = CFDictionaryCreateMutable(alloc, 0, &kTXTDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
		
		if (result) {
			
			/* Iterate over all the keys */
			for (i = 0; i < count; i++) {
				
				char key[256];
				uint8_t valLen = 0;
				const void* value = NULL;
				
				/* Only go through the other stuff if it could cleanly get the key and value */
				if (kDNSServiceErr_NoError == TXTRecordGetItemAtIndex(len, txt, i, sizeof(key), key, &valLen, &value)) {
					
					/* A null value is kCFNull, otherwise it's a CFDataRef of the given length */
					CFTypeRef data = /*(value != NULL) ? CFDataCreate(alloc, value, valLen) :*/ kCFNull;
					CFStringRef str = CFStringCreateWithCString(alloc, key, kCFStringEncodingUTF8);
					
					if (value)
						data = CFDataCreate(alloc, value, valLen);
					
				
					/* Only add if key and value were created and the key doesn't exists already */
					if (data && str && CFStringGetLength(str) && !CFDictionaryGetValue(result, str))
						CFDictionaryAddValue(result, str, data);
					
					if (data)
						CFRelease(data);
					
					if (str)
						CFRelease(str);
				}
			}
		}
	}
	
	return result;
}


/* extern */ CFDataRef 
CFNetServiceCreateTXTDataWithDictionary(CFAllocatorRef alloc, CFDictionaryRef keyValuePairs) {
	
	CFDataRef result = NULL;

	CFStringRef* keys = NULL;
	CFTypeRef* values = NULL;
	CFIndex count = CFDictionaryGetCount(keyValuePairs);
	
	keys = CFAllocatorAllocate(alloc, count * sizeof(keys[0]), 0);
	values = CFAllocatorAllocate(alloc, count * sizeof(values[0]), 0);
	
	if (keys && values) {
		
		CFIndex i;
		TXTRecordRef txt;
		CFTypeID strType = CFStringGetTypeID();
		CFTypeID dataType = CFDataGetTypeID();
		UInt8 key[256];
		
		/* Grab the key/value pairs for iteration. */
		CFDictionaryGetKeysAndValues(keyValuePairs, (const void**)keys, (const void**)values);
		
		/* Create the txt record */
		TXTRecordCreate(&txt, 0, NULL);
		
		/* Iterate over the key/value pairs. */
		for (i = 0; i < count; i++) {
			
			CFIndex length, converted, used = 0;
			CFTypeID type = CFGetTypeID(values[i]);
			DNSServiceErrorType set = kDNSServiceErr_Unknown;
			
			/* Keys must be CFStrings */
			if (CFGetTypeID(keys[i]) != strType)
				break;
			
			length = CFStringGetLength(keys[i]);
			converted = CFStringGetBytes(keys[i], CFRangeMake(0, length), kCFStringEncodingASCII, 0, FALSE, key, sizeof(key), &used);
			
			/* The key has to be cleanly converted must be between 1 and 255 bytes long */
			if (!length || (converted < length) || (used >= sizeof(key)))
				break;
			
			/* Cap the string for dns_sd. */
			key[used] = '\0';
			
			/* String types are converted to raw bytes */
			if (type == strType) {
				
				UInt8 value[256];
				
				/* Convert the string to raw bytes using UTF8 */
				length = CFStringGetLength(values[i]);
				converted = CFStringGetBytes(values[i], CFRangeMake(0, length), kCFStringEncodingUTF8, 0, FALSE, value, sizeof(value), &used);
				
				/* The value has to be cleanly converted and can't be longer than 255 (0 is permitted) */
				if ((converted < length) || (used >= sizeof(key)))
					break;
					
				/* Set the raw bytes */
				set = TXTRecordSetValue(&txt, (const char*)key, used, value);
			}
			
			/* If it's data, it needs to be in the range of 0 and 255, inclusive. */
			else if ((type == dataType) &&
					 (CFDataGetLength((CFDataRef)(values[i])) < 256) &&
					 (CFDataGetLength((CFDataRef)(values[i])) >= 0))
			{
				/* Set the raw bytes from the data */
				set = TXTRecordSetValue(&txt,
										(const char*)key,
										CFDataGetLength((CFDataRef)(values[i])),
										CFDataGetBytePtr((CFDataRef)(values[i])));
			}
			
			/* Allow null for a key with no value */
			else if (values[i] == kCFNull) {
				
				/* Sets the key to no value */
				set = TXTRecordSetValue(&txt, (const char*)key, 0, NULL);
			}
			
			/* Bad type */
			else
				break;
			
			/* If couldn't set, need to fail out. */
			if (set != kDNSServiceErr_NoError)
				break;
		}
		
		/* If all the keys and values were processed, create the data for the txt record. */
		if (i == count)
			result = CFDataCreate(alloc, TXTRecordGetBytesPtr(&txt), TXTRecordGetLength(&txt));
		
		TXTRecordDeallocate(&txt);
	}
	
	if (keys)
		CFAllocatorDeallocate(alloc, keys);
	
	if (values)
		CFAllocatorDeallocate(alloc, values);
	
	return result;
}


#if 0
#pragma mark -
#pragma mark Extern Function Definitions (SPI)
#endif

/* extern */ CFNetServiceRef
_CFNetServiceCreateCommon(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port) {
	
	__CFNetService* result = NULL;
	
	/* Domain, type, and name must be specified */
	if (domain && type && name) {
		
		CFTypeID class_type = CFNetServiceGetTypeID();
		
		if (class_type != _kCFRuntimeNotATypeID) {
			result = (__CFNetService*)_CFRuntimeCreateInstance(alloc,
															   class_type,
															   sizeof(result[0]) - sizeof(CFRuntimeBase),
															   NULL);
		}
		
		if (result) {
			
			CFDictionaryKeyCallBacks keys = {0, NULL, NULL, NULL, NULL, NULL};
			CFDictionaryValueCallBacks values = {0, NULL, NULL, NULL, NULL};
			
			/* Save a copy of the base so it's easier to zero the struct */
			CFRuntimeBase copy = result->_base;
			
			/* Clear everything. */
			memset(result, 0, sizeof(result[0]));
			
			/* Put back the base */
			memmove(&(result->_base), &copy, sizeof(result->_base));
			
			/* Create the dictionary of information */
			result->_info = CFDictionaryCreateMutable(alloc, 0, &keys, &kCFTypeDictionaryValueCallBacks);
			
			/* Create the dictionary for holding the published records */
			result->_records = CFDictionaryCreateMutable(alloc, 0, &keys, &values);
			
			/* Create the list of loops and modes */
			result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
			
			/* Create the list of sources */
			result->_sources = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
			
			/* Save the information if everything is good. */
			if (result->_info && result->_schedules && result->_records && result->_sources) {
				
				CFDictionaryAddValue(result->_info, (const void*)_kCFNetServiceDomain, domain);
				CFDictionaryAddValue(result->_info, (const void*)_kCFNetServiceType, type);
				CFDictionaryAddValue(result->_info, (const void*)_kCFNetServiceName, name);
				
				result->_port = port;
			}
			
			/* Failure needs to release and return null */
			else {
				CFRelease((CFTypeRef)result);
				result = NULL;
			}
		}
	}
	
	return (CFNetServiceRef)result;
}


/* extern */ CFTypeRef 
CFNetServiceGetInfo(CFNetServiceRef theService, UInt32 property) {
	
	CFTypeRef result = NULL;
	__CFNetService* service = (__CFNetService*)theService;
	
	/* Lock the service. */
	__CFSpinLock(&(service->_lock));
	
	/* Get the important bits */
	result = (CFTypeRef)CFDictionaryGetValue(service->_info, (const void*)property);
	
	/* Unlock the service again. */
	__CFSpinUnlock(&(service->_lock));
	
	return result;
}


/* extern */ Boolean 
CFNetServiceSetInfo(CFNetServiceRef theService, UInt32 property, CFTypeRef value) {	
	
	return _ServiceSetInfo((__CFNetService*)theService, property, value, TRUE);
}


/* extern */ Boolean
_CFNetServiceSetInfoNoPublish(CFNetServiceRef theService, UInt32 property, CFTypeRef value) {
	
	return _ServiceSetInfo((__CFNetService*)theService, property, value, FALSE);
}


#if 0
#pragma mark -
#pragma mark Deprecated API
#endif


/* extern */ dns_service_discovery_ref
_CFNetServiceGetDNSServiceDiscovery(CFNetServiceRef theService) {
	
	dns_service_discovery_ref result = NULL;
	__CFNetService* s = (__CFNetService*)theService;
	
	__CFSpinLock(&(s->_lock));
	
	result = s->_old_service;
	
	__CFSpinUnlock(&(s->_lock));
	
	return result;
}


/* extern */ CFStringRef
CFNetServiceGetProtocolSpecificInformation(CFNetServiceRef theService) {
	
	
	CFTypeRef result = CFNetServiceGetInfo(theService, _kCFNetServiceTXT);
	
	/*
	 ** This shouldn't really happen.  This is here in order to protect
	 ** against using the new TXT calls in conjunction with the old
	 ** deprecated calls.
	 */
	if (result && (CFGetTypeID(result) != CFStringGetTypeID()))
		result = NULL;
	
	return (CFStringRef)result;
}


/* extern */ void
CFNetServiceSetProtocolSpecificInformation(CFNetServiceRef theService, CFStringRef theInfo) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	__CFSpinLock(&(service->_lock));
	
	/* If not registered on the network, simply save the value */
	if (!service->_old_service)
		CFDictionarySetValue(service->_info, (const void*)_kCFNetServiceTXT, theInfo);
	
	else {
		char str[1024];
		CFIndex bytesUsed = 0;
		
		/* NOTE that the behavior here is legacy and should not change. */
		if (theInfo) {
			CFStringGetBytes(theInfo,
							 CFRangeMake(0, CFStringGetLength(theInfo)),
							 kCFStringEncodingUTF8,
							 0,
							 FALSE,
							 (UInt8*)str,
							 sizeof(str) - 1,
							 &bytesUsed);
		}
		str[bytesUsed] = '\0';
		
		/* Send it to the wire */
		DNSServiceRegistrationUpdateRecord_Deprecated(service->_old_service, 0, bytesUsed + 1, str, 0);
	}
	
	__CFSpinUnlock(&(service->_lock));
}


/* extern */ Boolean
CFNetServiceRegister(CFNetServiceRef theService, CFStreamError* error) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	CFStreamError extra;
	Boolean result = FALSE;
	
	if (!error)
		error = &extra;
	
	memset(error, 0, sizeof(error[0]));
	
	/*
	** Retain so it doesn't go away underneath in the case of a callout.  This is really
	** no worry for async, but makes the memmove for the error more difficult to place
	** for synchronous without it being here.
	*/
	CFRetain(theService);
	
	/* Lock down the service to start */
	__CFSpinLock(&(service->_lock));
	
	do {
		
		int i;
		char properties[4][1024];
		CFMachPortRef prt = NULL;
		CFAllocatorRef alloc = CFGetAllocator(theService);
		CFMachPortContext ctxt = {0, service, CFRetain, CFRelease, NULL};
		UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain, _kCFNetServiceTXT};
		
		/* Check to see if there is an ongoing process already */
		if (CFArrayGetCount(service->_sources)) {
		
			/* If there's already mdns activity, don't allow another. */
			if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
				service->_error.error = kCFNetServicesErrorInProgress;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			/* It's just the cancel that hasn't fired yet, so cancel it. */
			else {
				
				/* Remove the cancel from run loops and modes */
				_UnscheduleSources(service->_sources, service->_schedules);
				
				/* Invalidate the run loop source */
				_InvalidateSources(service->_sources);
			}
		}
		
		/* Get the raw data for the properties to send down to mdns */
		for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
			
			CFStringRef value = (CFStringRef)CFDictionaryGetValue(service->_info, (const void*)(keys[i]));
			if (!value)
				properties[i][0] = '\0';
			else {
				CFIndex used;
				CFStringGetBytes(value,
								 CFRangeMake(0, CFStringGetLength(value)),
								 kCFStringEncodingUTF8,
								 0,
								 FALSE,
								 (UInt8*)properties[i],
								 sizeof(properties[i]) - 1,
								 &used);
				properties[i][used] = '\0';
			}
		}
		
		/* Create the registration */
		service->_old_service = DNSServiceRegistrationCreate_Deprecated(properties[0],
																		 properties[1],
																		 properties[2],
																		 htons((service->_port & 0x0000FFFF)),
																		 properties[3],
																		 _LegacyRegistrationReply,
																		 service);
														 
		if (!service->_old_service) {
		
			/* Set the error to errno if there is one. */
			service->_error.error = errno;
			if (service->_error.error)
				service->_error.domain = kCFStreamErrorDomainPOSIX;
				
			/* Some unknown error occurred. */
			else {
				service->_error.error = kCFNetServicesErrorUnknown;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			break;
		}
		
		/* Create a CFMachPort wrapper on the register */
		prt = CFMachPortCreateWithPort(alloc,
									   DNSServiceDiscoveryMachPort_Deprecated(service->_old_service),
									   _MachPortCallBack,
									   &ctxt,
									   NULL);
													 
		/* Need to bail if it failed */
		if (!prt) {
			
			/* Set error to whatever happened. */
			service->_error.error = errno;
			if (!service->_error.error)
				service->_error.error = ENOMEM;
				
			service->_error.domain = kCFStreamErrorDomainPOSIX;
		
			/* Stop right away on failure */
			DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
			
			break;
		}
		
		/* Add the mach port to the list of sources */
		CFArrayAppendValue(service->_sources, prt);
		CFRelease(prt);
		
		/* Start with no error. */
		service->_error.error = 0;
		service->_error.domain = 0;
		
		/* Set the flags indicating a legacy registration */
		__CFBitSet(service->_flags, kFlagBitLegacyService);
		__CFBitSet(service->_flags, kFlagBitActiveRegister);
		
		/* Async mode is complete at this point */
		if (CFArrayGetCount(service->_schedules)) {
			
			/* Schedule the sources on the run loops and modes. */
			_ScheduleSources(service->_sources, service->_schedules);
			
			/* It's now succeeded. */
			result = TRUE;
		}
		
		/* Go into synchronous mode. */
		else {
			
			/* Unlock the service */
			__CFSpinUnlock(&(service->_lock));
			
			/* Wait for synchronous return */
			result = _ServiceBlockUntilComplete(service);
			
			/* Lock down the service */
			__CFSpinLock(&(service->_lock));
		}
		
	} while (0);
	
	/* Copy the error. */
	memmove(error, &service->_error, sizeof(error[0]));
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
	
	/* Release the earlier retain. */
	CFRelease(theService);
	
	return result;
}


/* extern */ Boolean
CFNetServiceResolve(CFNetServiceRef theService, CFStreamError* error) {
	
	__CFNetService* service = (__CFNetService*)theService;
	
	CFStreamError extra;
	Boolean result = FALSE;
	
	if (!error)
		error = &extra;
	
	memset(error, 0, sizeof(error[0]));
	
	/*
	** Retain so it doesn't go away underneath in the case of a callout.  This is really
	** no worry for async, but makes the memmove for the error more difficult to place
	** for synchronous without it being here.
	*/
	CFRetain(theService);
	
	/* Lock down the service to start */
	__CFSpinLock(&(service->_lock));
	
	do {
		
		int i;
		char properties[3][1024];
		CFMachPortRef prt;
		CFAllocatorRef alloc = CFGetAllocator(theService);
		CFMachPortContext ctxt = {0, service, CFRetain, CFRelease, NULL};
		UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain};
		
		/* Check to see if there is an ongoing process already */
		if (CFArrayGetCount(service->_sources)) {
		
			/* If there's already mdns activity, don't allow another. */
			if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
				service->_error.error = kCFNetServicesErrorInProgress;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			/* It's just the cancel that hasn't fired yet, so cancel it. */
			else {
				
				/* Remove the cancel from run loops and modes */
				_UnscheduleSources(service->_sources, service->_schedules);
				
				/* Invalidate the run loop source */
				_InvalidateSources(service->_sources);
			}
		}
		
		/* Get the raw data for the properties to send down to mdns */
		for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
			
			CFStringRef value = (CFStringRef)CFDictionaryGetValue(service->_info, (const void*)(keys[i]));
			if (!value)
				properties[i][0] = '\0';
			else {
				CFIndex used;
				CFStringGetBytes(value,
								 CFRangeMake(0, CFStringGetLength(value)),
								 kCFStringEncodingUTF8,
								 0,
								 FALSE,
								 (UInt8*)properties[i],
								 sizeof(properties[i]) - 1,
								 &used);
				properties[i][used] = '\0';
			}
		}
		
		/* Create the resolve */
		service->_old_service = DNSServiceResolverResolve_Deprecated(properties[0],
																	 properties[1],
																	 properties[2],
																	 _LegacyResolverReply,
																	 service);
														 
		if (!service->_old_service) {
		
			/* Set the error to errno if there is one. */
			service->_error.error = errno;
			if (service->_error.error)
				service->_error.domain = kCFStreamErrorDomainPOSIX;
				
			/* Some unknown error occurred. */
			else {
				service->_error.error = kCFNetServicesErrorUnknown;
				service->_error.domain = kCFStreamErrorDomainNetServices;
			}
			
			break;
		}
		
		/* Create a CFMachPort wrapper on the register */
		prt = CFMachPortCreateWithPort(alloc,
									   DNSServiceDiscoveryMachPort_Deprecated(service->_old_service),
									   _MachPortCallBack,
									   &ctxt,
									   NULL);
													 
		/* Need to bail if it failed */
		if (!prt) {
			
			/* Set error to whatever happened. */
			service->_error.error = errno;
			if (!service->_error.error)
				service->_error.error = ENOMEM;
				
			service->_error.domain = kCFStreamErrorDomainPOSIX;
		
			/* Stop right away on failure */
			DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
			
			break;
		}
		
		/* Remove any addresses so the resolve fills in new. */
		CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceAddress);
		
		/* Add the mach port to the list of sources */
		CFArrayAppendValue(service->_sources, prt);
		CFRelease(prt);
		
		/* Start with no error. */
		service->_error.error = 0;
		service->_error.domain = 0;
		
		/* Set the flags indicating a legacy resolve */
		__CFBitSet(service->_flags, kFlagBitLegacyService);
		__CFBitSet(service->_flags, kFlagBitActiveResolve);
		
		/* Async mode is complete at this point */
		if (CFArrayGetCount(service->_schedules)) {
			
			/* Schedule the sources on the run loops and modes. */
			_ScheduleSources(service->_sources, service->_schedules);
			
			/* It's now succeeded. */
			result = TRUE;
		}
		
		/* Go into synchronous mode. */
		else {
			
			/* Unlock the service */
			__CFSpinUnlock(&(service->_lock));
			
			/* Wait for synchronous return */
			result = _ServiceBlockUntilComplete(service);
			
			/* Lock down the service */
			__CFSpinLock(&(service->_lock));
		}
		
	} while (0);
	
	/* Copy the error. */
	memmove(error, &service->_error, sizeof(error[0]));
	
	/* Unlock the service */
	__CFSpinUnlock(&(service->_lock));
	
	/* Release the earlier retain. */
	CFRelease(theService);
	
	return result;
}