face: UDP face/channel/factory

Change-Id: I4683b45378637133982efd23edd16a0c35148948
refs #1189
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
new file mode 100644
index 0000000..8dafb57
--- /dev/null
+++ b/daemon/face/udp-factory.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "udp-factory.hpp"
+#include "core/global-io.hpp"
+#include "core/resolver.hpp"
+
+namespace nfd {
+
+using namespace boost::asio;
+  
+NFD_LOG_INIT("UdpFactory");
+
+UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
+  : m_defaultPort(defaultPort)
+{
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::createChannel(const udp::Endpoint& endpoint,
+                          const time::Duration& timeout)
+{
+  NFD_LOG_DEBUG("Creating unicast " << endpoint);
+  
+  shared_ptr<UdpChannel> channel = findChannel(endpoint);
+  if (static_cast<bool>(channel))
+    return channel;
+
+  
+  //checking if the endpoint is already in use for multicast face 
+  shared_ptr<MulticastUdpFace> multicast = findMulticastFace(endpoint);
+  if (static_cast<bool>(multicast))
+    throw Error("Cannot create the requested UDP unicast channel, local "
+                "endpoint is already allocated for a UDP multicast face");
+  
+  if (endpoint.address().is_multicast()) {
+    throw Error("This method is only for unicast channel. The provided "
+                "endpoint is multicast. Use createMulticastFace to "
+                "create a multicast face");
+  }
+
+  channel = make_shared<UdpChannel>(boost::cref(endpoint),
+                                    timeout);
+  m_channels[endpoint] = channel;
+
+  return channel;
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::createChannel(const std::string& localHost,
+                          const std::string& localPort,
+                          const time::Duration& timeout)
+{
+  return createChannel(UdpResolver::syncResolve(localHost, localPort),
+                       timeout);
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
+                                       const udp::Endpoint& multicastEndpoint)
+{
+  //checking if the local and musticast endpoint are already in use for a multicast face
+  shared_ptr<MulticastUdpFace> multicastFace = findMulticastFace(localEndpoint);
+  if (static_cast<bool>(multicastFace)) {
+    if (multicastFace->getMulticastGroup() == multicastEndpoint)
+      return multicastFace;
+    else
+      throw Error("Cannot create the requested UDP multicast face, local "
+                  "endpoint is already allocated for a UDP multicast face "
+                  "on a different multicast group");
+  }
+  
+  //checking if the local endpoint is already in use for an unicast channel
+  shared_ptr<UdpChannel> unicast = findChannel(localEndpoint);
+  if (static_cast<bool>(unicast)) {
+    throw Error("Cannot create the requested UDP multicast face, local "
+                "endpoint is already allocated for a UDP unicast channel");
+  }
+
+  if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
+    throw Error("IPv6 multicast is not supported yet. Please provide an IPv4 address");
+  }
+  
+  if (localEndpoint.port() != multicastEndpoint.port()) {
+    throw Error("Cannot create the requested UDP multicast face, "
+                "both endpoints should have the same port number. ");
+  }
+
+  if (!multicastEndpoint.address().is_multicast()) {
+    throw Error("Cannot create the requested UDP multicast face, "
+                "the multicast group given as input is not a multicast address");
+  }
+
+  shared_ptr<ip::udp::socket> clientSocket =
+    make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
+ 
+  clientSocket->open(multicastEndpoint.protocol());
+
+  clientSocket->set_option(ip::udp::socket::reuse_address(true));
+
+  try {
+    clientSocket->bind(multicastEndpoint);
+
+    if (localEndpoint.address() != ip::address::from_string("0.0.0.0")) {
+      clientSocket->set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
+    }
+    clientSocket->set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
+                                                       localEndpoint.address().to_v4()));
+  }
+  catch (boost::system::system_error& e) {
+    std::stringstream msg;
+    msg << "Failed to properly configure the socket, check the address (" << e.what() << ")";
+    throw Error(msg.str());
+  }
+
+  clientSocket->set_option(ip::multicast::enable_loopback(false));
+
+  multicastFace = make_shared<MulticastUdpFace>(boost::cref(clientSocket));
+  multicastFace->onFail += bind(&UdpFactory::afterFaceFailed, this, localEndpoint);
+
+  m_multicastFaces[localEndpoint] = multicastFace;
+
+  return multicastFace;
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const std::string& localIp,
+                                const std::string& multicastIp,
+                                const std::string& multicastPort)
+{
+  
+  return createMulticastFace(UdpResolver::syncResolve(localIp,
+                                                      multicastPort),
+                             UdpResolver::syncResolve(multicastIp,
+                                                      multicastPort));
+}
+
+void
+UdpFactory::createFace(const FaceUri& uri,
+                       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();
+  
+  UdpResolver::asyncResolve(uri.getDomain(),
+                            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;
+  }
+  
+  // very simple logic for now
+  
+  for (ChannelMap::iterator channel = m_channels.begin();
+       channel != m_channels.end();
+       ++channel)
+  {
+    if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
+        (channel->first.address().is_v6() && endpoint.address().is_v6()))
+    {
+      channel->second->connect(endpoint, onCreated);
+      return;
+    }
+  }
+  onConnectFailed("No channels available to connect to "
+                  + boost::lexical_cast<std::string>(endpoint));
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::findChannel(const udp::Endpoint& localEndpoint)
+{
+  ChannelMap::iterator i = m_channels.find(localEndpoint);
+  if (i != m_channels.end())
+    return i->second;
+  else
+    return shared_ptr<UdpChannel>();
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
+{
+  MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
+  if (i != m_multicastFaces.end())
+    return i->second;
+  else
+    return shared_ptr<MulticastUdpFace>();
+}
+
+void
+UdpFactory::afterFaceFailed(udp::Endpoint& endpoint)
+{
+  NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
+  m_multicastFaces.erase(endpoint);
+}
+
+
+} // namespace nfd