Introduce Denial-of-Existence (DoE) for Nack response.

Note this commit changes how names are stored in the database, compliant
to the canonical order.

Change-Id: I9857aaefc1f7da08ff53eff43c8f8c8bd5443953
Refs: #4152
diff --git a/src/clients/iterative-query-controller.cpp b/src/clients/iterative-query-controller.cpp
index 2b56426..442f9dc 100644
--- a/src/clients/iterative-query-controller.cpp
+++ b/src/clients/iterative-query-controller.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017, Regents of the University of California.
+ * Copyright (c) 2014-2018, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -68,11 +68,22 @@
 
   NDNS_LOG_TRACE("[* -> *] get a " << contentType
                  << " Response: " << data.getName());
-  if (m_validator == nullptr) {
-    this->onDataValidated(data, contentType);
+
+  const Data* toBeValidatedData = nullptr;
+  if (contentType == NDNS_NACK) {
+    m_doe = Data(data.getContent().blockFromValue());
+    toBeValidatedData = &m_doe;
+    contentType = NDNS_DOE;
   }
   else {
-    m_validator->validate(data,
+    toBeValidatedData = &data;
+  }
+
+  if (m_validator == nullptr) {
+    this->onDataValidated(*toBeValidatedData, contentType);
+  }
+  else {
+    m_validator->validate(*toBeValidatedData,
                           bind(&IterativeQueryController::onDataValidated, this, _1, contentType),
                           [this] (const Data& data, const security::v2::ValidationError& err) {
                             NDNS_LOG_WARN("data: " << data.getName() << " fails verification");
@@ -81,6 +92,7 @@
                           );
   }
 }
+
 void
 IterativeQueryController::onDataValidated(const Data& data, NdnsContentType contentType)
 {
@@ -90,8 +102,18 @@
 
   switch (m_step) {
   case QUERY_STEP_QUERY_NS:
-    if (contentType == NDNS_NACK) {
-      m_step = QUERY_STEP_QUERY_RR;
+    if (contentType == NDNS_DOE) {
+      // check if requested record is absent by looking up in doe
+      if (isAbsentByDoe(data)) {
+        m_step = QUERY_STEP_QUERY_RR;
+      }
+      else {
+        std::ostringstream oss;
+        oss << "In onDataValidated, absence of record can not be infered from DoE.";
+        oss << " Last query:" << m_lastLabelType << " ";
+        oss << *this;
+        BOOST_THROW_EXCEPTION(std::runtime_error(oss.str()));
+      }
     }
     else if (contentType == NDNS_LINK) {
       Link link(data.wireEncode());
@@ -236,10 +258,27 @@
                                              + oss.str()));
   }
 
+  m_lastLabelType = Name(query.getRrLabel()).append(query.getRrType());
   Interest interest = query.toInterest();
   return interest;
 }
 
+bool
+IterativeQueryController::isAbsentByDoe(const Data& data) const
+{
+  std::pair<Name, Name> range = Response::wireDecodeDoe(data.getContent());
+
+  // should not be simple <, use our own definition of compare
+  if (range.first < m_lastLabelType && m_lastLabelType < range.second) {
+    return true;
+  }
+  if (range.second < range.first &&
+      (m_lastLabelType < range.first || range.second < m_lastLabelType)) {
+    return true;
+  }
+  return false;
+}
+
 std::ostream&
 operator<<(std::ostream& os, const IterativeQueryController::QueryStep step)
 {
diff --git a/src/clients/iterative-query-controller.hpp b/src/clients/iterative-query-controller.hpp
index 83bf856..a04424e 100644
--- a/src/clients/iterative-query-controller.hpp
+++ b/src/clients/iterative-query-controller.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017, Regents of the University of California.
+ * Copyright (c) 2014-2018, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -79,6 +79,11 @@
   void
   onData(const ndn::Interest& interest, const Data& data);
 
+  /**
+   * @brief called when any data are validated.
+   * It will unwrap the NACK record,
+   * so onSucceed callback will be called with only inner DoE
+   */
   void
   onDataValidated(const Data& data, NdnsContentType contentType);
 
@@ -130,6 +135,10 @@
     return m_nTryComps;
   }
 
+private:
+  bool
+  isAbsentByDoe(const Data& data) const;
+
 protected:
   security::v2::Validator* m_validator;
   /**
@@ -151,6 +160,8 @@
 
 private:
   Block m_lastLink;
+  Data m_doe;
+  Name m_lastLabelType;
   ndn::InMemoryStorage* m_nsCache;
 };
 
diff --git a/src/clients/response.cpp b/src/clients/response.cpp
index a23bc3c..5f223d3 100644
--- a/src/clients/response.cpp
+++ b/src/clients/response.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017, Regents of the University of California.
+ * Copyright (c) 2014-2018, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -93,6 +93,16 @@
   }
 }
 
+std::pair<Name, Name>
+Response::wireDecodeDoe(const Block& wire)
+{
+  wire.parse();
+  if (wire.elements().size() != 2) {
+    BOOST_THROW_EXCEPTION(Error("Unexpected number of components while decoding DOE record"));
+  }
+  return std::make_pair(Name(wire.elements().front()), Name(wire.elements().back()));
+}
+
 bool
 Response::fromData(const Name& zone, const Data& data)
 {
diff --git a/src/clients/response.hpp b/src/clients/response.hpp
index a33e42a..a97fb76 100644
--- a/src/clients/response.hpp
+++ b/src/clients/response.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017, Regents of the University of California.
+ * Copyright (c) 2014-2018, Regents of the University of California.
  *
  * This file is part of NDNS (Named Data Networking Domain Name Service).
  * See AUTHORS.md for complete list of NDNS authors and contributors.
@@ -40,7 +40,7 @@
 /**
  * @brief Default life time of resource record
  */
-const time::seconds DEFAULT_RR_FRESHNESS_PERIOD(3600);
+const time::seconds DEFAULT_RR_FRESHNESS_PERIOD = 3600_s;
 
 
 /**
@@ -50,6 +50,12 @@
 class Response
 {
 public:
+  class Error : public ndn::tlv::Error
+  {
+  public:
+    using ndn::tlv::Error::Error;
+  };
+
   Response();
 
   Response(const Name& zone, const name::Component& queryType);
@@ -112,6 +118,9 @@
   size_t
   wireEncode(EncodingImpl<T>& block) const;
 
+  static std::pair<Name, Name>
+  wireDecodeDoe(const Block& wire);
+
 public:
   ///////////////////////////////////////////////
   // getter and setter