blob: d550fd536f5d194faf0f7f22e6d164adf30e7491 [file] [log] [blame]
Junxiao Shi77dcadd2014-10-05 14:40:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
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>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070036
37namespace ndn {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070038
39BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
40
41FaceUri::FaceUri()
42 : m_isV6(false)
43{
44}
45
46FaceUri::FaceUri(const std::string& uri)
47{
48 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070049 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070050 }
51}
52
53FaceUri::FaceUri(const char* uri)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050054 : FaceUri(std::string(uri))
Junxiao Shi77dcadd2014-10-05 14:40:54 -070055{
Junxiao Shi77dcadd2014-10-05 14:40:54 -070056}
57
58bool
59FaceUri::parse(const std::string& uri)
60{
61 m_scheme.clear();
62 m_host.clear();
Junxiao Shi77dcadd2014-10-05 14:40:54 -070063 m_port.clear();
64 m_path.clear();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050065 m_isV6 = false;
Junxiao Shi77dcadd2014-10-05 14:40:54 -070066
Weiwei Liud7f4fda2016-10-19 22:38:39 -070067 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070068 boost::smatch protocolMatch;
69 if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
70 return false;
71 }
72 m_scheme = protocolMatch[1];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070073 const std::string& authority = protocolMatch[3];
74 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070075
76 // pattern for IPv6 address enclosed in [ ], with optional port number
77 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
78 // pattern for Ethernet address in standard hex-digits-and-colons notation
79 static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
80 // pattern for IPv4-mapped IPv6 address, with optional port number
81 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
82 // pattern for IPv4/hostname/fd/ifname, with optional port number
83 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
84
85 if (authority.empty()) {
86 // UNIX, internal
87 }
88 else {
89 boost::smatch match;
90 m_isV6 = boost::regex_match(authority, match, v6Exp);
91 if (m_isV6 ||
92 boost::regex_match(authority, match, etherExp) ||
93 boost::regex_match(authority, match, v4MappedV6Exp) ||
94 boost::regex_match(authority, match, v4HostExp)) {
95 m_host = match[1];
96 m_port = match[2];
97 }
98 else {
99 return false;
100 }
101 }
102
103 return true;
104}
105
106FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
107{
108 m_isV6 = endpoint.address().is_v6();
109 m_scheme = m_isV6 ? "udp6" : "udp4";
110 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700111 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700112}
113
114FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
115{
116 m_isV6 = endpoint.address().is_v6();
117 m_scheme = m_isV6 ? "tcp6" : "tcp4";
118 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700119 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700120}
121
122FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700123{
124 m_isV6 = endpoint.address().is_v6();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500125 m_scheme = scheme;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700126 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700127 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700128}
129
130#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
131FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500132 : m_scheme("unix")
133 , m_path(endpoint.path())
134 , m_isV6(false)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700135{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700136}
137#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
138
139FaceUri
140FaceUri::fromFd(int fd)
141{
142 FaceUri uri;
143 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700144 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700145 return uri;
146}
147
148FaceUri::FaceUri(const ethernet::Address& address)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500149 : m_scheme("ether")
150 , m_host(address.toString())
151 , m_isV6(true)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700152{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700153}
154
155FaceUri
156FaceUri::fromDev(const std::string& ifname)
157{
158 FaceUri uri;
159 uri.m_scheme = "dev";
160 uri.m_host = ifname;
161 return uri;
162}
163
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700164FaceUri
165FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
166{
167 FaceUri uri;
168 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
169 uri.m_host = ifname;
170 uri.m_port = to_string(endpoint.port());
171 return uri;
172}
173
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700174bool
175FaceUri::operator==(const FaceUri& rhs) const
176{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500177 return m_isV6 == rhs.m_isV6 &&
178 m_scheme == rhs.m_scheme &&
179 m_host == rhs.m_host &&
180 m_port == rhs.m_port &&
181 m_path == rhs.m_path;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700182}
183
184bool
185FaceUri::operator!=(const FaceUri& rhs) const
186{
187 return !(*this == rhs);
188}
189
190std::string
191FaceUri::toString() const
192{
193 std::ostringstream os;
194 os << *this;
195 return os.str();
196}
197
198std::ostream&
199operator<<(std::ostream& os, const FaceUri& uri)
200{
201 os << uri.m_scheme << "://";
202 if (uri.m_isV6) {
203 os << "[" << uri.m_host << "]";
204 }
205 else {
206 os << uri.m_host;
207 }
208 if (!uri.m_port.empty()) {
209 os << ":" << uri.m_port;
210 }
211 os << uri.m_path;
212 return os;
213}
214
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500215
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700216/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
217 */
218class CanonizeProvider : noncopyable
219{
220public:
221 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200222 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700223
224 virtual std::set<std::string>
225 getSchemes() const = 0;
226
227 virtual bool
228 isCanonical(const FaceUri& faceUri) const = 0;
229
230 virtual void
231 canonize(const FaceUri& faceUri,
232 const FaceUri::CanonizeSuccessCallback& onSuccess,
233 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000234 boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700235};
236
237template<typename Protocol>
238class IpHostCanonizeProvider : public CanonizeProvider
239{
240public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500241 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200242 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700243 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500244 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700245 }
246
Davide Pesavento57c07df2016-12-11 18:41:45 -0500247 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200248 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700249 {
250 if (faceUri.getPort().empty()) {
251 return false;
252 }
253 if (!faceUri.getPath().empty()) {
254 return false;
255 }
256
257 boost::system::error_code ec;
258 boost::asio::ip::address addr;
259 if (faceUri.getScheme() == m_v4Scheme) {
260 addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
261 }
262 else if (faceUri.getScheme() == m_v6Scheme) {
263 addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
264 }
265 else {
266 return false;
267 }
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500268
269 return !ec && addr.to_string() == faceUri.getHost() && checkAddress(addr).first;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700270 }
271
Davide Pesavento57c07df2016-12-11 18:41:45 -0500272 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700273 canonize(const FaceUri& faceUri,
274 const FaceUri::CanonizeSuccessCallback& onSuccess,
275 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000276 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700277 {
278 if (this->isCanonical(faceUri)) {
279 onSuccess(faceUri);
280 return;
281 }
282
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700283 // make a copy because caller may modify faceUri
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500284 auto uri = make_shared<FaceUri>(faceUri);
Eric Newberry71aecae2017-05-29 00:22:39 -0700285 boost::system::error_code ec;
286 auto ipAddress = boost::asio::ip::address::from_string(faceUri.getHost(), ec);
287 if (!ec) {
288 // No need to resolve IP address if host is already an IP
289 if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
290 (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
291 return onFailure("IPv4/v6 mismatch");
292 }
293
294 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
295 }
296 else {
297 dns::AddressSelector addressSelector;
298 if (faceUri.getScheme() == m_v4Scheme) {
299 addressSelector = dns::Ipv4Only();
300 }
301 else if (faceUri.getScheme() == m_v6Scheme) {
302 addressSelector = dns::Ipv6Only();
303 }
304 else {
305 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
306 addressSelector = dns::AnyAddress();
307 }
308
309 dns::asyncResolve(faceUri.getHost(),
310 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
311 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
312 io, addressSelector, timeout);
313 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700314 }
315
316protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200317 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700318 IpHostCanonizeProvider(const std::string& baseScheme,
Davide Pesaventof78cb702016-12-11 14:42:40 -0500319 uint16_t defaultUnicastPort = 6363,
320 uint16_t defaultMulticastPort = 56363)
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700321 : m_baseScheme(baseScheme)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500322 , m_v4Scheme(baseScheme + '4')
323 , m_v6Scheme(baseScheme + '6')
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700324 , m_defaultUnicastPort(defaultUnicastPort)
325 , m_defaultMulticastPort(defaultMulticastPort)
326 {
327 }
328
329private:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700330 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500331 onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700332 const FaceUri::CanonizeSuccessCallback& onSuccess,
333 const FaceUri::CanonizeFailureCallback& onFailure,
334 const dns::IpAddress& ipAddress) const
335 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500336 bool isOk = false;
337 std::string reason;
338 std::tie(isOk, reason) = this->checkAddress(ipAddress);
339 if (!isOk) {
340 return onFailure(reason);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700341 }
342
Davide Pesaventof78cb702016-12-11 14:42:40 -0500343 uint16_t port = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700344 if (faceUri->getPort().empty()) {
345 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
346 }
347 else {
348 try {
Davide Pesaventof78cb702016-12-11 14:42:40 -0500349 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700350 }
Davide Pesaventof78cb702016-12-11 14:42:40 -0500351 catch (const boost::bad_lexical_cast&) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500352 return onFailure("invalid port number '" + faceUri->getPort() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700353 }
354 }
355
356 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
357 BOOST_ASSERT(canonicalUri.isCanonical());
358 onSuccess(canonicalUri);
359 }
360
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700361 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500362 onDnsFailure(const shared_ptr<FaceUri>& faceUri,
363 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700364 const std::string& reason) const
365 {
366 onFailure(reason);
367 }
368
369 /** \brief when overriden in a subclass, check the IP address is allowable
370 * \return (true,ignored) if the address is allowable;
371 * (false,reason) if the address is not allowable.
372 */
373 virtual std::pair<bool, std::string>
374 checkAddress(const dns::IpAddress& ipAddress) const
375 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200376 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700377 }
378
379private:
380 std::string m_baseScheme;
381 std::string m_v4Scheme;
382 std::string m_v6Scheme;
Davide Pesaventof78cb702016-12-11 14:42:40 -0500383 uint16_t m_defaultUnicastPort;
384 uint16_t m_defaultMulticastPort;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700385};
386
387class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
388{
389public:
390 UdpCanonizeProvider()
391 : IpHostCanonizeProvider("udp")
392 {
393 }
394
395protected:
396 // checkAddress is not overriden:
397 // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
398 // FaceMgmt protocol allows IPv6 multicast address in UDP.
399};
400
401class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
402{
403public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700404 TcpCanonizeProvider()
405 : IpHostCanonizeProvider("tcp")
406 {
407 }
408
409protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500410 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200411 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700412 {
413 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200414 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700415 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200416 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700417 }
418};
419
420class EtherCanonizeProvider : public CanonizeProvider
421{
422public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500423 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200424 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700425 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500426 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700427 }
428
Davide Pesavento57c07df2016-12-11 18:41:45 -0500429 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200430 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700431 {
432 if (!faceUri.getPort().empty()) {
433 return false;
434 }
435 if (!faceUri.getPath().empty()) {
436 return false;
437 }
438
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500439 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700440 return addr.toString() == faceUri.getHost();
441 }
442
Davide Pesavento57c07df2016-12-11 18:41:45 -0500443 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700444 canonize(const FaceUri& faceUri,
445 const FaceUri::CanonizeSuccessCallback& onSuccess,
446 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000447 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700448 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500449 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700450 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500451 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700452 }
453
454 FaceUri canonicalUri(addr);
455 BOOST_ASSERT(canonicalUri.isCanonical());
456 onSuccess(canonicalUri);
457 }
458};
459
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000460class DevCanonizeProvider : public CanonizeProvider
461{
462public:
463 std::set<std::string>
464 getSchemes() const override
465 {
466 return {"dev"};
467 }
468
469 bool
470 isCanonical(const FaceUri& faceUri) const override
471 {
472 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
473 }
474
475 void
476 canonize(const FaceUri& faceUri,
477 const FaceUri::CanonizeSuccessCallback& onSuccess,
478 const FaceUri::CanonizeFailureCallback& onFailure,
479 boost::asio::io_service& io, time::nanoseconds timeout) const override
480 {
481 if (faceUri.getHost().empty()) {
482 onFailure("network interface name is missing");
483 return;
484 }
485 if (!faceUri.getPort().empty()) {
486 onFailure("port number is not allowed");
487 return;
488 }
489 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
490 onFailure("path is not allowed");
491 return;
492 }
493
494 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
495 BOOST_ASSERT(canonicalUri.isCanonical());
496 onSuccess(canonicalUri);
497 }
498};
499
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700500class UdpDevCanonizeProvider : public CanonizeProvider
501{
502public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500503 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700504 getSchemes() const override
505 {
506 return {"udp4+dev", "udp6+dev"};
507 }
508
Davide Pesavento57c07df2016-12-11 18:41:45 -0500509 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700510 isCanonical(const FaceUri& faceUri) const override
511 {
512 if (faceUri.getPort().empty()) {
513 return false;
514 }
515 if (!faceUri.getPath().empty()) {
516 return false;
517 }
518 return true;
519 }
520
Davide Pesavento57c07df2016-12-11 18:41:45 -0500521 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700522 canonize(const FaceUri& faceUri,
523 const FaceUri::CanonizeSuccessCallback& onSuccess,
524 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000525 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700526 {
527 if (this->isCanonical(faceUri)) {
528 onSuccess(faceUri);
529 }
530 else {
531 onFailure("cannot canonize " + faceUri.toString());
532 }
533 }
534};
535
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500536using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
537 TcpCanonizeProvider*,
538 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000539 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500540 UdpDevCanonizeProvider*>;
541using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700542
543class CanonizeProviderTableInitializer
544{
545public:
546 explicit
547 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
548 : m_providerTable(providerTable)
549 {
550 }
551
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500552 template<typename CP>
553 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700554 operator()(CP*)
555 {
556 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500557 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700558 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500559
560 for (const auto& scheme : schemes) {
561 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
562 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700563 }
564 }
565
566private:
567 CanonizeProviderTable& m_providerTable;
568};
569
570static const CanonizeProvider*
571getCanonizeProvider(const std::string& scheme)
572{
573 static CanonizeProviderTable providerTable;
574 if (providerTable.empty()) {
575 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
576 BOOST_ASSERT(!providerTable.empty());
577 }
578
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200579 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500580 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700581}
582
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500583
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700584bool
585FaceUri::canCanonize(const std::string& scheme)
586{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500587 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700588}
589
590bool
591FaceUri::isCanonical() const
592{
593 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500594 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700595 return false;
596 }
597
598 return cp->isCanonical(*this);
599}
600
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700601void
602FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
603 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000604 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700605{
606 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200607 if (cp == nullptr) {
608 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700609 onFailure("scheme not supported");
610 }
611 return;
612 }
613
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200614 static CanonizeSuccessCallback successNop = bind([]{});
615 static CanonizeFailureCallback failureNop = bind([]{});
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700616 cp->canonize(*this,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200617 onSuccess ? onSuccess : successNop,
618 onFailure ? onFailure : failureNop,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700619 io, timeout);
620}
621
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700622} // namespace ndn