blob: 84a73d336634a5f5fe31e8f34fb809b14f9317f2 [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 Afanasyev4a771362014-04-24 21:29:33 -070025#include <ndn-cxx/face.hpp>
26#include <ndn-cxx/management/nfd-controller.hpp>
27#include <ndn-cxx/security/key-chain.hpp>
hilatadd50ada2014-03-13 12:48:47 -050028
29#include <sys/types.h>
30#include <netinet/in.h>
31#include <arpa/nameser.h>
32#include <resolv.h>
33
34#ifdef __APPLE__
35#include <arpa/nameser_compat.h>
36#endif
37
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070038namespace tools {
39
40class NdnAutoconfig
hilatadd50ada2014-03-13 12:48:47 -050041{
42public:
43 union QueryAnswer
44 {
45 HEADER header;
46 uint8_t buf[NS_PACKETSZ];
47 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070048
49 class Error : public std::runtime_error
hilatadd50ada2014-03-13 12:48:47 -050050 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070051 public:
52 explicit
53 Error(const std::string& what)
54 : std::runtime_error(what)
hilatadd50ada2014-03-13 12:48:47 -050055 {
56 }
57 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070058
hilatadd50ada2014-03-13 12:48:47 -050059 explicit
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070060 NdnAutoconfig()
61 : m_controller(m_face)
hilatadd50ada2014-03-13 12:48:47 -050062 {
63 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +010064
hilatadd50ada2014-03-13 12:48:47 -050065 // Start to look for a hub (NDN hub discovery first stage)
66 void
67 discoverHubStage1()
68 {
69 ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070070 interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
hilatadd50ada2014-03-13 12:48:47 -050071 interest.setMustBeFresh(true);
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070072
hilata3a4eb5c2014-04-29 00:32:05 -050073 std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
hilatadd50ada2014-03-13 12:48:47 -050074 m_face.expressInterest(interest,
75 ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
76 ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070077
hilatadd50ada2014-03-13 12:48:47 -050078 m_face.processEvents();
79 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070080
hilatadd50ada2014-03-13 12:48:47 -050081 // First stage OnData Callback
82 void
83 onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
84 {
85 const ndn::Block& content = data.getContent();
86 content.parse();
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070087
hilatadd50ada2014-03-13 12:48:47 -050088 // Get Uri
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070089 ndn::Block::element_const_iterator blockValue = content.find(ndn::tlv::nfd::Uri);
90 if (blockValue == content.elements_end())
hilatadd50ada2014-03-13 12:48:47 -050091 {
92 discoverHubStage2(interest, "Incorrect reply to stage1");
93 return;
94 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070095 std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
96 blockValue->value_size());
hilatadd50ada2014-03-13 12:48:47 -050097 connectToHub(faceMgmtUri);
98 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070099
hilatadd50ada2014-03-13 12:48:47 -0500100 // First stage OnTimeout callback - start 2nd stage
101 void
102 discoverHubStage2(const ndn::Interest& interest, const std::string& message)
103 {
104 std::cerr << message << std::endl;
105 std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700106
107 _res.retry = 2;
hilatadd50ada2014-03-13 12:48:47 -0500108 _res.ndots = 10;
109
110 QueryAnswer queryAnswer;
111
112 int answerSize = res_search("_ndn._udp",
113 ns_c_in,
114 ns_t_srv,
115 queryAnswer.buf,
116 sizeof(queryAnswer));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700117
hilatadd50ada2014-03-13 12:48:47 -0500118 // 2nd stage failed - move on to the third stage
119 if (answerSize < 0)
120 {
121 discoverHubStage3("Failed to find NDN router using default suffix DNS query");
122 }
123 else
124 {
125 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
126 if (isParsed == false)
127 {
128 // Failed to parse DNS response, try stage 3
129 discoverHubStage3("Failed to parse DNS response");
130 }
131 }
132 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700133
hilatadd50ada2014-03-13 12:48:47 -0500134 // Second stage OnTimeout callback
135 void
136 discoverHubStage3(const std::string& message)
137 {
138 std::cerr << message << std::endl;
139 std::cerr << "Stage 3: Trying to find home router..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700140
hilatadd50ada2014-03-13 12:48:47 -0500141 ndn::KeyChain keyChain;
142 ndn::Name identity = keyChain.getDefaultIdentity();
143 std::string serverName = "_ndn._udp.";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700144
hilatadd50ada2014-03-13 12:48:47 -0500145 for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
146 {
147 serverName.append(i->toEscapedString());
148 serverName.append(".");
149 }
150 serverName += "_homehub._autoconf.named-data.net";
151 std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700152
hilatadd50ada2014-03-13 12:48:47 -0500153 QueryAnswer queryAnswer;
154
155 int answerSize = res_query(serverName.c_str(),
156 ns_c_in,
157 ns_t_srv,
158 queryAnswer.buf,
159 sizeof(queryAnswer));
160
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700161
hilatadd50ada2014-03-13 12:48:47 -0500162 // 3rd stage failed - abort
163 if (answerSize < 0)
164 {
165 std::cerr << "Failed to find a home router" << std::endl;
166 std::cerr << "exit" << std::endl;
167 }
168 else
169 {
170 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
171 if (isParsed == false)
172 {
173 // Failed to parse DNS response
174 throw Error("Failed to parse DNS response");
175 }
176 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700177
hilatadd50ada2014-03-13 12:48:47 -0500178 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700179
hilatadd50ada2014-03-13 12:48:47 -0500180 void
181 connectToHub(const std::string& uri)
182 {
183 std::cerr << "about to connect to: " << uri << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700184
185 m_controller.start<ndn::nfd::FaceCreateCommand>(
hilata3a4eb5c2014-04-29 00:32:05 -0500186 ndn::nfd::ControlParameters()
187 .setUri(uri),
188 bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
189 bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
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
hilata3a4eb5c2014-04-29 00:32:05 -0500194 onHubConnectSuccess(const ndn::nfd::ControlParameters& resp)
hilatadd50ada2014-03-13 12:48:47 -0500195 {
hilata3a4eb5c2014-04-29 00:32:05 -0500196 std::cerr << "Successfully created face: " << resp << std::endl;
hilataf4f86732014-05-03 19:06:34 -0500197
198 // Register a prefix in RIB
199 ndn::nfd::ControlParameters ribParameters;
200 ribParameters
201 .setName("/ndn")
202 .setFaceId(resp.getFaceId());
203
204 m_controller.start<ndn::nfd::RibRegisterCommand>(
205 ribParameters,
206 bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
207 bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
hilatadd50ada2014-03-13 12:48:47 -0500208 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700209
hilatadd50ada2014-03-13 12:48:47 -0500210 void
hilata3a4eb5c2014-04-29 00:32:05 -0500211 onHubConnectError(uint32_t code, const std::string& error)
hilatadd50ada2014-03-13 12:48:47 -0500212 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700213 std::ostringstream os;
hilata3a4eb5c2014-04-29 00:32:05 -0500214 os << "Failed to create face: " << error << " (code: " << code << ")";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700215 throw Error(os.str());
hilatadd50ada2014-03-13 12:48:47 -0500216 }
217
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700218
hilatadd50ada2014-03-13 12:48:47 -0500219 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
220 {
221 // The references of the next classes are:
222 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
223 // https://gist.github.com/mologie/6027597
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700224
225 struct rechdr
hilatadd50ada2014-03-13 12:48:47 -0500226 {
hilatadd50ada2014-03-13 12:48:47 -0500227 uint16_t type;
228 uint16_t iclass;
229 uint32_t ttl;
230 uint16_t length;
231 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700232
233 struct srv_t
hilatadd50ada2014-03-13 12:48:47 -0500234 {
hilatadd50ada2014-03-13 12:48:47 -0500235 uint16_t priority;
236 uint16_t weight;
237 uint16_t port;
238 uint8_t* target;
239 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700240
hilatadd50ada2014-03-13 12:48:47 -0500241 if (ntohs(queryAnswer.header.ancount) == 0)
242 {
243 std::cerr << "No records found\n" << std::endl;
244 return false;
245 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700246
hilatadd50ada2014-03-13 12:48:47 -0500247 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700248
hilatadd50ada2014-03-13 12:48:47 -0500249 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700250
hilatadd50ada2014-03-13 12:48:47 -0500251 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
252 {
hilata3a4eb5c2014-04-29 00:32:05 -0500253 char srvName[NS_MAXDNAME];
254 int serverNameSize = dn_expand(queryAnswer.buf, // message pointer
255 queryAnswer.buf + answerSize, // end of message
256 blob, // compressed server name
257 srvName, // expanded server name
hilatadd50ada2014-03-13 12:48:47 -0500258 NS_MAXDNAME);
hilata3a4eb5c2014-04-29 00:32:05 -0500259 if (serverNameSize < 0)
hilatadd50ada2014-03-13 12:48:47 -0500260 {
261 return false;
262 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100263
264 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500265 uint16_t convertedPort = be16toh(server->port);
hilata3a4eb5c2014-04-29 00:32:05 -0500266
267 blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
268
269 char hostName[NS_MAXDNAME];
270 int hostNameSize = dn_expand(queryAnswer.buf, // message pointer
271 queryAnswer.buf + answerSize, // end of message
272 blob, // compressed host name
273 hostName, // expanded host name
274 NS_MAXDNAME);
275 if (hostNameSize < 0)
276 {
277 return false;
278 }
279
hilatadd50ada2014-03-13 12:48:47 -0500280 std::string uri = "udp://";
281 uri.append(hostName);
282 uri.append(":");
283 uri.append(boost::lexical_cast<std::string>(convertedPort));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700284
hilatadd50ada2014-03-13 12:48:47 -0500285 connectToHub(uri);
286 return true;
287 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700288
289 return false;
hilatadd50ada2014-03-13 12:48:47 -0500290 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700291
hilataf4f86732014-05-03 19:06:34 -0500292 void
293 onPrefixRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult)
294 {
295 std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
296 }
297
298 void
299 onPrefixRegistrationError(uint32_t code, const std::string& error)
300 {
301 std::ostringstream os;
302 os << "Failed in name registration, " << error << " (code: " << code << ")";
303 throw Error(os.str());
304 }
305
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700306private:
307 ndn::Face m_face;
308 ndn::nfd::Controller m_controller;
hilatadd50ada2014-03-13 12:48:47 -0500309};
310
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700311} // namespace tools
hilatadd50ada2014-03-13 12:48:47 -0500312
313int
314main()
315{
hilatadd50ada2014-03-13 12:48:47 -0500316 try
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700317 {
318 tools::NdnAutoconfig autoConfigInstance;
319
320 autoConfigInstance.discoverHubStage1();
321 }
hilatadd50ada2014-03-13 12:48:47 -0500322 catch (const std::exception& error)
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700323 {
324 std::cerr << "ERROR: " << error.what() << std::endl;
325 }
hilatadd50ada2014-03-13 12:48:47 -0500326 return 0;
327}