blob: 3e9f358a1ae2ec228d0157ce39726145d7b64ade [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"
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 Pesaventoe1081cd2016-12-19 23:58:15 -050038#include <boost/regex.hpp>
39#include <set>
Davide Pesaventoa84f4642017-08-23 16:14:51 -040040#include <sstream>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070041
42namespace ndn {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070043
44BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
45
46FaceUri::FaceUri()
47 : m_isV6(false)
48{
49}
50
51FaceUri::FaceUri(const std::string& uri)
52{
53 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070054 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070055 }
56}
57
58FaceUri::FaceUri(const char* uri)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050059 : FaceUri(std::string(uri))
Junxiao Shi77dcadd2014-10-05 14:40:54 -070060{
Junxiao Shi77dcadd2014-10-05 14:40:54 -070061}
62
63bool
64FaceUri::parse(const std::string& uri)
65{
66 m_scheme.clear();
67 m_host.clear();
Junxiao Shi77dcadd2014-10-05 14:40:54 -070068 m_port.clear();
69 m_path.clear();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050070 m_isV6 = false;
Junxiao Shi77dcadd2014-10-05 14:40:54 -070071
Weiwei Liud7f4fda2016-10-19 22:38:39 -070072 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070073 boost::smatch protocolMatch;
74 if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
75 return false;
76 }
77 m_scheme = protocolMatch[1];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070078 const std::string& authority = protocolMatch[3];
79 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070080
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070081 // pattern for IPv6 link local address enclosed in [ ], with optional port number
82 static const boost::regex v6LinkLocalExp("^\\[([a-fA-F0-9:]+)%([a-zA-Z0-9]+)\\]"
83 "(?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070084 // pattern for IPv6 address enclosed in [ ], with optional port number
85 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
86 // pattern for Ethernet address in standard hex-digits-and-colons notation
87 static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
88 // pattern for IPv4-mapped IPv6 address, with optional port number
89 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
90 // pattern for IPv4/hostname/fd/ifname, with optional port number
91 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
92
93 if (authority.empty()) {
94 // UNIX, internal
95 }
96 else {
97 boost::smatch match;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070098 if (boost::regex_match(authority, match, v6LinkLocalExp)) {
99 m_isV6 = true;
100 m_host = match[1] + "%" + match[2];
101 m_port = match[3];
102 return true;
103 }
104
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700105 m_isV6 = boost::regex_match(authority, match, v6Exp);
106 if (m_isV6 ||
107 boost::regex_match(authority, match, etherExp) ||
108 boost::regex_match(authority, match, v4MappedV6Exp) ||
109 boost::regex_match(authority, match, v4HostExp)) {
110 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;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700273 auto addr = ip::addressFromString(unescapeHost(faceUri.getHost()), ec);
274 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;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700326 auto ipAddress = ip::addressFromString(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 }
444
445protected:
446 // checkAddress is not overriden:
447 // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
448 // FaceMgmt protocol allows IPv6 multicast address in UDP.
449};
450
451class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
452{
453public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700454 TcpCanonizeProvider()
455 : IpHostCanonizeProvider("tcp")
456 {
457 }
458
459protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500460 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200461 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700462 {
463 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200464 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700465 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200466 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700467 }
468};
469
470class EtherCanonizeProvider : public CanonizeProvider
471{
472public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500473 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200474 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700475 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500476 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700477 }
478
Davide Pesavento57c07df2016-12-11 18:41:45 -0500479 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200480 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700481 {
482 if (!faceUri.getPort().empty()) {
483 return false;
484 }
485 if (!faceUri.getPath().empty()) {
486 return false;
487 }
488
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500489 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700490 return addr.toString() == faceUri.getHost();
491 }
492
Davide Pesavento57c07df2016-12-11 18:41:45 -0500493 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700494 canonize(const FaceUri& faceUri,
495 const FaceUri::CanonizeSuccessCallback& onSuccess,
496 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000497 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700498 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500499 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700500 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500501 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700502 }
503
504 FaceUri canonicalUri(addr);
505 BOOST_ASSERT(canonicalUri.isCanonical());
506 onSuccess(canonicalUri);
507 }
508};
509
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000510class DevCanonizeProvider : public CanonizeProvider
511{
512public:
513 std::set<std::string>
514 getSchemes() const override
515 {
516 return {"dev"};
517 }
518
519 bool
520 isCanonical(const FaceUri& faceUri) const override
521 {
522 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
523 }
524
525 void
526 canonize(const FaceUri& faceUri,
527 const FaceUri::CanonizeSuccessCallback& onSuccess,
528 const FaceUri::CanonizeFailureCallback& onFailure,
529 boost::asio::io_service& io, time::nanoseconds timeout) const override
530 {
531 if (faceUri.getHost().empty()) {
532 onFailure("network interface name is missing");
533 return;
534 }
535 if (!faceUri.getPort().empty()) {
536 onFailure("port number is not allowed");
537 return;
538 }
539 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
540 onFailure("path is not allowed");
541 return;
542 }
543
544 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
545 BOOST_ASSERT(canonicalUri.isCanonical());
546 onSuccess(canonicalUri);
547 }
548};
549
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700550class UdpDevCanonizeProvider : public CanonizeProvider
551{
552public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500553 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700554 getSchemes() const override
555 {
556 return {"udp4+dev", "udp6+dev"};
557 }
558
Davide Pesavento57c07df2016-12-11 18:41:45 -0500559 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700560 isCanonical(const FaceUri& faceUri) const override
561 {
562 if (faceUri.getPort().empty()) {
563 return false;
564 }
565 if (!faceUri.getPath().empty()) {
566 return false;
567 }
568 return true;
569 }
570
Davide Pesavento57c07df2016-12-11 18:41:45 -0500571 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700572 canonize(const FaceUri& faceUri,
573 const FaceUri::CanonizeSuccessCallback& onSuccess,
574 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000575 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700576 {
577 if (this->isCanonical(faceUri)) {
578 onSuccess(faceUri);
579 }
580 else {
581 onFailure("cannot canonize " + faceUri.toString());
582 }
583 }
584};
585
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500586using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
587 TcpCanonizeProvider*,
588 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000589 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500590 UdpDevCanonizeProvider*>;
591using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700592
593class CanonizeProviderTableInitializer
594{
595public:
596 explicit
597 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
598 : m_providerTable(providerTable)
599 {
600 }
601
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500602 template<typename CP>
603 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700604 operator()(CP*)
605 {
606 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500607 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700608 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500609
610 for (const auto& scheme : schemes) {
611 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
612 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700613 }
614 }
615
616private:
617 CanonizeProviderTable& m_providerTable;
618};
619
620static const CanonizeProvider*
621getCanonizeProvider(const std::string& scheme)
622{
623 static CanonizeProviderTable providerTable;
624 if (providerTable.empty()) {
625 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
626 BOOST_ASSERT(!providerTable.empty());
627 }
628
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200629 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500630 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700631}
632
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500633
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700634bool
635FaceUri::canCanonize(const std::string& scheme)
636{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500637 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700638}
639
640bool
641FaceUri::isCanonical() const
642{
643 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500644 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700645 return false;
646 }
647
648 return cp->isCanonical(*this);
649}
650
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700651void
652FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
653 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000654 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700655{
656 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200657 if (cp == nullptr) {
658 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700659 onFailure("scheme not supported");
660 }
661 return;
662 }
663
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200664 static CanonizeSuccessCallback successNop = bind([]{});
665 static CanonizeFailureCallback failureNop = bind([]{});
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700666 cp->canonize(*this,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200667 onSuccess ? onSuccess : successNop,
668 onFailure ? onFailure : failureNop,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700669 io, timeout);
670}
671
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700672} // namespace ndn