blob: 5bdf6fb4c9b4427f270c45da0f1b6ce59838abb0 [file] [log] [blame]
Shock Jiangae429122014-10-24 09:04:58 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yumin Xia2c509c22017-02-09 14:37:36 -08002/*
Davide Pesavento98026122022-03-14 22:00:03 -04003 * Copyright (c) 2014-2022, Regents of the University of California.
Shock Jiangae429122014-10-24 09:04:58 -07004 *
5 * This file is part of NDNS (Named Data Networking Domain Name Service).
6 * See AUTHORS.md for complete list of NDNS authors and contributors.
7 *
8 * NDNS is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * NDNS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * NDNS, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "clients/response.hpp"
21#include "clients/query.hpp"
22#include "ndns-label.hpp"
Yumin Xia99c821a2017-04-07 11:01:08 -070023#include "validator/validator.hpp"
Shock Jiangae429122014-10-24 09:04:58 -070024#include "ndns-enum.hpp"
25#include "ndns-tlv.hpp"
26#include "logger.hpp"
27#include "daemon/db-mgr.hpp"
28#include "util/util.hpp"
Yumin Xia2c509c22017-02-09 14:37:36 -080029#include "util/cert-helper.hpp"
Shock Jiangae429122014-10-24 09:04:58 -070030
Davide Pesavento98026122022-03-14 22:00:03 -040031#include <ndn-cxx/face.hpp>
Shock Jiangae429122014-10-24 09:04:58 -070032#include <ndn-cxx/security/key-chain.hpp>
Yumin Xia2c509c22017-02-09 14:37:36 -080033#include <ndn-cxx/security/signing-helpers.hpp>
Shock Jiangae429122014-10-24 09:04:58 -070034#include <ndn-cxx/util/io.hpp>
Yumin Xia2c509c22017-02-09 14:37:36 -080035#include <ndn-cxx/util/regex.hpp>
Shock Jiangae429122014-10-24 09:04:58 -070036
Davide Pesavento948c50c2020-12-26 21:30:45 -050037#include <boost/asio/io_service.hpp>
38#include <boost/program_options.hpp>
39
40#include <iostream>
Shock Jiangae429122014-10-24 09:04:58 -070041#include <tuple>
42
43namespace ndn {
44namespace ndns {
Alexander Afanasyevc7c99002015-10-09 17:27:30 -070045
Alexander Afanasyev08d18742018-03-15 16:31:28 -040046NDNS_LOG_INIT(NdnsUpdate);
Shock Jiangae429122014-10-24 09:04:58 -070047
Davide Pesavento948c50c2020-12-26 21:30:45 -050048class NdnsUpdate : boost::noncopyable
Shock Jiangae429122014-10-24 09:04:58 -070049{
50public:
Yumin Xia6343c5b2016-10-20 15:45:50 -070051 NdnsUpdate(const Name& zone, const shared_ptr<Data>& update, Face& face)
52 : m_zone(zone)
Shock Jiangae429122014-10-24 09:04:58 -070053 , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
54 , m_face(face)
Yumin Xia2c509c22017-02-09 14:37:36 -080055 , m_validator(NdnsValidatorBuilder::create(face))
Shock Jiang43e59b42014-12-02 15:05:02 -080056 , m_update(update)
Shock Jiangae429122014-10-24 09:04:58 -070057 , m_hasError(false)
58 {
Shock Jiangae429122014-10-24 09:04:58 -070059 }
60
61 void
Shock Jiang43e59b42014-12-02 15:05:02 -080062 start()
Shock Jiangae429122014-10-24 09:04:58 -070063 {
Shock Jiang43e59b42014-12-02 15:05:02 -080064 NDNS_LOG_INFO(" ================ "
Shock Jiangae429122014-10-24 09:04:58 -070065 << "start to update RR at Zone = " << this->m_zone
Shock Jiang43e59b42014-12-02 15:05:02 -080066 << " new RR is: " << m_update->getName()
67 <<" =================== ");
Davide Pesavento1bff1b22020-06-08 18:46:05 -040068 NDNS_LOG_INFO("new RR is signed by: " << m_update->getKeyLocator()->getName());
Shock Jiangae429122014-10-24 09:04:58 -070069
70 Interest interest = this->makeUpdateInterest();
Shock Jiang43e59b42014-12-02 15:05:02 -080071 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update->getName().toUri());
Shock Jiangae429122014-10-24 09:04:58 -070072 m_face.expressInterest(interest,
73 bind(&NdnsUpdate::onData, this, _1, _2),
Alexander Afanasyevf4193ea2017-06-12 15:35:13 -070074 bind(&NdnsUpdate::onTimeout, this, _1), // nack
Shock Jiangae429122014-10-24 09:04:58 -070075 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
76 );
Shock Jiangae429122014-10-24 09:04:58 -070077 }
78
79 void
80 stop()
81 {
82 m_face.getIoService().stop();
83 }
84
85private:
86 void
Davide Pesavento1bff1b22020-06-08 18:46:05 -040087 onData(const Interest&, const Data& data)
Shock Jiangae429122014-10-24 09:04:58 -070088 {
89 NDNS_LOG_INFO("get response of Update");
Davide Pesavento38fd3982022-04-18 22:22:02 -040090 auto [ret, msg] = parseResponse(data);
91 NDNS_LOG_INFO("Return Code: " << ret << ", and Update " << (ret == UPDATE_OK ? "succeeds" : "fails"));
92
93 if (ret != UPDATE_OK) {
Shock Jiangae429122014-10-24 09:04:58 -070094 m_hasError = true;
Davide Pesavento38fd3982022-04-18 22:22:02 -040095 }
Shock Jiangae429122014-10-24 09:04:58 -070096
97 if (!msg.empty()) {
98 NDNS_LOG_INFO("Return Msg: " << msg);
99 }
100
101 NDNS_LOG_INFO("to verify the response");
Yumin Xia2c509c22017-02-09 14:37:36 -0800102 m_validator->validate(data,
Davide Pesavento1bff1b22020-06-08 18:46:05 -0400103 bind(&NdnsUpdate::onDataValidated, this, _1),
104 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2));
Shock Jiangae429122014-10-24 09:04:58 -0700105 }
106
Davide Pesavento98026122022-03-14 22:00:03 -0400107 static std::tuple<int, std::string>
Shock Jiangae429122014-10-24 09:04:58 -0700108 parseResponse(const Data& data)
109 {
110 int ret = -1;
111 std::string msg;
112 Block blk = data.getContent();
113 blk.parse();
114 Block block = blk.blockFromValue();
115 block.parse();
Davide Pesavento1bff1b22020-06-08 18:46:05 -0400116 auto val = block.elements_begin();
Shock Jiangae429122014-10-24 09:04:58 -0700117 for (; val != block.elements_end(); ++val) {
118 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
119 ret = readNonNegativeInteger(*val);
120 }
121 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
Davide Pesavento38fd3982022-04-18 22:22:02 -0400122 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
Shock Jiangae429122014-10-24 09:04:58 -0700123 }
124 }
125
Davide Pesavento38fd3982022-04-18 22:22:02 -0400126 return {ret, msg};
Shock Jiangae429122014-10-24 09:04:58 -0700127 }
128
129 /**
130 * @brief construct a query (interest) which contains the update information
131 */
132 Interest
133 makeUpdateInterest()
134 {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700135 Query q(m_zone, label::NDNS_ITERATIVE_QUERY);
Davide Pesavento98026122022-03-14 22:00:03 -0400136 q.setRrLabel(Name().append(ndn::tlv::GenericNameComponent, m_update->wireEncode()));
Shock Jiangae429122014-10-24 09:04:58 -0700137 q.setRrType(label::NDNS_UPDATE_LABEL);
138 q.setInterestLifetime(m_interestLifetime);
139
140 return q.toInterest();
141 }
142
143private:
144 void
Davide Pesavento1bff1b22020-06-08 18:46:05 -0400145 onTimeout(const ndn::Interest&)
Shock Jiangae429122014-10-24 09:04:58 -0700146 {
Davide Pesavento1bff1b22020-06-08 18:46:05 -0400147 NDNS_LOG_TRACE("Update timeout");
Shock Jiangae429122014-10-24 09:04:58 -0700148 m_hasError = true;
149 this->stop();
150 }
151
152 void
Davide Pesavento1bff1b22020-06-08 18:46:05 -0400153 onDataValidated(const Data&)
Shock Jiangae429122014-10-24 09:04:58 -0700154 {
155 NDNS_LOG_INFO("data pass verification");
156 this->stop();
157 }
158
159 void
Alexander Afanasyev60514ec2020-06-03 14:18:53 -0400160 onDataValidationFailed(const Data&, const security::ValidationError&)
Shock Jiangae429122014-10-24 09:04:58 -0700161 {
162 NDNS_LOG_INFO("data does not pass verification");
163 m_hasError = true;
164 this->stop();
165 }
166
167public:
Shock Jiangae429122014-10-24 09:04:58 -0700168 void
169 setInterestLifetime(const time::milliseconds& interestLifetime)
170 {
171 m_interestLifetime = interestLifetime;
172 }
173
Alexander Afanasyev984ca9d2016-12-19 13:09:14 -0800174 bool
Shock Jiangae429122014-10-24 09:04:58 -0700175 hasError() const
176 {
177 return m_hasError;
178 }
179
180private:
Shock Jiangae429122014-10-24 09:04:58 -0700181 Name m_zone;
Shock Jiang43e59b42014-12-02 15:05:02 -0800182
Shock Jiangae429122014-10-24 09:04:58 -0700183 time::milliseconds m_interestLifetime;
184
185 Face& m_face;
Alexander Afanasyev60514ec2020-06-03 14:18:53 -0400186 unique_ptr<security::Validator> m_validator;
Shock Jiangae429122014-10-24 09:04:58 -0700187 KeyChain m_keyChain;
188
Shock Jiang43e59b42014-12-02 15:05:02 -0800189 shared_ptr<Data> m_update;
Shock Jiangae429122014-10-24 09:04:58 -0700190 bool m_hasError;
191};
192
193} // namespace ndns
194} // namespace ndn
195
196int
197main(int argc, char* argv[])
198{
Shock Jiangae429122014-10-24 09:04:58 -0700199 using std::string;
200 using namespace ndn;
201 using namespace ndn::ndns;
202
Shock Jiangae429122014-10-24 09:04:58 -0700203 Name zone;
204 int ttl = 4;
205 Name rrLabel;
206 string rrType = "TXT";
Yumin Xiaa484ba72016-11-10 20:40:12 -0800207 string contentTypeStr = "resp";
Shock Jiangae429122014-10-24 09:04:58 -0700208 Name certName;
Shock Jiang06cd2142014-11-23 17:36:02 -0800209 std::vector<string> contents;
Shock Jiangae429122014-10-24 09:04:58 -0700210 string contentFile;
Shock Jiang43e59b42014-12-02 15:05:02 -0800211 shared_ptr<Data> update;
212
Shock Jiangae429122014-10-24 09:04:58 -0700213 try {
214 namespace po = boost::program_options;
215 po::variables_map vm;
216
217 po::options_description generic("Generic Options");
218 generic.add_options()("help,h", "print help message");
219
220 po::options_description config("Configuration");
221 config.add_options()
Shock Jiangae429122014-10-24 09:04:58 -0700222 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
223 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
Yumin Xiaa484ba72016-11-10 20:40:12 -0800224 ("contentType,n", po::value<string>(&contentTypeStr), "Set the contentType of the resource record. "
225 "Potential values are [blob|link|nack|auth|resp]. Default: resp")
Shock Jiangae429122014-10-24 09:04:58 -0700226 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
Shock Jiang06cd2142014-11-23 17:36:02 -0800227 ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
228 "set the content of the RR")
Shock Jiangae429122014-10-24 09:04:58 -0700229 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
Shock Jiang43e59b42014-12-02 15:05:02 -0800230 " Response packet in base64 format")
Shock Jiangae429122014-10-24 09:04:58 -0700231 ;
232
233 po::options_description hidden("Hidden Options");
234 hidden.add_options()
235 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
236 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
237 ;
238 po::positional_options_description postion;
239 postion.add("zone", 1);
240 postion.add("rrlabel", 1);
241
242 po::options_description cmdline_options;
243 cmdline_options.add(generic).add(config).add(hidden);
244
245 po::options_description config_file_options;
246 config_file_options.add(config).add(hidden);
247
Shock Jiang43e59b42014-12-02 15:05:02 -0800248 po::options_description visible("Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
Yumin Xiaa484ba72016-11-10 20:40:12 -0800249 "[-n NdnsContentType] [-c cert] "
Shock Jiang43e59b42014-12-02 15:05:02 -0800250 "[-f contentFile]|[-o content]\n"
251 "Allowed options");
252
Shock Jiangae429122014-10-24 09:04:58 -0700253 visible.add(generic).add(config);
254
255 po::parsed_options parsed =
256 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
257
258 po::store(parsed, vm);
259 po::notify(vm);
260
261 if (vm.count("help")) {
Shock Jiangae429122014-10-24 09:04:58 -0700262 std::cout << visible << std::endl;
263 return 0;
264 }
265
Shock Jiang06cd2142014-11-23 17:36:02 -0800266
Shock Jiangae429122014-10-24 09:04:58 -0700267 if (vm.count("content") && vm.count("contentFile")) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800268 std::cerr << "both -o content and -f contentFile are set. Only one is allowed" << std::endl;
269 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700270 }
271
Shock Jiang43e59b42014-12-02 15:05:02 -0800272 if (!vm.count("contentFile")) {
273 NDNS_LOG_TRACE("content option is set. try to figure out the certificate");
274 if (!vm.count("zone") || !vm.count("rrlabel")) {
275 std::cerr << "-o option must be set together with -z zone and -r rrLabel" << std::endl;
276 return 1;
277 }
278
279 KeyChain keyChain;
280 if (certName.empty()) {
281 Name name = Name().append(zone).append(rrLabel);
282 // choosing the longest match of the identity who also have default certificate
283 for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
284 Name tmp = name.getPrefix(i - 1);
Yumin Xia2c509c22017-02-09 14:37:36 -0800285 if (CertHelper::doesIdentityExist(keyChain, tmp)) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800286 try {
Yumin Xia2c509c22017-02-09 14:37:36 -0800287 certName = CertHelper::getDefaultCertificateNameOfIdentity(keyChain, tmp);
Shock Jiang43e59b42014-12-02 15:05:02 -0800288 break;
289 }
Yumin Xia2c509c22017-02-09 14:37:36 -0800290 catch (const std::exception&) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800291 // If it cannot get a default certificate from one identity,
292 // just ignore this one try next identity.
Shock Jiang43e59b42014-12-02 15:05:02 -0800293 }
294 }
295 } // for
296
297 if (certName.empty()) {
298 std::cerr << "cannot figure out the certificate automatically. "
299 << "please set it with -c CERT_NAEME" << std::endl;
300 return 1;
301 }
302 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800303
Yumin Xiaa484ba72016-11-10 20:40:12 -0800304 NdnsContentType contentType = toNdnsContentType(contentTypeStr);
Shock Jiang43e59b42014-12-02 15:05:02 -0800305
Yumin Xiaa484ba72016-11-10 20:40:12 -0800306 if (contentType == ndns::NDNS_UNKNOWN) {
307 std::cerr << "unknown NdnsContentType: " << contentTypeStr << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800308 return 1;
309 }
310
311 Response re;
312 re.setZone(zone);
313 re.setRrLabel(rrLabel);
Yumin Xia918343d2017-03-17 19:04:55 -0700314 name::Component qType = ndns::label::NDNS_ITERATIVE_QUERY;
Shock Jiang43e59b42014-12-02 15:05:02 -0800315
316 re.setQueryType(qType);
317 re.setRrType(name::Component(rrType));
Yumin Xiaa484ba72016-11-10 20:40:12 -0800318 re.setContentType(contentType);
Shock Jiang43e59b42014-12-02 15:05:02 -0800319
320 for (const auto& content : contents) {
Davide Pesavento38fd3982022-04-18 22:22:02 -0400321 re.addRr(makeStringBlock(ndns::tlv::RrData, content));
Shock Jiang43e59b42014-12-02 15:05:02 -0800322 // re.addRr(content);
323 }
324
325 update = re.toData();
Yumin Xia2c509c22017-02-09 14:37:36 -0800326 keyChain.sign(*update, security::signingByCertificate(certName));
Shock Jiang43e59b42014-12-02 15:05:02 -0800327 }
328 else {
329 try {
330 update = ndn::io::load<ndn::Data>(contentFile);
331 NDNS_LOG_TRACE("load data " << update->getName() << " from content file: " << contentFile);
332 }
333 catch (const std::exception& e) {
334 std::cerr << "Error: load Data packet from file: " << contentFile
335 << ". Due to: " << e.what() << std::endl;
336 return 1;
337 }
338
339 try {
340 // must check the Data is a legal Response with right name
Yumin Xia918343d2017-03-17 19:04:55 -0700341 shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<NDNS>(<>+)<CERT><>*");
Shock Jiang43e59b42014-12-02 15:05:02 -0800342 shared_ptr<Regex> regex2 = make_shared<Regex>("(<>*)<NDNS>(<>+)");
343
344 Name zone2;
345 if (regex->match(update->getName())) {
346 zone2 = regex->expand("\\1");
347 }
348 else if (regex2->match(update->getName())) {
349 zone2 = regex2->expand("\\1");
350 }
351 else {
352 std::cerr << "The loaded Data packet cannot be stored in NDNS "
353 "since its does not have a proper name" << std::endl;
354 return 1;
355 }
356
357 if (vm.count("zone") && zone != zone2) {
358 std::cerr << "The loaded Data packet is supposed to be stored at zone: " << zone2
359 << " instead of zone: " << zone << std::endl;
360 return 1;
361 }
362 else {
363 zone = zone2;
364 }
365
366 Response re;
Yumin Xia6343c5b2016-10-20 15:45:50 -0700367 re.fromData(zone, *update);
Shock Jiang43e59b42014-12-02 15:05:02 -0800368
369 if (vm.count("rrlabel") && rrLabel != re.getRrLabel()) {
370 std::cerr << "The loaded Data packet is supposed to have rrLabel: " << re.getRrLabel()
371 << " instead of label: " << rrLabel << std::endl;
372 return 1;
373 }
374
375 if (vm.count("rrtype") && name::Component(rrType) != re.getRrType()) {
376 std::cerr << "The loaded Data packet is supposed to have rrType: " << re.getRrType()
377 << " instead of label: " << rrType << std::endl;
378 return 1;
379 }
380 }
Davide Pesavento98026122022-03-14 22:00:03 -0400381 catch (const std::exception&) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800382 std::cerr << "Error: the loaded Data packet cannot parse to a Response stored at zone: "
383 << zone << std::endl;
384 return 1;
385 }
Shock Jiangae429122014-10-24 09:04:58 -0700386 }
Shock Jiangae429122014-10-24 09:04:58 -0700387 }
388 catch (const std::exception& ex) {
389 std::cerr << "Parameter Error: " << ex.what() << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800390 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700391 }
392
393 Face face;
Shock Jiang43e59b42014-12-02 15:05:02 -0800394 try {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700395 NdnsUpdate updater(zone, update, face);
Shock Jiang43e59b42014-12-02 15:05:02 -0800396 updater.setInterestLifetime(ndn::time::seconds(ttl));
Shock Jiangae429122014-10-24 09:04:58 -0700397
Shock Jiang43e59b42014-12-02 15:05:02 -0800398 updater.start();
399 face.processEvents();
400 if (updater.hasError())
401 return 1;
402 else
Shock Jiang06cd2142014-11-23 17:36:02 -0800403 return 0;
Shock Jiangae429122014-10-24 09:04:58 -0700404 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800405 catch (const std::exception& e) {
406 std::cerr << "Error: " << e.what() << std::endl;
407 return 1;
408 }
Shock Jiangae429122014-10-24 09:04:58 -0700409}