blob: c996566f5b126003204c2a0e739b38b82e4c5f97 [file] [log] [blame]
hilatadd50ada2014-03-13 12:48:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07003 * Copyright (c) 2014 Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology
9 *
10 * This file is part of NFD (Named Data Networking Forwarding Daemon).
11 * See AUTHORS.md for complete list of NFD authors and contributors.
12 *
13 * NFD is free software: you can redistribute it and/or modify it under the terms
14 * of the GNU General Public License as published by the Free Software Foundation,
15 * either version 3 of the License, or (at your option) any later version.
16 *
17 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 * PURPOSE. See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
23 **/
Davide Pesavento6fc750f2014-03-18 19:49:39 +010024
Alexander Afanasyevb47d5382014-05-05 14:35:03 -070025#include "version.hpp"
26
Alexander Afanasyev4a771362014-04-24 21:29:33 -070027#include <ndn-cxx/face.hpp>
28#include <ndn-cxx/management/nfd-controller.hpp>
29#include <ndn-cxx/security/key-chain.hpp>
hilatadd50ada2014-03-13 12:48:47 -050030
Alexander Afanasyevb3893c92014-05-15 01:49:54 -070031#include <boost/lexical_cast.hpp>
32
hilatadd50ada2014-03-13 12:48:47 -050033#include <sys/types.h>
34#include <netinet/in.h>
35#include <arpa/nameser.h>
36#include <resolv.h>
37
38#ifdef __APPLE__
39#include <arpa/nameser_compat.h>
40#endif
41
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070042namespace tools {
43
Alexander Afanasyevb47d5382014-05-05 14:35:03 -070044void
45usage(const char* programName)
46{
47 std::cout << "Usage:\n" << programName << " [-h] [-V]\n"
48 << " -h - print usage and exit\n"
49 << " -V - print version number and exit\n"
50 << std::endl;
51}
52
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070053class NdnAutoconfig
hilatadd50ada2014-03-13 12:48:47 -050054{
55public:
56 union QueryAnswer
57 {
58 HEADER header;
59 uint8_t buf[NS_PACKETSZ];
60 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070061
62 class Error : public std::runtime_error
hilatadd50ada2014-03-13 12:48:47 -050063 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070064 public:
65 explicit
66 Error(const std::string& what)
67 : std::runtime_error(what)
hilatadd50ada2014-03-13 12:48:47 -050068 {
69 }
70 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070071
hilatadd50ada2014-03-13 12:48:47 -050072 explicit
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070073 NdnAutoconfig()
74 : m_controller(m_face)
hilatadd50ada2014-03-13 12:48:47 -050075 {
76 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +010077
hilatadd50ada2014-03-13 12:48:47 -050078 // Start to look for a hub (NDN hub discovery first stage)
79 void
80 discoverHubStage1()
81 {
82 ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070083 interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
hilatadd50ada2014-03-13 12:48:47 -050084 interest.setMustBeFresh(true);
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070085
hilata3a4eb5c2014-04-29 00:32:05 -050086 std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
hilatadd50ada2014-03-13 12:48:47 -050087 m_face.expressInterest(interest,
88 ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
89 ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070090
hilatadd50ada2014-03-13 12:48:47 -050091 m_face.processEvents();
92 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070093
hilatadd50ada2014-03-13 12:48:47 -050094 // First stage OnData Callback
95 void
96 onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
97 {
98 const ndn::Block& content = data.getContent();
99 content.parse();
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700100
hilatadd50ada2014-03-13 12:48:47 -0500101 // Get Uri
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700102 ndn::Block::element_const_iterator blockValue = content.find(ndn::tlv::nfd::Uri);
103 if (blockValue == content.elements_end())
hilatadd50ada2014-03-13 12:48:47 -0500104 {
105 discoverHubStage2(interest, "Incorrect reply to stage1");
106 return;
107 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700108 std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
109 blockValue->value_size());
hilatadd50ada2014-03-13 12:48:47 -0500110 connectToHub(faceMgmtUri);
111 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700112
hilatadd50ada2014-03-13 12:48:47 -0500113 // First stage OnTimeout callback - start 2nd stage
114 void
115 discoverHubStage2(const ndn::Interest& interest, const std::string& message)
116 {
117 std::cerr << message << std::endl;
118 std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700119
120 _res.retry = 2;
hilatadd50ada2014-03-13 12:48:47 -0500121 _res.ndots = 10;
122
123 QueryAnswer queryAnswer;
124
125 int answerSize = res_search("_ndn._udp",
126 ns_c_in,
127 ns_t_srv,
128 queryAnswer.buf,
129 sizeof(queryAnswer));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700130
hilatadd50ada2014-03-13 12:48:47 -0500131 // 2nd stage failed - move on to the third stage
132 if (answerSize < 0)
133 {
134 discoverHubStage3("Failed to find NDN router using default suffix DNS query");
135 }
136 else
137 {
138 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
139 if (isParsed == false)
140 {
141 // Failed to parse DNS response, try stage 3
142 discoverHubStage3("Failed to parse DNS response");
143 }
144 }
145 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700146
hilatadd50ada2014-03-13 12:48:47 -0500147 // Second stage OnTimeout callback
148 void
149 discoverHubStage3(const std::string& message)
150 {
151 std::cerr << message << std::endl;
152 std::cerr << "Stage 3: Trying to find home router..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700153
hilatadd50ada2014-03-13 12:48:47 -0500154 ndn::KeyChain keyChain;
155 ndn::Name identity = keyChain.getDefaultIdentity();
156 std::string serverName = "_ndn._udp.";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700157
hilatadd50ada2014-03-13 12:48:47 -0500158 for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
159 {
Alexander Afanasyevb3893c92014-05-15 01:49:54 -0700160 serverName.append(i->toUri());
hilatadd50ada2014-03-13 12:48:47 -0500161 serverName.append(".");
162 }
163 serverName += "_homehub._autoconf.named-data.net";
164 std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700165
hilatadd50ada2014-03-13 12:48:47 -0500166 QueryAnswer queryAnswer;
167
168 int answerSize = res_query(serverName.c_str(),
169 ns_c_in,
170 ns_t_srv,
171 queryAnswer.buf,
172 sizeof(queryAnswer));
173
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700174
hilatadd50ada2014-03-13 12:48:47 -0500175 // 3rd stage failed - abort
176 if (answerSize < 0)
177 {
178 std::cerr << "Failed to find a home router" << std::endl;
179 std::cerr << "exit" << std::endl;
180 }
181 else
182 {
183 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
184 if (isParsed == false)
185 {
186 // Failed to parse DNS response
187 throw Error("Failed to parse DNS response");
188 }
189 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700190
hilatadd50ada2014-03-13 12:48:47 -0500191 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700192
hilatadd50ada2014-03-13 12:48:47 -0500193 void
194 connectToHub(const std::string& uri)
195 {
196 std::cerr << "about to connect to: " << uri << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700197
198 m_controller.start<ndn::nfd::FaceCreateCommand>(
hilata3a4eb5c2014-04-29 00:32:05 -0500199 ndn::nfd::ControlParameters()
200 .setUri(uri),
201 bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
202 bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700203 );
hilatadd50ada2014-03-13 12:48:47 -0500204 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700205
hilatadd50ada2014-03-13 12:48:47 -0500206 void
hilata3a4eb5c2014-04-29 00:32:05 -0500207 onHubConnectSuccess(const ndn::nfd::ControlParameters& resp)
hilatadd50ada2014-03-13 12:48:47 -0500208 {
hilata3a4eb5c2014-04-29 00:32:05 -0500209 std::cerr << "Successfully created face: " << resp << std::endl;
hilataf4f86732014-05-03 19:06:34 -0500210
211 // Register a prefix in RIB
212 ndn::nfd::ControlParameters ribParameters;
213 ribParameters
214 .setName("/ndn")
215 .setFaceId(resp.getFaceId());
216
217 m_controller.start<ndn::nfd::RibRegisterCommand>(
218 ribParameters,
219 bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
220 bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
hilatadd50ada2014-03-13 12:48:47 -0500221 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700222
hilatadd50ada2014-03-13 12:48:47 -0500223 void
hilata3a4eb5c2014-04-29 00:32:05 -0500224 onHubConnectError(uint32_t code, const std::string& error)
hilatadd50ada2014-03-13 12:48:47 -0500225 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700226 std::ostringstream os;
hilata3a4eb5c2014-04-29 00:32:05 -0500227 os << "Failed to create face: " << error << " (code: " << code << ")";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700228 throw Error(os.str());
hilatadd50ada2014-03-13 12:48:47 -0500229 }
230
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700231
hilatadd50ada2014-03-13 12:48:47 -0500232 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
233 {
234 // The references of the next classes are:
235 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
236 // https://gist.github.com/mologie/6027597
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700237
238 struct rechdr
hilatadd50ada2014-03-13 12:48:47 -0500239 {
hilatadd50ada2014-03-13 12:48:47 -0500240 uint16_t type;
241 uint16_t iclass;
242 uint32_t ttl;
243 uint16_t length;
244 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700245
246 struct srv_t
hilatadd50ada2014-03-13 12:48:47 -0500247 {
hilatadd50ada2014-03-13 12:48:47 -0500248 uint16_t priority;
249 uint16_t weight;
250 uint16_t port;
251 uint8_t* target;
252 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700253
hilatadd50ada2014-03-13 12:48:47 -0500254 if (ntohs(queryAnswer.header.ancount) == 0)
255 {
256 std::cerr << "No records found\n" << std::endl;
257 return false;
258 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700259
hilatadd50ada2014-03-13 12:48:47 -0500260 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700261
hilatadd50ada2014-03-13 12:48:47 -0500262 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700263
hilatadd50ada2014-03-13 12:48:47 -0500264 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
265 {
hilata3a4eb5c2014-04-29 00:32:05 -0500266 char srvName[NS_MAXDNAME];
267 int serverNameSize = dn_expand(queryAnswer.buf, // message pointer
268 queryAnswer.buf + answerSize, // end of message
269 blob, // compressed server name
270 srvName, // expanded server name
hilatadd50ada2014-03-13 12:48:47 -0500271 NS_MAXDNAME);
hilata3a4eb5c2014-04-29 00:32:05 -0500272 if (serverNameSize < 0)
hilatadd50ada2014-03-13 12:48:47 -0500273 {
274 return false;
275 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100276
277 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500278 uint16_t convertedPort = be16toh(server->port);
hilata3a4eb5c2014-04-29 00:32:05 -0500279
280 blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
281
282 char hostName[NS_MAXDNAME];
283 int hostNameSize = dn_expand(queryAnswer.buf, // message pointer
284 queryAnswer.buf + answerSize, // end of message
285 blob, // compressed host name
286 hostName, // expanded host name
287 NS_MAXDNAME);
288 if (hostNameSize < 0)
289 {
290 return false;
291 }
292
hilatadd50ada2014-03-13 12:48:47 -0500293 std::string uri = "udp://";
294 uri.append(hostName);
295 uri.append(":");
296 uri.append(boost::lexical_cast<std::string>(convertedPort));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700297
hilatadd50ada2014-03-13 12:48:47 -0500298 connectToHub(uri);
299 return true;
300 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700301
302 return false;
hilatadd50ada2014-03-13 12:48:47 -0500303 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700304
hilataf4f86732014-05-03 19:06:34 -0500305 void
306 onPrefixRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult)
307 {
308 std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
309 }
310
311 void
312 onPrefixRegistrationError(uint32_t code, const std::string& error)
313 {
314 std::ostringstream os;
315 os << "Failed in name registration, " << error << " (code: " << code << ")";
316 throw Error(os.str());
317 }
318
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700319private:
320 ndn::Face m_face;
321 ndn::nfd::Controller m_controller;
hilatadd50ada2014-03-13 12:48:47 -0500322};
323
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700324} // namespace tools
hilatadd50ada2014-03-13 12:48:47 -0500325
326int
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700327main(int argc, char** argv)
hilatadd50ada2014-03-13 12:48:47 -0500328{
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700329 int opt;
330 const char* programName = argv[0];
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700331
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700332 while ((opt = getopt(argc, argv, "hV")) != -1) {
333 switch (opt) {
334 case 'h':
335 tools::usage(programName);
336 return 0;
337 case 'V':
338 std::cout << NFD_VERSION_BUILD_STRING << std::endl;
339 return 0;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700340 }
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700341 }
342
343 try {
344 tools::NdnAutoconfig autoConfigInstance;
345
346 autoConfigInstance.discoverHubStage1();
347 }
348 catch (const std::exception& error) {
349 std::cerr << "ERROR: " << error.what() << std::endl;
350 return 1;
351 }
hilatadd50ada2014-03-13 12:48:47 -0500352 return 0;
353}