blob: 67b2e08bce2bffd238adcb4a0eff01a54ea9e268 [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
hilatadd50ada2014-03-13 12:48:47 -050073 std::cerr << "Stage 1: Trying muticast discovery..." << std::endl;
74 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>(
186 ndn::nfd::ControlParameters()
187 .setUri(uri),
188 bind(&NdnAutoconfig::onHubConnectSuccess, this,
189 _1, "Succesfully created face: "),
190 bind(&NdnAutoconfig::onHubConnectError, this,
191 _1, _2, "Failed to create face: ")
192 );
hilatadd50ada2014-03-13 12:48:47 -0500193 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700194
hilatadd50ada2014-03-13 12:48:47 -0500195 void
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700196 onHubConnectSuccess(const ndn::nfd::ControlParameters& resp, const std::string& message)
hilatadd50ada2014-03-13 12:48:47 -0500197 {
198 std::cerr << message << resp << std::endl;
199 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700200
hilatadd50ada2014-03-13 12:48:47 -0500201 void
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700202 onHubConnectError(uint32_t code, const std::string& error, const std::string& message)
hilatadd50ada2014-03-13 12:48:47 -0500203 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700204 std::ostringstream os;
205 os << message << ": " << error << " (code: " << code << ")";
206 throw Error(os.str());
hilatadd50ada2014-03-13 12:48:47 -0500207 }
208
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700209
hilatadd50ada2014-03-13 12:48:47 -0500210 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
211 {
212 // The references of the next classes are:
213 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
214 // https://gist.github.com/mologie/6027597
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700215
216 struct rechdr
hilatadd50ada2014-03-13 12:48:47 -0500217 {
hilatadd50ada2014-03-13 12:48:47 -0500218 uint16_t type;
219 uint16_t iclass;
220 uint32_t ttl;
221 uint16_t length;
222 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700223
224 struct srv_t
hilatadd50ada2014-03-13 12:48:47 -0500225 {
hilatadd50ada2014-03-13 12:48:47 -0500226 uint16_t priority;
227 uint16_t weight;
228 uint16_t port;
229 uint8_t* target;
230 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700231
hilatadd50ada2014-03-13 12:48:47 -0500232 if (ntohs(queryAnswer.header.ancount) == 0)
233 {
234 std::cerr << "No records found\n" << std::endl;
235 return false;
236 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700237
hilatadd50ada2014-03-13 12:48:47 -0500238 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700239
hilatadd50ada2014-03-13 12:48:47 -0500240 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700241
hilatadd50ada2014-03-13 12:48:47 -0500242 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
243 {
244 char hostName[NS_MAXDNAME];
245 int domainNameSize = dn_expand(queryAnswer.buf,
246 queryAnswer.buf + answerSize,
247 blob,
248 hostName,
249 NS_MAXDNAME);
250 if (domainNameSize < 0)
251 {
252 return false;
253 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700254
hilatadd50ada2014-03-13 12:48:47 -0500255 blob += domainNameSize;
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100256
hilatadd50ada2014-03-13 12:48:47 -0500257 domainNameSize = dn_expand(queryAnswer.buf,
258 queryAnswer.buf + answerSize,
259 blob + 16,
260 hostName,
261 NS_MAXDNAME);
262 if (domainNameSize < 0)
263 {
264 return false;
265 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100266
267 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500268 uint16_t convertedPort = be16toh(server->port);
269 std::string uri = "udp://";
270 uri.append(hostName);
271 uri.append(":");
272 uri.append(boost::lexical_cast<std::string>(convertedPort));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700273
hilatadd50ada2014-03-13 12:48:47 -0500274 connectToHub(uri);
275 return true;
276 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700277
278 return false;
hilatadd50ada2014-03-13 12:48:47 -0500279 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700280
281private:
282 ndn::Face m_face;
283 ndn::nfd::Controller m_controller;
hilatadd50ada2014-03-13 12:48:47 -0500284};
285
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700286} // namespace tools
hilatadd50ada2014-03-13 12:48:47 -0500287
288int
289main()
290{
hilatadd50ada2014-03-13 12:48:47 -0500291 try
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700292 {
293 tools::NdnAutoconfig autoConfigInstance;
294
295 autoConfigInstance.discoverHubStage1();
296 }
hilatadd50ada2014-03-13 12:48:47 -0500297 catch (const std::exception& error)
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700298 {
299 std::cerr << "ERROR: " << error.what() << std::endl;
300 }
hilatadd50ada2014-03-13 12:48:47 -0500301 return 0;
302}