interest: recognize Interest in Packet Format v0.3
Interest::wireDecode accepts both v0.2 and v0.3 formats,
but Interest::wireEncode only encodes into v0.2 format.
refs #4527
Change-Id: I784b6f1d5c0d93db33155efddc0180359d8cab74
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 8fbe92d..10961c5 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -156,7 +156,7 @@
* @brief Determine whether a TLV-TYPE is "critical" for evolvability purpose.
* @sa https://named-data.net/doc/NDN-packet-spec/0.3/tlv.html#considerations-for-evolvability-of-tlv-based-encoding
*/
-inline bool
+constexpr bool
isCriticalType(uint32_t type)
{
return type <= 31 || (type & 0x01);
diff --git a/src/interest.cpp b/src/interest.cpp
index 40e78e4..f08eeb2 100644
--- a/src/interest.cpp
+++ b/src/interest.cpp
@@ -121,49 +121,193 @@
m_wire = wire;
m_wire.parse();
- if (m_wire.type() != tlv::Interest)
- BOOST_THROW_EXCEPTION(Error("Unexpected TLV number when decoding Interest"));
+ if (m_wire.type() != tlv::Interest) {
+ BOOST_THROW_EXCEPTION(Error("expecting Interest element, got " + to_string(m_wire.type())));
+ }
+
+ if (!decode02()) {
+ decode03();
+ if (!hasNonce()) {
+ setNonce(getNonce());
+ }
+ }
+}
+
+bool
+Interest::decode02()
+{
+ auto ele = m_wire.elements_begin();
// Name
- m_name.wireDecode(m_wire.get(tlv::Name));
-
- // Selectors
- Block::element_const_iterator val = m_wire.find(tlv::Selectors);
- if (val != m_wire.elements_end()) {
- m_selectors.wireDecode(*val);
+ if (ele != m_wire.elements_end() && ele->type() == tlv::Name) {
+ m_name.wireDecode(*ele);
+ ++ele;
}
- else
+ else {
+ return false;
+ }
+
+ // Selectors?
+ if (ele != m_wire.elements_end() && ele->type() == tlv::Selectors) {
+ m_selectors.wireDecode(*ele);
+ ++ele;
+ }
+ else {
m_selectors = Selectors();
+ }
// Nonce
- val = m_wire.find(tlv::Nonce);
- if (val == m_wire.elements_end()) {
- BOOST_THROW_EXCEPTION(Error("Nonce element is missing"));
+ if (ele != m_wire.elements_end() && ele->type() == tlv::Nonce) {
+ uint32_t nonce = 0;
+ if (ele->value_size() != sizeof(nonce)) {
+ BOOST_THROW_EXCEPTION(Error("Nonce element is malformed"));
+ }
+ std::memcpy(&nonce, ele->value(), sizeof(nonce));
+ m_nonce = nonce;
+ ++ele;
}
- uint32_t nonce = 0;
- if (val->value_size() != sizeof(nonce)) {
- BOOST_THROW_EXCEPTION(Error("Nonce element is malformed"));
+ else {
+ return false;
}
- std::memcpy(&nonce, val->value(), sizeof(nonce));
- m_nonce = nonce;
- // InterestLifetime
- val = m_wire.find(tlv::InterestLifetime);
- if (val != m_wire.elements_end()) {
- m_interestLifetime = time::milliseconds(readNonNegativeInteger(*val));
+ // InterestLifetime?
+ if (ele != m_wire.elements_end() && ele->type() == tlv::InterestLifetime) {
+ m_interestLifetime = time::milliseconds(readNonNegativeInteger(*ele));
+ ++ele;
}
else {
m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
}
- // ForwardingHint
- val = m_wire.find(tlv::ForwardingHint);
- if (val != m_wire.elements_end()) {
- m_forwardingHint.wireDecode(*val, false);
+ // ForwardingHint?
+ if (ele != m_wire.elements_end() && ele->type() == tlv::ForwardingHint) {
+ m_forwardingHint.wireDecode(*ele, false);
+ ++ele;
}
else {
m_forwardingHint = DelegationList();
}
+
+ return ele == m_wire.elements_end();
+}
+
+void
+Interest::decode03()
+{
+ // Interest ::= INTEREST-TYPE TLV-LENGTH
+ // Name
+ // CanBePrefix?
+ // MustBeFresh?
+ // ForwardingHint?
+ // Nonce?
+ // InterestLifetime?
+ // HopLimit?
+ // Parameters?
+
+ bool hasName = false;
+ m_selectors = Selectors().setMaxSuffixComponents(1); // CanBePrefix=0
+ m_nonce.reset();
+ m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
+ m_forwardingHint = DelegationList();
+
+ int lastEle = 0; // last recognized element index, in spec order
+ for (const Block& ele : m_wire.elements()) {
+ switch (ele.type()) {
+ case tlv::Name: {
+ if (lastEle >= 1) {
+ BOOST_THROW_EXCEPTION(Error("Name element is out of order"));
+ }
+ hasName = true;
+ m_name.wireDecode(ele);
+ if (m_name.empty()) {
+ BOOST_THROW_EXCEPTION(Error("Name has zero name components"));
+ }
+ lastEle = 1;
+ break;
+ }
+ case tlv::CanBePrefix: {
+ if (lastEle >= 2) {
+ BOOST_THROW_EXCEPTION(Error("CanBePrefix element is out of order"));
+ }
+ if (ele.value_size() != 0) {
+ BOOST_THROW_EXCEPTION(Error("CanBePrefix element has non-zero TLV-LENGTH"));
+ }
+ m_selectors.setMaxSuffixComponents(-1);
+ lastEle = 2;
+ break;
+ }
+ case tlv::MustBeFresh: {
+ if (lastEle >= 3) {
+ BOOST_THROW_EXCEPTION(Error("MustBeFresh element is out of order"));
+ }
+ if (ele.value_size() != 0) {
+ BOOST_THROW_EXCEPTION(Error("MustBeFresh element has non-zero TLV-LENGTH"));
+ }
+ m_selectors.setMustBeFresh(true);
+ lastEle = 3;
+ break;
+ }
+ case tlv::ForwardingHint: {
+ if (lastEle >= 4) {
+ BOOST_THROW_EXCEPTION(Error("ForwardingHint element is out of order"));
+ }
+ m_forwardingHint.wireDecode(ele);
+ lastEle = 4;
+ break;
+ }
+ case tlv::Nonce: {
+ if (lastEle >= 5) {
+ BOOST_THROW_EXCEPTION(Error("Nonce element is out of order"));
+ }
+ uint32_t nonce = 0;
+ if (ele.value_size() != sizeof(nonce)) {
+ BOOST_THROW_EXCEPTION(Error("Nonce element is malformed"));
+ }
+ std::memcpy(&nonce, ele.value(), sizeof(nonce));
+ m_nonce = nonce;
+ lastEle = 5;
+ break;
+ }
+ case tlv::InterestLifetime: {
+ if (lastEle >= 6) {
+ BOOST_THROW_EXCEPTION(Error("InterestLifetime element is out of order"));
+ }
+ m_interestLifetime = time::milliseconds(readNonNegativeInteger(ele));
+ lastEle = 6;
+ break;
+ }
+ case tlv::HopLimit: {
+ if (lastEle >= 7) {
+ break; // HopLimit is non-critical, ignore out-of-order appearance
+ }
+ if (ele.value_size() != 1) {
+ BOOST_THROW_EXCEPTION(Error("HopLimit element is malformed"));
+ }
+ // TLV-VALUE is ignored
+ lastEle = 7;
+ break;
+ }
+ case tlv::Parameters: {
+ if (lastEle >= 8) {
+ BOOST_THROW_EXCEPTION(Error("Parameters element is out of order"));
+ }
+ // TLV-VALUE is ignored
+ lastEle = 8;
+ break;
+ }
+ default: {
+ if (tlv::isCriticalType(ele.type())) {
+ BOOST_THROW_EXCEPTION(Error("unrecognized element of critical type " +
+ to_string(ele.type())));
+ }
+ break;
+ }
+ }
+ }
+
+ if (!hasName) {
+ BOOST_THROW_EXCEPTION(Error("Name element is missing"));
+ }
}
std::string
diff --git a/src/interest.hpp b/src/interest.hpp
index d547daa..5fc1c42 100644
--- a/src/interest.hpp
+++ b/src/interest.hpp
@@ -73,12 +73,16 @@
size_t
wireEncode(EncodingImpl<TAG>& encoder) const;
- /** @brief Encode to a @c Block in NDN Packet Format v0.2.
+ /** @brief Encode to a @c Block.
+ *
+ * Normally, this function encodes to NDN Packet Format v0.2. However, if this instance has
+ * cached wire encoding (@c hasWire() is true), the cached encoding is returned and it might
+ * be in v0.3 format.
*/
const Block&
wireEncode() const;
- /** @brief Decode from @p wire in NDN Packet Format v0.2.
+ /** @brief Decode from @p wire in NDN Packet Format v0.2 or v0.3.
*/
void
wireDecode(const Block& wire);
@@ -372,6 +376,21 @@
}
private:
+ /** @brief Decode @c m_wire as NDN Packet Format v0.2.
+ * @retval true decoding successful.
+ * @retval false decoding failed due to structural error.
+ * @throw tlv::Error decoding error within a sub-element.
+ */
+ bool
+ decode02();
+
+ /** @brief Decode @c m_wire as NDN Packet Format v0.3.
+ * @throw tlv::Error decoding error.
+ */
+ void
+ decode03();
+
+private:
Name m_name;
Selectors m_selectors;
mutable optional<uint32_t> m_nonce;
diff --git a/tests/unit-tests/interest.t.cpp b/tests/unit-tests/interest.t.cpp
index 4d970d5..2860551 100644
--- a/tests/unit-tests/interest.t.cpp
+++ b/tests/unit-tests/interest.t.cpp
@@ -24,6 +24,7 @@
#include "security/digest-sha256.hpp"
#include "security/signature-sha256-with-rsa.hpp"
+#include "block-literal.hpp"
#include "boost-test.hpp"
#include "identity-management-fixture.hpp"
@@ -37,13 +38,17 @@
BOOST_AUTO_TEST_CASE(DefaultConstructor)
{
Interest i;
+ BOOST_CHECK(!i.hasWire());
BOOST_CHECK_EQUAL(i.getName(), "/");
- BOOST_CHECK(i.getSelectors().empty());
- BOOST_CHECK_EQUAL(i.hasNonce(), false);
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ BOOST_CHECK(i.getForwardingHint().empty());
+ BOOST_CHECK(!i.hasNonce());
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(!i.hasSelectors());
}
-BOOST_AUTO_TEST_CASE(EncodeDecodeBasic)
+BOOST_AUTO_TEST_CASE(EncodeDecode02Basic)
{
const uint8_t WIRE[] = {
0x05, 0x1c, // Interest
@@ -69,7 +74,7 @@
BOOST_CHECK_EQUAL(i1, i2);
}
-BOOST_AUTO_TEST_CASE(EncodeDecodeFull)
+BOOST_AUTO_TEST_CASE(EncodeDecode02Full)
{
const uint8_t WIRE[] = {
0x05, 0x31, // Interest
@@ -108,58 +113,144 @@
BOOST_CHECK_EQUAL(i1, i2);
}
-BOOST_AUTO_TEST_CASE(WireDecodeReset) // checks wireDecode resets all fields
+class Decode03Fixture
{
- Interest i1;
- i1.setName("/test");
- i1.setMinSuffixComponents(100);
- i1.setNonce(10);
- i1.setInterestLifetime(10_s);
+protected:
+ Decode03Fixture()
+ {
+ // initialize all elements to non-empty, to verify wireDecode clears them
+ i.setName("/A");
+ i.setForwardingHint({{10309, "/F"}});
+ i.setNonce(0x03d645a8);
+ i.setInterestLifetime(18554_ms);
+ i.setPublisherPublicKeyLocator(Name("/K"));
+ }
- Interest i2(i1.wireEncode());
- BOOST_CHECK_EQUAL(i2.getName().toUri(), "/test");
- BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 10_s);
- BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), 100);
- BOOST_CHECK_EQUAL(i2.getNonce(), 10);
-
- i2.wireDecode(Interest().wireEncode());
- BOOST_CHECK_EQUAL(i2.getName().toUri(), "/");
- BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
- BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), -1);
- BOOST_WARN_NE(i2.getNonce(), 10);
-}
-
-BOOST_AUTO_TEST_CASE(DecodeNoName)
-{
- Block b(tlv::Interest);
- b.push_back(makeBinaryBlock(tlv::Nonce, "FISH", 4));
- b.encode();
-
+protected:
Interest i;
- BOOST_CHECK_THROW(i.wireDecode(b), tlv::Error);
-}
+};
-BOOST_AUTO_TEST_CASE(DecodeNoNonce)
+BOOST_FIXTURE_TEST_SUITE(Decode03, Decode03Fixture)
+
+BOOST_AUTO_TEST_CASE(Minimal)
{
- Block b(tlv::Interest);
- b.push_back(Name("/YvzNKtPWh").wireEncode());
- b.encode();
+ i.wireDecode("0505 0703080149"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), false);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ BOOST_CHECK(i.getForwardingHint().empty());
+ BOOST_CHECK(i.hasNonce()); // a random nonce is generated
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i.getPublisherPublicKeyLocator().empty());
- Interest i;
- BOOST_CHECK_THROW(i.wireDecode(b), tlv::Error);
+ BOOST_CHECK(!i.hasWire()); // nonce generation resets wire encoding
+
+ // modify then re-encode as v0.2 format
+ i.setNonce(0x54657c95);
+ BOOST_CHECK(i.wireEncode() == "0510 0703080149 09030E0101 0A04957C6554"_block);
}
-BOOST_AUTO_TEST_CASE(DecodeBadNonce)
+BOOST_AUTO_TEST_CASE(Full)
{
- Block b(tlv::Interest);
- b.push_back(Name("/BJzEHVxDJ").wireEncode());
- b.push_back(makeBinaryBlock(tlv::Nonce, "SKY", 3));
- b.encode();
+ i.wireDecode("053B FC00 0703080149 FC00 2100 FC00 1200 "
+ "FC00 1E0B(1F09 1E023E15 0703080148) FC00 0A044ACB1E4C "
+ "FC00 0C0276A1 FC00 2201D6 FC00 2304C0C1C2C3 FC00"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), true);
+ BOOST_CHECK_EQUAL(i.getForwardingHint(), DelegationList({{15893, "/H"}}));
+ BOOST_CHECK(i.hasNonce());
+ BOOST_CHECK_EQUAL(i.getNonce(), 0x4c1ecb4a);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), 30369_ms);
+ // HopLimit=214 is not stored
+ // Parameters="C0C1C2C3" is not stored
- Interest i;
- BOOST_CHECK_THROW(i.wireDecode(b), tlv::Error);
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 59);
+
+ // modify then re-encode as v0.2 format
+ i.setName("/J");
+ BOOST_CHECK(i.wireEncode() ==
+ "0520 070308014A 09021200 0A044ACB1E4C 0C0276A1 "
+ "1E0B(1F09 1E023E15 0703080148)"_block);
}
+BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
+{
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 2100 0703080149 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 1200 2100 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1E0B(1F09 1E023E15 0703080148) 1200 "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 0A044ACB1E4C "
+ "1E0B(1F09 1E023E15 0703080148) 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0C0276A1 0A044ACB1E4C 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 2201D6 0C0276A1 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "052F 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3 2304C0C1C2C3"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(HopLimitOutOfOrder)
+{
+ // HopLimit is non-critical, its out-of-order appearances are ignored
+ i.wireDecode("0514 0703080149 2201D6 2200 2304C0C1C2C3 22020101"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ // HopLimit=214 is not stored
+ // Parameters="C0C1C2C3" is not stored
+}
+
+BOOST_AUTO_TEST_CASE(NameMissing)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0500"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("0502 1200"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameEmpty)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0502 0700"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadCanBePrefix)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 210102"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadMustBeFresh)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 120102"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadNonce)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 0A00"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("050A 0703080149 0A0304C263"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("050C 0703080149 0A05EFA420B262"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 FB00"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode03
+
// ---- matching ----
BOOST_AUTO_TEST_CASE(MatchesData)