From 0b6ac076f9d8d4d0eb7bac131e9889a227ee91ba Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Thu, 22 Mar 2012 00:15:52 +0100
Subject: Using only one unbound context. Less resource usage.


diff --git a/Swiften/Network/UnboundDomainNameResolver.cpp b/Swiften/Network/UnboundDomainNameResolver.cpp
index 1b36012..698a99a 100644
--- a/Swiften/Network/UnboundDomainNameResolver.cpp
+++ b/Swiften/Network/UnboundDomainNameResolver.cpp
@@ -1,73 +1,54 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #include "UnboundDomainNameResolver.h"
 
 #include <vector>
 
 #include <boost/bind.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 #include <Swiften/Base/Log.h>
 #include <Swiften/Network/DomainNameAddressQuery.h>
 #include <Swiften/Network/DomainNameResolveError.h>
 #include <Swiften/Network/DomainNameServiceQuery.h>
 #include <Swiften/Network/HostAddress.h>
-#include <Swiften/Network/Timer.h>
 #include <Swiften/Network/TimerFactory.h>
 
 
 #include <arpa/inet.h>
 #include <ldns/ldns.h>
-#include <unbound.h>
 #include <unistd.h>
 
 namespace Swift {
 
-class UnboundDomainNameServiceQuery : public DomainNameServiceQuery {
-	public:
-		UnboundDomainNameServiceQuery(TimerFactory* timerFactory, const std::string& name) : timerFactory(timerFactory), name(name), ubContext(0) {
-
-		}
+struct UnboundWrapperHelper {
+	UnboundWrapperHelper(UnboundDomainNameResolver* resolver, boost::shared_ptr<UnboundQuery> query) : resolver(resolver), query(query) {}
+	UnboundDomainNameResolver* resolver;
+	boost::shared_ptr<UnboundQuery> query;
+};
 
-		virtual ~UnboundDomainNameServiceQuery() {
-			if (ubContext) {
-				ub_ctx_delete(ubContext);
-			}
+class UnboundDomainNameServiceQuery : public DomainNameServiceQuery, public UnboundQuery, public boost::enable_shared_from_this<UnboundDomainNameServiceQuery>  {
+	public:
+		UnboundDomainNameServiceQuery(UnboundDomainNameResolver* resolver, ub_ctx* context, std::string name) : UnboundQuery(resolver, context), name(name) {
 		}
 
-		static void unbound_service_callback(void* data, int err, struct ub_result* result) {
-			UnboundDomainNameServiceQuery* query = static_cast<UnboundDomainNameServiceQuery*>(data);
-			query->handleResult(err, result);
-		}
+		virtual ~UnboundDomainNameServiceQuery() { }
 
 		virtual void run() {
 			int retval;
+			UnboundWrapperHelper* helper = new UnboundWrapperHelper(resolver, shared_from_this());
 
-			/* create context */
-			ubContext = ub_ctx_create();
-			if(!ubContext) {
-				SWIFT_LOG(debug) << "could not create unbound context" << std::endl;
-				return;
-			}
-			ub_ctx_async(ubContext, true);
 			retval = ub_resolve_async(ubContext, const_cast<char*>(name.c_str()), LDNS_RR_TYPE_SRV,
 						  1 /* CLASS IN (internet) */,
-						  this, unbound_service_callback, NULL);
+						  helper, UnboundDomainNameResolver::unbound_callback_wrapper, NULL);
 			if(retval != 0) {
 				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(retval) << std::endl;
-			}
-
-			timer = timerFactory->createTimer(500);
-			timer->onTick.connect(boost::bind(&UnboundDomainNameServiceQuery::processData, this));
-			timer->start();
-		}
-
-		void processData() {
-			if (ub_poll(ubContext)) {
-				int ret = ub_process(ubContext);
-				if(ret != 0) {
-					SWIFT_LOG(debug) << "resolve error: " << ub_strerror(ret) << std::endl;
-				}
-			} else {
-				timer->start();
+				delete helper;
 			}
 		}
 
@@ -86,7 +67,7 @@ class UnboundDomainNameServiceQuery : public DomainNameServiceQuery {
 							ldns_rr* rr = ldns_rr_list_rr(rrList, n);
 							if ((ldns_rr_get_type(rr) != LDNS_RR_TYPE_SRV) ||
 								(ldns_rr_get_class(rr) != LDNS_RR_CLASS_IN) ||
-							    (ldns_rr_rd_count(rr) != 4)) {
+								(ldns_rr_rd_count(rr) != 4)) {
 								continue;
 							}
 
@@ -97,8 +78,8 @@ class UnboundDomainNameServiceQuery : public DomainNameServiceQuery {
 
 							ldns_buffer_rewind(buffer);
 							if ((ldns_rdf2buffer_str_dname(buffer, ldns_rr_rdf(rr, 3)) != LDNS_STATUS_OK) ||
-							    (ldns_buffer_position(buffer) < 2) ||
-							    !ldns_buffer_reserve(buffer, 1)) {
+								(ldns_buffer_position(buffer) < 2) ||
+								!ldns_buffer_reserve(buffer, 1)) {
 								// either name invalid, empty or buffer to small
 								continue;
 							}
@@ -120,60 +101,26 @@ class UnboundDomainNameServiceQuery : public DomainNameServiceQuery {
 		}
 
 	private:
-		TimerFactory* timerFactory;
-		Timer::ref timer;
 		std::string name;
-		ub_ctx* ubContext;
 };
 
-class UnboundDomainNameAddressQuery : public DomainNameAddressQuery {
+class UnboundDomainNameAddressQuery : public DomainNameAddressQuery, public UnboundQuery, public boost::enable_shared_from_this<UnboundDomainNameAddressQuery> {
 	public:
-		UnboundDomainNameAddressQuery(TimerFactory* timerFactory, const std::string& name) : timerFactory(timerFactory), name(name), ubContext(0) {
-
+		UnboundDomainNameAddressQuery(UnboundDomainNameResolver* resolver, ub_ctx* context, std::string name) : UnboundQuery(resolver, context), name(name) {
 		}
 
-		virtual ~UnboundDomainNameAddressQuery() {
-			if (ubContext) {
-				ub_ctx_delete(ubContext);
-			}
-		}
-
-		static void unbound_address_callback(void* data, int err, struct ub_result* result) {
-			UnboundDomainNameAddressQuery* query = static_cast<UnboundDomainNameAddressQuery*>(data);
-			query->handleResult(err, result);
-		}
+		virtual ~UnboundDomainNameAddressQuery() { }
 
 		virtual void run() {
 			int retval;
+			UnboundWrapperHelper* helper = new UnboundWrapperHelper(resolver, shared_from_this());
 
-			/* create context */
-			ubContext = ub_ctx_create();
-			if(!ubContext) {
-				SWIFT_LOG(debug) << "could not create unbound context" << std::endl;
-				return;
-			}
-
-			ub_ctx_async(ubContext, true);
 			retval = ub_resolve_async(ubContext, const_cast<char*>(name.c_str()), LDNS_RR_TYPE_A,
 						  1 /* CLASS IN (internet) */,
-						  this, unbound_address_callback, NULL);
+						  helper, UnboundDomainNameResolver::unbound_callback_wrapper, NULL);
 			if(retval != 0) {
 				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(retval) << std::endl;
-			}
-
-			timer = timerFactory->createTimer(500);
-			timer->onTick.connect(boost::bind(&UnboundDomainNameAddressQuery::processData, this));
-			timer->start();
-		}
-
-		void processData() {
-			if (ub_poll(ubContext)) {
-				int ret = ub_process(ubContext);
-				if(ret != 0) {
-					SWIFT_LOG(debug) << "resolve error: " << ub_strerror(ret) << std::endl;
-				}
-			} else {
-				timer->start();
+				delete helper;
 			}
 		}
 
@@ -197,25 +144,68 @@ class UnboundDomainNameAddressQuery : public DomainNameAddressQuery {
 		}
 
 	private:
-		TimerFactory* timerFactory;
-		Timer::ref timer;
 		std::string name;
-		ub_ctx* ubContext;
-
 };
 
 UnboundDomainNameResolver::UnboundDomainNameResolver(EventLoop* eventLoop, TimerFactory* timerFactory) : eventLoop(eventLoop), timerFactory(timerFactory) {
+	ubContext = ub_ctx_create();
+	if(!ubContext) {
+		SWIFT_LOG(debug) << "could not create unbound context" << std::endl;
+	}
+
+	ub_ctx_async(ubContext, true);
+
+	int ret;
+
+	/* read /etc/resolv.conf for DNS proxy settings (from DHCP) */
+	if( (ret=ub_ctx_resolvconf(ubContext, const_cast<char*>("/etc/resolv.conf"))) != 0) {
+		SWIFT_LOG(debug) << "error reading resolv.conf: " << ub_strerror(ret) << ". errno says: " << strerror(errno) << std::endl;
+	}
+	/* read /etc/hosts for locally supplied host addresses */
+	if( (ret=ub_ctx_hosts(ubContext, const_cast<char*>("/etc/hosts"))) != 0) {
+		SWIFT_LOG(debug) << "error reading hosts: " << ub_strerror(ret) << ". errno says: " << strerror(errno) << std::endl;
+	}
+
+	// FIXME: use boost's asio to get notified about unbound's fd readability
+	timer = timerFactory->createTimer(1000);
+	timer->onTick.connect(boost::bind(&UnboundDomainNameResolver::processData, this));
+	timer->start();
+
 }
 
 UnboundDomainNameResolver::~UnboundDomainNameResolver() {
+	if (ubContext) {
+		ub_ctx_delete(ubContext);
+	}
+}
+
+void UnboundDomainNameResolver::unbound_callback(boost::shared_ptr<UnboundQuery> query, int err, ub_result* result) {
+	query->handleResult(err, result);
+}
+
+void UnboundDomainNameResolver::unbound_callback_wrapper(void* data, int err, ub_result* result) {
+	UnboundWrapperHelper* helper = static_cast<UnboundWrapperHelper*>(data);
+	UnboundDomainNameResolver* resolver = helper->resolver;
+	resolver->unbound_callback(helper->query, err, result);
+	delete helper;
+}
+
+void UnboundDomainNameResolver::processData() {
+	if (ub_poll(ubContext)) {
+		int ret = ub_process(ubContext);
+		if(ret != 0) {
+			SWIFT_LOG(debug) << "resolve error: " << ub_strerror(ret) << std::endl;
+		}
+	}
+	timer->start();
 }
 
 boost::shared_ptr<DomainNameServiceQuery> UnboundDomainNameResolver::createServiceQuery(const std::string& name) {
-	return boost::make_shared<UnboundDomainNameServiceQuery>(timerFactory, name);
+	return boost::make_shared<UnboundDomainNameServiceQuery>(this, ubContext, name);
 }
 
 boost::shared_ptr<DomainNameAddressQuery> UnboundDomainNameResolver::createAddressQuery(const std::string& name) {
-	return boost::make_shared<UnboundDomainNameAddressQuery>(timerFactory, name);
+	return boost::make_shared<UnboundDomainNameAddressQuery>(this, ubContext, name);
 }
 
 }
diff --git a/Swiften/Network/UnboundDomainNameResolver.h b/Swiften/Network/UnboundDomainNameResolver.h
index 37cf2f6..cd0b167 100644
--- a/Swiften/Network/UnboundDomainNameResolver.h
+++ b/Swiften/Network/UnboundDomainNameResolver.h
@@ -1,11 +1,34 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #pragma once
 
 #include <Swiften/Network/DomainNameResolver.h>
+#include <Swiften/Network/Timer.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <unbound.h>
 
 namespace Swift {
 	class EventLoop;
 	class TimerFactory;
 
+	class UnboundDomainNameResolver;
+
+	class UnboundQuery {
+		public:
+			UnboundQuery(UnboundDomainNameResolver* resolver, ub_ctx* context) : resolver(resolver), ubContext(context) {}
+			virtual ~UnboundQuery() {}
+			virtual void handleResult(int err, ub_result* result) = 0;
+		protected:
+			UnboundDomainNameResolver* resolver;
+			ub_ctx* ubContext;
+	};
+
 	class UnboundDomainNameResolver : public DomainNameResolver {
 		public:
 			UnboundDomainNameResolver(EventLoop* eventLoop, TimerFactory* timerFactory);
@@ -14,9 +37,18 @@ namespace Swift {
 			virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const std::string& name);
 			virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const std::string& name);
 
+			static void unbound_callback_wrapper(void* data, int err, ub_result* result);
+
+		private:
+			void unbound_callback(boost::shared_ptr<UnboundQuery> query, int err, ub_result* result);
+
+			void processData();
+
 		private:
 			EventLoop* eventLoop;
 			TimerFactory* timerFactory;
+			ub_ctx* ubContext;
+			Timer::ref timer;
 	};
 
 }
-- 
cgit v0.10.2-6-g49f6