blob: 88340d9260a2d89fc6271694608832bbe0abd440 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2020, 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/validator.hpp"
#include "util/util.hpp"
#include <ndn-cxx/face.hpp>
#include <ndn-cxx/security/key-chain.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
NDNS_LOG_INIT(Dig);
namespace ndn {
namespace ndns {
class NdnsDig
{
public:
NdnsDig(const Name& dstLabel,
const name::Component& rrType, bool shouldValidateIntermediate)
: m_dstLabel(dstLabel)
, m_rrType(rrType)
, m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
, m_validator(NdnsValidatorBuilder::create(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.get()));
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
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 (const std::exception& e) {
std::cerr << "Error: " << e.what();
m_hasError = true;
}
}
void
stop()
{
m_face.getIoService().stop();
NDNS_LOG_TRACE("application stops.");
}
void
setStartZone(const Name& start)
{
m_ctr->setStartComponentIndex(start.size());
}
private:
void
onSucceed(const Data& data, const Response& response)
{
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.getContentType()
<< ". 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 (const 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),
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 Data& data)
{
NDNS_LOG_INFO("final data pass verification");
this->stop();
}
void
onDataValidationFailed(const Data& data, const security::ValidationError& err)
{
NDNS_LOG_INFO("final data does not pass verification");
m_hasError = true;
this->stop();
}
public:
void
setInterestLifetime(const time::milliseconds& lifetime)
{
m_interestLifetime = lifetime;
}
bool
hasError() const
{
return m_hasError;
}
void
setDstFile(const std::string& dstFile)
{
m_dstFile = dstFile;
}
private:
Name m_dstLabel;
name::Component m_rrType;
Name m_certName;
time::milliseconds m_interestLifetime;
Face m_face;
unique_ptr<security::Validator> m_validator;
bool m_shouldValidateIntermediate;
std::unique_ptr<QueryController> m_ctr;
bool m_hasError;
std::string m_dstFile;
};
} // namespace ndns
} // namespace ndn
int
main(int argc, char* argv[])
{
using std::string;
using namespace ndn;
Name dstLabel;
int ttl = 4;
string rrType = "TXT";
string dstFile;
bool shouldValidateIntermediate = true;
Name start("/ndn");
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), "query timeout. 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")
("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");
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("Usage: ndns-dig /name/to/be/resolved [-t rrType] [-T ttl]"
"[-d dstFile] [-s startZone] [-n]\n"
"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 << visible << std::endl;
return 0;
}
if (!vm.count("name")) {
std::cerr << "must contain a target label parameter." << std::endl;
std::cerr << visible << std::endl;
return 1;
}
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 1;
}
try {
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
return 0;
}
catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}