From 633c82407e47ec2ba7a92cef9c5b30a24a93fc68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sat, 18 Jul 2009 00:56:16 +0200
Subject: Resolve LinkLocal services.


diff --git a/Nim/main.cpp b/Nim/main.cpp
index 7c7105e..390bc6c 100644
--- a/Nim/main.cpp
+++ b/Nim/main.cpp
@@ -67,11 +67,11 @@ class Server {
 				dnsSDService_->onServiceRegistered.connect(boost::bind(&Server::handleServiceRegistered, this, _1));
 				LinkLocalServiceInfo info;
 				info.setFirstName("Remko");
-				info.setLastName("Tron\xc3\xe7on");
+				info.setLastName("Tron\xc3\xa7on");
 				info.setEMail("email@example.com");
 				info.setJID(JID("jid@example.com"));
 				info.setMessage("I'm not Here");
-				info.setNick("remko");
+				info.setNick("Remko");
 				info.setStatus(LinkLocalServiceInfo::Away);
 				info.setPort(linkLocalConnectionPort_);
 				dnsSDService_->registerService(session->getJID().toBare().toString(), linkLocalConnectionPort_, info);
@@ -85,6 +85,7 @@ class Server {
 		void handleSessionFinished(boost::shared_ptr<ServerFromClientSession> session) {
 			serverFromClientSessions_.erase(std::remove(serverFromClientSessions_.begin(), serverFromClientSessions_.end(), session), serverFromClientSessions_.end());
 			if (serverFromClientSessions_.empty()) {
+				std::cout << "Service unregistered" << std::endl;
 				dnsSDServiceRegistered_ = false;
 				dnsSDService_->unregisterService();
 			}
diff --git a/Swiften/LinkLocal/AppleDNSSDService.cpp b/Swiften/LinkLocal/AppleDNSSDService.cpp
index 4dd74eb..abf8548 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.cpp
+++ b/Swiften/LinkLocal/AppleDNSSDService.cpp
@@ -12,17 +12,12 @@ namespace Swift {
 
 #if 0
 namespace {
-	void handleServiceResolved( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context ) {
-		std::cerr << "Service resolved " << fullname << " " << hosttarget << " " << port << " " << txtLen << " " << /*std::string((const char*) txtRecord, txtLen) <<*/ std::endl;
-	}
-
 	void handleAddressInfoReceived ( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context ) {
 		//std::cerr << "Address received " << HostAddress((const unsigned char*) address->sa_data, 4).toString() << std::endl;
 	} 
 }
 
 /*
-		result = DNSServiceResolve(&resolveSDRef, 0, 6, "Remko@Micro", "_presence._tcp.", "local.", handleServiceResolved , 0);
 		result = DNSServiceGetAddrInfo(&addressSDRef, 0, 6, kDNSServiceProtocol_IPv4, "Micro.local.", handleAddressInfoReceived, 0);
 	*/
 #endif
@@ -51,19 +46,47 @@ void AppleDNSSDService::registerService(const String& name, int port, const Link
 	assert(!registerSDRef);
 	ByteArray txtRecord = info.toTXTRecord();
 	DNSServiceErrorType result = DNSServiceRegister(&registerSDRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtRecord.getSize(), txtRecord.getData(), &AppleDNSSDService::handleServiceRegisteredGlobal, this);
-	interruptSelect();
 	if (result != kDNSServiceErr_NoError) {
 		onError();
 	}
+
+	interruptSelect();
 }
 
 void AppleDNSSDService::unregisterService() {
 	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
 	assert(registerSDRef);
-	interruptSelect();
 	DNSServiceRefDeallocate(registerSDRef);
 	registerSDRef = NULL;
+
+	interruptSelect();
+}
+
+void AppleDNSSDService::startResolvingService(const Service& service) {
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+	DNSServiceRef resolveSDRef;
+	DNSServiceErrorType result = DNSServiceResolve(&resolveSDRef, 0, service.networkInterface, service.name.getUTF8Data(), service.type.getUTF8Data(), service.domain.getUTF8Data(), &AppleDNSSDService::handleServiceResolvedGlobal, this);
+	if (result != kDNSServiceErr_NoError) {
+		onError();
+	}
+
+	bool isNew = resolveSDRefs.insert(std::make_pair(service, resolveSDRef)).second;
+	assert(isNew);
+
+	interruptSelect();
+}
+
+void AppleDNSSDService::stopResolvingService(const Service& service) {
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+	ServiceSDRefMap::iterator i = resolveSDRefs.find(service);
+	assert(i != resolveSDRefs.end());
+	DNSServiceRefDeallocate(i->second);
+	resolveSDRefs.erase(i);
+
+	interruptSelect();
 }
 
 void AppleDNSSDService::stop() {
@@ -89,14 +112,15 @@ void AppleDNSSDService::doStart() {
 	while (!stopRequested) {
 		fd_set fdSet;
 		FD_ZERO(&fdSet);
-		int maxSocket = 0;
+		int maxSocket = interruptSelectReadSocket;
+		FD_SET(interruptSelectReadSocket, &fdSet);
 
 		{
 			boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
 			// Browsing
 			int browseSocket = DNSServiceRefSockFD(browseSDRef);
-			maxSocket = browseSocket;
+			maxSocket = std::max(maxSocket, browseSocket);
 			FD_SET(browseSocket, &fdSet);
 
 			// Registration
@@ -105,6 +129,13 @@ void AppleDNSSDService::doStart() {
 				maxSocket = std::max(maxSocket, registerSocket);
 				FD_SET(registerSocket, &fdSet);
 			}
+
+			// Resolving
+			for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
+				int resolveSocket = DNSServiceRefSockFD(i->second);
+				maxSocket = std::max(maxSocket, resolveSocket);
+				FD_SET(resolveSocket, &fdSet);
+			}
 		}
 
 		int selectResult = select(maxSocket+1, &fdSet, NULL, NULL, 0);
@@ -125,6 +156,11 @@ void AppleDNSSDService::doStart() {
 			if (registerSDRef && FD_ISSET(DNSServiceRefSockFD(registerSDRef), &fdSet)) {
 				DNSServiceProcessResult(registerSDRef);
 			}
+			for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
+				if (FD_ISSET(DNSServiceRefSockFD(i->second), &fdSet)) {
+					DNSServiceProcessResult(i->second);
+				}
+			}
 		}
 	}
 
@@ -174,4 +210,23 @@ void AppleDNSSDService::handleServiceRegistered(DNSServiceRef, DNSServiceFlags,
 	}
 }
 
+void AppleDNSSDService::handleServiceResolvedGlobal(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context ) {
+	static_cast<AppleDNSSDService*>(context)->handleServiceResolved(sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtLen, txtRecord);
+}
+
+void AppleDNSSDService::handleServiceResolved(DNSServiceRef sdRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) {
+	if (errorCode != kDNSServiceErr_NoError) {
+		std::cerr << "Resolve error " << hosttarget << std::endl;
+		return;
+	}
+	for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
+		if (i->second == sdRef) {
+			MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), i->first, ResolveResult(hosttarget, port, LinkLocalServiceInfo::createFromTXTRecord(ByteArray(reinterpret_cast<const char*>(txtRecord), txtLen)))), shared_from_this());
+			return;
+		}
+	}
+	assert(false);
+}
+
+
 }
diff --git a/Swiften/LinkLocal/AppleDNSSDService.h b/Swiften/LinkLocal/AppleDNSSDService.h
index 6299a96..bf6b4aa 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.h
+++ b/Swiften/LinkLocal/AppleDNSSDService.h
@@ -15,6 +15,8 @@ namespace Swift {
 			~AppleDNSSDService();
 
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&);
+			virtual void startResolvingService(const Service&);
+			virtual void stopResolvingService(const Service&);
 			virtual void unregisterService();
 			virtual void start();
 			virtual void stop();
@@ -27,6 +29,8 @@ namespace Swift {
 			void handleServiceDiscovered(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, const char *, const char *);
 			static void handleServiceRegisteredGlobal(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType, const char *, const char *, const char *, void *);
 			void handleServiceRegistered(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType, const char *, const char *, const char *);
+			static void handleServiceResolvedGlobal(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, const char *, uint16_t, uint16_t, const unsigned char *, void *);
+			void handleServiceResolved(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, const char *, uint16_t, uint16_t, const unsigned char *);
 
 		private:
 			boost::thread* thread;
@@ -36,5 +40,7 @@ namespace Swift {
 			boost::mutex sdRefsMutex;
 			DNSServiceRef browseSDRef;
 			DNSServiceRef registerSDRef;
+			typedef std::map<Service, DNSServiceRef> ServiceSDRefMap;
+			ServiceSDRefMap resolveSDRefs;
 	};
 }
diff --git a/Swiften/LinkLocal/DNSSDService.h b/Swiften/LinkLocal/DNSSDService.h
index 214cad4..6a9425f 100644
--- a/Swiften/LinkLocal/DNSSDService.h
+++ b/Swiften/LinkLocal/DNSSDService.h
@@ -4,6 +4,7 @@
 #include <map>
 
 #include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
 
 namespace Swift {
 	class LinkLocalServiceInfo;
@@ -12,21 +13,53 @@ namespace Swift {
 		public:
 			struct Service {
 				Service(const String& name, const String& type, const String& domain, int networkInterface) : name(name), type(type), domain(domain), networkInterface(networkInterface) {}
+				bool operator==(const Service& o) const {
+					return name == o.name && type == o.type && domain == o.domain && (networkInterface != 0 && o.networkInterface != 0 ? networkInterface == o.networkInterface : true);
+				}
+				bool operator<(const Service& o) const {
+					if (o.name == name) {
+						if (o.type == type) {
+							if (o.domain == domain) {
+								return networkInterface < o.networkInterface;
+							}
+							else {
+								return domain < o.domain;
+							}
+						}
+						else {
+							return type < o.type;
+						}
+					}
+					else {
+						return o.name < name;
+					}
+				}
+
 				String name;
 				String type;
 				String domain;
 				int networkInterface;
 			};
 
+			struct ResolveResult {
+				ResolveResult(const String& host, int port, const LinkLocalServiceInfo& info) : host(host), port(port), info(info) {}
+				String host;
+				int port;
+				LinkLocalServiceInfo info;
+			};
+
 			virtual ~DNSSDService();
 
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&) = 0;
+			virtual void startResolvingService(const Service&) = 0;
+			virtual void stopResolvingService(const Service&) = 0;
 			virtual void unregisterService() = 0;
 			virtual void start() = 0;
 
 			boost::signal<void (const Service&)> onServiceAdded;
 			boost::signal<void (const Service&)> onServiceRemoved;
 			boost::signal<void (const Service&)> onServiceRegistered;
+			boost::signal<void (const Service&, const ResolveResult&)> onServiceResolved;
 			boost::signal<void ()> onError;
 	};
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.cpp b/Swiften/LinkLocal/LinkLocalRoster.cpp
index 9f7e4ad..fde52a9 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.cpp
+++ b/Swiften/LinkLocal/LinkLocalRoster.cpp
@@ -6,16 +6,36 @@
 namespace Swift {
 
 LinkLocalRoster::LinkLocalRoster(boost::shared_ptr<DNSSDService> service) : dnsSDService(service) {
-	service->onServiceAdded.connect(boost::bind(&LinkLocalRoster::handleServiceAdded, this, _1));
-	service->onServiceRemoved.connect(boost::bind(&LinkLocalRoster::handleServiceRemoved, this, _1));
+	dnsSDService->onServiceAdded.connect(boost::bind(&LinkLocalRoster::handleServiceAdded, this, _1));
+	dnsSDService->onServiceRemoved.connect(boost::bind(&LinkLocalRoster::handleServiceRemoved, this, _1));
+	dnsSDService->onServiceResolved.connect(boost::bind(&LinkLocalRoster::handleServiceResolved, this, _1, _2));
+	dnsSDService->onError.connect(boost::bind(&LinkLocalRoster::handleDNSSDError, this));
+	dnsSDService->onServiceRegistered.connect(boost::bind(&LinkLocalRoster::handleServiceRegistered, this, _1));
 }
 
 void LinkLocalRoster::handleServiceAdded(const DNSSDService::Service& service) {
+	if (selfService && *selfService == service) {
+		return;
+	}
 	std::cout << "Service added " << service.name << " " << service.type << " " << service.domain << std::endl;
+	dnsSDService->startResolvingService(service);
 }
 
 void LinkLocalRoster::handleServiceRemoved(const DNSSDService::Service& service) {
 	std::cout << "Service removed " << service.name << " " << service.type << " " << service.domain << std::endl;
+	dnsSDService->stopResolvingService(service);
+}
+
+void LinkLocalRoster::handleServiceResolved(const DNSSDService::Service& service, const DNSSDService::ResolveResult& result) {
+	std::cout << "Service resolved: " << service.name << "->" << result.host << " " << result.port << " " << result.info.getLastName() << std::endl;
+}
+
+void LinkLocalRoster::handleServiceRegistered(const DNSSDService::Service& service) {
+	selfService = service;
+}
+
+void LinkLocalRoster::handleDNSSDError() {
+	std::cout << "DNSSD Error" << std::endl;
 }
 
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.h b/Swiften/LinkLocal/LinkLocalRoster.h
index 5b66b84..3cc8d55 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.h
+++ b/Swiften/LinkLocal/LinkLocalRoster.h
@@ -12,8 +12,12 @@ namespace Swift {
 		private:
 			void handleServiceAdded(const DNSSDService::Service&);
 			void handleServiceRemoved(const DNSSDService::Service&);
+			void handleServiceResolved(const DNSSDService::Service& service, const DNSSDService::ResolveResult& result);
+			void handleDNSSDError();
+			void handleServiceRegistered(const DNSSDService::Service& service);
 
 		private:
 			boost::shared_ptr<DNSSDService> dnsSDService;
+			boost::optional<DNSSDService::Service> selfService;
 	};
 }
-- 
cgit v0.10.2-6-g49f6