util: refactor and simplify ndn::dns

Change-Id: I356c37a56ec9241743c1ecc74e2c96ffea061c85
Refs: #2653
diff --git a/src/util/dns.cpp b/src/util/dns.cpp
index 7c992ff..0aebc3a 100644
--- a/src/util/dns.cpp
+++ b/src/util/dns.cpp
@@ -26,132 +26,128 @@
 namespace ndn {
 namespace dns {
 
-typedef boost::asio::ip::udp::endpoint EndPoint;
-typedef boost::asio::ip::basic_resolver<boost::asio::ip::udp> BoostResolver;
-
 class Resolver : noncopyable
 {
 public:
-  Resolver(const SuccessCallback& onSuccess,
-           const ErrorCallback& onError,
-           const ndn::dns::AddressSelector& addressSelector,
-           boost::asio::io_service& ioService)
+  typedef boost::asio::ip::udp protocol;
+  typedef protocol::resolver::iterator iterator;
+  typedef protocol::resolver::query query;
+
+public:
+  Resolver(boost::asio::io_service& ioService,
+           const AddressSelector& addressSelector)
     : m_resolver(ioService)
     , m_addressSelector(addressSelector)
-    , m_onSuccess(onSuccess)
-    , m_onError(onError)
     , m_scheduler(ioService)
   {
+    BOOST_ASSERT(m_addressSelector != nullptr);
   }
 
   void
-  asyncResolve(const std::string& host,
-               const time::nanoseconds& timeout,
+  asyncResolve(const query& q,
+               const SuccessCallback& onSuccess,
+               const ErrorCallback& onError,
+               time::nanoseconds timeout,
                const shared_ptr<Resolver>& self)
   {
-    BoostResolver::query query(host, NULL_PORT);
+    m_onSuccess = onSuccess;
+    m_onError = onError;
 
-    m_resolver.async_resolve(query, bind(&Resolver::onResolveSuccess, this, _1, _2, self));
+    m_resolver.async_resolve(q, bind(&Resolver::onResolveResult, this, _1, _2, self));
 
-    m_resolveTimeout = m_scheduler.scheduleEvent(timeout,
-                                                 bind(&Resolver::onResolveError, this,
-                                                 "Timeout", self));
+    m_resolveTimeout = m_scheduler.scheduleEvent(timeout, bind(&Resolver::onResolveTimeout, this, self));
   }
 
-  BoostResolver::iterator
-  syncResolve(BoostResolver::query query)
+  iterator
+  syncResolve(const query& q)
   {
-    return m_resolver.resolve(query);
+    return selectAddress(m_resolver.resolve(q));
   }
 
+private:
   void
-  onResolveSuccess(const boost::system::error_code& error,
-                   BoostResolver::iterator remoteEndpoint,
-                   const shared_ptr<Resolver>& self)
+  onResolveResult(const boost::system::error_code& error,
+                  iterator it, const shared_ptr<Resolver>& self)
   {
     m_scheduler.cancelEvent(m_resolveTimeout);
 
-    if (error)
-      {
-        if (error == boost::system::errc::operation_canceled)
-          {
-            return;
-          }
+    if (error) {
+      if (error == boost::asio::error::operation_aborted)
+        return;
 
-        return m_onError("Remote endpoint hostname or port cannot be resolved: " +
-                         error.category().message(error.value()));
-      }
+      if (m_onError)
+        m_onError("Hostname cannot be resolved: " + error.message());
 
-    BoostResolver::iterator end;
-    for (; remoteEndpoint != end; ++remoteEndpoint)
-      {
-        IpAddress address(EndPoint(*remoteEndpoint).address());
+      return;
+    }
 
-        if (m_addressSelector(address))
-          {
-            return m_onSuccess(address);
-          }
-      }
+    it = selectAddress(it);
 
-    m_onError("No endpoint matching the specified address selector found");
+    if (it != iterator() && m_onSuccess) {
+      m_onSuccess(it->endpoint().address());
+    }
+    else if (m_onError) {
+      m_onError("No endpoints match the specified address selector");
+    }
   }
 
   void
-  onResolveError(const std::string& errorInfo, const shared_ptr<Resolver>& self)
+  onResolveTimeout(const shared_ptr<Resolver>& self)
   {
     m_resolver.cancel();
-    m_onError(errorInfo);
+
+    if (m_onError)
+      m_onError("Hostname resolution timed out");
   }
 
-public:
-  static const std::string NULL_PORT;
+  iterator
+  selectAddress(iterator it) const
+  {
+    while (it != iterator() &&
+           !m_addressSelector(it->endpoint().address())) {
+      ++it;
+    }
+
+    return it;
+  }
 
 private:
-  BoostResolver m_resolver;
-  EventId m_resolveTimeout;
+  protocol::resolver m_resolver;
 
-  ndn::dns::AddressSelector m_addressSelector;
+  AddressSelector m_addressSelector;
   SuccessCallback m_onSuccess;
   ErrorCallback m_onError;
 
-  Scheduler m_scheduler;
+  util::scheduler::Scheduler m_scheduler;
+  util::scheduler::EventId m_resolveTimeout;
 };
 
-const std::string Resolver::NULL_PORT = "";
-
 void
 asyncResolve(const std::string& host,
              const SuccessCallback& onSuccess,
              const ErrorCallback& onError,
              boost::asio::io_service& ioService,
-             const ndn::dns::AddressSelector& addressSelector,
-             const time::nanoseconds& timeout)
+             const AddressSelector& addressSelector,
+             time::nanoseconds timeout)
 {
-  shared_ptr<Resolver> resolver = make_shared<Resolver>(onSuccess, onError,
-                                                        addressSelector, ndn::ref(ioService));
-  resolver->asyncResolve(host, timeout, resolver);
-  // resolver will be destroyed when async operation finishes or global IO service stops
+  auto resolver = make_shared<Resolver>(ref(ioService), addressSelector);
+  resolver->asyncResolve(Resolver::query(host, ""), onSuccess, onError, timeout, resolver);
+  // resolver will be destroyed when async operation finishes or ioService stops
 }
 
 IpAddress
-syncResolve(const std::string& host, boost::asio::io_service& ioService,
-            const ndn::dns::AddressSelector& addressSelector)
+syncResolve(const std::string& host,
+            boost::asio::io_service& ioService,
+            const AddressSelector& addressSelector)
 {
-  Resolver resolver(SuccessCallback(), ErrorCallback(), addressSelector, ioService);
+  Resolver resolver(ioService, addressSelector);
+  auto it = resolver.syncResolve(Resolver::query(host, ""));
 
-  BoostResolver::query query(host, Resolver::NULL_PORT);
+  if (it == Resolver::iterator()) {
+    BOOST_THROW_EXCEPTION(Error("No endpoints match the specified address selector"));
+  }
 
-  BoostResolver::iterator remoteEndpoint = resolver.syncResolve(query);
-
-  BoostResolver::iterator end;
-  for (; remoteEndpoint != end; ++remoteEndpoint)
-    {
-      if (addressSelector(EndPoint(*remoteEndpoint).address()))
-        {
-          return EndPoint(*remoteEndpoint).address();
-        }
-    }
-  BOOST_THROW_EXCEPTION(Error("No endpoint matching the specified address selector found"));
+  return it->endpoint().address();
 }
 
 } // namespace dns
diff --git a/src/util/dns.hpp b/src/util/dns.hpp
index 7f00898..64d3865 100644
--- a/src/util/dns.hpp
+++ b/src/util/dns.hpp
@@ -19,22 +19,24 @@
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
  */
 
-#ifndef NDN_UTIL_DNS_H
-#define NDN_UTIL_DNS_H
+#ifndef NDN_UTIL_DNS_HPP
+#define NDN_UTIL_DNS_HPP
 
 #include "../util/time.hpp"
+
 #include <boost/asio/ip/address.hpp>
 #include <boost/asio/io_service.hpp>
 
 namespace ndn {
 namespace dns {
 
-typedef function<bool (const boost::asio::ip::address& address)> AddressSelector;
+typedef boost::asio::ip::address IpAddress;
+typedef function<bool (const IpAddress& address)> AddressSelector;
 
 struct AnyAddress
 {
   bool
-  operator()(const boost::asio::ip::address& address)
+  operator()(const IpAddress& address) const
   {
     return true;
   }
@@ -43,7 +45,7 @@
 struct Ipv4Only
 {
   bool
-  operator()(const boost::asio::ip::address& address)
+  operator()(const IpAddress& address) const
   {
     return address.is_v4();
   }
@@ -52,7 +54,7 @@
 struct Ipv6Only
 {
   bool
-  operator()(const boost::asio::ip::address& address)
+  operator()(const IpAddress& address) const
   {
     return address.is_v6();
   }
@@ -60,14 +62,13 @@
 
 struct Error : public std::runtime_error
 {
+  explicit
   Error(const std::string& what)
     : std::runtime_error(what)
   {
   }
 };
 
-typedef boost::asio::ip::address IpAddress;
-
 typedef function<void (const IpAddress& address)> SuccessCallback;
 typedef function<void (const std::string& reason)> ErrorCallback;
 
@@ -78,9 +79,9 @@
  *
  * Available address selector predicates:
  *
- * - resolver::AnyAddress()
- * - resolver::Ipv4Address()
- * - resolver::Ipv6Address()
+ * - dns::AnyAddress()
+ * - dns::Ipv4Address()
+ * - dns::Ipv6Address()
  *
  * \warning Even after the DNS resolution has timed out, it's possible that
  *          \p ioService keeps running and \p onSuccess is invoked at a later time.
@@ -92,8 +93,8 @@
              const SuccessCallback& onSuccess,
              const ErrorCallback& onError,
              boost::asio::io_service& ioService,
-             const ndn::dns::AddressSelector& addressSelector = ndn::dns::AnyAddress(),
-             const time::nanoseconds& timeout = time::seconds(4));
+             const AddressSelector& addressSelector = AnyAddress(),
+             time::nanoseconds timeout = time::seconds(4));
 
 /** \brief Synchronously resolve host
  *
@@ -102,16 +103,16 @@
  *
  * Available address selector predicates:
  *
- * - resolver::AnyAddress()
- * - resolver::Ipv4Address()
- * - resolver::Ipv6Address()
+ * - dns::AnyAddress()
+ * - dns::Ipv4Address()
+ * - dns::Ipv6Address()
  */
 IpAddress
 syncResolve(const std::string& host,
             boost::asio::io_service& ioService,
-            const ndn::dns::AddressSelector& addressSelector = ndn::dns::AnyAddress());
+            const AddressSelector& addressSelector = AnyAddress());
 
 } // namespace dns
 } // namespace ndn
 
-#endif // NDN_UTIL_DNS_H
+#endif // NDN_UTIL_DNS_HPP