blob: 9dbbb2c4802441316298179c4e987caeae3e76c1 [file] [log] [blame]
Shock Jiangae429122014-10-24 09:04:58 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi767f35c2016-07-23 01:54:42 +00003 * Copyright (c) 2014-2016, 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"
23#include "validator.hpp"
24#include "ndns-enum.hpp"
25#include "ndns-tlv.hpp"
26#include "logger.hpp"
27#include "daemon/db-mgr.hpp"
28#include "util/util.hpp"
29
30#include <ndn-cxx/security/key-chain.hpp>
31#include <ndn-cxx/data.hpp>
32#include <ndn-cxx/util/io.hpp>
33#include <ndn-cxx/encoding/block.hpp>
34#include <ndn-cxx/encoding/block-helpers.hpp>
35#include <boost/noncopyable.hpp>
36#include <boost/program_options.hpp>
37#include <boost/asio.hpp>
38#include <boost/filesystem.hpp>
39
40#include <string>
41#include <tuple>
42
43namespace ndn {
44namespace ndns {
Alexander Afanasyevc7c99002015-10-09 17:27:30 -070045
46NDNS_LOG_INIT("NdnsUpdate")
Shock Jiangae429122014-10-24 09:04:58 -070047
48class NdnsUpdate : noncopyable
49{
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)
55 , m_validator(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 <<" =================== ");
68
69 NDNS_LOG_INFO("new RR is signed by: "
70 << m_update->getSignature().getKeyLocator().getName());
Shock Jiangae429122014-10-24 09:04:58 -070071
72 Interest interest = this->makeUpdateInterest();
Shock Jiang43e59b42014-12-02 15:05:02 -080073 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update->getName().toUri());
Shock Jiangae429122014-10-24 09:04:58 -070074 m_face.expressInterest(interest,
75 bind(&NdnsUpdate::onData, this, _1, _2),
76 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
77 );
Shock Jiangae429122014-10-24 09:04:58 -070078 }
79
80 void
81 stop()
82 {
83 m_face.getIoService().stop();
84 }
85
86private:
87 void
88 onData(const Interest& interest, const Data& data)
89 {
90 NDNS_LOG_INFO("get response of Update");
91 int ret = -1;
92 std::string msg;
93 std::tie(ret, msg) = this->parseResponse(data);
94 NDNS_LOG_INFO("Return Code: " << ret << ", and Update "
95 << (ret == UPDATE_OK ? "succeeds" : "fails"));
96 if (ret != UPDATE_OK)
97 m_hasError = true;
98
99 if (!msg.empty()) {
100 NDNS_LOG_INFO("Return Msg: " << msg);
101 }
102
103 NDNS_LOG_INFO("to verify the response");
104 m_validator.validate(data,
105 bind(&NdnsUpdate::onDataValidated, this, _1),
106 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2)
107 );
108 }
109
110 std::tuple<int, std::string>
111 parseResponse(const Data& data)
112 {
113 int ret = -1;
114 std::string msg;
115 Block blk = data.getContent();
116 blk.parse();
117 Block block = blk.blockFromValue();
118 block.parse();
119 Block::element_const_iterator val = block.elements_begin();
120 for (; val != block.elements_end(); ++val) {
121 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
122 ret = readNonNegativeInteger(*val);
123 }
124 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
125 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
126 }
127 }
128
129 return std::make_tuple(ret, msg);
130 }
131
132 /**
133 * @brief construct a query (interest) which contains the update information
134 */
135 Interest
136 makeUpdateInterest()
137 {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700138 Query q(m_zone, label::NDNS_ITERATIVE_QUERY);
Shock Jiang43e59b42014-12-02 15:05:02 -0800139 q.setRrLabel(Name().append(m_update->wireEncode()));
Shock Jiangae429122014-10-24 09:04:58 -0700140 q.setRrType(label::NDNS_UPDATE_LABEL);
141 q.setInterestLifetime(m_interestLifetime);
142
143 return q.toInterest();
144 }
145
146private:
147 void
148 onTimeout(const ndn::Interest& interest)
149 {
150 NDNS_LOG_TRACE("Update timeouts");
151 m_hasError = true;
152 this->stop();
153 }
154
155 void
156 onDataValidated(const shared_ptr<const Data>& data)
157 {
158 NDNS_LOG_INFO("data pass verification");
159 this->stop();
160 }
161
162 void
163 onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
164 {
165 NDNS_LOG_INFO("data does not pass verification");
166 m_hasError = true;
167 this->stop();
168 }
169
170public:
Shock Jiangae429122014-10-24 09:04:58 -0700171
172 void
173 setInterestLifetime(const time::milliseconds& interestLifetime)
174 {
175 m_interestLifetime = interestLifetime;
176 }
177
Alexander Afanasyev984ca9d2016-12-19 13:09:14 -0800178 bool
Shock Jiangae429122014-10-24 09:04:58 -0700179 hasError() const
180 {
181 return m_hasError;
182 }
183
184private:
Shock Jiangae429122014-10-24 09:04:58 -0700185 Name m_zone;
Shock Jiang43e59b42014-12-02 15:05:02 -0800186
Shock Jiangae429122014-10-24 09:04:58 -0700187 time::milliseconds m_interestLifetime;
188
189 Face& m_face;
190 Validator m_validator;
191 KeyChain m_keyChain;
192
Shock Jiang43e59b42014-12-02 15:05:02 -0800193 shared_ptr<Data> m_update;
Shock Jiangae429122014-10-24 09:04:58 -0700194 bool m_hasError;
195};
196
197} // namespace ndns
198} // namespace ndn
199
200int
201main(int argc, char* argv[])
202{
203 ndn::ndns::log::init();
204 using std::string;
205 using namespace ndn;
206 using namespace ndn::ndns;
207
Shock Jiangae429122014-10-24 09:04:58 -0700208 Name zone;
209 int ttl = 4;
210 Name rrLabel;
211 string rrType = "TXT";
212 string ndnsTypeStr = "resp";
213 Name certName;
Shock Jiang06cd2142014-11-23 17:36:02 -0800214 std::vector<string> contents;
Shock Jiangae429122014-10-24 09:04:58 -0700215 string contentFile;
Shock Jiang43e59b42014-12-02 15:05:02 -0800216 shared_ptr<Data> update;
217
Shock Jiangae429122014-10-24 09:04:58 -0700218 try {
219 namespace po = boost::program_options;
220 po::variables_map vm;
221
222 po::options_description generic("Generic Options");
223 generic.add_options()("help,h", "print help message");
224
225 po::options_description config("Configuration");
226 config.add_options()
Shock Jiangae429122014-10-24 09:04:58 -0700227 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
228 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
229 ("ndnsType,n", po::value<string>(&ndnsTypeStr), "Set the ndnsType of the resource record. "
230 "Potential values are [resp|nack|auth|raw]. Default: resp")
231 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
Shock Jiang06cd2142014-11-23 17:36:02 -0800232 ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
233 "set the content of the RR")
Shock Jiangae429122014-10-24 09:04:58 -0700234 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
Shock Jiang43e59b42014-12-02 15:05:02 -0800235 " Response packet in base64 format")
Shock Jiangae429122014-10-24 09:04:58 -0700236 ;
237
238 po::options_description hidden("Hidden Options");
239 hidden.add_options()
240 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
241 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
242 ;
243 po::positional_options_description postion;
244 postion.add("zone", 1);
245 postion.add("rrlabel", 1);
246
247 po::options_description cmdline_options;
248 cmdline_options.add(generic).add(config).add(hidden);
249
250 po::options_description config_file_options;
251 config_file_options.add(config).add(hidden);
252
Shock Jiang43e59b42014-12-02 15:05:02 -0800253 po::options_description visible("Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
Yumin Xia6343c5b2016-10-20 15:45:50 -0700254 "[-n NdnsType] [-c cert] "
Shock Jiang43e59b42014-12-02 15:05:02 -0800255 "[-f contentFile]|[-o content]\n"
256 "Allowed options");
257
Shock Jiangae429122014-10-24 09:04:58 -0700258 visible.add(generic).add(config);
259
260 po::parsed_options parsed =
261 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
262
263 po::store(parsed, vm);
264 po::notify(vm);
265
266 if (vm.count("help")) {
Shock Jiangae429122014-10-24 09:04:58 -0700267 std::cout << visible << std::endl;
268 return 0;
269 }
270
Shock Jiang06cd2142014-11-23 17:36:02 -0800271
Shock Jiangae429122014-10-24 09:04:58 -0700272 if (vm.count("content") && vm.count("contentFile")) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800273 std::cerr << "both -o content and -f contentFile are set. Only one is allowed" << std::endl;
274 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700275 }
276
Shock Jiang43e59b42014-12-02 15:05:02 -0800277 if (!vm.count("contentFile")) {
278 NDNS_LOG_TRACE("content option is set. try to figure out the certificate");
279 if (!vm.count("zone") || !vm.count("rrlabel")) {
280 std::cerr << "-o option must be set together with -z zone and -r rrLabel" << std::endl;
281 return 1;
282 }
283
284 KeyChain keyChain;
285 if (certName.empty()) {
286 Name name = Name().append(zone).append(rrLabel);
287 // choosing the longest match of the identity who also have default certificate
288 for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
289 Name tmp = name.getPrefix(i - 1);
290 if (keyChain.doesIdentityExist(tmp)) {
291 try {
292 certName = keyChain.getDefaultCertificateNameForIdentity(tmp);
293 break;
294 }
295 catch (std::exception&) {
296 // If it cannot get a default certificate from one identity,
297 // just ignore this one try next identity.
298 ;
299 }
300 }
301 } // for
302
303 if (certName.empty()) {
304 std::cerr << "cannot figure out the certificate automatically. "
305 << "please set it with -c CERT_NAEME" << std::endl;
306 return 1;
307 }
308 }
309 else {
310 if (!keyChain.doesCertificateExist(certName)) {
311 std::cerr << "certificate: " << certName << " does not exist" << std::endl;
312 return 1;
313 }
314 }
315
316 NdnsType ndnsType = toNdnsType(ndnsTypeStr);
317
318 if (ndnsType == ndns::NDNS_UNKNOWN) {
319 std::cerr << "unknown NdnsType: " << ndnsTypeStr << std::endl;
320 return 1;
321 }
322
323 Response re;
324 re.setZone(zone);
325 re.setRrLabel(rrLabel);
326 name::Component qType = (rrType == "ID-CERT" ?
327 ndns::label::NDNS_CERT_QUERY : ndns::label::NDNS_ITERATIVE_QUERY);
328
329 re.setQueryType(qType);
330 re.setRrType(name::Component(rrType));
331 re.setNdnsType(ndnsType);
332
333 for (const auto& content : contents) {
Junxiao Shi767f35c2016-07-23 01:54:42 +0000334 re.addRr(makeBinaryBlock(ndns::tlv::RrData, content.c_str(), content.size()));
Shock Jiang43e59b42014-12-02 15:05:02 -0800335
336 // re.addRr(content);
337 }
338
339 update = re.toData();
340 keyChain.sign(*update, certName);
341 }
342 else {
343 try {
344 update = ndn::io::load<ndn::Data>(contentFile);
345 NDNS_LOG_TRACE("load data " << update->getName() << " from content file: " << contentFile);
346 }
347 catch (const std::exception& e) {
348 std::cerr << "Error: load Data packet from file: " << contentFile
349 << ". Due to: " << e.what() << std::endl;
350 return 1;
351 }
352
353 try {
354 // must check the Data is a legal Response with right name
355 shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT><>*");
356 shared_ptr<Regex> regex2 = make_shared<Regex>("(<>*)<NDNS>(<>+)");
357
358 Name zone2;
359 if (regex->match(update->getName())) {
360 zone2 = regex->expand("\\1");
361 }
362 else if (regex2->match(update->getName())) {
363 zone2 = regex2->expand("\\1");
364 }
365 else {
366 std::cerr << "The loaded Data packet cannot be stored in NDNS "
367 "since its does not have a proper name" << std::endl;
368 return 1;
369 }
370
371 if (vm.count("zone") && zone != zone2) {
372 std::cerr << "The loaded Data packet is supposed to be stored at zone: " << zone2
373 << " instead of zone: " << zone << std::endl;
374 return 1;
375 }
376 else {
377 zone = zone2;
378 }
379
380 Response re;
Yumin Xia6343c5b2016-10-20 15:45:50 -0700381 re.fromData(zone, *update);
Shock Jiang43e59b42014-12-02 15:05:02 -0800382
383 if (vm.count("rrlabel") && rrLabel != re.getRrLabel()) {
384 std::cerr << "The loaded Data packet is supposed to have rrLabel: " << re.getRrLabel()
385 << " instead of label: " << rrLabel << std::endl;
386 return 1;
387 }
388
389 if (vm.count("rrtype") && name::Component(rrType) != re.getRrType()) {
390 std::cerr << "The loaded Data packet is supposed to have rrType: " << re.getRrType()
391 << " instead of label: " << rrType << std::endl;
392 return 1;
393 }
394 }
395 catch (const std::exception& e) {
396 std::cerr << "Error: the loaded Data packet cannot parse to a Response stored at zone: "
397 << zone << std::endl;
398 return 1;
399 }
400
Shock Jiangae429122014-10-24 09:04:58 -0700401 }
Shock Jiangae429122014-10-24 09:04:58 -0700402 }
403 catch (const std::exception& ex) {
404 std::cerr << "Parameter Error: " << ex.what() << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800405 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700406 }
407
408 Face face;
Shock Jiang43e59b42014-12-02 15:05:02 -0800409 try {
Yumin Xia6343c5b2016-10-20 15:45:50 -0700410 NdnsUpdate updater(zone, update, face);
Shock Jiang43e59b42014-12-02 15:05:02 -0800411 updater.setInterestLifetime(ndn::time::seconds(ttl));
Shock Jiangae429122014-10-24 09:04:58 -0700412
Shock Jiang43e59b42014-12-02 15:05:02 -0800413 updater.start();
414 face.processEvents();
415 if (updater.hasError())
416 return 1;
417 else
Shock Jiang06cd2142014-11-23 17:36:02 -0800418 return 0;
Shock Jiangae429122014-10-24 09:04:58 -0700419 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800420 catch (const ndn::ValidatorConfig::Error& e) {
421 std::cerr << "Fail to create the validator: " << e.what() << std::endl;
Shock Jiangae429122014-10-24 09:04:58 -0700422 return 1;
Shock Jiang43e59b42014-12-02 15:05:02 -0800423 }
424 catch (const std::exception& e) {
425 std::cerr << "Error: " << e.what() << std::endl;
426 return 1;
427 }
Shock Jiangae429122014-10-24 09:04:58 -0700428}