blob: a0d69342a4dbd4b320cac556ac5df1f7f6387a26 [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"
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070029#include "dns.hpp"
30#include "util/string-helper.hpp"
31
32#include <boost/algorithm/string.hpp>
Junxiao Shie3ef6ee2014-10-05 14:40:54 -070033#include <boost/lexical_cast.hpp>
Junxiao Shi4083c8d2014-10-12 16:43:16 -070034#include <boost/mpl/vector.hpp>
35#include <boost/mpl/for_each.hpp>
Davide Pesavento90db7ee2018-06-10 11:33:31 -040036
37#include <regex>
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050038#include <set>
Davide Pesaventoa84f4642017-08-23 16:14:51 -040039#include <sstream>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070040
41namespace ndn {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070042
43BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
44
45FaceUri::FaceUri()
46 : m_isV6(false)
47{
48}
49
50FaceUri::FaceUri(const std::string& uri)
51{
52 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070053 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070054 }
55}
56
57FaceUri::FaceUri(const char* uri)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050058 : FaceUri(std::string(uri))
Junxiao Shi77dcadd2014-10-05 14:40:54 -070059{
Junxiao Shi77dcadd2014-10-05 14:40:54 -070060}
61
62bool
63FaceUri::parse(const std::string& uri)
64{
65 m_scheme.clear();
66 m_host.clear();
Junxiao Shi77dcadd2014-10-05 14:40:54 -070067 m_port.clear();
68 m_path.clear();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -050069 m_isV6 = false;
Junxiao Shi77dcadd2014-10-05 14:40:54 -070070
Davide Pesavento90db7ee2018-06-10 11:33:31 -040071 static const std::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
72 std::smatch protocolMatch;
73 if (!std::regex_match(uri, protocolMatch, protocolExp)) {
Junxiao Shi77dcadd2014-10-05 14:40:54 -070074 return false;
75 }
76 m_scheme = protocolMatch[1];
Davide Pesavento90db7ee2018-06-10 11:33:31 -040077 std::string authority = protocolMatch[3];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070078 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070079
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070080 // pattern for IPv6 link local address enclosed in [ ], with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040081 static const std::regex v6LinkLocalExp("^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070082 // pattern for IPv6 address enclosed in [ ], with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040083 static const std::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070084 // pattern for Ethernet address in standard hex-digits-and-colons notation
Davide Pesavento90db7ee2018-06-10 11:33:31 -040085 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 -070086 // pattern for IPv4-mapped IPv6 address, with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040087 static const std::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070088 // pattern for IPv4/hostname/fd/ifname, with optional port number
Davide Pesavento90db7ee2018-06-10 11:33:31 -040089 static const std::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070090
91 if (authority.empty()) {
92 // UNIX, internal
93 }
94 else {
Davide Pesavento90db7ee2018-06-10 11:33:31 -040095 std::smatch match;
96 if (std::regex_match(authority, match, v6LinkLocalExp)) {
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070097 m_isV6 = true;
Davide Pesavento90db7ee2018-06-10 11:33:31 -040098 m_host = match[1].str() + "%" + match[2].str();
Yanbiao Liac8b4ca2017-07-10 02:27:50 -070099 m_port = match[3];
100 return true;
101 }
102
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400103 m_isV6 = std::regex_match(authority, match, v6Exp);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700104 if (m_isV6 ||
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400105 std::regex_match(authority, match, etherExp) ||
106 std::regex_match(authority, match, v4MappedV6Exp) ||
107 std::regex_match(authority, match, v4HostExp)) {
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700108 m_host = match[1];
109 m_port = match[2];
110 }
111 else {
112 return false;
113 }
114 }
115
116 return true;
117}
118
119FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
120{
121 m_isV6 = endpoint.address().is_v6();
122 m_scheme = m_isV6 ? "udp6" : "udp4";
123 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700124 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700125}
126
127FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
128{
129 m_isV6 = endpoint.address().is_v6();
130 m_scheme = m_isV6 ? "tcp6" : "tcp4";
131 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700132 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700133}
134
135FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700136{
137 m_isV6 = endpoint.address().is_v6();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500138 m_scheme = scheme;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700139 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700140 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700141}
142
143#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
144FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500145 : m_scheme("unix")
146 , m_path(endpoint.path())
147 , m_isV6(false)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700148{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700149}
150#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
151
152FaceUri
153FaceUri::fromFd(int fd)
154{
155 FaceUri uri;
156 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700157 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700158 return uri;
159}
160
161FaceUri::FaceUri(const ethernet::Address& address)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500162 : m_scheme("ether")
163 , m_host(address.toString())
164 , m_isV6(true)
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700165{
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700166}
167
168FaceUri
169FaceUri::fromDev(const std::string& ifname)
170{
171 FaceUri uri;
172 uri.m_scheme = "dev";
173 uri.m_host = ifname;
174 return uri;
175}
176
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700177FaceUri
178FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
179{
180 FaceUri uri;
181 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
182 uri.m_host = ifname;
183 uri.m_port = to_string(endpoint.port());
184 return uri;
185}
186
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700187bool
188FaceUri::operator==(const FaceUri& rhs) const
189{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500190 return m_isV6 == rhs.m_isV6 &&
191 m_scheme == rhs.m_scheme &&
192 m_host == rhs.m_host &&
193 m_port == rhs.m_port &&
194 m_path == rhs.m_path;
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700195}
196
197bool
198FaceUri::operator!=(const FaceUri& rhs) const
199{
200 return !(*this == rhs);
201}
202
203std::string
204FaceUri::toString() const
205{
206 std::ostringstream os;
207 os << *this;
208 return os.str();
209}
210
211std::ostream&
212operator<<(std::ostream& os, const FaceUri& uri)
213{
214 os << uri.m_scheme << "://";
215 if (uri.m_isV6) {
216 os << "[" << uri.m_host << "]";
217 }
218 else {
219 os << uri.m_host;
220 }
221 if (!uri.m_port.empty()) {
222 os << ":" << uri.m_port;
223 }
224 os << uri.m_path;
225 return os;
226}
227
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500228
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700229/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
230 */
231class CanonizeProvider : noncopyable
232{
233public:
234 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200235 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700236
237 virtual std::set<std::string>
238 getSchemes() const = 0;
239
240 virtual bool
241 isCanonical(const FaceUri& faceUri) const = 0;
242
243 virtual void
244 canonize(const FaceUri& faceUri,
245 const FaceUri::CanonizeSuccessCallback& onSuccess,
246 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000247 boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700248};
249
250template<typename Protocol>
251class IpHostCanonizeProvider : public CanonizeProvider
252{
253public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500254 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200255 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700256 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500257 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700258 }
259
Davide Pesavento57c07df2016-12-11 18:41:45 -0500260 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200261 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700262 {
263 if (faceUri.getPort().empty()) {
264 return false;
265 }
266 if (!faceUri.getPath().empty()) {
267 return false;
268 }
269
270 boost::system::error_code ec;
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400271 auto addr = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700272 if (ec) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700273 return false;
274 }
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500275
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700276 bool hasCorrectScheme = (faceUri.getScheme() == m_v4Scheme && addr.is_v4()) ||
277 (faceUri.getScheme() == m_v6Scheme && addr.is_v6());
278 if (!hasCorrectScheme) {
279 return false;
280 }
281
282 auto checkAddressWithUri = [] (const boost::asio::ip::address& addr,
283 const FaceUri& faceUri) -> bool {
284 if (addr.is_v4() || !addr.to_v6().is_link_local()) {
285 return addr.to_string() == faceUri.getHost();
286 }
287
288 std::vector<std::string> addrFields, faceUriFields;
289 std::string addrString = addr.to_string();
290 std::string faceUriString = faceUri.getHost();
291
292 boost::algorithm::split(addrFields, addrString, boost::is_any_of("%"));
293 boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of("%"));
294 if (addrFields.size() != 2 || faceUriFields.size() != 2) {
295 return false;
296 }
297
298 if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, "25") == 0) {
299 // %25... is accepted, but not a canonical form
300 return false;
301 }
302
303 return addrFields[0] == faceUriFields[0] &&
304 addrFields[1] == faceUriFields[1];
305 };
306
307 return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700308 }
309
Davide Pesavento57c07df2016-12-11 18:41:45 -0500310 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700311 canonize(const FaceUri& faceUri,
312 const FaceUri::CanonizeSuccessCallback& onSuccess,
313 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000314 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700315 {
316 if (this->isCanonical(faceUri)) {
317 onSuccess(faceUri);
318 return;
319 }
320
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700321 // make a copy because caller may modify faceUri
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500322 auto uri = make_shared<FaceUri>(faceUri);
Eric Newberry71aecae2017-05-29 00:22:39 -0700323 boost::system::error_code ec;
Davide Pesavento90db7ee2018-06-10 11:33:31 -0400324 auto ipAddress = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
Eric Newberry71aecae2017-05-29 00:22:39 -0700325 if (!ec) {
326 // No need to resolve IP address if host is already an IP
327 if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
328 (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
329 return onFailure("IPv4/v6 mismatch");
330 }
331
332 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
333 }
334 else {
335 dns::AddressSelector addressSelector;
336 if (faceUri.getScheme() == m_v4Scheme) {
337 addressSelector = dns::Ipv4Only();
338 }
339 else if (faceUri.getScheme() == m_v6Scheme) {
340 addressSelector = dns::Ipv6Only();
341 }
342 else {
343 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
344 addressSelector = dns::AnyAddress();
345 }
346
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700347 dns::asyncResolve(unescapeHost(faceUri.getHost()),
Eric Newberry71aecae2017-05-29 00:22:39 -0700348 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
349 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
350 io, addressSelector, timeout);
351 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700352 }
353
354protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200355 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700356 IpHostCanonizeProvider(const std::string& baseScheme,
Davide Pesaventof78cb702016-12-11 14:42:40 -0500357 uint16_t defaultUnicastPort = 6363,
358 uint16_t defaultMulticastPort = 56363)
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700359 : m_baseScheme(baseScheme)
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500360 , m_v4Scheme(baseScheme + '4')
361 , m_v6Scheme(baseScheme + '6')
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700362 , m_defaultUnicastPort(defaultUnicastPort)
363 , m_defaultMulticastPort(defaultMulticastPort)
364 {
365 }
366
367private:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700368 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500369 onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700370 const FaceUri::CanonizeSuccessCallback& onSuccess,
371 const FaceUri::CanonizeFailureCallback& onFailure,
372 const dns::IpAddress& ipAddress) const
373 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500374 bool isOk = false;
375 std::string reason;
376 std::tie(isOk, reason) = this->checkAddress(ipAddress);
377 if (!isOk) {
378 return onFailure(reason);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700379 }
380
Davide Pesaventof78cb702016-12-11 14:42:40 -0500381 uint16_t port = 0;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700382 if (faceUri->getPort().empty()) {
383 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
384 }
385 else {
386 try {
Davide Pesaventof78cb702016-12-11 14:42:40 -0500387 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700388 }
Davide Pesaventof78cb702016-12-11 14:42:40 -0500389 catch (const boost::bad_lexical_cast&) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500390 return onFailure("invalid port number '" + faceUri->getPort() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700391 }
392 }
393
394 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
395 BOOST_ASSERT(canonicalUri.isCanonical());
396 onSuccess(canonicalUri);
397 }
398
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700399 void
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500400 onDnsFailure(const shared_ptr<FaceUri>& faceUri,
401 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700402 const std::string& reason) const
403 {
404 onFailure(reason);
405 }
406
407 /** \brief when overriden in a subclass, check the IP address is allowable
408 * \return (true,ignored) if the address is allowable;
409 * (false,reason) if the address is not allowable.
410 */
411 virtual std::pair<bool, std::string>
412 checkAddress(const dns::IpAddress& ipAddress) const
413 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200414 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700415 }
416
Yanbiao Liac8b4ca2017-07-10 02:27:50 -0700417 static std::string
418 unescapeHost(std::string host)
419 {
420 auto escapePos = host.find("%25");
421 if (escapePos != std::string::npos && escapePos < host.size() - 3) {
422 host = unescape(host);
423 }
424 return host;
425 }
426
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700427private:
428 std::string m_baseScheme;
429 std::string m_v4Scheme;
430 std::string m_v6Scheme;
Davide Pesaventof78cb702016-12-11 14:42:40 -0500431 uint16_t m_defaultUnicastPort;
432 uint16_t m_defaultMulticastPort;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700433};
434
435class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
436{
437public:
438 UdpCanonizeProvider()
439 : IpHostCanonizeProvider("udp")
440 {
441 }
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700442};
443
444class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
445{
446public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700447 TcpCanonizeProvider()
448 : IpHostCanonizeProvider("tcp")
449 {
450 }
451
452protected:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500453 std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200454 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700455 {
456 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200457 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700458 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200459 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700460 }
461};
462
463class EtherCanonizeProvider : public CanonizeProvider
464{
465public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500466 std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200467 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700468 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500469 return {"ether"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700470 }
471
Davide Pesavento57c07df2016-12-11 18:41:45 -0500472 bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200473 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700474 {
475 if (!faceUri.getPort().empty()) {
476 return false;
477 }
478 if (!faceUri.getPath().empty()) {
479 return false;
480 }
481
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500482 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700483 return addr.toString() == faceUri.getHost();
484 }
485
Davide Pesavento57c07df2016-12-11 18:41:45 -0500486 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700487 canonize(const FaceUri& faceUri,
488 const FaceUri::CanonizeSuccessCallback& onSuccess,
489 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000490 boost::asio::io_service& io, time::nanoseconds timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700491 {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500492 auto addr = ethernet::Address::fromString(faceUri.getHost());
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700493 if (addr.isNull()) {
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500494 return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700495 }
496
497 FaceUri canonicalUri(addr);
498 BOOST_ASSERT(canonicalUri.isCanonical());
499 onSuccess(canonicalUri);
500 }
501};
502
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000503class DevCanonizeProvider : public CanonizeProvider
504{
505public:
506 std::set<std::string>
507 getSchemes() const override
508 {
509 return {"dev"};
510 }
511
512 bool
513 isCanonical(const FaceUri& faceUri) const override
514 {
515 return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
516 }
517
518 void
519 canonize(const FaceUri& faceUri,
520 const FaceUri::CanonizeSuccessCallback& onSuccess,
521 const FaceUri::CanonizeFailureCallback& onFailure,
522 boost::asio::io_service& io, time::nanoseconds timeout) const override
523 {
524 if (faceUri.getHost().empty()) {
525 onFailure("network interface name is missing");
526 return;
527 }
528 if (!faceUri.getPort().empty()) {
529 onFailure("port number is not allowed");
530 return;
531 }
532 if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
533 onFailure("path is not allowed");
534 return;
535 }
536
537 FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
538 BOOST_ASSERT(canonicalUri.isCanonical());
539 onSuccess(canonicalUri);
540 }
541};
542
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700543class UdpDevCanonizeProvider : public CanonizeProvider
544{
545public:
Davide Pesavento57c07df2016-12-11 18:41:45 -0500546 std::set<std::string>
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700547 getSchemes() const override
548 {
549 return {"udp4+dev", "udp6+dev"};
550 }
551
Davide Pesavento57c07df2016-12-11 18:41:45 -0500552 bool
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700553 isCanonical(const FaceUri& faceUri) const override
554 {
555 if (faceUri.getPort().empty()) {
556 return false;
557 }
558 if (!faceUri.getPath().empty()) {
559 return false;
560 }
561 return true;
562 }
563
Davide Pesavento57c07df2016-12-11 18:41:45 -0500564 void
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700565 canonize(const FaceUri& faceUri,
566 const FaceUri::CanonizeSuccessCallback& onSuccess,
567 const FaceUri::CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000568 boost::asio::io_service& io, time::nanoseconds timeout) const override
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700569 {
570 if (this->isCanonical(faceUri)) {
571 onSuccess(faceUri);
572 }
573 else {
574 onFailure("cannot canonize " + faceUri.toString());
575 }
576 }
577};
578
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500579using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
580 TcpCanonizeProvider*,
581 EtherCanonizeProvider*,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000582 DevCanonizeProvider*,
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500583 UdpDevCanonizeProvider*>;
584using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700585
586class CanonizeProviderTableInitializer
587{
588public:
589 explicit
590 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
591 : m_providerTable(providerTable)
592 {
593 }
594
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500595 template<typename CP>
596 void
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700597 operator()(CP*)
598 {
599 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500600 auto schemes = cp->getSchemes();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700601 BOOST_ASSERT(!schemes.empty());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500602
603 for (const auto& scheme : schemes) {
604 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
605 m_providerTable[scheme] = cp;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700606 }
607 }
608
609private:
610 CanonizeProviderTable& m_providerTable;
611};
612
613static const CanonizeProvider*
614getCanonizeProvider(const std::string& scheme)
615{
616 static CanonizeProviderTable providerTable;
617 if (providerTable.empty()) {
618 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
619 BOOST_ASSERT(!providerTable.empty());
620 }
621
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200622 auto it = providerTable.find(scheme);
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500623 return it == providerTable.end() ? nullptr : it->second.get();
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700624}
625
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500626
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700627bool
628FaceUri::canCanonize(const std::string& scheme)
629{
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500630 return getCanonizeProvider(scheme) != nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700631}
632
633bool
634FaceUri::isCanonical() const
635{
636 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoe1081cd2016-12-19 23:58:15 -0500637 if (cp == nullptr) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700638 return false;
639 }
640
641 return cp->isCanonical(*this);
642}
643
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700644void
645FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
646 const CanonizeFailureCallback& onFailure,
Junxiao Shid5c2f0c2017-04-04 09:52:11 +0000647 boost::asio::io_service& io, time::nanoseconds timeout) const
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700648{
649 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200650 if (cp == nullptr) {
651 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700652 onFailure("scheme not supported");
653 }
654 return;
655 }
656
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700657 cp->canonize(*this,
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400658 onSuccess ? onSuccess : [] (auto&&) {},
659 onFailure ? onFailure : [] (auto&&) {},
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700660 io, timeout);
661}
662
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700663} // namespace ndn