tools: Refactoring ndn-autoconfig implementation

Change-Id: Ib92942ba40f04aaee83479177b9ba5d32a09af04
Refs: #2421
diff --git a/tools/ndn-autoconfig.cpp b/tools/ndn-autoconfig.cpp
deleted file mode 100644
index 09f5dba..0000000
--- a/tools/ndn-autoconfig.cpp
+++ /dev/null
@@ -1,537 +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/>.
- */
-
-#include "version.hpp"
-
-#include <ndn-cxx/face.hpp>
-#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/management/nfd-controller.hpp>
-#include <ndn-cxx/management/nfd-face-status.hpp>
-#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/encoding/buffer-stream.hpp>
-#include <ndn-cxx/util/face-uri.hpp>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-
-#ifdef __APPLE__
-#include <arpa/nameser_compat.h>
-#endif
-
-namespace ndn {
-namespace tools {
-
-static const Name LOCALHOP_HUB_DISCOVERY_PREFIX = "/localhop/ndn-autoconf/hub";
-
-void
-usage(const char* programName)
-{
-  std::cout << "Usage:\n" << programName  << " [-h] [-V]\n"
-            << "   -h  - print usage and exit\n"
-            << "   -V  - print version number and exit\n"
-            << std::endl;
-}
-
-class NdnAutoconfig : boost::noncopyable
-{
-public:
-  union QueryAnswer
-  {
-    HEADER header;
-    uint8_t buf[NS_PACKETSZ];
-  };
-
-  class Error : public std::runtime_error
-  {
-  public:
-    explicit
-    Error(const std::string& what)
-      : std::runtime_error(what)
-    {
-    }
-  };
-
-  explicit
-  NdnAutoconfig()
-    : m_controller(m_face, m_keyChain)
-  {
-  }
-
-  void
-  run()
-  {
-    m_face.processEvents();
-  }
-
-  void
-  fetchSegments(const Data& data, const shared_ptr<OBufferStream>& buffer,
-                void (NdnAutoconfig::*onDone)(const shared_ptr<OBufferStream>&))
-  {
-    buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
-                  data.getContent().value_size());
-
-    uint64_t currentSegment = data.getName().get(-1).toSegment();
-
-    const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
-    if (finalBlockId.empty() ||
-        finalBlockId.toSegment() > currentSegment)
-      {
-        m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
-                               ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer, onDone),
-                               ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
-      }
-    else
-      {
-        return (this->*onDone)(buffer);
-      }
-  }
-
-  void
-  discoverHubStage1()
-  {
-    shared_ptr<OBufferStream> buffer = make_shared<OBufferStream>();
-
-    Interest interest("/localhost/nfd/faces/list");
-    interest.setChildSelector(1);
-    interest.setMustBeFresh(true);
-
-    m_face.expressInterest(interest,
-                           ndn::bind(&NdnAutoconfig::fetchSegments, this, _2, buffer,
-                                     &NdnAutoconfig::discoverHubStage1_registerHubDiscoveryPrefix),
-                           ndn::bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
-  }
-
-  void
-  discoverHubStage1_registerHubDiscoveryPrefix(const shared_ptr<OBufferStream>& buffer)
-  {
-    ConstBufferPtr buf = buffer->buf();
-    std::vector<uint64_t> multicastFaces;
-
-    size_t offset = 0;
-    while (offset < buf->size())
-      {
-        Block block;
-        bool ok = Block::fromBuffer(buf, offset, block);
-        if (!ok)
-          {
-            std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
-            break;
-          }
-
-        offset += block.size();
-
-        nfd::FaceStatus faceStatus(block);
-
-        ndn::util::FaceUri uri(faceStatus.getRemoteUri());
-        if (uri.getScheme() == "udp4") {
-          namespace ip = boost::asio::ip;
-          boost::system::error_code ec;
-          ip::address address = ip::address::from_string(uri.getHost(), ec);
-
-          if (!ec && address.is_multicast()) {
-            multicastFaces.push_back(faceStatus.getFaceId());
-          }
-          else
-            continue;
-        }
-      }
-
-    if (multicastFaces.empty()) {
-      discoverHubStage2("No multicast faces available, skipping stage 1");
-    }
-    else {
-      shared_ptr<nfd::Controller> controller = make_shared<nfd::Controller>(ref(m_face));
-      shared_ptr<std::pair<size_t, size_t> > nRegistrations =
-        make_shared<std::pair<size_t, size_t> >(0, 0);
-
-      nfd::ControlParameters parameters;
-      parameters
-        .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
-        .setCost(1)
-        .setExpirationPeriod(time::seconds(30));
-
-      nRegistrations->first = multicastFaces.size();
-
-      for (std::vector<uint64_t>::iterator i = multicastFaces.begin();
-           i != multicastFaces.end(); ++i) {
-        parameters.setFaceId(*i);
-
-        controller->start<nfd::RibRegisterCommand>(parameters,
-          bind(&NdnAutoconfig::discoverHubStage1_onRegisterSuccess,
-               this, controller, nRegistrations),
-          bind(&NdnAutoconfig::discoverHubStage1_onRegisterFailure,
-               this, _1, _2, controller, nRegistrations));
-      }
-    }
-  }
-
-  void
-  discoverHubStage1_onRegisterSuccess(const shared_ptr<nfd::Controller>& controller,
-                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
-  {
-    nRegistrations->second++;
-
-    if (nRegistrations->first == nRegistrations->second) {
-      discoverHubStage1_setStrategy(controller);
-    }
-  }
-
-  void
-  discoverHubStage1_onRegisterFailure(uint32_t code, const std::string& error,
-                                      const shared_ptr<nfd::Controller>& controller,
-                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
-  {
-    std::cerr << "ERROR: " << error << " (code: " << code << ")" << std::endl;
-    nRegistrations->first--;
-
-    if (nRegistrations->first == nRegistrations->second) {
-      if (nRegistrations->first > 0) {
-        discoverHubStage1_setStrategy(controller);
-      } else {
-        discoverHubStage2("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
-                          " for all multicast faces");
-      }
-    }
-  }
-
-  void
-  discoverHubStage1_setStrategy(const shared_ptr<nfd::Controller>& controller)
-  {
-    nfd::ControlParameters parameters;
-    parameters
-      .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
-      .setStrategy("/localhost/nfd/strategy/broadcast");
-
-    controller->start<nfd::StrategyChoiceSetCommand>(parameters,
-      bind(&NdnAutoconfig::discoverHubStage1_onSetStrategySuccess,
-           this, controller),
-      bind(&NdnAutoconfig::discoverHubStage1_onSetStrategyFailure,
-           this, _2, controller));
-  }
-
-  void
-  discoverHubStage1_onSetStrategySuccess(const shared_ptr<nfd::Controller>& controller)
-  {
-    discoverHubStage1_requestHubData();
-  }
-
-  void
-  discoverHubStage1_onSetStrategyFailure(const std::string& error,
-                                         const shared_ptr<nfd::Controller>& controller)
-  {
-    discoverHubStage2("Failed to set broadcast strategy for " +
-                      LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + error + ")");
-  }
-
-  // Start to look for a hub (NDN hub discovery first stage)
-  void
-  discoverHubStage1_requestHubData()
-  {
-    Interest interest(LOCALHOP_HUB_DISCOVERY_PREFIX);
-    interest.setInterestLifetime(time::milliseconds(4000)); // 4 seconds
-    interest.setMustBeFresh(true);
-
-    std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
-    m_face.expressInterest(interest,
-                           bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
-                           bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
-  }
-
-  // First stage OnData Callback
-  void
-  onDiscoverHubStage1Success(const Interest& interest, Data& data)
-  {
-    const Block& content = data.getContent();
-    content.parse();
-
-    // Get Uri
-    Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
-    if (blockValue == content.elements_end()) {
-      discoverHubStage2("Incorrect reply to stage1");
-      return;
-    }
-    std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
-                            blockValue->value_size());
-    connectToHub(faceMgmtUri);
-  }
-
-  // First stage OnTimeout callback - start 2nd stage
-  void
-  discoverHubStage2(const std::string& message)
-  {
-    std::cerr << message << std::endl;
-    std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
-
-    _res.retry = 2;
-    _res.ndots = 10;
-
-    QueryAnswer queryAnswer;
-
-    int answerSize = res_search("_ndn._udp",
-                                ns_c_in,
-                                ns_t_srv,
-                                queryAnswer.buf,
-                                sizeof(queryAnswer));
-
-    // 2nd stage failed - move on to the third stage
-    if (answerSize < 0) {
-      discoverHubStage3("Failed to find NDN router using default suffix DNS query");
-    }
-    else
-    {
-      bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
-      if (isParsed == false) {
-        // Failed to parse DNS response, try stage 3
-        discoverHubStage3("Failed to parse DNS response");
-      }
-    }
-  }
-
-  // Second stage OnTimeout callback
-  void
-  discoverHubStage3(const std::string& message)
-  {
-    std::cerr << message << std::endl;
-    std::cerr << "Stage 3: Trying to find home router..." << std::endl;
-
-    KeyChain keyChain;
-    Name identity = keyChain.getDefaultIdentity();
-    std::string serverName = "_ndn._udp.";
-
-    for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++) {
-      serverName.append(i->toUri());
-      serverName.append(".");
-    }
-    serverName += "_homehub._autoconf.named-data.net";
-    std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
-
-    QueryAnswer queryAnswer;
-
-    int answerSize = res_query(serverName.c_str(),
-                               ns_c_in,
-                               ns_t_srv,
-                               queryAnswer.buf,
-                               sizeof(queryAnswer));
-
-
-    // 3rd stage failed - abort
-    if (answerSize < 0) {
-      std::cerr << "Failed to find a home router" << std::endl;
-      std::cerr << "exit" << std::endl;
-    }
-    else
-    {
-      bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
-      if (isParsed == false) {
-        // Failed to parse DNS response
-        throw Error("Failed to parse DNS response");
-      }
-    }
-
-  }
-
-  bool
-  parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
-  {
-    // The references of the next classes are:
-    // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
-    // https://gist.github.com/mologie/6027597
-
-    struct rechdr
-    {
-      uint16_t type;
-      uint16_t iclass;
-      uint32_t ttl;
-      uint16_t length;
-    };
-
-    struct srv_t
-    {
-      uint16_t priority;
-      uint16_t weight;
-      uint16_t port;
-      uint8_t* target;
-    };
-
-    if (ntohs(queryAnswer.header.ancount) == 0) {
-      std::cerr << "No records found\n" << std::endl;
-      return false;
-    }
-
-    uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
-
-    blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
-
-    for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++) {
-      char srvName[NS_MAXDNAME];
-      int serverNameSize = dn_expand(queryAnswer.buf,               // message pointer
-                                     queryAnswer.buf + answerSize,  // end of message
-                                     blob,                          // compressed server name
-                                     srvName,                       // expanded server name
-                                     NS_MAXDNAME);
-      if (serverNameSize < 0) {
-        return false;
-      }
-
-      srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
-      uint16_t convertedPort = be16toh(server->port);
-
-      blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
-
-      char hostName[NS_MAXDNAME];
-      int hostNameSize = dn_expand(queryAnswer.buf,               // message pointer
-                                   queryAnswer.buf + answerSize,  // end of message
-                                   blob,                          // compressed host name
-                                   hostName,                      // expanded host name
-                                   NS_MAXDNAME);
-      if (hostNameSize < 0) {
-        return false;
-      }
-
-      std::string uri = "udp://";
-      uri.append(hostName);
-      uri.append(":");
-      uri.append(boost::lexical_cast<std::string>(convertedPort));
-
-      connectToHub(uri);
-
-      return true;
-    }
-    return false;
-  }
-
-  void
-  connectToHub(const std::string& uri)
-  {
-    ndn::util::FaceUri faceUri(uri);
-
-    faceUri.canonize(bind(&NdnAutoconfig::onCanonizeSuccess, this, _1),
-                     bind(&NdnAutoconfig::onCanonizeFailure, this, _1),
-                     m_face.getIoService(), ndn::time::seconds(4));
-
-  }
-
-  void onCanonizeSuccess(const ndn::util::FaceUri& canonicalUri)
-  {
-    std::cerr << "about to connect to: " << canonicalUri.toString() << std::endl;
-
-    m_controller.start<nfd::FaceCreateCommand>(
-      nfd::ControlParameters()
-        .setUri(canonicalUri.toString()),
-      bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
-      bind(&NdnAutoconfig::onHubConnectError, this, _1, _2));
-  }
-
-  void
-  onCanonizeFailure(const std::string& reason)
-  {
-    std::ostringstream os;
-    os << "Canonize faceUri failed: " << reason;
-    throw Error(os.str());
-  }
-
-  void
-  onHubConnectSuccess(const nfd::ControlParameters& resp)
-  {
-    std::cerr << "Successfully created face: " << resp << std::endl;
-
-    // Register a prefix in RIB
-    static const Name TESTBED_PREFIX("/ndn");
-    m_controller.start<nfd::RibRegisterCommand>(
-      nfd::ControlParameters()
-        .setName(TESTBED_PREFIX)
-        .setFaceId(resp.getFaceId())
-        .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
-        .setCost(100)
-        .setExpirationPeriod(time::milliseconds::max()),
-      bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
-      bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
-  }
-
-  void
-  onHubConnectError(uint32_t code, const std::string& error)
-  {
-    std::ostringstream os;
-    os << "Failed to create face: " << error << " (code: " << code << ")";
-    throw Error(os.str());
-  }
-
-  void
-  onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
-  {
-    std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
-  }
-
-  void
-  onPrefixRegistrationError(uint32_t code, const std::string& error)
-  {
-    std::ostringstream os;
-    os << "Failed in name registration, " << error << " (code: " << code << ")";
-    throw Error(os.str());
-  }
-
-private:
-  Face m_face;
-  KeyChain m_keyChain;
-  nfd::Controller m_controller;
-};
-
-} // namespace tools
-} // namespace ndn
-
-int
-main(int argc, char** argv)
-{
-  int opt;
-  const char* programName = argv[0];
-
-  while ((opt = getopt(argc, argv, "hV")) != -1) {
-    switch (opt) {
-    case 'h':
-      ndn::tools::usage(programName);
-      return 0;
-    case 'V':
-      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
-      return 0;
-    }
-  }
-
-  try {
-    ndn::tools::NdnAutoconfig autoConfigInstance;
-
-    autoConfigInstance.discoverHubStage1();
-    autoConfigInstance.run();
-  }
-  catch (const std::exception& error) {
-    std::cerr << "ERROR: " << error.what() << std::endl;
-    return 1;
-  }
-  return 0;
-}
diff --git a/tools/ndn-autoconfig/base-dns.cpp b/tools/ndn-autoconfig/base-dns.cpp
new file mode 100644
index 0000000..23e8782
--- /dev/null
+++ b/tools/ndn-autoconfig/base-dns.cpp
@@ -0,0 +1,161 @@
+/* -*- 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/>.
+ */
+
+#include "base-dns.hpp"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+
+#ifdef __APPLE__
+#include <arpa/nameser_compat.h>
+#endif
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+union BaseDns::QueryAnswer
+{
+  HEADER header;
+  uint8_t buf[NS_PACKETSZ];
+};
+
+BaseDns::BaseDns(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure)
+  : Base(face, keyChain, nextStageOnFailure)
+{
+}
+
+std::string
+BaseDns::querySrvRr(const std::string& fqdn)
+{
+  std::string srvDomain = "_ndn._udp." + fqdn;
+  std::cerr << "Sending DNS query for SRV record for " << srvDomain << std::endl;
+
+  QueryAnswer queryAnswer;
+  int answerSize = res_query(srvDomain.c_str(),
+                             ns_c_in,
+                             ns_t_srv,
+                             queryAnswer.buf,
+                             sizeof(queryAnswer));
+  if (answerSize == 0) {
+    throw Error("No DNS SRV records found for " + srvDomain);
+  }
+  return parseSrvRr(queryAnswer, answerSize);
+}
+
+/**
+ * @brief Send DNS SRV request using search domain list
+ */
+std::string
+BaseDns::querySrvRrSearch()
+{
+  std::cerr << "Sending DNS query for SRV record for _ndn._udp" << std::endl;
+
+  QueryAnswer queryAnswer;
+
+  _res.retry = 2;
+  _res.ndots = 10;
+
+  int answerSize = res_search("_ndn._udp",
+                              ns_c_in,
+                              ns_t_srv,
+                              queryAnswer.buf,
+                              sizeof(queryAnswer));
+
+  if (answerSize == 0) {
+    throw Error("No DNS SRV records found for _ndn._udp");
+  }
+
+  return parseSrvRr(queryAnswer, answerSize);
+}
+
+std::string
+BaseDns::parseSrvRr(const QueryAnswer& queryAnswer, int answerSize)
+{
+  // The references of the next classes are:
+  // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
+
+  struct rechdr
+  {
+    uint16_t type;
+    uint16_t iclass;
+    uint32_t ttl;
+    uint16_t length;
+  };
+
+  struct srv_t
+  {
+    uint16_t priority;
+    uint16_t weight;
+    uint16_t port;
+    uint8_t* target;
+  };
+
+  if (ntohs(queryAnswer.header.ancount) == 0) {
+    throw Error("SRV record cannot be parsed");
+  }
+
+  const uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
+
+  blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
+
+  char srvName[NS_MAXDNAME];
+  int serverNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                                 queryAnswer.buf + answerSize,  // end of message
+                                 blob,                          // compressed server name
+                                 srvName,                       // expanded server name
+                                 NS_MAXDNAME);
+  if (serverNameSize <= 0) {
+    throw Error("SRV record cannot be parsed (error decoding domain name)");
+  }
+
+  const srv_t* server = reinterpret_cast<const srv_t*>(&blob[sizeof(rechdr)]);
+  uint16_t convertedPort = be16toh(server->port);
+
+  blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
+
+  char hostName[NS_MAXDNAME];
+  int hostNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                               queryAnswer.buf + answerSize,  // end of message
+                               blob,                          // compressed host name
+                               hostName,                      // expanded host name
+                               NS_MAXDNAME);
+  if (hostNameSize <= 0) {
+    throw Error("SRV record cannot be parsed (error decoding host name)");
+  }
+
+  std::string uri = "udp://";
+  uri.append(hostName);
+  uri.append(":");
+  uri.append(std::to_string(convertedPort));
+
+  return uri;
+}
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/base-dns.hpp b/tools/ndn-autoconfig/base-dns.hpp
new file mode 100644
index 0000000..1b881ba
--- /dev/null
+++ b/tools/ndn-autoconfig/base-dns.hpp
@@ -0,0 +1,85 @@
+/* -*- 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_TOOLS_NDN_AUTOCONFIG_BASE_DNS_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_BASE_DNS_HPP
+
+#include "base.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+/**
+ * @brief Base class for stages that use DNS-based guessing
+ */
+class BaseDns : public Base
+{
+protected:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  BaseDns(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure);
+
+  /**
+   * @brief Send DNS SRV request for a @p fqdn fully qualified domain name
+   * @return FaceUri of the hub from the requested SRV record
+   * @throw Error if query returns nothing or SRV record cannot be parsed
+   */
+  std::string
+  querySrvRr(const std::string& fqdn);
+
+  /**
+   * @brief Send DNS SRV request using search domain list
+   * @return FaceUri of the hub from the requested SRV record
+   * @throw Error if query returns nothing or SRV record cannot be parsed
+   */
+  std::string
+  querySrvRrSearch();
+
+private:
+  union QueryAnswer;
+
+  /**
+   * @brief Parse SRV record
+   * @return FaceUri of the hub from the SRV record
+   * @throw Error if SRV record cannot be parsed
+   */
+  std::string
+  parseSrvRr(const QueryAnswer& queryAnswer, int answerSize);
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_BASE_DNS_HPP
diff --git a/tools/ndn-autoconfig/base.cpp b/tools/ndn-autoconfig/base.cpp
new file mode 100644
index 0000000..ed051e1
--- /dev/null
+++ b/tools/ndn-autoconfig/base.cpp
@@ -0,0 +1,119 @@
+/* -*- 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/>.
+ */
+
+#include "base.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+Base::Base(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure)
+  : m_face(face)
+  , m_keyChain(keyChain)
+  , m_controller(face, keyChain)
+  , m_nextStageOnFailure(nextStageOnFailure)
+{
+}
+
+void
+Base::connectToHub(const std::string& uri)
+{
+  util::FaceUri faceUri(uri);
+
+  faceUri.canonize(bind(&Base::onCanonizeSuccess, this, _1),
+                   bind(&Base::onCanonizeFailure, this, _1),
+                   m_face.getIoService(), time::seconds(4));
+
+}
+
+
+void
+Base::onCanonizeSuccess(const util::FaceUri& canonicalUri)
+{
+  std::cerr << "About to connect to: " << canonicalUri.toString() << std::endl;
+
+  m_controller.start<nfd::FaceCreateCommand>(nfd::ControlParameters()
+                                               .setUri(canonicalUri.toString()),
+                                             bind(&Base::onHubConnectSuccess, this, _1),
+                                             bind(&Base::onHubConnectError, this, _1, _2));
+}
+
+void
+Base::onCanonizeFailure(const std::string& reason)
+{
+  std::ostringstream os;
+  os << "FaceUri canonization failed: " << reason;
+  throw Error(os.str());
+}
+
+void
+Base::onHubConnectSuccess(const nfd::ControlParameters& resp)
+{
+  std::cerr << "Successfully created face: " << resp << std::endl;
+
+  static const Name TESTBED_PREFIX = "/ndn";
+  registerPrefix(TESTBED_PREFIX, resp.getFaceId());
+}
+
+void
+Base::onHubConnectError(uint32_t code, const std::string& error)
+{
+  std::ostringstream os;
+  os << "Failed to create face: " << error << " (code: " << code << ")";
+  throw Error(os.str());
+}
+
+void
+Base::registerPrefix(const Name& prefix, uint64_t faceId)
+{
+  // Register a prefix in RIB
+  m_controller.start<nfd::RibRegisterCommand>(nfd::ControlParameters()
+                                                .setName(prefix)
+                                                .setFaceId(faceId)
+                                                .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+                                                .setCost(100)
+                                                .setExpirationPeriod(time::milliseconds::max()),
+                                              bind(&Base::onPrefixRegistrationSuccess, this, _1),
+                                              bind(&Base::onPrefixRegistrationError, this, _1, _2));
+}
+
+void
+Base::onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
+{
+  std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
+}
+
+void
+Base::onPrefixRegistrationError(uint32_t code, const std::string& error)
+{
+  std::ostringstream os;
+  os << "Failed in name registration, " << error << " (code: " << code << ")";
+  throw Error(os.str());
+}
+
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/base.hpp b/tools/ndn-autoconfig/base.hpp
new file mode 100644
index 0000000..bf195c6
--- /dev/null
+++ b/tools/ndn-autoconfig/base.hpp
@@ -0,0 +1,120 @@
+/* -*- 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_TOOLS_NDN_AUTOCONFIG_BASE_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_BASE_HPP
+
+#include "common.hpp"
+
+#include <boost/noncopyable.hpp>
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+/**
+ * @brief Base class for discovery stages
+ */
+class Base : boost::noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Callback to be called when the stage fails
+   */
+  typedef std::function<void(const std::string&)> NextStageCallback;
+
+  /**
+   * @brief Start the stage
+   */
+  virtual void
+  start() = 0;
+
+protected:
+  /**
+   * @brief Initialize variables and create nfd::Controller instance
+   * @param face Face to be used for all operations (e.g., will send registration commands)
+   * @param keyChain KeyChain object
+   * @param nextStageOnFailure Callback to be called after the stage failed
+   */
+  Base(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure);
+
+  /**
+   * @brief Attempt to connect to local hub using the \p uri FaceUri
+   * @throw Base::Error when failed to establish the tunnel
+   */
+  void
+  connectToHub(const std::string& uri);
+
+private:
+  void
+  onCanonizeSuccess(const util::FaceUri& canonicalUri);
+
+  void
+  onCanonizeFailure(const std::string& reason);
+
+  void
+  onHubConnectSuccess(const nfd::ControlParameters& resp);
+
+  void
+  onHubConnectError(uint32_t code, const std::string& error);
+
+  void
+  registerPrefix(const Name& prefix, uint64_t faceId);
+
+  void
+  onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult);
+
+  void
+  onPrefixRegistrationError(uint32_t code, const std::string& error);
+
+protected:
+  Face& m_face;
+  KeyChain& m_keyChain;
+  nfd::Controller m_controller;
+  NextStageCallback m_nextStageOnFailure;
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_BASE_HPP
diff --git a/tools/ndn-autoconfig/guess-from-identity-name.cpp b/tools/ndn-autoconfig/guess-from-identity-name.cpp
new file mode 100644
index 0000000..5a6df3e
--- /dev/null
+++ b/tools/ndn-autoconfig/guess-from-identity-name.cpp
@@ -0,0 +1,63 @@
+/* -*- 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/>.
+ */
+
+#include "guess-from-identity-name.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+GuessFromIdentityName::GuessFromIdentityName(Face& face, KeyChain& keyChain,
+                                             const NextStageCallback& nextStageOnFailure)
+  : BaseDns(face, keyChain, nextStageOnFailure)
+{
+}
+
+void
+GuessFromIdentityName::start()
+{
+  std::cerr << "Trying to find home router based on the default identity name..." << std::endl;
+
+  Name identity = m_keyChain.getDefaultIdentity();
+
+  std::ostringstream serverName;
+  for (auto i = identity.rbegin(); i != identity.rend(); ++i) {
+    serverName << i->toUri() << ".";
+  }
+  serverName << "_homehub._autoconf.named-data.net";
+
+  try {
+    std::string hubUri = BaseDns::querySrvRr(serverName.str());
+    this->connectToHub(hubUri);
+  }
+  catch (const BaseDns::Error& e) {
+    m_nextStageOnFailure(std::string("Failed to find a home router based on the default identity "
+                                     "name (") + e.what() + ")");
+  }
+}
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/guess-from-identity-name.hpp b/tools/ndn-autoconfig/guess-from-identity-name.hpp
new file mode 100644
index 0000000..3e0008f
--- /dev/null
+++ b/tools/ndn-autoconfig/guess-from-identity-name.hpp
@@ -0,0 +1,74 @@
+/* -*- 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_TOOLS_NDN_AUTOCONFIG_GUESS_FROM_IDENTITY_NAME_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_GUESS_FROM_IDENTITY_NAME_HPP
+
+#include "base-dns.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+/**
+ * @brief Guessing home router based on the default identity name
+ *
+ * This stage assumes that user has configured default certificate using
+ * http://ndncert.named-data.net/
+ *
+ * - Request
+ *
+ *     The end host loads the default user identity (eg. /ndn/edu/ucla/cs/afanasev), and
+ *     converts it to DNS format.
+ *
+ *     The end host sends a DNS query for an SRV record of name _ndn._udp. + user identity in
+ *     DNS format + _homehub._auto-conf.named-data.net. For example:
+ *
+ *         _ndn._udp.afanasev.cs.ucla.edu.ndn._homehub._autoconf.named-data.net
+ *
+ * - Response
+ *
+ *     The DNS server should answer with an SRV record that contains the hostname and UDP port
+ *     number of the home NDN router of this user's site.
+ */
+class GuessFromIdentityName : public BaseDns
+{
+public:
+  /**
+   * @brief Create stage to guess home router based on the default identity name
+   * @sa Base::Base
+   */
+  GuessFromIdentityName(Face& face, KeyChain& keyChain,
+                        const NextStageCallback& nextStageOnFailure);
+
+  virtual void
+  start() DECL_OVERRIDE;
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_GUESSING_FROM_IDENTITY_NAME_HPP
diff --git a/tools/ndn-autoconfig/guess-from-search-domains.cpp b/tools/ndn-autoconfig/guess-from-search-domains.cpp
new file mode 100644
index 0000000..2f452e7
--- /dev/null
+++ b/tools/ndn-autoconfig/guess-from-search-domains.cpp
@@ -0,0 +1,53 @@
+/* -*- 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/>.
+ */
+
+#include "guess-from-search-domains.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+GuessFromSearchDomains::GuessFromSearchDomains(Face& face, KeyChain& keyChain,
+                                               const NextStageCallback& nextStageOnFailure)
+  : BaseDns(face, keyChain, nextStageOnFailure)
+{
+}
+
+void
+GuessFromSearchDomains::start()
+{
+  try {
+    std::string hubUri = BaseDns::querySrvRrSearch();
+    this->connectToHub(hubUri);
+  }
+  catch (const BaseDns::Error& e) {
+    m_nextStageOnFailure(std::string("Failed to find NDN router using default suffix DNS query (") +
+                         e.what() + ")");
+  }
+}
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/guess-from-search-domains.hpp b/tools/ndn-autoconfig/guess-from-search-domains.hpp
new file mode 100644
index 0000000..e2eaa5a
--- /dev/null
+++ b/tools/ndn-autoconfig/guess-from-search-domains.hpp
@@ -0,0 +1,67 @@
+/* -*- 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_TOOLS_NDN_AUTOCONFIG_GUESS_FROM_SEARCH_DOMAINS_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_GUESS_FROM_SEARCH_DOMAINS_HPP
+
+#include "base-dns.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+/**
+ * @brief Guessing home router based on DNS query with default suffix
+ *
+ * - Request
+ *
+ *     The end host sends a DNS query that is equivalent to this command:
+ *
+ *         dig +search +short +cmd +tries=2 +ndots=10 _ndn._udp srv
+ *
+ * - Response
+ *
+ *     The DNS server should answer with an SRV record that contains the hostname and UDP port
+ *     number of the NDN router.
+ */
+class GuessFromSearchDomains : public BaseDns
+{
+public:
+  /**
+   * @brief Create stage to guess home router based on DNS query with default suffix
+   * @sa Base::Base
+   */
+  GuessFromSearchDomains(Face& face, KeyChain& keyChain,
+                         const NextStageCallback& nextStageOnFailure);
+
+  virtual void
+  start() DECL_OVERRIDE;
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_GUESSING_FROM_SEARCH_DOMAINS_HPP
diff --git a/tools/ndn-autoconfig/main.cpp b/tools/ndn-autoconfig/main.cpp
new file mode 100644
index 0000000..d604c9d
--- /dev/null
+++ b/tools/ndn-autoconfig/main.cpp
@@ -0,0 +1,126 @@
+/* -*- 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/>.
+ */
+
+#include "version.hpp"
+
+#include "multicast-discovery.hpp"
+#include "guess-from-search-domains.hpp"
+#include "guess-from-identity-name.hpp"
+
+#include <boost/noncopyable.hpp>
+
+namespace ndn {
+namespace tools {
+
+class NdnAutoconfig : boost::noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  NdnAutoconfig()
+    : m_stage1(m_face, m_keyChain,
+               [&] (const std::string& errorMessage) {
+                 std::cerr << "Stage 1 failed: " << errorMessage << std::endl;
+                 m_stage2.start();
+               })
+    , m_stage2(m_face, m_keyChain,
+               [&] (const std::string& errorMessage) {
+                 std::cerr << "Stage 2 failed: " << errorMessage << std::endl;
+                 m_stage3.start();
+               })
+    , m_stage3(m_face, m_keyChain,
+               [&] (const std::string& errorMessage) {
+                 std::cerr << "Stage 3 failed: " << errorMessage << std::endl;
+                 throw Error("No more stages, automatic discovery failed");
+               })
+  {
+    m_stage1.start();
+  }
+
+  void
+  run()
+  {
+    m_face.processEvents();
+  }
+
+  static void
+  usage(const char* programName)
+  {
+    std::cout << "Usage:\n"
+              << "  " << programName  << " [options]\n"
+              << "\n"
+              << "Options:\n"
+              << "  [-h]  - print usage and exit\n"
+              << "  [-V]  - print version number and exit\n"
+              << std::endl;
+  }
+
+private:
+  Face m_face;
+  KeyChain m_keyChain;
+
+  autoconfig::MulticastDiscovery m_stage1;
+  autoconfig::GuessFromSearchDomains m_stage2;
+  autoconfig::GuessFromIdentityName m_stage3;
+};
+
+} // namespace tools
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  int opt;
+  const char* programName = argv[0];
+
+  while ((opt = getopt(argc, argv, "hV")) != -1) {
+    switch (opt) {
+    case 'h':
+      ndn::tools::NdnAutoconfig::usage(programName);
+      return 0;
+    case 'V':
+      std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+      return 0;
+    }
+  }
+
+  try {
+    ndn::tools::NdnAutoconfig autoConfigInstance;
+    autoConfigInstance.run();
+  }
+  catch (const std::exception& error) {
+    std::cerr << "ERROR: " << error.what() << std::endl;
+    return 1;
+  }
+  return 0;
+}
diff --git a/tools/ndn-autoconfig/multicast-discovery.cpp b/tools/ndn-autoconfig/multicast-discovery.cpp
new file mode 100644
index 0000000..59db21f
--- /dev/null
+++ b/tools/ndn-autoconfig/multicast-discovery.cpp
@@ -0,0 +1,194 @@
+/* -*- 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/>.
+ */
+
+#include "multicast-discovery.hpp"
+
+#include <ndn-cxx/util/segment-fetcher.hpp>
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+static const Name LOCALHOP_HUB_DISCOVERY_PREFIX = "/localhop/ndn-autoconf/hub";
+
+MulticastDiscovery::MulticastDiscovery(Face& face, KeyChain& keyChain,
+                                       const NextStageCallback& nextStageOnFailure)
+  : Base(face, keyChain, nextStageOnFailure)
+  , nRequestedRegs(0)
+  , nFinishedRegs(0)
+{
+}
+
+void
+MulticastDiscovery::start()
+{
+  std::cerr << "Trying multicast discovery..." << std::endl;
+
+  util::SegmentFetcher::fetch(m_face, Interest("/localhost/nfd/faces/list"),
+                              ndn::util::DontVerifySegment(),
+                              [this] (const ConstBufferPtr& data) {
+                                registerHubDiscoveryPrefix(data);
+                              },
+                              [this] (uint32_t code, const std::string& msg) {
+                                m_nextStageOnFailure(msg);
+                              });
+}
+
+void
+MulticastDiscovery::registerHubDiscoveryPrefix(const ConstBufferPtr& buffer)
+{
+  std::vector<uint64_t> multicastFaces;
+
+  size_t offset = 0;
+  while (offset < buffer->size()) {
+    Block block;
+    bool ok = Block::fromBuffer(buffer, offset, block);
+    if (!ok)
+      {
+        std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+        break;
+      }
+
+    offset += block.size();
+
+    nfd::FaceStatus faceStatus(block);
+
+    ndn::util::FaceUri uri(faceStatus.getRemoteUri());
+    if (uri.getScheme() == "udp4") {
+      namespace ip = boost::asio::ip;
+      boost::system::error_code ec;
+      ip::address address = ip::address::from_string(uri.getHost(), ec);
+
+      if (!ec && address.is_multicast()) {
+        multicastFaces.push_back(faceStatus.getFaceId());
+      }
+      else
+        continue;
+    }
+  }
+
+  if (multicastFaces.empty()) {
+    m_nextStageOnFailure("No multicast faces available, skipping multicast discovery stage");
+  }
+  else {
+    nfd::ControlParameters parameters;
+    parameters
+      .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
+      .setCost(1)
+      .setExpirationPeriod(time::seconds(30));
+
+    nRequestedRegs = multicastFaces.size();
+    nFinishedRegs = 0;
+
+    for (const auto& face : multicastFaces) {
+      parameters.setFaceId(face);
+      m_controller.start<nfd::RibRegisterCommand>(parameters,
+                                                  bind(&MulticastDiscovery::onRegisterSuccess,
+                                                       this),
+                                                  bind(&MulticastDiscovery::onRegisterFailure,
+                                                       this, _1, _2));
+    }
+  }
+}
+
+void
+MulticastDiscovery::onRegisterSuccess()
+{
+  ++nFinishedRegs;
+
+  if (nRequestedRegs == nFinishedRegs) {
+    MulticastDiscovery::setStrategy();
+  }
+}
+
+void
+MulticastDiscovery::onRegisterFailure(uint32_t code, const std::string& error)
+{
+  std::cerr << "ERROR: " << error << " (code: " << code << ")" << std::endl;
+  --nRequestedRegs;
+
+  if (nRequestedRegs == nFinishedRegs) {
+    if (nRequestedRegs > 0) {
+      MulticastDiscovery::setStrategy();
+    } else {
+      m_nextStageOnFailure("Failed to register " + LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() +
+                           " for all multicast faces, skipping multicast discovery stage");
+    }
+  }
+}
+
+void
+MulticastDiscovery::setStrategy()
+{
+  nfd::ControlParameters parameters;
+  parameters
+    .setName(LOCALHOP_HUB_DISCOVERY_PREFIX)
+    .setStrategy("/localhost/nfd/strategy/broadcast");
+
+  m_controller.start<nfd::StrategyChoiceSetCommand>(parameters,
+                                                    bind(&MulticastDiscovery::requestHubData, this),
+                                                    bind(&MulticastDiscovery::onSetStrategyFailure,
+                                                         this, _2));
+}
+
+void
+MulticastDiscovery::onSetStrategyFailure(const std::string& error)
+{
+  m_nextStageOnFailure("Failed to set broadcast strategy for " +
+                       LOCALHOP_HUB_DISCOVERY_PREFIX.toUri() + " namespace (" + error + "). "
+                       "Skipping multicast discovery stage");
+}
+
+void
+MulticastDiscovery::requestHubData()
+{
+  Interest interest(LOCALHOP_HUB_DISCOVERY_PREFIX);
+  interest.setInterestLifetime(time::milliseconds(4000)); // 4 seconds
+  interest.setMustBeFresh(true);
+
+  m_face.expressInterest(interest,
+                         bind(&MulticastDiscovery::onSuccess, this, _2),
+                         bind(m_nextStageOnFailure, "Timeout"));
+}
+
+void
+MulticastDiscovery::onSuccess(Data& data)
+{
+  const Block& content = data.getContent();
+  content.parse();
+
+  // Get Uri
+  Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
+  if (blockValue == content.elements_end()) {
+    m_nextStageOnFailure("Incorrect reply to multicast discovery stage");
+    return;
+  }
+  std::string hubUri(reinterpret_cast<const char*>(blockValue->value()), blockValue->value_size());
+  this->connectToHub(hubUri);
+}
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tools/ndn-autoconfig/multicast-discovery.hpp b/tools/ndn-autoconfig/multicast-discovery.hpp
new file mode 100644
index 0000000..72717fc
--- /dev/null
+++ b/tools/ndn-autoconfig/multicast-discovery.hpp
@@ -0,0 +1,94 @@
+/* -*- 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_TOOLS_NDN_AUTOCONFIG_MULTICAST_DISCOVERY_HPP
+#define NFD_TOOLS_NDN_AUTOCONFIG_MULTICAST_DISCOVERY_HPP
+
+#include "base.hpp"
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+
+/**
+ * @brief Multicast discovery stage
+ *
+ * - Request
+ *
+ *     The end host sends an Interest over a multicast face.
+ *
+ *     Interest Name is /localhop/ndn-autoconf/hub.
+ *
+ * - Response
+ *
+ *     A producer app on the HUB answer this Interest with a Data packet that contains a
+ *     TLV-encoded Uri block.  The value of this block is the URI for the HUB, preferably a
+ *     UDP tunnel.
+ */
+class MulticastDiscovery : public Base
+{
+public:
+  /**
+   * @brief Create multicast discovery stage
+   * @sa Base::Base
+   */
+  MulticastDiscovery(Face& face, KeyChain& keyChain, const NextStageCallback& nextStageOnFailure);
+
+  virtual void
+  start() DECL_OVERRIDE;
+
+private:
+  void
+  registerHubDiscoveryPrefix(const ConstBufferPtr& buffer);
+
+  void
+  onRegisterSuccess();
+
+  void
+  onRegisterFailure(uint32_t code, const std::string& error);
+
+  void
+  setStrategy();
+
+  void
+  onSetStrategyFailure(const std::string& error);
+
+  // Start to look for a hub (NDN hub discovery first stage)
+  void
+  requestHubData();
+
+  void
+  onSuccess(Data& data);
+
+private:
+  size_t nRequestedRegs;
+  size_t nFinishedRegs;
+};
+
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
+
+#endif // NFD_TOOLS_NDN_AUTOCONFIG_MULTICAST_DISCOVERY_HPP