blob: 97fe0f74154dd87eea3412d2b5f69fef5a15a60b [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 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
Davide Pesaventob984e322018-01-24 19:40:07 -050082 static const boost::regex v6LinkLocalExp("^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070083 // pattern for IPv6 address enclosed in [ ], with optional port number
84 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
85 // pattern for Ethernet address in standard hex-digits-and-colons notation
86 static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
87 // pattern for IPv4-mapped IPv6 address, with optional port number
88 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
89 // pattern for IPv4/hostname/fd/ifname, with optional port number
90 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
91
92 if (authority.empty()) {
93 // UNIX, internal
94 }
95 else {
96 boost::smatch match;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070097 if (boost::regex_match(authority, match, v6LinkLocalExp)) {
98 m_isV6 = true;
99 m_host = match[1] + "%" + match[2];
100 m_port = match[3];
101 return true;
102 }
103
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700104 m_isV6 = boost::regex_match(authority, match, v6Exp);
105 if (m_isV6 ||
106 boost::regex_match(authority, match, etherExp) ||
107 boost::regex_match(authority, match, v4MappedV6Exp) ||
108 boost::regex_match(authority, match, v4HostExp)) {
109 m_host = match[1];
110 m_port = match[2];
111 }
112 else {
113 return false;
114 }
115 }
116
117 return true;
118}
119
120FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
121{
122 m_isV6 = endpoint.address().is_v6();
123 m_scheme = m_isV6 ? "udp6" : "udp4";
124 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700125 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700126}
127
128FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
129{
130 m_isV6 = endpoint.address().is_v6();
131 m_scheme = m_isV6 ? "tcp6" : "tcp4";
132 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700133 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700134}
135
136FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700137{
138 m_isV6 = endpoint.address().is_v6();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500139 m_scheme = scheme;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700140 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700141 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700142}
143
144#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
145FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500146 : m_scheme("unix")
147 , m_path(endpoint.path())
148 , m_isV6(false)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700149{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700150}
151#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
152
153FaceUri
154FaceUri::fromFd(int fd)
155{
156 FaceUri uri;
157 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700158 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700159 return uri;
160}
161
162FaceUri::FaceUri(const ethernet::Address& address)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500163 : m_scheme("ether")
164 , m_host(address.toString())
165 , m_isV6(true)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700166{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700167}
168
169FaceUri
170FaceUri::fromDev(const std::string& ifname)
171{
172 FaceUri uri;
173 uri.m_scheme = "dev";
174 uri.m_host = ifname;
175 return uri;
176}
177
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700178FaceUri
179FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
180{
181 FaceUri uri;
182 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
183 uri.m_host = ifname;
184 uri.m_port = to_string(endpoint.port());
185 return uri;
186}
187
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700188bool
189FaceUri::operator==(const FaceUri& rhs) const
190{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500191 return m_isV6 == rhs.m_isV6 &&
192 m_scheme == rhs.m_scheme &&
193 m_host == rhs.m_host &&
194 m_port == rhs.m_port &&
195 m_path == rhs.m_path;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700196}
197
198bool
199FaceUri::operator!=(const FaceUri& rhs) const
200{
201 return !(*this == rhs);
202}
203
204std::string
205FaceUri::toString() const
206{
207 std::ostringstream os;
208 os << *this;
209 return os.str();
210}
211
212std::ostream&
213operator<<(std::ostream& os, const FaceUri& uri)
214{
215 os << uri.m_scheme << "://";
216 if (uri.m_isV6) {
217 os << "[" << uri.m_host << "]";
218 }
219 else {
220 os << uri.m_host;
221 }
222 if (!uri.m_port.empty()) {
223 os << ":" << uri.m_port;
224 }
225 os << uri.m_path;
226 return os;
227}
228
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500229
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700230/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
231 */
232class CanonizeProvider : noncopyable
233{
234public:
235 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200236 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700237
238 virtual std::set<std::string>
239 getSchemes() const = 0;
240
241 virtual bool
242 isCanonical(const FaceUri& faceUri) const = 0;
243
244 virtual void
245 canonize(const FaceUri& faceUri,
246 const FaceUri::CanonizeSuccessCallback& onSuccess,
247 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000248 boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700249};
250
251template<typename Protocol>
252class IpHostCanonizeProvider : public CanonizeProvider
253{
254public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500255 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200256 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700257 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500258 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700259 }
260
Davide Pesavento57c07df2016-12-11 18:41:45 -0500261 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200262 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700263 {
264 if (faceUri.getPort().empty()) {
265 return false;
266 }
267 if (!faceUri.getPath().empty()) {
268 return false;
269 }
270
271 boost::system::error_code ec;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700272 auto addr = ip::addressFromString(unescapeHost(faceUri.getHost()), ec);
273 if (ec) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700274 return false;
275 }
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500276
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700277 bool hasCorrectScheme = (faceUri.getScheme() == m_v4Scheme && addr.is_v4()) ||
278 (faceUri.getScheme() == m_v6Scheme && addr.is_v6());
279 if (!hasCorrectScheme) {
280 return false;
281 }
282
283 auto checkAddressWithUri = [] (const boost::asio::ip::address& addr,
284 const FaceUri& faceUri) -> bool {
285 if (addr.is_v4() || !addr.to_v6().is_link_local()) {
286 return addr.to_string() == faceUri.getHost();
287 }
288
289 std::vector<std::string> addrFields, faceUriFields;
290 std::string addrString = addr.to_string();
291 std::string faceUriString = faceUri.getHost();
292
293 boost::algorithm::split(addrFields, addrString, boost::is_any_of("%"));
294 boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of("%"));
295 if (addrFields.size() != 2 || faceUriFields.size() != 2) {
296 return false;
297 }
298
299 if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, "25") == 0) {
300 // %25... is accepted, but not a canonical form
301 return false;
302 }
303
304 return addrFields[0] == faceUriFields[0] &&
305 addrFields[1] == faceUriFields[1];
306 };
307
308 return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700309 }
310
Davide Pesavento57c07df2016-12-11 18:41:45 -0500311 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700312 canonize(const FaceUri& faceUri,
313 const FaceUri::CanonizeSuccessCallback& onSuccess,
314 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000315 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700316 {
317 if (this->isCanonical(faceUri)) {
318 onSuccess(faceUri);
319 return;
320 }
321
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700322 // make a copy because caller may modify faceUri
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500323 auto uri = make_shared<FaceUri>(faceUri);
Eric Newberry71aecae2017-05-29 00:22:39 -0700324 boost::system::error_code ec;
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700325 auto ipAddress = ip::addressFromString(unescapeHost(faceUri.getHost()), ec);
Eric Newberry71aecae2017-05-29 00:22:39 -0700326 if (!ec) {
327 // No need to resolve IP address if host is already an IP
328 if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
329 (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
330 return onFailure("IPv4/v6 mismatch");
331 }
332
333 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
334 }
335 else {
336 dns::AddressSelector addressSelector;
337 if (faceUri.getScheme() == m_v4Scheme) {
338 addressSelector = dns::Ipv4Only();
339 }
340 else if (faceUri.getScheme() == m_v6Scheme) {
341 addressSelector = dns::Ipv6Only();
342 }
343 else {
344 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
345 addressSelector = dns::AnyAddress();
346 }
347
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700348 dns::asyncResolve(unescapeHost(faceUri.getHost()),
Eric Newberry71aecae2017-05-29 00:22:39 -0700349 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
350 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
351 io, addressSelector, timeout);
352 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700353 }
354
355protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200356 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700357 IpHostCanonizeProvider(const std::string& baseScheme,
Davide Pesaventof78cb702016-12-11 14:42:40 -0500358 uint16_t defaultUnicastPort = 6363,
359 uint16_t defaultMulticastPort = 56363)
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700360 : m_baseScheme(baseScheme)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500361 , m_v4Scheme(baseScheme + '4')
362 , m_v6Scheme(baseScheme + '6')
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700363 , m_defaultUnicastPort(defaultUnicastPort)
364 , m_defaultMulticastPort(defaultMulticastPort)
365 {
366 }
367
368private:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700369 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500370 onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700371 const FaceUri::CanonizeSuccessCallback& onSuccess,
372 const FaceUri::CanonizeFailureCallback& onFailure,
373 const dns::IpAddress& ipAddress) const
374 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500375 bool isOk = false;
376 std::string reason;
377 std::tie(isOk, reason) = this->checkAddress(ipAddress);
378 if (!isOk) {
379 return onFailure(reason);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700380 }
381
Davide Pesaventof78cb702016-12-11 14:42:40 -0500382 uint16_t port = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700383 if (faceUri->getPort().empty()) {
384 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
385 }
386 else {
387 try {
Davide Pesaventof78cb702016-12-11 14:42:40 -0500388 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700389 }
Davide Pesaventof78cb702016-12-11 14:42:40 -0500390 catch (const boost::bad_lexical_cast&) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500391 return onFailure("invalid port number '" + faceUri->getPort() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700392 }
393 }
394
395 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
396 BOOST_ASSERT(canonicalUri.isCanonical());
397 onSuccess(canonicalUri);
398 }
399
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700400 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500401 onDnsFailure(const shared_ptr<FaceUri>& faceUri,
402 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700403 const std::string& reason) const
404 {
405 onFailure(reason);
406 }
407
408 /** \brief when overriden in a subclass, check the IP address is allowable
409 * \return (true,ignored) if the address is allowable;
410 * (false,reason) if the address is not allowable.
411 */
412 virtual std::pair<bool, std::string>
413 checkAddress(const dns::IpAddress& ipAddress) const
414 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200415 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700416 }
417
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700418 static std::string
419 unescapeHost(std::string host)
420 {
421 auto escapePos = host.find("%25");
422 if (escapePos != std::string::npos && escapePos < host.size() - 3) {
423 host = unescape(host);
424 }
425 return host;
426 }
427
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700428private:
429 std::string m_baseScheme;
430 std::string m_v4Scheme;
431 std::string m_v6Scheme;
Davide Pesaventof78cb702016-12-11 14:42:40 -0500432 uint16_t m_defaultUnicastPort;
433 uint16_t m_defaultMulticastPort;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700434};
435
436class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
437{
438public:
439 UdpCanonizeProvider()
440 : IpHostCanonizeProvider("udp")
441 {
442 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700443};
444
445class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
446{
447public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700448 TcpCanonizeProvider()
449 : IpHostCanonizeProvider("tcp")
450 {
451 }
452
453protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500454 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200455 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700456 {
457 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200458 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700459 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200460 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700461 }
462};
463
464class EtherCanonizeProvider : public CanonizeProvider
465{
466public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500467 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200468 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700469 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500470 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700471 }
472
Davide Pesavento57c07df2016-12-11 18:41:45 -0500473 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200474 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700475 {
476 if (!faceUri.getPort().empty()) {
477 return false;
478 }
479 if (!faceUri.getPath().empty()) {
480 return false;
481 }
482
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500483 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700484 return addr.toString() == faceUri.getHost();
485 }
486
Davide Pesavento57c07df2016-12-11 18:41:45 -0500487 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700488 canonize(const FaceUri& faceUri,
489 const FaceUri::CanonizeSuccessCallback& onSuccess,
490 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000491 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700492 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500493 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700494 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500495 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700496 }
497
498 FaceUri canonicalUri(addr);
499 BOOST_ASSERT(canonicalUri.isCanonical());
500 onSuccess(canonicalUri);
501 }
502};
503
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000504class DevCanonizeProvider : public CanonizeProvider
505{
506public:
507 std::set<std::string>
508 getSchemes() const override
509 {
510 return {"dev"};
511 }
512
513 bool
514 isCanonical(const FaceUri& faceUri) const override
515 {
516 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
517 }
518
519 void
520 canonize(const FaceUri& faceUri,
521 const FaceUri::CanonizeSuccessCallback& onSuccess,
522 const FaceUri::CanonizeFailureCallback& onFailure,
523 boost::asio::io_service& io, time::nanoseconds timeout) const override
524 {
525 if (faceUri.getHost().empty()) {
526 onFailure("network interface name is missing");
527 return;
528 }
529 if (!faceUri.getPort().empty()) {
530 onFailure("port number is not allowed");
531 return;
532 }
533 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
534 onFailure("path is not allowed");
535 return;
536 }
537
538 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
539 BOOST_ASSERT(canonicalUri.isCanonical());
540 onSuccess(canonicalUri);
541 }
542};
543
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700544class UdpDevCanonizeProvider : public CanonizeProvider
545{
546public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500547 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700548 getSchemes() const override
549 {
550 return {"udp4+dev", "udp6+dev"};
551 }
552
Davide Pesavento57c07df2016-12-11 18:41:45 -0500553 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700554 isCanonical(const FaceUri& faceUri) const override
555 {
556 if (faceUri.getPort().empty()) {
557 return false;
558 }
559 if (!faceUri.getPath().empty()) {
560 return false;
561 }
562 return true;
563 }
564
Davide Pesavento57c07df2016-12-11 18:41:45 -0500565 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700566 canonize(const FaceUri& faceUri,
567 const FaceUri::CanonizeSuccessCallback& onSuccess,
568 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000569 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700570 {
571 if (this->isCanonical(faceUri)) {
572 onSuccess(faceUri);
573 }
574 else {
575 onFailure("cannot canonize " + faceUri.toString());
576 }
577 }
578};
579
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500580using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
581 TcpCanonizeProvider*,
582 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000583 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500584 UdpDevCanonizeProvider*>;
585using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700586
587class CanonizeProviderTableInitializer
588{
589public:
590 explicit
591 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
592 : m_providerTable(providerTable)
593 {
594 }
595
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500596 template<typename CP>
597 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700598 operator()(CP*)
599 {
600 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500601 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700602 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500603
604 for (const auto& scheme : schemes) {
605 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
606 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700607 }
608 }
609
610private:
611 CanonizeProviderTable& m_providerTable;
612};
613
614static const CanonizeProvider*
615getCanonizeProvider(const std::string& scheme)
616{
617 static CanonizeProviderTable providerTable;
618 if (providerTable.empty()) {
619 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
620 BOOST_ASSERT(!providerTable.empty());
621 }
622
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200623 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500624 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700625}
626
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500627
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700628bool
629FaceUri::canCanonize(const std::string& scheme)
630{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500631 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700632}
633
634bool
635FaceUri::isCanonical() const
636{
637 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500638 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700639 return false;
640 }
641
642 return cp->isCanonical(*this);
643}
644
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700645void
646FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
647 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000648 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700649{
650 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200651 if (cp == nullptr) {
652 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700653 onFailure("scheme not supported");
654 }
655 return;
656 }
657
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200658 static CanonizeSuccessCallback successNop = bind([]{});
659 static CanonizeFailureCallback failureNop = bind([]{});
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700660 cp->canonize(*this,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200661 onSuccess ? onSuccess : successNop,
662 onFailure ? onFailure : failureNop,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700663 io, timeout);
664}
665
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700666} // namespace ndn