// netservice browser callback - calls whenever service is discovered void ofxBonjourIp::NetServiceBrowserCallBack(CFNetServiceBrowserRef browser,CFOptionFlags flags,CFTypeRef domainOrService,CFStreamError* error,void* info) { ofLog() << "----------------------"; if(error->error != 0) { ofLog() << "Error: " << error->error; return; } // unresovled still... but can get type and domain (pointless) CFNetServiceRef netServiceRef = (CFNetServiceRef) domainOrService; // casting to this thing //service removed/closed if (flags & kCFNetServiceFlagRemove) { ofLog() << "Service was removed."; if(info != NULL) { // notify service has been removed ofxBonjourIp* bonjour = (ofxBonjourIp*)info; ofNotifyEvent(bonjour->removedServiceEvent,bonjour->serverIp,bonjour); // reset bonjour->serverHostName = ""; bonjour->serverIp = ""; bonjour->connectedToService = false; // dont know if i need this? maybe causing a crash after long period of time. I don't think it's been added to the run loop yet either //CFNetServiceUnscheduleFromRunLoop(netServiceRef, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); //CFNetServiceSetClient(netServiceRef, NULL, NULL); } return; } // the 'flags' property = 8 when it closes, and 0 when it opens. let's use that for now? /*if(flags != 0) { ofLog() << "Flag error/ service closed elsewhere (8): " << flags; return; }*/ // resolve the service CFNetServiceClientContext clientContext = { 0, info, NULL, NULL, NULL }; CFNetServiceSetClient(netServiceRef, ofxBonjourIp::NetServiceResolvedCallBack, &clientContext); CFNetServiceScheduleWithRunLoop(netServiceRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); if(!CFNetServiceResolveWithTimeout(netServiceRef, 0, NULL)) { ofLog() << "Error resolving service"; CFNetServiceUnscheduleFromRunLoop(netServiceRef, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); CFNetServiceSetClient(netServiceRef, NULL, NULL); } }
void ofxBonjourIp::startService( string type, string name, int port, string domain ){ // format parameters CFStringRef serviceType = CFStringCreateWithCString(kCFAllocatorDefault, type.c_str(), kCFStringEncodingUTF8); CFStringRef serviceName = CFStringCreateWithCString(kCFAllocatorDefault, name.c_str(), kCFStringEncodingUTF8); // if empty becomes device name SInt32 chosenPort = (SInt32) port; CFStringRef serviceDomain = CFStringCreateWithCString(kCFAllocatorDefault, domain.c_str(), kCFStringEncodingUTF8); // start service- async netService = CFNetServiceCreate(kCFAllocatorDefault, serviceDomain, serviceType, serviceName, chosenPort); CFNetServiceScheduleWithRunLoop(netService, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); // creat a client context for a callback function when service successfully publishes CFNetServiceClientContext clientContext = { 0, this, NULL, NULL, NULL }; CFNetServiceSetClient(netService, ofxBonjourIp::NetServicePublishedCallBack, &clientContext); //CFNetServiceSetClient(netService, registerCallback, &clientContext); if (!CFNetServiceRegisterWithOptions(netService, kCFNetServiceFlagNoAutoRename, NULL)) { stopService(); ofLog() << "Could not register Bonjour service"; } // do i need to do this? CFRelease(serviceType); CFRelease(serviceName); CFRelease(serviceDomain); }
void CZeroconfOSX::cancelRegistration(CFNetServiceRef theService) { assert(theService != NULL); CFNetServiceUnscheduleFromRunLoop(theService, m_runloop, kCFRunLoopCommonModes); CFNetServiceSetClient(theService, NULL, NULL); CFNetServiceCancel(theService); CFRelease(theService); }
static void bonjour_stop_service(CFNetServiceRef *svc) { CFNetServiceUnscheduleFromRunLoop(*svc, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); CFNetServiceSetClient(*svc, NULL, NULL); CFRelease(*svc); }
//methods to implement for concrete implementations bool CZeroconfOSX::doPublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port) { CLog::Log(LOGDEBUG, "CZeroconfOSX::doPublishService identifier: %s type: %s name:%s port:%i", fcr_identifier.c_str(), fcr_type.c_str(), fcr_name.c_str(), f_port); CFStringRef name = CFStringCreateWithCString (NULL, assemblePublishedName(fcr_name).c_str(), kCFStringEncodingUTF8 ); CFStringRef type = CFStringCreateWithCString (NULL, fcr_type.c_str(), kCFStringEncodingUTF8 ); CFNetServiceRef netService = CFNetServiceCreate(NULL, CFSTR(""), type, name, f_port); CFRelease(name); CFRelease(type); //now register it CFNetServiceClientContext clientContext = { 0, this, NULL, NULL, NULL }; CFStreamError error; CFNetServiceSetClient(netService, registerCallback, &clientContext); CFNetServiceScheduleWithRunLoop(netService, m_runloop, kCFRunLoopCommonModes); Boolean result = CFNetServiceRegisterWithOptions (netService, 0, &error); if (result == false) { // Something went wrong so lets clean up. CFNetServiceUnscheduleFromRunLoop(netService, m_runloop, kCFRunLoopCommonModes); CFNetServiceSetClient(netService, NULL, NULL); CFRelease(netService); netService = NULL; CLog::Log(LOGERROR, "CZeroconfOSX::doPublishService CFNetServiceRegister returned (domain = %d, error = %ld)\n", (int)error.domain, error.error); } else { CSingleLock lock(m_data_guard); m_services.insert(make_pair(fcr_identifier, netService)); } return result; }
void ofxBonjourIp::stopService(){ if(netService) { CFNetServiceCancel(netService); CFNetServiceUnscheduleFromRunLoop(netService, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); CFNetServiceSetClient(netService, NULL, NULL); CFRelease(netService); netService = NULL; } }
/* extern */ void _CFTypeInvalidate(CFTypeRef obj) { CFTypeID t = CFGetTypeID(obj); /* Invalidate according to type of object. */ if (t == CFRunLoopSourceGetTypeID()) { CFRunLoopSourceInvalidate((CFRunLoopSourceRef)obj); } else if (t == CFMachPortGetTypeID()) { CFMachPortInvalidate((CFMachPortRef)obj); } else if (t == CFSocketGetTypeID()) { CFSocketInvalidate((CFSocketRef)obj); } /* For scheduled types of objects, it is invalidated by setting the client to NULL. */ else if (t == CFReadStreamGetTypeID()) { CFReadStreamSetClient((CFReadStreamRef)obj, kCFStreamEventNone, NULL, NULL); } else if (t == CFWriteStreamGetTypeID()) { CFWriteStreamSetClient((CFWriteStreamRef)obj, kCFStreamEventNone, NULL, NULL); } else if (t == CFHostGetTypeID()) { CFHostSetClient((CFHostRef)obj, NULL, NULL); } else if (t == SCNetworkReachabilityGetTypeID()) { SCNetworkReachabilitySetCallback((SCNetworkReachabilityRef)obj, NULL, NULL); } else if (t == CFRunLoopTimerGetTypeID()) { CFRunLoopTimerInvalidate((CFRunLoopTimerRef)obj); } else if (t == CFNetServiceGetTypeID()) { CFNetServiceSetClient((CFNetServiceRef)obj, NULL, NULL); } else if (t == CFNetServiceBrowserGetTypeID()) { CFNetServiceBrowserInvalidate((CFNetServiceBrowserRef)obj); } else if (t == CFNetServiceMonitorGetTypeID()) { CFNetServiceMonitorInvalidate((CFNetServiceMonitorRef)obj); } else if (t == SCNetworkReachabilityGetTypeID()) { SCNetworkConnectionStop((SCNetworkConnectionRef)obj, FALSE); } }
/* static */ void _ServerReleaseNetService(Server* server) { // Unschedule, cancel, and release the net service if there is one. if (server->_service != NULL) { CFNetServiceUnscheduleFromRunLoop(server->_service, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); CFNetServiceSetClient(server->_service, NULL, NULL); CFNetServiceCancel(server->_service); CFRelease(server->_service); server->_service = NULL; } }
static void bonjour_start_service(CFNetServiceRef *svc, char *service_type, uint32_t port, txt_rec_t *txt) { CFStringRef str; CFStreamError error = {0}; CFNetServiceClientContext context = {0, NULL, NULL, NULL, NULL}; str = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, service_type, kCFStringEncodingASCII, kCFAllocatorNull); *svc = CFNetServiceCreate(NULL, CFSTR(""), str, CFSTR("Tvheadend"), port); if (!*svc) { tvhlog(LOG_ERR, "bonjour", "service creation failed"); return; } CFNetServiceSetClient(*svc, bonjour_callback, &context); CFNetServiceScheduleWithRunLoop(*svc, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); if (txt) { CFDataRef data = NULL; CFMutableDictionaryRef dict; dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); while(txt->key) { str = CFStringCreateWithCString (NULL, txt->key, kCFStringEncodingASCII); data = CFDataCreate (NULL, (uint8_t *) txt->value, strlen(txt->value)); CFDictionaryAddValue(dict, str, data); txt++; } data = CFNetServiceCreateTXTDataWithDictionary(NULL, dict); CFNetServiceSetTXTData(*svc, data); CFRelease(data); CFRelease(dict); } if (!CFNetServiceRegisterWithOptions(*svc, 0, &error)) tvhlog(LOG_ERR, "bonjour", "registration failed (service type = %s, " "domain = %ld, error =%d)", service_type, error.domain, error.error); else tvhlog(LOG_INFO, "bonjour", "service '%s' successfully established", service_type); }
//methods to implement for concrete implementations bool CZeroconfOSX::doPublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, std::map<std::string, std::string> txt) { CLog::Log(LOGDEBUG, "CZeroconfOSX::doPublishService identifier: %s type: %s name:%s port:%i", fcr_identifier.c_str(), fcr_type.c_str(), fcr_name.c_str(), f_port); CFStringRef name = CFStringCreateWithCString (NULL, fcr_name.c_str(), kCFStringEncodingUTF8 ); CFStringRef type = CFStringCreateWithCString (NULL, fcr_type.c_str(), kCFStringEncodingUTF8 ); CFNetServiceRef netService = CFNetServiceCreate(NULL, CFSTR(""), type, name, f_port); CFRelease(name); CFRelease(type); //now register it CFNetServiceClientContext clientContext = { 0, this, NULL, NULL, NULL }; CFStreamError error; CFNetServiceSetClient(netService, registerCallback, &clientContext); CFNetServiceScheduleWithRunLoop(netService, m_runloop, kCFRunLoopCommonModes); //add txt records if(!txt.empty()) { //txt map to dictionary CFDataRef txtData = NULL; CFMutableDictionaryRef txtDict = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); for(std::map<std::string, std::string>::const_iterator it = txt.begin(); it != txt.end(); ++it) { CFStringRef key = CFStringCreateWithCString (NULL, it->first.c_str(), kCFStringEncodingUTF8 ); CFDataRef value = CFDataCreate ( NULL, (UInt8 *)it->second.c_str(), strlen(it->second.c_str()) ); CFDictionaryAddValue(txtDict,key, value); } //add txt records to service txtData = CFNetServiceCreateTXTDataWithDictionary(NULL, txtDict); CFNetServiceSetTXTData(netService, txtData); CFRelease(txtData); CFRelease(txtDict); } Boolean result = CFNetServiceRegisterWithOptions (netService, 0, &error); if (result == false) { // Something went wrong so lets clean up. CFNetServiceUnscheduleFromRunLoop(netService, m_runloop, kCFRunLoopCommonModes); CFNetServiceSetClient(netService, NULL, NULL); CFRelease(netService); netService = NULL; CLog::Log(LOGERROR, "CZeroconfOSX::doPublishService CFNetServiceRegister returned " "(domain = %d, error = %"PRId64")", (int)error.domain, (int64_t)error.error); } else { CSingleLock lock(m_data_guard); m_services.insert(make_pair(fcr_identifier, netService)); } return result; }
void ofxBonjourIp::NetServiceResolvedCallBack(CFNetServiceRef theService, CFStreamError* error, void* info) { //ofLog() << "netService resolved"; bool serviceResolved = false; if(error->error != 0) ofLog() << "Error: " << error->error; CFArrayRef addresses = CFNetServiceGetAddressing(theService); struct sockaddr * socketAddress = NULL; for(int i=0; i < CFArrayGetCount(addresses); i++) { // error stopped here once! socketAddress = (struct sockaddr *) CFDataGetBytePtr((CFDataRef)CFArrayGetValueAtIndex(addresses, i)); /* Only continue if this is an IPv4 address. */ //|| socketAddress->sa_family == AF_INET6 ) == 0.0.0.0 if (socketAddress && socketAddress->sa_family == AF_INET && info != NULL) { string addr = inet_ntoa(((struct sockaddr_in *)socketAddress)->sin_addr); // pdp_ip0 int port = ntohs(((struct sockaddr_in *)socketAddress)->sin_port); // don't connect to self or 127.0.0.1 or 0.0.0.0 ofxBonjourIp* bonjour = (ofxBonjourIp*)info; if(addr != "0.0.0.0" && addr != "127.0.0.1" && addr != bonjour->deviceIp) { serviceResolved = true; ofLog() << "* Successful connection: " << addr << ", " << port; // info has a reference to the class object // set the server ip bonjour->serverIp = addr; bonjour->serverHostName = CFStringGetCStringPtr(CFNetServiceGetTargetHost(theService), kCFStringEncodingMacRoman); bonjour->connectedToService = true; ofNotifyEvent(bonjour->discoveredServiceEvent,bonjour->serverIp,bonjour); } else { ofLog() << "Not connecting to self: " << addr << ", " << port; } } } // all the service details /*const char *host = CFStringGetCStringPtr(CFNetServiceGetTargetHost(theService), kCFStringEncodingMacRoman); //trents-MacBook-Pro.local ofLog() << "host: " << host; const char *type = CFStringGetCStringPtr(CFNetServiceGetType(theService), kCFStringEncodingMacRoman); //_ofxBonjourIp._tcp. ofLog() << "type: " << type; const char *domain = CFStringGetCStringPtr(CFNetServiceGetDomain(theService), kCFStringEncodingMacRoman); //local. ofLog() << "domain: " << domain; int port = CFNetServiceGetPortNumber(theService); //7777 ofLog() << "port: " << port; const char *name = CFStringGetCStringPtr(CFNetServiceGetName(theService), kCFStringEncodingMacRoman); ofLog() << "name: " << name; // name is "" if not defined. should become device name once resolved?*/ // release stuff (loop and callback) if(!serviceResolved) { CFNetServiceUnscheduleFromRunLoop(theService, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); CFNetServiceSetClient(theService, NULL, NULL); } //CFNetServiceUnscheduleFromRunLoop(theService, CFRunLoopGetCurrent(),kCFRunLoopCommonModes); //CFNetServiceSetClient(theService, NULL, NULL); //CFRelease(theService); breaks stuff //CFRelease(addresses); }
/* static */ Boolean _ServerCreateAndRegisterNetService(Server* server) { do { UInt32 port = server->_port; Boolean didSet, didRegister; CFNetServiceClientContext netSvcCtxt = {0, server, (CFAllocatorRetainCallBack)&CFRetain, (CFAllocatorReleaseCallBack)&CFRelease, (CFAllocatorCopyDescriptionCallBack)&CFCopyDescription}; // If the port was unspecified, get the port from the socket. if (port == 0) { // Get the local address CFDataRef addr = CFSocketCopyAddress(server->_sockets[0]); struct sockaddr_in* nativeAddr = (struct sockaddr_in*)CFDataGetBytePtr(addr); CFRelease(addr); port = ntohs(nativeAddr->sin_port); } // Create the service for registration. server->_service = CFNetServiceCreate(CFGetAllocator((_CFServerRef)server), _kCFServerEmptyString, server->_type, server->_name, port); // Require the service for the socket. if (server->_service == NULL) break; // Try setting the client on the service. didSet = CFNetServiceSetClient(server->_service, (CFNetServiceClientCallBack)&_NetServiceCallBack, &netSvcCtxt); // Check to make sure it set before registering. if (!didSet) break; // Schedule the service on the run loop. CFNetServiceScheduleWithRunLoop(server->_service, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); // Start the registration. didRegister = CFNetServiceRegisterWithOptions(server->_service, 0, NULL); // If registration failed, die. if (!didRegister) break; return TRUE; } while (0); // Failed to set up the service, so clean up anything that succeeded. _ServerReleaseNetService(server); return FALSE; }