blob: acdbc118b1cc7004ca8c5a466cbd166fa49f73c1 [file] [log] [blame]
Junxiao Shi77dcadd2014-10-05 14:40:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +02003 * Copyright (c) 2015-2016, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
Junxiao Shi77dcadd2014-10-05 14:40:54 -070010 *
11 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
12 *
13 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
14 * terms of the GNU Lesser General Public License as published by the Free Software
15 * Foundation, either version 3 of the License, or (at your option) any later version.
16 *
17 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
20 *
21 * You should have received copies of the GNU General Public License and GNU Lesser
22 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
26 */
27
28#include "face-uri.hpp"
Junxiao Shi4083c8d2014-10-12 16:43:16 -070029#include "dns.hpp"
Junxiao Shi77dcadd2014-10-05 14:40:54 -070030
Junxiao Shi4083c8d2014-10-12 16:43:16 -070031#include <set>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070032#include <boost/concept_check.hpp>
33#include <boost/regex.hpp>
Junxiao Shie3ef6ee2014-10-05 14:40:54 -070034#include <boost/lexical_cast.hpp>
Junxiao Shi4083c8d2014-10-12 16:43:16 -070035#include <boost/mpl/vector.hpp>
36#include <boost/mpl/for_each.hpp>
Junxiao Shi77dcadd2014-10-05 14:40:54 -070037
38namespace ndn {
39namespace util {
40
41BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
42
43FaceUri::FaceUri()
44 : m_isV6(false)
45{
46}
47
48FaceUri::FaceUri(const std::string& uri)
49{
50 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070051 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070052 }
53}
54
55FaceUri::FaceUri(const char* uri)
56{
57 if (!parse(uri)) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070058 BOOST_THROW_EXCEPTION(Error("Malformed URI: " + std::string(uri)));
Junxiao Shi77dcadd2014-10-05 14:40:54 -070059 }
60}
61
62bool
63FaceUri::parse(const std::string& uri)
64{
65 m_scheme.clear();
66 m_host.clear();
67 m_isV6 = false;
68 m_port.clear();
69 m_path.clear();
70
Weiwei Liud7f4fda2016-10-19 22:38:39 -070071 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
Junxiao Shi77dcadd2014-10-05 14:40:54 -070072 boost::smatch protocolMatch;
73 if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
74 return false;
75 }
76 m_scheme = protocolMatch[1];
Weiwei Liud7f4fda2016-10-19 22:38:39 -070077 const std::string& authority = protocolMatch[3];
78 m_path = protocolMatch[4];
Junxiao Shi77dcadd2014-10-05 14:40:54 -070079
80 // pattern for IPv6 address enclosed in [ ], with optional port number
81 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
82 // pattern for Ethernet address in standard hex-digits-and-colons notation
83 static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
84 // pattern for IPv4-mapped IPv6 address, with optional port number
85 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
86 // pattern for IPv4/hostname/fd/ifname, with optional port number
87 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
88
89 if (authority.empty()) {
90 // UNIX, internal
91 }
92 else {
93 boost::smatch match;
94 m_isV6 = boost::regex_match(authority, match, v6Exp);
95 if (m_isV6 ||
96 boost::regex_match(authority, match, etherExp) ||
97 boost::regex_match(authority, match, v4MappedV6Exp) ||
98 boost::regex_match(authority, match, v4HostExp)) {
99 m_host = match[1];
100 m_port = match[2];
101 }
102 else {
103 return false;
104 }
105 }
106
107 return true;
108}
109
110FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
111{
112 m_isV6 = endpoint.address().is_v6();
113 m_scheme = m_isV6 ? "udp6" : "udp4";
114 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700115 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700116}
117
118FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
119{
120 m_isV6 = endpoint.address().is_v6();
121 m_scheme = m_isV6 ? "tcp6" : "tcp4";
122 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700123 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700124}
125
126FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
127 : m_scheme(scheme)
128{
129 m_isV6 = endpoint.address().is_v6();
130 m_host = endpoint.address().to_string();
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700131 m_port = to_string(endpoint.port());
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700132}
133
134#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
135FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
136 : m_isV6(false)
137{
138 m_scheme = "unix";
139 m_path = endpoint.path();
140}
141#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
142
143FaceUri
144FaceUri::fromFd(int fd)
145{
146 FaceUri uri;
147 uri.m_scheme = "fd";
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700148 uri.m_host = to_string(fd);
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700149 return uri;
150}
151
152FaceUri::FaceUri(const ethernet::Address& address)
153 : m_isV6(true)
154{
155 m_scheme = "ether";
156 m_host = address.toString();
157}
158
159FaceUri
160FaceUri::fromDev(const std::string& ifname)
161{
162 FaceUri uri;
163 uri.m_scheme = "dev";
164 uri.m_host = ifname;
165 return uri;
166}
167
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700168FaceUri
169FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
170{
171 FaceUri uri;
172 uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
173 uri.m_host = ifname;
174 uri.m_port = to_string(endpoint.port());
175 return uri;
176}
177
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700178bool
179FaceUri::operator==(const FaceUri& rhs) const
180{
181 return (m_scheme == rhs.m_scheme &&
182 m_host == rhs.m_host &&
183 m_isV6 == rhs.m_isV6 &&
184 m_port == rhs.m_port &&
185 m_path == rhs.m_path);
186}
187
188bool
189FaceUri::operator!=(const FaceUri& rhs) const
190{
191 return !(*this == rhs);
192}
193
194std::string
195FaceUri::toString() const
196{
197 std::ostringstream os;
198 os << *this;
199 return os.str();
200}
201
202std::ostream&
203operator<<(std::ostream& os, const FaceUri& uri)
204{
205 os << uri.m_scheme << "://";
206 if (uri.m_isV6) {
207 os << "[" << uri.m_host << "]";
208 }
209 else {
210 os << uri.m_host;
211 }
212 if (!uri.m_port.empty()) {
213 os << ":" << uri.m_port;
214 }
215 os << uri.m_path;
216 return os;
217}
218
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700219/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
220 */
221class CanonizeProvider : noncopyable
222{
223public:
224 virtual
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200225 ~CanonizeProvider() = default;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700226
227 virtual std::set<std::string>
228 getSchemes() const = 0;
229
230 virtual bool
231 isCanonical(const FaceUri& faceUri) const = 0;
232
233 virtual void
234 canonize(const FaceUri& faceUri,
235 const FaceUri::CanonizeSuccessCallback& onSuccess,
236 const FaceUri::CanonizeFailureCallback& onFailure,
237 boost::asio::io_service& io, const time::nanoseconds& timeout) const = 0;
238};
239
240template<typename Protocol>
241class IpHostCanonizeProvider : public CanonizeProvider
242{
243public:
244 virtual std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200245 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700246 {
247 std::set<std::string> schemes;
248 schemes.insert(m_baseScheme);
249 schemes.insert(m_v4Scheme);
250 schemes.insert(m_v6Scheme);
251 return schemes;
252 }
253
254 virtual bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200255 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700256 {
257 if (faceUri.getPort().empty()) {
258 return false;
259 }
260 if (!faceUri.getPath().empty()) {
261 return false;
262 }
263
264 boost::system::error_code ec;
265 boost::asio::ip::address addr;
266 if (faceUri.getScheme() == m_v4Scheme) {
267 addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
268 }
269 else if (faceUri.getScheme() == m_v6Scheme) {
270 addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
271 }
272 else {
273 return false;
274 }
275 return !static_cast<bool>(ec) && addr.to_string() == faceUri.getHost() &&
276 this->checkAddress(addr).first;
277 }
278
279 virtual void
280 canonize(const FaceUri& faceUri,
281 const FaceUri::CanonizeSuccessCallback& onSuccess,
282 const FaceUri::CanonizeFailureCallback& onFailure,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200283 boost::asio::io_service& io, const time::nanoseconds& timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700284 {
285 if (this->isCanonical(faceUri)) {
286 onSuccess(faceUri);
287 return;
288 }
289
290 dns::AddressSelector addressSelector;
291 if (faceUri.getScheme() == m_v4Scheme) {
292 addressSelector = dns::Ipv4Only();
293 }
294 else if (faceUri.getScheme() == m_v6Scheme) {
295 addressSelector = dns::Ipv6Only();
296 }
297 else {
298 BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
299 addressSelector = dns::AnyAddress();
300 }
301
302 // make a copy because caller may modify faceUri
303 shared_ptr<FaceUri> uri = make_shared<FaceUri>(faceUri);
304 dns::asyncResolve(faceUri.getHost(),
305 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
306 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
307 io, addressSelector, timeout);
308 }
309
310protected:
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200311 explicit
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700312 IpHostCanonizeProvider(const std::string& baseScheme,
313 uint32_t defaultUnicastPort = 6363,
314 uint32_t defaultMulticastPort = 56363)
315 : m_baseScheme(baseScheme)
316 , m_v4Scheme(baseScheme + "4")
317 , m_v6Scheme(baseScheme + "6")
318 , m_defaultUnicastPort(defaultUnicastPort)
319 , m_defaultMulticastPort(defaultMulticastPort)
320 {
321 }
322
323private:
324 // faceUri is a shared_ptr passed by value because this function can take ownership
325 void
326 onDnsSuccess(shared_ptr<FaceUri> faceUri,
327 const FaceUri::CanonizeSuccessCallback& onSuccess,
328 const FaceUri::CanonizeFailureCallback& onFailure,
329 const dns::IpAddress& ipAddress) const
330 {
331 std::pair<bool, std::string> checkAddressRes = this->checkAddress(ipAddress);
332 if (!checkAddressRes.first) {
333 onFailure(checkAddressRes.second);
334 return;
335 }
336
337 uint32_t port = 0;
338 if (faceUri->getPort().empty()) {
339 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
340 }
341 else {
342 try {
343 port = boost::lexical_cast<uint32_t>(faceUri->getPort());
344 }
345 catch (boost::bad_lexical_cast&) {
346 onFailure("invalid port number");
347 return;
348 }
349 }
350
351 FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
352 BOOST_ASSERT(canonicalUri.isCanonical());
353 onSuccess(canonicalUri);
354 }
355
356 // faceUri is a shared_ptr passed by value because this function can take ownership
357 void
358 onDnsFailure(shared_ptr<FaceUri> faceUri, const FaceUri::CanonizeFailureCallback& onFailure,
359 const std::string& reason) const
360 {
361 onFailure(reason);
362 }
363
364 /** \brief when overriden in a subclass, check the IP address is allowable
365 * \return (true,ignored) if the address is allowable;
366 * (false,reason) if the address is not allowable.
367 */
368 virtual std::pair<bool, std::string>
369 checkAddress(const dns::IpAddress& ipAddress) const
370 {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200371 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700372 }
373
374private:
375 std::string m_baseScheme;
376 std::string m_v4Scheme;
377 std::string m_v6Scheme;
378 uint32_t m_defaultUnicastPort;
379 uint32_t m_defaultMulticastPort;
380};
381
382class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
383{
384public:
385 UdpCanonizeProvider()
386 : IpHostCanonizeProvider("udp")
387 {
388 }
389
390protected:
391 // checkAddress is not overriden:
392 // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
393 // FaceMgmt protocol allows IPv6 multicast address in UDP.
394};
395
396class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
397{
398public:
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700399 TcpCanonizeProvider()
400 : IpHostCanonizeProvider("tcp")
401 {
402 }
403
404protected:
405 virtual std::pair<bool, std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200406 checkAddress(const dns::IpAddress& ipAddress) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700407 {
408 if (ipAddress.is_multicast()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200409 return {false, "cannot use multicast address"};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700410 }
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200411 return {true, ""};
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700412 }
413};
414
415class EtherCanonizeProvider : public CanonizeProvider
416{
417public:
418 virtual std::set<std::string>
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200419 getSchemes() const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700420 {
421 std::set<std::string> schemes;
422 schemes.insert("ether");
423 return schemes;
424 }
425
426 virtual bool
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200427 isCanonical(const FaceUri& faceUri) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700428 {
429 if (!faceUri.getPort().empty()) {
430 return false;
431 }
432 if (!faceUri.getPath().empty()) {
433 return false;
434 }
435
436 ethernet::Address addr = ethernet::Address::fromString(faceUri.getHost());
437 return addr.toString() == faceUri.getHost();
438 }
439
440 virtual void
441 canonize(const FaceUri& faceUri,
442 const FaceUri::CanonizeSuccessCallback& onSuccess,
443 const FaceUri::CanonizeFailureCallback& onFailure,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200444 boost::asio::io_service& io, const time::nanoseconds& timeout) const override
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700445 {
446 ethernet::Address addr = ethernet::Address::fromString(faceUri.getHost());
447 if (addr.isNull()) {
448 onFailure("cannot parse address");
449 return;
450 }
451
452 FaceUri canonicalUri(addr);
453 BOOST_ASSERT(canonicalUri.isCanonical());
454 onSuccess(canonicalUri);
455 }
456};
457
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700458class UdpDevCanonizeProvider : public CanonizeProvider
459{
460public:
461 virtual std::set<std::string>
462 getSchemes() const override
463 {
464 return {"udp4+dev", "udp6+dev"};
465 }
466
467 virtual bool
468 isCanonical(const FaceUri& faceUri) const override
469 {
470 if (faceUri.getPort().empty()) {
471 return false;
472 }
473 if (!faceUri.getPath().empty()) {
474 return false;
475 }
476 return true;
477 }
478
479 virtual void
480 canonize(const FaceUri& faceUri,
481 const FaceUri::CanonizeSuccessCallback& onSuccess,
482 const FaceUri::CanonizeFailureCallback& onFailure,
483 boost::asio::io_service& io, const time::nanoseconds& timeout) const override
484 {
485 if (this->isCanonical(faceUri)) {
486 onSuccess(faceUri);
487 }
488 else {
489 onFailure("cannot canonize " + faceUri.toString());
490 }
491 }
492};
493
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700494typedef boost::mpl::vector<
495 UdpCanonizeProvider*,
496 TcpCanonizeProvider*,
Weiwei Liud7f4fda2016-10-19 22:38:39 -0700497 EtherCanonizeProvider*,
498 UdpDevCanonizeProvider*
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700499 > CanonizeProviders;
500typedef std::map<std::string, shared_ptr<CanonizeProvider> > CanonizeProviderTable;
501
502class CanonizeProviderTableInitializer
503{
504public:
505 explicit
506 CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
507 : m_providerTable(providerTable)
508 {
509 }
510
511 template<typename CP> void
512 operator()(CP*)
513 {
514 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
515
516 std::set<std::string> schemes = cp->getSchemes();
517 BOOST_ASSERT(!schemes.empty());
518 for (std::set<std::string>::iterator it = schemes.begin();
519 it != schemes.end(); ++it) {
520 BOOST_ASSERT(m_providerTable.count(*it) == 0);
521 m_providerTable[*it] = cp;
522 }
523 }
524
525private:
526 CanonizeProviderTable& m_providerTable;
527};
528
529static const CanonizeProvider*
530getCanonizeProvider(const std::string& scheme)
531{
532 static CanonizeProviderTable providerTable;
533 if (providerTable.empty()) {
534 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
535 BOOST_ASSERT(!providerTable.empty());
536 }
537
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200538 auto it = providerTable.find(scheme);
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700539 if (it == providerTable.end()) {
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200540 return nullptr;
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700541 }
542 return it->second.get();
543}
544
545bool
546FaceUri::canCanonize(const std::string& scheme)
547{
548 return getCanonizeProvider(scheme) != 0;
549}
550
551bool
552FaceUri::isCanonical() const
553{
554 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
555 if (cp == 0) {
556 return false;
557 }
558
559 return cp->isCanonical(*this);
560}
561
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700562void
563FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
564 const CanonizeFailureCallback& onFailure,
565 boost::asio::io_service& io, const time::nanoseconds& timeout) const
566{
567 const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200568 if (cp == nullptr) {
569 if (onFailure) {
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700570 onFailure("scheme not supported");
571 }
572 return;
573 }
574
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200575 static CanonizeSuccessCallback successNop = bind([]{});
576 static CanonizeFailureCallback failureNop = bind([]{});
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700577
578 cp->canonize(*this,
Davide Pesaventoaeeb3fc2016-08-14 03:40:02 +0200579 onSuccess ? onSuccess : successNop,
580 onFailure ? onFailure : failureNop,
Junxiao Shi4083c8d2014-10-12 16:43:16 -0700581 io, timeout);
582}
583
Junxiao Shi77dcadd2014-10-05 14:40:54 -0700584} // namespace util
585} // namespace ndn