32 #include <boost/algorithm/string.hpp>    33 #include <boost/lexical_cast.hpp>    34 #include <boost/mpl/vector.hpp>    35 #include <boost/mpl/for_each.hpp>    43 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
    71   static const std::regex protocolExp(
"(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
    72   std::smatch protocolMatch;
    73   if (!std::regex_match(uri, protocolMatch, protocolExp)) {
    76   m_scheme = protocolMatch[1];
    77   std::string authority = protocolMatch[3];
    78   m_path = protocolMatch[4];
    81   static const std::regex v6LinkLocalExp(
"^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
    83   static const std::regex v6Exp(
"^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
    85   static const std::regex etherExp(
"^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
    87   static const std::regex v4MappedV6Exp(
"^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
    89   static const std::regex v4HostExp(
"^([^:]+)(?:\\:(\\d+))?$");
    91   if (authority.empty()) {
    96     if (std::regex_match(authority, match, v6LinkLocalExp)) {
    98       m_host = match[1].str() + 
"%" + match[2].str();
   103     m_isV6 = std::regex_match(authority, match, v6Exp);
   105         std::regex_match(authority, match, etherExp) ||
   106         std::regex_match(authority, match, v4MappedV6Exp) ||
   107         std::regex_match(authority, match, v4HostExp)) {
   121   m_isV6 = endpoint.address().is_v6();
   122   m_scheme = m_isV6 ? 
"udp6" : 
"udp4";
   123   m_host = endpoint.address().to_string();
   129   m_isV6 = endpoint.address().is_v6();
   130   m_scheme = m_isV6 ? 
"tcp6" : 
"tcp4";
   131   m_host = endpoint.address().to_string();
   135 FaceUri::FaceUri(
const boost::asio::ip::tcp::endpoint& endpoint, 
const std::string& scheme)
   137   m_isV6 = endpoint.address().is_v6();
   139   m_host = endpoint.address().to_string();
   143 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS   144 FaceUri::FaceUri(
const boost::asio::local::stream_protocol::endpoint& endpoint)
   146   , m_path(endpoint.path())
   150 #endif // BOOST_ASIO_HAS_LOCAL_SOCKETS   172   uri.m_scheme = 
"dev";
   181   uri.m_scheme = endpoint.address().is_v6() ? 
"udp6+dev" : 
"udp4+dev";
   190   std::ostringstream os;
   198   os << uri.m_scheme << 
"://";
   200     os << 
"[" << uri.m_host << 
"]";
   205   if (!uri.m_port.empty()) {
   206     os << 
":" << uri.m_port;
   215 class CanonizeProvider : noncopyable
   219   ~CanonizeProvider() = 
default;
   221   virtual std::set<std::string>
   222   getSchemes() 
const = 0;
   231            boost::asio::io_service& io, time::nanoseconds timeout) 
const = 0;
   234 template<
typename Protocol>
   235 class IpHostCanonizeProvider : 
public CanonizeProvider
   238   std::set<std::string>
   239   getSchemes()
 const override   241     return {m_baseScheme, m_v4Scheme, m_v6Scheme};
   247     if (faceUri.
getPort().empty()) {
   250     if (!faceUri.
getPath().empty()) {
   254     boost::system::error_code ec;
   255     auto addr = boost::asio::ip::address::from_string(unescapeHost(faceUri.
getHost()), ec);
   260     bool hasCorrectScheme = (faceUri.
getScheme() == m_v4Scheme && addr.is_v4()) ||
   261                             (faceUri.
getScheme() == m_v6Scheme && addr.is_v6());
   262     if (!hasCorrectScheme) {
   266     auto checkAddressWithUri = [] (
const boost::asio::ip::address& addr,
   267                                    const FaceUri& faceUri) -> 
bool {
   268       if (addr.is_v4() || !addr.to_v6().is_link_local()) {
   269         return addr.to_string() == faceUri.
getHost();
   272       std::vector<std::string> addrFields, faceUriFields;
   273       std::string addrString = addr.to_string();
   274       std::string faceUriString = faceUri.
getHost();
   276       boost::algorithm::split(addrFields, addrString, boost::is_any_of(
"%"));
   277       boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of(
"%"));
   278       if (addrFields.size() != 2 || faceUriFields.size() != 2) {
   282       if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, 
"25") == 0) {
   287       return addrFields[0] == faceUriFields[0] &&
   288              addrFields[1] == faceUriFields[1];
   291     return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
   298            boost::asio::io_service& io, time::nanoseconds timeout)
 const override   306     auto uri = make_shared<FaceUri>(faceUri);
   307     boost::system::error_code ec;
   308     auto ipAddress = boost::asio::ip::address::from_string(unescapeHost(faceUri.
getHost()), ec);
   311       if ((faceUri.
getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
   312           (faceUri.
getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
   313         return onFailure(
"IPv4/v6 mismatch");
   316       onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
   323       else if (faceUri.
getScheme() == m_v6Scheme) {
   327         BOOST_ASSERT(faceUri.
getScheme() == m_baseScheme);
   332         bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, 
this, uri, onSuccess, onFailure, _1),
   333         bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, 
this, uri, onFailure, _1),
   334         io, addressSelector, timeout);
   340   IpHostCanonizeProvider(
const std::string& baseScheme,
   341                          uint16_t defaultUnicastPort = 6363,
   342                          uint16_t defaultMulticastPort = 56363)
   343     : m_baseScheme(baseScheme)
   344     , m_v4Scheme(baseScheme + 
'4')
   345     , m_v6Scheme(baseScheme + 
'6')
   346     , m_defaultUnicastPort(defaultUnicastPort)
   347     , m_defaultMulticastPort(defaultMulticastPort)
   353   onDnsSuccess(
const shared_ptr<FaceUri>& faceUri,
   360     std::tie(isOk, reason) = this->checkAddress(ipAddress);
   362       return onFailure(reason);
   366     if (faceUri->getPort().empty()) {
   367       port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
   371         port = boost::lexical_cast<uint16_t>(faceUri->getPort());
   373       catch (
const boost::bad_lexical_cast&) {
   374         return onFailure(
"invalid port number '" + faceUri->getPort() + 
"'");
   378     FaceUri canonicalUri(
typename Protocol::endpoint(ipAddress, port));
   379     BOOST_ASSERT(canonicalUri.isCanonical());
   380     onSuccess(canonicalUri);
   384   onDnsFailure(
const shared_ptr<FaceUri>& faceUri,
   386                const std::string& reason)
 const   395   virtual std::pair<bool, std::string>
   402   unescapeHost(std::string host)
   404     auto escapePos = host.find(
"%25");
   405     if (escapePos != std::string::npos && escapePos < host.size() - 3) {
   412   std::string m_baseScheme;
   413   std::string m_v4Scheme;
   414   std::string m_v6Scheme;
   415   uint16_t m_defaultUnicastPort;
   416   uint16_t m_defaultMulticastPort;
   419 class UdpCanonizeProvider : 
public IpHostCanonizeProvider<boost::asio::ip::udp>
   422   UdpCanonizeProvider()
   423     : IpHostCanonizeProvider(
"udp")
   428 class TcpCanonizeProvider : 
public IpHostCanonizeProvider<boost::asio::ip::tcp>
   431   TcpCanonizeProvider()
   432     : IpHostCanonizeProvider(
"tcp")
   437   std::pair<bool, std::string>
   440     if (ipAddress.is_multicast()) {
   441       return {
false, 
"cannot use multicast address"};
   447 class EtherCanonizeProvider : 
public CanonizeProvider
   450   std::set<std::string>
   451   getSchemes()
 const override   459     if (!faceUri.
getPort().empty()) {
   462     if (!faceUri.
getPath().empty()) {
   474            boost::asio::io_service& io, time::nanoseconds timeout)
 const override   478       return onFailure(
"invalid ethernet address '" + faceUri.
getHost() + 
"'");
   482     BOOST_ASSERT(canonicalUri.isCanonical());
   483     onSuccess(canonicalUri);
   487 class DevCanonizeProvider : 
public CanonizeProvider
   490   std::set<std::string>
   491   getSchemes()
 const override   506            boost::asio::io_service& io, time::nanoseconds timeout)
 const override   508     if (faceUri.
getHost().empty()) {
   509       onFailure(
"network interface name is missing");
   512     if (!faceUri.
getPort().empty()) {
   513       onFailure(
"port number is not allowed");
   517       onFailure(
"path is not allowed");
   523     onSuccess(canonicalUri);
   527 class UdpDevCanonizeProvider : 
public CanonizeProvider
   530   std::set<std::string>
   531   getSchemes()
 const override   533     return {
"udp4+dev", 
"udp6+dev"};
   539     if (faceUri.
getPort().empty()) {
   542     if (!faceUri.
getPath().empty()) {
   552            boost::asio::io_service& io, time::nanoseconds timeout)
 const override   558       onFailure(
"cannot canonize " + faceUri.
toString());
   564                                              TcpCanonizeProvider*,
   565                                              EtherCanonizeProvider*,
   566                                              DevCanonizeProvider*,
   567                                              UdpDevCanonizeProvider*>;
   570 class CanonizeProviderTableInitializer
   575     : m_providerTable(providerTable)
   579   template<
typename CP>
   583     shared_ptr<CanonizeProvider> cp = make_shared<CP>();
   584     auto schemes = cp->getSchemes();
   585     BOOST_ASSERT(!schemes.empty());
   587     for (
const auto& scheme : schemes) {
   588       BOOST_ASSERT(m_providerTable.count(scheme) == 0);
   589       m_providerTable[scheme] = cp;
   597 static const CanonizeProvider*
   601   if (providerTable.empty()) {
   602     boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
   603     BOOST_ASSERT(!providerTable.empty());
   606   auto it = providerTable.find(scheme);
   607   return it == providerTable.end() ? nullptr : it->second.get();
   625   return cp->isCanonical(*
this);
   631                   boost::asio::io_service& io, time::nanoseconds timeout)
 const   636       onFailure(
"scheme not supported");
   642                onSuccess ? onSuccess : [] (
auto&&) {},
   643                onFailure ? onFailure : [] (
auto&&) {},
 
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. 
function< void(const std::string &reason)> CanonizeFailureCallback
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor 
const std::string & getHost() const
get host (domain) 
std::string to_string(const T &val)
function< void(const FaceUri &)> CanonizeSuccessCallback
static bool canCanonize(const std::string &scheme)
std::string toString() const
write as a string 
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
const std::string & getPort() const
get port 
std::string toString(char sep=':') const
Converts the address to a human-readable string. 
function< bool(const IpAddress &address)> AddressSelector
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 
bool parse(const std::string &uri)
exception-safe parsing 
std::string unescape(const std::string &str)
Decode a percent-encoded string. 
void canonize(const CanonizeSuccessCallback &onSuccess, const CanonizeFailureCallback &onFailure, boost::asio::io_service &io, time::nanoseconds timeout) const
asynchronously convert this FaceUri to canonical form 
represents the underlying protocol and address used by a Face 
represents an Ethernet hardware address 
const std::string & getScheme() const
get scheme (protocol) 
boost::asio::ip::address IpAddress
bool isCanonical() const
determine whether this FaceUri is in canonical form 
std::map< std::string, shared_ptr< CanonizeProvider > > CanonizeProviderTable
const std::string & getPath() const
get path 
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, DevCanonizeProvider *, UdpDevCanonizeProvider * > CanonizeProviders
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. 
static FaceUri fromDev(const std::string &ifname)
create dev FaceUri from network device name 
friend std::ostream & operator<<(std::ostream &os, const FaceUri &uri)