clients: add dig

Change-Id: I78822e9d68b36973362f0790f3c2f6e676cc6b94
diff --git a/src/clients/iterative-query-controller.cpp b/src/clients/iterative-query-controller.cpp
new file mode 100644
index 0000000..1d7af26
--- /dev/null
+++ b/src/clients/iterative-query-controller.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "iterative-query-controller.hpp"
+#include "logger.hpp"
+#include <iostream>
+
+namespace ndn {
+namespace ndns {
+NDNS_LOG_INIT("IterQueryCtr")
+
+IterativeQueryController::IterativeQueryController(const Name& dstLabel,
+                                                   const name::Component& rrType,
+                                                   const time::milliseconds& interestLifetime,
+                                                   const QuerySucceedCallback& onSucceed,
+                                                   const QueryFailCallback& onFail,
+                                                   Face& face)
+  : QueryController(dstLabel, rrType, interestLifetime, onSucceed, onFail, face)
+  , 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
+IterativeQueryController::onTimeout(const Interest& interest)
+{
+  NDNS_LOG_INFO("[* !! *] timeout happens: " << interest.getName());
+  NDNS_LOG_TRACE(*this);
+  this->abort();
+}
+
+void
+IterativeQueryController::abort()
+{
+  NDNS_LOG_DEBUG("abort iterative query");
+  if (m_onFail != nullptr)
+    m_onFail(0, "abort");
+  else
+    NDNS_LOG_TRACE("m_onFail is 0");
+
+}
+
+void
+IterativeQueryController::onData(const ndn::Interest& interest, const Data& data)
+{
+  NdnsType ndnsType = NDNS_RAW;
+  const Block* block = data.getMetaInfo().findAppMetaInfo(ndns::tlv::NdnsType);
+  if (block != nullptr) {
+    ndnsType = static_cast<NdnsType>(readNonNegativeInteger(*block));
+  }
+
+  NDNS_LOG_TRACE("[* -> *] get a " << ndnsType
+                 << " Response: " << data.getName());
+
+  switch (m_step) {
+  case QUERY_STEP_QUERY_NS:
+    if (ndnsType == NDNS_NACK) {
+      if (m_nFinishedComps + m_nTryComps == m_dstLabel.size() && m_rrType != label::NS_RR_TYPE)
+        m_step = QUERY_STEP_QUERY_RR;
+      else
+        m_step = QUERY_STEP_ANSWER_STUB;
+    }
+    else if (ndnsType == NDNS_RESP) {
+      if (m_nFinishedComps + m_nTryComps == m_dstLabel.size() && m_rrType == label::NS_RR_TYPE) {
+        // NS_RR_TYPE is different, since its record is stored at higher level
+        m_step = QUERY_STEP_ANSWER_STUB;
+      }
+      else {
+        m_nFinishedComps += m_nTryComps;
+        m_nTryComps = 1;
+      }
+    }
+    else if (ndnsType == NDNS_AUTH) {
+      m_nTryComps += 1;
+    }
+    else if (ndnsType == NDNS_RAW) {
+      std::ostringstream oss;
+      oss << *this;
+      NDNS_LOG_WARN("get unexpected Response: NDNS_RAW for QUERY_NS: " << oss.str());
+    }
+    //
+    if (m_nFinishedComps + m_nTryComps > m_dstLabel.size()) {
+      if (m_rrType == label::NS_RR_TYPE) {
+        m_step = QUERY_STEP_ANSWER_STUB;
+      }
+      else
+        m_step = QUERY_STEP_QUERY_RR;
+    }
+    break;
+  case QUERY_STEP_QUERY_RR:
+    m_step = QUERY_STEP_ANSWER_STUB;
+    break;
+  default:
+    NDNS_LOG_WARN("get unexpected Response at State " << *this);
+    // throw std::runtime_error("call makeLatestInterest() unexpected: " << *this);
+    // do not throw except since it may be duplicated Data
+    m_step = QUERY_STEP_ABORT;
+    break;
+  }
+
+  if (!hasEnded())
+    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);
+    if (m_onSucceed != nullptr)
+      m_onSucceed(data, re);
+    else
+      NDNS_LOG_TRACE("succeed callback is nullptr");
+  }
+  else if (m_step == QUERY_STEP_ABORT)
+    this->abort();
+}
+
+bool
+IterativeQueryController::hasEnded()
+{
+  return (m_step != QUERY_STEP_QUERY_NS && m_step != QUERY_STEP_QUERY_RR);
+}
+
+void
+IterativeQueryController::start()
+{
+  Interest interest = this->makeLatestInterest();
+  express(interest);
+}
+
+
+void
+IterativeQueryController::express(const Interest& interest)
+{
+  NDNS_LOG_DEBUG("[* <- *] send a Query: " << interest.getName());
+  m_face.expressInterest(interest,
+                         bind(&IterativeQueryController::onData, this, _1, _2),
+                         bind(&IterativeQueryController::onTimeout, this, _1)
+                         );
+}
+
+
+const Response
+IterativeQueryController::parseFinalResponse(const Data& data)
+{
+  Response re;
+  Name zone = m_dstLabel.getPrefix(m_nFinishedComps);
+  re.fromData("", zone, data);
+  return re;
+}
+
+const Interest
+IterativeQueryController::makeLatestInterest()
+{
+  // NDNS_LOG_TRACE("get latest Interest");
+  Query query;
+  //const Name& dstLabel = m_query.getRrLabel();
+
+  query.setZone(m_dstLabel.getPrefix(m_nFinishedComps));
+  query.setInterestLifetime(m_interestLifetime);
+  switch (m_step) {
+  case QUERY_STEP_QUERY_NS:
+    query.setQueryType(label::NDNS_ITERATIVE_QUERY);
+    query.setRrLabel(m_dstLabel.getSubName(m_nFinishedComps, m_nTryComps));
+    query.setRrType(label::NS_RR_TYPE);
+    break;
+  case QUERY_STEP_QUERY_RR:
+    if (m_rrType == label::CERT_RR_TYPE) {
+      // this only works for dsk, and ksk needs different mechanism
+      query.setQueryType(label::NDNS_CERT_QUERY);
+    }
+    else {
+      query.setQueryType(label::NDNS_ITERATIVE_QUERY);
+    }
+    query.setRrLabel(m_dstLabel.getSubName(m_nFinishedComps));
+    query.setRrType(m_rrType);
+    break;
+  default:
+    std::ostringstream oss;
+    oss << *this;
+    NDNS_LOG_WARN("unexpected state: " << oss.str());
+    throw std::runtime_error("call makeLatestInterest() unexpected: " + oss.str());
+  }
+
+
+  Interest interest = query.toInterest();
+  return interest;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const IterativeQueryController::QueryStep step)
+{
+  switch (step) {
+  case IterativeQueryController::QUERY_STEP_QUERY_NS:
+    os << "QueryNS";
+    break;
+  case IterativeQueryController::QUERY_STEP_QUERY_RR:
+    os << "QueryRR";
+    break;
+  case IterativeQueryController::QUERY_STEP_ANSWER_STUB:
+    os << "AnswerStub";
+    break;
+  case IterativeQueryController::QUERY_STEP_ABORT:
+    os << "Abort";
+    break;
+  default:
+    os << "UNKNOW";
+    break;
+  }
+  return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const IterativeQueryController& ctr)
+{
+  os << "InterativeQueryController: dstLabel=" << ctr.getDstLabel()
+     << " rrType=" << ctr.getRrType()
+     << " currentStep="  << ctr.getStep()
+     << " nFinishedComps=" << ctr.getNFinishedComps()
+     << " nTryComp=" << ctr.getNTryComps()
+    ;
+
+  return os;
+}
+
+} // namespace ndns
+} // namespace ndn
diff --git a/src/clients/iterative-query-controller.hpp b/src/clients/iterative-query-controller.hpp
new file mode 100644
index 0000000..c97cc66
--- /dev/null
+++ b/src/clients/iterative-query-controller.hpp
@@ -0,0 +1,154 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDNS_CLIENTS_ITERATIVE_QUERY_CONTROLLER_HPP
+#define NDNS_CLIENTS_ITERATIVE_QUERY_CONTROLLER_HPP
+
+#include "ndns-enum.hpp"
+#include "query.hpp"
+#include "response.hpp"
+#include "query-controller.hpp"
+#include "config.hpp"
+#include "common.hpp"
+
+#include <ndn-cxx/common.hpp>
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace ndns {
+
+/**
+ * @brief controller which iteratively query a target label
+ */
+class IterativeQueryController : public QueryController
+{
+public:
+  /**
+   * @brief indicates a step in an iterative query process.
+   * Iterative Query contains multiple steps which are listed here
+   */
+  enum QueryStep {
+    QUERY_STEP_QUERY_NS = 1, ///< to query the naming server, before querying NS & waiting for Data
+    QUERY_STEP_QUERY_RR, ///< to query RR, before querying RR & waiting for it Data
+    QUERY_STEP_ANSWER_STUB, ///< to answer to stub resolver, after getting final Response,
+                            ///< or NACK or timeout
+    QUERY_STEP_ABORT, ///< to abort the resolver process, if unexpected behavior happens
+    QUERY_STEP_UNKNOWN = 255
+  };
+
+public:
+  explicit
+  IterativeQueryController(const Name& dstLabel, const name::Component& rrType,
+                           const time::milliseconds& interestLifetime,
+                           const QuerySucceedCallback& onSucceed, const QueryFailCallback& onFail,
+                           Face& face);
+
+  virtual void
+  start();
+
+  /**
+   * @brief return false if the current status is not QUEYR_STEP_QUERY_NS
+   * or QUERY_STEP_QUERY_RR
+   */
+  virtual bool
+  hasEnded();
+
+NDNS_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  void
+  onData(const ndn::Interest& interest, const Data& data);
+
+  /**
+   * @brief change the Controller state according to timeout. For current,
+   * abort the query when timeout
+   */
+  void
+  onTimeout(const Interest& interest);
+
+  void
+  abort();
+
+  /**
+   * @brief get the Interest according to current Controller state.
+   * Only be valid on State QueryNS & QueryRR, or throw exception
+   */
+  const Interest
+  makeLatestInterest();
+
+  const Response
+  parseFinalResponse(const Data& data);
+
+  void
+  express(const Interest& interest);
+
+  void
+  setNFinishedComps(size_t finished)
+  {
+    m_nFinishedComps = finished;
+  }
+
+public:
+  QueryStep
+  getStep() const
+  {
+    return m_step;
+  }
+
+  size_t
+  getNFinishedComps() const
+  {
+    return m_nFinishedComps;
+  }
+
+  size_t
+  getNTryComps() const
+  {
+    return m_nTryComps;
+  }
+
+protected:
+  /**
+   * @brief current query step
+   */
+  QueryStep m_step;
+
+  /*
+   * the number of label that has been resolved successfully
+   * This also define the next AuthZone prefix
+   * AuthZone = m_query.getRrLabel().getPrefix(m_nFinishedComps)
+   */
+  size_t m_nFinishedComps;
+
+  /*
+   * used when query the KSK (key signing key), e.g., /net/ndnsim/ksk-1
+   */
+  size_t m_nTryComps;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const IterativeQueryController& iq);
+
+std::ostream&
+operator<<(std::ostream& os, const IterativeQueryController::QueryStep step);
+
+} // namespace ndns
+} // namespace ndn
+
+#endif // NDNS_CLIENTS_ITERATIVE_QUERY_HPP
diff --git a/src/clients/query-controller.cpp b/src/clients/query-controller.cpp
new file mode 100644
index 0000000..9cd3559
--- /dev/null
+++ b/src/clients/query-controller.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "query-controller.hpp"
+#include "logger.hpp"
+#include <iostream>
+
+namespace ndn {
+namespace ndns {
+
+QueryController::QueryController(const Name& dstLabel, const name::Component& rrType,
+                                 const time::milliseconds& interestLifetime,
+                                 const QuerySucceedCallback& onSucceed, const QueryFailCallback& onFail,
+                                 Face& face)
+  : m_dstLabel(dstLabel)
+  , m_rrType(rrType)
+  , m_interestLifetime(interestLifetime)
+  , m_onSucceed(onSucceed)
+  , m_onFail(onFail)
+  , m_face(face)
+{
+}
+
+std::ostream&
+operator<<(std::ostream& os, const QueryController& ctr)
+{
+  os << "QueryController: dstLabel=" << ctr.getDstLabel()
+     << " rrType=" << ctr.getRrType()
+    ;
+
+  return os;
+}
+} // namespace ndns
+} // namespace ndn
diff --git a/src/clients/query-controller.hpp b/src/clients/query-controller.hpp
new file mode 100644
index 0000000..778d563
--- /dev/null
+++ b/src/clients/query-controller.hpp
@@ -0,0 +1,111 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDNS_CLIENTS_QUERY_CONTROLLER_HPP
+#define NDNS_CLIENTS_QUERY_CONTROLLER_HPP
+
+#include "query.hpp"
+#include "response.hpp"
+
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/face.hpp>
+
+
+namespace ndn {
+namespace ndns {
+
+/**
+ * @brief callback function when succeeding getting the final Response
+ * @param[in] Data the Data packet which contains the Response, client should verify the packet
+ * @param[in] Response the Final Response converted from Data
+ */
+typedef function<void(const Data&, const Response&)> QuerySucceedCallback;
+
+/**
+ * @brief callback function when failing to get the final Response
+ */
+typedef function<void(uint32_t errCode, const std::string& errMsg)> QueryFailCallback;
+
+/**
+ * @brief a Query Controller interface
+ *
+ */
+class QueryController : noncopyable
+{
+public:
+  QueryController(const Name& dstLabel, const name::Component& rrType,
+                  const time::milliseconds& interestLifetime,
+                  const QuerySucceedCallback& onSucceed, const QueryFailCallback& onFail,
+                  Face& face);
+
+  /**
+   * @brief start query process.
+   */
+  virtual void
+  start() = 0;
+
+  /**
+   * @brief should keep sending query, or has ended yet,
+   */
+  virtual bool
+  hasEnded() = 0;
+
+public:
+  ////////////////
+  // getter
+
+  const Name&
+  getDstLabel() const
+  {
+    return m_dstLabel;
+  }
+
+  const time::milliseconds&
+  getInterestLifetime() const
+  {
+    return m_interestLifetime;
+  }
+
+  const name::Component&
+  getRrType() const
+  {
+    return m_rrType;
+  }
+
+protected:
+  const Name m_dstLabel;
+  const name::Component m_rrType;
+  const time::milliseconds m_interestLifetime;
+
+  const QuerySucceedCallback m_onSucceed;
+  const QueryFailCallback m_onFail;
+
+  Face& m_face;
+
+};
+
+std::ostream&
+operator<<(std::ostream& os, const QueryController& ctr);
+
+} // namespace ndns
+} // namespace ndn
+
+#endif // NDNS_CLIENTS_QUERY_CONTROLLER_HPP
diff --git a/tests/unit/clients/iterative-query-controller.cpp b/tests/unit/clients/iterative-query-controller.cpp
new file mode 100644
index 0000000..0346419
--- /dev/null
+++ b/tests/unit/clients/iterative-query-controller.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "clients/iterative-query-controller.hpp"
+#include "daemon/name-server.hpp"
+#include "logger.hpp"
+#include "../database-test-data.hpp"
+#include "../../boost-test.hpp"
+
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <boost/asio.hpp>
+
+namespace ndn {
+namespace ndns {
+namespace tests {
+NDNS_LOG_INIT("IterativeQueryControllerTest");
+
+class QueryControllerFixture : public DbTestData
+{
+public:
+  QueryControllerFixture()
+    : producerFace(ndn::util::makeDummyClientFace(io, { false, true }))
+    , consumerFace(ndn::util::makeDummyClientFace(io, { false, true }))
+    , validator(*producerFace)
+    , top(m_root.getName(), m_certName, *producerFace, m_session, m_keyChain, validator)
+    , net(m_net.getName(), m_certName, *producerFace, m_session, m_keyChain, validator)
+    , ndnsim(m_ndnsim.getName(), m_certName, *producerFace, m_session, m_keyChain, validator)
+  {
+    run();
+    producerFace->onInterest += [&] (const Interest& interest) { consumerFace->receive(interest); };
+    consumerFace->onInterest += [&] (const Interest& interest) { producerFace->receive(interest); };
+    producerFace->onData += [&] (const Data& data) { consumerFace->receive(data); };
+    consumerFace->onData += [&] (const Data& data) { producerFace->receive(data); };
+  }
+
+  void
+  run()
+  {
+    io.poll();
+    io.reset();
+  }
+
+public:
+  boost::asio::io_service io;
+  shared_ptr<ndn::util::DummyClientFace> producerFace;
+  shared_ptr<ndn::util::DummyClientFace> consumerFace;
+
+  Name hint;
+  Validator validator;
+  ndns::NameServer top;
+  ndns::NameServer net;
+  ndns::NameServer ndnsim;
+};
+
+
+BOOST_AUTO_TEST_SUITE(IterativeQueryController)
+
+BOOST_FIXTURE_TEST_CASE(Basic, QueryControllerFixture)
+{
+  using std::string;
+  using ndns::NameServer;
+
+  string hint;
+  Name name = m_ndnsim.getName();
+  Name dstLabel(name.append("www"));
+  name::Component rrType("TXT");
+  time::milliseconds lifetime(4000);
+
+  bool hasDataBack = false;
+  auto ctr = make_shared<ndns::IterativeQueryController>(
+    dstLabel, rrType, lifetime,
+    [&hasDataBack] (const Data&, const Response&) {
+      hasDataBack = true;
+      BOOST_CHECK(true);
+    },
+    [&hasDataBack] (uint32_t errCode, const std::string& errMsg) {
+      BOOST_CHECK(false);
+    },
+    *consumerFace);
+
+  // 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->start();
+
+  run();
+  BOOST_CHECK_EQUAL(hasDataBack, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace ndns
+} // namespace ndn
diff --git a/tools/ndns-dig.cpp b/tools/ndns-dig.cpp
new file mode 100644
index 0000000..06d7397
--- /dev/null
+++ b/tools/ndns-dig.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, 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.
+ *
+ * NDNS 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.
+ *
+ * NDNS 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
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ndns-label.hpp"
+#include "logger.hpp"
+#include "clients/response.hpp"
+#include "clients/query.hpp"
+#include "clients/iterative-query-controller.hpp"
+#include "validator.hpp"
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/face.hpp>
+#include <boost/program_options.hpp>
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <memory>
+#include <string>
+
+
+namespace ndn {
+namespace ndns {
+NDNS_LOG_INIT("NdnsDig");
+
+class NdnsDig
+{
+public:
+  NdnsDig(const Name& hint, const Name& dstLabel,
+          const name::Component& rrType)
+    : 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_hasError(false)
+  {
+  }
+
+  void
+  run()
+  {
+    NDNS_LOG_INFO(" =================================== "
+                  << "start to dig label = " << this->m_dstLabel
+                  << " for type = " << this->m_rrType
+                  << " =================================== ");
+
+    try {
+      m_ctr->start(); // non-block, may throw exception
+      m_face.processEvents();
+    }
+    catch (std::exception& e) {
+      NDNS_LOG_FATAL("Error: Face fails to process events: " << e.what());
+      m_hasError = true;
+    }
+  }
+
+  void
+  stop()
+  {
+    m_face.getIoService().stop();
+    NDNS_LOG_TRACE("application stops.");
+  }
+
+private:
+  void
+  onSucceed(const Data& data, const Response& response)
+  {
+    NDNS_LOG_INFO("Dig get following final Response (need verification):");
+    NDNS_LOG_INFO(response);
+    NDNS_LOG_TRACE("to verify the response");
+    m_validator.validate(data,
+                         bind(&NdnsDig::onDataValidated, this, _1),
+                         bind(&NdnsDig::onDataValidationFailed, this, _1, _2)
+                         );
+  }
+
+  void
+  onFail(uint32_t errCode, const std::string& errMsg)
+  {
+    NDNS_LOG_INFO("fail to get response: errCode=" << errCode << " msg=" << errMsg);
+    m_hasError = true;
+    this->stop();
+  }
+
+  void
+  onDataValidated(const shared_ptr<const Data>& data)
+  {
+    NDNS_LOG_INFO("final data pass verification");
+    this->stop();
+  }
+
+  void
+  onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
+  {
+    NDNS_LOG_INFO("final data does not pass verification");
+    m_hasError = true;
+    this->stop();
+  }
+
+public:
+  void
+  setInterestLifetime(const time::milliseconds& lifetime)
+  {
+    m_interestLifetime = lifetime;
+  }
+
+  const bool
+  hasError() const
+  {
+    return m_hasError;
+  }
+
+private:
+  Name m_dstLabel;
+  name::Component m_rrType;
+
+  Name m_hint;
+  Name m_certName;
+  time::milliseconds m_interestLifetime;
+
+  Face m_face;
+
+  Validator m_validator;
+  std::unique_ptr<QueryController> m_ctr;
+
+  bool m_hasError;
+};
+
+} // namespace ndns
+} // namespace ndn
+
+
+int
+main(int argc, char* argv[])
+{
+  ndn::ndns::log::init();
+  using std::string;
+  using namespace ndn;
+
+  Name dstLabel;
+  int ttl = 4;
+  string rrType = "TXT";
+
+  try {
+    namespace po = boost::program_options;
+    po::variables_map vm;
+
+    po::options_description generic("Generic Options");
+    generic.add_options()("help,h", "print help message");
+
+    po::options_description config("Configuration");
+    config.add_options()
+      ("timeout,T", po::value<int>(&ttl), "waiting seconds of query. default: 10 sec")
+      ("rrtype,t", po::value<std::string>(&rrType), "set request RR Type. default: TXT")
+      ;
+
+    po::options_description hidden("Hidden Options");
+    hidden.add_options()
+      ("name", po::value<Name>(&dstLabel), "name to be resolved")
+      ;
+    po::positional_options_description postion;
+    postion.add("name", 1);
+
+    po::options_description cmdline_options;
+    cmdline_options.add(generic).add(config).add(hidden);
+
+    po::options_description config_file_options;
+    config_file_options.add(config).add(hidden);
+
+    po::options_description visible("Allowed options");
+    visible.add(generic).add(config);
+
+    po::parsed_options parsed =
+      po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
+
+    po::store(parsed, vm);
+    po::notify(vm);
+
+    if (vm.count("help")) {
+      std::cout << "Usage: dig /name/to/be/resolved [-t rrType] [-T ttl]" << std::endl;
+      std::cout << visible << std::endl;
+      return 0;
+    }
+  }
+  catch (const std::exception& ex) {
+    std::cerr << "Parameter Error: " << ex.what() << std::endl;
+    return 0;
+  }
+
+  ndn::ndns::NdnsDig dig("", dstLabel, ndn::name::Component(rrType));
+  dig.setInterestLifetime(ndn::time::milliseconds(ttl * 1000));
+
+  dig.run();
+  if (dig.hasError())
+    return 1;
+  else
+    return 0;
+}