blob: bb08adf2b110dde4dd89bf09a81b291d9aea9c83 [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 << " =================================== ");
79
80 Interest interest = this->makeUpdateInterest();
81 NDNS_LOG_TRACE("[* <- *] send Update: " << m_update);
82 m_face.expressInterest(interest,
83 bind(&NdnsUpdate::onData, this, _1, _2),
84 bind(&NdnsUpdate::onTimeout, this, _1) //dynamic binding
85 );
86 try {
87 m_face.processEvents();
88 }
89 catch (std::exception& e) {
90 NDNS_LOG_FATAL("Face fails to process events: " << e.what());
91 m_hasError = true;
92 }
93 }
94
95 void
96 stop()
97 {
98 m_face.getIoService().stop();
99 }
100
101private:
102 void
103 onData(const Interest& interest, const Data& data)
104 {
105 NDNS_LOG_INFO("get response of Update");
106 int ret = -1;
107 std::string msg;
108 std::tie(ret, msg) = this->parseResponse(data);
109 NDNS_LOG_INFO("Return Code: " << ret << ", and Update "
110 << (ret == UPDATE_OK ? "succeeds" : "fails"));
111 if (ret != UPDATE_OK)
112 m_hasError = true;
113
114 if (!msg.empty()) {
115 NDNS_LOG_INFO("Return Msg: " << msg);
116 }
117
118 NDNS_LOG_INFO("to verify the response");
119 m_validator.validate(data,
120 bind(&NdnsUpdate::onDataValidated, this, _1),
121 bind(&NdnsUpdate::onDataValidationFailed, this, _1, _2)
122 );
123 }
124
125 std::tuple<int, std::string>
126 parseResponse(const Data& data)
127 {
128 int ret = -1;
129 std::string msg;
130 Block blk = data.getContent();
131 blk.parse();
132 Block block = blk.blockFromValue();
133 block.parse();
134 Block::element_const_iterator val = block.elements_begin();
135 for (; val != block.elements_end(); ++val) {
136 if (val->type() == ndns::tlv::UpdateReturnCode) { // the first must be return code
137 ret = readNonNegativeInteger(*val);
138 }
139 else if (val->type() == ndns::tlv::UpdateReturnMsg) {
140 msg = std::string(reinterpret_cast<const char*>(val->value()), val->value_size());
141 }
142 }
143
144 return std::make_tuple(ret, msg);
145 }
146
147 /**
148 * @brief construct a query (interest) which contains the update information
149 */
150 Interest
151 makeUpdateInterest()
152 {
153 shared_ptr<Data> data = m_update.toData();
154 m_keyChain.sign(*data, m_certName);
155
156 Query q(m_hint, m_zone, label::NDNS_ITERATIVE_QUERY);
157 q.setRrLabel(Name().append(data->wireEncode()));
158 q.setRrType(label::NDNS_UPDATE_LABEL);
159 q.setInterestLifetime(m_interestLifetime);
160
161 return q.toInterest();
162 }
163
164private:
165 void
166 onTimeout(const ndn::Interest& interest)
167 {
168 NDNS_LOG_TRACE("Update timeouts");
169 m_hasError = true;
170 this->stop();
171 }
172
173 void
174 onDataValidated(const shared_ptr<const Data>& data)
175 {
176 NDNS_LOG_INFO("data pass verification");
177 this->stop();
178 }
179
180 void
181 onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
182 {
183 NDNS_LOG_INFO("data does not pass verification");
184 m_hasError = true;
185 this->stop();
186 }
187
188public:
189 void
190 setUpdateAppContent(const Block& block)
191 {
192 m_update.setAppContent(block);
193 }
194
195 void
196 addUpdateRr(const Block& block)
197 {
198 m_update.addRr(block);
199 }
200
201 void
202 setInterestLifetime(const time::milliseconds& interestLifetime)
203 {
204 m_interestLifetime = interestLifetime;
205 }
206
207 const bool
208 hasError() const
209 {
210 return m_hasError;
211 }
212
213private:
214 name::Component m_queryType; ///< NDNS or KEY
215 name::Component m_rrType;
216
217 Name m_hint;
218 Name m_zone;
219 Name m_certName;
220 time::milliseconds m_interestLifetime;
221
222 Face& m_face;
223 Validator m_validator;
224 KeyChain m_keyChain;
225
226 Response m_update;
227 bool m_hasError;
228};
229
230} // namespace ndns
231} // namespace ndn
232
233int
234main(int argc, char* argv[])
235{
236 ndn::ndns::log::init();
237 using std::string;
238 using namespace ndn;
239 using namespace ndn::ndns;
240
241 Name hint;
242 Name zone;
243 int ttl = 4;
244 Name rrLabel;
245 string rrType = "TXT";
246 string ndnsTypeStr = "resp";
247 Name certName;
248 string content;
249 string contentFile;
250 ndn::Block block;
251 try {
252 namespace po = boost::program_options;
253 po::variables_map vm;
254
255 po::options_description generic("Generic Options");
256 generic.add_options()("help,h", "print help message");
257
258 po::options_description config("Configuration");
259 config.add_options()
260 ("hint,H", po::value<Name>(&hint), "forwarding hint")
261 ("ttl,T", po::value<int>(&ttl), "TTL of query. default: 4 sec")
262 ("rrtype,t", po::value<string>(&rrType), "set request RR Type. default: TXT")
263 ("ndnsType,n", po::value<string>(&ndnsTypeStr), "Set the ndnsType of the resource record. "
264 "Potential values are [resp|nack|auth|raw]. Default: resp")
265 ("cert,c", po::value<Name>(&certName), "set the name of certificate to sign the update")
266 ("content,o", po::value<string>(&content), "set the content of the RR")
267 ("contentFile,f", po::value<string>(&contentFile), "set the path of file which contain"
268 " content of the RR in base64 format")
269 ;
270
271 po::options_description hidden("Hidden Options");
272 hidden.add_options()
273 ("zone,z", po::value<Name>(&zone), "zone the record is delegated")
274 ("rrlabel,l", po::value<Name>(&rrLabel), "set request RR Label")
275 ;
276 po::positional_options_description postion;
277 postion.add("zone", 1);
278 postion.add("rrlabel", 1);
279
280 po::options_description cmdline_options;
281 cmdline_options.add(generic).add(config).add(hidden);
282
283 po::options_description config_file_options;
284 config_file_options.add(config).add(hidden);
285
286 po::options_description visible("Allowed options");
287 visible.add(generic).add(config);
288
289 po::parsed_options parsed =
290 po::command_line_parser(argc, argv).options(cmdline_options).positional(postion).run();
291
292 po::store(parsed, vm);
293 po::notify(vm);
294
295 if (vm.count("help")) {
296 std::cout << "Usage: ndns-update zone rrLabel [-t rrType] [-w waitingSec] "
297 "[-H hint] [-n NdnsType] [-c cert] [-f contentFile]|[-o content]" << std::endl;
298 std::cout << visible << std::endl;
299 return 0;
300 }
301
302 KeyChain keyChain;
303 if (certName.empty()) {
304 certName = keyChain.getDefaultCertificateName().toUri();
305 }
306 else {
307 if (!keyChain.doesCertificateExist(certName)) {
308 std::cerr << "certificate: " << certName << " does not exist" << std::endl;
309 return 0;
310 }
311 }
312
313 if (vm.count("content") && vm.count("contentFile")) {
314 std::cerr <<"both content and contentFile are set. Only one is allowed" << std::endl;
315 return 0;
316 }
317
318 if (!contentFile.empty()) {
319 shared_ptr<ndn::Data> data = ndn::io::load<ndn::Data>(contentFile);
320 block = data->wireEncode();
321 }
322 else {
323 if (!content.empty())
324 block = ndn::dataBlock(ndn::ndns::tlv::RrData, content.c_str(), content.size());
325 }
326 }
327 catch (const std::exception& ex) {
328 std::cerr << "Parameter Error: " << ex.what() << std::endl;
329 return 0;
330 }
331
332 Face face;
333 NdnsType ndnsType = toNdnsType(ndnsTypeStr);
334
335 NdnsUpdate update(hint, zone, rrLabel, ndn::name::Component(rrType),
336 ndnsType, certName, face);
337 update.setInterestLifetime(ndn::time::seconds(ttl));
338
339 if (!block.empty()) {
340 if (ndnsType == ndn::ndns::NDNS_RAW)
341 update.setUpdateAppContent(block);
342 else
343 update.addUpdateRr(block);
344 }
345
346 update.run();
347
348 if (update.hasError())
349 return 1;
350 else
351 return 0;
352}