face-uri.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2018 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.
10  *
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"
29 
30 #include "address-converter.hpp"
31 #include "dns.hpp"
32 #include "util/string-helper.hpp"
33 
34 #include <boost/algorithm/string.hpp>
35 #include <boost/lexical_cast.hpp>
36 #include <boost/mpl/vector.hpp>
37 #include <boost/mpl/for_each.hpp>
38 
39 #include <regex>
40 #include <set>
41 #include <sstream>
42 
43 namespace ndn {
44 
45 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
46 
48  : m_isV6(false)
49 {
50 }
51 
52 FaceUri::FaceUri(const std::string& uri)
53 {
54  if (!parse(uri)) {
55  BOOST_THROW_EXCEPTION(Error("Malformed URI: " + uri));
56  }
57 }
58 
59 FaceUri::FaceUri(const char* uri)
60  : FaceUri(std::string(uri))
61 {
62 }
63 
64 bool
65 FaceUri::parse(const std::string& uri)
66 {
67  m_scheme.clear();
68  m_host.clear();
69  m_port.clear();
70  m_path.clear();
71  m_isV6 = false;
72 
73  static const std::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
74  std::smatch protocolMatch;
75  if (!std::regex_match(uri, protocolMatch, protocolExp)) {
76  return false;
77  }
78  m_scheme = protocolMatch[1];
79  std::string authority = protocolMatch[3];
80  m_path = protocolMatch[4];
81 
82  // pattern for IPv6 link local address enclosed in [ ], with optional port number
83  static const std::regex v6LinkLocalExp("^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
84  // pattern for IPv6 address enclosed in [ ], with optional port number
85  static const std::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
86  // pattern for Ethernet address in standard hex-digits-and-colons notation
87  static const std::regex etherExp("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
88  // pattern for IPv4-mapped IPv6 address, with optional port number
89  static const std::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
90  // pattern for IPv4/hostname/fd/ifname, with optional port number
91  static const std::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
92 
93  if (authority.empty()) {
94  // UNIX, internal
95  }
96  else {
97  std::smatch match;
98  if (std::regex_match(authority, match, v6LinkLocalExp)) {
99  m_isV6 = true;
100  m_host = match[1].str() + "%" + match[2].str();
101  m_port = match[3];
102  return true;
103  }
104 
105  m_isV6 = std::regex_match(authority, match, v6Exp);
106  if (m_isV6 ||
107  std::regex_match(authority, match, etherExp) ||
108  std::regex_match(authority, match, v4MappedV6Exp) ||
109  std::regex_match(authority, match, v4HostExp)) {
110  m_host = match[1];
111  m_port = match[2];
112  }
113  else {
114  return false;
115  }
116  }
117 
118  return true;
119 }
120 
121 FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
122 {
123  m_isV6 = endpoint.address().is_v6();
124  m_scheme = m_isV6 ? "udp6" : "udp4";
125  m_host = endpoint.address().to_string();
126  m_port = to_string(endpoint.port());
127 }
128 
129 FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint)
130 {
131  m_isV6 = endpoint.address().is_v6();
132  m_scheme = m_isV6 ? "tcp6" : "tcp4";
133  m_host = endpoint.address().to_string();
134  m_port = to_string(endpoint.port());
135 }
136 
137 FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
138 {
139  m_isV6 = endpoint.address().is_v6();
140  m_scheme = scheme;
141  m_host = endpoint.address().to_string();
142  m_port = to_string(endpoint.port());
143 }
144 
145 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
146 FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
147  : m_scheme("unix")
148  , m_path(endpoint.path())
149  , m_isV6(false)
150 {
151 }
152 #endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
153 
154 FaceUri
156 {
157  FaceUri uri;
158  uri.m_scheme = "fd";
159  uri.m_host = to_string(fd);
160  return uri;
161 }
162 
164  : m_scheme("ether")
165  , m_host(address.toString())
166  , m_isV6(true)
167 {
168 }
169 
170 FaceUri
171 FaceUri::fromDev(const std::string& ifname)
172 {
173  FaceUri uri;
174  uri.m_scheme = "dev";
175  uri.m_host = ifname;
176  return uri;
177 }
178 
179 FaceUri
180 FaceUri::fromUdpDev(const boost::asio::ip::udp::endpoint& endpoint, const std::string& ifname)
181 {
182  FaceUri uri;
183  uri.m_scheme = endpoint.address().is_v6() ? "udp6+dev" : "udp4+dev";
184  uri.m_host = ifname;
185  uri.m_port = to_string(endpoint.port());
186  return uri;
187 }
188 
189 bool
190 FaceUri::operator==(const FaceUri& rhs) const
191 {
192  return m_isV6 == rhs.m_isV6 &&
193  m_scheme == rhs.m_scheme &&
194  m_host == rhs.m_host &&
195  m_port == rhs.m_port &&
196  m_path == rhs.m_path;
197 }
198 
199 bool
200 FaceUri::operator!=(const FaceUri& rhs) const
201 {
202  return !(*this == rhs);
203 }
204 
205 std::string
207 {
208  std::ostringstream os;
209  os << *this;
210  return os.str();
211 }
212 
213 std::ostream&
214 operator<<(std::ostream& os, const FaceUri& uri)
215 {
216  os << uri.m_scheme << "://";
217  if (uri.m_isV6) {
218  os << "[" << uri.m_host << "]";
219  }
220  else {
221  os << uri.m_host;
222  }
223  if (!uri.m_port.empty()) {
224  os << ":" << uri.m_port;
225  }
226  os << uri.m_path;
227  return os;
228 }
229 
230 
233 class CanonizeProvider : noncopyable
234 {
235 public:
236  virtual
237  ~CanonizeProvider() = default;
238 
239  virtual std::set<std::string>
240  getSchemes() const = 0;
241 
242  virtual bool
243  isCanonical(const FaceUri& faceUri) const = 0;
244 
245  virtual void
246  canonize(const FaceUri& faceUri,
247  const FaceUri::CanonizeSuccessCallback& onSuccess,
248  const FaceUri::CanonizeFailureCallback& onFailure,
249  boost::asio::io_service& io, time::nanoseconds timeout) const = 0;
250 };
251 
252 template<typename Protocol>
253 class IpHostCanonizeProvider : public CanonizeProvider
254 {
255 public:
256  std::set<std::string>
257  getSchemes() const override
258  {
259  return {m_baseScheme, m_v4Scheme, m_v6Scheme};
260  }
261 
262  bool
263  isCanonical(const FaceUri& faceUri) const override
264  {
265  if (faceUri.getPort().empty()) {
266  return false;
267  }
268  if (!faceUri.getPath().empty()) {
269  return false;
270  }
271 
272  boost::system::error_code ec;
273  auto addr = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
274  if (ec) {
275  return false;
276  }
277 
278  bool hasCorrectScheme = (faceUri.getScheme() == m_v4Scheme && addr.is_v4()) ||
279  (faceUri.getScheme() == m_v6Scheme && addr.is_v6());
280  if (!hasCorrectScheme) {
281  return false;
282  }
283 
284  auto checkAddressWithUri = [] (const boost::asio::ip::address& addr,
285  const FaceUri& faceUri) -> bool {
286  if (addr.is_v4() || !addr.to_v6().is_link_local()) {
287  return addr.to_string() == faceUri.getHost();
288  }
289 
290  std::vector<std::string> addrFields, faceUriFields;
291  std::string addrString = addr.to_string();
292  std::string faceUriString = faceUri.getHost();
293 
294  boost::algorithm::split(addrFields, addrString, boost::is_any_of("%"));
295  boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of("%"));
296  if (addrFields.size() != 2 || faceUriFields.size() != 2) {
297  return false;
298  }
299 
300  if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, "25") == 0) {
301  // %25... is accepted, but not a canonical form
302  return false;
303  }
304 
305  return addrFields[0] == faceUriFields[0] &&
306  addrFields[1] == faceUriFields[1];
307  };
308 
309  return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
310  }
311 
312  void
313  canonize(const FaceUri& faceUri,
314  const FaceUri::CanonizeSuccessCallback& onSuccess,
315  const FaceUri::CanonizeFailureCallback& onFailure,
316  boost::asio::io_service& io, time::nanoseconds timeout) const override
317  {
318  if (this->isCanonical(faceUri)) {
319  onSuccess(faceUri);
320  return;
321  }
322 
323  // make a copy because caller may modify faceUri
324  auto uri = make_shared<FaceUri>(faceUri);
325  boost::system::error_code ec;
326  auto ipAddress = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
327  if (!ec) {
328  // No need to resolve IP address if host is already an IP
329  if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
330  (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
331  return onFailure("IPv4/v6 mismatch");
332  }
333 
334  onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
335  }
336  else {
337  dns::AddressSelector addressSelector;
338  if (faceUri.getScheme() == m_v4Scheme) {
339  addressSelector = dns::Ipv4Only();
340  }
341  else if (faceUri.getScheme() == m_v6Scheme) {
342  addressSelector = dns::Ipv6Only();
343  }
344  else {
345  BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
346  addressSelector = dns::AnyAddress();
347  }
348 
349  dns::asyncResolve(unescapeHost(faceUri.getHost()),
350  bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
351  bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
352  io, addressSelector, timeout);
353  }
354  }
355 
356 protected:
357  explicit
358  IpHostCanonizeProvider(const std::string& baseScheme,
359  uint16_t defaultUnicastPort = 6363,
360  uint16_t defaultMulticastPort = 56363)
361  : m_baseScheme(baseScheme)
362  , m_v4Scheme(baseScheme + '4')
363  , m_v6Scheme(baseScheme + '6')
364  , m_defaultUnicastPort(defaultUnicastPort)
365  , m_defaultMulticastPort(defaultMulticastPort)
366  {
367  }
368 
369 private:
370  void
371  onDnsSuccess(const shared_ptr<FaceUri>& faceUri,
372  const FaceUri::CanonizeSuccessCallback& onSuccess,
373  const FaceUri::CanonizeFailureCallback& onFailure,
374  const dns::IpAddress& ipAddress) const
375  {
376  bool isOk = false;
377  std::string reason;
378  std::tie(isOk, reason) = this->checkAddress(ipAddress);
379  if (!isOk) {
380  return onFailure(reason);
381  }
382 
383  uint16_t port = 0;
384  if (faceUri->getPort().empty()) {
385  port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
386  }
387  else {
388  try {
389  port = boost::lexical_cast<uint16_t>(faceUri->getPort());
390  }
391  catch (const boost::bad_lexical_cast&) {
392  return onFailure("invalid port number '" + faceUri->getPort() + "'");
393  }
394  }
395 
396  FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
397  BOOST_ASSERT(canonicalUri.isCanonical());
398  onSuccess(canonicalUri);
399  }
400 
401  void
402  onDnsFailure(const shared_ptr<FaceUri>& faceUri,
403  const FaceUri::CanonizeFailureCallback& onFailure,
404  const std::string& reason) const
405  {
406  onFailure(reason);
407  }
408 
413  virtual std::pair<bool, std::string>
414  checkAddress(const dns::IpAddress& ipAddress) const
415  {
416  return {true, ""};
417  }
418 
419  static std::string
420  unescapeHost(std::string host)
421  {
422  auto escapePos = host.find("%25");
423  if (escapePos != std::string::npos && escapePos < host.size() - 3) {
424  host = unescape(host);
425  }
426  return host;
427  }
428 
429 private:
430  std::string m_baseScheme;
431  std::string m_v4Scheme;
432  std::string m_v6Scheme;
433  uint16_t m_defaultUnicastPort;
434  uint16_t m_defaultMulticastPort;
435 };
436 
437 class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
438 {
439 public:
440  UdpCanonizeProvider()
441  : IpHostCanonizeProvider("udp")
442  {
443  }
444 };
445 
446 class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
447 {
448 public:
449  TcpCanonizeProvider()
450  : IpHostCanonizeProvider("tcp")
451  {
452  }
453 
454 protected:
455  std::pair<bool, std::string>
456  checkAddress(const dns::IpAddress& ipAddress) const override
457  {
458  if (ipAddress.is_multicast()) {
459  return {false, "cannot use multicast address"};
460  }
461  return {true, ""};
462  }
463 };
464 
465 class EtherCanonizeProvider : public CanonizeProvider
466 {
467 public:
468  std::set<std::string>
469  getSchemes() const override
470  {
471  return {"ether"};
472  }
473 
474  bool
475  isCanonical(const FaceUri& faceUri) const override
476  {
477  if (!faceUri.getPort().empty()) {
478  return false;
479  }
480  if (!faceUri.getPath().empty()) {
481  return false;
482  }
483 
484  auto addr = ethernet::Address::fromString(faceUri.getHost());
485  return addr.toString() == faceUri.getHost();
486  }
487 
488  void
489  canonize(const FaceUri& faceUri,
490  const FaceUri::CanonizeSuccessCallback& onSuccess,
491  const FaceUri::CanonizeFailureCallback& onFailure,
492  boost::asio::io_service& io, time::nanoseconds timeout) const override
493  {
494  auto addr = ethernet::Address::fromString(faceUri.getHost());
495  if (addr.isNull()) {
496  return onFailure("invalid ethernet address '" + faceUri.getHost() + "'");
497  }
498 
499  FaceUri canonicalUri(addr);
500  BOOST_ASSERT(canonicalUri.isCanonical());
501  onSuccess(canonicalUri);
502  }
503 };
504 
505 class DevCanonizeProvider : public CanonizeProvider
506 {
507 public:
508  std::set<std::string>
509  getSchemes() const override
510  {
511  return {"dev"};
512  }
513 
514  bool
515  isCanonical(const FaceUri& faceUri) const override
516  {
517  return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
518  }
519 
520  void
521  canonize(const FaceUri& faceUri,
522  const FaceUri::CanonizeSuccessCallback& onSuccess,
523  const FaceUri::CanonizeFailureCallback& onFailure,
524  boost::asio::io_service& io, time::nanoseconds timeout) const override
525  {
526  if (faceUri.getHost().empty()) {
527  onFailure("network interface name is missing");
528  return;
529  }
530  if (!faceUri.getPort().empty()) {
531  onFailure("port number is not allowed");
532  return;
533  }
534  if (!faceUri.getPath().empty() && faceUri.getPath() != "/") { // permit trailing slash only
535  onFailure("path is not allowed");
536  return;
537  }
538 
539  FaceUri canonicalUri = FaceUri::fromDev(faceUri.getHost());
540  BOOST_ASSERT(canonicalUri.isCanonical());
541  onSuccess(canonicalUri);
542  }
543 };
544 
545 class UdpDevCanonizeProvider : public CanonizeProvider
546 {
547 public:
548  std::set<std::string>
549  getSchemes() const override
550  {
551  return {"udp4+dev", "udp6+dev"};
552  }
553 
554  bool
555  isCanonical(const FaceUri& faceUri) const override
556  {
557  if (faceUri.getPort().empty()) {
558  return false;
559  }
560  if (!faceUri.getPath().empty()) {
561  return false;
562  }
563  return true;
564  }
565 
566  void
567  canonize(const FaceUri& faceUri,
568  const FaceUri::CanonizeSuccessCallback& onSuccess,
569  const FaceUri::CanonizeFailureCallback& onFailure,
570  boost::asio::io_service& io, time::nanoseconds timeout) const override
571  {
572  if (this->isCanonical(faceUri)) {
573  onSuccess(faceUri);
574  }
575  else {
576  onFailure("cannot canonize " + faceUri.toString());
577  }
578  }
579 };
580 
581 using CanonizeProviders = boost::mpl::vector<UdpCanonizeProvider*,
582  TcpCanonizeProvider*,
583  EtherCanonizeProvider*,
584  DevCanonizeProvider*,
585  UdpDevCanonizeProvider*>;
586 using CanonizeProviderTable = std::map<std::string, shared_ptr<CanonizeProvider>>;
587 
588 class CanonizeProviderTableInitializer
589 {
590 public:
591  explicit
592  CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
593  : m_providerTable(providerTable)
594  {
595  }
596 
597  template<typename CP>
598  void
599  operator()(CP*)
600  {
601  shared_ptr<CanonizeProvider> cp = make_shared<CP>();
602  auto schemes = cp->getSchemes();
603  BOOST_ASSERT(!schemes.empty());
604 
605  for (const auto& scheme : schemes) {
606  BOOST_ASSERT(m_providerTable.count(scheme) == 0);
607  m_providerTable[scheme] = cp;
608  }
609  }
610 
611 private:
612  CanonizeProviderTable& m_providerTable;
613 };
614 
615 static const CanonizeProvider*
616 getCanonizeProvider(const std::string& scheme)
617 {
618  static CanonizeProviderTable providerTable;
619  if (providerTable.empty()) {
620  boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
621  BOOST_ASSERT(!providerTable.empty());
622  }
623 
624  auto it = providerTable.find(scheme);
625  return it == providerTable.end() ? nullptr : it->second.get();
626 }
627 
628 
629 bool
630 FaceUri::canCanonize(const std::string& scheme)
631 {
632  return getCanonizeProvider(scheme) != nullptr;
633 }
634 
635 bool
637 {
638  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
639  if (cp == nullptr) {
640  return false;
641  }
642 
643  return cp->isCanonical(*this);
644 }
645 
646 void
648  const CanonizeFailureCallback& onFailure,
649  boost::asio::io_service& io, time::nanoseconds timeout) const
650 {
651  const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
652  if (cp == nullptr) {
653  if (onFailure) {
654  onFailure("scheme not supported");
655  }
656  return;
657  }
658 
659  cp->canonize(*this,
660  onSuccess ? onSuccess : [] (auto&&) {},
661  onFailure ? onFailure : [] (auto&&) {},
662  io, timeout);
663 }
664 
665 } // namespace ndn
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:92
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:65
function< void(const std::string &reason)> CanonizeFailureCallback
Definition: face-uri.hpp:165
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
Definition: face-uri.cpp:155
function< void(const FaceUri &)> CanonizeSuccessCallback
Definition: face-uri.hpp:164
static bool canCanonize(const std::string &scheme)
Definition: face-uri.cpp:630
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
Definition: face-uri.cpp:616
STL namespace.
function< bool(const IpAddress &address)> AddressSelector
Definition: dns.hpp:34
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:180
bool operator!=(const FaceUri &rhs) const
Definition: face-uri.cpp:200
void canonize(const CanonizeSuccessCallback &onSuccess, const CanonizeFailureCallback &onFailure, boost::asio::io_service &io, time::nanoseconds timeout) const
asynchronously convert this FaceUri to canonical form
Definition: face-uri.cpp:647
bool parse(const std::string &uri)
exception-safe parsing
Definition: face-uri.cpp:65
std::string toString() const
write as a string
Definition: face-uri.cpp:206
std::string unescape(const std::string &str)
Decode a percent-encoded string.
std::string toString(char sep= ':') const
Converts the address to a human-readable string.
Definition: ethernet.cpp:78
represents the underlying protocol and address used by a Face
Definition: face-uri.hpp:44
std::map< std::string, shared_ptr< CanonizeProvider >> CanonizeProviderTable
Definition: face-uri.cpp:586
represents an Ethernet hardware address
Definition: ethernet.hpp:52
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:636
bool operator==(const FaceUri &rhs) const
Definition: face-uri.cpp:190
const std::string & getScheme() const
get scheme (protocol)
Definition: face-uri.hpp:113
boost::asio::ip::address IpAddress
Definition: dns.hpp:33
std::string to_string(const V &v)
Definition: backports.hpp:65
const std::string & getHost() const
get host (domain)
Definition: face-uri.hpp:120
const std::string & getPort() const
get port
Definition: face-uri.hpp:127
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, DevCanonizeProvider *, UdpDevCanonizeProvider * > CanonizeProviders
Definition: face-uri.cpp:585
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
static FaceUri fromDev(const std::string &ifname)
create dev FaceUri from network device name
Definition: face-uri.cpp:171
const std::string & getPath() const
get path
Definition: face-uri.hpp:134
friend std::ostream & operator<<(std::ostream &os, const FaceUri &uri)
Definition: face-uri.cpp:214