CFNetServiceMonitor.c [plain text]
#if 0
#pragma mark Includes
#endif
#include <CFNetwork/CFNetwork.h>
#include "CFNetworkInternal.h" // for __CFSpinLock and __CFSpinUnlock
#include "CFNetworkSchedule.h"
#include <dns_sd.h>
#include <nameser.h>
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern Boolean _CFNetServiceSetInfoNoPublish(CFNetServiceRef theService, UInt32 property, CFTypeRef value);
#if 0
#pragma mark -
#pragma mark Constant Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFNetServiceMonitorBlockingMode CFSTR("_kCFNetServiceMonitorBlockingMode")
#else
static CONST_STRING_DECL(_kCFNetServiceMonitorBlockingMode, "_kCFNetServiceMonitorBlockingMode")
#endif
static const char _kCFNetServiceMonitorClassName[] = "CFNetServiceMonitor";
#if 0
#pragma mark -
#pragma mark CFNetServiceMonitor struct
#endif
typedef struct {
CFRuntimeBase _base;
CFSpinLock_t _lock;
CFStreamError _error;
CFNetServiceRef _service;
CFTypeRef _trigger;
DNSServiceRef _monitor;
CFNetServiceMonitorType _type;
CFMutableArrayRef _schedules; CFNetServiceMonitorClientCallBack _callback;
CFNetServiceClientContext _client;
} __CFNetServiceMonitor;
#if 0
#pragma mark -
#pragma mark Static Function Declarations
#endif
static void _CFNetServiceMonitorRegisterClass(void);
static void _MonitorDestroy(__CFNetServiceMonitor* monitor);
static void _MonitorCancel(__CFNetServiceMonitor* monitor);
static Boolean _MonitorBlockUntilComplete(__CFNetServiceMonitor* monitor);
static void _QueryRecordReply(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 void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
#if 0
#pragma mark -
#pragma mark Globals
#endif
static _CFOnceLock _kCFNetServiceMonitorRegisterClass = _CFOnceInitializer;
static CFTypeID _kCFNetServiceMonitorTypeID = _kCFRuntimeNotATypeID;
static CFRuntimeClass* _kCFNetServiceMonitorClass = NULL;
#if 0
#pragma mark -
#pragma mark Static Function Definitions
#endif
void
_CFNetServiceMonitorRegisterClass(void) {
_kCFNetServiceMonitorClass = (CFRuntimeClass*)calloc(1, sizeof(_kCFNetServiceMonitorClass[0]));
if (_kCFNetServiceMonitorClass) {
_kCFNetServiceMonitorClass->version = 0;
_kCFNetServiceMonitorClass->className = _kCFNetServiceMonitorClassName;
_kCFNetServiceMonitorClass->finalize = (void(*)(CFTypeRef))_MonitorDestroy;
_kCFNetServiceMonitorTypeID = _CFRuntimeRegisterClass(_kCFNetServiceMonitorClass);
}
}
void
_MonitorDestroy(__CFNetServiceMonitor* monitor) {
__CFSpinLock(&(monitor->_lock));
if (monitor->_client.info && monitor->_client.release)
monitor->_client.release(monitor->_client.info);
if (monitor->_trigger) {
if (monitor->_schedules)
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
_CFTypeInvalidate(monitor->_trigger);
CFRelease(monitor->_trigger);
}
if (monitor->_monitor) {
DNSServiceRefDeallocate(monitor->_monitor);
}
if (monitor->_service) {
CFRelease(monitor->_service);
monitor->_service = NULL;
}
if (monitor->_schedules)
CFRelease(monitor->_schedules);
}
void
_MonitorCancel(__CFNetServiceMonitor* monitor) {
CFNetServiceMonitorClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFRetain(monitor);
__CFSpinLock(&monitor->_lock);
if (monitor->_trigger) {
cb = monitor->_callback;
memmove(&error, &(monitor->_error), sizeof(error));
info = monitor->_client.info;
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(monitor->_trigger));
CFRelease(monitor->_trigger);
monitor->_trigger = NULL;
}
__CFSpinUnlock(&monitor->_lock);
if (cb)
cb((CFNetServiceMonitorRef)monitor, NULL, 0, NULL, &error, info);
CFRelease(monitor);
}
Boolean
_MonitorBlockUntilComplete(__CFNetServiceMonitor* monitor) {
Boolean result = TRUE;
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFNetServiceMonitorScheduleWithRunLoop((CFNetServiceMonitorRef)monitor, rl, _kCFNetServiceMonitorBlockingMode);
__CFSpinLock(&(monitor->_lock));
while (monitor->_trigger) {
__CFSpinUnlock(&(monitor->_lock));
CFRunLoopRunInMode(_kCFNetServiceMonitorBlockingMode, DBL_MAX, TRUE);
__CFSpinLock(&(monitor->_lock));
}
if (monitor->_error.error)
result = FALSE;
__CFSpinUnlock(&(monitor->_lock));
CFNetServiceMonitorUnscheduleFromRunLoop((CFNetServiceMonitorRef)monitor, rl, _kCFNetServiceMonitorBlockingMode);
return result;
}
void
_QueryRecordReply(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)
{
__CFNetServiceMonitor* monitor = context;
CFNetServiceMonitorClientCallBack cb = NULL;
CFStreamError error;
void* info = NULL;
CFDataRef data = NULL;
CFNetServiceRef service = NULL;
CFNetServiceMonitorType type = 0;
CFRetain(monitor);
__CFSpinLock(&monitor->_lock);
if (monitor->_monitor) {
service = (CFNetServiceRef)CFRetain(monitor->_service);
type = monitor->_type;
if (errorCode) {
monitor->_error.error = errorCode;
monitor->_error.domain = kCFStreamErrorDomainNetServices;
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
CFSocketInvalidate((CFSocketRef)(monitor->_trigger));
CFRelease(monitor->_trigger);
monitor->_trigger = NULL;
DNSServiceRefDeallocate(monitor->_monitor);
monitor->_monitor = NULL;
}
else if (rdata && (flags & kDNSServiceFlagsAdd)) {
data = CFDataCreate(CFGetAllocator(monitor), rdata, rdlen);
_CFNetServiceSetInfoNoPublish(service, type, data);
}
cb = monitor->_callback;
memmove(&error, &(monitor->_error), sizeof(error));
info = monitor->_client.info;
}
__CFSpinUnlock(&monitor->_lock);
if (cb && (flags & kDNSServiceFlagsAdd)) {
cb((CFNetServiceMonitorRef)monitor, service, type, data, &error, info);
}
if (service)
CFRelease(service);
if (data)
CFRelease(data);
CFRelease(monitor);
}
void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
DNSServiceErrorType err;
__CFNetServiceMonitor* monitor = info;
(void)s; (void)type; (void)address; (void)data;
CFRetain(monitor);
err = DNSServiceProcessResult(monitor->_monitor);
if (err)
_QueryRecordReply(monitor->_monitor, 0, 0, err, NULL, 0, 0, 0, NULL, 0, info);
CFRelease(monitor);
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif
CFTypeID
CFNetServiceMonitorGetTypeID(void) {
_CFDoOnce(&_kCFNetServiceMonitorRegisterClass, _CFNetServiceMonitorRegisterClass);
return _kCFNetServiceMonitorTypeID;
}
CFNetServiceMonitorRef
CFNetServiceMonitorCreate(CFAllocatorRef alloc, CFNetServiceRef theService, CFNetServiceMonitorClientCallBack clientCB, CFNetServiceClientContext* context) {
__CFNetServiceMonitor* result = NULL;
if (clientCB && context) {
CFTypeID class_type = CFNetServiceMonitorGetTypeID();
if (class_type != _kCFRuntimeNotATypeID) {
result = (__CFNetServiceMonitor*)_CFRuntimeCreateInstance(alloc,
class_type,
sizeof(result[0]) - sizeof(CFRuntimeBase),
NULL);
}
if (result) {
CFRuntimeBase copy = result->_base;
memset(result, 0, sizeof(result[0]));
memmove(&(result->_base), ©, sizeof(result->_base));
result->_callback = clientCB;
memmove(&(result->_client), context, sizeof(result->_client));
if (result->_client.info && result->_client.retain)
result->_client.info = (void*)(result->_client.retain(result->_client.info));
result->_schedules = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (result->_schedules)
result->_service = (CFNetServiceRef)CFRetain(theService);
else {
CFRelease((CFTypeRef)result);
result = NULL;
}
}
}
return (CFNetServiceMonitorRef)result;
}
void
CFNetServiceMonitorInvalidate(CFNetServiceMonitorRef theMonitor) {
__CFNetServiceMonitor* monitor = (__CFNetServiceMonitor*)theMonitor;
__CFSpinLock(&(monitor->_lock));
if (monitor->_client.info && monitor->_client.release)
monitor->_client.release(monitor->_client.info);
if (monitor->_trigger) {
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
_CFTypeInvalidate(monitor->_trigger);
CFRelease(monitor->_trigger);
monitor->_trigger = NULL;
}
if (monitor->_monitor) {
DNSServiceRefDeallocate(monitor->_monitor);
monitor->_monitor = NULL;
}
if (monitor->_service) {
CFRelease(monitor->_service);
monitor->_service = NULL;
}
monitor->_callback = NULL;
memset(&(monitor->_client), 0, sizeof(monitor->_client));
__CFSpinUnlock(&(monitor->_lock));
}
Boolean
CFNetServiceMonitorStart(CFNetServiceMonitorRef theMonitor, CFNetServiceMonitorType recordType, CFStreamError* error) {
__CFNetServiceMonitor* monitor = (__CFNetServiceMonitor*)theMonitor;
CFStreamError extra;
Boolean result = FALSE;
if (!error)
error = &extra;
memset(error, 0, sizeof(error[0]));
CFRetain(monitor);
__CFSpinLock(&(monitor->_lock));
do {
UInt16 rrtype = (recordType & 0x0000FFFF);
UInt16 rrclass = ((recordType & 0xFFFF0000) >> 16);
int i;
char properties[4][1024];
CFStringRef values[] = {
CFNetServiceGetDomain(monitor->_service),
CFNetServiceGetType(monitor->_service),
CFNetServiceGetName(monitor->_service)
};
CFSocketContext ctxt = {0, monitor, CFRetain, CFRelease, NULL};
if (!monitor->_callback) {
monitor->_error.error = kCFNetServicesErrorInvalid;
monitor->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
if (monitor->_trigger) {
if (CFGetTypeID(monitor->_trigger) == CFSocketGetTypeID()) {
monitor->_error.error = kCFNetServicesErrorInProgress;
monitor->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
else {
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(monitor->_trigger));
CFRelease(monitor->_trigger);
monitor->_trigger = NULL;
}
}
if (recordType == kCFNetServiceMonitorTXT) {
rrtype = ns_t_txt;
rrclass = ns_c_in;
}
for (i = 0; i < 3; i++) {
CFIndex used;
CFStringGetBytes(values[i],
CFRangeMake(0, CFStringGetLength(values[i])),
kCFStringEncodingUTF8,
0,
FALSE,
(UInt8*)properties[i],
sizeof(properties[i]) - 1,
&used);
properties[i][used] = '\0';
}
DNSServiceConstructFullName(properties[3], properties[2], properties[1], properties[0]);
monitor->_type = recordType;
monitor->_error.error = DNSServiceQueryRecord(&monitor->_monitor,
kDNSServiceFlagsLongLivedQuery,
0,
properties[3],
rrtype,
rrclass,
_QueryRecordReply,
monitor);
if (monitor->_error.error) {
monitor->_error.domain = kCFStreamErrorDomainNetServices;
break;
}
monitor->_trigger = CFSocketCreateWithNative(CFGetAllocator(monitor),
DNSServiceRefSockFD(monitor->_monitor),
kCFSocketReadCallBack,
_SocketCallBack,
&ctxt);
if (!monitor->_trigger) {
monitor->_error.error = errno;
if (!monitor->_error.error)
monitor->_error.error = ENOMEM;
monitor->_error.domain = kCFStreamErrorDomainPOSIX;
DNSServiceRefDeallocate(monitor->_monitor);
monitor->_monitor = NULL;
break;
}
CFSocketSetSocketFlags((CFSocketRef)monitor->_trigger,
CFSocketGetSocketFlags((CFSocketRef)monitor->_trigger) & ~kCFSocketCloseOnInvalidate);
if (CFArrayGetCount(monitor->_schedules)) {
_CFTypeScheduleOnMultipleRunLoops(monitor->_trigger, monitor->_schedules);
result = TRUE;
}
else {
__CFSpinUnlock(&(monitor->_lock));
result = _MonitorBlockUntilComplete(monitor);
__CFSpinLock(&(monitor->_lock));
}
} while (0);
memmove(error, &monitor->_error, sizeof(error[0]));
__CFSpinUnlock(&(monitor->_lock));
CFRelease(monitor);
return result;
}
void
CFNetServiceMonitorStop(CFNetServiceMonitorRef theMonitor, CFStreamError* error) {
__CFNetServiceMonitor* monitor = (__CFNetServiceMonitor*)theMonitor;
CFStreamError extra = {kCFStreamErrorDomainNetServices , kCFNetServicesErrorCancel};
if (!error)
error = &extra;
__CFSpinLock(&(monitor->_lock));
if (monitor->_trigger) {
CFRunLoopSourceContext ctxt = {
0, monitor, NULL, NULL, NULL, NULL, NULL, NULL, NULL, (void(*)(void*))(&_MonitorCancel) };
_CFTypeUnscheduleFromMultipleRunLoops(monitor->_trigger, monitor->_schedules);
_CFTypeInvalidate(monitor->_trigger);
CFRelease(monitor->_trigger);
if (monitor->_monitor) {
DNSServiceRefDeallocate(monitor->_monitor);
monitor->_monitor = NULL;
}
memmove(&(monitor->_error), error, sizeof(error[0]));
monitor->_trigger = CFRunLoopSourceCreate(CFGetAllocator(monitor), 0, &ctxt);
if (monitor->_trigger) {
CFArrayRef schedules = monitor->_schedules;
CFIndex i, count = CFArrayGetCount(schedules);
_CFTypeScheduleOnMultipleRunLoops(monitor->_trigger, schedules);
CFRunLoopSourceSignal((CFRunLoopSourceRef)(monitor->_trigger));
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, (CFRunLoopSourceRef)(monitor->_trigger), mode)) {
CFRunLoopWakeUp(runloop);
}
CFRelease(mode);
}
}
}
}
}
__CFSpinUnlock(&(monitor->_lock));
}
void
CFNetServiceMonitorScheduleWithRunLoop(CFNetServiceMonitorRef theMonitor, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetServiceMonitor* monitor = (__CFNetServiceMonitor*)theMonitor;
__CFSpinLock(&(monitor->_lock));
if (_SchedulesAddRunLoopAndMode(monitor->_schedules, runLoop, runLoopMode)) {
if (monitor->_trigger) {
_CFTypeScheduleOnRunLoop(monitor->_trigger, runLoop, runLoopMode);
}
}
__CFSpinUnlock(&(monitor->_lock));
}
void
CFNetServiceMonitorUnscheduleFromRunLoop(CFNetServiceMonitorRef theMonitor, CFRunLoopRef runLoop, CFStringRef runLoopMode) {
__CFNetServiceMonitor* monitor = (__CFNetServiceMonitor*)theMonitor;
__CFSpinLock(&(monitor->_lock));
if (_SchedulesRemoveRunLoopAndMode(monitor->_schedules, runLoop, runLoopMode)) {
if (monitor->_trigger) {
_CFTypeUnscheduleFromRunLoop(monitor->_trigger, runLoop, runLoopMode);
}
}
__CFSpinUnlock(&(monitor->_lock));
}