core: use ndn::dns from ndn-cxx

refs #2207

Change-Id: Ice97c9203283b5b89982049cb29c0ccfed0ef422
diff --git a/core/resolver.hpp b/core/resolver.hpp
deleted file mode 100644
index d144645..0000000
--- a/core/resolver.hpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NFD_CORE_RESOLVER_H
-#define NFD_CORE_RESOLVER_H
-
-#include "common.hpp"
-#include "core/global-io.hpp"
-#include "core/scheduler.hpp"
-
-namespace nfd {
-namespace resolver {
-
-typedef function<bool (const boost::asio::ip::address& address)> AddressSelector;
-
-struct AnyAddress {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return true;
-  }
-};
-
-struct Ipv4Address {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return address.is_v4();
-  }
-};
-
-struct Ipv6Address {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return address.is_v6();
-  }
-};
-
-} // namespace resolver
-
-template<class Protocol>
-class Resolver
-{
-public:
-  struct Error : public std::runtime_error
-  {
-    Error(const std::string& what) : std::runtime_error(what) {}
-  };
-
-  typedef function<void (const typename Protocol::endpoint& endpoint)> SuccessCallback;
-  typedef function<void (const std::string& reason)> ErrorCallback;
-
-  typedef boost::asio::ip::basic_resolver< Protocol > resolver;
-
-  /** \brief Asynchronously resolve host and port
-   *
-   * If an address selector predicate is specified, then each resolved IP address
-   * is checked against the predicate.
-   *
-   * Available address selector predicates:
-   *
-   * - resolver::AnyAddress()
-   * - resolver::Ipv4Address()
-   * - resolver::Ipv6Address()
-   */
-  static void
-  asyncResolve(const std::string& host, const std::string& port,
-               const SuccessCallback& onSuccess,
-               const ErrorCallback& onError,
-               const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress(),
-               const time::seconds& timeout = time::seconds(4))
-  {
-    shared_ptr<Resolver> resolver =
-      shared_ptr<Resolver>(new Resolver(onSuccess, onError,
-                                        addressSelector));
-
-    resolver->asyncResolve(host, port, timeout, resolver);
-    // resolver will be destroyed when async operation finishes or global IO service stops
-  }
-
-  /** \brief Synchronously resolve host and port
-   *
-   * If an address selector predicate is specified, then each resolved IP address
-   * is checked against the predicate.
-   *
-   * Available address selector predicates:
-   *
-   * - resolver::AnyAddress()
-   * - resolver::Ipv4Address()
-   * - resolver::Ipv6Address()
-   */
-  static typename Protocol::endpoint
-  syncResolve(const std::string& host, const std::string& port,
-              const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress())
-  {
-    Resolver resolver(SuccessCallback(), ErrorCallback(), addressSelector);
-
-    typename resolver::query query(host, port
-#if not defined(__FreeBSD__)
-                                   , resolver::query::all_matching
-#endif
-                                   );
-
-    typename resolver::iterator remoteEndpoint = resolver.m_resolver.resolve(query);
-    typename resolver::iterator end;
-    for (; remoteEndpoint != end; ++remoteEndpoint)
-      {
-        if (addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
-          return *remoteEndpoint;
-      }
-    throw Error("No endpoint matching the specified address selector found");
-  }
-
-private:
-  Resolver(const SuccessCallback& onSuccess,
-           const ErrorCallback& onError,
-           const nfd::resolver::AddressSelector& addressSelector)
-    : m_resolver(getGlobalIoService())
-    , m_addressSelector(addressSelector)
-    , m_onSuccess(onSuccess)
-    , m_onError(onError)
-  {
-  }
-
-  void
-  asyncResolve(const std::string& host, const std::string& port,
-               const time::seconds& timeout,
-               const shared_ptr<Resolver>& self)
-  {
-    typename resolver::query query(host, port
-#if not defined(__FreeBSD__)
-                                   , resolver::query::all_matching
-#endif
-                                   );
-
-    m_resolver.async_resolve(query,
-                             bind(&Resolver::onResolveSuccess, this, _1, _2, self));
-
-    m_resolveTimeout = scheduler::schedule(timeout,
-                                           bind(&Resolver::onResolveError, this,
-                                                "Timeout", self));
-  }
-
-  void
-  onResolveSuccess(const boost::system::error_code& error,
-                   typename resolver::iterator remoteEndpoint,
-                   const shared_ptr<Resolver>& self)
-  {
-    scheduler::cancel(m_resolveTimeout);
-
-    if (error)
-      {
-        if (error == boost::system::errc::operation_canceled)
-          return;
-
-        return m_onError("Remote endpoint hostname or port cannot be resolved: " +
-                         error.category().message(error.value()));
-      }
-
-    typename resolver::iterator end;
-    for (; remoteEndpoint != end; ++remoteEndpoint)
-      {
-        if (m_addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
-          return m_onSuccess(*remoteEndpoint);
-      }
-
-    m_onError("No endpoint matching the specified address selector found");
-  }
-
-  void
-  onResolveError(const std::string& errorInfo,
-                 const shared_ptr<Resolver>& self)
-  {
-    m_resolver.cancel();
-    m_onError(errorInfo);
-  }
-
-private:
-  resolver m_resolver;
-  scheduler::EventId m_resolveTimeout;
-
-  nfd::resolver::AddressSelector m_addressSelector;
-  SuccessCallback m_onSuccess;
-  ErrorCallback m_onError;
-};
-
-typedef Resolver<boost::asio::ip::tcp> TcpResolver;
-typedef Resolver<boost::asio::ip::udp> UdpResolver;
-
-} // namespace nfd
-
-#endif // NFD_CORE_RESOLVER_H
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 598a2c4..0a152b1 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,9 +24,10 @@
  */
 
 #include "tcp-factory.hpp"
-#include "core/resolver.hpp"
 #include "core/logger.hpp"
 #include "core/network-interface.hpp"
+#include "core/global-io.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 NFD_LOG_INIT("TcpFactory");
 
@@ -110,7 +111,9 @@
 shared_ptr<TcpChannel>
 TcpFactory::createChannel(const std::string& localHost, const std::string& localPort)
 {
-  return createChannel(TcpResolver::syncResolve(localHost, localPort));
+  tcp::Endpoint endpoint(ndn::dns::syncResolve(localHost, getGlobalIoService()),
+                         boost::lexical_cast<uint16_t>(localPort));
+  return createChannel(endpoint);
 }
 
 shared_ptr<TcpChannel>
@@ -128,30 +131,10 @@
                        const FaceCreatedCallback& onCreated,
                        const FaceConnectFailedCallback& onConnectFailed)
 {
-  resolver::AddressSelector addressSelector = resolver::AnyAddress();
-  if (uri.getScheme() == "tcp4")
-    addressSelector = resolver::Ipv4Address();
-  else if (uri.getScheme() == "tcp6")
-    addressSelector = resolver::Ipv6Address();
+  BOOST_ASSERT(uri.isCanonical());
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
+  tcp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
 
-  if (!uri.getPath().empty() && uri.getPath() != "/")
-    {
-      onConnectFailed("Invalid URI");
-    }
-
-  TcpResolver::asyncResolve(uri.getHost(),
-                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
-                            bind(&TcpFactory::continueCreateFaceAfterResolve, this, _1,
-                                 onCreated, onConnectFailed),
-                            onConnectFailed,
-                            addressSelector);
-}
-
-void
-TcpFactory::continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
-                                           const FaceCreatedCallback& onCreated,
-                                           const FaceConnectFailedCallback& onConnectFailed)
-{
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end())
     {
       onConnectFailed("Requested endpoint is prohibited "
@@ -160,7 +143,6 @@
     }
 
   // very simple logic for now
-
   for (ChannelMap::iterator channel = m_channels.begin();
        channel != m_channels.end();
        ++channel)
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index e2ebda5..e50bf66 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -29,6 +29,7 @@
 #include "protocol-factory.hpp"
 #include "tcp-channel.hpp"
 
+
 namespace nfd {
 
 class TcpFactory : public ProtocolFactory
@@ -110,11 +111,6 @@
   shared_ptr<TcpChannel>
   findChannel(const tcp::Endpoint& localEndpoint);
 
-  void
-  continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
-                                 const FaceCreatedCallback& onCreated,
-                                 const FaceConnectFailedCallback& onConnectFailed);
-
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   typedef std::map< tcp::Endpoint, shared_ptr<TcpChannel> > ChannelMap;
   ChannelMap m_channels;
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 74064c3..4ef4e17 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -25,8 +25,8 @@
 
 #include "udp-factory.hpp"
 #include "core/global-io.hpp"
-#include "core/resolver.hpp"
 #include "core/network-interface.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 #if defined(__linux__)
 #include <sys/socket.h>
@@ -140,7 +140,9 @@
                           const std::string& localPort,
                           const time::seconds& timeout)
 {
-  return createChannel(UdpResolver::syncResolve(localHost, localPort), timeout);
+  udp::Endpoint endPoint(ndn::dns::syncResolve(localHost, getGlobalIoService()),
+                         boost::lexical_cast<uint16_t>(localPort));
+  return createChannel(endPoint, timeout);
 }
 
 shared_ptr<MulticastUdpFace>
@@ -251,12 +253,13 @@
                                 const std::string& multicastPort,
                                 const std::string& networkInterfaceName /* "" */)
 {
+  udp::Endpoint localEndpoint(ndn::dns::syncResolve(localIp, getGlobalIoService()),
+                              boost::lexical_cast<uint16_t>(multicastPort));
 
-  return createMulticastFace(UdpResolver::syncResolve(localIp,
-                                                      multicastPort),
-                             UdpResolver::syncResolve(multicastIp,
-                                                      multicastPort),
-                             networkInterfaceName);
+  udp::Endpoint multicastEndpoint(ndn::dns::syncResolve(multicastIp, getGlobalIoService()),
+                                  boost::lexical_cast<uint16_t>(multicastPort));
+
+  return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
 }
 
 void
@@ -264,31 +267,10 @@
                        const FaceCreatedCallback& onCreated,
                        const FaceConnectFailedCallback& onConnectFailed)
 {
-  resolver::AddressSelector addressSelector = resolver::AnyAddress();
-  if (uri.getScheme() == "udp4")
-    addressSelector = resolver::Ipv4Address();
-  else if (uri.getScheme() == "udp6")
-    addressSelector = resolver::Ipv6Address();
+  BOOST_ASSERT(uri.isCanonical());
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
+  udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
 
-  if (!uri.getPath().empty() && uri.getPath() != "/")
-    {
-      onConnectFailed("Invalid URI");
-    }
-
-  UdpResolver::asyncResolve(uri.getHost(),
-                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
-                            bind(&UdpFactory::continueCreateFaceAfterResolve, this, _1,
-                                 onCreated, onConnectFailed),
-                            onConnectFailed,
-                            addressSelector);
-
-}
-
-void
-UdpFactory::continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
-                                           const FaceCreatedCallback& onCreated,
-                                           const FaceConnectFailedCallback& onConnectFailed)
-{
   if (endpoint.address().is_multicast()) {
     onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
     return;
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 1f7f7de..db4760b 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -197,11 +197,6 @@
   shared_ptr<MulticastUdpFace>
   findMulticastFace(const udp::Endpoint& localEndpoint);
 
-  void
-  continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
-                                 const FaceCreatedCallback& onCreated,
-                                 const FaceConnectFailedCallback& onConnectFailed);
-
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   typedef std::map< udp::Endpoint, shared_ptr<UdpChannel> > ChannelMap;
 
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index 30f3afd..49f28b0 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,7 +24,7 @@
  */
 
 #include "websocket-factory.hpp"
-#include "core/resolver.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 namespace nfd {
 
@@ -53,7 +53,8 @@
 shared_ptr<WebSocketChannel>
 WebSocketFactory::createChannel(const std::string& host, const std::string& port)
 {
-  ip::tcp::endpoint tcpEndpoint = TcpResolver::syncResolve(host, port);
+  ip::tcp::endpoint tcpEndpoint(ndn::dns::syncResolve(host, getGlobalIoService()),
+                                boost::lexical_cast<uint16_t>(port));
   websocket::Endpoint endpoint(tcpEndpoint.address(), tcpEndpoint.port());
   return createChannel(endpoint);
 }
diff --git a/tests/core/resolver.cpp b/tests/core/resolver.cpp
deleted file mode 100644
index 18d35b7..0000000
--- a/tests/core/resolver.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "core/resolver.hpp"
-#include "core/logger.hpp"
-
-#include "tests/test-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-NFD_LOG_INIT("tests.CoreResolver");
-
-using boost::asio::ip::address_v4;
-using boost::asio::ip::address_v6;
-using boost::asio::ip::tcp;
-using boost::asio::ip::udp;
-
-BOOST_FIXTURE_TEST_SUITE(CoreResolver, BaseFixture)
-
-template<class Protocol>
-class ResolverFixture : protected BaseFixture
-{
-public:
-  ResolverFixture()
-    : m_nFailures(0)
-    , m_nSuccesses(0)
-  {
-  }
-
-  void
-  onSuccess(const typename Protocol::endpoint& resolvedEndpoint,
-            const typename Protocol::endpoint& expectedEndpoint,
-            bool isValid, bool wantCheckAddress = false)
-  {
-    NFD_LOG_DEBUG("Resolved to: " << resolvedEndpoint);
-    ++m_nSuccesses;
-
-    if (!isValid)
-      {
-        BOOST_FAIL("Resolved to " + boost::lexical_cast<std::string>(resolvedEndpoint)
-                   + ", but it should have failed");
-      }
-
-    BOOST_CHECK_EQUAL(resolvedEndpoint.port(), expectedEndpoint.port());
-
-    BOOST_CHECK_EQUAL(resolvedEndpoint.address().is_v4(), expectedEndpoint.address().is_v4());
-
-    // checking address is not deterministic and should be enabled only
-    // if only one IP address will be returned by resolution
-    if (wantCheckAddress)
-      {
-        BOOST_CHECK_EQUAL(resolvedEndpoint.address(), expectedEndpoint.address());
-      }
-  }
-
-  void
-  onFailure(bool isValid)
-  {
-    ++m_nFailures;
-
-    if (!isValid)
-      BOOST_FAIL("Resolution should not have failed");
-
-    BOOST_CHECK_MESSAGE(true, "Resolution failed as expected");
-  }
-
-
-  uint32_t m_nFailures;
-  uint32_t m_nSuccesses;
-};
-
-BOOST_FIXTURE_TEST_CASE(Tcp, ResolverFixture<tcp>)
-{
-  TcpResolver::asyncResolve("www.named-data.net", "6363",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 6363), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("www.named-data.net", "notport",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 0), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
-
-
-  TcpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 6363), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
-
-  TcpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv4Address()); // request IPv4 address
-
-  TcpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv6Address()); // request IPv6 address
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv6Address());
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true), // should fail
-                            resolver::Ipv4Address());
-
-  TcpResolver::asyncResolve("192.0.2.1", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6::
-                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
-                                               80), true, true),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  g_io.run();
-
-  BOOST_CHECK_EQUAL(m_nFailures, 3);
-  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
-}
-
-BOOST_AUTO_TEST_CASE(SyncTcp)
-{
-  tcp::endpoint endpoint;
-  BOOST_CHECK_NO_THROW(endpoint = TcpResolver::syncResolve("www.named-data.net", "6363"));
-  NFD_LOG_DEBUG("Resolved to: " << endpoint);
-  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
-  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
-}
-
-BOOST_FIXTURE_TEST_CASE(Udp, ResolverFixture<udp>)
-{
-  UdpResolver::asyncResolve("www.named-data.net", "6363",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 6363), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("www.named-data.net", "notport",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 0), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
-
-
-  UdpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 6363), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
-
-  UdpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv4Address()); // request IPv4 address
-
-  UdpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv6Address()); // request IPv6 address
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv6Address());
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true), // should fail
-                            resolver::Ipv4Address());
-
-  UdpResolver::asyncResolve("192.0.2.1", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6::
-                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
-                                               80), true, true),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  g_io.run();
-
-  BOOST_CHECK_EQUAL(m_nFailures, 3);
-  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
-}
-
-BOOST_AUTO_TEST_CASE(SyncUdp)
-{
-  udp::endpoint endpoint;
-  BOOST_CHECK_NO_THROW(endpoint = UdpResolver::syncResolve("www.named-data.net", "6363"));
-  NFD_LOG_DEBUG("Resolved to: " << endpoint);
-  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
-  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
-}
-
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/face/face-history.hpp b/tests/daemon/face/face-history.hpp
new file mode 100644
index 0000000..263a23a
--- /dev/null
+++ b/tests/daemon/face/face-history.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
+#define NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
+
+#include "face/face.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \brief captures signals from Face
+ */
+class FaceHistory : noncopyable
+{
+public:
+  explicit
+  FaceHistory(Face& face)
+    : m_limitedIo(nullptr)
+  {
+    this->construct(face);
+  }
+
+  FaceHistory(Face& face, LimitedIo& limitedIo)
+    : m_limitedIo(&limitedIo)
+  {
+    this->construct(face);
+  }
+
+private:
+  void
+  construct(Face& face)
+  {
+    m_receiveInterestConn = face.onReceiveInterest.connect([this] (const Interest& interest) {
+      this->receivedInterests.push_back(interest);
+      this->afterOp();
+    });
+    m_receiveDataConn = face.onReceiveData.connect([this] (const Data& data) {
+      this->receivedData.push_back(data);
+      this->afterOp();
+    });
+    m_failConn = face.onFail.connect([this] (const std::string& reason) {
+      this->failures.push_back(reason);
+      this->afterOp();
+    });
+  }
+
+  void
+  afterOp()
+  {
+    if (m_limitedIo != nullptr) {
+      m_limitedIo->afterOp();
+    }
+  }
+
+public:
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedData;
+  std::vector<std::string> failures;
+
+private:
+  LimitedIo* m_limitedIo;
+  signal::ScopedConnection m_receiveInterestConn;
+  signal::ScopedConnection m_receiveDataConn;
+  signal::ScopedConnection m_failConn;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
diff --git a/tests/daemon/face/tcp.cpp b/tests/daemon/face/tcp.cpp
index 51c4ae1..23899e6 100644
--- a/tests/daemon/face/tcp.cpp
+++ b/tests/daemon/face/tcp.cpp
@@ -24,7 +24,7 @@
  */
 
 #include "face/tcp-factory.hpp"
-#include "core/resolver.hpp"
+#include <ndn-cxx/util/dns.hpp>
 #include "core/network-interface.hpp"
 #include <ndn-cxx/security/key-chain.hpp>
 
@@ -98,17 +98,16 @@
 {
   TcpFactory factory = TcpFactory();
 
-  factory.createFace(FaceUri("tcp4://127.0.0.1"),
+  factory.createFace(FaceUri("tcp4://127.0.0.1:6363"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::checkError, this, _1,
+                          "No channels available to connect to 127.0.0.1:6363"));
+
+  factory.createChannel("127.0.0.1", "20071");
+
+  factory.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                      bind(&FaceCreateFixture::ignore, this),
                      bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("tcp4://127.0.0.1/"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("tcp4://127.0.0.1/path"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
 }
 
 class EndToEndFixture : protected BaseFixture
@@ -263,7 +262,7 @@
   shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
   factory2.createChannel("127.0.0.2", "20071");
 
-  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+  factory2.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
@@ -348,7 +347,7 @@
 
   factory2.createChannel("::2", "20070");
 
-  factory2.createFace(FaceUri("tcp://[::1]:20070"),
+  factory2.createFace(FaceUri("tcp6://[::1]:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
@@ -550,7 +549,8 @@
   BOOST_REQUIRE_EQUAL(channel->isListening(), true);
 
   DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
-  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve("127.0.0.1", "20070"));
+  tcp::Endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 20070);
+  sender.start(endpoint);
 
   BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
                                     time::seconds(1)) == LimitedIo::EXCEED_TIME,
@@ -582,7 +582,8 @@
   BOOST_REQUIRE_EQUAL(channel->isListening(), true);
 
   DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
-  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve(someIpv4Address, "20070"));
+  tcp::Endpoint endpoint(ndn::dns::syncResolve(someIpv4Address, getGlobalIoService()), 20070);
+  sender.start(endpoint);
 
   BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
                                     time::seconds(1)) == LimitedIo::EXCEED_TIME,
@@ -621,7 +622,7 @@
   TcpFactory factory;
   shared_ptr<TcpChannel> channel = factory.createChannel("0.0.0.0", "20070");
 
-  factory.createFace(FaceUri("tcp://192.0.2.1:20070"),
+  factory.createFace(FaceUri("tcp4://192.0.2.1:20070"),
                      bind(&FaceCreateTimeoutFixture::onFaceCreated, this, _1),
                      bind(&FaceCreateTimeoutFixture::onConnectFailed, this, _1));
 
@@ -650,7 +651,7 @@
   shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
   factory2.createChannel("127.0.0.2", "20071");
 
-  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+  factory2.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
diff --git a/tests/daemon/face/udp.cpp b/tests/daemon/face/udp.cpp
index 7109808..4d29e7d 100644
--- a/tests/daemon/face/udp.cpp
+++ b/tests/daemon/face/udp.cpp
@@ -28,6 +28,7 @@
 
 #include "tests/test-common.hpp"
 #include "tests/limited-io.hpp"
+#include "face-history.hpp"
 
 namespace nfd {
 namespace tests {
@@ -231,756 +232,373 @@
 {
   UdpFactory factory = UdpFactory();
 
-  factory.createFace(FaceUri("udp4://127.0.0.1"),
+  factory.createFace(FaceUri("udp4://127.0.0.1:6363"),
                      bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("udp4://127.0.0.1/"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("udp4://127.0.0.1/path"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
-
-}
-
-class EndToEndFixture : protected BaseFixture
-{
-public:
-  void
-  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face1));
-    channel1_onFaceCreatedNoCheck(newFace);
-  }
-
-  void
-  channel1_onFaceCreatedNoCheck(const shared_ptr<Face>& newFace)
-  {
-    face1 = newFace;
-    face1->onReceiveInterest.connect(bind(&EndToEndFixture::face1_onReceiveInterest, this, _1));
-    face1->onReceiveData.connect(bind(&EndToEndFixture::face1_onReceiveData, this, _1));
-    face1->onFail.connect(bind(&EndToEndFixture::face1_onFail, this));
-    BOOST_CHECK_MESSAGE(true, "channel 1 face created");
-
-    faces.push_back(face1);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel1_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onReceiveInterest(const Interest& interest)
-  {
-    face1_receivedInterests.push_back(interest);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onReceiveData(const Data& data)
-  {
-    face1_receivedDatas.push_back(data);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onFail()
-  {
-    face1.reset();
-    limitedIo.afterOp();
-  }
-
-  void
-  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face2));
-    face2 = newFace;
-    face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
-    face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
-    face2->onFail.connect(bind(&EndToEndFixture::face2_onFail, this));
-
-    faces.push_back(face2);
-
-    BOOST_CHECK_MESSAGE(true, "channel 2 face created");
-    limitedIo.afterOp();
-  }
-
-  void
-  channel2_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onReceiveInterest(const Interest& interest)
-  {
-    face2_receivedInterests.push_back(interest);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onReceiveData(const Data& data)
-  {
-    face2_receivedDatas.push_back(data);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onFail()
-  {
-    face2.reset();
-    limitedIo.afterOp();
-  }
-
-  void
-  channel3_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face1));
-    face3 = newFace;
-    faces.push_back(newFace);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    faces.push_back(newFace);
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onConnectFailedOk(const std::string& reason)
-  {
-    // it's ok, it was supposed to fail
-    limitedIo.afterOp();
-  }
-
-  void
-  checkFaceList(size_t shouldBe)
-  {
-    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
-  }
-
-  void
-  connect(const shared_ptr<UdpChannel>& channel,
-          const std::string& remoteHost,
-          const std::string& remotePort)
-  {
-    channel->connect(remoteHost, remotePort,
-                     bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-  }
-
-public:
-  LimitedIo limitedIo;
-
-  shared_ptr<Face> face1;
-  std::vector<Interest> face1_receivedInterests;
-  std::vector<Data> face1_receivedDatas;
-  shared_ptr<Face> face2;
-  std::vector<Interest> face2_receivedInterests;
-  std::vector<Data> face2_receivedDatas;
-  shared_ptr<Face> face3;
-
-  std::list< shared_ptr<Face> > faces;
-};
-
-
-BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
-{
-  UdpFactory factory;
+                     bind(&FaceCreateFixture::checkError, this, _1,
+                          "No channels available to connect to 127.0.0.1:6363"));
 
   factory.createChannel("127.0.0.1", "20071");
 
   factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+}
 
+class EndToEndIpv4
+{
+public:
+  static const std::string
+  getScheme()
+  {
+    return "udp4";
+  }
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
+  static const std::string
+  getLocalIp()
+  {
+    return "127.0.0.1";
+  }
 
-  BOOST_REQUIRE(static_cast<bool>(face2));
-  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp4://127.0.0.1:20070");
-  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp4://127.0.0.1:20071");
-  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  static const std::string
+  getPort1()
+  {
+    return "20071";
+  }
+
+  static const std::string
+  getPort2()
+  {
+    return "20072";
+  }
+
+  static const std::string
+  getPort3()
+  {
+    return "20073";
+  }
+
+  static const FaceUri
+  getFaceUri1()
+  {
+    return FaceUri("udp4://127.0.0.1:20071");
+  }
+
+  static const FaceUri
+  getFaceUri2()
+  {
+    return FaceUri("udp4://127.0.0.1:20072");
+  }
+
+  static const FaceUri
+  getFaceUri3()
+  {
+    return FaceUri("udp4://127.0.0.1:20073");
+  }
+};
+
+class EndToEndIpv6
+{
+public:
+  static const std::string
+  getScheme()
+  {
+    return "udp6";
+  }
+
+  static const std::string
+  getLocalIp()
+  {
+    return "::1";
+  }
+
+  static const std::string
+  getPort1()
+  {
+    return "20071";
+  }
+
+  static const std::string
+  getPort2()
+  {
+    return "20072";
+  }
+
+  static const std::string
+  getPort3()
+  {
+    return "20073";
+  }
+
+  static const FaceUri
+  getFaceUri1()
+  {
+    return FaceUri("udp6://[::1]:20071");
+  }
+
+  static const FaceUri
+  getFaceUri2()
+  {
+    return FaceUri("udp6://[::1]:20072");
+  }
+
+  static const FaceUri
+  getFaceUri3()
+  {
+    return FaceUri("udp6://[::1]:20073");
+  }
+};
+
+typedef boost::mpl::list<EndToEndIpv4, EndToEndIpv6> EndToEndAddresses;
+
+// end to end communication
+BOOST_AUTO_TEST_CASE_TEMPLATE(EndToEnd, A, EndToEndAddresses)
+{
+  LimitedIo limitedIo;
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+
+  // face1 (on channel1) connects to face2 (on channel2, to be created)
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  factory.createFace(A::getFaceUri2(),
+                     [&] (shared_ptr<Face> newFace) {
+                       face1 = newFace;
+                       history1.reset(new FaceHistory(*face1, limitedIo));
+                       limitedIo.afterOp();
+                     },
+                     [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::seconds(1));
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(face1->getRemoteUri(), A::getFaceUri2());
+  BOOST_CHECK_EQUAL(face1->getLocalUri(), A::getFaceUri1());
+  BOOST_CHECK_EQUAL(face1->isLocal(), false); // UdpFace is never local
+  BOOST_CHECK_EQUAL(face1->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face1->getCounters().getNOutBytes(), 0);
+
+  // channel2 creation must be after face1 creation,
+  // otherwise channel2's endpoint would be prohibited
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2());
+  shared_ptr<Face> face2;
+  unique_ptr<FaceHistory> history2;
+  channel2->listen([&] (shared_ptr<Face> newFace) {
+                     BOOST_CHECK(face2 == nullptr);
+                     face2 = newFace;
+                     history2.reset(new FaceHistory(*face2, limitedIo));
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::seconds(1));
+  BOOST_CHECK(face2 == nullptr); // face2 shouldn't be created until face1 sends something
+
+  shared_ptr<Interest> interest1 = makeInterest("/I1");
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  shared_ptr<Data> data1 = makeData("/D1");
+  shared_ptr<Data> data2 = makeData("/D2");
+
+  // face1 sends to channel2, creates face2
+  face1->sendInterest(*interest1);
+  face1->sendData(*data1);
+  face1->sendData(*data1);
+  face1->sendData(*data1);
+  size_t nBytesSent1 = interest1->wireEncode().size() + 3 * data1->wireEncode().size();
+
+  limitedIo.run(5, time::seconds(1)); // 1 accept, 4 receives
+
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(face2->getRemoteUri(), A::getFaceUri1());
+  BOOST_CHECK_EQUAL(face2->getLocalUri(), A::getFaceUri2());
+  BOOST_CHECK_EQUAL(face2->isLocal(), false); // UdpFace is never local
+  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), nBytesSent1);
   BOOST_CHECK_EQUAL(face2->getCounters().getNOutBytes(), 0);
-  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), 0);
-  // face1 is not created yet
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(history2->receivedInterests.size(), 1);
+  BOOST_CHECK_EQUAL(history2->receivedInterests.front().getName(), interest1->getName());
+  BOOST_REQUIRE_EQUAL(history2->receivedData.size(), 3);
+  BOOST_CHECK_EQUAL(history2->receivedData.front().getName(), data1->getName());
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
-  Interest interest3("ndn:/QWiIhjgkj5sL");
-  Data     data3    ("ndn:/XNBV794f");
-  data3.setContent(0, 0);
+  // face2 sends to face1
+  face2->sendInterest(*interest2);
+  face2->sendInterest(*interest2);
+  face2->sendInterest(*interest2);
+  face2->sendData(*data2);
+  size_t nBytesSent2 = 3 * interest2->wireEncode().size() + data2->wireEncode().size();
 
+  limitedIo.run(4, time::seconds(1)); // 4 receives
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
-                                        reinterpret_cast<const uint8_t*>(0),
-                                        0));
+  BOOST_REQUIRE_EQUAL(history1->receivedInterests.size(), 3);
+  BOOST_CHECK_EQUAL(history1->receivedInterests.front().getName(), interest2->getName());
+  BOOST_REQUIRE_EQUAL(history1->receivedData.size(), 1);
+  BOOST_CHECK_EQUAL(history1->receivedData.front().getName(), data2->getName());
 
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-  data3.setSignature(fakeSignature);
-
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  size_t nBytesSent2 = interest2.wireEncode().size() + data2.wireEncode().size() * 3;
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(5,//4 send + 1 listen return
-                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp4://127.0.0.1:20071");
-  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp4://127.0.0.1:20070");
-  BOOST_CHECK_EQUAL(face1->isLocal(), false);
-
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
-  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
-  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-
-
-
-  //checking if the connection accepting mechanism works properly.
-
-  face2->sendData    (data3    );
-  face2->sendInterest(interest3);
-  nBytesSent2 += data3.wireEncode().size() + interest3.wireEncode().size();
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 4);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [3].getName(), data3.getName());
-
+  // counters
   const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 2);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 4);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas(), 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas(), 3);
   BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
+  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
 
   const FaceCounters& counters2 = face2->getCounters();
-  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
-  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 2);
-  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 4);
+  BOOST_CHECK_EQUAL(counters2.getNInInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas(), 3);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNInBytes(), nBytesSent1);
   BOOST_CHECK_EQUAL(counters2.getNOutBytes(), nBytesSent2);
 }
 
-BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+// channel accepting multiple incoming connections
+BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, A, EndToEndAddresses)
 {
+  LimitedIo limitedIo;
   UdpFactory factory;
 
-  factory.createChannel("::1", "20071");
+  // channel1 is listening
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+  std::vector<shared_ptr<Face>> faces1;
+  channel1->listen([&] (shared_ptr<Face> newFace) {
+                     faces1.push_back(newFace);
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  factory.createFace(FaceUri("udp://[::1]:20070"),
-                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+  // face2 (on channel2) connects to channel1
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2());
+  BOOST_CHECK_NE(channel1, channel2);
+  shared_ptr<Face> face2;
+  boost::asio::ip::address ipAddress2 = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint2(ipAddress2, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel2->connect(endpoint2,
+                    [&] (shared_ptr<Face> newFace) {
+                      face2 = newFace;
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
 
+  limitedIo.run(1, time::seconds(1)); // 1 create (on channel2)
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(faces1.size(), 0); // channel1 won't create face until face2 sends something
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
+  // face3 (on channel3) connects to channel1
+  shared_ptr<UdpChannel> channel3 = factory.createChannel(A::getLocalIp(), A::getPort3());
+  BOOST_CHECK_NE(channel1, channel3);
+  shared_ptr<Face> face3;
+  boost::asio::ip::address ipAddress3 = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint3(ipAddress3, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel3->connect(endpoint3,
+                    [&] (shared_ptr<Face> newFace) {
+                      face3 = newFace;
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  BOOST_REQUIRE(static_cast<bool>(face2));
-  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp6://[::1]:20070");
-  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp6://[::1]:20071");
-  BOOST_CHECK_EQUAL(face2->isLocal(), false);
-  // face1 is not created yet
+  limitedIo.run(1, time::seconds(1)); // 1 create (on channel3)
+  BOOST_REQUIRE(face3 != nullptr);
+  BOOST_CHECK_EQUAL(faces1.size(), 0); // channel1 won't create face until face3 sends something
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  // face2 sends to channel1
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  face2->sendInterest(*interest2);
+  limitedIo.run(1, time::milliseconds(100)); // 1 accept (on channel1)
+  BOOST_REQUIRE_EQUAL(faces1.size(), 1);
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(faces1.at(0)->getRemoteUri(), A::getFaceUri2());
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
-  Interest interest3("ndn:/QWiIhjgkj5sL");
-  Data     data3    ("ndn:/XNBV794f");
-  data3.setContent(0, 0);
-
-
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
-                                        reinterpret_cast<const uint8_t*>(0),
-                                        0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-  data3.setSignature(fakeSignature);
-
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(3,//2 send + 1 listen return
-                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp6://[::1]:20071");
-  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp6://[::1]:20070");
-  BOOST_CHECK_EQUAL(face1->isLocal(), false);
-
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
-  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-
-
-
-  //checking if the connection accepting mechanism works properly.
-
-  face2->sendData    (data3    );
-  face2->sendInterest(interest3);
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+  // face3 sends to channel1
+  shared_ptr<Data> data3 = makeData("/D3");
+  face3->sendData(*data3);
+  limitedIo.run(1, time::milliseconds(100)); // 1 accept (on channel1)
+  BOOST_REQUIRE_EQUAL(faces1.size(), 2);
+  BOOST_CHECK_EQUAL(channel1->size(), 2);
+  BOOST_CHECK_EQUAL(faces1.at(1)->getRemoteUri(), A::getFaceUri3());
 }
 
-BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+// manually close a face
+BOOST_AUTO_TEST_CASE_TEMPLATE(ManualClose, A, EndToEndAddresses)
 {
-  Interest interest1("ndn:/TpnzGvW9R");
-  Interest interest2("ndn:/QWiIMfj5sL");
-
+  LimitedIo limitedIo;
   UdpFactory factory;
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  factory.createFace(A::getFaceUri2(),
+                     [&] (shared_ptr<Face> newFace) {
+                       face1 = newFace;
+                       history1.reset(new FaceHistory(*face1, limitedIo));
+                       limitedIo.afterOp();
+                     },
+                     [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+  limitedIo.run(1, time::milliseconds(100));
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
 
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  BOOST_CHECK_EQUAL(faces.size(), 1);
-
-  shared_ptr<UdpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
-  channel3->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-
-  shared_ptr<UdpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
-
-  BOOST_CHECK_NE(channel3, channel4);
-
-  scheduler::schedule(time::milliseconds(500),
-                      bind(&EndToEndFixture::connect, this, channel4, "127.0.0.1", "20070"));
-
-  scheduler::schedule(time::milliseconds(400), bind(&EndToEndFixture::checkFaceList, this, 2));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,// 2 connects
-                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept multiple connections");
-
-  BOOST_CHECK_EQUAL(faces.size(), 3);
-
-
-  face2->sendInterest(interest1);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_CHECK_EQUAL(faces.size(), 4);
-
-  face3->sendInterest(interest2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
-  BOOST_CHECK_EQUAL(faces.size(), 5);
-  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,     this, _1),
-                                     bind(&EndToEndFixture::channel_onConnectFailedOk, this, _1)),
-                    UdpChannel::Error);
-}
-
-//Test commented because it required to be run in a machine that can resolve ipv6 query
-//BOOST_FIXTURE_TEST_CASE(EndToEndIpv6, EndToEndFixture)
-//{
-//  UdpFactory factory = UdpFactory();
-//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
-//
-//  EventId abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                              "UdpChannel error: cannot connect or cannot accept connection"));
-//
-//  limitedIoRemaining = 1;
-//
-//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
-//
-//  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-//                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-//
-//  channel2->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE(static_cast<bool>(face2));
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//
-//  Interest interest1("ndn:/TpnzGvW9R");
-//  Data     data1    ("ndn:/KfczhUqVix");
-//  data1.setContent(0, 0);
-//  Interest interest2("ndn:/QWiIMfj5sL");
-//  Data     data2    ("ndn:/XNBV796f");
-//  data2.setContent(0, 0);
-//  Interest interest3("ndn:/QWiIhjgkj5sL");
-//  Data     data3    ("ndn:/XNBV794f");
-//  data3.setContent(0, 0);
-//
-//  ndn::SignatureSha256WithRsa fakeSignature;
-//  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-//
-//  // set fake signature on data1 and data2
-//  data1.setSignature(fakeSignature);
-//  data2.setSignature(fakeSignature);
-//  data3.setSignature(fakeSignature);
-//
-//  face2->sendInterest(interest2);
-//  face2->sendData    (data2    );
-//
-//  limitedIoRemaining = 3; //2 send + 1 listen return
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE(static_cast<bool>(face1));
-//
-//  face1->sendInterest(interest1);
-//  face1->sendData    (data1    );
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 2;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
-//  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-//
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-//  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-//  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-//
-//  //checking if the connection accepting mechanism works properly.
-//
-//  face2->sendData    (data3    );
-//  face2->sendInterest(interest3);
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 2;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
-//
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-//  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
-//
-//}
-
-
-//Test commented because it required to be run in a machine that can resolve ipv6 query
-//BOOST_FIXTURE_TEST_CASE(MultipleAcceptsIpv6, EndToEndFixture)
-//{
-//  Interest interest1("ndn:/TpnzGvW9R");
-//  Interest interest2("ndn:/QWiIMfj5sL");
-//  Interest interest3("ndn:/QWiIhjgkj5sL");
-//
-//  UdpFactory factory = UdpFactory();
-//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
-//
-//  EventId abortEvent =
-//  scheduler.scheduleEvent(time::seconds(4),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot connect or cannot accept connection"));
-//
-//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
-//
-//  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
-//                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  channel2->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 1);
-//
-//  shared_ptr<UdpChannel> channel3 = factory.createChannel("::1", "20072");
-//  channel3->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20073");
-//
-//  BOOST_CHECK_NE(channel3, channel4);
-//
-//  scheduler
-//  .scheduleEvent(time::milliseconds(500),
-//                 bind(&UdpChannel::connect, channel4,
-//                      "::1", "20070",
-//                      static_cast<UdpChannel::FaceCreatedCallback>(bind(&EndToEndFixture::
-//                                                                        channel_onFaceCreated, this, _1)),
-//                      static_cast<UdpChannel::ConnectFailedCallback>(bind(&EndToEndFixture::
-//                                                                          channel_onConnectFailed, this, _1))));
-//
-//  limitedIoRemaining = 2; // 2 connects
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(4),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot connect or cannot accept multiple connections"));
-//
-//  scheduler.scheduleEvent(time::seconds(0.4),
-//                          bind(&EndToEndFixture::checkFaceList, this, 2));
-//
-//  g_io.run();
-//  g_io.reset();
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 3);
-//
-//
-//  face2->sendInterest(interest1);
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 4);
-//
-//  face3->sendInterest(interest2);
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
-//  BOOST_CHECK_EQUAL(faces.size(), 5);
-//  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,
-//                                          this,
-//                                          _1),
-//                                      bind(&EndToEndFixture::channel_onConnectFailedOk,
-//                                          this,
-//                                          _1)),
-//                    UdpChannel::Error);
-//}
-
-
-BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
-{
-  UdpFactory factory = UdpFactory();
-
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
-
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-
-  BOOST_CHECK(static_cast<bool>(face2));
-
-  // Face::close must be invoked during io run to be counted as an op
-  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face2));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "FaceClosing error: cannot properly close faces");
-
-  BOOST_CHECK(!static_cast<bool>(face2));
-
-  BOOST_CHECK_EQUAL(channel2->size(), 0);
-}
-
-BOOST_FIXTURE_TEST_CASE(ClosingIdleFace, EndToEndFixture)
-{
-  Interest interest1("ndn:/TpnzGvW9R");
-  Interest interest2("ndn:/QWiIMfj5sL");
-
-  UdpFactory factory;
-
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070",
-                                                          time::seconds(2));
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071",
-                                                          time::seconds(2));
-
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  face2->sendInterest(interest1);
-  BOOST_CHECK(!face2->isOnDemand());
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_CHECK_EQUAL(faces.size(), 2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(2)) == LimitedIo::EXCEED_TIME,
-                      "Idle face should be still open because has been used recently");
-  BOOST_CHECK_EQUAL(faces.size(), 2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "Closing idle face error: face should be closed by now");
-
-  //the face on listen should be closed now
+  face1->close();
+  getGlobalIoService().poll();
+  BOOST_CHECK_EQUAL(history1->failures.size(), 1);
   BOOST_CHECK_EQUAL(channel1->size(), 0);
-  //checking that face2 has not been closed
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_REQUIRE(static_cast<bool>(face2));
+}
 
-  face2->sendInterest(interest2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-  //channel1 should have created a new face by now
+// automatically close an idle incoming face
+BOOST_AUTO_TEST_CASE_TEMPLATE(IdleClose, A, EndToEndAddresses)
+{
+  LimitedIo limitedIo;
+  UdpFactory factory;
+
+  // channel1 is listening
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1(),
+                                                          time::seconds(2));
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  channel1->listen([&] (shared_ptr<Face> newFace) {
+                     BOOST_CHECK(face1 == nullptr);
+                     face1 = newFace;
+                     history1.reset(new FaceHistory(*face1, limitedIo));
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  // face2 (on channel2) connects to channel1
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2(),
+                                                          time::seconds(2));
+  shared_ptr<Face> face2;
+  unique_ptr<FaceHistory> history2;
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel2->connect(endpoint,
+                    [&] (shared_ptr<Face> newFace) {
+                      face2 = newFace;
+                      history2.reset(new FaceHistory(*face2, limitedIo));
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::milliseconds(100)); // 1 create (on channel2)
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(face2->isOnDemand(), false);
+
+  // face2 sends to channel1, creates face1
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  face2->sendInterest(*interest2);
+
+  limitedIo.run(2, time::seconds(1)); // 1 accept (on channel1), 1 receive (on face1)
   BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK(face1->isOnDemand());
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(face1->isOnDemand(), true);
 
-  channel1->connect("127.0.0.1", "20071",
-                    bind(&EndToEndFixture::channel1_onFaceCreatedNoCheck, this, _1),
-                    bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  limitedIo.defer(time::seconds(1));
+  BOOST_CHECK_EQUAL(history1->failures.size(), 0); // face1 is not idle
+  BOOST_CHECK_EQUAL(history2->failures.size(), 0); // face2 is outgoing face and never closed
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1,//1 connect
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect");
-
-  BOOST_CHECK(!face1->isOnDemand());
-
-  //the connect should have set face1 as permanent face,
-  //but it shouln't have created any additional faces
-  BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_TIME,
-                      "Idle face should be still open because it's permanent now");
-  //both faces are permanent, nothing should have changed
-  BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  limitedIo.defer(time::seconds(4));
+  BOOST_CHECK_EQUAL(history1->failures.size(), 1); // face1 is idle and automatically closed
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  BOOST_CHECK_EQUAL(history2->failures.size(), 0); // face2 is outgoing face and never closed
 }
 
 class FakeNetworkInterfaceFixture : public BaseFixture