#if 0
#pragma mark Includes
#endif
#include <CFNetwork/CFNetwork.h>
#include <CFNetwork/CFNetworkPriv.h>
#include "CFNetworkInternal.h"
#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
const SInt32 kCFStreamErrorDomainNetServices = 10;
const SInt32 kCFStreamErrorDomainMach = 11;
#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
static const char _kCFNetServiceClassName[] = "CFNetService";
#if 0
#pragma mark -
#pragma mark Enum Values
#endif
enum {
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;
CFMutableArrayRef _sources;
UInt32 _interface;
union {
dns_service_discovery_ref _old_service;
DNSServiceRef _new_service;
};
DNSServiceRef _a;
DNSServiceRef _aaaa;
CFMutableDictionaryRef _records;
CFMutableArrayRef _schedules;
CFNetServiceClientCallBack _callback;
CFNetServiceClientContext _client;
} __CFNetService;
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern CFNetServiceRef _CFNetServiceCreateCommon(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port);
extern dns_service_discovery_ref _CFNetServiceGetDNSServiceDiscovery(CFNetServiceRef theService);
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
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
void
_ServiceDestroy(__CFNetService* service) {
__CFSpinLock(&(service->_lock));
if (service->_client.info && service->_client.release)
service->_client.release(service->_client.info);
if (service->_sources) {
if (service->_schedules)
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
CFRelease(service->_sources);
}
if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
if (service->_old_service) {
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
}
}
else {
if (service->_new_service) {
DNSServiceRefDeallocate(service->_new_service);
}
}
if (service->_a)
DNSServiceRefDeallocate(service->_a);
if (service->_aaaa)
DNSServiceRefDeallocate(service->_aaaa);
if (service->_records)
CFRelease(service->_records);
if (service->_info)
CFRelease(service->_info);
if (service->_schedules)
CFRelease(service->_schedules);
}
Boolean
_ServiceEqual(__CFNetService* s1, __CFNetService* s2) {
Boolean result = FALSE;
CFStringRef t1, t2;
__CFSpinLock(&s1->_lock);
__CFSpinLock(&s2->_lock);
t1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceType);
t2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceType);
if (CFEqual(t1, t2)) {
CFStringRef d1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceDomain);
CFStringRef d2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceDomain);
if (CFEqual(d1, _kCFNetServiceEmptyString))
d1 = _kCFNetServiceEmptyString;
if (CFEqual(d2, _kCFNetServiceEmptyString))
d2 = _kCFNetServiceEmptyString;
if (CFEqual(d1, d2)) {
CFStringRef n1 = CFDictionaryGetValue(s1->_info, (const void*)_kCFNetServiceName);
CFStringRef n2 = CFDictionaryGetValue(s2->_info, (const void*)_kCFNetServiceName);
if (CFEqual(n1, n2))
result = TRUE;
else if (CFEqual(n1, _kCFNetServiceEmptyString) || CFEqual(n2, _kCFNetServiceEmptyString)) {
CFStringRef computer_name = SCDynamicStoreCopyLocalHostName(NULL);
if (computer_name) {
if (CFEqual(n1, _kCFNetServiceEmptyString))
n1 = computer_name;
if (CFEqual(n2, _kCFNetServiceEmptyString))
n2 = computer_name;
if (CFEqual(n1, n2))
result = TRUE;
CFRelease(computer_name);
}
}
}
}
__CFSpinUnlock(&s1->_lock);
__CFSpinUnlock(&s2->_lock);
return result;
}
CFHashCode
_ServiceHash(__CFNetService* service) {
CFHashCode result;
CFStringRef name;
__CFSpinLock(&service->_lock);
name = CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceName);
result = CFHash(name);
if (CFEqual(name, _kCFNetServiceEmptyString)) {
name = SCDynamicStoreCopyLocalHostName(NULL);
if (name) {
CFHash(name);
CFRelease(name);
}
}
__CFSpinUnlock(&service->_lock);
return result;
}
CFStringRef
_ServiceDescribe(__CFNetService* service) {
CFStringRef result = NULL;
__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));
__CFSpinUnlock(&service->_lock);
return result;
}
Boolean
_ServiceSetInfo(__CFNetService* service, UInt32 property, CFTypeRef value, Boolean publish) {
Boolean result = FALSE;
__CFSpinLock(&(service->_lock));
if (!__CFBitIsSet(service->_flags, kFlagBitLegacyService) &&
!__CFBitIsSet(service->_flags, kFlagBitActiveResolve))
{
if (value)
CFDictionarySetValue(service->_info, (const void*)property, value);
else
CFDictionaryRemoveValue(service->_info, (const void*)property);
result = TRUE;
if (publish && service->_new_service) {
DNSServiceErrorType err = 0;
if (property == _kCFNetServiceTXT) {
err = DNSServiceUpdateRecord(service->_new_service,
NULL,
0,
value ? CFDataGetLength(value) : 0,
value ? CFDataGetBytePtr(value) : NULL,
0);
if (err)
result = FALSE;
}
else if ((0xFFFF0000 & property) == 0x00010000) {
DNSRecordRef record = (DNSRecordRef)CFDictionaryGetValue(service->_records, (const void*)property);
if (!value) {
if (record)
err = DNSServiceRemoveRecord(service->_new_service, record, 0);
CFDictionaryRemoveValue(service->_records, (const void*)property);
}
else if (record) {
err = DNSServiceUpdateRecord(service->_new_service,
record,
0,
CFDataGetLength(value),
CFDataGetBytePtr(value),
0);
}
else {
err = DNSServiceAddRecord(service->_new_service,
&record,
0,
(0x0000FFFF & property),
CFDataGetLength(value),
CFDataGetBytePtr(value),
0);
CFDictionaryAddValue(service->_records, (const void*)property, record);
}
if (err) {
if (record)
DNSServiceRemoveRecord(service->_new_service, record, 0);
CFDictionaryRemoveValue(service->_records, (const void*)property);
result = FALSE;
}
}
}
}
__CFSpinUnlock(&(service->_lock));
return result;
}
void
_ServiceCancel(__CFNetService* service) {
CFNetServiceClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFRetain(service);
__CFSpinLock(&service->_lock);
if (CFArrayGetCount(service->_sources)) {
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
}
__CFBitClear(service->_flags, kFlagBitCancel);
__CFSpinUnlock(&service->_lock);
if (cb)
cb((CFNetServiceRef)service, &error, info);
CFRelease(service);
}
void
_ServiceCancelDNSService_NoLock(__CFNetService* service, DNSServiceRef cancel) {
CFTypeID t = CFSocketGetTypeID();
int fd = DNSServiceRefSockFD(cancel);
int i, count = CFArrayGetCount(service->_sources);
for (i = 0; i < count; i++) {
CFTypeRef sock = CFArrayGetValueAtIndex(service->_sources, i);
if (t == CFGetTypeID(sock)) {
if (CFSocketGetNative((CFSocketRef)sock) == fd) {
_CFTypeUnscheduleFromMultipleRunLoops(sock, service->_schedules);
_CFTypeInvalidate(sock);
DNSServiceRefDeallocate(cancel);
CFArrayRemoveValueAtIndex(service->_sources, i);
break;
}
}
}
}
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;
if (rrtype == ns_t_invalid) {
which = &(service->_new_service);
service->_interface = 0;
service->_error.error = DNSServiceResolve(which,
0,
service->_interface,
name,
regtype,
domain,
_ResolveReply,
service);
}
else {
DNSServiceQueryRecordReply reply = NULL;
switch (rrtype) {
case ns_t_a:
which = &(service->_a);
reply = _AddressQueryRecordReply;
cb = _AQuerySocketCallBack;
break;
case ns_t_aaaa:
which = &(service->_aaaa);
reply = _AddressQueryRecordReply;
cb = _AAAAQuerySocketCallBack;
break;
default:
break;
}
if (which) {
service->_error.error = DNSServiceQueryRecord(which,
0,
service->_interface,
name,
rrtype,
ns_c_in,
reply,
service);
}
}
if (service->_error.error)
service->_error.domain = kCFStreamErrorDomainNetServices;
else {
CFSocketContext ctxt = {0, service, CFRetain, CFRelease, NULL};
CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator((CFNetServiceRef)service),
DNSServiceRefSockFD(*which),
kCFSocketReadCallBack,
cb,
&ctxt);
if (sock) {
CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate);
CFArrayAppendValue(service->_sources, sock);
if (schedule)
_CFTypeScheduleOnMultipleRunLoops(sock, service->_schedules);
CFRelease(sock);
}
else {
service->_error.error = errno;
if (!service->_error.error)
service->_error.error = ENOMEM;
service->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceRefDeallocate(*which);
*which = NULL;
}
}
}
Boolean
_ServiceBlockUntilComplete(__CFNetService* service) {
Boolean result = TRUE;
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFNetServiceScheduleWithRunLoop((CFNetServiceRef)service, rl, _kCFNetServiceBlockingMode);
__CFSpinLock(&(service->_lock));
while (CFArrayGetCount(service->_sources)) {
__CFSpinUnlock(&(service->_lock));
CFRunLoopRunInMode(_kCFNetServiceBlockingMode, DBL_MAX, TRUE);
__CFSpinLock(&(service->_lock));
}
if (service->_error.error)
result = FALSE;
__CFSpinUnlock(&(service->_lock));
CFNetServiceUnscheduleFromRunLoop((CFNetServiceRef)service, rl, _kCFNetServiceBlockingMode);
return result;
}
#if 0
#pragma mark * Service Discovery CallBacks
#endif
void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
DNSServiceErrorType err;
__CFNetService* service = info;
(void)s;
(void)type;
(void)address;
(void)data;
CFRetain(service);
err = DNSServiceProcessResult(service->_new_service);
if (err) {
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, err};
CFNetServiceClientCallBack cb = NULL;
__CFSpinLock(&service->_lock);
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
DNSServiceRefDeallocate(service->_new_service);
service->_new_service = NULL;
__CFBitClear(service->_flags, kFlagBitActiveRegister);
cb = service->_callback;
info = service->_client.info;
memmove(&(service->_error), &error, sizeof(error));
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
}
}
CFRelease(service);
}
void
_AQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
DNSServiceErrorType err;
__CFNetService* service = info;
(void)s;
(void)type;
(void)address;
(void)data;
CFRetain(service);
err = DNSServiceProcessResult(service->_a);
if (err)
_AddressQueryRecordReply(service->_a, 0, 0, err, NULL, 0, 0, 0, NULL, 0, info);
CFRelease(service);
}
void
_AAAAQuerySocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
DNSServiceErrorType err;
__CFNetService* service = info;
(void)s;
(void)type;
(void)address;
(void)data;
CFRetain(service);
err = DNSServiceProcessResult(service->_aaaa);
if (err)
_AddressQueryRecordReply(service->_aaaa, 0, 0, err, NULL, 0, 0, 0, NULL, 0, info);
CFRelease(service);
}
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;
CFRetain(service);
__CFSpinLock(&service->_lock);
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 = errorCode;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
if (values[i]) {
CFStringRef string = CFStringCreateWithCString(alloc, values[i], kCFStringEncodingUTF8);
if (string) {
CFDictionarySetValue(service->_info, (const void*)(keys[i]), string);
CFRelease(string);
}
}
}
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
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;
CFRetain(service);
__CFSpinLock(&service->_lock);
if (service->_new_service) {
if (errorCode) {
service->_error.error = errorCode;
service->_error.domain = kCFStreamErrorDomainNetServices;
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
if (service->_a) {
DNSServiceRefDeallocate(service->_a);
service->_a = NULL;
}
if (service->_aaaa) {
DNSServiceRefDeallocate(service->_aaaa);
service->_aaaa = NULL;
}
__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;
service->_port = ntohs(port);
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
if (txt) {
CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTXT, txt);
CFRelease(txt);
}
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTargetHost);
if (tgt) {
CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTargetHost, tgt);
CFRelease(tgt);
}
service->_interface = interfaceIndex;
_ServiceCreateQuery_NoLock(service, ns_t_a, hosttarget, NULL, NULL, TRUE);
_ServiceCreateQuery_NoLock(service, ns_t_aaaa, hosttarget, NULL, NULL, TRUE);
if (service->_error.error) {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
if (service->_a) {
DNSServiceRefDeallocate(service->_a);
service->_a = NULL;
}
if (service->_aaaa) {
DNSServiceRefDeallocate(service->_aaaa);
service->_aaaa = NULL;
}
__CFBitSet(service->_flags, kFlagBitAComplete);
__CFBitSet(service->_flags, kFlagBitAAAAComplete);
}
}
_ServiceCancelDNSService_NoLock(service, service->_new_service);
service->_new_service = NULL;
if (__CFBitIsSet(service->_flags, kFlagBitAComplete) &&
__CFBitIsSet(service->_flags, kFlagBitAAAAComplete))
{
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
void
_LongTimerCallBack(CFRunLoopTimerRef timer, void *context) {
__CFNetService* service = (__CFNetService*)context;
CFNetServiceClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFRetain(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 (service->_new_service || !list || (CFArrayGetCount(list) == 0)) {
service->_error.error = kCFNetServicesErrorTimeout;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
for (i = 0; i < (sizeof(lookups) / sizeof(lookups[0])); i++) {
if (*lookups[i]) {
DNSServiceRefDeallocate(*lookups[i]);
*lookups[i] = NULL;
}
}
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
void
_ShortTimerCallBack(CFRunLoopTimerRef timer, void *context) {
__CFNetService* service = (__CFNetService*)context;
CFNetServiceClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
Boolean a_complete, aaaa_complete;
CFRetain(service);
__CFSpinLock(&service->_lock);
{
int fd = -1;
CFSocketCallBack c = NULL;
if (service->_a) {
fd = DNSServiceRefSockFD(service->_a);
c = _AQuerySocketCallBack;
}
else if (service->_aaaa) {
fd = DNSServiceRefSockFD(service->_aaaa);
c = _AAAAQuerySocketCallBack;
}
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);
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;
}
}
if (s) {
__CFSpinUnlock(&service->_lock);
c(s, kCFSocketReadCallBack, NULL, NULL, service);
__CFSpinLock(&service->_lock);
}
}
}
}
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)
};
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
for (i = 0; i < (sizeof(lookups) / sizeof(lookups[0])); i++) {
if (*lookups[i]) {
DNSServiceRefDeallocate(*lookups[i]);
*lookups[i] = NULL;
}
}
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
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;
CFRetain(service);
__CFSpinLock(&service->_lock);
if (service->_a || service->_aaaa) {
UInt32 service_flags_copy = service->_flags;
CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
CFMutableArrayRef list = (CFMutableArrayRef)CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress);
if (!list) {
list = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (list) {
CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceAddress, list);
CFRelease(list);
}
}
if (list) {
CFDataRef addr = _CFDataCreateWithRecord(alloc,
rrtype,
rdlen,
rdata,
(service->_port & 0x0000FFFF),
interfaceIndex);
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 (!(flags & kDNSServiceFlagsMoreComing) || errorCode) {
_ServiceCancelDNSService_NoLock(service, sdRef);
if (rrtype == ns_t_a) {
service->_a = NULL;
__CFBitSet(service->_flags, kFlagBitAComplete);
}
else if (rrtype == ns_t_aaaa) {
service->_aaaa = NULL;
__CFBitSet(service->_flags, kFlagBitAAAAComplete);
}
}
if (__CFBitIsSet(service->_flags, kFlagBitAComplete) &&
__CFBitIsSet(service->_flags, kFlagBitAAAAComplete) &&
!service->_new_service)
{
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
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};
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(alloc,
CFAbsoluteTimeGetCurrent() + kCFNetServiceShortTimeout,
0.0,
0,
0,
_ShortTimerCallBack,
&c);
if (timer) {
CFArrayAppendValue(service->_sources, timer);
_CFTypeScheduleOnMultipleRunLoops(timer, service->_schedules);
CFRelease(timer);
}
}
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
#if 0
#pragma mark * Utility Functions
#endif
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));
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));
}
}
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;
}
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);
}
}
void
_DictionaryApplier(const void *key, const void *value, void *context) {
CFTypeID t = CFGetTypeID((CFTypeRef)value);
if (t == CFStringGetTypeID()) {
CFStringRef c = CFStringCreateCopy(CFGetAllocator((CFTypeRef)context), (CFStringRef)value);
if (c) {
CFDictionaryAddValue((CFMutableDictionaryRef)context, key, c);
CFRelease(c);
}
}
else if (t == CFArrayGetTypeID()) {
CFArrayRef c = CFArrayCreateCopy(CFGetAllocator((CFTypeRef)context), (CFArrayRef)value);
if (c) {
CFDictionaryAddValue((CFMutableDictionaryRef)context, key, c);
CFRelease(c);
}
}
else
CFDictionaryAddValue((CFMutableDictionaryRef)context, key, value);
}
void
_ScheduleSources(CFArrayRef sources, CFArrayRef schedules) {
int i, count = CFArrayGetCount(sources);
for (i = 0; i < count; i++)
_CFTypeScheduleOnMultipleRunLoops(CFArrayGetValueAtIndex(sources, i), schedules);
}
void
_UnscheduleSources(CFArrayRef sources, CFArrayRef schedules) {
int i, count = CFArrayGetCount(sources);
for (i = 0; i < count; i++)
_CFTypeUnscheduleFromMultipleRunLoops(CFArrayGetValueAtIndex(sources, i), schedules);
}
void
_InvalidateSources(CFMutableArrayRef sources) {
int i, count = CFArrayGetCount(sources);
for (i = 0; i < count; i++)
_CFTypeInvalidate(CFArrayGetValueAtIndex(sources, i));
CFArrayRemoveAllValues(sources);
}
#if 0
#pragma mark * Legacy Support
#endif
void
_MachPortCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) {
DNSServiceDiscovery_handleReply(msg);
}
void
_LegacyRegistrationReply(int errorCode, void* context) {
__CFNetService* service = (__CFNetService*)context;
CFNetServiceClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFRetain(service);
__CFSpinLock(&service->_lock);
if (service->_old_service) {
if (errorCode) {
service->_error.error = kCFNetServicesErrorCollision;
service->_error.domain = kCFStreamErrorDomainNetServices;
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
service->_old_service = NULL;
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitClear(service->_flags, kFlagBitActiveResolve);
cb = service->_callback;
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
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;
CFRetain(service);
__CFSpinLock(&service->_lock);
if (service->_old_service) {
CFAllocatorRef alloc = CFGetAllocator((CFNetServiceRef)service);
CFMutableArrayRef list = (CFMutableArrayRef)CFDictionaryGetValue(service->_info, (const void*)_kCFNetServiceAddress);
if (!list) {
list = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (list) {
CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceAddress, list);
CFRelease(list);
}
}
if (list) {
int i;
for (i = CFArrayGetCount(list) - 1; i >= 0; i--) {
struct sockaddr* saved = (struct sockaddr*)CFDataGetBytePtr((CFDataRef)CFArrayGetValueAtIndex(list, i));
int compare = (saved->sa_family == AF_INET) ? 8 : saved->sa_len;
if (!memcmp(saved, address, compare))
break;
}
if (i < 0) {
CFDataRef data = CFDataCreate(alloc, (const UInt8*)address, address->sa_len);
if (data) {
CFArrayAppendValue(list, data);
CFRelease(data);
}
}
}
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
if (txtRecord) {
CFStringRef t = CFStringCreateWithCString(alloc, txtRecord, kCFStringEncodingUTF8);
if (t) {
CFDictionaryAddValue(service->_info, (const void*)_kCFNetServiceTXT, t);
CFRelease(t);
}
}
cb = service->_callback;
if (!cb) {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
service->_old_service = NULL;
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitClear(service->_flags, kFlagBitActiveRegister);
}
memmove(&error, &(service->_error), sizeof(error));
info = service->_client.info;
}
__CFSpinUnlock(&service->_lock);
if (cb) {
cb((CFNetServiceRef)service, &error, info);
}
CFRelease(service);
}
#if 0
#pragma mark * TXT Dictionary Key Callbacks
#endif
const void*
TXTDictionaryKeyRetain(CFAllocatorRef allocator, CFStringRef key) {
(void)allocator;
return (const void*)CFRetain(key);
}
void
TXTDictionaryKeyRelease(CFAllocatorRef allocator, CFStringRef key) {
(void)allocator;
CFRelease(key);
}
Boolean
TXTDictionaryKeyEqual(CFStringRef key1, CFStringRef key2) {
return (CFStringCompare(key1, key2, kCFCompareCaseInsensitive) == kCFCompareEqualTo);
}
CFHashCode
TXTDictionaryKeyHash(CFStringRef key) {
return (CFHashCode)CFStringGetLength(key);
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif
CFTypeID
CFNetServiceGetTypeID(void) {
_CFDoOnce(&_kCFNetServiceRegisterClass, _CFNetServiceRegisterClass);
return _kCFNetServiceTypeID;
}
CFNetServiceRef
CFNetServiceCreate(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port) {
CFNetServiceRef result = NULL;
if (domain && type && name) {
CFMutableStringRef d = CFStringCreateMutableCopy(alloc, 0, domain);
CFMutableStringRef t = CFStringCreateMutableCopy(alloc, 0, type);
CFMutableStringRef n = CFStringCreateMutableCopy(alloc, 0, name);
if (d && t && n) {
CFStringNormalize(d, kCFStringNormalizationFormC);
CFStringNormalize(t, kCFStringNormalizationFormC);
CFStringNormalize(n, kCFStringNormalizationFormC);
result = _CFNetServiceCreateCommon(alloc, d, t, n, port);
}
if (d)
CFRelease(d);
if (t)
CFRelease(t);
if (n)
CFRelease(n);
}
return (CFNetServiceRef)result;
}
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};
CFRuntimeBase copy = result->_base;
memset(result, 0, sizeof(result[0]));
memmove(&(result->_base), ©, sizeof(result->_base));
result->_info = CFDictionaryCreateMutable(alloc, 0, &keys, &kCFTypeDictionaryValueCallBacks);
result->_records = CFDictionaryCreateMutable(alloc, 0, &keys, &values);
if (result->_info)
CFDictionaryApplyFunction(s->_info, _DictionaryApplier, result->_info);
result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
result->_sources = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
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;
}
CFStringRef
CFNetServiceGetDomain(CFNetServiceRef theService) {
return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceDomain);
}
CFStringRef
CFNetServiceGetType(CFNetServiceRef theService) {
return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceType);
}
CFStringRef
CFNetServiceGetName(CFNetServiceRef theService) {
return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceName);
}
CFArrayRef
CFNetServiceGetAddressing(CFNetServiceRef theService) {
return (CFArrayRef)CFNetServiceGetInfo(theService, _kCFNetServiceAddress);
}
CFStringRef
CFNetServiceGetTargetHost(CFNetServiceRef theService) {
return (CFStringRef)CFNetServiceGetInfo(theService, _kCFNetServiceTargetHost);
}
CFDataRef
CFNetServiceGetTXTData(CFNetServiceRef theService) {
CFTypeRef result = CFNetServiceGetInfo(theService, _kCFNetServiceTXT);
if (result && (CFGetTypeID(result) != CFDataGetTypeID()))
result = NULL;
return (CFDataRef)result;
}
Boolean
CFNetServiceSetTXTData(CFNetServiceRef theService, CFDataRef txtRecord) {
return CFNetServiceSetInfo(theService, _kCFNetServiceTXT, txtRecord);
}
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]));
CFRetain(theService);
__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);
if (CFArrayGetCount(service->_sources)) {
if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
service->_error.error = kCFNetServicesErrorInProgress;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
else {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
}
}
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';
}
}
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.domain = kCFStreamErrorDomainNetServices;
break;
}
CFDictionaryApplyFunction(service->_info, _AddRecords, service);
if (service->_error.error) {
service->_error.domain = kCFStreamErrorDomainNetServices;
DNSServiceRefDeallocate(service->_new_service);
service->_new_service = NULL;
CFDictionaryRemoveAllValues(service->_records);
break;
}
sock = CFSocketCreateWithNative(CFGetAllocator(theService),
DNSServiceRefSockFD(service->_new_service),
kCFSocketReadCallBack,
_SocketCallBack,
&ctxt);
if (!sock) {
service->_error.error = errno;
if (!service->_error.error)
service->_error.error = ENOMEM;
service->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceRefDeallocate(service->_new_service);
service->_new_service = NULL;
CFDictionaryRemoveAllValues(service->_records);
break;
}
CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate);
CFArrayAppendValue(service->_sources, sock);
CFRelease(sock);
service->_error.error = 0;
service->_error.domain = 0;
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitSet(service->_flags, kFlagBitActiveRegister);
if (CFArrayGetCount(service->_schedules)) {
_ScheduleSources(service->_sources, service->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(service->_lock));
result = _ServiceBlockUntilComplete(service);
__CFSpinLock(&(service->_lock));
}
} while (0);
memmove(error, &service->_error, sizeof(error[0]));
__CFSpinUnlock(&(service->_lock));
CFRelease(theService);
return result;
}
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]));
CFRetain(theService);
__CFSpinLock(&(service->_lock));
do {
int i;
char properties[3][1024];
UInt32 keys[] = {_kCFNetServiceName, _kCFNetServiceType, _kCFNetServiceDomain};
if (CFArrayGetCount(service->_sources)) {
if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
service->_error.error = kCFNetServicesErrorInProgress;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
else {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
}
}
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 (!service->_error.error && (timeout > 0.0)) {
CFRunLoopTimerContext c = {0, service, NULL, NULL, NULL};
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(CFGetAllocator(theService),
CFAbsoluteTimeGetCurrent() + timeout,
0.0,
0,
0,
_LongTimerCallBack,
&c);
__CFBitClear(service->_flags, kFlagBitAReceived);
__CFBitClear(service->_flags, kFlagBitAAAAReceived);
if (timer) {
CFArrayAppendValue(service->_sources, timer);
CFRelease(timer);
}
else {
service->_error.error = errno;
if (!service->_error.error)
service->_error.error = ENOMEM;
service->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
if (service->_error.error) {
_InvalidateSources(service->_sources);
DNSServiceRefDeallocate(service->_new_service);
service->_new_service = NULL;
break;
}
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceAddress);
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTXT);
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceTargetHost);
service->_port = 0;
service->_error.error = 0;
service->_error.domain = 0;
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitSet(service->_flags, kFlagBitActiveResolve);
__CFBitClear(service->_flags, kFlagBitAComplete);
__CFBitClear(service->_flags, kFlagBitAAAAComplete);
if (CFArrayGetCount(service->_schedules)) {
_ScheduleSources(service->_sources, service->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(service->_lock));
result = _ServiceBlockUntilComplete(service);
__CFSpinLock(&(service->_lock));
}
} while (0);
memmove(error, &service->_error, sizeof(error[0]));
__CFSpinUnlock(&(service->_lock));
CFRelease(theService);
return result;
}
void
CFNetServiceCancel(CFNetServiceRef theService) {
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
if (CFArrayGetCount(service->_sources)) {
CFRunLoopSourceRef src = NULL;
CFRunLoopSourceContext ctxt = {
0,
service,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
(void(*)(void*))(&_ServiceCancel)
};
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
if (service->_old_service) {
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
service->_old_service = NULL;
}
}
else {
int i;
DNSServiceRef* services[] = {&service->_new_service, &service->_a, &service->_aaaa};
for (i = 0; i < (sizeof(services) / sizeof(services[0])); i++) {
if (*services[i]) {
DNSServiceRefDeallocate(*services[i]);
*services[i] = NULL;
}
}
CFDictionaryRemoveAllValues(service->_records);
}
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitClear(service->_flags, kFlagBitActiveResolve);
__CFBitClear(service->_flags, kFlagBitActiveRegister);
__CFBitClear(service->_flags, kFlagBitCancel);
service->_error.error = kCFNetServicesErrorCancel;
service->_error.domain = kCFStreamErrorDomainNetServices;
src = CFRunLoopSourceCreate(CFGetAllocator(theService), 0, &ctxt);
if (src) {
CFArrayRef schedules = service->_schedules;
CFIndex i, count = CFArrayGetCount(schedules);
__CFBitSet(service->_flags, kFlagBitCancel);
CFArrayAppendValue(service->_sources, src);
_ScheduleSources(service->_sources, service->_schedules);
CFRunLoopSourceSignal(src);
for (i = 0; i < count; i += 2) {
CFRunLoopRef runloop = (CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i);
if (CFRunLoopIsWaiting(runloop)) {
CFStringRef mode = CFRunLoopCopyCurrentMode(runloop);
if (mode) {
if (CFRunLoopContainsSource(runloop, src, mode)) {
CFRunLoopWakeUp(runloop);
}
CFRelease(mode);
}
}
}
CFRelease(src);
}
}
__CFSpinUnlock(&(service->_lock));
}
Boolean
CFNetServiceSetClient(CFNetServiceRef theService, CFNetServiceClientCallBack clientCB, CFNetServiceClientContext* clientContext) {
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
if (service->_client.info && service->_client.release)
service->_client.release(service->_client.info);
if (!clientCB || !clientContext) {
if (CFArrayGetCount(service->_sources)) {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
if (__CFBitIsSet(service->_flags, kFlagBitLegacyService)) {
if (service->_old_service) {
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
service->_old_service = NULL;
}
}
else {
if (service->_new_service) {
DNSServiceRefDeallocate(service->_new_service);
service->_new_service = NULL;
CFDictionaryRemoveAllValues(service->_records);
}
}
}
__CFBitClear(service->_flags, kFlagBitLegacyService);
__CFBitClear(service->_flags, kFlagBitActiveResolve);
__CFBitClear(service->_flags, kFlagBitActiveRegister);
__CFBitClear(service->_flags, kFlagBitCancel);
service->_callback = NULL;
memset(&(service->_client), 0, sizeof(service->_client));
}
else {
if (!service->_callback && CFArrayGetCount(service->_sources))
_ScheduleSources(service->_sources, service->_schedules);
service->_callback = clientCB;
memmove(&(service->_client), clientContext, sizeof(service->_client));
if (service->_client.info && service->_client.retain)
service->_client.info = (void*)(service->_client.retain(service->_client.info));
}
__CFSpinUnlock(&(service->_lock));
return TRUE;
}
void
CFNetServiceScheduleWithRunLoop(CFNetServiceRef theService, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
if (_SchedulesAddRunLoopAndMode(service->_schedules, runLoop, runLoopMode)) {
int i, count = CFArrayGetCount(service->_sources);
for (i = 0; i < count; i++)
_CFTypeScheduleOnRunLoop(CFArrayGetValueAtIndex(service->_sources, i), runLoop, runLoopMode);
}
__CFSpinUnlock(&(service->_lock));
}
void
CFNetServiceUnscheduleFromRunLoop(CFNetServiceRef theService, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
if (_SchedulesRemoveRunLoopAndMode(service->_schedules, runLoop, runLoopMode)) {
int i, count = CFArrayGetCount(service->_sources);
for (i = 0; i < count; i++)
_CFTypeUnscheduleFromRunLoop(CFArrayGetValueAtIndex(service->_sources, i), runLoop, runLoopMode);
}
__CFSpinUnlock(&(service->_lock));
}
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
};
uint16_t i, count = TXTRecordGetCount(len, txt);
result = CFDictionaryCreateMutable(alloc, 0, &kTXTDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (result) {
for (i = 0; i < count; i++) {
char key[256];
uint8_t valLen = 0;
const void* value = NULL;
if (kDNSServiceErr_NoError == TXTRecordGetItemAtIndex(len, txt, i, sizeof(key), key, &valLen, &value)) {
CFTypeRef data = kCFNull;
CFStringRef str = CFStringCreateWithCString(alloc, key, kCFStringEncodingUTF8);
if (value)
data = CFDataCreate(alloc, value, valLen);
if (data && str && CFStringGetLength(str) && !CFDictionaryGetValue(result, str))
CFDictionaryAddValue(result, str, data);
if (data)
CFRelease(data);
if (str)
CFRelease(str);
}
}
}
}
return result;
}
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];
CFDictionaryGetKeysAndValues(keyValuePairs, (const void**)keys, (const void**)values);
TXTRecordCreate(&txt, 0, NULL);
for (i = 0; i < count; i++) {
CFIndex length, converted, used = 0;
CFTypeID type = CFGetTypeID(values[i]);
DNSServiceErrorType set = kDNSServiceErr_Unknown;
if (CFGetTypeID(keys[i]) != strType)
break;
length = CFStringGetLength(keys[i]);
converted = CFStringGetBytes(keys[i], CFRangeMake(0, length), kCFStringEncodingASCII, 0, FALSE, key, sizeof(key), &used);
if (!length || (converted < length) || (used >= sizeof(key)))
break;
key[used] = '\0';
if (type == strType) {
UInt8 value[256];
length = CFStringGetLength(values[i]);
converted = CFStringGetBytes(values[i], CFRangeMake(0, length), kCFStringEncodingUTF8, 0, FALSE, value, sizeof(value), &used);
if ((converted < length) || (used >= sizeof(key)))
break;
set = TXTRecordSetValue(&txt, (const char*)key, used, value);
}
else if ((type == dataType) &&
(CFDataGetLength((CFDataRef)(values[i])) < 256) &&
(CFDataGetLength((CFDataRef)(values[i])) >= 0))
{
set = TXTRecordSetValue(&txt,
(const char*)key,
CFDataGetLength((CFDataRef)(values[i])),
CFDataGetBytePtr((CFDataRef)(values[i])));
}
else if (values[i] == kCFNull) {
set = TXTRecordSetValue(&txt, (const char*)key, 0, NULL);
}
else
break;
if (set != kDNSServiceErr_NoError)
break;
}
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
CFNetServiceRef
_CFNetServiceCreateCommon(CFAllocatorRef alloc, CFStringRef domain, CFStringRef type, CFStringRef name, UInt32 port) {
__CFNetService* result = NULL;
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};
CFRuntimeBase copy = result->_base;
memset(result, 0, sizeof(result[0]));
memmove(&(result->_base), ©, sizeof(result->_base));
result->_info = CFDictionaryCreateMutable(alloc, 0, &keys, &kCFTypeDictionaryValueCallBacks);
result->_records = CFDictionaryCreateMutable(alloc, 0, &keys, &values);
result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
result->_sources = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
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;
}
else {
CFRelease((CFTypeRef)result);
result = NULL;
}
}
}
return (CFNetServiceRef)result;
}
CFTypeRef
CFNetServiceGetInfo(CFNetServiceRef theService, UInt32 property) {
CFTypeRef result = NULL;
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
result = (CFTypeRef)CFDictionaryGetValue(service->_info, (const void*)property);
__CFSpinUnlock(&(service->_lock));
return result;
}
Boolean
CFNetServiceSetInfo(CFNetServiceRef theService, UInt32 property, CFTypeRef value) {
return _ServiceSetInfo((__CFNetService*)theService, property, value, TRUE);
}
Boolean
_CFNetServiceSetInfoNoPublish(CFNetServiceRef theService, UInt32 property, CFTypeRef value) {
return _ServiceSetInfo((__CFNetService*)theService, property, value, FALSE);
}
#if 0
#pragma mark -
#pragma mark Deprecated API
#endif
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;
}
CFStringRef
CFNetServiceGetProtocolSpecificInformation(CFNetServiceRef theService) {
CFTypeRef result = CFNetServiceGetInfo(theService, _kCFNetServiceTXT);
if (result && (CFGetTypeID(result) != CFStringGetTypeID()))
result = NULL;
return (CFStringRef)result;
}
void
CFNetServiceSetProtocolSpecificInformation(CFNetServiceRef theService, CFStringRef theInfo) {
__CFNetService* service = (__CFNetService*)theService;
__CFSpinLock(&(service->_lock));
if (!service->_old_service)
CFDictionarySetValue(service->_info, (const void*)_kCFNetServiceTXT, theInfo);
else {
char str[1024];
CFIndex bytesUsed = 0;
if (theInfo) {
CFStringGetBytes(theInfo,
CFRangeMake(0, CFStringGetLength(theInfo)),
kCFStringEncodingUTF8,
0,
FALSE,
(UInt8*)str,
sizeof(str) - 1,
&bytesUsed);
}
str[bytesUsed] = '\0';
DNSServiceRegistrationUpdateRecord_Deprecated(service->_old_service, 0, bytesUsed + 1, str, 0);
}
__CFSpinUnlock(&(service->_lock));
}
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]));
CFRetain(theService);
__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};
if (CFArrayGetCount(service->_sources)) {
if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
service->_error.error = kCFNetServicesErrorInProgress;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
else {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
}
}
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';
}
}
service->_old_service = DNSServiceRegistrationCreate_Deprecated(properties[0],
properties[1],
properties[2],
htons((service->_port & 0x0000FFFF)),
properties[3],
_LegacyRegistrationReply,
service);
if (!service->_old_service) {
service->_error.error = errno;
if (service->_error.error)
service->_error.domain = kCFStreamErrorDomainPOSIX;
else {
service->_error.error = kCFNetServicesErrorUnknown;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
break;
}
prt = CFMachPortCreateWithPort(alloc,
DNSServiceDiscoveryMachPort_Deprecated(service->_old_service),
_MachPortCallBack,
&ctxt,
NULL);
if (!prt) {
service->_error.error = errno;
if (!service->_error.error)
service->_error.error = ENOMEM;
service->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
break;
}
CFArrayAppendValue(service->_sources, prt);
CFRelease(prt);
service->_error.error = 0;
service->_error.domain = 0;
__CFBitSet(service->_flags, kFlagBitLegacyService);
__CFBitSet(service->_flags, kFlagBitActiveRegister);
if (CFArrayGetCount(service->_schedules)) {
_ScheduleSources(service->_sources, service->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(service->_lock));
result = _ServiceBlockUntilComplete(service);
__CFSpinLock(&(service->_lock));
}
} while (0);
memmove(error, &service->_error, sizeof(error[0]));
__CFSpinUnlock(&(service->_lock));
CFRelease(theService);
return result;
}
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]));
CFRetain(theService);
__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};
if (CFArrayGetCount(service->_sources)) {
if (!__CFBitIsSet(service->_flags, kFlagBitCancel)) {
service->_error.error = kCFNetServicesErrorInProgress;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
else {
_UnscheduleSources(service->_sources, service->_schedules);
_InvalidateSources(service->_sources);
}
}
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';
}
}
service->_old_service = DNSServiceResolverResolve_Deprecated(properties[0],
properties[1],
properties[2],
_LegacyResolverReply,
service);
if (!service->_old_service) {
service->_error.error = errno;
if (service->_error.error)
service->_error.domain = kCFStreamErrorDomainPOSIX;
else {
service->_error.error = kCFNetServicesErrorUnknown;
service->_error.domain = kCFStreamErrorDomainNetServices;
}
break;
}
prt = CFMachPortCreateWithPort(alloc,
DNSServiceDiscoveryMachPort_Deprecated(service->_old_service),
_MachPortCallBack,
&ctxt,
NULL);
if (!prt) {
service->_error.error = errno;
if (!service->_error.error)
service->_error.error = ENOMEM;
service->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceDiscoveryDeallocate_Deprecated(service->_old_service);
break;
}
CFDictionaryRemoveValue(service->_info, (const void*)_kCFNetServiceAddress);
CFArrayAppendValue(service->_sources, prt);
CFRelease(prt);
service->_error.error = 0;
service->_error.domain = 0;
__CFBitSet(service->_flags, kFlagBitLegacyService);
__CFBitSet(service->_flags, kFlagBitActiveResolve);
if (CFArrayGetCount(service->_schedules)) {
_ScheduleSources(service->_sources, service->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(service->_lock));
result = _ServiceBlockUntilComplete(service);
__CFSpinLock(&(service->_lock));
}
} while (0);
memmove(error, &service->_error, sizeof(error[0]));
__CFSpinUnlock(&(service->_lock));
CFRelease(theService);
return result;
}