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 
31 #include <set>
32 #include <boost/concept_check.hpp>
33 #include <boost/regex.hpp>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/mpl/vector.hpp>
36 #include <boost/mpl/for_each.hpp>
37 
38 namespace ndn {
39 namespace util {
40 
41 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
42 
44  : m_isV6(false)
45 {
46 }
47 
48 FaceUri::FaceUri(const std::string& uri)
49 {
50  if (!parse(uri)) {
51  BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
52  }
53 }
54 
55 FaceUri::FaceUri(const char* uri)
56 {
57  if (!parse(uri)) {
58  BOOST_THROW_EXCEPTION(Error("Malformed URI: " + std::string(uri)));
59  }
60 }
61 
62 bool
63 FaceUri::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 
71  static const boost::regex protocolExp("(\\w+\\d?)://([^/]*)(\\/[^?]*)?");
72  boost::smatch protocolMatch;
73  if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
74  return false;
75  }
76  m_scheme = protocolMatch[1];
77  const std::string& authority = protocolMatch[2];
78  m_path = protocolMatch[3];
79 
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 
110 FaceUri::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();
115  m_port = boost::lexical_cast<std::string>(endpoint.port());
116 }
117 
118 FaceUri::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();
123  m_port = boost::lexical_cast<std::string>(endpoint.port());
124 }
125 
126 FaceUri::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();
131  m_port = boost::lexical_cast<std::string>(endpoint.port());
132 }
133 
134 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
135 FaceUri::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 
143 FaceUri
145 {
146  FaceUri uri;
147  uri.m_scheme = "fd";
148  uri.m_host = boost::lexical_cast<std::string>(fd);
149  return uri;
150 }
151 
153  : m_isV6(true)
154 {
155  m_scheme = "ether";
156  m_host = address.toString();
157 }
158 
159 FaceUri
160 FaceUri::fromDev(const std::string& ifname)
161 {
162  FaceUri uri;
163  uri.m_scheme = "dev";
164  uri.m_host = ifname;
165  return uri;
166 }
167 
168 bool
169 FaceUri::operator==(const FaceUri& rhs) const
170 {
171  return (m_scheme == rhs.m_scheme &&
172  m_host == rhs.m_host &&
173  m_isV6 == rhs.m_isV6 &&
174  m_port == rhs.m_port &&
175  m_path == rhs.m_path);
176 }
177 
178 bool
179 FaceUri::operator!=(const FaceUri& rhs) const
180 {
181  return !(*this == rhs);
182 }
183 
184 std::string
186 {
187  std::ostringstream os;
188  os << *this;
189  return os.str();
190 }
191 
192 std::ostream&
193 operator<<(std::ostream& os, const FaceUri& uri)
194 {
195  os << uri.m_scheme << "://";
196  if (uri.m_isV6) {
197  os << "[" << uri.m_host << "]";
198  }
199  else {
200  os << uri.m_host;
201  }
202  if (!uri.m_port.empty()) {
203  os << ":" << uri.m_port;
204  }
205  os << uri.m_path;
206  return os;
207 }
208 
211 class CanonizeProvider : noncopyable
212 {
213 public:
214  virtual
215  ~CanonizeProvider() = default;
216 
217  virtual std::set<std::string>
218  getSchemes() const = 0;
219 
220  virtual bool
221  isCanonical(const FaceUri& faceUri) const = 0;
222 
223  virtual void
224  canonize(const FaceUri& faceUri,
225  const FaceUri::CanonizeSuccessCallback& onSuccess,
226  const FaceUri::CanonizeFailureCallback& onFailure,
227  boost::asio::io_service& io, const time::nanoseconds& timeout) const = 0;
228 };
229 
230 template<typename Protocol>
232 {
233 public:
234  virtual std::set<std::string>
235  getSchemes() const override
236  {
237  std::set<std::string> schemes;
238  schemes.insert(m_baseScheme);
239  schemes.insert(m_v4Scheme);
240  schemes.insert(m_v6Scheme);
241  return schemes;
242  }
243 
244  virtual bool
245  isCanonical(const FaceUri& faceUri) const override
246  {
247  if (faceUri.getPort().empty()) {
248  return false;
249  }
250  if (!faceUri.getPath().empty()) {
251  return false;
252  }
253 
254  boost::system::error_code ec;
255  boost::asio::ip::address addr;
256  if (faceUri.getScheme() == m_v4Scheme) {
257  addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
258  }
259  else if (faceUri.getScheme() == m_v6Scheme) {
260  addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
261  }
262  else {
263  return false;
264  }
265  return !static_cast<bool>(ec) && addr.to_string() == faceUri.getHost() &&
266  this->checkAddress(addr).first;
267  }
268 
269  virtual void
270  canonize(const FaceUri& faceUri,
271  const FaceUri::CanonizeSuccessCallback& onSuccess,
272  const FaceUri::CanonizeFailureCallback& onFailure,
273  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
274  {
275  if (this->isCanonical(faceUri)) {
276  onSuccess(faceUri);
277  return;
278  }
279 
280  dns::AddressSelector addressSelector;
281  if (faceUri.getScheme() == m_v4Scheme) {
282  addressSelector = dns::Ipv4Only();
283  }
284  else if (faceUri.getScheme() == m_v6Scheme) {
285  addressSelector = dns::Ipv6Only();
286  }
287  else {
288  BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
289  addressSelector = dns::AnyAddress();
290  }
291 
292  // make a copy because caller may modify faceUri
293  shared_ptr<FaceUri> uri = make_shared<FaceUri>(faceUri);
294  dns::asyncResolve(faceUri.getHost(),
295  bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
296  bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
297  io, addressSelector, timeout);
298  }
299 
300 protected:
301  explicit
302  IpHostCanonizeProvider(const std::string& baseScheme,
303  uint32_t defaultUnicastPort = 6363,
304  uint32_t defaultMulticastPort = 56363)
305  : m_baseScheme(baseScheme)
306  , m_v4Scheme(baseScheme + "4")
307  , m_v6Scheme(baseScheme + "6")
308  , m_defaultUnicastPort(defaultUnicastPort)
309  , m_defaultMulticastPort(defaultMulticastPort)
310  {
311  }
312 
313 private:
314  // faceUri is a shared_ptr passed by value because this function can take ownership
315  void
316  onDnsSuccess(shared_ptr<FaceUri> faceUri,
317  const FaceUri::CanonizeSuccessCallback& onSuccess,
318  const FaceUri::CanonizeFailureCallback& onFailure,
319  const dns::IpAddress& ipAddress) const
320  {
321  std::pair<bool, std::string> checkAddressRes = this->checkAddress(ipAddress);
322  if (!checkAddressRes.first) {
323  onFailure(checkAddressRes.second);
324  return;
325  }
326 
327  uint32_t port = 0;
328  if (faceUri->getPort().empty()) {
329  port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
330  }
331  else {
332  try {
333  port = boost::lexical_cast<uint32_t>(faceUri->getPort());
334  }
335  catch (boost::bad_lexical_cast&) {
336  onFailure("invalid port number");
337  return;
338  }
339  }
340 
341  FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
342  BOOST_ASSERT(canonicalUri.isCanonical());
343  onSuccess(canonicalUri);
344  }
345 
346  // faceUri is a shared_ptr passed by value because this function can take ownership
347  void
348  onDnsFailure(shared_ptr<FaceUri> faceUri, const FaceUri::CanonizeFailureCallback& onFailure,
349  const std::string& reason) const
350  {
351  onFailure(reason);
352  }
353 
358  virtual std::pair<bool, std::string>
359  checkAddress(const dns::IpAddress& ipAddress) const
360  {
361  return {true, ""};
362  }
363 
364 private:
365  std::string m_baseScheme;
366  std::string m_v4Scheme;
367  std::string m_v6Scheme;
368  uint32_t m_defaultUnicastPort;
369  uint32_t m_defaultMulticastPort;
370 };
371 
372 class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
373 {
374 public:
376  : IpHostCanonizeProvider("udp")
377  {
378  }
379 
380 protected:
381  // checkAddress is not overriden:
382  // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
383  // FaceMgmt protocol allows IPv6 multicast address in UDP.
384 };
385 
386 class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
387 {
388 public:
390  : IpHostCanonizeProvider("tcp")
391  {
392  }
393 
394 protected:
395  virtual std::pair<bool, std::string>
396  checkAddress(const dns::IpAddress& ipAddress) const override
397  {
398  if (ipAddress.is_multicast()) {
399  return {false, "cannot use multicast address"};
400  }
401  return {true, ""};
402  }
403 };
404 
406 {
407 public:
408  virtual std::set<std::string>
409  getSchemes() const override
410  {
411  std::set<std::string> schemes;
412  schemes.insert("ether");
413  return schemes;
414  }
415 
416  virtual bool
417  isCanonical(const FaceUri& faceUri) const override
418  {
419  if (!faceUri.getPort().empty()) {
420  return false;
421  }
422  if (!faceUri.getPath().empty()) {
423  return false;
424  }
425 
427  return addr.toString() == faceUri.getHost();
428  }
429 
430  virtual void
431  canonize(const FaceUri& faceUri,
432  const FaceUri::CanonizeSuccessCallback& onSuccess,
433  const FaceUri::CanonizeFailureCallback& onFailure,
434  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
435  {
437  if (addr.isNull()) {
438  onFailure("cannot parse address");
439  return;
440  }
441 
442  FaceUri canonicalUri(addr);
443  BOOST_ASSERT(canonicalUri.isCanonical());
444  onSuccess(canonicalUri);
445  }
446 };
447 
448 typedef boost::mpl::vector<
449  UdpCanonizeProvider*,
450  TcpCanonizeProvider*,
451  EtherCanonizeProvider*
453 typedef std::map<std::string, shared_ptr<CanonizeProvider> > CanonizeProviderTable;
454 
456 {
457 public:
458  explicit
459  CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
460  : m_providerTable(providerTable)
461  {
462  }
463 
464  template<typename CP> void
466  {
467  shared_ptr<CanonizeProvider> cp = make_shared<CP>();
468 
469  std::set<std::string> schemes = cp->getSchemes();
470  BOOST_ASSERT(!schemes.empty());
471  for (std::set<std::string>::iterator it = schemes.begin();
472  it != schemes.end(); ++it) {
473  BOOST_ASSERT(m_providerTable.count(*it) == 0);
474  m_providerTable[*it] = cp;
475  }
476  }
477 
478 private:
479  CanonizeProviderTable& m_providerTable;
480 };
481 
482 static const CanonizeProvider*
483 getCanonizeProvider(const std::string& scheme)
484 {
485  static CanonizeProviderTable providerTable;
486  if (providerTable.empty()) {
487  boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
488  BOOST_ASSERT(!providerTable.empty());
489  }
490 
491  auto it = providerTable.find(scheme);
492  if (it == providerTable.end()) {
493  return nullptr;
494  }
495  return it->second.get();
496 }
497 
498 bool
499 FaceUri::canCanonize(const std::string& scheme)
500 {
501  return getCanonizeProvider(scheme) != 0;
502 }
503 
504 bool
506 {
507  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
508  if (cp == 0) {
509  return false;
510  }
511 
512  return cp->isCanonical(*this);
513 }
514 
515 void
517  const CanonizeFailureCallback& onFailure,
518  boost::asio::io_service& io, const time::nanoseconds& timeout) const
519 {
520  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
521  if (cp == nullptr) {
522  if (onFailure) {
523  onFailure("scheme not supported");
524  }
525  return;
526  }
527 
528  static CanonizeSuccessCallback successNop = bind([]{});
529  static CanonizeFailureCallback failureNop = bind([]{});
530 
531  cp->canonize(*this,
532  onSuccess ? onSuccess : successNop,
533  onFailure ? onFailure : failureNop,
534  io, timeout);
535 }
536 
537 } // namespace util
538 } // namespace ndn
bool isNull() const
True if this is a null address (00:00:00:00:00:00)
Definition: ethernet.cpp:66
virtual bool isCanonical(const FaceUri &faceUri) const =0
virtual ~CanonizeProvider()=default
Copyright (c) 2013-2016 Regents of the University of California.
Definition: common.hpp:74
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider * > CanonizeProviders
Definition: face-uri.cpp:452
const std::string & getPath() const
get path
Definition: face-uri.hpp:130
bool parse(const std::string &uri)
exception-safe parsing
Definition: face-uri.cpp:63
virtual bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:417
static bool canCanonize(const std::string &scheme)
Definition: face-uri.cpp:499
bool operator==(const FaceUri &rhs) const
Definition: face-uri.cpp:169
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:44
virtual std::set< std::string > getSchemes() const =0
IpHostCanonizeProvider(const std::string &baseScheme, uint32_t defaultUnicastPort=6363, uint32_t defaultMulticastPort=56363)
Definition: face-uri.cpp:302
virtual 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:396
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
Definition: face-uri.cpp:483
function< void(const FaceUri &)> CanonizeSuccessCallback
Definition: face-uri.hpp:160
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
const std::string & getPort() const
get port
Definition: face-uri.hpp:123
CanonizeProviderTableInitializer(CanonizeProviderTable &providerTable)
Definition: face-uri.cpp:459
virtual 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:270
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:505
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:109
virtual 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:431
std::map< std::string, shared_ptr< CanonizeProvider > > CanonizeProviderTable
Definition: face-uri.cpp:453
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
std::ostream & operator<<(std::ostream &os, Digest< Hash > &digest)
Definition: digest.cpp:160
bool operator!=(const FaceUri &rhs) const
Definition: face-uri.cpp:179
a CanonizeProvider provides FaceUri canonization functionality for a group of schemes ...
Definition: face-uri.cpp:211
const std::string & getHost() const
get host (domain)
Definition: face-uri.hpp:116
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:160
std::string toString() const
write as a string
Definition: face-uri.cpp:185
virtual std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:409
virtual bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:245
function< void(const std::string &reason)> CanonizeFailureCallback
Definition: face-uri.hpp:161
virtual std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:235
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
Definition: face-uri.cpp:144
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:516