NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.3: NDN, CCN, CCNx, content centric networks
API Documentation
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?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
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[3];
78  m_path = protocolMatch[4];
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 = to_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 = to_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 = to_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 = to_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 FaceUri
169 FaceUri::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 
178 bool
179 FaceUri::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 
188 bool
189 FaceUri::operator!=(const FaceUri& rhs) const
190 {
191  return !(*this == rhs);
192 }
193 
194 std::string
196 {
197  std::ostringstream os;
198  os << *this;
199  return os.str();
200 }
201 
202 std::ostream&
203 operator<<(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 
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  virtual std::set<std::string>
245  getSchemes() const override
246  {
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
255  isCanonical(const FaceUri& faceUri) const override
256  {
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,
283  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
284  {
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 
310 protected:
311  explicit
312  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 
323 private:
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 
368  virtual std::pair<bool, std::string>
369  checkAddress(const dns::IpAddress& ipAddress) const
370  {
371  return {true, ""};
372  }
373 
374 private:
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 
382 class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
383 {
384 public:
386  : IpHostCanonizeProvider("udp")
387  {
388  }
389 
390 protected:
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 
396 class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
397 {
398 public:
400  : IpHostCanonizeProvider("tcp")
401  {
402  }
403 
404 protected:
405  virtual std::pair<bool, std::string>
406  checkAddress(const dns::IpAddress& ipAddress) const override
407  {
408  if (ipAddress.is_multicast()) {
409  return {false, "cannot use multicast address"};
410  }
411  return {true, ""};
412  }
413 };
414 
416 {
417 public:
418  virtual std::set<std::string>
419  getSchemes() const override
420  {
421  std::set<std::string> schemes;
422  schemes.insert("ether");
423  return schemes;
424  }
425 
426  virtual bool
427  isCanonical(const FaceUri& faceUri) const override
428  {
429  if (!faceUri.getPort().empty()) {
430  return false;
431  }
432  if (!faceUri.getPath().empty()) {
433  return false;
434  }
435 
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,
444  boost::asio::io_service& io, const time::nanoseconds& timeout) const override
445  {
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 
459 {
460 public:
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 
494 typedef boost::mpl::vector<
500 typedef std::map<std::string, shared_ptr<CanonizeProvider> > CanonizeProviderTable;
501 
503 {
504 public:
505  explicit
506  CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
507  : m_providerTable(providerTable)
508  {
509  }
510 
511  template<typename CP> void
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 
525 private:
526  CanonizeProviderTable& m_providerTable;
527 };
528 
529 static const CanonizeProvider*
530 getCanonizeProvider(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 
538  auto it = providerTable.find(scheme);
539  if (it == providerTable.end()) {
540  return nullptr;
541  }
542  return it->second.get();
543 }
544 
545 bool
546 FaceUri::canCanonize(const std::string& scheme)
547 {
548  return getCanonizeProvider(scheme) != 0;
549 }
550 
551 bool
553 {
554  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
555  if (cp == 0) {
556  return false;
557  }
558 
559  return cp->isCanonical(*this);
560 }
561 
562 void
564  const CanonizeFailureCallback& onFailure,
565  boost::asio::io_service& io, const time::nanoseconds& timeout) const
566 {
567  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
568  if (cp == nullptr) {
569  if (onFailure) {
570  onFailure("scheme not supported");
571  }
572  return;
573  }
574 
575  static CanonizeSuccessCallback successNop = bind([]{});
576  static CanonizeFailureCallback failureNop = bind([]{});
577 
578  cp->canonize(*this,
579  onSuccess ? onSuccess : successNop,
580  onFailure ? onFailure : failureNop,
581  io, timeout);
582 }
583 
584 } // namespace util
585 } // namespace ndn
Copyright (c) 2011-2015 Regents of the University of California.
virtual bool isCanonical(const FaceUri &faceUri) const =0
bool parse(const std::string &uri)
exception-safe parsing
Definition: face-uri.cpp:63
static bool canCanonize(const std::string &scheme)
Definition: face-uri.cpp:546
virtual std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:419
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 bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:468
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:552
std::string toString() const
write as a string
Definition: face-uri.cpp:195
IpHostCanonizeProvider(const std::string &baseScheme, uint32_t defaultUnicastPort=6363, uint32_t defaultMulticastPort=56363)
Definition: face-uri.cpp:312
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
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
Definition: face-uri.cpp:530
function< void(const FaceUri &)> CanonizeSuccessCallback
Definition: face-uri.hpp:164
function< bool(const IpAddress &address)> AddressSelector
Definition: dns.hpp:40
bool operator!=(const FaceUri &rhs) const
Definition: face-uri.cpp:189
const std::string & getScheme() const
get scheme (protocol)
Definition: face-uri.hpp:113
Table::const_iterator iterator
Definition: cs-internal.hpp:41
virtual std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:245
CanonizeProviderTableInitializer(CanonizeProviderTable &providerTable)
Definition: face-uri.cpp:506
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:280
friend std::ostream & operator<<(std::ostream &os, const FaceUri &uri)
Definition: face-uri.cpp:203
const std::string & getPort() const
get port
Definition: face-uri.hpp:127
std::map< std::string, shared_ptr< CanonizeProvider > > CanonizeProviderTable
Definition: face-uri.cpp:500
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:406
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
virtual bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:427
a CanonizeProvider provides FaceUri canonization functionality for a group of schemes ...
Definition: face-uri.cpp:221
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:563
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, UdpDevCanonizeProvider *> CanonizeProviders
Definition: face-uri.cpp:499
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
virtual std::set< std::string > getSchemes() const override
Definition: face-uri.cpp:462
bool operator==(const FaceUri &rhs) const
Definition: face-uri.cpp:179
std::string to_string(const V &v)
Definition: backports.hpp:51
bool isNull() const
True if this is a null address (00:00:00:00:00:00)
Definition: ethernet.cpp:66
const std::string & getHost() const
get host (domain)
Definition: face-uri.hpp:120
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:169
virtual bool isCanonical(const FaceUri &faceUri) const override
Definition: face-uri.cpp:255
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:480
const std::string & getPath() const
get path
Definition: face-uri.hpp:134
function< void(const std::string &reason)> CanonizeFailureCallback
Definition: face-uri.hpp:165
std::string toString(char sep=':') const
Converts the address to a human-readable string.
Definition: ethernet.cpp:72
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
Definition: face-uri.cpp:144
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:441