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);