face: Throw Face::Error if supplied Interest or Data exceeds maximum packet size

Maximum packet size is defined as MAX_NDN_PACKET_SIZE constant and does
not include any link-layer overhead.

Change-Id: I4c2787d38894096b43fb4e8ffc3175fa7ed24738
Refs: #1774
diff --git a/src/common.hpp b/src/common.hpp
index a35addb..9d15e49 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -37,6 +37,12 @@
 #define DEPRECATED(func) func
 #endif
 
+namespace ndn {
+
+const size_t MAX_NDN_PACKET_SIZE = 8800;
+
+} // namespace ndn
+
 #ifdef NDN_CXX_HAVE_CXX11
 
 #if defined(__GNUC__)
@@ -49,7 +55,6 @@
 #  error "NDN-CXX library is configured and compiled in C++11 mode, but the current compiler is not C++11 enabled"
 #endif // defined(__clang__) && (__cplusplus < 201103L)
 
-
 #include <memory>
 #include <functional>
 
diff --git a/src/face.cpp b/src/face.cpp
index 28482ba..f547f45 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -144,6 +144,10 @@
 {
   shared_ptr<Interest> interestToExpress = make_shared<Interest>(interest);
 
+  // Use `interestToExpress` to avoid wire format creation for the original Interest
+  if (interestToExpress->wireEncode().size() > MAX_NDN_PACKET_SIZE)
+    throw Error("Interest size exceeds maximum limit");
+
   // If the same ioService thread, dispatch directly calls the method
   m_ioService->dispatch(bind(&Impl::asyncExpressInterest, m_impl,
                              interestToExpress, onData, onTimeout));
@@ -165,6 +169,10 @@
 void
 Face::put(const Data& data)
 {
+  // Use original `data`, since wire format should already exist for the original Data
+  if (data.wireEncode().size() > MAX_NDN_PACKET_SIZE)
+    throw Error("Data size exceeds maximum limit");
+
   shared_ptr<const Data> dataPtr;
   try {
     dataPtr = data.shared_from_this();
@@ -305,43 +313,40 @@
 Face::processEvents(const time::milliseconds& timeout/* = time::milliseconds::zero()*/,
                     bool keepThread/* = false*/)
 {
-  try
-    {
-      if (timeout < time::milliseconds::zero())
-        {
-          // do not block if timeout is negative, but process pending events
-          m_ioService->poll();
-          return;
-        }
-
-      if (timeout > time::milliseconds::zero())
-        {
-          m_impl->m_processEventsTimeoutTimer->expires_from_now(time::milliseconds(timeout));
-          m_impl->m_processEventsTimeoutTimer->async_wait(&fireProcessEventsTimeout);
-        }
-
-      if (keepThread) {
-        // work will ensure that m_ioService is running until work object exists
-        m_impl->m_ioServiceWork = make_shared<boost::asio::io_service::work>(ref(*m_ioService));
+  try {
+    if (timeout < time::milliseconds::zero())
+      {
+        // do not block if timeout is negative, but process pending events
+        m_ioService->poll();
+        return;
       }
 
-      m_ioService->run();
-      m_ioService->reset(); // so it is possible to run processEvents again (if necessary)
+    if (timeout > time::milliseconds::zero())
+      {
+        m_impl->m_processEventsTimeoutTimer->expires_from_now(time::milliseconds(timeout));
+        m_impl->m_processEventsTimeoutTimer->async_wait(&fireProcessEventsTimeout);
+      }
+
+    if (keepThread) {
+      // work will ensure that m_ioService is running until work object exists
+      m_impl->m_ioServiceWork = make_shared<boost::asio::io_service::work>(ref(*m_ioService));
     }
-  catch (Face::ProcessEventsTimeout&)
-    {
-      // break
-      m_impl->m_ioServiceWork.reset();
-      m_ioService->reset();
-    }
-  catch (std::exception&)
-    {
-      m_impl->m_ioServiceWork.reset();
-      m_ioService->reset();
-      m_impl->m_pendingInterestTable.clear();
-      m_impl->m_registeredPrefixTable.clear();
-      throw;
-    }
+
+    m_ioService->run();
+    m_ioService->reset(); // so it is possible to run processEvents again (if necessary)
+  }
+  catch (Face::ProcessEventsTimeout&) {
+    // break
+    m_impl->m_ioServiceWork.reset();
+    m_ioService->reset();
+  }
+  catch (...) {
+    m_impl->m_ioServiceWork.reset();
+    m_ioService->reset();
+    m_impl->m_pendingInterestTable.clear();
+    m_impl->m_registeredPrefixTable.clear();
+    throw;
+  }
 }
 
 void
diff --git a/src/face.hpp b/src/face.hpp
index b915e83..9427b03 100644
--- a/src/face.hpp
+++ b/src/face.hpp
@@ -192,6 +192,8 @@
    * @param onTimeout (optional) A function object to call if the interest times out
    *
    * @return The pending interest ID which can be used with removePendingInterest
+   *
+   * @throws Error when Interest size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   const PendingInterestId*
   expressInterest(const Interest& interest,
@@ -206,6 +208,8 @@
    * @param onTimeout (optional) A function object to call if the interest times out
    *
    * @return Opaque pending interest ID which can be used with removePendingInterest
+   *
+   * @throws Error when Interest size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   const PendingInterestId*
   expressInterest(const Name& name,
@@ -446,6 +450,8 @@
    *             was created using make_shared<Data>(...).  Otherwise, put() will make an
    *             extra copy of the Data packet to ensure validity of published Data until
    *             asynchronous put() operation finishes.
+   *
+   * @throws Error when Data size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   void
   put(const Data& data);
diff --git a/src/transport/stream-transport.hpp b/src/transport/stream-transport.hpp
index 491f77a..9bb5b7b 100644
--- a/src/transport/stream-transport.hpp
+++ b/src/transport/stream-transport.hpp
@@ -28,20 +28,16 @@
 
 namespace ndn {
 
-const size_t MAX_LENGTH = 9000;
-
 template<class BaseTransport, class Protocol>
 class StreamTransportImpl
 {
 public:
-  typedef BaseTransport base_transport;
-  typedef Protocol      protocol;
-  typedef StreamTransportImpl<BaseTransport,Protocol> impl;
+  typedef StreamTransportImpl<BaseTransport,Protocol> Impl;
 
   typedef std::list<Block> BlockSequence;
   typedef std::list<BlockSequence> TransmissionQueue;
 
-  StreamTransportImpl(base_transport& transport, boost::asio::io_service& ioService)
+  StreamTransportImpl(BaseTransport& transport, boost::asio::io_service& ioService)
     : m_transport(transport)
     , m_socket(ioService)
     , m_inputBufferSize(0)
@@ -63,7 +59,7 @@
 
         if (!m_transmissionQueue.empty()) {
           boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
-                                   bind(&impl::handleAsyncWrite, this, _1,
+                                   bind(&Impl::handleAsyncWrite, this, _1,
                                         m_transmissionQueue.begin()));
         }
       }
@@ -87,19 +83,19 @@
   }
 
   void
-  connect(const typename protocol::endpoint& endpoint)
+  connect(const typename Protocol::endpoint& endpoint)
   {
     if (!m_connectionInProgress) {
       m_connectionInProgress = true;
 
-      // Wait at most 4 time::seconds to connect
+      // Wait at most 4 seconds to connect
       /// @todo Decide whether this number should be configurable
       m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
-      m_connectTimer.async_wait(bind(&impl::connectTimeoutHandler, this, _1));
+      m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
 
       m_socket.open();
       m_socket.async_connect(endpoint,
-                             bind(&impl::connectHandler, this, _1));
+                             bind(&Impl::connectHandler, this, _1));
     }
   }
 
@@ -141,8 +137,8 @@
       {
         m_transport.m_isExpectingData = true;
         m_inputBufferSize = 0;
-        m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_LENGTH), 0,
-                               bind(&impl::handle_async_receive, this, _1, _2));
+        m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
+                               bind(&Impl::handleAsyncReceive, this, _1, _2));
       }
   }
 
@@ -155,7 +151,7 @@
 
     if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
       boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
-                               bind(&impl::handleAsyncWrite, this, _1,
+                               bind(&Impl::handleAsyncWrite, this, _1,
                                     m_transmissionQueue.begin()));
     }
 
@@ -173,7 +169,7 @@
 
     if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
       boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
-                               bind(&impl::handleAsyncWrite, this, _1,
+                               bind(&Impl::handleAsyncWrite, this, _1,
                                     m_transmissionQueue.begin()));
     }
 
@@ -200,18 +196,18 @@
 
     if (!m_transmissionQueue.empty()) {
       boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
-                               bind(&impl::handleAsyncWrite, this, _1,
+                               bind(&Impl::handleAsyncWrite, this, _1,
                                     m_transmissionQueue.begin()));
     }
   }
 
-  inline bool
-  processAll(uint8_t* buffer, size_t& offset, size_t availableSize)
+  bool
+  processAll(uint8_t* buffer, size_t& offset, size_t nBytesAvailable)
   {
-    Block element;
-    while(offset < availableSize)
+    while (offset < nBytesAvailable)
       {
-        bool ok = Block::fromBuffer(buffer + offset, availableSize - offset, element);
+        Block element;
+        bool ok = Block::fromBuffer(buffer + offset, nBytesAvailable - offset, element);
         if (!ok)
           return false;
 
@@ -222,7 +218,7 @@
   }
 
   void
-  handle_async_receive(const boost::system::error_code& error, std::size_t bytes_recvd)
+  handleAsyncReceive(const boost::system::error_code& error, std::size_t nBytesRecvd)
   {
     if (error)
       {
@@ -235,12 +231,12 @@
         throw Transport::Error(error, "error while receiving data from socket");
       }
 
-    m_inputBufferSize += bytes_recvd;
+    m_inputBufferSize += nBytesRecvd;
     // do magic
 
     std::size_t offset = 0;
-    bool ok = processAll(m_inputBuffer, offset, m_inputBufferSize);
-    if (!ok && m_inputBufferSize == MAX_LENGTH && offset == 0)
+    bool hasProcessedSome = processAll(m_inputBuffer, offset, m_inputBufferSize);
+    if (!hasProcessedSome && m_inputBufferSize == MAX_NDN_PACKET_SIZE && offset == 0)
       {
         m_transport.close();
         throw Transport::Error(boost::system::error_code(),
@@ -262,15 +258,15 @@
       }
 
     m_socket.async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
-                                               MAX_LENGTH - m_inputBufferSize), 0,
-                           bind(&impl::handle_async_receive, this, _1, _2));
+                                               MAX_NDN_PACKET_SIZE - m_inputBufferSize), 0,
+                           bind(&Impl::handleAsyncReceive, this, _1, _2));
   }
 
 protected:
-  base_transport& m_transport;
+  BaseTransport& m_transport;
 
-  typename protocol::socket m_socket;
-  uint8_t m_inputBuffer[MAX_LENGTH];
+  typename Protocol::socket m_socket;
+  uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
   size_t m_inputBufferSize;
 
   TransmissionQueue m_transmissionQueue;
@@ -284,19 +280,17 @@
 class StreamTransportWithResolverImpl : public StreamTransportImpl<BaseTransport, Protocol>
 {
 public:
-  typedef BaseTransport base_transport;
-  typedef Protocol      protocol;
-  typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> impl;
+  typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> Impl;
 
-  StreamTransportWithResolverImpl(base_transport& transport, boost::asio::io_service& ioService)
-    : StreamTransportImpl<base_transport, protocol>(transport, ioService)
+  StreamTransportWithResolverImpl(BaseTransport& transport, boost::asio::io_service& ioService)
+    : StreamTransportImpl<BaseTransport, Protocol>(transport, ioService)
   {
   }
 
   void
   resolveHandler(const boost::system::error_code& error,
-                 typename protocol::resolver::iterator endpoint,
-                 const shared_ptr<typename protocol::resolver>&)
+                 typename Protocol::resolver::iterator endpoint,
+                 const shared_ptr<typename Protocol::resolver>&)
   {
     if (error)
       {
@@ -306,7 +300,7 @@
         throw Transport::Error(error, "Error during resolution of host or port");
       }
 
-    typename protocol::resolver::iterator end;
+    typename Protocol::resolver::iterator end;
     if (endpoint == end)
       {
         this->m_transport.close();
@@ -314,25 +308,25 @@
       }
 
     this->m_socket.async_connect(*endpoint,
-                                 bind(&impl::connectHandler, this, _1));
+                                 bind(&Impl::connectHandler, this, _1));
   }
 
   void
-  connect(const typename protocol::resolver::query& query)
+  connect(const typename Protocol::resolver::query& query)
   {
     if (!this->m_connectionInProgress) {
       this->m_connectionInProgress = true;
 
-      // Wait at most 4 time::seconds to connect
+      // Wait at most 4 seconds to connect
       /// @todo Decide whether this number should be configurable
       this->m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
-      this->m_connectTimer.async_wait(bind(&impl::connectTimeoutHandler, this, _1));
+      this->m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
 
-      // typename boost::asio::ip::basic_resolver< protocol > resolver;
-      shared_ptr<typename protocol::resolver> resolver =
-        make_shared<typename protocol::resolver>(ref(this->m_socket.get_io_service()));
+      // typename boost::asio::ip::basic_resolver< Protocol > resolver;
+      shared_ptr<typename Protocol::resolver> resolver =
+        make_shared<typename Protocol::resolver>(ref(this->m_socket.get_io_service()));
 
-      resolver->async_resolve(query, bind(&impl::resolveHandler, this, _1, _2, resolver));
+      resolver->async_resolve(query, bind(&Impl::resolveHandler, this, _1, _2, resolver));
     }
   }
 };
diff --git a/tests/integrated/test-faces.cpp b/tests/integrated/test-faces.cpp
index c86cd62..e7ce988 100644
--- a/tests/integrated/test-faces.cpp
+++ b/tests/integrated/test-faces.cpp
@@ -21,6 +21,7 @@
 
 #include "face.hpp"
 #include "util/scheduler.hpp"
+#include "security/key-chain.hpp"
 
 #include "boost-test.hpp"
 
@@ -136,6 +137,20 @@
                        bind(&FacesFixture::onData, this),
                        bind(&FacesFixture::onTimeout, this));
 
+  Name veryLongName;
+  for (size_t i = 0; i <= MAX_NDN_PACKET_SIZE / 10; i++)
+    {
+      veryLongName.append("0123456789");
+    }
+
+  BOOST_CHECK_THROW(face.expressInterest(veryLongName, OnData(), OnTimeout()), Face::Error);
+
+  shared_ptr<Data> data = make_shared<Data>(veryLongName);
+  data->setContent(reinterpret_cast<const uint8_t*>("01234567890"), 10);
+  KeyChain keyChain;
+  keyChain.sign(*data);
+  BOOST_CHECK_THROW(face.put(*data), Face::Error);
+
   BOOST_REQUIRE_NO_THROW(face.processEvents());
 
   BOOST_CHECK_EQUAL(nData, 1);