ndns-dig: adapt to the fact that NDN testbed does not have root zone

- Enable dig to set the start label during iterative process.
- Enable validation of intermediate results with an option to disable
it

Change-Id: I221e3c328c875ad06a1f8094f1004e68d9d46a57
diff --git a/src/clients/iterative-query-controller.cpp b/src/clients/iterative-query-controller.cpp
index c5e5104..be3f9f8 100644
--- a/src/clients/iterative-query-controller.cpp
+++ b/src/clients/iterative-query-controller.cpp
@@ -17,6 +17,7 @@
  * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "validator.hpp"
 #include "iterative-query-controller.hpp"
 #include "logger.hpp"
 #include <iostream>
@@ -30,14 +31,14 @@
                                                    const time::milliseconds& interestLifetime,
                                                    const QuerySucceedCallback& onSucceed,
                                                    const QueryFailCallback& onFail,
-                                                   Face& face)
+                                                   Face& face,
+                                                   Validator* validator)
   : QueryController(dstLabel, rrType, interestLifetime, onSucceed, onFail, face)
+  , m_validator(validator)
   , m_step(QUERY_STEP_QUERY_NS)
   , m_nFinishedComps(0)
   , m_nTryComps(1)
 {
-  if (m_dstLabel.size() == 1) // the first one is to Query RR directly
-    m_step = QUERY_STEP_QUERY_RR;
 }
 
 void
@@ -70,7 +71,22 @@
 
   NDNS_LOG_TRACE("[* -> *] get a " << ndnsType
                  << " Response: " << data.getName());
-
+  if (m_validator == nullptr) {
+    this->onDataValidated(make_shared<Data>(data), ndnsType);
+  }
+  else {
+    m_validator->validate(data,
+                          bind(&IterativeQueryController::onDataValidated, this, _1, ndnsType),
+                          [this] (const shared_ptr<const Data>& data, const std::string& str) {
+                            NDNS_LOG_WARN("data: " << data->getName() << " fails verification");
+                            this->abort();
+                          }
+                          );
+  }
+}
+void
+IterativeQueryController::onDataValidated(const shared_ptr<const Data>& data, NdnsType ndnsType)
+{
   switch (m_step) {
   case QUERY_STEP_QUERY_NS:
     if (ndnsType == NDNS_NACK) {
@@ -118,9 +134,9 @@
     this->express(this->makeLatestInterest()); // express new Expres
   else if (m_step == QUERY_STEP_ANSWER_STUB) {
     NDNS_LOG_TRACE("query ends: " << *this);
-    Response re = this->parseFinalResponse(data);
+    Response re = this->parseFinalResponse(*data);
     if (m_onSucceed != nullptr)
-      m_onSucceed(data, re);
+      m_onSucceed(*data, re);
     else
       NDNS_LOG_TRACE("succeed callback is nullptr");
   }
@@ -137,6 +153,9 @@
 void
 IterativeQueryController::start()
 {
+  if (m_dstLabel.size() == m_nFinishedComps)
+    m_step = QUERY_STEP_QUERY_RR;
+
   Interest interest = this->makeLatestInterest();
   express(interest);
 }
diff --git a/src/clients/iterative-query-controller.hpp b/src/clients/iterative-query-controller.hpp
index c97cc66..714a504 100644
--- a/src/clients/iterative-query-controller.hpp
+++ b/src/clients/iterative-query-controller.hpp
@@ -26,6 +26,7 @@
 #include "query-controller.hpp"
 #include "config.hpp"
 #include "common.hpp"
+#include "validator.hpp"
 
 #include <ndn-cxx/common.hpp>
 #include <ndn-cxx/data.hpp>
@@ -59,7 +60,7 @@
   IterativeQueryController(const Name& dstLabel, const name::Component& rrType,
                            const time::milliseconds& interestLifetime,
                            const QuerySucceedCallback& onSucceed, const QueryFailCallback& onFail,
-                           Face& face);
+                           Face& face, Validator* validator = nullptr);
 
   virtual void
   start();
@@ -75,6 +76,9 @@
   void
   onData(const ndn::Interest& interest, const Data& data);
 
+  void
+  onDataValidated(const shared_ptr<const Data>& data, NdnsType ndnsType);
+
   /**
    * @brief change the Controller state according to timeout. For current,
    * abort the query when timeout
@@ -98,13 +102,13 @@
   void
   express(const Interest& interest);
 
+public:
   void
-  setNFinishedComps(size_t finished)
+  setStartComponentIndex(size_t finished)
   {
     m_nFinishedComps = finished;
   }
 
-public:
   QueryStep
   getStep() const
   {
@@ -124,6 +128,7 @@
   }
 
 protected:
+  Validator* m_validator;
   /**
    * @brief current query step
    */
diff --git a/src/clients/query-controller.hpp b/src/clients/query-controller.hpp
index 778d563..7729ea2 100644
--- a/src/clients/query-controller.hpp
+++ b/src/clients/query-controller.hpp
@@ -68,6 +68,9 @@
   virtual bool
   hasEnded() = 0;
 
+  virtual void
+  setStartComponentIndex(size_t startIndex) = 0;
+
 public:
   ////////////////
   // getter
diff --git a/tests/unit/clients/iterative-query-controller.cpp b/tests/unit/clients/iterative-query-controller.cpp
index 0346419..7713a5b 100644
--- a/tests/unit/clients/iterative-query-controller.cpp
+++ b/tests/unit/clients/iterative-query-controller.cpp
@@ -98,7 +98,7 @@
   // IterativeQueryController is a whole process
   // the tester should not send Interest one by one
   // instead of starting it and letting it handle Interest/Data automatically
-  ctr->setNFinishedComps(1);
+  ctr->setStartComponentIndex(1);
 
   ctr->start();
 
diff --git a/tools/ndns-dig.cpp b/tools/ndns-dig.cpp
index 18142a0..29afa4f 100644
--- a/tools/ndns-dig.cpp
+++ b/tools/ndns-dig.cpp
@@ -35,26 +35,36 @@
 #include <memory>
 #include <string>
 
+NDNS_LOG_INIT("NdnsDig");
+
 namespace ndn {
 namespace ndns {
-NDNS_LOG_INIT("NdnsDig");
 
 class NdnsDig
 {
 public:
   NdnsDig(const Name& hint, const Name& dstLabel,
-          const name::Component& rrType)
+          const name::Component& rrType, bool shouldValidateIntermediate)
     : m_dstLabel(dstLabel)
     , m_rrType(rrType)
     , m_hint(hint)
     , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
     , m_validator(m_face)
-    , m_ctr(new IterativeQueryController(m_dstLabel, m_rrType, m_interestLifetime,
-                                         bind(&NdnsDig::onSucceed, this, _1, _2),
-                                         bind(&NdnsDig::onFail, this, _1, _2),
-                                         m_face))
+    , m_shouldValidateIntermediate(shouldValidateIntermediate)
     , m_hasError(false)
   {
+    if (m_shouldValidateIntermediate)
+      m_ctr = std::unique_ptr<IterativeQueryController>
+        (new IterativeQueryController(m_dstLabel, m_rrType, m_interestLifetime,
+                                      bind(&NdnsDig::onSucceed, this, _1, _2),
+                                      bind(&NdnsDig::onFail, this, _1, _2),
+                                      m_face, &m_validator));
+    else
+      m_ctr = std::unique_ptr<IterativeQueryController>
+        (new IterativeQueryController(m_dstLabel, m_rrType, m_interestLifetime,
+                                      bind(&NdnsDig::onSucceed, this, _1, _2),
+                                      bind(&NdnsDig::onFail, this, _1, _2),
+                                      m_face, nullptr));
   }
 
   void
@@ -70,7 +80,7 @@
       m_face.processEvents();
     }
     catch (std::exception& e) {
-      NDNS_LOG_FATAL("Error: Face fails to process events: " << e.what());
+      std::cerr << "Error: " << e.what();
       m_hasError = true;
     }
   }
@@ -82,6 +92,12 @@
     NDNS_LOG_TRACE("application stops.");
   }
 
+  void
+  setStartZone(const Name& start)
+  {
+    m_ctr->setStartComponentIndex(start.size());
+  }
+
 private:
   void
   onSucceed(const Data& data, const Response& response)
@@ -190,6 +206,7 @@
   Face m_face;
 
   Validator m_validator;
+  bool m_shouldValidateIntermediate;
   std::unique_ptr<QueryController> m_ctr;
 
   bool m_hasError;
@@ -211,6 +228,9 @@
   int ttl = 4;
   string rrType = "TXT";
   string dstFile;
+  bool shouldValidateIntermediate = true;
+  Name start("/ndn");
+
   try {
     namespace po = boost::program_options;
     po::variables_map vm;
@@ -224,6 +244,8 @@
       ("rrtype,t", po::value<std::string>(&rrType), "set request RR Type. default: TXT")
       ("dstFile,d", po::value<std::string>(&dstFile), "set output file of the received Data. "
        "if omitted, not print; if set to be -, print to stdout; else print to file")
+      ("start,s", po::value<Name>(&start)->default_value("/ndn"), "set first zone to query")
+      ("not-validate,n", "trigger not validate intermediate results")
       ;
 
     po::options_description hidden("Hidden Options");
@@ -255,17 +277,40 @@
       std::cout << visible << std::endl;
       return 0;
     }
+
+    if (!start.isPrefixOf(dstLabel)) {
+      std::cerr << "Error: start zone " << start << " is not prefix of the target label "
+                << dstLabel << std::endl;
+      return 1;
+    }
+
+    if (vm.count("not-validate")) {
+      shouldValidateIntermediate = false;
+    }
+
+    if (ttl < 0) {
+      std::cerr << "Error: ttl parameter cannot be negative" << std::endl;
+      return 1;
+    }
   }
   catch (const std::exception& ex) {
     std::cerr << "Parameter Error: " << ex.what() << std::endl;
-    return 0;
+    return 1;
   }
 
-  ndn::ndns::NdnsDig dig("", dstLabel, ndn::name::Component(rrType));
-  dig.setInterestLifetime(ndn::time::milliseconds(ttl * 1000));
+  NDNS_LOG_TRACE("validateIntermediate=" << shouldValidateIntermediate);
+
+  ndn::ndns::NdnsDig dig("", dstLabel, ndn::name::Component(rrType), !shouldValidateIntermediate);
+  dig.setInterestLifetime(ndn::time::seconds(ttl));
   dig.setDstFile(dstFile);
 
+  // Due to ndn testbed does not contain the root zone
+  // dig here starts from the TLD (Top-level Domain)
+  // precondition is that TLD : 1) only contains one component in its name; 2) its name is routable
+  dig.setStartZone(start);
+
   dig.run();
+
   if (dig.hasError())
     return 1;
   else