static void MyDNSServiceAddToRunLoop(MyDNSServiceState *ref) { CFSocketNativeHandle sock; CFOptionFlags sockFlags; CFRunLoopSourceRef source; CFSocketContext context = { 0, ref, NULL, NULL, NULL }; // Use MyDNSServiceState as context data. assert(ref); // Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. sock = DNSServiceRefSockFD(ref->service); assert(sock >= 0); // Create a CFSocket using the Unix domain socket. ref->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MyDNSServiceSocketCallBack, &context); assert(ref->socket); // Prevent CFSocketInvalidate from closing DNSServiceRef's socket. sockFlags = CFSocketGetSocketFlags(ref->socket); CFSocketSetSocketFlags(ref->socket, sockFlags & (~kCFSocketCloseOnInvalidate)); // Create a CFRunLoopSource from the CFSocket. source = CFSocketCreateRunLoopSource(NULL, ref->socket, 0); assert(source); // Add the CFRunLoopSource to the current runloop. CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); // We no longer need our reference to source. The runloop continues to // hold a reference to it, but we don't care about that. When we invalidate // the socket, the runloop will drop its reference and the source will get // destroyed. CFRelease(source); }
/* Register a QSocketNotifier with the mac event system by creating a CFSocket with with a read/write callback. Qt has separate socket notifiers for reading and writing, but on the mac there is a limitation of one CFSocket object for each native socket. */ void QEventDispatcherMac::registerSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); int nativeSocket = notifier->socket(); int type = notifier->type(); #ifndef QT_NO_DEBUG if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { qWarning("QSocketNotifier: Internal error"); return; } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); return; } #endif Q_D(QEventDispatcherMac); QEventDispatcherUNIX::registerSocketNotifier(notifier); if (type == QSocketNotifier::Exception) { qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); return; } // Check if we have a CFSocket for the native socket, create one if not. MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket); if (!socketInfo) { socketInfo = new MacSocketInfo(); // Create CFSocket, specify that we want both read and write callbacks (the callbacks // are enabled/disabled later on). const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack; CFSocketContext context = {0, this, NULL, NULL, NULL}; socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context); if (CFSocketIsValid(socketInfo->socket) == false) { qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket"); return; } CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket); flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation CFSocketSetSocketFlags(socketInfo->socket, flags); // Add CFSocket to runloop. if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) { qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop"); CFSocketInvalidate(socketInfo->socket); CFRelease(socketInfo->socket); return; } // Disable both callback types by default. This must be done after // we add the CFSocket to the runloop, or else these calls will have // no effect. CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); d->macSockets.insert(nativeSocket, socketInfo); } // Increment read/write counters and select enable callbacks if necessary. if (type == QSocketNotifier::Read) { if (++socketInfo->read == 1) CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); } else if (type == QSocketNotifier::Write) { if (++socketInfo->write == 1) CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); } }
int tnet_transport_wrap(tnet_transport_t *transport, int index) { transport_context_t *context = transport->context; transport_socket_t *sock = context->sockets[index]; // If the socket is already wrapped in a CFSocket then return. if (sock->cf_socket || sock->cf_read_stream) { return 1; } // Put a reference to the transport context const CFSocketContext socket_context = { 0, transport, NULL, NULL, NULL }; if (TNET_SOCKET_TYPE_IS_DGRAM(sock->type)) { // Create a CFSocket from the native socket and register for Read events sock->cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault, sock->fd, kCFSocketReadCallBack, &__CFSocketCallBack, &socket_context); // Don't close the socket if the CFSocket is invalidated CFOptionFlags flags = CFSocketGetSocketFlags(sock->cf_socket); flags = flags & ~kCFSocketCloseOnInvalidate; CFSocketSetSocketFlags(sock->cf_socket, flags); // Create a new RunLoopSource and register it with the main thread RunLoop sock->cf_run_loop_source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sock->cf_socket, 0); CFRunLoopAddSource(context->cf_run_loop, sock->cf_run_loop_source, kCFRunLoopDefaultMode); CFRelease(sock->cf_run_loop_source); } else if (TNET_SOCKET_TYPE_IS_STREAM(sock->type)) { // Create a pair of streams (read/write) from the socket CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock->fd, &sock->cf_read_stream, &sock->cf_write_stream); // Don't close underlying socket CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); if (TNET_SOCKET_TYPE_IS_SECURE(sock->type)) { CFMutableDictionaryRef settings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(settings, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue); CFDictionaryAddValue(settings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); CFDictionaryAddValue(settings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); CFDictionaryAddValue(settings, kCFStreamSSLPeerName, kCFNull); // Set the SSL settings CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL); CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySSLSettings, settings); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySSLSettings, settings); CFRelease(settings); } #if __IPHONE_4_0 // Mark the stream for VoIP usage CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); #endif // Setup a context for the streams CFStreamClientContext streamContext = { 0, transport, NULL, NULL, NULL }; // Set the client callback for the stream CFReadStreamSetClient(sock->cf_read_stream, kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, &__CFReadStreamClientCallBack, &streamContext); CFWriteStreamSetClient(sock->cf_write_stream, kCFStreamEventOpenCompleted | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, &__CFWriteStreamClientCallBack, &streamContext); // Enroll streams in the run-loop CFReadStreamScheduleWithRunLoop(sock->cf_read_stream, context->cf_run_loop, kCFRunLoopDefaultMode); CFWriteStreamScheduleWithRunLoop(sock->cf_write_stream, context->cf_run_loop, kCFRunLoopDefaultMode); // Release references CFRelease(sock->cf_read_stream); CFRelease(sock->cf_write_stream); CFReadStreamOpen(sock->cf_read_stream); CFWriteStreamOpen(sock->cf_write_stream); } return 0; }
/* CF_EXPORT */ Boolean CFNetServiceBrowserSearchForServices(CFNetServiceBrowserRef b, CFStringRef domain, CFStringRef type, CFStreamError* error) { __CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b; 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(browser); // Lock down the browser to start search __CFSpinLock(&(browser->_lock)); do { int i; char properties[2][1024]; CFStringRef argProperties[] = {type, domain}; CFSocketContext ctxt = {0, browser, CFRetain, CFRelease, NULL}; if (!browser->_callback) { browser->_error.error = kCFNetServicesErrorInvalid; browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // Check to see if there is an ongoing search already if (browser->_trigger) { // If it's a mdns search, don't allow another. if (CFGetTypeID(browser->_trigger) == CFSocketGetTypeID()) { browser->_error.error = kCFNetServicesErrorInProgress; browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // It's just the cancel that hasn't fired yet, so cancel it. else { // Remove the trigger from run loops and modes _CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules); // Invalidate the run loop source CFRunLoopSourceInvalidate((CFRunLoopSourceRef)(browser->_trigger)); // Release the trigger now. CFRelease(browser->_trigger); browser->_trigger = NULL; } } // Convert the type and domain to c strings to pass down for (i = 0; i < (sizeof(properties) / sizeof(properties[0])); i++) { if (!argProperties[i]) properties[i][0] = '\0'; else { CFIndex bytesUsed; CFStringGetBytes(argProperties[i], CFRangeMake(0, CFStringGetLength(argProperties[i])), kCFStringEncodingUTF8, 0, FALSE, (UInt8*)properties[i], sizeof(properties[i]) - 1, &bytesUsed); properties[i][bytesUsed] = '\0'; } } browser->_domainSearch = FALSE; // Create the service search at the service discovery level browser->_error.error = DNSServiceBrowse(&browser->_browse, 0, 0, properties[0], properties[1], _BrowseReply, browser); // Fail if it did. if (browser->_error.error) { browser->_error.error = _DNSServiceErrorToCFNetServiceError(browser->_error.error); browser->_error.domain = kCFStreamErrorDomainNetServices; break; } // Create the trigger for the browse browser->_trigger = CFSocketCreateWithNative(CFGetAllocator(browser), DNSServiceRefSockFD(browser->_browse), kCFSocketReadCallBack, _SocketCallBack, &ctxt); // Make sure the CFSocket wrapper succeeded if (!browser->_trigger) { // Try to use errno for the error browser->_error.error = errno; // If it has no error in it, assume no memory if (!browser->_error.error) browser->_error.error = ENOMEM; // Correct domain and bail. browser->_error.domain = kCFStreamErrorDomainPOSIX; DNSServiceRefDeallocate(browser->_browse); browser->_browse = NULL; break; } // Tell CFSocket not to close the native socket on invalidation. CFSocketSetSocketFlags((CFSocketRef)browser->_trigger, CFSocketGetSocketFlags((CFSocketRef)browser->_trigger) & ~kCFSocketCloseOnInvalidate); // Async mode is complete at this point if (CFArrayGetCount(browser->_schedules)) { // Schedule the trigger on the run loops and modes. _CFTypeScheduleOnMultipleRunLoops(browser->_trigger, browser->_schedules); // It's now succeeded. result = TRUE; } // If there is no callback, go into synchronous mode. else { // Unlock the browser __CFSpinUnlock(&(browser->_lock)); // Wait for synchronous return result = _BrowserBlockUntilComplete(browser); // Lock down the browser __CFSpinLock(&(browser->_lock)); } } while (0); // Copy the error. memmove(error, &browser->_error, sizeof(error[0])); // Unlock the browser __CFSpinUnlock(&(browser->_lock)); // Release the earlier retain. CFRelease(browser); return result; }
bool wxServiceDiscoveryTaskBase::Start( void ) { wxASSERT( m_rServiceRef == NULL ); bool output = DoStart(); if ( output ) { wxASSERT( m_rServiceRef != NULL ); if ( m_rServiceRef != NULL ) { if ( m_bUseThreads ) { wxASSERT( m_pThread == NULL ); if ( m_pThread != NULL ) { m_pThread->Delete(); delete m_pThread; m_pThread = NULL; } m_pThread = new wxServiceDiscoveryServiceHelperThread( m_rServiceRef ); if ( m_pThread != NULL ) { m_pThread->Create(); m_pThread->Run(); } } else { dns_sd_fd = DNSServiceRefSockFD( m_rServiceRef ); #ifdef __DARWIN__ CFSocketContext context = { 0, this, NULL, NULL, NULL }; cfsocket = CFSocketCreateWithNative(NULL, dns_sd_fd, kCFSocketReadCallBack, wxServiceDiscoveryTaskBase::Mac_Socket_Callback, &context); // Prevent CFSocketInvalidate from closing DNSServiceRef's socket. CFOptionFlags f = CFSocketGetSocketFlags( cfsocket ); CFSocketSetSocketFlags( cfsocket, f & ~kCFSocketCloseOnInvalidate); source = CFSocketCreateRunLoopSource( NULL, cfsocket, 0 ); CFRunLoopAddSource( CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes ); #elif defined( _WIN32 ) // Create the window. This window won't actually be shown, // but it demonstrates how to use DNS-SD with Windows GUI // applications by having DNS-SD events processed as messages // to a Window. wind = CreateWindow(GetWndClass().lpszClassName, GetWndClass().lpszClassName, 0, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, // requires 2000/XP http://msdn2.microsoft.com/en-us/library/ms632599.aspx#message_only NULL, instance, NULL ); BOOL status = SetProp( wind, PROPNAME, this ); wxASSERT( status != 0 ); // Associate the DNS-SD browser with our window // using the WSAAsyncSelect mechanism. Whenever something // related to the DNS-SD browser occurs, our private Windows message // will be sent to our window so we can give DNS-SD a // chance to process it. This allows DNS-SD to avoid using a // secondary thread (and all the issues with synchronization that // would introduce), but still process everything asynchronously. // This also simplifies app code because DNS-SD will only run when we // explicitly call it. err = WSAAsyncSelect( dns_sd_fd, wind, DNSSD_EVENT, FD_READ | FD_CLOSE); assert(err == kDNSServiceErr_NoError); #else nfds = dns_sd_fd + 1; FD_ZERO(&readfds); FD_SET(dns_sd_fd,&readfds); #endif } } } return output; }