blob: dc70760c175cd8943028c31822f9316932356356 [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/*
Davide Pesaventob984e322018-01-24 19:40:07 -05003 * Copyright (c) 2013-2018 Regents of the University of California,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +00004 * 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"
29
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070030#include "address-converter.hpp"
31#include "dns.hpp"
32#include "util/string-helper.hpp"
33
34#include <boost/algorithm/string.hpp>
Junxiao Shie3ef6ee2014-10-05 14:40:54 -070035#include <boost/lexical_cast.hpp>
Junxiao Shi4083c8d2014-10-12 16:43:16 -070036#include <boost/mpl/vector.hpp>
37#include <boost/mpl/for_each.hpp>
Davide Pesavento90db7ee2018-06-10 11:33:31 -040038
39#include <regex>
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050040#include <set>
Davide Pesaventoa84f4642017-08-23 16:14:51 -040041#include <sstream>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070042
43namespace ndn {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070044
45BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
46
47FaceUri::FaceUri()
48 : m_isV6(false)
49{
50}
51
52FaceUri::FaceUri(const std::string& uri)
53{
54 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070055 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070056 }
57}
58
59FaceUri::FaceUri(const char* uri)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050060 : FaceUri(std::string(uri))
Junxiao Shi77dcadd2014-10-05 14:40:54 -070061{
Junxiao Shi77dcadd2014-10-05 14:40:54 -070062}
63
64bool
65FaceUri::parse(const std::string& uri)
66{
67 m_scheme.clear();
68 m_host.clear();
Junxiao Shi77dcadd2014-10-05 14:40:54 -070069 m_port.clear();
70 m_path.clear();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050071 m_isV6 = false;
Junxiao Shi77dcadd2014-10-05 14:40:54 -070072
Davide Pesavento90db7ee2018-06-10 11:33:31 -040073 static const std::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
74 std::smatch protocolMatch;
75 if (!std::regex_match(uri, protocolMatch, protocolExp)) {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070076 return false;
77 }
78 m_scheme = protocolMatch[1];
Davide Pesavento90db7ee2018-06-10 11:33:31 -040079 std::string authority = protocolMatch[3];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070080 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070081
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070082 // pattern for IPv6 link local address enclosed in [ ], with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040083 static const std::regex v6LinkLocalExp("^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070084 // pattern for IPv6 address enclosed in [ ], with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040085 static const std::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070086 // pattern for Ethernet address in standard hex-digits-and-colons notation
Davide Pesavento90db7ee2018-06-10 11:33:31 -040087 static const std::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070088 // pattern for IPv4-mapped IPv6 address, with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040089 static const std::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070090 // pattern for IPv4/hostname/fd/ifname, with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040091 static const std::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070092
93 if (authority.empty()) {
94 // UNIX, internal
95 }
96 else {
Davide Pesavento90db7ee2018-06-10 11:33:31 -040097 std::smatch match;
98 if (std::regex_match(authority, match, v6LinkLocalExp)) {
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070099 m_isV6 = true;
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400100 m_host = match[1].str() + "%" + match[2].str();
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700101 m_port = match[3];
102 return true;
103 }
104
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400105 m_isV6 = std::regex_match(authority, match, v6Exp);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700106 if (m_isV6 ||
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400107 std::regex_match(authority, match, etherExp) ||
108 std::regex_match(authority, match, v4MappedV6Exp) ||
109 std::regex_match(authority, match, v4HostExp)) {
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700110 m_host = match[1];
111 m_port = match[2];
112 }
113 else {
114 return false;
115 }
116 }
117
118 return true;
119}
120
121FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
122{
123 m_isV6 = endpoint.address().is_v6();
124 m_scheme = m_isV6 ? "udp6" : "udp4";
125 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700126 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700127}
128
129FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
130{
131 m_isV6 = endpoint.address().is_v6();
132 m_scheme = m_isV6 ? "tcp6" : "tcp4";
133 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700134 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700135}
136
137FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700138{
139 m_isV6 = endpoint.address().is_v6();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500140 m_scheme = scheme;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700141 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700142 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700143}
144
145#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
146FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500147 : m_scheme("unix")
148 , m_path(endpoint.path())
149 , m_isV6(false)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700150{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700151}
152#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
153
154FaceUri
155FaceUri::fromFd(int fd)
156{
157 FaceUri uri;
158 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700159 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700160 return uri;
161}
162
163FaceUri::FaceUri(const ethernet::Address& address)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500164 : m_scheme("ether")
165 , m_host(address.toString())
166 , m_isV6(true)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700167{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700168}
169
170FaceUri
171FaceUri::fromDev(const std::string& ifname)
172{
173 FaceUri uri;
174 uri.m_scheme = "dev";
175 uri.m_host = ifname;
176 return uri;
177}
178
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700179FaceUri
180FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
181{
182 FaceUri uri;
183 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
184 uri.m_host = ifname;
185 uri.m_port = to_string(endpoint.port());
186 return uri;
187}
188
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700189bool
190FaceUri::operator==(const FaceUri& rhs) const
191{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500192 return m_isV6 == rhs.m_isV6 &&
193 m_scheme == rhs.m_scheme &&
194 m_host == rhs.m_host &&
195 m_port == rhs.m_port &&
196 m_path == rhs.m_path;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700197}
198
199bool
200FaceUri::operator!=(const FaceUri& rhs) const
201{
202 return !(*this == rhs);
203}
204
205std::string
206FaceUri::toString() const
207{
208 std::ostringstream os;
209 os << *this;
210 return os.str();
211}
212
213std::ostream&
214operator<<(std::ostream& os, const FaceUri& uri)
215{
216 os << uri.m_scheme << "://";
217 if (uri.m_isV6) {
218 os << "[" << uri.m_host << "]";
219 }
220 else {
221 os << uri.m_host;
222 }
223 if (!uri.m_port.empty()) {
224 os << ":" << uri.m_port;
225 }
226 os << uri.m_path;
227 return os;
228}
229
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500230
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700231/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
232 */
233class CanonizeProvider : noncopyable
234{
235public:
236 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200237 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700238
239 virtual std::set<std::string>
240 getSchemes() const = 0;
241
242 virtual bool
243 isCanonical(const FaceUri& faceUri) const = 0;
244
245 virtual void
246 canonize(const FaceUri& faceUri,
247 const FaceUri::CanonizeSuccessCallback& onSuccess,
248 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000249 boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700250};
251
252template<typename Protocol>
253class IpHostCanonizeProvider : public CanonizeProvider
254{
255public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500256 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200257 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700258 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500259 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700260 }
261
Davide Pesavento57c07df2016-12-11 18:41:45 -0500262 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200263 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700264 {
265 if (faceUri.getPort().empty()) {
266 return false;
267 }
268 if (!faceUri.getPath().empty()) {
269 return false;
270 }
271
272 boost::system::error_code ec;
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400273 auto addr = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700274 if (ec) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700275 return false;
276 }
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500277
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700278 bool hasCorrectScheme = (faceUri.getScheme() == m_v4Scheme && addr.is_v4()) ||
279 (faceUri.getScheme() == m_v6Scheme && addr.is_v6());
280 if (!hasCorrectScheme) {
281 return false;
282 }
283
284 auto checkAddressWithUri = [] (const boost::asio::ip::address& addr,
285 const FaceUri& faceUri) -> bool {
286 if (addr.is_v4() || !addr.to_v6().is_link_local()) {
287 return addr.to_string() == faceUri.getHost();
288 }
289
290 std::vector<std::string> addrFields, faceUriFields;
291 std::string addrString = addr.to_string();
292 std::string faceUriString = faceUri.getHost();
293
294 boost::algorithm::split(addrFields, addrString, boost::is_any_of("%"));
295 boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of("%"));
296 if (addrFields.size() != 2 || faceUriFields.size() != 2) {
297 return false;
298 }
299
300 if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, "25") == 0) {
301 // %25... is accepted, but not a canonical form
302 return false;
303 }
304
305 return addrFields[0] == faceUriFields[0] &&
306 addrFields[1] == faceUriFields[1];
307 };
308
309 return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700310 }
311
Davide Pesavento57c07df2016-12-11 18:41:45 -0500312 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700313 canonize(const FaceUri& faceUri,
314 const FaceUri::CanonizeSuccessCallback& onSuccess,
315 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000316 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700317 {
318 if (this->isCanonical(faceUri)) {
319 onSuccess(faceUri);
320 return;
321 }
322
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700323 // make a copy because caller may modify faceUri
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500324 auto uri = make_shared<FaceUri>(faceUri);
Eric Newberry71aecae2017-05-29 00:22:39 -0700325 boost::system::error_code ec;
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400326 auto ipAddress = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
Eric Newberry71aecae2017-05-29 00:22:39 -0700327 if (!ec) {
328 // No need to resolve IP address if host is already an IP
329 if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
330 (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
331 return onFailure("IPv4/v6 mismatch");
332 }
333
334 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
335 }
336 else {
337 dns::AddressSelector addressSelector;
338 if (faceUri.getScheme() == m_v4Scheme) {
339 addressSelector = dns::Ipv4Only();
340 }
341 else if (faceUri.getScheme() == m_v6Scheme) {
342 addressSelector = dns::Ipv6Only();
343 }
344 else {
345 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
346 addressSelector = dns::AnyAddress();
347 }
348
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700349 dns::asyncResolve(unescapeHost(faceUri.getHost()),
Eric Newberry71aecae2017-05-29 00:22:39 -0700350 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
351 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
352 io, addressSelector, timeout);
353 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700354 }
355
356protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200357 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700358 IpHostCanonizeProvider(const std::string& baseScheme,
Davide Pesaventof78cb702016-12-11 14:42:40 -0500359 uint16_t defaultUnicastPort = 6363,
360 uint16_t defaultMulticastPort = 56363)
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700361 : m_baseScheme(baseScheme)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500362 , m_v4Scheme(baseScheme + '4')
363 , m_v6Scheme(baseScheme + '6')
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700364 , m_defaultUnicastPort(defaultUnicastPort)
365 , m_defaultMulticastPort(defaultMulticastPort)
366 {
367 }
368
369private:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700370 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500371 onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700372 const FaceUri::CanonizeSuccessCallback& onSuccess,
373 const FaceUri::CanonizeFailureCallback& onFailure,
374 const dns::IpAddress& ipAddress) const
375 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500376 bool isOk = false;
377 std::string reason;
378 std::tie(isOk, reason) = this->checkAddress(ipAddress);
379 if (!isOk) {
380 return onFailure(reason);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700381 }
382
Davide Pesaventof78cb702016-12-11 14:42:40 -0500383 uint16_t port = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700384 if (faceUri->getPort().empty()) {
385 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
386 }
387 else {
388 try {
Davide Pesaventof78cb702016-12-11 14:42:40 -0500389 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700390 }
Davide Pesaventof78cb702016-12-11 14:42:40 -0500391 catch (const boost::bad_lexical_cast&) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500392 return onFailure("invalid port number '" + faceUri->getPort() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700393 }
394 }
395
396 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
397 BOOST_ASSERT(canonicalUri.isCanonical());
398 onSuccess(canonicalUri);
399 }
400
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700401 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500402 onDnsFailure(const shared_ptr<FaceUri>& faceUri,
403 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700404 const std::string& reason) const
405 {
406 onFailure(reason);
407 }
408
409 /** \brief when overriden in a subclass, check the IP address is allowable
410 * \return (true,ignored) if the address is allowable;
411 * (false,reason) if the address is not allowable.
412 */
413 virtual std::pair<bool, std::string>
414 checkAddress(const dns::IpAddress& ipAddress) const
415 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200416 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700417 }
418
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700419 static std::string
420 unescapeHost(std::string host)
421 {
422 auto escapePos = host.find("%25");
423 if (escapePos != std::string::npos && escapePos < host.size() - 3) {
424 host = unescape(host);
425 }
426 return host;
427 }
428
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700429private:
430 std::string m_baseScheme;
431 std::string m_v4Scheme;
432 std::string m_v6Scheme;
Davide Pesaventof78cb702016-12-11 14:42:40 -0500433 uint16_t m_defaultUnicastPort;
434 uint16_t m_defaultMulticastPort;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700435};
436
437class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
438{
439public:
440 UdpCanonizeProvider()
441 : IpHostCanonizeProvider("udp")
442 {
443 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700444};
445
446class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
447{
448public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700449 TcpCanonizeProvider()
450 : IpHostCanonizeProvider("tcp")
451 {
452 }
453
454protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500455 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200456 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700457 {
458 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200459 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700460 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200461 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700462 }
463};
464
465class EtherCanonizeProvider : public CanonizeProvider
466{
467public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500468 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200469 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700470 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500471 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700472 }
473
Davide Pesavento57c07df2016-12-11 18:41:45 -0500474 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200475 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700476 {
477 if (!faceUri.getPort().empty()) {
478 return false;
479 }
480 if (!faceUri.getPath().empty()) {
481 return false;
482 }
483
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500484 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700485 return addr.toString() == faceUri.getHost();
486 }
487
Davide Pesavento57c07df2016-12-11 18:41:45 -0500488 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700489 canonize(const FaceUri& faceUri,
490 const FaceUri::CanonizeSuccessCallback& onSuccess,
491 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000492 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700493 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500494 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700495 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500496 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700497 }
498
499 FaceUri canonicalUri(addr);
500 BOOST_ASSERT(canonicalUri.isCanonical());
501 onSuccess(canonicalUri);
502 }
503};
504
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000505class DevCanonizeProvider : public CanonizeProvider
506{
507public:
508 std::set<std::string>
509 getSchemes() const override
510 {
511 return {"dev"};
512 }
513
514 bool
515 isCanonical(const FaceUri& faceUri) const override
516 {
517 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
518 }
519
520 void
521 canonize(const FaceUri& faceUri,
522 const FaceUri::CanonizeSuccessCallback& onSuccess,
523 const FaceUri::CanonizeFailureCallback& onFailure,
524 boost::asio::io_service& io, time::nanoseconds timeout) const override
525 {
526 if (faceUri.getHost().empty()) {
527 onFailure("network interface name is missing");
528 return;
529 }
530 if (!faceUri.getPort().empty()) {
531 onFailure("port number is not allowed");
532 return;
533 }
534 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
535 onFailure("path is not allowed");
536 return;
537 }
538
539 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
540 BOOST_ASSERT(canonicalUri.isCanonical());
541 onSuccess(canonicalUri);
542 }
543};
544
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700545class UdpDevCanonizeProvider : public CanonizeProvider
546{
547public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500548 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700549 getSchemes() const override
550 {
551 return {"udp4+dev", "udp6+dev"};
552 }
553
Davide Pesavento57c07df2016-12-11 18:41:45 -0500554 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700555 isCanonical(const FaceUri& faceUri) const override
556 {
557 if (faceUri.getPort().empty()) {
558 return false;
559 }
560 if (!faceUri.getPath().empty()) {
561 return false;
562 }
563 return true;
564 }
565
Davide Pesavento57c07df2016-12-11 18:41:45 -0500566 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700567 canonize(const FaceUri& faceUri,
568 const FaceUri::CanonizeSuccessCallback& onSuccess,
569 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000570 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700571 {
572 if (this->isCanonical(faceUri)) {
573 onSuccess(faceUri);
574 }
575 else {
576 onFailure("cannot canonize " + faceUri.toString());
577 }
578 }
579};
580
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500581using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
582 TcpCanonizeProvider*,
583 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000584 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500585 UdpDevCanonizeProvider*>;
586using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700587
588class CanonizeProviderTableInitializer
589{
590public:
591 explicit
592 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
593 : m_providerTable(providerTable)
594 {
595 }
596
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500597 template<typename CP>
598 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700599 operator()(CP*)
600 {
601 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500602 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700603 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500604
605 for (const auto& scheme : schemes) {
606 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
607 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700608 }
609 }
610
611private:
612 CanonizeProviderTable& m_providerTable;
613};
614
615static const CanonizeProvider*
616getCanonizeProvider(const std::string& scheme)
617{
618 static CanonizeProviderTable providerTable;
619 if (providerTable.empty()) {
620 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
621 BOOST_ASSERT(!providerTable.empty());
622 }
623
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200624 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500625 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700626}
627
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500628
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700629bool
630FaceUri::canCanonize(const std::string& scheme)
631{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500632 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700633}
634
635bool
636FaceUri::isCanonical() const
637{
638 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500639 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700640 return false;
641 }
642
643 return cp->isCanonical(*this);
644}
645
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700646void
647FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
648 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000649 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700650{
651 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200652 if (cp == nullptr) {
653 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700654 onFailure("scheme not supported");
655 }
656 return;
657 }
658
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700659 cp->canonize(*this,
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400660 onSuccess ? onSuccess : [] (auto&&) {},
661 onFailure ? onFailure : [] (auto&&) {},
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700662 io, timeout);
663}
664
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700665} // namespace ndn