NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.0: 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?)://([^/]*)(\\/[^?]*)?");
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
216  {
217  }
218 
219  virtual std::set<std::string>
220  getSchemes() const = 0;
221 
222  virtual bool
223  isCanonical(const FaceUri& faceUri) const = 0;
224 
225  virtual void
226  canonize(const FaceUri& faceUri,
227  const FaceUri::CanonizeSuccessCallback& onSuccess,
228  const FaceUri::CanonizeFailureCallback& onFailure,
229  boost::asio::io_service& io, const time::nanoseconds& timeout) const = 0;
230 };
231 
232 template<typename Protocol>
234 {
235 public:
236  virtual std::set<std::string>
237  getSchemes() const
238  {
239  std::set<std::string> schemes;
240  schemes.insert(m_baseScheme);
241  schemes.insert(m_v4Scheme);
242  schemes.insert(m_v6Scheme);
243  return schemes;
244  }
245 
246  virtual bool
247  isCanonical(const FaceUri& faceUri) const
248  {
249  if (faceUri.getPort().empty()) {
250  return false;
251  }
252  if (!faceUri.getPath().empty()) {
253  return false;
254  }
255 
256  boost::system::error_code ec;
257  boost::asio::ip::address addr;
258  if (faceUri.getScheme() == m_v4Scheme) {
259  addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
260  }
261  else if (faceUri.getScheme() == m_v6Scheme) {
262  addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
263  }
264  else {
265  return false;
266  }
267  return !static_cast<bool>(ec) && addr.to_string() == faceUri.getHost() &&
268  this->checkAddress(addr).first;
269  }
270 
271  virtual void
272  canonize(const FaceUri& faceUri,
273  const FaceUri::CanonizeSuccessCallback& onSuccess,
274  const FaceUri::CanonizeFailureCallback& onFailure,
275  boost::asio::io_service& io, const time::nanoseconds& timeout) const
276  {
277  if (this->isCanonical(faceUri)) {
278  onSuccess(faceUri);
279  return;
280  }
281 
282  dns::AddressSelector addressSelector;
283  if (faceUri.getScheme() == m_v4Scheme) {
284  addressSelector = dns::Ipv4Only();
285  }
286  else if (faceUri.getScheme() == m_v6Scheme) {
287  addressSelector = dns::Ipv6Only();
288  }
289  else {
290  BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
291  addressSelector = dns::AnyAddress();
292  }
293 
294  // make a copy because caller may modify faceUri
295  shared_ptr<FaceUri> uri = make_shared<FaceUri>(faceUri);
296  dns::asyncResolve(faceUri.getHost(),
297  bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
298  bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
299  io, addressSelector, timeout);
300  }
301 
302 protected:
303  IpHostCanonizeProvider(const std::string& baseScheme,
304  uint32_t defaultUnicastPort = 6363,
305  uint32_t defaultMulticastPort = 56363)
306  : m_baseScheme(baseScheme)
307  , m_v4Scheme(baseScheme + "4")
308  , m_v6Scheme(baseScheme + "6")
309  , m_defaultUnicastPort(defaultUnicastPort)
310  , m_defaultMulticastPort(defaultMulticastPort)
311  {
312  }
313 
314 private:
315  // faceUri is a shared_ptr passed by value because this function can take ownership
316  void
317  onDnsSuccess(shared_ptr<FaceUri> faceUri,
318  const FaceUri::CanonizeSuccessCallback& onSuccess,
319  const FaceUri::CanonizeFailureCallback& onFailure,
320  const dns::IpAddress& ipAddress) const
321  {
322  std::pair<bool, std::string> checkAddressRes = this->checkAddress(ipAddress);
323  if (!checkAddressRes.first) {
324  onFailure(checkAddressRes.second);
325  return;
326  }
327 
328  uint32_t port = 0;
329  if (faceUri->getPort().empty()) {
330  port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
331  }
332  else {
333  try {
334  port = boost::lexical_cast<uint32_t>(faceUri->getPort());
335  }
336  catch (boost::bad_lexical_cast&) {
337  onFailure("invalid port number");
338  return;
339  }
340  }
341 
342  FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
343  BOOST_ASSERT(canonicalUri.isCanonical());
344  onSuccess(canonicalUri);
345  }
346 
347  // faceUri is a shared_ptr passed by value because this function can take ownership
348  void
349  onDnsFailure(shared_ptr<FaceUri> faceUri, const FaceUri::CanonizeFailureCallback& onFailure,
350  const std::string& reason) const
351  {
352  onFailure(reason);
353  }
354 
359  virtual std::pair<bool, std::string>
360  checkAddress(const dns::IpAddress& ipAddress) const
361  {
362  return std::make_pair(true, "");
363  }
364 
365 private:
366  std::string m_baseScheme;
367  std::string m_v4Scheme;
368  std::string m_v6Scheme;
369  uint32_t m_defaultUnicastPort;
370  uint32_t m_defaultMulticastPort;
371 };
372 
373 class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
374 {
375 public:
377  : IpHostCanonizeProvider("udp")
378  {
379  }
380 
381 protected:
382  // checkAddress is not overriden:
383  // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
384  // FaceMgmt protocol allows IPv6 multicast address in UDP.
385 };
386 
387 class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
388 {
389 public:
390 public:
392  : IpHostCanonizeProvider("tcp")
393  {
394  }
395 
396 protected:
397  virtual std::pair<bool, std::string>
398  checkAddress(const dns::IpAddress& ipAddress) const
399  {
400  if (ipAddress.is_multicast()) {
401  return std::make_pair(false, "cannot use multicast address");
402  }
403  return std::make_pair(true, "");
404  }
405 };
406 
408 {
409 public:
410  virtual std::set<std::string>
411  getSchemes() const
412  {
413  std::set<std::string> schemes;
414  schemes.insert("ether");
415  return schemes;
416  }
417 
418  virtual bool
419  isCanonical(const FaceUri& faceUri) const
420  {
421  if (!faceUri.getPort().empty()) {
422  return false;
423  }
424  if (!faceUri.getPath().empty()) {
425  return false;
426  }
427 
429  return addr.toString() == faceUri.getHost();
430  }
431 
432  virtual void
433  canonize(const FaceUri& faceUri,
434  const FaceUri::CanonizeSuccessCallback& onSuccess,
435  const FaceUri::CanonizeFailureCallback& onFailure,
436  boost::asio::io_service& io, const time::nanoseconds& timeout) const
437  {
439  if (addr.isNull()) {
440  onFailure("cannot parse address");
441  return;
442  }
443 
444  FaceUri canonicalUri(addr);
445  BOOST_ASSERT(canonicalUri.isCanonical());
446  onSuccess(canonicalUri);
447  }
448 };
449 
450 typedef boost::mpl::vector<
455 typedef std::map<std::string, shared_ptr<CanonizeProvider> > CanonizeProviderTable;
456 
458 {
459 public:
460  explicit
461  CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
462  : m_providerTable(providerTable)
463  {
464  }
465 
466  template<typename CP> void
468  {
469  shared_ptr<CanonizeProvider> cp = make_shared<CP>();
470 
471  std::set<std::string> schemes = cp->getSchemes();
472  BOOST_ASSERT(!schemes.empty());
473  for (std::set<std::string>::iterator it = schemes.begin();
474  it != schemes.end(); ++it) {
475  BOOST_ASSERT(m_providerTable.count(*it) == 0);
476  m_providerTable[*it] = cp;
477  }
478  }
479 
480 private:
481  CanonizeProviderTable& m_providerTable;
482 };
483 
484 static const CanonizeProvider*
485 getCanonizeProvider(const std::string& scheme)
486 {
487  static CanonizeProviderTable providerTable;
488  if (providerTable.empty()) {
489  boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
490  BOOST_ASSERT(!providerTable.empty());
491  }
492 
493  CanonizeProviderTable::const_iterator it = providerTable.find(scheme);
494  if (it == providerTable.end()) {
495  return 0;
496  }
497  return it->second.get();
498 }
499 
500 bool
501 FaceUri::canCanonize(const std::string& scheme)
502 {
503  return getCanonizeProvider(scheme) != 0;
504 }
505 
506 bool
508 {
509  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
510  if (cp == 0) {
511  return false;
512  }
513 
514  return cp->isCanonical(*this);
515 }
516 
517 static inline void
519 {
520 }
521 
522 void
524  const CanonizeFailureCallback& onFailure,
525  boost::asio::io_service& io, const time::nanoseconds& timeout) const
526 {
527  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
528  if (cp == 0) {
529  if (static_cast<bool>(onFailure)) {
530  onFailure("scheme not supported");
531  }
532  return;
533  }
534 
535  static CanonizeSuccessCallback successNop = bind(&nop);
536  static CanonizeFailureCallback failureNop = bind(&nop);
537 
538  cp->canonize(*this,
539  static_cast<bool>(onSuccess) ? onSuccess : successNop,
540  static_cast<bool>(onFailure) ? onFailure : failureNop,
541  io, timeout);
542 }
543 
544 } // namespace util
545 } // namespace ndn
virtual void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const
Definition: face-uri.cpp:433
virtual std::set< std::string > getSchemes() const
Definition: face-uri.cpp:237
virtual std::pair< bool, std::string > checkAddress(const dns::IpAddress &ipAddress) const
when overriden in a subclass, check the IP address is allowable
Definition: face-uri.cpp:398
Copyright (c) 2011-2015 Regents of the University of California.
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *> CanonizeProviders
Definition: face-uri.cpp:454
virtual bool isCanonical(const FaceUri &faceUri) const =0
virtual std::set< std::string > getSchemes() const
Definition: face-uri.cpp:411
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:501
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
static void nop()
Definition: face-uri.cpp:518
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:507
std::string toString() const
write as a string
Definition: face-uri.cpp:185
IpHostCanonizeProvider(const std::string &baseScheme, uint32_t defaultUnicastPort=6363, uint32_t defaultMulticastPort=56363)
Definition: face-uri.cpp:303
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:485
function< void(const FaceUri &)> CanonizeSuccessCallback
Definition: face-uri.hpp:160
function< bool(const IpAddress &address)> AddressSelector
Definition: dns.hpp:40
bool operator!=(const FaceUri &rhs) const
Definition: face-uri.cpp:179
const std::string & getScheme() const
get scheme (protocol)
Definition: face-uri.hpp:109
Table::const_iterator iterator
Definition: cs-internal.hpp:41
CanonizeProviderTableInitializer(CanonizeProviderTable &providerTable)
Definition: face-uri.cpp:461
virtual bool isCanonical(const FaceUri &faceUri) const
Definition: face-uri.cpp:247
friend std::ostream & operator<<(std::ostream &os, const FaceUri &uri)
Definition: face-uri.cpp:193
const std::string & getPort() const
get port
Definition: face-uri.hpp:123
virtual void canonize(const FaceUri &faceUri, const FaceUri::CanonizeSuccessCallback &onSuccess, const FaceUri::CanonizeFailureCallback &onFailure, boost::asio::io_service &io, const time::nanoseconds &timeout) const
Definition: face-uri.cpp:272
std::map< std::string, shared_ptr< CanonizeProvider > > CanonizeProviderTable
Definition: face-uri.cpp:455
virtual bool isCanonical(const FaceUri &faceUri) const
Definition: face-uri.cpp:419
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
a CanonizeProvider provides FaceUri canonization functionality for a group of schemes ...
Definition: face-uri.cpp:211
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:523
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
bool operator==(const FaceUri &rhs) const
Definition: face-uri.cpp:169
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:116
const std::string & getPath() const
get path
Definition: face-uri.hpp:130
function< void(const std::string &reason)> CanonizeFailureCallback
Definition: face-uri.hpp:161
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