face: process face_system.tcp config section in TcpFactory

refs #3904

Change-Id: I509f07e6835a96c7ba05137529f29da76a6514fd
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 8ae2337..a3028b5 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,77 +25,93 @@
 
 #include "tcp-factory.hpp"
 #include "core/logger.hpp"
-#include "core/network-interface.hpp"
 
 namespace nfd {
+namespace face {
 
 namespace ip = boost::asio::ip;
 
 NFD_LOG_INIT("TcpFactory");
 
 void
-TcpFactory::prohibitEndpoint(const tcp::Endpoint& endpoint)
+TcpFactory::processConfig(OptionalConfigSection configSection,
+                          FaceSystem::ConfigContext& context)
 {
-  if (endpoint.address().is_v4() &&
-      endpoint.address() == ip::address_v4::any()) {
-    prohibitAllIpv4Endpoints(endpoint.port());
-  }
-  else if (endpoint.address().is_v6() &&
-           endpoint.address() == ip::address_v6::any()) {
-    prohibitAllIpv6Endpoints(endpoint.port());
+  // tcp
+  // {
+  //   listen yes
+  //   port 6363
+  //   enable_v4 yes
+  //   enable_v6 yes
+  // }
+
+  if (!configSection) {
+    if (!context.isDryRun && !m_channels.empty()) {
+      NFD_LOG_WARN("Cannot disable tcp4 and tcp6 channels after initialization");
+    }
+    return;
   }
 
-  NFD_LOG_TRACE("prohibiting TCP " << endpoint);
-  m_prohibitedEndpoints.insert(endpoint);
-}
+  bool wantListen = true;
+  uint16_t port = 6363;
+  bool enableV4 = true;
+  bool enableV6 = true;
 
-void
-TcpFactory::prohibitAllIpv4Endpoints(uint16_t port)
-{
-  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
-    for (const auto& addr : nic.ipv4Addresses) {
-      if (addr != ip::address_v4::any()) {
-        prohibitEndpoint(tcp::Endpoint(addr, port));
-      }
+  for (const auto& pair : *configSection) {
+    const std::string& key = pair.first;
+
+    if (key == "listen") {
+      wantListen = ConfigFile::parseYesNo(pair, "face_system.tcp");
+    }
+    else if (key == "port") {
+      port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.tcp");
+    }
+    else if (key == "enable_v4") {
+      enableV4 = ConfigFile::parseYesNo(pair, "face_system.tcp");
+    }
+    else if (key == "enable_v6") {
+      enableV6 = ConfigFile::parseYesNo(pair, "face_system.tcp");
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.tcp." + key));
     }
   }
-}
 
-void
-TcpFactory::prohibitAllIpv6Endpoints(uint16_t port)
-{
-  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
-    for (const auto& addr : nic.ipv6Addresses) {
-      if (addr != ip::address_v6::any()) {
-        prohibitEndpoint(tcp::Endpoint(addr, port));
+  if (!enableV4 && !enableV6) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error(
+      "IPv4 and IPv6 TCP channels have been disabled. Remove face_system.tcp section to disable "
+      "TCP channels or enable at least one channel type."));
+  }
+
+  if (!context.isDryRun) {
+    providedSchemes.insert("tcp");
+
+    if (enableV4) {
+      tcp::Endpoint endpoint(ip::tcp::v4(), port);
+      shared_ptr<TcpChannel> v4Channel = this->createChannel(endpoint);
+      if (wantListen && !v4Channel->isListening()) {
+        v4Channel->listen(context.addFace, nullptr);
       }
+      providedSchemes.insert("tcp4");
+    }
+    else if (providedSchemes.count("tcp4") > 0) {
+      NFD_LOG_WARN("Cannot close tcp4 channel after its creation");
+    }
+
+    if (enableV6) {
+      tcp::Endpoint endpoint(ip::tcp::v6(), port);
+      shared_ptr<TcpChannel> v6Channel = this->createChannel(endpoint);
+      if (wantListen && !v6Channel->isListening()) {
+        v6Channel->listen(context.addFace, nullptr);
+      }
+      providedSchemes.insert("tcp6");
+    }
+    else if (providedSchemes.count("tcp6") > 0) {
+      NFD_LOG_WARN("Cannot close tcp6 channel after its creation");
     }
   }
 }
 
-shared_ptr<TcpChannel>
-TcpFactory::createChannel(const tcp::Endpoint& endpoint)
-{
-  auto channel = findChannel(endpoint);
-  if (channel)
-    return channel;
-
-  channel = make_shared<TcpChannel>(endpoint);
-  m_channels[endpoint] = channel;
-  prohibitEndpoint(endpoint);
-
-  NFD_LOG_DEBUG("Channel [" << endpoint << "] created");
-  return channel;
-}
-
-shared_ptr<TcpChannel>
-TcpFactory::createChannel(const std::string& localIp, const std::string& localPort)
-{
-  tcp::Endpoint endpoint(ip::address::from_string(localIp),
-                         boost::lexical_cast<uint16_t>(localPort));
-  return createChannel(endpoint);
-}
-
 void
 TcpFactory::createFace(const FaceUri& uri,
                        ndn::nfd::FacePersistency persistency,
@@ -146,16 +162,75 @@
   onFailure(504, "No channels available to connect");
 }
 
+void
+TcpFactory::prohibitEndpoint(const tcp::Endpoint& endpoint)
+{
+  if (endpoint.address().is_v4() &&
+      endpoint.address() == ip::address_v4::any()) {
+    prohibitAllIpv4Endpoints(endpoint.port());
+  }
+  else if (endpoint.address().is_v6() &&
+           endpoint.address() == ip::address_v6::any()) {
+    prohibitAllIpv6Endpoints(endpoint.port());
+  }
+
+  NFD_LOG_TRACE("prohibiting TCP " << endpoint);
+  m_prohibitedEndpoints.insert(endpoint);
+}
+
+void
+TcpFactory::prohibitAllIpv4Endpoints(uint16_t port)
+{
+  ///\todo prohibited endpoints need to react to dynamic NIC changes
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const auto& addr : nic.ipv4Addresses) {
+      if (addr != ip::address_v4::any()) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
+    }
+  }
+}
+
+void
+TcpFactory::prohibitAllIpv6Endpoints(uint16_t port)
+{
+  ///\todo prohibited endpoints need to react to dynamic NIC changes
+  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
+    for (const auto& addr : nic.ipv6Addresses) {
+      if (addr != ip::address_v6::any()) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
+    }
+  }
+}
+
+shared_ptr<TcpChannel>
+TcpFactory::createChannel(const tcp::Endpoint& endpoint)
+{
+  auto channel = findChannel(endpoint);
+  if (channel)
+    return channel;
+
+  channel = make_shared<TcpChannel>(endpoint);
+  m_channels[endpoint] = channel;
+  prohibitEndpoint(endpoint);
+
+  NFD_LOG_DEBUG("Channel [" << endpoint << "] created");
+  return channel;
+}
+
+shared_ptr<TcpChannel>
+TcpFactory::createChannel(const std::string& localIp, const std::string& localPort)
+{
+  tcp::Endpoint endpoint(ip::address::from_string(localIp),
+                         boost::lexical_cast<uint16_t>(localPort));
+  return createChannel(endpoint);
+}
+
 std::vector<shared_ptr<const Channel>>
 TcpFactory::getChannels() const
 {
-  std::vector<shared_ptr<const Channel>> channels;
-  channels.reserve(m_channels.size());
-
-  for (const auto& i : m_channels)
-    channels.push_back(i.second);
-
-  return channels;
+  return getChannelsFromMap(m_channels);
 }
 
 shared_ptr<TcpChannel>
@@ -168,4 +243,5 @@
     return nullptr;
 }
 
+} // namespace face
 } // namespace nfd