32 #include <boost/algorithm/string/classification.hpp> 
   33 #include <boost/algorithm/string/split.hpp> 
   34 #include <boost/lexical_cast.hpp> 
   35 #include <boost/mpl/for_each.hpp> 
   36 #include <boost/mpl/vector.hpp> 
   44 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
 
   72   static const std::regex protocolExp(
"(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
 
   73   std::smatch protocolMatch;
 
   74   if (!std::regex_match(uri, protocolMatch, protocolExp)) {
 
   77   m_scheme = protocolMatch[1];
 
   78   std::string authority = protocolMatch[3];
 
   79   m_path = protocolMatch[4];
 
   82   static const std::regex v6LinkLocalExp(
"^\\[([a-fA-F0-9:]+)%([^\\s/:]+)\\](?:\\:(\\d+))?$");
 
   84   static const std::regex v6Exp(
"^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
 
   86   static const std::regex etherExp(
"^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
 
   88   static const std::regex v4MappedV6Exp(
"^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
 
   90   static const std::regex v4HostExp(
"^([^:]+)(?:\\:(\\d+))?$");
 
   92   if (authority.empty()) {
 
   97     if (std::regex_match(authority, match, v6LinkLocalExp)) {
 
   99       m_host = match[1].str() + 
"%" + match[2].str();
 
  104     m_isV6 = std::regex_match(authority, match, v6Exp);
 
  106         std::regex_match(authority, match, etherExp) ||
 
  107         std::regex_match(authority, match, v4MappedV6Exp) ||
 
  108         std::regex_match(authority, match, v4HostExp)) {
 
  122   m_isV6 = endpoint.address().is_v6();
 
  123   m_scheme = m_isV6 ? 
"udp6" : 
"udp4";
 
  124   m_host = endpoint.address().to_string();
 
  130   m_isV6 = endpoint.address().is_v6();
 
  131   m_scheme = m_isV6 ? 
"tcp6" : 
"tcp4";
 
  132   m_host = endpoint.address().to_string();
 
  136 FaceUri::FaceUri(
const boost::asio::ip::tcp::endpoint& endpoint, 
const std::string& scheme)
 
  138   m_isV6 = endpoint.address().is_v6();
 
  140   m_host = endpoint.address().to_string();
 
  144 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS 
  145 FaceUri::FaceUri(
const boost::asio::local::stream_protocol::endpoint& endpoint)
 
  147   , m_path(endpoint.path())
 
  173   uri.m_scheme = 
"dev";
 
  182   uri.m_scheme = endpoint.address().is_v6() ? 
"udp6+dev" : 
"udp4+dev";
 
  191   std::ostringstream os;
 
  199   os << uri.m_scheme << 
"://";
 
  201     os << 
"[" << uri.m_host << 
"]";
 
  206   if (!uri.m_port.empty()) {
 
  207     os << 
":" << uri.m_port;
 
  216 class CanonizeProvider : noncopyable
 
  220   ~CanonizeProvider() = 
default;
 
  222   virtual std::set<std::string>
 
  223   getSchemes() 
const = 0;
 
  226   isCanonical(
const FaceUri& faceUri) 
const = 0;
 
  229   canonize(
const FaceUri& faceUri,
 
  235 template<
typename Protocol>
 
  236 class IpHostCanonizeProvider : 
public CanonizeProvider
 
  239   std::set<std::string>
 
  240   getSchemes()
 const override 
  242     return {m_baseScheme, m_v4Scheme, m_v6Scheme};
 
  246   isCanonical(
const FaceUri& faceUri)
 const override 
  248     if (faceUri.getPort().empty()) {
 
  251     if (!faceUri.getPath().empty()) {
 
  255     boost::system::error_code ec;
 
  256     auto addr = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
 
  261     bool hasCorrectScheme = (faceUri.getScheme() == m_v4Scheme && addr.is_v4()) ||
 
  262                             (faceUri.getScheme() == m_v6Scheme && addr.is_v6());
 
  263     if (!hasCorrectScheme) {
 
  267     auto checkAddressWithUri = [] (
const boost::asio::ip::address& addr,
 
  268                                    const FaceUri& faceUri) -> 
bool {
 
  269       if (addr.is_v4() || !addr.to_v6().is_link_local()) {
 
  270         return addr.to_string() == faceUri.getHost();
 
  273       std::vector<std::string> addrFields, faceUriFields;
 
  274       std::string addrString = addr.to_string();
 
  275       std::string faceUriString = faceUri.getHost();
 
  277       boost::algorithm::split(addrFields, addrString, boost::is_any_of(
"%"));
 
  278       boost::algorithm::split(faceUriFields, faceUriString, boost::is_any_of(
"%"));
 
  279       if (addrFields.size() != 2 || faceUriFields.size() != 2) {
 
  283       if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2, 
"25") == 0) {
 
  288       return addrFields[0] == faceUriFields[0] &&
 
  289              addrFields[1] == faceUriFields[1];
 
  292     return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
 
  296   canonize(
const FaceUri& faceUri,
 
  301     if (this->isCanonical(faceUri)) {
 
  307     auto uri = make_shared<FaceUri>(faceUri);
 
  308     boost::system::error_code ec;
 
  309     auto ipAddress = boost::asio::ip::address::from_string(unescapeHost(faceUri.getHost()), ec);
 
  312       if ((faceUri.getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
 
  313           (faceUri.getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
 
  314         return onFailure(
"IPv4/v6 mismatch");
 
  317       onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
 
  321       if (faceUri.getScheme() == m_v4Scheme) {
 
  322         addressSelector = dns::Ipv4Only();
 
  324       else if (faceUri.getScheme() == m_v6Scheme) {
 
  325         addressSelector = dns::Ipv6Only();
 
  328         BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
 
  329         addressSelector = dns::AnyAddress();
 
  333         [=] (
const auto& ipAddr) { onDnsSuccess(uri, onSuccess, onFailure, ipAddr); },
 
  334         [=] (
const auto& reason) { onDnsFailure(uri, onFailure, reason); },
 
  335         io, addressSelector, timeout);
 
  341   IpHostCanonizeProvider(
const std::string& baseScheme,
 
  342                          uint16_t defaultUnicastPort = 6363,
 
  343                          uint16_t defaultMulticastPort = 56363)
 
  344     : m_baseScheme(baseScheme)
 
  345     , m_v4Scheme(baseScheme + 
'4')
 
  346     , m_v6Scheme(baseScheme + 
'6')
 
  347     , m_defaultUnicastPort(defaultUnicastPort)
 
  348     , m_defaultMulticastPort(defaultMulticastPort)
 
  354   onDnsSuccess(
const shared_ptr<FaceUri>& faceUri,
 
  361     std::tie(isOk, reason) = this->checkAddress(ipAddress);
 
  363       return onFailure(reason);
 
  367     if (faceUri->getPort().empty()) {
 
  368       port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
 
  372         port = boost::lexical_cast<uint16_t>(faceUri->getPort());
 
  374       catch (
const boost::bad_lexical_cast&) {
 
  375         return onFailure(
"invalid port number '" + faceUri->getPort() + 
"'");
 
  379     FaceUri canonicalUri(
typename Protocol::endpoint(ipAddress, port));
 
  380     BOOST_ASSERT(canonicalUri.isCanonical());
 
  381     onSuccess(canonicalUri);
 
  385   onDnsFailure(
const shared_ptr<FaceUri>&,
 
  387                const std::string& reason)
 const 
  396   virtual std::pair<bool, std::string>
 
  403   unescapeHost(std::string host)
 
  405     auto escapePos = host.find(
"%25");
 
  406     if (escapePos != std::string::npos && escapePos < host.size() - 3) {
 
  413   std::string m_baseScheme;
 
  414   std::string m_v4Scheme;
 
  415   std::string m_v6Scheme;
 
  416   uint16_t m_defaultUnicastPort;
 
  417   uint16_t m_defaultMulticastPort;
 
  420 class UdpCanonizeProvider : 
public IpHostCanonizeProvider<boost::asio::ip::udp>
 
  423   UdpCanonizeProvider()
 
  424     : IpHostCanonizeProvider(
"udp")
 
  429 class TcpCanonizeProvider : 
public IpHostCanonizeProvider<boost::asio::ip::tcp>
 
  432   TcpCanonizeProvider()
 
  433     : IpHostCanonizeProvider(
"tcp")
 
  438   std::pair<bool, std::string>
 
  441     if (ipAddress.is_multicast()) {
 
  442       return {
false, 
"cannot use multicast address"};
 
  448 class EtherCanonizeProvider : 
public CanonizeProvider
 
  451   std::set<std::string>
 
  452   getSchemes()
 const override 
  458   isCanonical(
const FaceUri& faceUri)
 const override 
  460     if (!faceUri.getPort().empty()) {
 
  463     if (!faceUri.getPath().empty()) {
 
  468     return addr.toString() == faceUri.getHost();
 
  472   canonize(
const FaceUri& faceUri,
 
  479       return onFailure(
"invalid ethernet address '" + faceUri.getHost() + 
"'");
 
  482     FaceUri canonicalUri(addr);
 
  483     BOOST_ASSERT(canonicalUri.isCanonical());
 
  484     onSuccess(canonicalUri);
 
  488 class DevCanonizeProvider : 
public CanonizeProvider
 
  491   std::set<std::string>
 
  492   getSchemes()
 const override 
  498   isCanonical(
const FaceUri& faceUri)
 const override 
  500     return !faceUri.getHost().empty() && faceUri.getPort().empty() && faceUri.getPath().empty();
 
  504   canonize(
const FaceUri& faceUri,
 
  509     if (faceUri.getHost().empty()) {
 
  510       onFailure(
"network interface name is missing");
 
  513     if (!faceUri.getPort().empty()) {
 
  514       onFailure(
"port number is not allowed");
 
  517     if (!faceUri.getPath().empty() && faceUri.getPath() != 
"/") { 
 
  518       onFailure(
"path is not allowed");
 
  523     BOOST_ASSERT(canonicalUri.isCanonical());
 
  524     onSuccess(canonicalUri);
 
  528 class UdpDevCanonizeProvider : 
public CanonizeProvider
 
  531   std::set<std::string>
 
  532   getSchemes()
 const override 
  534     return {
"udp4+dev", 
"udp6+dev"};
 
  538   isCanonical(
const FaceUri& faceUri)
 const override 
  540     if (faceUri.getPort().empty()) {
 
  543     if (!faceUri.getPath().empty()) {
 
  550   canonize(
const FaceUri& faceUri,
 
  555     if (this->isCanonical(faceUri)) {
 
  559       onFailure(
"cannot canonize " + faceUri.toString());
 
  565                                              TcpCanonizeProvider*,
 
  566                                              EtherCanonizeProvider*,
 
  567                                              DevCanonizeProvider*,
 
  568                                              UdpDevCanonizeProvider*>;
 
  571 class CanonizeProviderTableInitializer
 
  576     : m_providerTable(providerTable)
 
  580   template<
typename CP>
 
  584     shared_ptr<CanonizeProvider> cp = make_shared<CP>();
 
  585     auto schemes = cp->getSchemes();
 
  586     BOOST_ASSERT(!schemes.empty());
 
  588     for (
const auto& scheme : schemes) {
 
  589       BOOST_ASSERT(m_providerTable.count(scheme) == 0);
 
  590       m_providerTable[scheme] = cp;
 
  598 static const CanonizeProvider*
 
  599 getCanonizeProvider(
const std::string& scheme)
 
  602   if (providerTable.empty()) {
 
  603     boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
 
  604     BOOST_ASSERT(!providerTable.empty());
 
  607   auto it = providerTable.find(scheme);
 
  608   return it == providerTable.end() ? nullptr : it->second.get();
 
  615   return getCanonizeProvider(scheme) != 
nullptr;
 
  621   const CanonizeProvider* cp = getCanonizeProvider(this->
getScheme());
 
  626   return cp->isCanonical(*
this);
 
  634   const CanonizeProvider* cp = getCanonizeProvider(this->
getScheme());
 
  637       onFailure(
"scheme not supported");
 
  643                onSuccess ? onSuccess : [] (
auto&&) {},
 
  644                onFailure ? onFailure : [] (
auto&&) {},
 
represents the underlying protocol and address used by a Face
 
std::string toString() const
write as a string
 
function< void(const FaceUri &)> CanonizeSuccessCallback
 
const std::string & getScheme() const
get scheme (protocol)
 
bool parse(const std::string &uri)
exception-safe parsing
 
static bool canCanonize(const std::string &scheme)
 
bool isCanonical() const
determine whether this FaceUri is in canonical form
 
static FaceUri fromDev(const std::string &ifname)
create dev FaceUri from network device name
 
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
 
void canonize(const CanonizeSuccessCallback &onSuccess, const CanonizeFailureCallback &onFailure, boost::asio::io_service &io, time::nanoseconds timeout) const
asynchronously convert this FaceUri to canonical form
 
function< void(const std::string &reason)> CanonizeFailureCallback
 
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
 
represents an Ethernet hardware address
 
static Address fromString(const std::string &str)
Creates an Address from a string containing an Ethernet address in hexadecimal notation,...
 
boost::asio::ip::address IpAddress
 
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.
 
function< bool(const IpAddress &address)> AddressSelector
 
std::string to_string(const errinfo_stacktrace &x)
 
std::string toString(const system_clock::time_point &timePoint, const std::string &format, const std::locale &locale)
Convert time point to string with specified format.
 
boost::chrono::nanoseconds nanoseconds
 
std::string unescape(const std::string &str)
Decode a percent-encoded string.
 
std::map< std::string, shared_ptr< CanonizeProvider > > CanonizeProviderTable
 
std::ostream & operator<<(std::ostream &os, const Data &data)
 
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, DevCanonizeProvider *, UdpDevCanonizeProvider * > CanonizeProviders