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