face: Gracefully handle tlv::Error when received TLV block is malformed

Change-Id: I954b13d0e26fec7bc7fe23edcbc015dfac654ef8
Refs: #1494
diff --git a/daemon/face/face.cpp b/daemon/face/face.cpp
index d0d42db..46dbcf6 100644
--- a/daemon/face/face.cpp
+++ b/daemon/face/face.cpp
@@ -90,24 +90,29 @@
 bool
 Face::decodeAndDispatchInput(const Block& element)
 {
-  /// \todo Ensure lazy field decoding process
+  try {
+    /// \todo Ensure lazy field decoding process
 
-  if (element.type() == tlv::Interest)
-    {
-      shared_ptr<Interest> i = make_shared<Interest>();
-      i->wireDecode(element);
-      this->onReceiveInterest(*i);
-    }
-  else if (element.type() == tlv::Data)
-    {
-      shared_ptr<Data> d = make_shared<Data>();
-      d->wireDecode(element);
-      this->onReceiveData(*d);
-    }
-  else
+    if (element.type() == tlv::Interest)
+      {
+        shared_ptr<Interest> i = make_shared<Interest>();
+        i->wireDecode(element);
+        this->onReceiveInterest(*i);
+      }
+    else if (element.type() == tlv::Data)
+      {
+        shared_ptr<Data> d = make_shared<Data>();
+        d->wireDecode(element);
+        this->onReceiveData(*d);
+      }
+    else
+      return false;
+
+    return true;
+  }
+  catch (tlv::Error&) {
     return false;
-
-  return true;
+  }
 }
 
 } //namespace nfd
diff --git a/daemon/face/local-face.hpp b/daemon/face/local-face.hpp
index 118dce7..e1c34c3 100644
--- a/daemon/face/local-face.hpp
+++ b/daemon/face/local-face.hpp
@@ -138,47 +138,52 @@
 inline bool
 LocalFace::decodeAndDispatchInput(const Block& element)
 {
-  const Block& payload = ndn::nfd::LocalControlHeader::getPayload(element);
+  try {
+    const Block& payload = ndn::nfd::LocalControlHeader::getPayload(element);
 
-  // If received LocalControlHeader, but it is not enabled on the face
-  if ((&payload != &element) && !this->isLocalControlHeaderEnabled())
+    // If received LocalControlHeader, but it is not enabled on the face
+    if ((&payload != &element) && !this->isLocalControlHeaderEnabled())
+      return false;
+
+    if (payload.type() == tlv::Interest)
+      {
+        shared_ptr<Interest> i = make_shared<Interest>();
+        i->wireDecode(payload);
+        if (&payload != &element)
+          {
+            i->getLocalControlHeader().wireDecode(element,
+              false,
+              this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+          }
+
+        this->onReceiveInterest(*i);
+      }
+    else if (payload.type() == tlv::Data)
+      {
+        shared_ptr<Data> d = make_shared<Data>();
+        d->wireDecode(payload);
+
+        /// \todo Uncomment and correct the following when we have more
+        ///       options in LocalControlHeader that apply for incoming
+        ///       Data packets (if ever)
+        // if (&payload != &element)
+        //   {
+        //
+        //     d->getLocalControlHeader().wireDecode(element,
+        //       false,
+        //       false);
+        //   }
+
+        this->onReceiveData(*d);
+      }
+    else
+      return false;
+
+    return true;
+  }
+  catch (tlv::Error&) {
     return false;
-
-  if (payload.type() == tlv::Interest)
-    {
-      shared_ptr<Interest> i = make_shared<Interest>();
-      i->wireDecode(payload);
-      if (&payload != &element)
-        {
-          i->getLocalControlHeader().wireDecode(element,
-            false,
-            this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-        }
-
-      this->onReceiveInterest(*i);
-    }
-  else if (payload.type() == tlv::Data)
-    {
-      shared_ptr<Data> d = make_shared<Data>();
-      d->wireDecode(payload);
-
-      /// \todo Uncomment and correct the following when we have more
-      ///       options in LocalControlHeader that apply for incoming
-      ///       Data packets (if ever)
-      // if (&payload != &element)
-      //   {
-      //
-      //     d->getLocalControlHeader().wireDecode(element,
-      //       false,
-      //       false);
-      //   }
-
-      this->onReceiveData(*d);
-    }
-  else
-    return false;
-
-  return true;
+  }
 }
 
 inline bool
diff --git a/tests/daemon/face/dummy-stream-sender.hpp b/tests/daemon/face/dummy-stream-sender.hpp
new file mode 100644
index 0000000..af0e114
--- /dev/null
+++ b/tests/daemon/face/dummy-stream-sender.hpp
@@ -0,0 +1,116 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  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_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
+#define NFD_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
+
+#include "core/scheduler.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+
+template<class Protocol, class Dataset>
+class DummyStreamSender : public Dataset
+{
+public:
+  typedef typename Protocol::endpoint Endpoint;
+  typedef typename Protocol::socket Socket;
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  DummyStreamSender()
+    : socket(getGlobalIoService())
+  {
+  }
+
+  void
+  start(const Endpoint& endpoint)
+  {
+    socket.async_connect(endpoint,
+                         bind(&DummyStreamSender::onSuccessfullConnect, this, _1));
+  }
+
+  void
+  onSuccessfullConnect(const boost::system::error_code& error)
+  {
+    if (error)
+      {
+        throw Error("Connection aborted");
+      }
+
+    // This value may need to be adjusted if some dataset exceeds 100k
+    socket.set_option(boost::asio::socket_base::send_buffer_size(100000));
+
+    for (typename Dataset::Container::iterator i = this->data.begin();
+         i != this->data.end(); ++i)
+      {
+        socket.async_send(boost::asio::buffer(*i),
+                          bind(&DummyStreamSender::onSendFinished, this, _1, false));
+      }
+
+    socket.async_send(boost::asio::buffer(static_cast<const uint8_t*>(0), 0),
+                      bind(&DummyStreamSender::onSendFinished, this, _1, true));
+  }
+
+  void
+  onSendFinished(const boost::system::error_code& error, bool isFinal)
+  {
+    if (error) {
+      throw Error("Connection aborted");
+    }
+
+    if (isFinal) {
+      scheduler::schedule(ndn::time::seconds(1),
+                          bind(&DummyStreamSender::stop, this));
+    }
+  }
+
+  void
+  stop()
+  {
+    // Terminate test
+    boost::system::error_code error;
+    socket.shutdown(Socket::shutdown_both, error);
+    socket.close(error);
+  }
+
+public:
+  Socket socket;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_DUMMY_STREAM_SENDER_HPP
diff --git a/tests/daemon/face/packet-datasets.cpp b/tests/daemon/face/packet-datasets.cpp
new file mode 100644
index 0000000..86e01cd
--- /dev/null
+++ b/tests/daemon/face/packet-datasets.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  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 "packet-datasets.hpp"
+#include "tests/test-common.hpp"
+
+#include <boost/foreach.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(Datasets, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Currupted)
+{
+  {
+    typedef CorruptedInterest Dataset;
+    Dataset dataset;
+
+    BOOST_FOREACH(Dataset::Container::value_type& data, dataset.data)
+      {
+        Block block(data.buf(), data.size());
+
+        BOOST_CHECK_THROW((Interest(block)), tlv::Error);
+      }
+  }
+
+  {
+    typedef CorruptedInterestWithLocalControlHeader Dataset;
+    Dataset dataset;
+
+    BOOST_FOREACH(Dataset::Container::value_type& data, dataset.data)
+      {
+        Block block(data.buf(), data.size());
+
+        BOOST_CHECK_THROW(ndn::nfd::LocalControlHeader::getPayload(block), tlv::Error);
+      }
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/packet-datasets.hpp b/tests/daemon/face/packet-datasets.hpp
new file mode 100644
index 0000000..373f4e6
--- /dev/null
+++ b/tests/daemon/face/packet-datasets.hpp
@@ -0,0 +1,91 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  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_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
+#define NFD_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class CorruptedInterestWithLocalControlHeader
+{
+public:
+  typedef std::vector<ndn::Buffer> Container;
+
+  static std::string
+  getName()
+  {
+    return "CorruptedInterestWithLocalControlHeader";
+  }
+
+  CorruptedInterestWithLocalControlHeader()
+  {
+    static const uint8_t interest[] = {
+      0x50, 0x22, 0x51, 0x81, 0x0a, 0x05, 0x1d, 0x07, 0x14, 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61,
+      0x6c, 0x08, 0x03, 0x6e, 0x64, 0x6e, 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x09,
+      0x02, 0x12, 0x00, 0x0a, 0x01, 0x01
+    };
+
+    data.push_back(ndn::Buffer(interest, sizeof(interest)));
+  }
+public:
+  Container data;
+};
+
+class CorruptedInterest
+{
+public:
+  typedef std::vector<ndn::Buffer> Container;
+
+  static std::string
+  getName()
+  {
+    return "CorruptedInterest";
+  }
+
+  CorruptedInterest()
+  {
+    static const uint8_t interest[] = {
+      0x05, 0x1d, 0x07, 0x84, 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x08, 0x03, 0x6e, 0x64,
+      0x6e, 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x09, 0x02, 0x12, 0x00, 0x0a, 0x01,
+      0x01
+    };
+
+    data.push_back(ndn::Buffer(interest, sizeof(interest)));
+  }
+public:
+  Container data;
+};
+
+
+typedef boost::mpl::vector< CorruptedInterestWithLocalControlHeader,
+                            CorruptedInterest> CorruptedPackets;
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_PACKET_DATASETS_HPP
diff --git a/tests/daemon/face/tcp.cpp b/tests/daemon/face/tcp.cpp
index bc3ac30..166f257 100644
--- a/tests/daemon/face/tcp.cpp
+++ b/tests/daemon/face/tcp.cpp
@@ -23,10 +23,14 @@
  **/
 
 #include "face/tcp-factory.hpp"
+#include "core/resolver.hpp"
+#include "core/network-interface.hpp"
 #include <ndn-cxx/security/key-chain.hpp>
 
 #include "tests/test-common.hpp"
 #include "tests/limited-io.hpp"
+#include "dummy-stream-sender.hpp"
+#include "packet-datasets.hpp"
 
 namespace nfd {
 namespace tests {
@@ -223,28 +227,19 @@
 
   // integrated tests needs to check that TcpFace for non-loopback fails these tests...
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
 
   BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(10)) == LimitedIo::EXCEED_OPS,
                       "TcpChannel error: cannot send or receive Interest/Data packets");
@@ -255,10 +250,10 @@
   BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
   BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
 
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
 
   const FaceCounters& counters1 = face1->getCounters();
   BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
@@ -309,24 +304,15 @@
 
   // integrated tests needs to check that TcpFace for non-loopback fails these tests...
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
 
   BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(10)) == LimitedIo::EXCEED_OPS,
                       "TcpChannel error: cannot send or receive Interest/Data packets");
@@ -337,10 +323,10 @@
   BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
   BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
 
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
 }
 
 BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
@@ -434,6 +420,127 @@
 }
 
 
+
+
+class SimpleEndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  onFaceCreated(const shared_ptr<Face>& face)
+  {
+    face->onReceiveInterest +=
+      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
+    face->onReceiveData +=
+      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
+    face->onFail +=
+      bind(&SimpleEndToEndFixture::onFail, this, face);
+
+    if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+    }
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveInterest(const Interest& interest)
+  {
+    receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveData(const Data& data)
+  {
+    receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onFail(const shared_ptr<Face>& face)
+  {
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedDatas;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(LocalFaceCorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel = factory.createChannel("127.0.0.1", "20070");
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(channel->isListening(), true);
+
+  DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
+  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve("127.0.0.1", "20070"));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FaceCorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  // tests with non-local Face
+  std::string someIpv4Address;
+  std::list< shared_ptr<NetworkInterfaceInfo> > ifs = listNetworkInterfaces();
+  for (std::list< shared_ptr<NetworkInterfaceInfo> >::const_iterator i = ifs.begin();
+       i != ifs.end();
+       ++i)
+    {
+      if (!(*i)->isLoopback() && (*i)->isUp() && !(*i)->ipv4Addresses.empty())
+        {
+          someIpv4Address = (*i)->ipv4Addresses[0].to_string();
+          break;
+        }
+    }
+  if (someIpv4Address.empty())
+    {
+      BOOST_TEST_MESSAGE("Test with non-local Face cannot be run "
+                         "(no non-local interface with IPv4 address available)");
+      return;
+    }
+
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel = factory.createChannel(someIpv4Address, "20070");
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(channel->isListening(), true);
+
+
+  DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
+  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve(someIpv4Address, "20070"));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/daemon/face/unix-stream.cpp b/tests/daemon/face/unix-stream.cpp
index 4181aa1..06ff5d3 100644
--- a/tests/daemon/face/unix-stream.cpp
+++ b/tests/daemon/face/unix-stream.cpp
@@ -26,6 +26,8 @@
 
 #include "tests/test-common.hpp"
 #include "tests/limited-io.hpp"
+#include "dummy-stream-sender.hpp"
+#include "packet-datasets.hpp"
 
 namespace nfd {
 namespace tests {
@@ -178,28 +180,19 @@
   face2->onReceiveData +=
     bind(&EndToEndFixture::face2_onReceiveData, this, _1);
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
+  face2->sendData    (*data2    );
 
   BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(1)) == LimitedIo::EXCEED_OPS,
                       "UnixStreamChannel error: cannot send or receive Interest/Data packets");
@@ -209,10 +202,10 @@
   BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
   BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
 
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
 
   const FaceCounters& counters1 = face1->getCounters();
   BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
@@ -267,24 +260,15 @@
   face2->onReceiveInterest += bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
   face2->onReceiveData += bind(&EndToEndFixture::face2_onReceiveData, this, _1);
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
+  shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
+  shared_ptr<Data>     data2     = makeData("ndn:/XNBV796f");
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
+  face1->sendInterest(*interest1);
+  face1->sendData    (*data1    );
+  face2->sendInterest(*interest2);
+  face2->sendData    (*data2    );
 
   BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(1)) == LimitedIo::EXCEED_OPS,
                       "UnixStreamChannel error: cannot send or receive Interest/Data packets");
@@ -294,10 +278,10 @@
   BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
   BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
 
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2->getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2->getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1->getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
 }
 
 static inline void
@@ -329,19 +313,8 @@
   face2->onReceiveData +=
     bind(&EndToEndFixture::face2_onReceiveData, this, _1);
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
-
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
+  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
 
   face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
   face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
@@ -357,15 +330,15 @@
 
   ////////////////////////////////////////////////////////
 
-  interest1.setIncomingFaceId(11);
-  interest1.setNextHopFaceId(111);
+  interest1->setIncomingFaceId(11);
+  interest1->setNextHopFaceId(111);
 
-  face1->sendInterest(interest1);
+  face1->sendInterest(*interest1);
 
-  data1.setIncomingFaceId(22);
-  data1.getLocalControlHeader().setNextHopFaceId(222);
+  data1->setIncomingFaceId(22);
+  data1->getLocalControlHeader().setNextHopFaceId(222);
 
-  face1->sendData    (data1);
+  face1->sendData(*data1);
 
   //
 
@@ -387,14 +360,14 @@
   using namespace boost::asio;
 
   std::vector<const_buffer> interestWithHeader;
-  Block iHeader  = interest1.getLocalControlHeader().wireEncode(interest1, true, true);
-  Block iPayload = interest1.wireEncode();
+  Block iHeader  = interest1->getLocalControlHeader().wireEncode(*interest1, true, true);
+  Block iPayload = interest1->wireEncode();
   interestWithHeader.push_back(buffer(iHeader.wire(),  iHeader.size()));
   interestWithHeader.push_back(buffer(iPayload.wire(), iPayload.size()));
 
   std::vector<const_buffer> dataWithHeader;
-  Block dHeader  = data1.getLocalControlHeader().wireEncode(data1, true, true);
-  Block dPayload = data1.wireEncode();
+  Block dHeader  = data1->getLocalControlHeader().wireEncode(*data1, true, true);
+  Block dPayload = data1->wireEncode();
   dataWithHeader.push_back(buffer(dHeader.wire(),  dHeader.size()));
   dataWithHeader.push_back(buffer(dPayload.wire(), dPayload.size()));
 
@@ -417,6 +390,87 @@
   BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
 }
 
+
+class SimpleEndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  onFaceCreated(const shared_ptr<Face>& face)
+  {
+    face->onReceiveInterest +=
+      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
+    face->onReceiveData +=
+      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
+    face->onFail +=
+      bind(&SimpleEndToEndFixture::onFail, this, face);
+
+    if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+      static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
+        LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+    }
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveInterest(const Interest& interest)
+  {
+    receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onReceiveData(const Data& data)
+  {
+    receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  onFail(const shared_ptr<Face>& face)
+  {
+    limitedIo.afterOp();
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedDatas;
+};
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(CorruptedInput, Dataset,
+                                 CorruptedPackets, SimpleEndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel = factory.createChannel(CHANNEL_PATH1);
+  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
+                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
+
+  DummyStreamSender<stream_protocol, Dataset> sender;
+  sender.start(stream_protocol::endpoint(CHANNEL_PATH1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
+                                    time::seconds(1)) == LimitedIo::EXCEED_TIME,
+                      "Exception thrown for " + Dataset::getName());
+}
+
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests