From 7388443bda19877980e368a0654b55097db0bda7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sat, 18 Jul 2009 12:17:45 +0200
Subject: Add DNS-SD hostname resolving.


diff --git a/Swiften/LinkLocal/AppleDNSSDService.cpp b/Swiften/LinkLocal/AppleDNSSDService.cpp
index abf8548..18cdcb0 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.cpp
+++ b/Swiften/LinkLocal/AppleDNSSDService.cpp
@@ -4,30 +4,21 @@
 #include <unistd.h>
 #include <iostream>
 #include <sys/socket.h>
+#include <netinet/in.h>
+#include <fcntl.h>
 
 #include "Swiften/EventLoop/MainEventLoop.h"
 #include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/Network/HostAddress.h"
 
 namespace Swift {
 
-#if 0
-namespace {
-	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 = DNSServiceGetAddrInfo(&addressSDRef, 0, 6, kDNSServiceProtocol_IPv4, "Micro.local.", handleAddressInfoReceived, 0);
-	*/
-#endif
-
-
-AppleDNSSDService::AppleDNSSDService() : thread(0), stopRequested(false), browseSDRef(0), registerSDRef(0) {
+AppleDNSSDService::AppleDNSSDService() : thread(0), stopRequested(false), haveError(false), browseSDRef(0), registerSDRef(0) {
 	int fds[2];
 	int result = pipe(fds);
 	assert(result == 0);
 	interruptSelectReadSocket = fds[0];
+	fcntl(interruptSelectReadSocket, F_SETFL, fcntl(interruptSelectReadSocket, F_GETFL)|O_NONBLOCK);
 	interruptSelectWriteSocket = fds[1];
 }
 
@@ -36,10 +27,20 @@ AppleDNSSDService::~AppleDNSSDService() {
 }
 
 void AppleDNSSDService::start() {
-	assert(!thread);
+	stop();
 	thread = new boost::thread(boost::bind(&AppleDNSSDService::doStart, shared_from_this()));
 }
 
+void AppleDNSSDService::stop() {
+	if (thread) {
+		stopRequested = true;
+		interruptSelect();
+		thread->join();
+		delete thread;
+		stopRequested = false;
+	}
+}
+
 void AppleDNSSDService::registerService(const String& name, int port, const LinkLocalServiceInfo& info) {
 	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
@@ -47,7 +48,8 @@ void AppleDNSSDService::registerService(const String& name, int port, const Link
 	ByteArray txtRecord = info.toTXTRecord();
 	DNSServiceErrorType result = DNSServiceRegister(&registerSDRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtRecord.getSize(), txtRecord.getData(), &AppleDNSSDService::handleServiceRegisteredGlobal, this);
 	if (result != kDNSServiceErr_NoError) {
-		onError();
+		std::cerr << "Error creating service registration" << std::endl;
+		haveError = true;
 	}
 
 	interruptSelect();
@@ -57,10 +59,8 @@ void AppleDNSSDService::unregisterService() {
 	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
 
 	assert(registerSDRef);
-	DNSServiceRefDeallocate(registerSDRef);
+	DNSServiceRefDeallocate(registerSDRef); // Interrupts select()
 	registerSDRef = NULL;
-
-	interruptSelect();
 }
 
 void AppleDNSSDService::startResolvingService(const Service& service) {
@@ -69,11 +69,13 @@ void AppleDNSSDService::startResolvingService(const Service& service) {
 	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();
+		std::cerr << "Error creating service resolve query" << std::endl;
+		haveError = true;
+	}
+	else {
+		bool isNew = resolveSDRefs.insert(std::make_pair(service, resolveSDRef)).second;
+		assert(isNew);
 	}
-
-	bool isNew = resolveSDRefs.insert(std::make_pair(service, resolveSDRef)).second;
-	assert(isNew);
 
 	interruptSelect();
 }
@@ -83,33 +85,40 @@ void AppleDNSSDService::stopResolvingService(const Service& service) {
 
 	ServiceSDRefMap::iterator i = resolveSDRefs.find(service);
 	assert(i != resolveSDRefs.end());
-	DNSServiceRefDeallocate(i->second);
+	DNSServiceRefDeallocate(i->second); // Interrupts select()
 	resolveSDRefs.erase(i);
-
-	interruptSelect();
 }
 
-void AppleDNSSDService::stop() {
-	if (thread) {
-		stopRequested = true;
-		interruptSelect();
-		thread->join();
-		delete thread;
-		stopRequested = false;
+void AppleDNSSDService::resolveHostname(const String& hostname, int interfaceIndex) {
+	boost::lock_guard<boost::mutex> lock(sdRefsMutex);
+
+	DNSServiceRef hostnameResolveSDRef;
+	DNSServiceErrorType result = DNSServiceGetAddrInfo(&hostnameResolveSDRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, hostname.getUTF8Data(), &AppleDNSSDService::handleHostnameResolvedGlobal, this);
+	if (result != kDNSServiceErr_NoError) {
+		std::cerr << "Error creating hostname resolve query" << std::endl;
+		haveError = true;
+	}
+	else {
+		hostnameResolveSDRefs.push_back(hostnameResolveSDRef);
 	}
+
+	interruptSelect();
 }
 
 void AppleDNSSDService::doStart() {
+	haveError = false;
+	onStarted();
+
 	// Listen for new services
 	assert(!browseSDRef);
 	DNSServiceErrorType result = DNSServiceBrowse(&browseSDRef, 0, 0, "_presence._tcp", 0, &AppleDNSSDService::handleServiceDiscoveredGlobal , this);
 	if (result != kDNSServiceErr_NoError) {
-		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
-		return;
+		std::cerr << "Error creating browse query" << std::endl;
+		haveError = true;
 	}
 
 	// Run the main loop
-	while (!stopRequested) {
+	while (!haveError && !stopRequested) {
 		fd_set fdSet;
 		FD_ZERO(&fdSet);
 		int maxSocket = interruptSelectReadSocket;
@@ -130,24 +139,32 @@ void AppleDNSSDService::doStart() {
 				FD_SET(registerSocket, &fdSet);
 			}
 
-			// Resolving
+			// Service 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);
 			}
+
+			// Hostname resolving
+			for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
+				int hostnameResolveSocket = DNSServiceRefSockFD(*i);
+				maxSocket = std::max(maxSocket, hostnameResolveSocket);
+				FD_SET(hostnameResolveSocket, &fdSet);
+			}
 		}
 
 		int selectResult = select(maxSocket+1, &fdSet, NULL, NULL, 0);
 
+		// Flush the interruptSelectReadSocket
+		if (FD_ISSET(interruptSelectReadSocket, &fdSet)) {
+			char dummy;
+			while (read(interruptSelectReadSocket, &dummy, 1) > 0) {}
+		}
+
 		{
 			boost::lock_guard<boost::mutex> lock(sdRefsMutex);
-
-			if (selectResult == -1) {
-				MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
-				return;
-			}
-			if (selectResult == 0) {
+			if (selectResult <= 0) {
 				continue;
 			}
 			if (FD_ISSET(DNSServiceRefSockFD(browseSDRef), &fdSet)) {
@@ -161,9 +178,27 @@ void AppleDNSSDService::doStart() {
 					DNSServiceProcessResult(i->second);
 				}
 			}
+			for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
+				if (FD_ISSET(DNSServiceRefSockFD(*i), &fdSet)) {
+					DNSServiceProcessResult(*i);
+					hostnameResolveSDRefs.erase(std::remove(hostnameResolveSDRefs.begin(), hostnameResolveSDRefs.end(), *i), hostnameResolveSDRefs.end());
+					DNSServiceRefDeallocate(*i);
+					break; // Stop the loop, because we removed an element
+				}
+			}
 		}
 	}
 
+	for (ServiceSDRefMap::const_iterator i = resolveSDRefs.begin(); i != resolveSDRefs.end(); ++i) {
+		DNSServiceRefDeallocate(i->second);
+	}
+	resolveSDRefs.clear();
+
+	for (HostnameSDRefs::const_iterator i = hostnameResolveSDRefs.begin(); i != hostnameResolveSDRefs.end(); ++i) {
+		DNSServiceRefDeallocate(*i);
+	}
+	hostnameResolveSDRefs.clear();
+
 	if (registerSDRef) {
 		DNSServiceRefDeallocate(registerSDRef);
 		registerSDRef = NULL;
@@ -171,6 +206,8 @@ void AppleDNSSDService::doStart() {
 
 	DNSServiceRefDeallocate(browseSDRef);
 	browseSDRef = NULL;
+
+	MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), haveError), shared_from_this());
 }
 
 void AppleDNSSDService::interruptSelect() {
@@ -184,7 +221,7 @@ void AppleDNSSDService::handleServiceDiscoveredGlobal(DNSServiceRef sdRef, DNSSe
 
 void AppleDNSSDService::handleServiceDiscovered(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain) {
 	if (errorCode != kDNSServiceErr_NoError) {
-		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+		return;
 	}
 	else {
 		Service service(serviceName, regtype, replyDomain, interfaceIndex);
@@ -203,7 +240,8 @@ void AppleDNSSDService::handleServiceRegisteredGlobal(DNSServiceRef sdRef, DNSSe
 
 void AppleDNSSDService::handleServiceRegistered(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
 	if (errorCode != kDNSServiceErr_NoError) {
-		MainEventLoop::postEvent(boost::ref(onError), shared_from_this());
+		std::cerr << "Error registering service" << std::endl;
+		haveError = true;
 	}
 	else {
 		MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRegistered), Service(name, regtype, domain, 0)), shared_from_this());
@@ -228,5 +266,21 @@ void AppleDNSSDService::handleServiceResolved(DNSServiceRef sdRef, DNSServiceFla
 	assert(false);
 }
 
+void AppleDNSSDService::handleHostnameResolvedGlobal(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) {
+	static_cast<AppleDNSSDService*>(context)->handleHostnameResolved(sdRef, flags, interfaceIndex, errorCode, hostname, address, ttl);
+} 
+
+void AppleDNSSDService::handleHostnameResolved(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *rawAddress, uint32_t) {
+	if (errorCode) {
+		std::cerr << "Error resolving hostname" << std::endl;
+		MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), hostname, boost::optional<HostAddress>()), shared_from_this());
+	}
+	else {
+		assert(rawAddress->sa_family == AF_INET);
+		const sockaddr_in* sa = reinterpret_cast<const sockaddr_in*>(rawAddress);
+		uint32_t address = ntohl(sa->sin_addr.s_addr);
+		MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), String(hostname), HostAddress(reinterpret_cast<unsigned char*>(&address), 4)), shared_from_this());
+	}
+}
 
 }
diff --git a/Swiften/LinkLocal/AppleDNSSDService.h b/Swiften/LinkLocal/AppleDNSSDService.h
index bf6b4aa..cdeafed 100644
--- a/Swiften/LinkLocal/AppleDNSSDService.h
+++ b/Swiften/LinkLocal/AppleDNSSDService.h
@@ -14,12 +14,16 @@ namespace Swift {
 			AppleDNSSDService();
 			~AppleDNSSDService();
 
+			virtual void start();
+			virtual void stop();
+
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&);
+			virtual void unregisterService();
+
 			virtual void startResolvingService(const Service&);
 			virtual void stopResolvingService(const Service&);
-			virtual void unregisterService();
-			virtual void start();
-			virtual void stop();
+
+			virtual void resolveHostname(const String& hostname, int interfaceIndex = 0);
 
 		private:
 			void doStart();
@@ -31,10 +35,13 @@ namespace Swift {
 			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 *);
+			static void handleHostnameResolvedGlobal(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context);
+			void handleHostnameResolved(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl);
 
 		private:
 			boost::thread* thread;
 			bool stopRequested;
+			bool haveError;
 			int interruptSelectReadSocket;
 			int interruptSelectWriteSocket;
 			boost::mutex sdRefsMutex;
@@ -42,5 +49,7 @@ namespace Swift {
 			DNSServiceRef registerSDRef;
 			typedef std::map<Service, DNSServiceRef> ServiceSDRefMap;
 			ServiceSDRefMap resolveSDRefs;
+			typedef std::vector<DNSServiceRef> HostnameSDRefs;
+			HostnameSDRefs hostnameResolveSDRefs;
 	};
 }
diff --git a/Swiften/LinkLocal/DNSSDService.h b/Swiften/LinkLocal/DNSSDService.h
index 6a9425f..3dd8c7b 100644
--- a/Swiften/LinkLocal/DNSSDService.h
+++ b/Swiften/LinkLocal/DNSSDService.h
@@ -8,6 +8,7 @@
 
 namespace Swift {
 	class LinkLocalServiceInfo;
+	class HostAddress;
 
 	class DNSSDService {
 		public:
@@ -50,16 +51,23 @@ namespace Swift {
 
 			virtual ~DNSSDService();
 
+			virtual void start() = 0;
+			virtual void stop() = 0;
+
 			virtual void registerService(const String& name, int port, const LinkLocalServiceInfo&) = 0;
+			virtual void unregisterService() = 0;
+
 			virtual void startResolvingService(const Service&) = 0;
 			virtual void stopResolvingService(const Service&) = 0;
-			virtual void unregisterService() = 0;
-			virtual void start() = 0;
+			
+			virtual void resolveHostname(const String& hostname, int interfaceIndex = 0) = 0;
 
+			boost::signal<void ()> onStarted;
+			boost::signal<void (bool)> onStopped;
 			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;
+			boost::signal<void (const String&, const boost::optional<HostAddress>&)> onHostnameResolved;
 	};
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.cpp b/Swiften/LinkLocal/LinkLocalRoster.cpp
index fde52a9..feec7b7 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.cpp
+++ b/Swiften/LinkLocal/LinkLocalRoster.cpp
@@ -2,15 +2,17 @@
 #include <iostream>
 
 #include "Swiften/LinkLocal/LinkLocalRoster.h"
+#include "Swiften/Network/HostAddress.h"
 
 namespace Swift {
 
 LinkLocalRoster::LinkLocalRoster(boost::shared_ptr<DNSSDService> service) : dnsSDService(service) {
+	dnsSDService->onStopped.connect(boost::bind(&LinkLocalRoster::handleStopped, this, _1));
+	dnsSDService->onServiceRegistered.connect(boost::bind(&LinkLocalRoster::handleServiceRegistered, 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));
+	dnsSDService->onHostnameResolved.connect(boost::bind(&LinkLocalRoster::handleHostnameResolved, this, _1, _2));
 }
 
 void LinkLocalRoster::handleServiceAdded(const DNSSDService::Service& service) {
@@ -28,14 +30,25 @@ void LinkLocalRoster::handleServiceRemoved(const DNSSDService::Service& 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;
+	dnsSDService->resolveHostname(result.host);
 }
 
+void LinkLocalRoster::handleHostnameResolved(const String& hostname, const boost::optional<HostAddress>& address) {
+	if (address) {
+		std::cout << "Address resolved: " << hostname << " " << address->toString() << std::endl;
+	}
+	else {
+		std::cout << "Unable to resolve address for " << hostname << std::endl;
+	}
+}
+
+
 void LinkLocalRoster::handleServiceRegistered(const DNSSDService::Service& service) {
 	selfService = service;
 }
 
-void LinkLocalRoster::handleDNSSDError() {
-	std::cout << "DNSSD Error" << std::endl;
+void LinkLocalRoster::handleStopped(bool error) {
+	std::cout << "DNSSDService stopped: " << error << std::endl;
 }
 
 }
diff --git a/Swiften/LinkLocal/LinkLocalRoster.h b/Swiften/LinkLocal/LinkLocalRoster.h
index 3cc8d55..321bd96 100644
--- a/Swiften/LinkLocal/LinkLocalRoster.h
+++ b/Swiften/LinkLocal/LinkLocalRoster.h
@@ -1,20 +1,24 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
 
 #include "Swiften/LinkLocal/DNSSDService.h"
 
 namespace Swift {
+	class HostAddress;
+
 	class LinkLocalRoster {
 		public:
 			LinkLocalRoster(boost::shared_ptr<DNSSDService> service);
 
 		private:
+			void handleStopped(bool);
+			void handleServiceRegistered(const DNSSDService::Service& service);
 			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);
+			void handleHostnameResolved(const String& hostname, const boost::optional<HostAddress>& address);
 
 		private:
 			boost::shared_ptr<DNSSDService> dnsSDService;
-- 
cgit v0.10.2-6-g49f6