blob: 3059dc404def3039d73945b646b95364cd54aa2b [file] [log] [blame]
hilatadd50ada2014-03-13 12:48:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
Davide Pesavento6fc750f2014-03-18 19:49:39 +01006
hilatadd50ada2014-03-13 12:48:47 -05007#include <ndn-cpp-dev/face.hpp>
8#include <ndn-cpp-dev/management/nfd-controller.hpp>
hilatadd50ada2014-03-13 12:48:47 -05009#include <ndn-cpp-dev/security/key-chain.hpp>
10
11#include <sys/types.h>
12#include <netinet/in.h>
13#include <arpa/nameser.h>
14#include <resolv.h>
15
16#ifdef __APPLE__
17#include <arpa/nameser_compat.h>
18#endif
19
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070020namespace tools {
21
22class NdnAutoconfig
hilatadd50ada2014-03-13 12:48:47 -050023{
24public:
25 union QueryAnswer
26 {
27 HEADER header;
28 uint8_t buf[NS_PACKETSZ];
29 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070030
31 class Error : public std::runtime_error
hilatadd50ada2014-03-13 12:48:47 -050032 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070033 public:
34 explicit
35 Error(const std::string& what)
36 : std::runtime_error(what)
hilatadd50ada2014-03-13 12:48:47 -050037 {
38 }
39 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070040
hilatadd50ada2014-03-13 12:48:47 -050041 explicit
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070042 NdnAutoconfig()
43 : m_controller(m_face)
hilatadd50ada2014-03-13 12:48:47 -050044 {
45 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +010046
hilatadd50ada2014-03-13 12:48:47 -050047 // Start to look for a hub (NDN hub discovery first stage)
48 void
49 discoverHubStage1()
50 {
51 ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070052 interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
hilatadd50ada2014-03-13 12:48:47 -050053 interest.setMustBeFresh(true);
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070054
hilatadd50ada2014-03-13 12:48:47 -050055 std::cerr << "Stage 1: Trying muticast discovery..." << std::endl;
56 m_face.expressInterest(interest,
57 ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
58 ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070059
hilatadd50ada2014-03-13 12:48:47 -050060 m_face.processEvents();
61 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070062
hilatadd50ada2014-03-13 12:48:47 -050063 // First stage OnData Callback
64 void
65 onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
66 {
67 const ndn::Block& content = data.getContent();
68 content.parse();
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070069
hilatadd50ada2014-03-13 12:48:47 -050070 // Get Uri
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070071 ndn::Block::element_const_iterator blockValue = content.find(ndn::tlv::nfd::Uri);
72 if (blockValue == content.elements_end())
hilatadd50ada2014-03-13 12:48:47 -050073 {
74 discoverHubStage2(interest, "Incorrect reply to stage1");
75 return;
76 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070077 std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
78 blockValue->value_size());
hilatadd50ada2014-03-13 12:48:47 -050079 connectToHub(faceMgmtUri);
80 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070081
hilatadd50ada2014-03-13 12:48:47 -050082 // First stage OnTimeout callback - start 2nd stage
83 void
84 discoverHubStage2(const ndn::Interest& interest, const std::string& message)
85 {
86 std::cerr << message << std::endl;
87 std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070088
89 _res.retry = 2;
hilatadd50ada2014-03-13 12:48:47 -050090 _res.ndots = 10;
91
92 QueryAnswer queryAnswer;
93
94 int answerSize = res_search("_ndn._udp",
95 ns_c_in,
96 ns_t_srv,
97 queryAnswer.buf,
98 sizeof(queryAnswer));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -070099
hilatadd50ada2014-03-13 12:48:47 -0500100 // 2nd stage failed - move on to the third stage
101 if (answerSize < 0)
102 {
103 discoverHubStage3("Failed to find NDN router using default suffix DNS query");
104 }
105 else
106 {
107 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
108 if (isParsed == false)
109 {
110 // Failed to parse DNS response, try stage 3
111 discoverHubStage3("Failed to parse DNS response");
112 }
113 }
114 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700115
hilatadd50ada2014-03-13 12:48:47 -0500116 // Second stage OnTimeout callback
117 void
118 discoverHubStage3(const std::string& message)
119 {
120 std::cerr << message << std::endl;
121 std::cerr << "Stage 3: Trying to find home router..." << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700122
hilatadd50ada2014-03-13 12:48:47 -0500123 ndn::KeyChain keyChain;
124 ndn::Name identity = keyChain.getDefaultIdentity();
125 std::string serverName = "_ndn._udp.";
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700126
hilatadd50ada2014-03-13 12:48:47 -0500127 for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
128 {
129 serverName.append(i->toEscapedString());
130 serverName.append(".");
131 }
132 serverName += "_homehub._autoconf.named-data.net";
133 std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700134
hilatadd50ada2014-03-13 12:48:47 -0500135 QueryAnswer queryAnswer;
136
137 int answerSize = res_query(serverName.c_str(),
138 ns_c_in,
139 ns_t_srv,
140 queryAnswer.buf,
141 sizeof(queryAnswer));
142
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700143
hilatadd50ada2014-03-13 12:48:47 -0500144 // 3rd stage failed - abort
145 if (answerSize < 0)
146 {
147 std::cerr << "Failed to find a home router" << std::endl;
148 std::cerr << "exit" << std::endl;
149 }
150 else
151 {
152 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
153 if (isParsed == false)
154 {
155 // Failed to parse DNS response
156 throw Error("Failed to parse DNS response");
157 }
158 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700159
hilatadd50ada2014-03-13 12:48:47 -0500160 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700161
hilatadd50ada2014-03-13 12:48:47 -0500162 void
163 connectToHub(const std::string& uri)
164 {
165 std::cerr << "about to connect to: " << uri << std::endl;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700166
167 m_controller.start<ndn::nfd::FaceCreateCommand>(
168 ndn::nfd::ControlParameters()
169 .setUri(uri),
170 bind(&NdnAutoconfig::onHubConnectSuccess, this,
171 _1, "Succesfully created face: "),
172 bind(&NdnAutoconfig::onHubConnectError, this,
173 _1, _2, "Failed to create face: ")
174 );
hilatadd50ada2014-03-13 12:48:47 -0500175 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700176
hilatadd50ada2014-03-13 12:48:47 -0500177 void
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700178 onHubConnectSuccess(const ndn::nfd::ControlParameters& resp, const std::string& message)
hilatadd50ada2014-03-13 12:48:47 -0500179 {
180 std::cerr << message << resp << std::endl;
181 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700182
hilatadd50ada2014-03-13 12:48:47 -0500183 void
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700184 onHubConnectError(uint32_t code, const std::string& error, const std::string& message)
hilatadd50ada2014-03-13 12:48:47 -0500185 {
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700186 std::ostringstream os;
187 os << message << ": " << error << " (code: " << code << ")";
188 throw Error(os.str());
hilatadd50ada2014-03-13 12:48:47 -0500189 }
190
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700191
hilatadd50ada2014-03-13 12:48:47 -0500192 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
193 {
194 // The references of the next classes are:
195 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
196 // https://gist.github.com/mologie/6027597
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700197
198 struct rechdr
hilatadd50ada2014-03-13 12:48:47 -0500199 {
hilatadd50ada2014-03-13 12:48:47 -0500200 uint16_t type;
201 uint16_t iclass;
202 uint32_t ttl;
203 uint16_t length;
204 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700205
206 struct srv_t
hilatadd50ada2014-03-13 12:48:47 -0500207 {
hilatadd50ada2014-03-13 12:48:47 -0500208 uint16_t priority;
209 uint16_t weight;
210 uint16_t port;
211 uint8_t* target;
212 };
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700213
hilatadd50ada2014-03-13 12:48:47 -0500214 if (ntohs(queryAnswer.header.ancount) == 0)
215 {
216 std::cerr << "No records found\n" << std::endl;
217 return false;
218 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700219
hilatadd50ada2014-03-13 12:48:47 -0500220 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700221
hilatadd50ada2014-03-13 12:48:47 -0500222 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700223
hilatadd50ada2014-03-13 12:48:47 -0500224 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
225 {
226 char hostName[NS_MAXDNAME];
227 int domainNameSize = dn_expand(queryAnswer.buf,
228 queryAnswer.buf + answerSize,
229 blob,
230 hostName,
231 NS_MAXDNAME);
232 if (domainNameSize < 0)
233 {
234 return false;
235 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700236
hilatadd50ada2014-03-13 12:48:47 -0500237 blob += domainNameSize;
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100238
hilatadd50ada2014-03-13 12:48:47 -0500239 domainNameSize = dn_expand(queryAnswer.buf,
240 queryAnswer.buf + answerSize,
241 blob + 16,
242 hostName,
243 NS_MAXDNAME);
244 if (domainNameSize < 0)
245 {
246 return false;
247 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100248
249 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500250 uint16_t convertedPort = be16toh(server->port);
251 std::string uri = "udp://";
252 uri.append(hostName);
253 uri.append(":");
254 uri.append(boost::lexical_cast<std::string>(convertedPort));
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700255
hilatadd50ada2014-03-13 12:48:47 -0500256 connectToHub(uri);
257 return true;
258 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700259
260 return false;
hilatadd50ada2014-03-13 12:48:47 -0500261 }
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700262
263private:
264 ndn::Face m_face;
265 ndn::nfd::Controller m_controller;
hilatadd50ada2014-03-13 12:48:47 -0500266};
267
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700268} // namespace tools
hilatadd50ada2014-03-13 12:48:47 -0500269
270int
271main()
272{
hilatadd50ada2014-03-13 12:48:47 -0500273 try
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700274 {
275 tools::NdnAutoconfig autoConfigInstance;
276
277 autoConfigInstance.discoverHubStage1();
278 }
hilatadd50ada2014-03-13 12:48:47 -0500279 catch (const std::exception& error)
Alexander Afanasyev352e14e2014-03-27 16:02:12 -0700280 {
281 std::cerr << "ERROR: " << error.what() << std::endl;
282 }
hilatadd50ada2014-03-13 12:48:47 -0500283 return 0;
284}