blob: 6caf991dcd3401c96fb5051f9cdd813ca75debd0 [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 {
45NDNS_LOG_INIT("NdnsUpdate");
46
47class NdnsUpdate : noncopyable
48{
49public:
50 NdnsUpdate(const Name& hint, const Name& zone, const Name& rrLabel,
51 const name::Component& rrType, NdnsType ndnsType, const Name& certName,
52 Face& face)
53 : m_queryType(rrType == label::CERT_RR_TYPE ?
54 label::NDNS_CERT_QUERY : label::NDNS_ITERATIVE_QUERY)
55 , m_rrType(rrType)
56 , m_hint(hint)
57 , m_zone(zone)
58 , m_certName(certName)
59 , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
60 , m_face(face)
61 , m_validator(face)
62 , m_hasError(false)
63 {
64 m_update.setZone(m_zone);
65 m_update.setRrLabel(rrLabel);
66 m_update.setQueryType(m_queryType);
67 m_update.setRrType(name::Component(rrType));
68 m_update.setNdnsType(ndnsType);
69 }
70
71 void
72 run()
73 {
74 NDNS_LOG_INFO(" =================================== "
75 << "start to update RR at Zone = " << this->m_zone
76 << " with rrLabel = " << this->m_update.getRrLabel()
77 << " and rrType = " << this->m_rrType
78 << " =================================== ");
Shock Jiang06cd2142014-11-23 17:36:02 -080079 NDNS_LOG_INFO("certificate to sign the data is: " << m_certName);
Shock Jiangae429122014-10-24 09:04:58 -070080
81 Interest interest = this->makeUpdateInterest();
82 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update);
83 m_face.expressInterest(interest,
84 bind(&NdnsUpdate::onData, this, _1, _2),
85 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
86 );
87 try {
88 m_face.processEvents();
89 }
90 catch (std::exception& e) {
91 NDNS_LOG_FATAL("Face fails to process events: " << e.what());
92 m_hasError = true;
93 }
94 }
95
96 void
97 stop()
98 {
99 m_face.getIoService().stop();
100 }
101
102private:
103 void
104 onData(const Interest& interest, const Data& data)
105 {
106 NDNS_LOG_INFO("get response of Update");
107 int ret = -1;
108 std::string msg;
109 std::tie(ret, msg) = this->parseResponse(data);
110 NDNS_LOG_INFO("Return Code: " << ret << ", and Update "
111 << (ret == UPDATE_OK ? "succeeds" : "fails"));
112 if (ret != UPDATE_OK)
113 m_hasError = true;
114
115 if (!msg.empty()) {
116 NDNS_LOG_INFO("Return Msg: " << msg);
117 }
118
119 NDNS_LOG_INFO("to verify the response");
120 m_validator.validate(data,
121 bind(&NdnsUpdate::onDataValidated, this, _1),
122 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2)
123 );
124 }
125
126 std::tuple<int, std::string>
127 parseResponse(const Data& data)
128 {
129 int ret = -1;
130 std::string msg;
131 Block blk = data.getContent();
132 blk.parse();
133 Block block = blk.blockFromValue();
134 block.parse();
135 Block::element_const_iterator val = block.elements_begin();
136 for (; val != block.elements_end(); ++val) {
137 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
138 ret = readNonNegativeInteger(*val);
139 }
140 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
141 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
142 }
143 }
144
145 return std::make_tuple(ret, msg);
146 }
147
148 /**
149 * @brief construct a query (interest) which contains the update information
150 */
151 Interest
152 makeUpdateInterest()
153 {
154 shared_ptr<Data> data = m_update.toData();
155 m_keyChain.sign(*data, m_certName);
156
157 Query q(m_hint, m_zone, label::NDNS_ITERATIVE_QUERY);
158 q.setRrLabel(Name().append(data->wireEncode()));
159 q.setRrType(label::NDNS_UPDATE_LABEL);
160 q.setInterestLifetime(m_interestLifetime);
161
162 return q.toInterest();
163 }
164
165private:
166 void
167 onTimeout(const ndn::Interest& interest)
168 {
169 NDNS_LOG_TRACE("Update timeouts");
170 m_hasError = true;
171 this->stop();
172 }
173
174 void
175 onDataValidated(const shared_ptr<const Data>& data)
176 {
177 NDNS_LOG_INFO("data pass verification");
178 this->stop();
179 }
180
181 void
182 onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
183 {
184 NDNS_LOG_INFO("data does not pass verification");
185 m_hasError = true;
186 this->stop();
187 }
188
189public:
190 void
191 setUpdateAppContent(const Block& block)
192 {
193 m_update.setAppContent(block);
194 }
195
196 void
197 addUpdateRr(const Block& block)
198 {
199 m_update.addRr(block);
200 }
201
202 void
203 setInterestLifetime(const time::milliseconds& interestLifetime)
204 {
205 m_interestLifetime = interestLifetime;
206 }
207
208 const bool
209 hasError() const
210 {
211 return m_hasError;
212 }
213
Shock Jiang06cd2142014-11-23 17:36:02 -0800214 const Response&
215 getUpdate() const
216 {
217 return m_update;
218 }
219
Shock Jiangae429122014-10-24 09:04:58 -0700220private:
221 name::Component m_queryType; ///< NDNS or KEY
222 name::Component m_rrType;
223
224 Name m_hint;
225 Name m_zone;
226 Name m_certName;
227 time::milliseconds m_interestLifetime;
228
229 Face& m_face;
230 Validator m_validator;
231 KeyChain m_keyChain;
232
233 Response m_update;
234 bool m_hasError;
235};
236
237} // namespace ndns
238} // namespace ndn
239
240int
241main(int argc, char* argv[])
242{
243 ndn::ndns::log::init();
244 using std::string;
245 using namespace ndn;
246 using namespace ndn::ndns;
247
248 Name hint;
249 Name zone;
250 int ttl = 4;
251 Name rrLabel;
252 string rrType = "TXT";
253 string ndnsTypeStr = "resp";
254 Name certName;
Shock Jiang06cd2142014-11-23 17:36:02 -0800255 std::vector<string> contents;
Shock Jiangae429122014-10-24 09:04:58 -0700256 string contentFile;
Shock Jiang06cd2142014-11-23 17:36:02 -0800257 string dstFile;
Shock Jiangae429122014-10-24 09:04:58 -0700258 ndn::Block block;
259 try {
260 namespace po = boost::program_options;
261 po::variables_map vm;
262
263 po::options_description generic("Generic Options");
264 generic.add_options()("help,h", "print help message");
265
266 po::options_description config("Configuration");
267 config.add_options()
268 ("hint,H", po::value<Name>(&hint), "forwarding hint")
269 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
270 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
271 ("ndnsType,n", po::value<string>(&ndnsTypeStr), "Set the ndnsType of the resource record. "
272 "Potential values are [resp|nack|auth|raw]. Default: resp")
273 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
Shock Jiang06cd2142014-11-23 17:36:02 -0800274 ("content,o", po::value<std::vector<string>>(&contents)->multitoken(),
275 "set the content of the RR")
Shock Jiangae429122014-10-24 09:04:58 -0700276 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
277 " content of the RR in base64 format")
278 ;
279
280 po::options_description hidden("Hidden Options");
281 hidden.add_options()
282 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
283 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
284 ;
285 po::positional_options_description postion;
286 postion.add("zone", 1);
287 postion.add("rrlabel", 1);
288
289 po::options_description cmdline_options;
290 cmdline_options.add(generic).add(config).add(hidden);
291
292 po::options_description config_file_options;
293 config_file_options.add(config).add(hidden);
294
295 po::options_description visible("Allowed options");
296 visible.add(generic).add(config);
297
298 po::parsed_options parsed =
299 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
300
301 po::store(parsed, vm);
302 po::notify(vm);
303
304 if (vm.count("help")) {
Shock Jiang06cd2142014-11-23 17:36:02 -0800305 std::cout << "Usage: ndns-update zone rrLabel [-t rrType] [-T TTL] "
Shock Jiangae429122014-10-24 09:04:58 -0700306 "[-H hint] [-n NdnsType] [-c cert] [-f contentFile]|[-o content]" << std::endl;
307 std::cout << visible << std::endl;
308 return 0;
309 }
310
311 KeyChain keyChain;
312 if (certName.empty()) {
Shock Jiang06cd2142014-11-23 17:36:02 -0800313 Name name = Name().append(zone).append(rrLabel);
314 // choosing the longest match of the identity who also have default certificate
315 for (size_t i = name.size() + 1; i > 0; --i) { // i >=0 will present warnning
316 Name tmp = name.getPrefix(i - 1);
317 if (keyChain.doesIdentityExist(tmp)) {
318 try {
319 certName = keyChain.getDefaultCertificateNameForIdentity(tmp);
320 break;
321 }
322 catch (std::exception&) {
323 // If it cannot get a default certificate from one identity,
324 // just ignore this one try next identity.
325 ;
326 }
327 }
328 }
Shock Jiangae429122014-10-24 09:04:58 -0700329 }
330 else {
331 if (!keyChain.doesCertificateExist(certName)) {
332 std::cerr << "certificate: " << certName << " does not exist" << std::endl;
333 return 0;
334 }
335 }
336
Shock Jiang06cd2142014-11-23 17:36:02 -0800337 if (certName.empty()) {
338 std::cerr << "cannot figure out the certificate automatically. "
339 << "please set it with -c CERT_NAEME" << std::endl;
340 }
341
Shock Jiangae429122014-10-24 09:04:58 -0700342 if (vm.count("content") && vm.count("contentFile")) {
Shock Jiang06cd2142014-11-23 17:36:02 -0800343 std::cerr << "both content and contentFile are set. Only one is allowed" << std::endl;
Shock Jiangae429122014-10-24 09:04:58 -0700344 return 0;
345 }
346
347 if (!contentFile.empty()) {
348 shared_ptr<ndn::Data> data = ndn::io::load<ndn::Data>(contentFile);
349 block = data->wireEncode();
350 }
Shock Jiangae429122014-10-24 09:04:58 -0700351 }
352 catch (const std::exception& ex) {
353 std::cerr << "Parameter Error: " << ex.what() << std::endl;
354 return 0;
355 }
356
357 Face face;
358 NdnsType ndnsType = toNdnsType(ndnsTypeStr);
359
360 NdnsUpdate update(hint, zone, rrLabel, ndn::name::Component(rrType),
361 ndnsType, certName, face);
362 update.setInterestLifetime(ndn::time::seconds(ttl));
363
Shock Jiang06cd2142014-11-23 17:36:02 -0800364 if (!contentFile.empty()) {
365 if (!block.empty()) {
366 if (ndnsType == ndn::ndns::NDNS_RAW)
367 update.setUpdateAppContent(block);
368 else {
369 update.addUpdateRr(block);
370 }
371 }
372 }
373 else {
374 if (ndnsType == ndn::ndns::NDNS_RAW) {
375 // since NDNS_RAW's message tlv type cannot be decided, here we stop this option
376 std::cerr << "--content (-o) does not support NDNS-RAW Response" << std::endl;
377 return 0;
378 }
379 else {
380 for (const auto& content : contents) {
381 block = ndn::dataBlock(ndn::ndns::tlv::RrData, content.c_str(), content.size());
382 update.addUpdateRr(block);
383 }
384 }
Shock Jiangae429122014-10-24 09:04:58 -0700385 }
386
387 update.run();
388
389 if (update.hasError())
390 return 1;
391 else
392 return 0;
393}