blob: 2a454f864891e39f549e1f7d91632171775f7893 [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/*
Alexander Afanasyev08d18742018-03-15 16:31:28 -04003 * Copyright (c) 2014-2018, 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
31#include <ndn-cxx/security/key-chain.hpp>
Yumin Xia2c509c22017-02-09 14:37:36 -080032#include <ndn-cxx/security/signing-helpers.hpp>
Shock Jiangae429122014-10-24 09:04:58 -070033#include <ndn-cxx/data.hpp>
34#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#include <ndn-cxx/encoding/block.hpp>
37#include <ndn-cxx/encoding/block-helpers.hpp>
38#include <boost/noncopyable.hpp>
39#include <boost/program_options.hpp>
40#include <boost/asio.hpp>
41#include <boost/filesystem.hpp>
42
43#include <string>
44#include <tuple>
45
46namespace ndn {
47namespace ndns {
Alexander Afanasyevc7c99002015-10-09 17:27:30 -070048
Alexander Afanasyev08d18742018-03-15 16:31:28 -040049NDNS_LOG_INIT(NdnsUpdate);
Shock Jiangae429122014-10-24 09:04:58 -070050
51class NdnsUpdate : noncopyable
52{
53public:
Yumin Xia6343c5b2016-10-20 15:45:50 -070054 NdnsUpdate(const Name& zone, const shared_ptr<Data>& update, Face& face)
55 : m_zone(zone)
Shock Jiangae429122014-10-24 09:04:58 -070056 , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
57 , m_face(face)
Yumin Xia2c509c22017-02-09 14:37:36 -080058 , m_validator(NdnsValidatorBuilder::create(face))
Shock Jiang43e59b42014-12-02 15:05:02 -080059 , m_update(update)
Shock Jiangae429122014-10-24 09:04:58 -070060 , m_hasError(false)
61 {
Shock Jiangae429122014-10-24 09:04:58 -070062 }
63
64 void
Shock Jiang43e59b42014-12-02 15:05:02 -080065 start()
Shock Jiangae429122014-10-24 09:04:58 -070066 {
Shock Jiang43e59b42014-12-02 15:05:02 -080067 NDNS_LOG_INFO(" ================ "
Shock Jiangae429122014-10-24 09:04:58 -070068 << "start to update RR at Zone = " << this->m_zone
Shock Jiang43e59b42014-12-02 15:05:02 -080069 << " new RR is: " << m_update->getName()
70 <<" =================== ");
71
72 NDNS_LOG_INFO("new RR is signed by: "
73 << m_update->getSignature().getKeyLocator().getName());
Shock Jiangae429122014-10-24 09:04:58 -070074
75 Interest interest = this->makeUpdateInterest();
Shock Jiang43e59b42014-12-02 15:05:02 -080076 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update->getName().toUri());
Shock Jiangae429122014-10-24 09:04:58 -070077 m_face.expressInterest(interest,
78 bind(&NdnsUpdate::onData, this, _1, _2),
Alexander Afanasyevf4193ea2017-06-12 15:35:13 -070079 bind(&NdnsUpdate::onTimeout, this, _1), // nack
Shock Jiangae429122014-10-24 09:04:58 -070080 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
81 );
Shock Jiangae429122014-10-24 09:04:58 -070082 }
83
84 void
85 stop()
86 {
87 m_face.getIoService().stop();
88 }
89
90private:
91 void
92 onData(const Interest& interest, const Data& data)
93 {
94 NDNS_LOG_INFO("get response of Update");
95 int ret = -1;
96 std::string msg;
97 std::tie(ret, msg) = this->parseResponse(data);
98 NDNS_LOG_INFO("Return Code: " << ret << ", and Update "
99 << (ret == UPDATE_OK ? "succeeds" : "fails"));
100 if (ret != UPDATE_OK)
101 m_hasError = true;
102
103 if (!msg.empty()) {
104 NDNS_LOG_INFO("Return Msg: " << msg);
105 }
106
107 NDNS_LOG_INFO("to verify the response");
Yumin Xia2c509c22017-02-09 14:37:36 -0800108 m_validator->validate(data,
Shock Jiangae429122014-10-24 09:04:58 -0700109 bind(&NdnsUpdate::onDataValidated, this, _1),
110 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2)
111 );
112 }
113
114 std::tuple<int, std::string>
115 parseResponse(const Data& data)
116 {
117 int ret = -1;
118 std::string msg;
119 Block blk = data.getContent();
120 blk.parse();
121 Block block = blk.blockFromValue();
122 block.parse();
123 Block::element_const_iterator val = block.elements_begin();
124 for (; val != block.elements_end(); ++val) {
125 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
126 ret = readNonNegativeInteger(*val);
127 }
128 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
129 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
130 }
131 }
132
133 return std::make_tuple(ret, msg);
134 }
135
136 /**
137 * @brief construct a query (interest) which contains the update information
138 */
139 Interest
140 makeUpdateInterest()
141 {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700142 Query q(m_zone, label::NDNS_ITERATIVE_QUERY);
Shock Jiang43e59b42014-12-02 15:05:02 -0800143 q.setRrLabel(Name().append(m_update->wireEncode()));
Shock Jiangae429122014-10-24 09:04:58 -0700144 q.setRrType(label::NDNS_UPDATE_LABEL);
145 q.setInterestLifetime(m_interestLifetime);
146
147 return q.toInterest();
148 }
149
150private:
151 void
152 onTimeout(const ndn::Interest& interest)
153 {
154 NDNS_LOG_TRACE("Update timeouts");
155 m_hasError = true;
156 this->stop();
157 }
158
159 void
Yumin Xia2c509c22017-02-09 14:37:36 -0800160 onDataValidated(const Data& data)
Shock Jiangae429122014-10-24 09:04:58 -0700161 {
162 NDNS_LOG_INFO("data pass verification");
163 this->stop();
164 }
165
166 void
Yumin Xia2c509c22017-02-09 14:37:36 -0800167 onDataValidationFailed(const Data& data, const security::v2::ValidationError& str)
Shock Jiangae429122014-10-24 09:04:58 -0700168 {
169 NDNS_LOG_INFO("data does not pass verification");
170 m_hasError = true;
171 this->stop();
172 }
173
174public:
Shock Jiangae429122014-10-24 09:04:58 -0700175
176 void
177 setInterestLifetime(const time::milliseconds& interestLifetime)
178 {
179 m_interestLifetime = interestLifetime;
180 }
181
Alexander Afanasyev984ca9d2016-12-19 13:09:14 -0800182 bool
Shock Jiangae429122014-10-24 09:04:58 -0700183 hasError() const
184 {
185 return m_hasError;
186 }
187
188private:
Shock Jiangae429122014-10-24 09:04:58 -0700189 Name m_zone;
Shock Jiang43e59b42014-12-02 15:05:02 -0800190
Shock Jiangae429122014-10-24 09:04:58 -0700191 time::milliseconds m_interestLifetime;
192
193 Face& m_face;
Yumin Xia2c509c22017-02-09 14:37:36 -0800194 unique_ptr<security::v2::Validator> m_validator;
Shock Jiangae429122014-10-24 09:04:58 -0700195 KeyChain m_keyChain;
196
Shock Jiang43e59b42014-12-02 15:05:02 -0800197 shared_ptr<Data> m_update;
Shock Jiangae429122014-10-24 09:04:58 -0700198 bool m_hasError;
199};
200
201} // namespace ndns
202} // namespace ndn
203
204int
205main(int argc, char* argv[])
206{
Shock Jiangae429122014-10-24 09:04:58 -0700207 using std::string;
208 using namespace ndn;
209 using namespace ndn::ndns;
210
Shock Jiangae429122014-10-24 09:04:58 -0700211 Name zone;
212 int ttl = 4;
213 Name rrLabel;
214 string rrType = "TXT";
Yumin Xiaa484ba72016-11-10 20:40:12 -0800215 string contentTypeStr = "resp";
Shock Jiangae429122014-10-24 09:04:58 -0700216 Name certName;
Shock Jiang06cd2142014-11-23 17:36:02 -0800217 std::vector<string> contents;
Shock Jiangae429122014-10-24 09:04:58 -0700218 string contentFile;
Shock Jiang43e59b42014-12-02 15:05:02 -0800219 shared_ptr<Data> update;
220
Shock Jiangae429122014-10-24 09:04:58 -0700221 try {
222 namespace po = boost::program_options;
223 po::variables_map vm;
224
225 po::options_description generic("Generic Options");
226 generic.add_options()("help,h", "print help message");
227
228 po::options_description config("Configuration");
229 config.add_options()
Shock Jiangae429122014-10-24 09:04:58 -0700230 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
231 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
Yumin Xiaa484ba72016-11-10 20:40:12 -0800232 ("contentType,n", po::value<string>(&contentTypeStr), "Set the contentType of the resource record. "
233 "Potential values are [blob|link|nack|auth|resp]. Default: resp")
Shock Jiangae429122014-10-24 09:04:58 -0700234 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
Shock Jiang06cd2142014-11-23 17:36:02 -0800235 ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
236 "set the content of the RR")
Shock Jiangae429122014-10-24 09:04:58 -0700237 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
Shock Jiang43e59b42014-12-02 15:05:02 -0800238 " Response packet in base64 format")
Shock Jiangae429122014-10-24 09:04:58 -0700239 ;
240
241 po::options_description hidden("Hidden Options");
242 hidden.add_options()
243 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
244 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
245 ;
246 po::positional_options_description postion;
247 postion.add("zone", 1);
248 postion.add("rrlabel", 1);
249
250 po::options_description cmdline_options;
251 cmdline_options.add(generic).add(config).add(hidden);
252
253 po::options_description config_file_options;
254 config_file_options.add(config).add(hidden);
255
Shock Jiang43e59b42014-12-02 15:05:02 -0800256 po::options_description visible("Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
Yumin Xiaa484ba72016-11-10 20:40:12 -0800257 "[-n NdnsContentType] [-c cert] "
Shock Jiang43e59b42014-12-02 15:05:02 -0800258 "[-f contentFile]|[-o content]\n"
259 "Allowed options");
260
Shock Jiangae429122014-10-24 09:04:58 -0700261 visible.add(generic).add(config);
262
263 po::parsed_options parsed =
264 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
265
266 po::store(parsed, vm);
267 po::notify(vm);
268
269 if (vm.count("help")) {
Shock Jiangae429122014-10-24 09:04:58 -0700270 std::cout << visible << std::endl;
271 return 0;
272 }
273
Shock Jiang06cd2142014-11-23 17:36:02 -0800274
Shock Jiangae429122014-10-24 09:04:58 -0700275 if (vm.count("content") && vm.count("contentFile")) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800276 std::cerr << "both -o content and -f contentFile are set. Only one is allowed" << std::endl;
277 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700278 }
279
Shock Jiang43e59b42014-12-02 15:05:02 -0800280 if (!vm.count("contentFile")) {
281 NDNS_LOG_TRACE("content option is set. try to figure out the certificate");
282 if (!vm.count("zone") || !vm.count("rrlabel")) {
283 std::cerr << "-o option must be set together with -z zone and -r rrLabel" << std::endl;
284 return 1;
285 }
286
287 KeyChain keyChain;
288 if (certName.empty()) {
289 Name name = Name().append(zone).append(rrLabel);
290 // choosing the longest match of the identity who also have default certificate
291 for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
292 Name tmp = name.getPrefix(i - 1);
Yumin Xia2c509c22017-02-09 14:37:36 -0800293 if (CertHelper::doesIdentityExist(keyChain, tmp)) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800294 try {
Yumin Xia2c509c22017-02-09 14:37:36 -0800295 certName = CertHelper::getDefaultCertificateNameOfIdentity(keyChain, tmp);
Shock Jiang43e59b42014-12-02 15:05:02 -0800296 break;
297 }
Yumin Xia2c509c22017-02-09 14:37:36 -0800298 catch (const std::exception&) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800299 // If it cannot get a default certificate from one identity,
300 // just ignore this one try next identity.
301 ;
302 }
303 }
304 } // for
305
306 if (certName.empty()) {
307 std::cerr << "cannot figure out the certificate automatically. "
308 << "please set it with -c CERT_NAEME" << std::endl;
309 return 1;
310 }
311 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800312
Yumin Xiaa484ba72016-11-10 20:40:12 -0800313 NdnsContentType contentType = toNdnsContentType(contentTypeStr);
Shock Jiang43e59b42014-12-02 15:05:02 -0800314
Yumin Xiaa484ba72016-11-10 20:40:12 -0800315 if (contentType == ndns::NDNS_UNKNOWN) {
316 std::cerr << "unknown NdnsContentType: " << contentTypeStr << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800317 return 1;
318 }
319
320 Response re;
321 re.setZone(zone);
322 re.setRrLabel(rrLabel);
Yumin Xia918343d2017-03-17 19:04:55 -0700323 name::Component qType = ndns::label::NDNS_ITERATIVE_QUERY;
Shock Jiang43e59b42014-12-02 15:05:02 -0800324
325 re.setQueryType(qType);
326 re.setRrType(name::Component(rrType));
Yumin Xiaa484ba72016-11-10 20:40:12 -0800327 re.setContentType(contentType);
Shock Jiang43e59b42014-12-02 15:05:02 -0800328
329 for (const auto& content : contents) {
Junxiao Shi767f35c2016-07-23 01:54:42 +0000330 re.addRr(makeBinaryBlock(ndns::tlv::RrData, content.c_str(), content.size()));
Shock Jiang43e59b42014-12-02 15:05:02 -0800331
332 // re.addRr(content);
333 }
334
335 update = re.toData();
Yumin Xia2c509c22017-02-09 14:37:36 -0800336 keyChain.sign(*update, security::signingByCertificate(certName));
Shock Jiang43e59b42014-12-02 15:05:02 -0800337 }
338 else {
339 try {
340 update = ndn::io::load<ndn::Data>(contentFile);
341 NDNS_LOG_TRACE("load data " << update->getName() << " from content file: " << contentFile);
342 }
343 catch (const std::exception& e) {
344 std::cerr << "Error: load Data packet from file: " << contentFile
345 << ". Due to: " << e.what() << std::endl;
346 return 1;
347 }
348
349 try {
350 // must check the Data is a legal Response with right name
Yumin Xia918343d2017-03-17 19:04:55 -0700351 shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<NDNS>(<>+)<CERT><>*");
Shock Jiang43e59b42014-12-02 15:05:02 -0800352 shared_ptr<Regex> regex2 = make_shared<Regex>("(<>*)<NDNS>(<>+)");
353
354 Name zone2;
355 if (regex->match(update->getName())) {
356 zone2 = regex->expand("\\1");
357 }
358 else if (regex2->match(update->getName())) {
359 zone2 = regex2->expand("\\1");
360 }
361 else {
362 std::cerr << "The loaded Data packet cannot be stored in NDNS "
363 "since its does not have a proper name" << std::endl;
364 return 1;
365 }
366
367 if (vm.count("zone") && zone != zone2) {
368 std::cerr << "The loaded Data packet is supposed to be stored at zone: " << zone2
369 << " instead of zone: " << zone << std::endl;
370 return 1;
371 }
372 else {
373 zone = zone2;
374 }
375
376 Response re;
Yumin Xia6343c5b2016-10-20 15:45:50 -0700377 re.fromData(zone, *update);
Shock Jiang43e59b42014-12-02 15:05:02 -0800378
379 if (vm.count("rrlabel") && rrLabel != re.getRrLabel()) {
380 std::cerr << "The loaded Data packet is supposed to have rrLabel: " << re.getRrLabel()
381 << " instead of label: " << rrLabel << std::endl;
382 return 1;
383 }
384
385 if (vm.count("rrtype") && name::Component(rrType) != re.getRrType()) {
386 std::cerr << "The loaded Data packet is supposed to have rrType: " << re.getRrType()
387 << " instead of label: " << rrType << std::endl;
388 return 1;
389 }
390 }
391 catch (const std::exception& e) {
392 std::cerr << "Error: the loaded Data packet cannot parse to a Response stored at zone: "
393 << zone << std::endl;
394 return 1;
395 }
396
Shock Jiangae429122014-10-24 09:04:58 -0700397 }
Shock Jiangae429122014-10-24 09:04:58 -0700398 }
399 catch (const std::exception& ex) {
400 std::cerr << "Parameter Error: " << ex.what() << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800401 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700402 }
403
404 Face face;
Shock Jiang43e59b42014-12-02 15:05:02 -0800405 try {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700406 NdnsUpdate updater(zone, update, face);
Shock Jiang43e59b42014-12-02 15:05:02 -0800407 updater.setInterestLifetime(ndn::time::seconds(ttl));
Shock Jiangae429122014-10-24 09:04:58 -0700408
Shock Jiang43e59b42014-12-02 15:05:02 -0800409 updater.start();
410 face.processEvents();
411 if (updater.hasError())
412 return 1;
413 else
Shock Jiang06cd2142014-11-23 17:36:02 -0800414 return 0;
Shock Jiangae429122014-10-24 09:04:58 -0700415 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800416 catch (const std::exception& e) {
417 std::cerr << "Error: " << e.what() << std::endl;
418 return 1;
419 }
Shock Jiangae429122014-10-24 09:04:58 -0700420}