blob: 10bab42cf1c4d63cd34ec3a3d4506a3fa54a1ca3 [file] [log] [blame]
Shock Jiangae429122014-10-24 09:04:58 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014, Regents of the University of California.
4 *
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:
Shock Jiang43e59b42014-12-02 15:05:02 -080051 NdnsUpdate(const Name& hint, const Name& zone, const shared_ptr<Data>& update, Face& face)
52 : m_hint(hint)
Shock Jiangae429122014-10-24 09:04:58 -070053 , m_zone(zone)
Shock Jiangae429122014-10-24 09:04:58 -070054 , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
55 , m_face(face)
56 , m_validator(face)
Shock Jiang43e59b42014-12-02 15:05:02 -080057 , m_update(update)
Shock Jiangae429122014-10-24 09:04:58 -070058 , m_hasError(false)
59 {
Shock Jiangae429122014-10-24 09:04:58 -070060 }
61
62 void
Shock Jiang43e59b42014-12-02 15:05:02 -080063 start()
Shock Jiangae429122014-10-24 09:04:58 -070064 {
Shock Jiang43e59b42014-12-02 15:05:02 -080065 NDNS_LOG_INFO(" ================ "
Shock Jiangae429122014-10-24 09:04:58 -070066 << "start to update RR at Zone = " << this->m_zone
Shock Jiang43e59b42014-12-02 15:05:02 -080067 << " new RR is: " << m_update->getName()
68 <<" =================== ");
69
70 NDNS_LOG_INFO("new RR is signed by: "
71 << m_update->getSignature().getKeyLocator().getName());
Shock Jiangae429122014-10-24 09:04:58 -070072
73 Interest interest = this->makeUpdateInterest();
Shock Jiang43e59b42014-12-02 15:05:02 -080074 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update->getName().toUri());
Shock Jiangae429122014-10-24 09:04:58 -070075 m_face.expressInterest(interest,
76 bind(&NdnsUpdate::onData, this, _1, _2),
77 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
78 );
Shock Jiangae429122014-10-24 09:04:58 -070079 }
80
81 void
82 stop()
83 {
84 m_face.getIoService().stop();
85 }
86
87private:
88 void
89 onData(const Interest& interest, const Data& data)
90 {
91 NDNS_LOG_INFO("get response of Update");
92 int ret = -1;
93 std::string msg;
94 std::tie(ret, msg) = this->parseResponse(data);
95 NDNS_LOG_INFO("Return Code: " << ret << ", and Update "
96 << (ret == UPDATE_OK ? "succeeds" : "fails"));
97 if (ret != UPDATE_OK)
98 m_hasError = true;
99
100 if (!msg.empty()) {
101 NDNS_LOG_INFO("Return Msg: " << msg);
102 }
103
104 NDNS_LOG_INFO("to verify the response");
105 m_validator.validate(data,
106 bind(&NdnsUpdate::onDataValidated, this, _1),
107 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2)
108 );
109 }
110
111 std::tuple<int, std::string>
112 parseResponse(const Data& data)
113 {
114 int ret = -1;
115 std::string msg;
116 Block blk = data.getContent();
117 blk.parse();
118 Block block = blk.blockFromValue();
119 block.parse();
120 Block::element_const_iterator val = block.elements_begin();
121 for (; val != block.elements_end(); ++val) {
122 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
123 ret = readNonNegativeInteger(*val);
124 }
125 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
126 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
127 }
128 }
129
130 return std::make_tuple(ret, msg);
131 }
132
133 /**
134 * @brief construct a query (interest) which contains the update information
135 */
136 Interest
137 makeUpdateInterest()
138 {
Shock Jiangae429122014-10-24 09:04:58 -0700139 Query q(m_hint, m_zone, label::NDNS_ITERATIVE_QUERY);
Shock Jiang43e59b42014-12-02 15:05:02 -0800140 q.setRrLabel(Name().append(m_update->wireEncode()));
Shock Jiangae429122014-10-24 09:04:58 -0700141 q.setRrType(label::NDNS_UPDATE_LABEL);
142 q.setInterestLifetime(m_interestLifetime);
143
144 return q.toInterest();
145 }
146
147private:
148 void
149 onTimeout(const ndn::Interest& interest)
150 {
151 NDNS_LOG_TRACE("Update timeouts");
152 m_hasError = true;
153 this->stop();
154 }
155
156 void
157 onDataValidated(const shared_ptr<const Data>& data)
158 {
159 NDNS_LOG_INFO("data pass verification");
160 this->stop();
161 }
162
163 void
164 onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
165 {
166 NDNS_LOG_INFO("data does not pass verification");
167 m_hasError = true;
168 this->stop();
169 }
170
171public:
Shock Jiangae429122014-10-24 09:04:58 -0700172
173 void
174 setInterestLifetime(const time::milliseconds& interestLifetime)
175 {
176 m_interestLifetime = interestLifetime;
177 }
178
179 const bool
180 hasError() const
181 {
182 return m_hasError;
183 }
184
185private:
Shock Jiangae429122014-10-24 09:04:58 -0700186 Name m_hint;
187 Name m_zone;
Shock Jiang43e59b42014-12-02 15:05:02 -0800188
Shock Jiangae429122014-10-24 09:04:58 -0700189 time::milliseconds m_interestLifetime;
190
191 Face& m_face;
192 Validator m_validator;
193 KeyChain m_keyChain;
194
Shock Jiang43e59b42014-12-02 15:05:02 -0800195 shared_ptr<Data> m_update;
Shock Jiangae429122014-10-24 09:04:58 -0700196 bool m_hasError;
197};
198
199} // namespace ndns
200} // namespace ndn
201
202int
203main(int argc, char* argv[])
204{
205 ndn::ndns::log::init();
206 using std::string;
207 using namespace ndn;
208 using namespace ndn::ndns;
209
210 Name hint;
211 Name zone;
212 int ttl = 4;
213 Name rrLabel;
214 string rrType = "TXT";
215 string ndnsTypeStr = "resp";
216 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()
230 ("hint,H", po::value<Name>(&hint), "forwarding hint")
231 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
232 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
233 ("ndnsType,n", po::value<string>(&ndnsTypeStr), "Set the ndnsType of the resource record. "
234 "Potential values are [resp|nack|auth|raw]. Default: resp")
235 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
Shock Jiang06cd2142014-11-23 17:36:02 -0800236 ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
237 "set the content of the RR")
Shock Jiangae429122014-10-24 09:04:58 -0700238 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
Shock Jiang43e59b42014-12-02 15:05:02 -0800239 " Response packet in base64 format")
Shock Jiangae429122014-10-24 09:04:58 -0700240 ;
241
242 po::options_description hidden("Hidden Options");
243 hidden.add_options()
244 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
245 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
246 ;
247 po::positional_options_description postion;
248 postion.add("zone", 1);
249 postion.add("rrlabel", 1);
250
251 po::options_description cmdline_options;
252 cmdline_options.add(generic).add(config).add(hidden);
253
254 po::options_description config_file_options;
255 config_file_options.add(config).add(hidden);
256
Shock Jiang43e59b42014-12-02 15:05:02 -0800257 po::options_description visible("Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
258 "[-H hint] [-n NdnsType] [-c cert] "
259 "[-f contentFile]|[-o content]\n"
260 "Allowed options");
261
Shock Jiangae429122014-10-24 09:04:58 -0700262 visible.add(generic).add(config);
263
264 po::parsed_options parsed =
265 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
266
267 po::store(parsed, vm);
268 po::notify(vm);
269
270 if (vm.count("help")) {
Shock Jiangae429122014-10-24 09:04:58 -0700271 std::cout << visible << std::endl;
272 return 0;
273 }
274
Shock Jiang06cd2142014-11-23 17:36:02 -0800275
Shock Jiangae429122014-10-24 09:04:58 -0700276 if (vm.count("content") && vm.count("contentFile")) {
Shock Jiang43e59b42014-12-02 15:05:02 -0800277 std::cerr << "both -o content and -f contentFile are set. Only one is allowed" << std::endl;
278 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700279 }
280
Shock Jiang43e59b42014-12-02 15:05:02 -0800281 if (!vm.count("contentFile")) {
282 NDNS_LOG_TRACE("content option is set. try to figure out the certificate");
283 if (!vm.count("zone") || !vm.count("rrlabel")) {
284 std::cerr << "-o option must be set together with -z zone and -r rrLabel" << std::endl;
285 return 1;
286 }
287
288 KeyChain keyChain;
289 if (certName.empty()) {
290 Name name = Name().append(zone).append(rrLabel);
291 // choosing the longest match of the identity who also have default certificate
292 for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
293 Name tmp = name.getPrefix(i - 1);
294 if (keyChain.doesIdentityExist(tmp)) {
295 try {
296 certName = keyChain.getDefaultCertificateNameForIdentity(tmp);
297 break;
298 }
299 catch (std::exception&) {
300 // If it cannot get a default certificate from one identity,
301 // just ignore this one try next identity.
302 ;
303 }
304 }
305 } // for
306
307 if (certName.empty()) {
308 std::cerr << "cannot figure out the certificate automatically. "
309 << "please set it with -c CERT_NAEME" << std::endl;
310 return 1;
311 }
312 }
313 else {
314 if (!keyChain.doesCertificateExist(certName)) {
315 std::cerr << "certificate: " << certName << " does not exist" << std::endl;
316 return 1;
317 }
318 }
319
320 NdnsType ndnsType = toNdnsType(ndnsTypeStr);
321
322 if (ndnsType == ndns::NDNS_UNKNOWN) {
323 std::cerr << "unknown NdnsType: " << ndnsTypeStr << std::endl;
324 return 1;
325 }
326
327 Response re;
328 re.setZone(zone);
329 re.setRrLabel(rrLabel);
330 name::Component qType = (rrType == "ID-CERT" ?
331 ndns::label::NDNS_CERT_QUERY : ndns::label::NDNS_ITERATIVE_QUERY);
332
333 re.setQueryType(qType);
334 re.setRrType(name::Component(rrType));
335 re.setNdnsType(ndnsType);
336
337 for (const auto& content : contents) {
338 re.addRr(ndn::dataBlock(ndn::ndns::tlv::RrData, content.c_str(), content.size()));
339
340 // re.addRr(content);
341 }
342
343 update = re.toData();
344 keyChain.sign(*update, certName);
345 }
346 else {
347 try {
348 update = ndn::io::load<ndn::Data>(contentFile);
349 NDNS_LOG_TRACE("load data " << update->getName() << " from content file: " << contentFile);
350 }
351 catch (const std::exception& e) {
352 std::cerr << "Error: load Data packet from file: " << contentFile
353 << ". Due to: " << e.what() << std::endl;
354 return 1;
355 }
356
357 try {
358 // must check the Data is a legal Response with right name
359 shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT><>*");
360 shared_ptr<Regex> regex2 = make_shared<Regex>("(<>*)<NDNS>(<>+)");
361
362 Name zone2;
363 if (regex->match(update->getName())) {
364 zone2 = regex->expand("\\1");
365 }
366 else if (regex2->match(update->getName())) {
367 zone2 = regex2->expand("\\1");
368 }
369 else {
370 std::cerr << "The loaded Data packet cannot be stored in NDNS "
371 "since its does not have a proper name" << std::endl;
372 return 1;
373 }
374
375 if (vm.count("zone") && zone != zone2) {
376 std::cerr << "The loaded Data packet is supposed to be stored at zone: " << zone2
377 << " instead of zone: " << zone << std::endl;
378 return 1;
379 }
380 else {
381 zone = zone2;
382 }
383
384 Response re;
385 re.fromData(hint, zone, *update);
386
387 if (vm.count("rrlabel") && rrLabel != re.getRrLabel()) {
388 std::cerr << "The loaded Data packet is supposed to have rrLabel: " << re.getRrLabel()
389 << " instead of label: " << rrLabel << std::endl;
390 return 1;
391 }
392
393 if (vm.count("rrtype") && name::Component(rrType) != re.getRrType()) {
394 std::cerr << "The loaded Data packet is supposed to have rrType: " << re.getRrType()
395 << " instead of label: " << rrType << std::endl;
396 return 1;
397 }
398 }
399 catch (const std::exception& e) {
400 std::cerr << "Error: the loaded Data packet cannot parse to a Response stored at zone: "
401 << zone << std::endl;
402 return 1;
403 }
404
Shock Jiangae429122014-10-24 09:04:58 -0700405 }
Shock Jiangae429122014-10-24 09:04:58 -0700406 }
407 catch (const std::exception& ex) {
408 std::cerr << "Parameter Error: " << ex.what() << std::endl;
Shock Jiang43e59b42014-12-02 15:05:02 -0800409 return 1;
Shock Jiangae429122014-10-24 09:04:58 -0700410 }
411
412 Face face;
Shock Jiang43e59b42014-12-02 15:05:02 -0800413 try {
414 NdnsUpdate updater(hint, zone, update, face);
415 updater.setInterestLifetime(ndn::time::seconds(ttl));
Shock Jiangae429122014-10-24 09:04:58 -0700416
Shock Jiang43e59b42014-12-02 15:05:02 -0800417 updater.start();
418 face.processEvents();
419 if (updater.hasError())
420 return 1;
421 else
Shock Jiang06cd2142014-11-23 17:36:02 -0800422 return 0;
Shock Jiangae429122014-10-24 09:04:58 -0700423 }
Shock Jiang43e59b42014-12-02 15:05:02 -0800424 catch (const ndn::ValidatorConfig::Error& e) {
425 std::cerr << "Fail to create the validator: " << e.what() << std::endl;
Shock Jiangae429122014-10-24 09:04:58 -0700426 return 1;
Shock Jiang43e59b42014-12-02 15:05:02 -0800427 }
428 catch (const std::exception& e) {
429 std::cerr << "Error: " << e.what() << std::endl;
430 return 1;
431 }
Shock Jiangae429122014-10-24 09:04:58 -0700432}