face-uri.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
28 #include "face-uri.hpp"
29 #include "dns.hpp"
30 #include "ethernet.hpp"
31 
32 #include <boost/concept_check.hpp>
33 #include <boost/lexical_cast.hpp>
34 #include <boost/mpl/vector.hpp>
35 #include <boost/mpl/for_each.hpp>
36 #include <boost/regex.hpp>
37 #include <set>
38 
39 namespace ndn {
40 namespace util {
41 
42 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
43 
45  : m_isV6(false)
46 {
47 }
48 
49 FaceUri::FaceUri(const std::string& uri)
50 {
51  if (!parse(uri)) {
52  BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
53  }
54 }
55 
56 FaceUri::FaceUri(const char* uri)
57  : FaceUri(std::string(uri))
58 {
59 }
60 
61 bool
62 FaceUri::parse(const std::string& uri)
63 {
64  m_scheme.clear();
65  m_host.clear();
66  m_port.clear();
67  m_path.clear();
68  m_isV6 = false;
69 
70  static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
71  boost::smatch protocolMatch;
72  if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
73  return false;
74  }
75  m_scheme = protocolMatch[1];
76  const std::string& authority = protocolMatch[3];
77  m_path = protocolMatch[4];
78 
79  // pattern for IPv6 address enclosed in [ ], with optional port number
80  static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
81  // pattern for Ethernet address in standard hex-digits-and-colons notation
82  static const boost::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
83  // pattern for IPv4-mapped IPv6 address, with optional port number
84  static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
85  // pattern for IPv4/hostname/fd/ifname, with optional port number
86  static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
87 
88  if (authority.empty()) {
89  // UNIX, internal
90  }
91  else {
92  boost::smatch match;
93  m_isV6 = boost::regex_match(authority, match, v6Exp);
94  if (m_isV6 ||
95  boost::regex_match(authority, match, etherExp) ||
96  boost::regex_match(authority, match, v4MappedV6Exp) ||
97  boost::regex_match(authority, match, v4HostExp)) {
98  m_host = match[1];
99  m_port = match[2];
100  }
101  else {
102  return false;
103  }
104  }
105 
106  return true;
107 }
108 
109 FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
110 {
111  m_isV6 = endpoint.address().is_v6();
112  m_scheme = m_isV6 ? "udp6" : "udp4";
113  m_host = endpoint.address().to_string();
114  m_port = to_string(endpoint.port());
115 }
116 
117 FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
118 {
119  m_isV6 = endpoint.address().is_v6();
120  m_scheme = m_isV6 ? "tcp6" : "tcp4";
121  m_host = endpoint.address().to_string();
122  m_port = to_string(endpoint.port());
123 }
124 
125 FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
126 {
127  m_isV6 = endpoint.address().is_v6();
128  m_scheme = scheme;
129  m_host = endpoint.address().to_string();
130  m_port = to_string(endpoint.port());
131 }
132 
133 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
134 FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
135  : m_scheme("unix")
136  , m_path(endpoint.path())
137  , m_isV6(false)
138 {
139 }
140 #endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
141 
142 FaceUri
144 {
145  FaceUri uri;
146  uri.m_scheme = "fd";
147  uri.m_host = to_string(fd);
148  return uri;
149 }
150 
152  : m_scheme("ether")
153  , m_host(address.toString())
154  , m_isV6(true)
155 {
156 }
157 
158 FaceUri
159 FaceUri::fromDev(const std::string& ifname)
160 {
161  FaceUri uri;
162  uri.m_scheme = "dev";
163  uri.m_host = ifname;
164  return uri;
165 }
166 
167 FaceUri
168 FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
169 {
170  FaceUri uri;
171  uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
172  uri.m_host = ifname;
173  uri.m_port = to_string(endpoint.port());
174  return uri;
175 }
176 
177 bool
178 FaceUri::operator==(const FaceUri& rhs) const
179 {
180  return m_isV6 == rhs.m_isV6 &&
181  m_scheme == rhs.m_scheme &&
182  m_host == rhs.m_host &&
183  m_port == rhs.m_port &&
184  m_path == rhs.m_path;
185 }
186 
187 bool
188 FaceUri::operator!=(const FaceUri& rhs) const
189 {
190  return !(*this == rhs);
191 }
192 
193 std::string
195 {
196  std::ostringstream os;
197  os << *this;
198  return os.str();
199 }
200 
201 std::ostream&
202 operator<<(std::ostream& os, const FaceUri& uri)
203 {
204  os << uri.m_scheme << "://";
205  if (uri.m_isV6) {
206  os << "[" << uri.m_host << "]";
207  }
208  else {
209  os << uri.m_host;
210  }
211  if (!uri.m_port.empty()) {
212  os << ":" << uri.m_port;
213  }
214  os << uri.m_path;
215  return os;
216 }
217 
218 
221 class CanonizeProvider : noncopyable
222 {
223 public:
224  virtual
225  ~CanonizeProvider() = default;
226 
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 
240 template<typename Protocol>
242 {
243 public:
244  std::set<std::string>
245  getSchemes() const override
246  {
247  return {m_baseScheme, m_v4Scheme, m_v6Scheme};
248  }
249 
250  bool
251  isCanonical(const FaceUri& faceUri) const override
252  {
253  if (faceUri.getPort().empty()) {
254  return false;
255  }
256  if (!faceUri.getPath().empty()) {
257  return false;
258  }
259 
260  boost::system::error_code ec;
261  boost::asio::ip::address addr;
262  if (faceUri.getScheme() == m_v4Scheme) {
263  addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
264  }
265  else if (faceUri.getScheme() == m_v6Scheme) {
266  addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
267  }
268  else {
269  return false;
270  }
271 
272  return !ec && addr.to_string() == faceUri.getHost() && checkAddress(addr).first;
273  }
274 
275  void
276  canonize(const FaceUri& faceUri,
277  const FaceUri::CanonizeSuccessCallback& onSuccess,
278  const FaceUri::CanonizeFailureCallback& onFailure,
279  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
280  {
281  if (this->isCanonical(faceUri)) {
282  onSuccess(faceUri);
283  return;
284  }
285 
286  dns::AddressSelector addressSelector;
287  if (faceUri.getScheme() == m_v4Scheme) {
288  addressSelector = dns::Ipv4Only();
289  }
290  else if (faceUri.getScheme() == m_v6Scheme) {
291  addressSelector = dns::Ipv6Only();
292  }
293  else {
294  BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
295  addressSelector = dns::AnyAddress();
296  }
297 
298  // make a copy because caller may modify faceUri
299  auto uri = make_shared<FaceUri>(faceUri);
300  dns::asyncResolve(faceUri.getHost(),
301  bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
302  bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
303  io, addressSelector, timeout);
304  }
305 
306 protected:
307  explicit
308  IpHostCanonizeProvider(const std::string& baseScheme,
309  uint16_t defaultUnicastPort = 6363,
310  uint16_t defaultMulticastPort = 56363)
311  : m_baseScheme(baseScheme)
312  , m_v4Scheme(baseScheme + '4')
313  , m_v6Scheme(baseScheme + '6')
314  , m_defaultUnicastPort(defaultUnicastPort)
315  , m_defaultMulticastPort(defaultMulticastPort)
316  {
317  }
318 
319 private:
320  void
321  onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
322  const FaceUri::CanonizeSuccessCallback& onSuccess,
323  const FaceUri::CanonizeFailureCallback& onFailure,
324  const dns::IpAddress& ipAddress) const
325  {
326  bool isOk = false;
327  std::string reason;
328  std::tie(isOk, reason) = this->checkAddress(ipAddress);
329  if (!isOk) {
330  return onFailure(reason);
331  }
332 
333  uint16_t port = 0;
334  if (faceUri->getPort().empty()) {
335  port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
336  }
337  else {
338  try {
339  port = boost::lexical_cast<uint16_t>(faceUri->getPort());
340  }
341  catch (const boost::bad_lexical_cast&) {
342  return onFailure("invalid port number '" + faceUri->getPort() + "'");
343  }
344  }
345 
346  FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
347  BOOST_ASSERT(canonicalUri.isCanonical());
348  onSuccess(canonicalUri);
349  }
350 
351  void
352  onDnsFailure(const shared_ptr<FaceUri>& faceUri,
353  const FaceUri::CanonizeFailureCallback& onFailure,
354  const std::string& reason) const
355  {
356  onFailure(reason);
357  }
358 
363  virtual std::pair<bool, std::string>
364  checkAddress(const dns::IpAddress& ipAddress) const
365  {
366  return {true, ""};
367  }
368 
369 private:
370  std::string m_baseScheme;
371  std::string m_v4Scheme;
372  std::string m_v6Scheme;
373  uint16_t m_defaultUnicastPort;
374  uint16_t m_defaultMulticastPort;
375 };
376 
377 class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
378 {
379 public:
381  : IpHostCanonizeProvider("udp")
382  {
383  }
384 
385 protected:
386  // checkAddress is not overriden:
387  // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
388  // FaceMgmt protocol allows IPv6 multicast address in UDP.
389 };
390 
391 class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
392 {
393 public:
395  : IpHostCanonizeProvider("tcp")
396  {
397  }
398 
399 protected:
400  std::pair<bool, std::string>
401  checkAddress(const dns::IpAddress& ipAddress) const override
402  {
403  if (ipAddress.is_multicast()) {
404  return {false, "cannot use multicast address"};
405  }
406  return {true, ""};
407  }
408 };
409 
411 {
412 public:
413  std::set<std::string>
414  getSchemes() const override
415  {
416  return {"ether"};
417  }
418 
419  bool
420  isCanonical(const FaceUri& faceUri) const override
421  {
422  if (!faceUri.getPort().empty()) {
423  return false;
424  }
425  if (!faceUri.getPath().empty()) {
426  return false;
427  }
428 
429  auto addr = ethernet::Address::fromString(faceUri.getHost());
430  return addr.toString() == faceUri.getHost();
431  }
432 
433  void
434  canonize(const FaceUri& faceUri,
435  const FaceUri::CanonizeSuccessCallback& onSuccess,
436  const FaceUri::CanonizeFailureCallback& onFailure,
437  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
438  {
439  auto addr = ethernet::Address::fromString(faceUri.getHost());
440  if (addr.isNull()) {
441  return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
442  }
443 
444  FaceUri canonicalUri(addr);
445  BOOST_ASSERT(canonicalUri.isCanonical());
446  onSuccess(canonicalUri);
447  }
448 };
449 
451 {
452 public:
453  std::set<std::string>
454  getSchemes() const override
455  {
456  return {"udp4+dev", "udp6+dev"};
457  }
458 
459  bool
460  isCanonical(const FaceUri& faceUri) const override
461  {
462  if (faceUri.getPort().empty()) {
463  return false;
464  }
465  if (!faceUri.getPath().empty()) {
466  return false;
467  }
468  return true;
469  }
470 
471  void
472  canonize(const FaceUri& faceUri,
473  const FaceUri::CanonizeSuccessCallback& onSuccess,
474  const FaceUri::CanonizeFailureCallback& onFailure,
475  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
476  {
477  if (this->isCanonical(faceUri)) {
478  onSuccess(faceUri);
479  }
480  else {
481  onFailure("cannot canonize " + faceUri.toString());
482  }
483  }
484 };
485 
486 using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
487  TcpCanonizeProvider*,
488  EtherCanonizeProvider*,
490 using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
491 
493 {
494 public:
495  explicit
497  : m_providerTable(providerTable)
498  {
499  }
500 
501  template<typename CP>
502  void
504  {
505  shared_ptr<CanonizeProvider> cp = make_shared<CP>();
506  auto schemes = cp->getSchemes();
507  BOOST_ASSERT(!schemes.empty());
508 
509  for (const auto& scheme : schemes) {
510  BOOST_ASSERT(m_providerTable.count(scheme) == 0);
511  m_providerTable[scheme] = cp;
512  }
513  }
514 
515 private:
516  CanonizeProviderTable& m_providerTable;
517 };
518 
519 static const CanonizeProvider*
520 getCanonizeProvider(const std::string& scheme)
521 {
522  static CanonizeProviderTable providerTable;
523  if (providerTable.empty()) {
524  boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
525  BOOST_ASSERT(!providerTable.empty());
526  }
527 
528  auto it = providerTable.find(scheme);
529  return it == providerTable.end() ? nullptr : it->second.get();
530 }
531 
532 
533 bool
534 FaceUri::canCanonize(const std::string& scheme)
535 {
536  return getCanonizeProvider(scheme) != nullptr;
537 }
538 
539 bool
541 {
542  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
543  if (cp == nullptr) {
544  return false;
545  }
546 
547  return cp->isCanonical(*this);
548 }
549 
550 void
552  const CanonizeFailureCallback& onFailure,
553  boost::asio::io_service& io, const time::nanoseconds& timeout) const
554 {
555  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
556  if (cp == nullptr) {
557  if (onFailure) {
558  onFailure("scheme not supported");
559  }
560  return;
561  }
562 
563  static CanonizeSuccessCallback successNop = bind([]{});
564  static CanonizeFailureCallback failureNop = bind([]{});
565  cp->canonize(*this,
566  onSuccess ? onSuccess : successNop,
567  onFailure ? onFailure : failureNop,
568  io, timeout);
569 }
570 
571 } // namespace util
572 } // namespace ndn
virtual bool isCanonical(const FaceUri &faceUri) const =0
std::map< std::string, shared_ptr< CanonizeProvider >> CanonizeProviderTable
Definition: face-uri.cpp:490
virtual ~CanonizeProvider()=default
Copyright (c) 2013-2016 Regents of the University of California.
Definition: common.hpp:74
bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:420
const std::string & getPath() const
get path
Definition: face-uri.hpp:138
bool parse(const std::string &uri)
exception-safe parsing
Definition: face-uri.cpp:62
static bool canCanonize(const std::string &scheme)
Definition: face-uri.cpp:534
bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:251
bool operator==(const FaceUri &rhs) const
Definition: face-uri.cpp:178
represents an Ethernet hardware address
Definition: ethernet.hpp:53
static Address fromString(const std::string &str)
Creates an Address from a string containing an Ethernet address in hexadecimal notation, with colons or hyphens as separators.
Definition: ethernet.cpp:86
represents the underlying protocol and address used by a Face
Definition: face-uri.hpp:48
virtual std::set< std::string > getSchemes() const =0
bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:460
STL namespace.
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
Definition: face-uri.cpp:520
function< void(const FaceUri &)> CanonizeSuccessCallback
Definition: face-uri.hpp:168
void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const override
Definition: face-uri.cpp:472
function< bool(const IpAddress &address)> AddressSelector
Definition: dns.hpp:40
std::string toString(char sep= ':') const
Converts the address to a human-readable string.
Definition: ethernet.cpp:72
std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:414
const std::string & getPort() const
get port
Definition: face-uri.hpp:131
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, UdpDevCanonizeProvider * > CanonizeProviders
Definition: face-uri.cpp:489
CanonizeProviderTableInitializer(CanonizeProviderTable &providerTable)
Definition: face-uri.cpp:496
std::string toString(const system_clock::TimePoint &timePoint, const std::string &format, const std::locale &locale)
Convert time point to string with specified format.
Definition: time.cpp:162
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:540
virtual void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const =0
const std::string & getScheme() const
get scheme (protocol)
Definition: face-uri.hpp:117
void asyncResolve(const std::string &host, const SuccessCallback &onSuccess, const ErrorCallback &onError, boost::asio::io_service &ioService, const AddressSelector &addressSelector, time::nanoseconds timeout)
Asynchronously resolve host.
Definition: dns.cpp:132
IpHostCanonizeProvider(const std::string &baseScheme, uint16_t defaultUnicastPort=6363, uint16_t defaultMulticastPort=56363)
Definition: face-uri.cpp:308
std::ostream & operator<<(std::ostream &os, Digest< Hash > &digest)
Definition: digest.cpp:170
bool operator!=(const FaceUri &rhs) const
Definition: face-uri.cpp:188
std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:454
a CanonizeProvider provides FaceUri canonization functionality for a group of schemes ...
Definition: face-uri.cpp:221
const std::string & getHost() const
get host (domain)
Definition: face-uri.hpp:124
boost::asio::ip::address IpAddress
Definition: dns.hpp:39
static FaceUri fromDev(const std::string &ifname)
create dev FaceUri from network device name
Definition: face-uri.cpp:159
std::string to_string(const V &v)
Definition: backports.hpp:51
std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:245
static FaceUri fromUdpDev(const boost::asio::ip::udp::endpoint &endpoint, const std::string &ifname)
create udp4 or udp6 NIC-associated FaceUri from endpoint and network device name
Definition: face-uri.cpp:168
std::string toString() const
write as a string
Definition: face-uri.cpp:194
void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const override
Definition: face-uri.cpp:434
std::pair< bool, std::string > checkAddress(const dns::IpAddress &ipAddress) const override
when overriden in a subclass, check the IP address is allowable
Definition: face-uri.cpp:401
function< void(const std::string &reason)> CanonizeFailureCallback
Definition: face-uri.hpp:169
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
Definition: face-uri.cpp:143
void canonize(const CanonizeSuccessCallback &onSuccess, const CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const
asynchronously convert this FaceUri to canonical form
Definition: face-uri.cpp:551
void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const override
Definition: face-uri.cpp:276