blob: 11e4fe56b3c6f532295d8dc45fb2a8e6e46f0497 [file] [log] [blame]
Junxiao Shi77dcadd2014-10-05 14:40:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoa84f4642017-08-23 16:14:51 -04002/*
Junxiao Shid5c2f0c2017-04-04 09:52:11 +00003 * Copyright (c) 2013-2017 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 * The University of Memphis.
Junxiao Shi77dcadd2014-10-05 14:40:54 -070010 *
11 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
12 *
13 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
14 * terms of the GNU Lesser General Public License as published by the Free Software
15 * Foundation, either version 3 of the License, or (at your option) any later version.
16 *
17 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
20 *
21 * You should have received copies of the GNU General Public License and GNU Lesser
22 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
26 */
27
28#include "face-uri.hpp"
Junxiao Shi4083c8d2014-10-12 16:43:16 -070029#include "dns.hpp"
Junxiao Shi77dcadd2014-10-05 14:40:54 -070030
Junxiao Shie3ef6ee2014-10-05 14:40:54 -070031#include <boost/lexical_cast.hpp>
Junxiao Shi4083c8d2014-10-12 16:43:16 -070032#include <boost/mpl/vector.hpp>
33#include <boost/mpl/for_each.hpp>
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050034#include <boost/regex.hpp>
35#include <set>
Davide Pesaventoa84f4642017-08-23 16:14:51 -040036#include <sstream>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070037
38namespace ndn {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070039
40BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
41
42FaceUri::FaceUri()
43 : m_isV6(false)
44{
45}
46
47FaceUri::FaceUri(const std::string& uri)
48{
49 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070050 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070051 }
52}
53
54FaceUri::FaceUri(const char* uri)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050055 : FaceUri(std::string(uri))
Junxiao Shi77dcadd2014-10-05 14:40:54 -070056{
Junxiao Shi77dcadd2014-10-05 14:40:54 -070057}
58
59bool
60FaceUri::parse(const std::string& uri)
61{
62 m_scheme.clear();
63 m_host.clear();
Junxiao Shi77dcadd2014-10-05 14:40:54 -070064 m_port.clear();
65 m_path.clear();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050066 m_isV6 = false;
Junxiao Shi77dcadd2014-10-05 14:40:54 -070067
Weiwei Liud7f4fda2016-10-19 22:38:39 -070068 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070069 boost::smatch protocolMatch;
70 if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
71 return false;
72 }
73 m_scheme = protocolMatch[1];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070074 const std::string& authority = protocolMatch[3];
75 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070076
77 // pattern for IPv6 address enclosed in [ ], with optional port number
78 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
79 // pattern for Ethernet address in standard hex-digits-and-colons notation
80 static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
81 // pattern for IPv4-mapped IPv6 address, with optional port number
82 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
83 // pattern for IPv4/hostname/fd/ifname, with optional port number
84 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
85
86 if (authority.empty()) {
87 // UNIX, internal
88 }
89 else {
90 boost::smatch match;
91 m_isV6 = boost::regex_match(authority, match, v6Exp);
92 if (m_isV6 ||
93 boost::regex_match(authority, match, etherExp) ||
94 boost::regex_match(authority, match, v4MappedV6Exp) ||
95 boost::regex_match(authority, match, v4HostExp)) {
96 m_host = match[1];
97 m_port = match[2];
98 }
99 else {
100 return false;
101 }
102 }
103
104 return true;
105}
106
107FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
108{
109 m_isV6 = endpoint.address().is_v6();
110 m_scheme = m_isV6 ? "udp6" : "udp4";
111 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700112 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700113}
114
115FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
116{
117 m_isV6 = endpoint.address().is_v6();
118 m_scheme = m_isV6 ? "tcp6" : "tcp4";
119 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700120 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700121}
122
123FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700124{
125 m_isV6 = endpoint.address().is_v6();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500126 m_scheme = scheme;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700127 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700128 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700129}
130
131#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
132FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500133 : m_scheme("unix")
134 , m_path(endpoint.path())
135 , m_isV6(false)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700136{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700137}
138#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
139
140FaceUri
141FaceUri::fromFd(int fd)
142{
143 FaceUri uri;
144 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700145 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700146 return uri;
147}
148
149FaceUri::FaceUri(const ethernet::Address& address)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500150 : m_scheme("ether")
151 , m_host(address.toString())
152 , m_isV6(true)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700153{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700154}
155
156FaceUri
157FaceUri::fromDev(const std::string& ifname)
158{
159 FaceUri uri;
160 uri.m_scheme = "dev";
161 uri.m_host = ifname;
162 return uri;
163}
164
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700165FaceUri
166FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
167{
168 FaceUri uri;
169 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
170 uri.m_host = ifname;
171 uri.m_port = to_string(endpoint.port());
172 return uri;
173}
174
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700175bool
176FaceUri::operator==(const FaceUri& rhs) const
177{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500178 return m_isV6 == rhs.m_isV6 &&
179 m_scheme == rhs.m_scheme &&
180 m_host == rhs.m_host &&
181 m_port == rhs.m_port &&
182 m_path == rhs.m_path;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700183}
184
185bool
186FaceUri::operator!=(const FaceUri& rhs) const
187{
188 return !(*this == rhs);
189}
190
191std::string
192FaceUri::toString() const
193{
194 std::ostringstream os;
195 os << *this;
196 return os.str();
197}
198
199std::ostream&
200operator<<(std::ostream& os, const FaceUri& uri)
201{
202 os << uri.m_scheme << "://";
203 if (uri.m_isV6) {
204 os << "[" << uri.m_host << "]";
205 }
206 else {
207 os << uri.m_host;
208 }
209 if (!uri.m_port.empty()) {
210 os << ":" << uri.m_port;
211 }
212 os << uri.m_path;
213 return os;
214}
215
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500216
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700217/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
218 */
219class CanonizeProvider : noncopyable
220{
221public:
222 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200223 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700224
225 virtual std::set<std::string>
226 getSchemes() const = 0;
227
228 virtual bool
229 isCanonical(const FaceUri& faceUri) const = 0;
230
231 virtual void
232 canonize(const FaceUri& faceUri,
233 const FaceUri::CanonizeSuccessCallback& onSuccess,
234 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000235 boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700236};
237
238template<typename Protocol>
239class IpHostCanonizeProvider : public CanonizeProvider
240{
241public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500242 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200243 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700244 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500245 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700246 }
247
Davide Pesavento57c07df2016-12-11 18:41:45 -0500248 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200249 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700250 {
251 if (faceUri.getPort().empty()) {
252 return false;
253 }
254 if (!faceUri.getPath().empty()) {
255 return false;
256 }
257
258 boost::system::error_code ec;
259 boost::asio::ip::address addr;
260 if (faceUri.getScheme() == m_v4Scheme) {
261 addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
262 }
263 else if (faceUri.getScheme() == m_v6Scheme) {
264 addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
265 }
266 else {
267 return false;
268 }
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500269
270 return !ec && addr.to_string() == faceUri.getHost() && checkAddress(addr).first;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700271 }
272
Davide Pesavento57c07df2016-12-11 18:41:45 -0500273 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700274 canonize(const FaceUri& faceUri,
275 const FaceUri::CanonizeSuccessCallback& onSuccess,
276 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000277 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700278 {
279 if (this->isCanonical(faceUri)) {
280 onSuccess(faceUri);
281 return;
282 }
283
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700284 // make a copy because caller may modify faceUri
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500285 auto uri = make_shared<FaceUri>(faceUri);
Eric Newberry71aecae2017-05-29 00:22:39 -0700286 boost::system::error_code ec;
287 auto ipAddress = boost::asio::ip::address::from_string(faceUri.getHost(), ec);
288 if (!ec) {
289 // No need to resolve IP address if host is already an IP
290 if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
291 (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
292 return onFailure("IPv4/v6 mismatch");
293 }
294
295 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
296 }
297 else {
298 dns::AddressSelector addressSelector;
299 if (faceUri.getScheme() == m_v4Scheme) {
300 addressSelector = dns::Ipv4Only();
301 }
302 else if (faceUri.getScheme() == m_v6Scheme) {
303 addressSelector = dns::Ipv6Only();
304 }
305 else {
306 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
307 addressSelector = dns::AnyAddress();
308 }
309
310 dns::asyncResolve(faceUri.getHost(),
311 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
312 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
313 io, addressSelector, timeout);
314 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700315 }
316
317protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200318 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700319 IpHostCanonizeProvider(const std::string& baseScheme,
Davide Pesaventof78cb702016-12-11 14:42:40 -0500320 uint16_t defaultUnicastPort = 6363,
321 uint16_t defaultMulticastPort = 56363)
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700322 : m_baseScheme(baseScheme)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500323 , m_v4Scheme(baseScheme + '4')
324 , m_v6Scheme(baseScheme + '6')
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700325 , m_defaultUnicastPort(defaultUnicastPort)
326 , m_defaultMulticastPort(defaultMulticastPort)
327 {
328 }
329
330private:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700331 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500332 onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700333 const FaceUri::CanonizeSuccessCallback& onSuccess,
334 const FaceUri::CanonizeFailureCallback& onFailure,
335 const dns::IpAddress& ipAddress) const
336 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500337 bool isOk = false;
338 std::string reason;
339 std::tie(isOk, reason) = this->checkAddress(ipAddress);
340 if (!isOk) {
341 return onFailure(reason);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700342 }
343
Davide Pesaventof78cb702016-12-11 14:42:40 -0500344 uint16_t port = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700345 if (faceUri->getPort().empty()) {
346 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
347 }
348 else {
349 try {
Davide Pesaventof78cb702016-12-11 14:42:40 -0500350 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700351 }
Davide Pesaventof78cb702016-12-11 14:42:40 -0500352 catch (const boost::bad_lexical_cast&) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500353 return onFailure("invalid port number '" + faceUri->getPort() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700354 }
355 }
356
357 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
358 BOOST_ASSERT(canonicalUri.isCanonical());
359 onSuccess(canonicalUri);
360 }
361
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700362 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500363 onDnsFailure(const shared_ptr<FaceUri>& faceUri,
364 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700365 const std::string& reason) const
366 {
367 onFailure(reason);
368 }
369
370 /** \brief when overriden in a subclass, check the IP address is allowable
371 * \return (true,ignored) if the address is allowable;
372 * (false,reason) if the address is not allowable.
373 */
374 virtual std::pair<bool, std::string>
375 checkAddress(const dns::IpAddress& ipAddress) const
376 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200377 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700378 }
379
380private:
381 std::string m_baseScheme;
382 std::string m_v4Scheme;
383 std::string m_v6Scheme;
Davide Pesaventof78cb702016-12-11 14:42:40 -0500384 uint16_t m_defaultUnicastPort;
385 uint16_t m_defaultMulticastPort;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700386};
387
388class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
389{
390public:
391 UdpCanonizeProvider()
392 : IpHostCanonizeProvider("udp")
393 {
394 }
395
396protected:
397 // checkAddress is not overriden:
398 // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
399 // FaceMgmt protocol allows IPv6 multicast address in UDP.
400};
401
402class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
403{
404public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700405 TcpCanonizeProvider()
406 : IpHostCanonizeProvider("tcp")
407 {
408 }
409
410protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500411 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200412 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700413 {
414 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200415 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700416 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200417 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700418 }
419};
420
421class EtherCanonizeProvider : public CanonizeProvider
422{
423public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500424 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200425 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700426 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500427 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700428 }
429
Davide Pesavento57c07df2016-12-11 18:41:45 -0500430 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200431 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700432 {
433 if (!faceUri.getPort().empty()) {
434 return false;
435 }
436 if (!faceUri.getPath().empty()) {
437 return false;
438 }
439
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500440 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700441 return addr.toString() == faceUri.getHost();
442 }
443
Davide Pesavento57c07df2016-12-11 18:41:45 -0500444 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700445 canonize(const FaceUri& faceUri,
446 const FaceUri::CanonizeSuccessCallback& onSuccess,
447 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000448 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700449 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500450 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700451 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500452 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700453 }
454
455 FaceUri canonicalUri(addr);
456 BOOST_ASSERT(canonicalUri.isCanonical());
457 onSuccess(canonicalUri);
458 }
459};
460
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000461class DevCanonizeProvider : public CanonizeProvider
462{
463public:
464 std::set<std::string>
465 getSchemes() const override
466 {
467 return {"dev"};
468 }
469
470 bool
471 isCanonical(const FaceUri& faceUri) const override
472 {
473 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
474 }
475
476 void
477 canonize(const FaceUri& faceUri,
478 const FaceUri::CanonizeSuccessCallback& onSuccess,
479 const FaceUri::CanonizeFailureCallback& onFailure,
480 boost::asio::io_service& io, time::nanoseconds timeout) const override
481 {
482 if (faceUri.getHost().empty()) {
483 onFailure("network interface name is missing");
484 return;
485 }
486 if (!faceUri.getPort().empty()) {
487 onFailure("port number is not allowed");
488 return;
489 }
490 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
491 onFailure("path is not allowed");
492 return;
493 }
494
495 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
496 BOOST_ASSERT(canonicalUri.isCanonical());
497 onSuccess(canonicalUri);
498 }
499};
500
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700501class UdpDevCanonizeProvider : public CanonizeProvider
502{
503public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500504 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700505 getSchemes() const override
506 {
507 return {"udp4+dev", "udp6+dev"};
508 }
509
Davide Pesavento57c07df2016-12-11 18:41:45 -0500510 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700511 isCanonical(const FaceUri& faceUri) const override
512 {
513 if (faceUri.getPort().empty()) {
514 return false;
515 }
516 if (!faceUri.getPath().empty()) {
517 return false;
518 }
519 return true;
520 }
521
Davide Pesavento57c07df2016-12-11 18:41:45 -0500522 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700523 canonize(const FaceUri& faceUri,
524 const FaceUri::CanonizeSuccessCallback& onSuccess,
525 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000526 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700527 {
528 if (this->isCanonical(faceUri)) {
529 onSuccess(faceUri);
530 }
531 else {
532 onFailure("cannot canonize " + faceUri.toString());
533 }
534 }
535};
536
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500537using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
538 TcpCanonizeProvider*,
539 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000540 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500541 UdpDevCanonizeProvider*>;
542using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700543
544class CanonizeProviderTableInitializer
545{
546public:
547 explicit
548 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
549 : m_providerTable(providerTable)
550 {
551 }
552
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500553 template<typename CP>
554 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700555 operator()(CP*)
556 {
557 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500558 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700559 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500560
561 for (const auto& scheme : schemes) {
562 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
563 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700564 }
565 }
566
567private:
568 CanonizeProviderTable& m_providerTable;
569};
570
571static const CanonizeProvider*
572getCanonizeProvider(const std::string& scheme)
573{
574 static CanonizeProviderTable providerTable;
575 if (providerTable.empty()) {
576 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
577 BOOST_ASSERT(!providerTable.empty());
578 }
579
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200580 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500581 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700582}
583
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500584
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700585bool
586FaceUri::canCanonize(const std::string& scheme)
587{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500588 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700589}
590
591bool
592FaceUri::isCanonical() const
593{
594 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500595 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700596 return false;
597 }
598
599 return cp->isCanonical(*this);
600}
601
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700602void
603FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
604 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000605 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700606{
607 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200608 if (cp == nullptr) {
609 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700610 onFailure("scheme not supported");
611 }
612 return;
613 }
614
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200615 static CanonizeSuccessCallback successNop = bind([]{});
616 static CanonizeFailureCallback failureNop = bind([]{});
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700617 cp->canonize(*this,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200618 onSuccess ? onSuccess : successNop,
619 onFailure ? onFailure : failureNop,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700620 io, timeout);
621}
622
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700623} // namespace ndn