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;
}
Beispiel #4
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;	
}