util: Use default resolver behavior in util::dns::Resolver

As of this commit, we are using the default behavior of
boost::asio::ip::resolver (only return IPv4 addresses if a non-loopback
IPv4 address is configured for the system; only return IPv6 addresses if
a non-loopback IPv6 address is configured for the system).

Unit tests are now conditioned upon availability of IPv4/IPv6 and will
not fail if IPv4 or IPv6 addresses cannot be resolved.

Change-Id: I8d7fa9a205ad26a2ca85fd22d582ffe6b0a02a92
Refs: #2415
diff --git a/src/util/dns.cpp b/src/util/dns.cpp
index 8df097f..98f53d5 100644
--- a/src/util/dns.cpp
+++ b/src/util/dns.cpp
@@ -49,12 +49,7 @@
                const time::nanoseconds& timeout,
                const shared_ptr<Resolver>& self)
   {
-    BoostResolver::query query(host, NULL_PORT
-
-#if not defined(__FreeBSD__)
-                               , BoostResolver::query::all_matching
-#endif
-                               );
+    BoostResolver::query query(host, NULL_PORT);
 
     m_resolver.async_resolve(query, bind(&Resolver::onResolveSuccess, this, _1, _2, self));
 
@@ -144,11 +139,7 @@
 {
   Resolver resolver(SuccessCallback(), ErrorCallback(), addressSelector, ioService);
 
-  BoostResolver::query query(host, Resolver::NULL_PORT
-#if not defined(__FreeBSD__)
-                                 , BoostResolver::query::all_matching
-#endif
-                                 );
+  BoostResolver::query query(host, Resolver::NULL_PORT);
 
   BoostResolver::iterator remoteEndpoint = resolver.syncResolve(query);
 
diff --git a/tests/unit-tests/network-configuration-detector.cpp b/tests/unit-tests/network-configuration-detector.cpp
new file mode 100644
index 0000000..f00b3e8
--- /dev/null
+++ b/tests/unit-tests/network-configuration-detector.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "network-configuration-detector.hpp"
+
+#include <boost/asio/ip/address.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/ip/basic_resolver.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/range/iterator_range_core.hpp>
+
+namespace ndn {
+namespace tests {
+
+bool NetworkConfigurationDetector::m_isInitialized = false;
+bool NetworkConfigurationDetector::m_hasIpv4 = false;
+bool NetworkConfigurationDetector::m_hasIpv6 = false;
+
+bool
+NetworkConfigurationDetector::hasIpv4()
+{
+  if (!m_isInitialized) {
+    detect();
+  }
+  return m_hasIpv4;
+}
+
+bool
+NetworkConfigurationDetector::hasIpv6()
+{
+  if (!m_isInitialized) {
+    detect();
+  }
+  return m_hasIpv6;
+}
+
+void
+NetworkConfigurationDetector::detect()
+{
+  typedef boost::asio::ip::basic_resolver<boost::asio::ip::udp> BoostResolver;
+
+  boost::asio::io_service ioService;
+  BoostResolver resolver(ioService);
+
+  // The specified hostname must contain both A and AAAA records
+  BoostResolver::query query("a.root-servers.net", "");
+
+  boost::system::error_code errorCode;
+  BoostResolver::iterator begin = resolver.resolve(query, errorCode);
+  if (errorCode) {
+    m_isInitialized = true;
+    return;
+  }
+  BoostResolver::iterator end;
+
+  for (const auto& i : boost::make_iterator_range(begin, end)) {
+    if (i.endpoint().address().is_v4()) {
+      m_hasIpv4 = true;
+    }
+    else if (i.endpoint().address().is_v6()) {
+      m_hasIpv6 = true;
+    }
+  }
+
+  m_isInitialized = true;
+}
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit-tests/network-configuration-detector.hpp b/tests/unit-tests/network-configuration-detector.hpp
new file mode 100644
index 0000000..5619b58
--- /dev/null
+++ b/tests/unit-tests/network-configuration-detector.hpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_NETWORK_CONFIGURATION_DETECTOR_HPP
+#define NDN_TESTS_NETWORK_CONFIGURATION_DETECTOR_HPP
+
+namespace ndn {
+namespace tests {
+
+class NetworkConfigurationDetector
+{
+public:
+  static bool
+  hasIpv4();
+
+  static bool
+  hasIpv6();
+
+private:
+  static void
+  detect();
+
+private:
+  static bool m_isInitialized;
+  static bool m_hasIpv4;
+  static bool m_hasIpv6;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_NETWORK_CONFIGURATION_DETECTOR_HPP
diff --git a/tests/unit-tests/util/face-uri.cpp b/tests/unit-tests/util/face-uri.cpp
index 3ed42f5..52585b4 100644
--- a/tests/unit-tests/util/face-uri.cpp
+++ b/tests/unit-tests/util/face-uri.cpp
@@ -28,9 +28,13 @@
 #include "util/face-uri.hpp"
 
 #include "boost-test.hpp"
+#include "../network-configuration-detector.hpp"
 
 namespace ndn {
 namespace util {
+namespace tests {
+
+using ndn::tests::NetworkConfigurationDetector;
 
 BOOST_AUTO_TEST_SUITE(UtilTestFaceUri)
 
@@ -172,7 +176,7 @@
   BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "udp6://[2001:db8::1]:7777");
 }
 
-BOOST_FIXTURE_TEST_CASE(CanonizeUdp, CanonizeFixture)
+BOOST_FIXTURE_TEST_CASE(CheckCanonicalUdp, CanonizeFixture)
 {
   BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp"), true);
   BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp4"), true);
@@ -188,6 +192,14 @@
   BOOST_CHECK_EQUAL(FaceUri("udp4://example.net:6363").isCanonical(), false);
   BOOST_CHECK_EQUAL(FaceUri("udp6://example.net:6363").isCanonical(), false);
   BOOST_CHECK_EQUAL(FaceUri("udp4://224.0.23.170:56363").isCanonical(), true);
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeUdpV4, CanonizeFixture)
+{
+  if (!NetworkConfigurationDetector::hasIpv4()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv4, skipping the test case");
+    return;
+  }
 
   // IPv4 unicast
   addTest("udp4://192.0.2.1:6363", true, "udp4://192.0.2.1:6363");
@@ -204,6 +216,16 @@
   addTest("udp4://224.0.23.170", true, "udp4://224.0.23.170:56363");
   addTest("udp4://all-routers.mcast.net:56363", true, "udp4://224.0.0.2:56363");
 
+  runTests();
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeUdpV6, CanonizeFixture)
+{
+  if (!NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv6, skipping the test case");
+    return;
+  }
+
   // IPv6 unicast
   addTest("udp6://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
   addTest("udp://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
@@ -245,7 +267,7 @@
   BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "tcp6://[2001:db8::1]:7777");
 }
 
-BOOST_FIXTURE_TEST_CASE(CanonizeTcp, CanonizeFixture)
+BOOST_FIXTURE_TEST_CASE(CheckCanonicalTcp, CanonizeFixture)
 {
   BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp"), true);
   BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp4"), true);
@@ -261,6 +283,14 @@
   BOOST_CHECK_EQUAL(FaceUri("tcp4://example.net:6363").isCanonical(), false);
   BOOST_CHECK_EQUAL(FaceUri("tcp6://example.net:6363").isCanonical(), false);
   BOOST_CHECK_EQUAL(FaceUri("tcp4://224.0.23.170:56363").isCanonical(), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeTcpV4, CanonizeFixture)
+{
+  if (!NetworkConfigurationDetector::hasIpv4()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv4, skipping the test case");
+    return;
+  }
 
   // IPv4 unicast
   addTest("tcp4://192.0.2.1:6363", true, "tcp4://192.0.2.1:6363");
@@ -277,6 +307,16 @@
   addTest("tcp4://224.0.23.170", false, "");
   addTest("tcp4://all-routers.mcast.net:56363", false, "");
 
+  runTests();
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeTcpV6, CanonizeFixture)
+{
+  if (!NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv6, skipping the test case");
+    return;
+  }
+
   // IPv6 unicast
   addTest("tcp6://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
   addTest("tcp://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
@@ -441,5 +481,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // namespace tests
 } // namespace util
 } // namespace ndn
diff --git a/tests/unit-tests/util/test-dns.cpp b/tests/unit-tests/util/test-dns.cpp
index 6eeb6e0..23bce2d 100644
--- a/tests/unit-tests/util/test-dns.cpp
+++ b/tests/unit-tests/util/test-dns.cpp
@@ -22,13 +22,18 @@
 #include "util/dns.hpp"
 
 #include "boost-test.hpp"
+#include "../network-configuration-detector.hpp"
 #include <boost/lexical_cast.hpp>
 
 namespace ndn {
+namespace util {
+namespace tests {
 
 using boost::asio::ip::address_v4;
 using boost::asio::ip::address_v6;
 
+using ndn::tests::NetworkConfigurationDetector;
+
 class DnsFixture
 {
 public:
@@ -88,6 +93,74 @@
 
 BOOST_AUTO_TEST_CASE(Asynchronous)
 {
+  if (!NetworkConfigurationDetector::hasIpv4() && !NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support both IPv4 and IPv6, skipping test case");
+    return;
+  }
+
+  dns::asyncResolve("nothost.nothost.nothost.arpa",
+                    bind(&DnsFixture::onSuccess, this, _1,
+                         dns::IpAddress(address_v4()), false, false),
+                    bind(&DnsFixture::onFailure, this, true),
+                    m_ioService); // should fail
+  m_ioService.run();
+
+  BOOST_CHECK_EQUAL(m_nFailures, 1);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 0);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV4)
+{
+  if (!NetworkConfigurationDetector::hasIpv4()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv4, skipping the test case");
+    return;
+  }
+
+  dns::asyncResolve("192.0.2.1",
+                    bind(&DnsFixture::onSuccess, this, _1,
+                         dns::IpAddress(address_v4::from_string("192.0.2.1")),
+                         true, true),
+                    bind(&DnsFixture::onFailure, this, false),
+                    m_ioService);
+  m_ioService.run();
+
+  BOOST_CHECK_EQUAL(m_nFailures, 0);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 1);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV6)
+{
+  if (!NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support IPv6, skipping the test case");
+    return;
+  }
+
+  dns::asyncResolve("ipv6.google.com", // only IPv6 address should be available
+                    bind(&DnsFixture::onSuccess, this, _1,
+                         dns::IpAddress(address_v6()), true, false),
+                    bind(&DnsFixture::onFailure, this, false),
+                    m_ioService);
+
+  dns::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3",
+                    bind(&DnsFixture::onSuccess, this, _1,
+                         dns::IpAddress(address_v6::
+                                      from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3")),
+                         true, true),
+                    bind(&DnsFixture::onFailure, this, false),
+                    m_ioService);
+  m_ioService.run();
+
+  BOOST_CHECK_EQUAL(m_nFailures, 0);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 2);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV4AndV6)
+{
+  if (!NetworkConfigurationDetector::hasIpv4() || !NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support either IPv4 or IPv6, skipping test case");
+    return;
+  }
+
   dns::asyncResolve("www.named-data.net",
                     bind(&DnsFixture::onSuccess, this, _1,
                          dns::IpAddress(address_v4()), true, false),
@@ -95,20 +168,14 @@
                     m_ioService,
                     dns::Ipv4Only());
 
-  dns::asyncResolve("nothost.nothost.nothost.arpa",
-                    bind(&DnsFixture::onSuccess, this, _1,
-                         dns::IpAddress(address_v4()), false, false),
-                    bind(&DnsFixture::onFailure, this, true),
-                    m_ioService); // should fail
-
-  dns::asyncResolve("www.google.com",
+  dns::asyncResolve("a.root-servers.net",
                     bind(&DnsFixture::onSuccess, this, _1,
                          dns::IpAddress(address_v4()), true, false),
                     bind(&DnsFixture::onFailure, this, false),
                     m_ioService,
                     dns::Ipv4Only()); // request IPv4 address
 
-  dns::asyncResolve("www.google.com",
+  dns::asyncResolve("a.root-servers.net",
                     bind(&DnsFixture::onSuccess, this, _1,
                          dns::IpAddress(address_v6()), true, false),
                     bind(&DnsFixture::onFailure, this, false),
@@ -119,12 +186,6 @@
                     bind(&DnsFixture::onSuccess, this, _1,
                          dns::IpAddress(address_v6()), true, false),
                     bind(&DnsFixture::onFailure, this, false),
-                    m_ioService);
-
-  dns::asyncResolve("ipv6.google.com", // only IPv6 address should be available
-                    bind(&DnsFixture::onSuccess, this, _1,
-                         dns::IpAddress(address_v6()), true, false),
-                    bind(&DnsFixture::onFailure, this, false),
                     m_ioService,
                     dns::Ipv6Only());
 
@@ -134,30 +195,18 @@
                     bind(&DnsFixture::onFailure, this, true), // should fail
                     m_ioService,
                     dns::Ipv4Only());
-
-  dns::asyncResolve("192.0.2.1",
-                    bind(&DnsFixture::onSuccess, this, _1,
-                         dns::IpAddress(address_v4::from_string("192.0.2.1")),
-                         true, true),
-                    bind(&DnsFixture::onFailure, this, false),
-                    m_ioService);
-
-  dns::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3",
-                    bind(&DnsFixture::onSuccess, this, _1,
-                         dns::IpAddress(address_v6::
-                                      from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3")),
-                         true, true),
-                    bind(&DnsFixture::onFailure, this, false),
-                    m_ioService);
-
   m_ioService.run();
 
-  BOOST_CHECK_EQUAL(m_nFailures, 2);
-  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
+  BOOST_CHECK_EQUAL(m_nFailures, 1);
+  BOOST_CHECK_EQUAL(m_nSuccesses, 4);
 }
 
 BOOST_AUTO_TEST_CASE(Synchronous)
 {
+  if (!NetworkConfigurationDetector::hasIpv4() && !NetworkConfigurationDetector::hasIpv6()) {
+    BOOST_TEST_MESSAGE("Platform does not support both IPv4 and IPv6, skipping test case");
+    return;
+  }
   dns::IpAddress address;
   BOOST_CHECK_NO_THROW(address = dns::syncResolve("www.named-data.net", m_ioService));
 
@@ -166,4 +215,6 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+} // namespace tests
+} // namespace util
 } // namespace ndn