tools: improve user experience by choosing best certificate
automatically and print final response in a pretty way

ndns-update
 - pick the best certificate to sign the Update automatically
 - support multiple token for content option

ndns-dig
 - enhance output option
 - change behavior of iterative query controller when NS query is NACKed

logger
 - set default logger output to std::cerr

Change-Id: Ib06981c224ccad2a122a4505c84a60977184a55b
diff --git a/log4cxx.properties.sample.in b/log4cxx.properties.sample.in
index 15a36d7..7b0185f 100644
--- a/log4cxx.properties.sample.in
+++ b/log4cxx.properties.sample.in
@@ -1,5 +1,6 @@
 log4j.rootLogger=TRACE, A1
 log4j.appender.A1=org.apache.log4j.ConsoleAppender
+log4j.appender.A1.target=System.err
 log4j.appender.A1.layout=org.apache.log4j.PatternLayout
 
 # Print the date in ISO 8601 format
diff --git a/src/clients/iterative-query-controller.cpp b/src/clients/iterative-query-controller.cpp
index 1d7af26..c5e5104 100644
--- a/src/clients/iterative-query-controller.cpp
+++ b/src/clients/iterative-query-controller.cpp
@@ -74,10 +74,7 @@
   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;
+      m_step = QUERY_STEP_QUERY_RR;
     }
     else if (ndnsType == NDNS_RESP) {
       if (m_nFinishedComps + m_nTryComps == m_dstLabel.size() && m_rrType == label::NS_RR_TYPE) {
diff --git a/src/daemon/name-server.cpp b/src/daemon/name-server.cpp
index b02a423..f796e13 100644
--- a/src/daemon/name-server.cpp
+++ b/src/daemon/name-server.cpp
@@ -138,8 +138,7 @@
                          bind(&NameServer::doUpdate, this, interest.shared_from_this(), data),
                          [this] (const shared_ptr<const Data>& data, const std::string& msg) {
                            NDNS_LOG_WARN("Ignoring update that did not pass the verification. "
-                                         << "Validator cannot fetch certificate from the face "
-                                         << "that used by validator itself to send Interest");
+                                         << "Check the root certificate")
                          });
   }
 }
diff --git a/src/logger.cpp b/src/logger.cpp
index 274e10e..e4f5b9b 100644
--- a/src/logger.cpp
+++ b/src/logger.cpp
@@ -46,7 +46,7 @@
   else {
     PatternLayoutPtr   layout(new PatternLayout("%d{HH:mm:ss} %p %c{1} - %m%n"));
     ConsoleAppenderPtr appender(new ConsoleAppender(layout));
-
+    appender->setTarget("System.cerr");
     BasicConfigurator::configure(appender);
     Logger::getRootLogger()->setLevel(log4cxx::Level::getInfo());
   }
diff --git a/src/util/util.cpp b/src/util/util.cpp
new file mode 100644
index 0000000..1b14896
--- /dev/null
+++ b/src/util/util.cpp
@@ -0,0 +1,60 @@
+/* -*- 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 "util.hpp"
+#include <ndn-cxx/security/cryptopp.hpp>
+
+namespace ndn {
+namespace ndns {
+
+NdnsType
+toNdnsType(const std::string& str)
+{
+  if (str == "resp")
+    return NDNS_RESP;
+  else if (str == "nack")
+    return NDNS_NACK;
+  else if (str == "auth")
+    return NDNS_AUTH;
+  else if (str == "raw")
+    return NDNS_RAW;
+  else
+    return NDNS_UNKNOWN;
+}
+
+void
+output(const Data& data, std::ostream& os, const bool isPretty)
+{
+  using namespace CryptoPP;
+  const Block& block = data.wireEncode();
+  if (!isPretty) {
+    StringSource ss(block.wire(), block.size(), true,
+                    new Base64Encoder(new FileSink(os), true, 64));
+  }
+  else {
+    os << "Name: " << data.getName().toUri() << std::endl;
+    os << "KeyLocator: " << data.getSignature().getKeyLocator().getName().toUri() << std::endl;
+    StringSource ss(block.wire(), block.size(), true,
+                    new Base64Encoder(new FileSink(os), true, 64));
+    os << std::endl;
+  }
+}
+
+} // namespace ndns
+} // namespace ndn
diff --git a/src/util/util.hpp b/src/util/util.hpp
index 2733cf1..7acf496 100644
--- a/src/util/util.hpp
+++ b/src/util/util.hpp
@@ -20,23 +20,23 @@
 #ifndef NDNS_UTIL_UTIL_HPP
 #define NDNS_UTIL_UTIL_HPP
 
+#include "ndns-enum.hpp"
+#include <ndn-cxx/data.hpp>
+
 namespace ndn {
 namespace ndns {
 
 NdnsType
-toNdnsType(const std::string& str)
-{
-  if (str == "resp")
-    return NDNS_RESP;
-  else if (str == "nack")
-    return NDNS_NACK;
-  else if (str == "auth")
-    return NDNS_AUTH;
-  else if (str == "raw")
-    return NDNS_RAW;
-  else
-    return NDNS_UNKNOWN;
-}
+toNdnsType(const std::string& str);
+
+/**
+ * @brief print the data in a flexible way
+ * @param data The data to be printed
+ * @param os the ostream that received printed message
+ * @param isPretty whether to use pretty way
+ */
+void
+output(const Data& data, std::ostream& os, const bool isPretty);
 
 } // namespace ndns
 } // namespace ndn
diff --git a/tools/ndns-daemon.cpp b/tools/ndns-daemon.cpp
index 5937562..b40f6f0 100644
--- a/tools/ndns-daemon.cpp
+++ b/tools/ndns-daemon.cpp
@@ -218,11 +218,12 @@
     // NFD does not to forward Interests to the face it was received from.
     // If the name server and its validator share same face,
     // the validator cannot be forwarded to the name server itself
+    // For current, two faces are used here.
+
     // refs: http://redmine.named-data.net/issues/2206
     // @TODO enhance validator to get the certificate from the local db if it has
 
     NdnsDaemon daemon(configFile, face, validatorFace);
-
     face.processEvents();
   }
   catch (std::exception& e) {
diff --git a/tools/ndns-dig.cpp b/tools/ndns-dig.cpp
index 06d7397..18142a0 100644
--- a/tools/ndns-dig.cpp
+++ b/tools/ndns-dig.cpp
@@ -23,6 +23,7 @@
 #include "clients/query.hpp"
 #include "clients/iterative-query-controller.hpp"
 #include "validator.hpp"
+#include "util/util.hpp"
 
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/face.hpp>
@@ -34,7 +35,6 @@
 #include <memory>
 #include <string>
 
-
 namespace ndn {
 namespace ndns {
 NDNS_LOG_INIT("NdnsDig");
@@ -86,8 +86,49 @@
   void
   onSucceed(const Data& data, const Response& response)
   {
-    NDNS_LOG_INFO("Dig get following final Response (need verification):");
+    NDNS_LOG_INFO("Dig get following Response (need verification):");
+    Name name = Name().append(response.getZone()).append(response.getRrLabel());
+    if (name == m_dstLabel && m_rrType == response.getRrType()) {
+      NDNS_LOG_INFO("This is the final response returned by zone=" << response.getZone()
+                    << " and NdnsType=" << response.getNdnsType()
+                    << ". It contains " << response.getRrs().size() << " RR(s)");
+
+      std::string msg;
+      size_t i = 0;
+      for (const auto& rr : response.getRrs()) {
+        try {
+          msg = std::string(reinterpret_cast<const char*>(rr.value()), rr.value_size());
+          NDNS_LOG_INFO("succeed to get the info from RR[" << i << "]"
+                        "type=" << rr.type() << " content=" << msg);
+        }
+        catch (std::exception& e) {
+          NDNS_LOG_INFO("error to get the info from RR[" << i << "]"
+                        "type=" << rr.type());
+        }
+        ++i;
+      }
+    }
+    else {
+      NDNS_LOG_INFO("[* !! *] This is not final response.The target Label: "
+                    << m_dstLabel << " may not exist");
+    }
+
+    if (m_dstFile.empty()) {
+      ;
+    }
+    else if (m_dstFile == "-") {
+      output(data, std::cout, true);
+    }
+    else {
+      NDNS_LOG_INFO("output Data packet to " << m_dstFile << " with BASE64 encoding format");
+      std::filebuf fb;
+      fb.open(m_dstFile, std::ios::out);
+      std::ostream os(&fb);
+      output(data, os, false);
+    }
+
     NDNS_LOG_INFO(response);
+
     NDNS_LOG_TRACE("to verify the response");
     m_validator.validate(data,
                          bind(&NdnsDig::onDataValidated, this, _1),
@@ -95,6 +136,7 @@
                          );
   }
 
+
   void
   onFail(uint32_t errCode, const std::string& errMsg)
   {
@@ -131,6 +173,12 @@
     return m_hasError;
   }
 
+  void
+  setDstFile(const std::string& dstFile)
+  {
+    m_dstFile = dstFile;
+  }
+
 private:
   Name m_dstLabel;
   name::Component m_rrType;
@@ -145,6 +193,7 @@
   std::unique_ptr<QueryController> m_ctr;
 
   bool m_hasError;
+  std::string m_dstFile;
 };
 
 } // namespace ndns
@@ -161,7 +210,7 @@
   Name dstLabel;
   int ttl = 4;
   string rrType = "TXT";
-
+  string dstFile;
   try {
     namespace po = boost::program_options;
     po::variables_map vm;
@@ -171,8 +220,10 @@
 
     po::options_description config("Configuration");
     config.add_options()
-      ("timeout,T", po::value<int>(&ttl), "waiting seconds of query. default: 10 sec")
+      ("timeout,T", po::value<int>(&ttl), "waiting seconds of query. default: 4 sec")
       ("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")
       ;
 
     po::options_description hidden("Hidden Options");
@@ -188,7 +239,10 @@
     po::options_description config_file_options;
     config_file_options.add(config).add(hidden);
 
-    po::options_description visible("Allowed options");
+    po::options_description visible("Usage: ndns-dig /name/to/be/resolved [-t rrType] [-T ttl]"
+                                    "[-d dstFile]\n"
+                                    "Allowed options");
+
     visible.add(generic).add(config);
 
     po::parsed_options parsed =
@@ -198,7 +252,6 @@
     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;
     }
@@ -210,6 +263,7 @@
 
   ndn::ndns::NdnsDig dig("", dstLabel, ndn::name::Component(rrType));
   dig.setInterestLifetime(ndn::time::milliseconds(ttl * 1000));
+  dig.setDstFile(dstFile);
 
   dig.run();
   if (dig.hasError())
diff --git a/tools/ndns-update.cpp b/tools/ndns-update.cpp
index bb08adf..6caf991 100644
--- a/tools/ndns-update.cpp
+++ b/tools/ndns-update.cpp
@@ -76,6 +76,7 @@
                   << " with rrLabel = " << this->m_update.getRrLabel()
                   << " and rrType = " << this->m_rrType
                   << " =================================== ");
+    NDNS_LOG_INFO("certificate to sign the data is: " << m_certName);
 
     Interest interest = this->makeUpdateInterest();
     NDNS_LOG_TRACE("[* <- *] send Update: " << m_update);
@@ -210,6 +211,12 @@
     return m_hasError;
   }
 
+  const Response&
+  getUpdate() const
+  {
+    return m_update;
+  }
+
 private:
   name::Component m_queryType; ///< NDNS or KEY
   name::Component m_rrType;
@@ -245,8 +252,9 @@
   string rrType = "TXT";
   string ndnsTypeStr = "resp";
   Name certName;
-  string content;
+  std::vector<string> contents;
   string contentFile;
+  string dstFile;
   ndn::Block block;
   try {
     namespace po = boost::program_options;
@@ -263,7 +271,8 @@
       ("ndnsType,n", po::value<string>(&ndnsTypeStr), "Set the ndnsType of the resource record. "
        "Potential values are [resp|nack|auth|raw]. Default: resp")
       ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
-      ("content,o", po::value<string>(&content), "set the content of the RR")
+      ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
+       "set the content of the RR")
       ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
        " content of the RR in base64 format")
       ;
@@ -293,7 +302,7 @@
     po::notify(vm);
 
     if (vm.count("help")) {
-      std::cout << "Usage: ndns-update zone rrLabel [-t rrType] [-w waitingSec] "
+      std::cout << "Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
         "[-H hint] [-n NdnsType] [-c cert] [-f contentFile]|[-o content]" << std::endl;
       std::cout << visible << std::endl;
       return 0;
@@ -301,7 +310,22 @@
 
     KeyChain keyChain;
     if (certName.empty()) {
-      certName = keyChain.getDefaultCertificateName().toUri();
+      Name name = Name().append(zone).append(rrLabel);
+      // choosing the longest match of the identity who also have default certificate
+      for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
+        Name tmp = name.getPrefix(i - 1);
+        if (keyChain.doesIdentityExist(tmp)) {
+          try {
+            certName = keyChain.getDefaultCertificateNameForIdentity(tmp);
+            break;
+          }
+          catch (std::exception&) {
+            // If it cannot get a default certificate from one identity,
+            // just ignore this one try next identity.
+            ;
+          }
+        }
+      }
     }
     else {
       if (!keyChain.doesCertificateExist(certName)) {
@@ -310,8 +334,13 @@
       }
     }
 
+    if (certName.empty()) {
+      std::cerr << "cannot figure out the certificate automatically. "
+                << "please set it with -c CERT_NAEME" << std::endl;
+    }
+
     if (vm.count("content") && vm.count("contentFile")) {
-      std::cerr <<"both content and contentFile are set. Only one is allowed" << std::endl;
+      std::cerr << "both content and contentFile are set. Only one is allowed" << std::endl;
       return 0;
     }
 
@@ -319,10 +348,6 @@
       shared_ptr<ndn::Data> data = ndn::io::load<ndn::Data>(contentFile);
       block = data->wireEncode();
     }
-    else {
-      if (!content.empty())
-        block = ndn::dataBlock(ndn::ndns::tlv::RrData, content.c_str(), content.size());
-    }
   }
   catch (const std::exception& ex) {
     std::cerr << "Parameter Error: " << ex.what() << std::endl;
@@ -336,11 +361,27 @@
                     ndnsType, certName, face);
   update.setInterestLifetime(ndn::time::seconds(ttl));
 
-  if (!block.empty()) {
-    if (ndnsType == ndn::ndns::NDNS_RAW)
-      update.setUpdateAppContent(block);
-    else
-      update.addUpdateRr(block);
+  if (!contentFile.empty()) {
+    if (!block.empty()) {
+      if (ndnsType == ndn::ndns::NDNS_RAW)
+        update.setUpdateAppContent(block);
+      else {
+        update.addUpdateRr(block);
+      }
+    }
+  }
+  else {
+    if (ndnsType == ndn::ndns::NDNS_RAW) {
+      // since NDNS_RAW's message tlv type cannot be decided, here we stop this option
+      std::cerr << "--content (-o) does not support NDNS-RAW Response" << std::endl;
+      return 0;
+    }
+    else {
+      for (const auto& content : contents) {
+        block = ndn::dataBlock(ndn::ndns::tlv::RrData, content.c_str(), content.size());
+        update.addUpdateRr(block);
+      }
+    }
   }
 
   update.run();