blob: fe8ce19303c20a36323c2de9ebc949409517b68f [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
31#include <sys/types.h>
32#include <netinet/in.h>
33#include <arpa/nameser.h>
34#include <resolv.h>
35
36#ifdef __APPLE__
37#include <arpa/nameser_compat.h>
38#endif
39
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070040namespace tools {
41
Alexander Afanasyevb47d5382014-05-05 14:35:03 -070042void
43usage(const char* programName)
44{
45 std::cout << "Usage:\n" << programName << " [-h] [-V]\n"
46 << " -h - print usage and exit\n"
47 << " -V - print version number and exit\n"
48 << std::endl;
49}
50
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070051class NdnAutoconfig
hilatadd50ada2014-03-13 12:48:47 -050052{
53public:
54 union QueryAnswer
55 {
56 HEADER header;
57 uint8_t buf[NS_PACKETSZ];
58 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070059
60 class Error : public std::runtime_error
hilatadd50ada2014-03-13 12:48:47 -050061 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070062 public:
63 explicit
64 Error(const std::string& what)
65 : std::runtime_error(what)
hilatadd50ada2014-03-13 12:48:47 -050066 {
67 }
68 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070069
hilatadd50ada2014-03-13 12:48:47 -050070 explicit
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070071 NdnAutoconfig()
72 : m_controller(m_face)
hilatadd50ada2014-03-13 12:48:47 -050073 {
74 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +010075
hilatadd50ada2014-03-13 12:48:47 -050076 // Start to look for a hub (NDN hub discovery first stage)
77 void
78 discoverHubStage1()
79 {
80 ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070081 interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
hilatadd50ada2014-03-13 12:48:47 -050082 interest.setMustBeFresh(true);
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070083
hilata3a4eb5c2014-04-29 00:32:05 -050084 std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
hilatadd50ada2014-03-13 12:48:47 -050085 m_face.expressInterest(interest,
86 ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
87 ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070088
hilatadd50ada2014-03-13 12:48:47 -050089 m_face.processEvents();
90 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070091
hilatadd50ada2014-03-13 12:48:47 -050092 // First stage OnData Callback
93 void
94 onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
95 {
96 const ndn::Block& content = data.getContent();
97 content.parse();
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070098
hilatadd50ada2014-03-13 12:48:47 -050099 // Get Uri
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700100 ndn::Block::element_const_iterator blockValue = content.find(ndn::tlv::nfd::Uri);
101 if (blockValue == content.elements_end())
hilatadd50ada2014-03-13 12:48:47 -0500102 {
103 discoverHubStage2(interest, "Incorrect reply to stage1");
104 return;
105 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700106 std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
107 blockValue->value_size());
hilatadd50ada2014-03-13 12:48:47 -0500108 connectToHub(faceMgmtUri);
109 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700110
hilatadd50ada2014-03-13 12:48:47 -0500111 // First stage OnTimeout callback - start 2nd stage
112 void
113 discoverHubStage2(const ndn::Interest& interest, const std::string& message)
114 {
115 std::cerr << message << std::endl;
116 std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700117
118 _res.retry = 2;
hilatadd50ada2014-03-13 12:48:47 -0500119 _res.ndots = 10;
120
121 QueryAnswer queryAnswer;
122
123 int answerSize = res_search("_ndn._udp",
124 ns_c_in,
125 ns_t_srv,
126 queryAnswer.buf,
127 sizeof(queryAnswer));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700128
hilatadd50ada2014-03-13 12:48:47 -0500129 // 2nd stage failed - move on to the third stage
130 if (answerSize < 0)
131 {
132 discoverHubStage3("Failed to find NDN router using default suffix DNS query");
133 }
134 else
135 {
136 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
137 if (isParsed == false)
138 {
139 // Failed to parse DNS response, try stage 3
140 discoverHubStage3("Failed to parse DNS response");
141 }
142 }
143 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700144
hilatadd50ada2014-03-13 12:48:47 -0500145 // Second stage OnTimeout callback
146 void
147 discoverHubStage3(const std::string& message)
148 {
149 std::cerr << message << std::endl;
150 std::cerr << "Stage 3: Trying to find home router..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700151
hilatadd50ada2014-03-13 12:48:47 -0500152 ndn::KeyChain keyChain;
153 ndn::Name identity = keyChain.getDefaultIdentity();
154 std::string serverName = "_ndn._udp.";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700155
hilatadd50ada2014-03-13 12:48:47 -0500156 for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
157 {
158 serverName.append(i->toEscapedString());
159 serverName.append(".");
160 }
161 serverName += "_homehub._autoconf.named-data.net";
162 std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700163
hilatadd50ada2014-03-13 12:48:47 -0500164 QueryAnswer queryAnswer;
165
166 int answerSize = res_query(serverName.c_str(),
167 ns_c_in,
168 ns_t_srv,
169 queryAnswer.buf,
170 sizeof(queryAnswer));
171
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700172
hilatadd50ada2014-03-13 12:48:47 -0500173 // 3rd stage failed - abort
174 if (answerSize < 0)
175 {
176 std::cerr << "Failed to find a home router" << std::endl;
177 std::cerr << "exit" << std::endl;
178 }
179 else
180 {
181 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
182 if (isParsed == false)
183 {
184 // Failed to parse DNS response
185 throw Error("Failed to parse DNS response");
186 }
187 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700188
hilatadd50ada2014-03-13 12:48:47 -0500189 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700190
hilatadd50ada2014-03-13 12:48:47 -0500191 void
192 connectToHub(const std::string& uri)
193 {
194 std::cerr << "about to connect to: " << uri << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700195
196 m_controller.start<ndn::nfd::FaceCreateCommand>(
hilata3a4eb5c2014-04-29 00:32:05 -0500197 ndn::nfd::ControlParameters()
198 .setUri(uri),
199 bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
200 bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700201 );
hilatadd50ada2014-03-13 12:48:47 -0500202 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700203
hilatadd50ada2014-03-13 12:48:47 -0500204 void
hilata3a4eb5c2014-04-29 00:32:05 -0500205 onHubConnectSuccess(const ndn::nfd::ControlParameters& resp)
hilatadd50ada2014-03-13 12:48:47 -0500206 {
hilata3a4eb5c2014-04-29 00:32:05 -0500207 std::cerr << "Successfully created face: " << resp << std::endl;
hilataf4f86732014-05-03 19:06:34 -0500208
209 // Register a prefix in RIB
210 ndn::nfd::ControlParameters ribParameters;
211 ribParameters
212 .setName("/ndn")
213 .setFaceId(resp.getFaceId());
214
215 m_controller.start<ndn::nfd::RibRegisterCommand>(
216 ribParameters,
217 bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
218 bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
hilatadd50ada2014-03-13 12:48:47 -0500219 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700220
hilatadd50ada2014-03-13 12:48:47 -0500221 void
hilata3a4eb5c2014-04-29 00:32:05 -0500222 onHubConnectError(uint32_t code, const std::string& error)
hilatadd50ada2014-03-13 12:48:47 -0500223 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700224 std::ostringstream os;
hilata3a4eb5c2014-04-29 00:32:05 -0500225 os << "Failed to create face: " << error << " (code: " << code << ")";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700226 throw Error(os.str());
hilatadd50ada2014-03-13 12:48:47 -0500227 }
228
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700229
hilatadd50ada2014-03-13 12:48:47 -0500230 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
231 {
232 // The references of the next classes are:
233 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
234 // https://gist.github.com/mologie/6027597
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700235
236 struct rechdr
hilatadd50ada2014-03-13 12:48:47 -0500237 {
hilatadd50ada2014-03-13 12:48:47 -0500238 uint16_t type;
239 uint16_t iclass;
240 uint32_t ttl;
241 uint16_t length;
242 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700243
244 struct srv_t
hilatadd50ada2014-03-13 12:48:47 -0500245 {
hilatadd50ada2014-03-13 12:48:47 -0500246 uint16_t priority;
247 uint16_t weight;
248 uint16_t port;
249 uint8_t* target;
250 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700251
hilatadd50ada2014-03-13 12:48:47 -0500252 if (ntohs(queryAnswer.header.ancount) == 0)
253 {
254 std::cerr << "No records found\n" << std::endl;
255 return false;
256 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700257
hilatadd50ada2014-03-13 12:48:47 -0500258 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700259
hilatadd50ada2014-03-13 12:48:47 -0500260 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700261
hilatadd50ada2014-03-13 12:48:47 -0500262 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
263 {
hilata3a4eb5c2014-04-29 00:32:05 -0500264 char srvName[NS_MAXDNAME];
265 int serverNameSize = dn_expand(queryAnswer.buf, // message pointer
266 queryAnswer.buf + answerSize, // end of message
267 blob, // compressed server name
268 srvName, // expanded server name
hilatadd50ada2014-03-13 12:48:47 -0500269 NS_MAXDNAME);
hilata3a4eb5c2014-04-29 00:32:05 -0500270 if (serverNameSize < 0)
hilatadd50ada2014-03-13 12:48:47 -0500271 {
272 return false;
273 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100274
275 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500276 uint16_t convertedPort = be16toh(server->port);
hilata3a4eb5c2014-04-29 00:32:05 -0500277
278 blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
279
280 char hostName[NS_MAXDNAME];
281 int hostNameSize = dn_expand(queryAnswer.buf, // message pointer
282 queryAnswer.buf + answerSize, // end of message
283 blob, // compressed host name
284 hostName, // expanded host name
285 NS_MAXDNAME);
286 if (hostNameSize < 0)
287 {
288 return false;
289 }
290
hilatadd50ada2014-03-13 12:48:47 -0500291 std::string uri = "udp://";
292 uri.append(hostName);
293 uri.append(":");
294 uri.append(boost::lexical_cast<std::string>(convertedPort));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700295
hilatadd50ada2014-03-13 12:48:47 -0500296 connectToHub(uri);
297 return true;
298 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700299
300 return false;
hilatadd50ada2014-03-13 12:48:47 -0500301 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700302
hilataf4f86732014-05-03 19:06:34 -0500303 void
304 onPrefixRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult)
305 {
306 std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
307 }
308
309 void
310 onPrefixRegistrationError(uint32_t code, const std::string& error)
311 {
312 std::ostringstream os;
313 os << "Failed in name registration, " << error << " (code: " << code << ")";
314 throw Error(os.str());
315 }
316
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700317private:
318 ndn::Face m_face;
319 ndn::nfd::Controller m_controller;
hilatadd50ada2014-03-13 12:48:47 -0500320};
321
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700322} // namespace tools
hilatadd50ada2014-03-13 12:48:47 -0500323
324int
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700325main(int argc, char** argv)
hilatadd50ada2014-03-13 12:48:47 -0500326{
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700327 int opt;
328 const char* programName = argv[0];
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700329
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700330 while ((opt = getopt(argc, argv, "hV")) != -1) {
331 switch (opt) {
332 case 'h':
333 tools::usage(programName);
334 return 0;
335 case 'V':
336 std::cout << NFD_VERSION_BUILD_STRING << std::endl;
337 return 0;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700338 }
Alexander Afanasyevb47d5382014-05-05 14:35:03 -0700339 }
340
341 try {
342 tools::NdnAutoconfig autoConfigInstance;
343
344 autoConfigInstance.discoverHubStage1();
345 }
346 catch (const std::exception& error) {
347 std::cerr << "ERROR: " << error.what() << std::endl;
348 return 1;
349 }
hilatadd50ada2014-03-13 12:48:47 -0500350 return 0;
351}