tools: Refactoring ndn-autoconfig implementation

Change-Id: Ib92942ba40f04aaee83479177b9ba5d32a09af04
Refs: #2421
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