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 #include <boost/regex.hpp> 54 BOOST_THROW_EXCEPTION(
Error(
"Malformed URI: " + uri));
72 static const boost::regex protocolExp(
"(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
73 boost::smatch protocolMatch;
74 if (!boost::regex_match(uri, protocolMatch, protocolExp)) {
77 m_scheme = protocolMatch[1];
78 const std::string& authority = protocolMatch[3];
79 m_path = protocolMatch[4];
82 static const boost::regex v6LinkLocalExp(
"^\\[([a-fA-F0-9:]+)%([a-zA-Z0-9]+)\\]" 85 static const boost::regex v6Exp(
"^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
87 static const boost::regex etherExp(
"^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
89 static const boost::regex v4MappedV6Exp(
"^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
91 static const boost::regex v4HostExp(
"^([^:]+)(?:\\:(\\d+))?$");
93 if (authority.empty()) {
98 if (boost::regex_match(authority, match, v6LinkLocalExp)) {
100 m_host = match[1] +
"%" + match[2];
105 m_isV6 = boost::regex_match(authority, match, v6Exp);
107 boost::regex_match(authority, match, etherExp) ||
108 boost::regex_match(authority, match, v4MappedV6Exp) ||
109 boost::regex_match(authority, match, v4HostExp)) {
123 m_isV6 = endpoint.address().is_v6();
124 m_scheme = m_isV6 ?
"udp6" :
"udp4";
125 m_host = endpoint.address().to_string();
131 m_isV6 = endpoint.address().is_v6();
132 m_scheme = m_isV6 ?
"tcp6" :
"tcp4";
133 m_host = endpoint.address().to_string();
137 FaceUri::FaceUri(
const boost::asio::ip::tcp::endpoint& endpoint,
const std::string& scheme)
139 m_isV6 = endpoint.address().is_v6();
141 m_host = endpoint.address().to_string();
145 #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS 146 FaceUri::FaceUri(
const boost::asio::local::stream_protocol::endpoint& endpoint)
148 , m_path(endpoint.path())
152 #endif // BOOST_ASIO_HAS_LOCAL_SOCKETS 174 uri.m_scheme =
"dev";
183 uri.m_scheme = endpoint.address().is_v6() ?
"udp6+dev" :
"udp4+dev";
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;
202 return !(*
this == rhs);
208 std::ostringstream os;
216 os << uri.m_scheme <<
"://";
218 os <<
"[" << uri.m_host <<
"]";
223 if (!uri.m_port.empty()) {
224 os <<
":" << uri.m_port;
233 class CanonizeProvider : noncopyable
237 ~CanonizeProvider() =
default;
239 virtual std::set<std::string>
240 getSchemes()
const = 0;
249 boost::asio::io_service& io, time::nanoseconds timeout)
const = 0;
252 template<
typename Protocol>
253 class IpHostCanonizeProvider :
public CanonizeProvider
256 std::set<std::string>
257 getSchemes()
const override 259 return {m_baseScheme, m_v4Scheme, m_v6Scheme};
265 if (faceUri.
getPort().empty()) {
268 if (!faceUri.
getPath().empty()) {
272 boost::system::error_code ec;
278 bool hasCorrectScheme = (faceUri.
getScheme() == m_v4Scheme && addr.is_v4()) ||
279 (faceUri.
getScheme() == m_v6Scheme && addr.is_v6());
280 if (!hasCorrectScheme) {
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();
290 std::vector<std::string> addrFields, faceUriFields;
291 std::string addrString = addr.to_string();
292 std::string faceUriString = faceUri.
getHost();
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) {
300 if (faceUriFields[1].size() > 2 && faceUriFields[1].compare(0, 2,
"25") == 0) {
305 return addrFields[0] == faceUriFields[0] &&
306 addrFields[1] == faceUriFields[1];
309 return checkAddressWithUri(addr, faceUri) && checkAddress(addr).first;
316 boost::asio::io_service& io, time::nanoseconds timeout)
const override 324 auto uri = make_shared<FaceUri>(faceUri);
325 boost::system::error_code ec;
329 if ((faceUri.
getScheme() == m_v4Scheme && !ipAddress.is_v4()) ||
330 (faceUri.
getScheme() == m_v6Scheme && !ipAddress.is_v6())) {
331 return onFailure(
"IPv4/v6 mismatch");
334 onDnsSuccess(uri, onSuccess, onFailure, ipAddress);
341 else if (faceUri.
getScheme() == m_v6Scheme) {
345 BOOST_ASSERT(faceUri.
getScheme() == m_baseScheme);
350 bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess,
this, uri, onSuccess, onFailure, _1),
351 bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure,
this, uri, onFailure, _1),
352 io, addressSelector, timeout);
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)
371 onDnsSuccess(
const shared_ptr<FaceUri>& faceUri,
378 std::tie(isOk, reason) = this->checkAddress(ipAddress);
380 return onFailure(reason);
384 if (faceUri->getPort().empty()) {
385 port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
389 port = boost::lexical_cast<uint16_t>(faceUri->getPort());
391 catch (
const boost::bad_lexical_cast&) {
392 return onFailure(
"invalid port number '" + faceUri->getPort() +
"'");
396 FaceUri canonicalUri(
typename Protocol::endpoint(ipAddress, port));
397 BOOST_ASSERT(canonicalUri.isCanonical());
398 onSuccess(canonicalUri);
402 onDnsFailure(
const shared_ptr<FaceUri>& faceUri,
404 const std::string& reason)
const 413 virtual std::pair<bool, std::string>
420 unescapeHost(std::string host)
422 auto escapePos = host.find(
"%25");
423 if (escapePos != std::string::npos && escapePos < host.size() - 3) {
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;
437 class UdpCanonizeProvider :
public IpHostCanonizeProvider<boost::asio::ip::udp>
440 UdpCanonizeProvider()
441 : IpHostCanonizeProvider(
"udp")
451 class TcpCanonizeProvider :
public IpHostCanonizeProvider<boost::asio::ip::tcp>
454 TcpCanonizeProvider()
455 : IpHostCanonizeProvider(
"tcp")
460 std::pair<bool, std::string>
463 if (ipAddress.is_multicast()) {
464 return {
false,
"cannot use multicast address"};
470 class EtherCanonizeProvider :
public CanonizeProvider
473 std::set<std::string>
474 getSchemes()
const override 482 if (!faceUri.
getPort().empty()) {
485 if (!faceUri.
getPath().empty()) {
497 boost::asio::io_service& io, time::nanoseconds timeout)
const override 501 return onFailure(
"invalid ethernet address '" + faceUri.
getHost() +
"'");
505 BOOST_ASSERT(canonicalUri.isCanonical());
506 onSuccess(canonicalUri);
510 class DevCanonizeProvider :
public CanonizeProvider
513 std::set<std::string>
514 getSchemes()
const override 529 boost::asio::io_service& io, time::nanoseconds timeout)
const override 531 if (faceUri.
getHost().empty()) {
532 onFailure(
"network interface name is missing");
535 if (!faceUri.
getPort().empty()) {
536 onFailure(
"port number is not allowed");
540 onFailure(
"path is not allowed");
546 onSuccess(canonicalUri);
550 class UdpDevCanonizeProvider :
public CanonizeProvider
553 std::set<std::string>
554 getSchemes()
const override 556 return {
"udp4+dev",
"udp6+dev"};
562 if (faceUri.
getPort().empty()) {
565 if (!faceUri.
getPath().empty()) {
575 boost::asio::io_service& io, time::nanoseconds timeout)
const override 581 onFailure(
"cannot canonize " + faceUri.
toString());
587 TcpCanonizeProvider*,
588 EtherCanonizeProvider*,
589 DevCanonizeProvider*,
590 UdpDevCanonizeProvider*>;
593 class CanonizeProviderTableInitializer
598 : m_providerTable(providerTable)
602 template<
typename CP>
606 shared_ptr<CanonizeProvider> cp = make_shared<CP>();
607 auto schemes = cp->getSchemes();
608 BOOST_ASSERT(!schemes.empty());
610 for (
const auto& scheme : schemes) {
611 BOOST_ASSERT(m_providerTable.count(scheme) == 0);
612 m_providerTable[scheme] = cp;
620 static const CanonizeProvider*
624 if (providerTable.empty()) {
625 boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
626 BOOST_ASSERT(!providerTable.empty());
629 auto it = providerTable.find(scheme);
630 return it == providerTable.end() ?
nullptr : it->second.get();
648 return cp->isCanonical(*
this);
654 boost::asio::io_service& io, time::nanoseconds timeout)
const 659 onFailure(
"scheme not supported");
667 onSuccess ? onSuccess : successNop,
668 onFailure ? onFailure : failureNop,
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.
Copyright (c) 2013-2017 Regents of the University of California.
function< void(const std::string &reason)> CanonizeFailureCallback
static FaceUri fromFd(int fd)
create fd FaceUri from file descriptor
BOOST_CONCEPT_ASSERT((boost::EqualityComparable< Data >))
function< void(const FaceUri &)> CanonizeSuccessCallback
static bool canCanonize(const std::string &scheme)
static const CanonizeProvider * getCanonizeProvider(const std::string &scheme)
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 operator!=(const FaceUri &rhs) const
void canonize(const CanonizeSuccessCallback &onSuccess, const CanonizeFailureCallback &onFailure, boost::asio::io_service &io, time::nanoseconds timeout) const
asynchronously convert this FaceUri to canonical form
bool parse(const std::string &uri)
exception-safe parsing
std::string toString() const
write as a string
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.
represents the underlying protocol and address used by a Face
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.
std::map< std::string, shared_ptr< CanonizeProvider >> CanonizeProviderTable
boost::asio::ip::address addressFromString(const std::string &address, boost::system::error_code &ec)
parse and convert the input string into an IP address
represents an Ethernet hardware address
bool isCanonical() const
determine whether this FaceUri is in canonical form
bool operator==(const FaceUri &rhs) const
const std::string & getScheme() const
get scheme (protocol)
boost::asio::ip::address IpAddress
std::string to_string(const V &v)
const std::string & getHost() const
get host (domain)
const std::string & getPort() const
get port
boost::mpl::vector< UdpCanonizeProvider *, TcpCanonizeProvider *, EtherCanonizeProvider *, DevCanonizeProvider *, UdpDevCanonizeProvider * > CanonizeProviders
static FaceUri fromDev(const std::string &ifname)
create dev FaceUri from network device name
const std::string & getPath() const
get path
friend std::ostream & operator<<(std::ostream &os, const FaceUri &uri)