blob: 1fb02b1ed81592e4f43432af5482bc8940a67088 [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>
9#include <ndn-cpp-dev/management/nfd-face-management-options.hpp>
10#include <ndn-cpp-dev/security/key-chain.hpp>
11
12#include <sys/types.h>
13#include <netinet/in.h>
14#include <arpa/nameser.h>
15#include <resolv.h>
16
17#ifdef __APPLE__
18#include <arpa/nameser_compat.h>
19#endif
20
21class NdnAutoconfig : public ndn::nfd::Controller
22{
23public:
24 union QueryAnswer
25 {
26 HEADER header;
27 uint8_t buf[NS_PACKETSZ];
28 };
29
30 struct Error : public std::runtime_error
31 {
32 Error(const std::string& what) : std::runtime_error(what)
33 {
34 }
35 };
36
37 explicit
38 NdnAutoconfig(ndn::Face& face)
39 : ndn::nfd::Controller(face)
40 {
41 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +010042
hilatadd50ada2014-03-13 12:48:47 -050043 // Start to look for a hub (NDN hub discovery first stage)
44 void
45 discoverHubStage1()
46 {
47 ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070048 interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
hilatadd50ada2014-03-13 12:48:47 -050049 interest.setMustBeFresh(true);
50
51 std::cerr << "Stage 1: Trying muticast discovery..." << std::endl;
52 m_face.expressInterest(interest,
53 ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
54 ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
55
56 m_face.processEvents();
57 }
58
59 // First stage OnData Callback
60 void
61 onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
62 {
63 const ndn::Block& content = data.getContent();
64 content.parse();
65
66 // Get Uri
67 ndn::Block::element_const_iterator bockValue = content.find(ndn::tlv::nfd::Uri);
68 if (bockValue == content.elements_end())
69 {
70 discoverHubStage2(interest, "Incorrect reply to stage1");
71 return;
72 }
73 std::string faceMgmtUri(reinterpret_cast<const char*>(bockValue->value()), bockValue->value_size());
74 connectToHub(faceMgmtUri);
75 }
76
77 // First stage OnTimeout callback - start 2nd stage
78 void
79 discoverHubStage2(const ndn::Interest& interest, const std::string& message)
80 {
81 std::cerr << message << std::endl;
82 std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
83
84 _res.retry = 2;
85 _res.ndots = 10;
86
87 QueryAnswer queryAnswer;
88
89 int answerSize = res_search("_ndn._udp",
90 ns_c_in,
91 ns_t_srv,
92 queryAnswer.buf,
93 sizeof(queryAnswer));
94
95 // 2nd stage failed - move on to the third stage
96 if (answerSize < 0)
97 {
98 discoverHubStage3("Failed to find NDN router using default suffix DNS query");
99 }
100 else
101 {
102 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
103 if (isParsed == false)
104 {
105 // Failed to parse DNS response, try stage 3
106 discoverHubStage3("Failed to parse DNS response");
107 }
108 }
109 }
110
111 // Second stage OnTimeout callback
112 void
113 discoverHubStage3(const std::string& message)
114 {
115 std::cerr << message << std::endl;
116 std::cerr << "Stage 3: Trying to find home router..." << std::endl;
117
118 ndn::KeyChain keyChain;
119 ndn::Name identity = keyChain.getDefaultIdentity();
120 std::string serverName = "_ndn._udp.";
121
122 for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
123 {
124 serverName.append(i->toEscapedString());
125 serverName.append(".");
126 }
127 serverName += "_homehub._autoconf.named-data.net";
128 std::cerr << "Stage3: About to query for a home router: " << serverName << std::endl;
129
130 QueryAnswer queryAnswer;
131
132 int answerSize = res_query(serverName.c_str(),
133 ns_c_in,
134 ns_t_srv,
135 queryAnswer.buf,
136 sizeof(queryAnswer));
137
138
139 // 3rd stage failed - abort
140 if (answerSize < 0)
141 {
142 std::cerr << "Failed to find a home router" << std::endl;
143 std::cerr << "exit" << std::endl;
144 }
145 else
146 {
147 bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
148 if (isParsed == false)
149 {
150 // Failed to parse DNS response
151 throw Error("Failed to parse DNS response");
152 }
153 }
154
155 }
156
157 void
158 connectToHub(const std::string& uri)
159 {
160 std::cerr << "about to connect to: " << uri << std::endl;
161 ndn::nfd::FaceManagementOptions faceOptions;
162
163 faceOptions.setUri(uri);
164 startFaceCommand("create",
165 faceOptions,
166 bind(&NdnAutoconfig::onHubConnectSuccess, this, _1, "Succesfully created face: "),
167 bind(&NdnAutoconfig::onHubConnectError, this, _1, "Failed to create face: "));
168 }
169
170 void
171 onHubConnectSuccess(const ndn::nfd::FaceManagementOptions& resp, const std::string& message)
172 {
173 std::cerr << message << resp << std::endl;
174 }
175
176 void
177 onHubConnectError(const std::string& error, const std::string& message)
178 {
179 throw Error(message + ": " + error);
180 }
181
182
183 bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
184 {
185 // The references of the next classes are:
186 // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
187 // https://gist.github.com/mologie/6027597
188
189 class rechdr
190 {
191 public:
192 uint16_t type;
193 uint16_t iclass;
194 uint32_t ttl;
195 uint16_t length;
196 };
197
198 class srv_t
199 {
200 public:
201 uint16_t priority;
202 uint16_t weight;
203 uint16_t port;
204 uint8_t* target;
205 };
206
207 if (ntohs(queryAnswer.header.ancount) == 0)
208 {
209 std::cerr << "No records found\n" << std::endl;
210 return false;
211 }
212
213 uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
214
215 blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
216
217 for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
218 {
219 char hostName[NS_MAXDNAME];
220 int domainNameSize = dn_expand(queryAnswer.buf,
221 queryAnswer.buf + answerSize,
222 blob,
223 hostName,
224 NS_MAXDNAME);
225 if (domainNameSize < 0)
226 {
227 return false;
228 }
229
230 blob += domainNameSize;
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100231
hilatadd50ada2014-03-13 12:48:47 -0500232 domainNameSize = dn_expand(queryAnswer.buf,
233 queryAnswer.buf + answerSize,
234 blob + 16,
235 hostName,
236 NS_MAXDNAME);
237 if (domainNameSize < 0)
238 {
239 return false;
240 }
Davide Pesavento6fc750f2014-03-18 19:49:39 +0100241
242 srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
hilatadd50ada2014-03-13 12:48:47 -0500243 uint16_t convertedPort = be16toh(server->port);
244 std::string uri = "udp://";
245 uri.append(hostName);
246 uri.append(":");
247 uri.append(boost::lexical_cast<std::string>(convertedPort));
248
249 connectToHub(uri);
250 return true;
251 }
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700252
253 return false;
hilatadd50ada2014-03-13 12:48:47 -0500254 }
255};
256
257
258int
259main()
260{
261 ndn::Face face;
262 NdnAutoconfig autoConfig(face);
263 try
264 {
265 autoConfig.discoverHubStage1();
266 }
267 catch (const std::exception& error)
268 {
269 std::cerr << "ERROR: " << error.what() << std::endl;
270 }
271 return 0;
272}