// -*- C++ -*- // Copyright (C) 2015-2024 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . /** @file experimental/internet * This is a TS C++ Library header. * @ingroup networking-ts */ #ifndef _GLIBCXX_EXPERIMENTAL_INTERNET #define _GLIBCXX_EXPERIMENTAL_INTERNET #pragma GCC system_header #include // experimental is currently omitted #if __cplusplus >= 201402L #include #include #include #include #include #include #include #include #include #ifdef _GLIBCXX_HAVE_UNISTD_H # include #endif #ifdef _GLIBCXX_HAVE_SYS_SOCKET_H # include // AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM #endif #ifdef _GLIBCXX_HAVE_ARPA_INET_H # include // inet_ntop #endif #ifdef _GLIBCXX_HAVE_NETINET_IN_H # include // IPPROTO_IP, IPPROTO_IPV6, in_addr, in6_addr #endif #ifdef _GLIBCXX_HAVE_NETINET_TCP_H # include // TCP_NODELAY #endif #ifdef _GLIBCXX_HAVE_NETDB_H # include // getaddrinfo etc. #endif #if defined _WIN32 && __has_include() # include #endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace experimental { namespace net { inline namespace v1 { namespace ip { /** @addtogroup networking-ts * @{ */ /** Error codes for resolver errors. * @{ */ enum class resolver_errc : int { #ifdef _GLIBCXX_HAVE_NETDB_H host_not_found = EAI_NONAME, host_not_found_try_again = EAI_AGAIN, service_not_found = EAI_SERVICE // N.B. POSIX defines additional errors that have no enumerator here: // EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_SOCKTYPE, EAI_SYSTEM // Some C libraries define additional errors: // EAI_BADHINTS, EAI_OVERFLOW, EAI_PROTOCOL // Some C libraries define additional (obsolete?) errors: // EAI_ADDRFAMILY, EAI_NODATA #endif }; /// Error category for resolver errors. inline const error_category& resolver_category() noexcept // TODO non-inline { struct __cat : error_category { const char* name() const noexcept { return "resolver"; } std::string message(int __e) const { #ifdef _GLIBCXX_HAVE_NETDB_H return ::gai_strerror(__e); #else return "name resolution requires "; #endif } virtual void __message(int) { } // TODO dual ABI XXX }; static __cat __c; return __c; } inline error_code make_error_code(resolver_errc __e) noexcept { return error_code(static_cast(__e), resolver_category()); } inline error_condition make_error_condition(resolver_errc __e) noexcept { return error_condition(static_cast(__e), resolver_category()); } /// @cond undocumented inline error_code __make_resolver_error_code(int __ai_err, [[__maybe_unused__]] int __sys_err) noexcept { #ifdef EAI_SYSTEM if (__builtin_expect(__ai_err == EAI_SYSTEM, 0)) return error_code(__sys_err, std::generic_category()); #endif return error_code(__ai_err, resolver_category()); } /// @endcond /// @} using port_type = uint_least16_t; ///< Type used for port numbers. using scope_id_type = uint_least32_t; ///< Type used for IPv6 scope IDs. /// Convenience alias for constraining allocators for strings. template using __string_with = enable_if_t::value, std::basic_string, _Alloc>>; constexpr errc __unsupported_err() noexcept { #if defined EAFNOSUPPORT return std::errc::address_family_not_supported; #else return std::errc::operation_not_supported; #endif } /** Tag indicating conversion between IPv4 and IPv4-mapped IPv6 addresses. * @{ */ struct v4_mapped_t {}; constexpr v4_mapped_t v4_mapped; /// @} /// An IPv4 address. class address_v4 { public: // types: using uint_type = uint_least32_t; struct bytes_type : array { template explicit constexpr bytes_type(_Tp... __tp) : array{{static_cast(__tp)...}} { #if UCHAR_MAX > 0xFF for (auto __b : *this) if (__b > 0xFF) __throw_out_of_range("invalid address_v4::bytes_type value"); #endif } }; // constructors: constexpr address_v4() noexcept : _M_addr(0) { } constexpr address_v4(const address_v4& a) noexcept = default; constexpr address_v4(const bytes_type& __b) #if __has_builtin(__builtin_bit_cast) : _M_addr(__builtin_bit_cast(uint_type, __b)) #else : _M_addr(_S_hton_32((__b[0] << 24) | (__b[1] << 16) | (__b[2] << 8) | __b[3])) #endif { } explicit constexpr address_v4(uint_type __val) : _M_addr(_S_hton_32(__val)) { #if UINT_LEAST32_MAX > 0xFFFFFFFF if (__val > 0xFFFFFFFF) __throw_out_of_range("invalid address_v4::uint_type value"); #endif } // assignment: address_v4& operator=(const address_v4& a) noexcept = default; // members: constexpr bool is_unspecified() const noexcept { return to_uint() == 0; } constexpr bool is_loopback() const noexcept { return (to_uint() & 0xFF000000) == 0x7F000000; } constexpr bool is_multicast() const noexcept { return (to_uint() & 0xF0000000) == 0xE0000000; } constexpr bytes_type to_bytes() const noexcept { #if __has_builtin(__builtin_bit_cast) return __builtin_bit_cast(bytes_type, _M_addr); #else auto __host = to_uint(); return bytes_type{ (__host >> 24) & 0xFF, (__host >> 16) & 0xFF, (__host >> 8) & 0xFF, __host & 0xFF }; #endif } constexpr uint_type to_uint() const noexcept { return _S_ntoh_32(_M_addr); } template> __string_with<_Allocator> to_string(const _Allocator& __a = _Allocator()) const { auto __write = [__addr = to_uint()](char* __p, size_t) { auto __to_chars = [](char* __p, uint8_t __v) { unsigned __n = __v >= 100u ? 3 : __v >= 10u ? 2 : 1; std::__detail::__to_chars_10_impl(__p, __n, __v); return __p + __n; }; const auto __begin = __p; __p = __to_chars(__p, uint8_t(__addr >> 24)); for (int __i = 2; __i >= 0; __i--) { *__p++ = '.'; __p = __to_chars(__p, uint8_t(__addr >> (__i * 8))); } return __p - __begin; }; __string_with<_Allocator> __str(__a); #if __cpp_lib_string_resize_and_overwrite __str.resize_and_overwrite(15, __write); #else __str.resize(15); __str.resize(__write(&__str.front(), 15)); #endif return __str; } // static members: static constexpr address_v4 any() noexcept { return address_v4{}; } static constexpr address_v4 loopback() noexcept { return address_v4{0x7F000001}; } static constexpr address_v4 broadcast() noexcept { return address_v4{0xFFFFFFFF}; } private: template friend class basic_endpoint; friend address_v4 make_address_v4(const char*, error_code&) noexcept; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ static constexpr uint16_t _S_hton_16(uint16_t __h) { return __h; } static constexpr uint16_t _S_ntoh_16(uint16_t __n) { return __n; } static constexpr uint32_t _S_hton_32(uint32_t __h) { return __h; } static constexpr uint32_t _S_ntoh_32(uint32_t __n) { return __n; } #else static constexpr uint16_t _S_hton_16(uint16_t __h) { return __builtin_bswap16(__h); } static constexpr uint16_t _S_ntoh_16(uint16_t __n) { return __builtin_bswap16(__n); } static constexpr uint32_t _S_hton_32(uint32_t __h) { return __builtin_bswap32(__h); } static constexpr uint32_t _S_ntoh_32(uint32_t __n) { return __builtin_bswap32(__n); } #endif #ifdef _GLIBCXX_HAVE_ARPA_INET_H in_addr_t _M_addr; // network byte order #else uint32_t _M_addr; #endif }; /// An IPv6 address. class address_v6 { public: // types: struct bytes_type : array { template explicit constexpr bytes_type(_Tp... __t) : array{{static_cast(__t)...}} { } }; // constructors: constexpr address_v6() noexcept : _M_bytes(), _M_scope_id() { } constexpr address_v6(const address_v6& __a) noexcept = default; constexpr address_v6(const bytes_type& __bytes, scope_id_type __scope = 0) : _M_bytes(__bytes), _M_scope_id(__scope) { } // assignment: address_v6& operator=(const address_v6& __a) noexcept = default; // members: void scope_id(scope_id_type __id) noexcept { _M_scope_id = __id; } constexpr scope_id_type scope_id() const noexcept { return _M_scope_id; } constexpr bool is_unspecified() const noexcept { for (int __i = 0; __i < 16; ++__i) if (_M_bytes[__i] != 0x00) return false; return _M_scope_id == 0; } constexpr bool is_loopback() const noexcept { for (int __i = 0; __i < 15; ++__i) if (_M_bytes[__i] != 0x00) return false; return _M_bytes[15] == 0x01 && _M_scope_id == 0; } constexpr bool is_multicast() const noexcept { return _M_bytes[0] == 0xFF; } constexpr bool is_link_local() const noexcept { return _M_bytes[0] == 0xFE && (_M_bytes[1] & 0xC0) == 0x80; } constexpr bool is_site_local() const noexcept { return _M_bytes[0] == 0xFE && (_M_bytes[1] & 0xC0) == 0xC0; } constexpr bool is_v4_mapped() const noexcept { const bytes_type& __b = _M_bytes; return __b[0] == 0 && __b[1] == 0 && __b[ 2] == 0 && __b[ 3] == 0 && __b[4] == 0 && __b[5] == 0 && __b[ 6] == 0 && __b[ 7] == 0 && __b[8] == 0 && __b[9] == 0 && __b[10] == 0xFF && __b[11] == 0xFF; } constexpr bool is_multicast_node_local() const noexcept { return is_multicast() && (_M_bytes[1] & 0x0F) == 0x01; } constexpr bool is_multicast_link_local() const noexcept { return is_multicast() && (_M_bytes[1] & 0x0F) == 0x02; } constexpr bool is_multicast_site_local() const noexcept { return is_multicast() && (_M_bytes[1] & 0x0F) == 0x05; } constexpr bool is_multicast_org_local() const noexcept { return is_multicast() && (_M_bytes[1] & 0x0F) == 0x08; } constexpr bool is_multicast_global() const noexcept { return is_multicast() && (_M_bytes[1] & 0x0F) == 0x0b; } constexpr bytes_type to_bytes() const noexcept { return _M_bytes; } template> __string_with<_Allocator> to_string(const _Allocator& __a = _Allocator()) const { #ifdef _GLIBCXX_HAVE_ARPA_INET_H __string_with<_Allocator> __str(__a); __str.resize(INET6_ADDRSTRLEN + (_M_scope_id ? 11 : 0)); char* const __p = &__str.front(); if (inet_ntop(AF_INET6, &_M_bytes, __p, __str.size())) { auto __end = __str.find('\0'); if (unsigned long __scope = _M_scope_id) { __end += #if _GLIBCXX_USE_C99_STDIO __builtin_snprintf(__p + __end, __str.size() - __end, "%%%lu", __scope); #else __builtin_sprintf(__p + __end, "%%%lu", __scope); #endif } __str.erase(__end); } else __str.resize(0); return __str; #else std::__throw_system_error((int)__unsupported_err()); #endif } // static members: static constexpr address_v6 any() noexcept { return {}; } static constexpr address_v6 loopback() noexcept { return {bytes_type{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}}; } private: template friend class basic_endpoint; friend constexpr bool operator==(const address_v6&, const address_v6&) noexcept; friend constexpr bool operator< (const address_v6&, const address_v6&) noexcept; bytes_type _M_bytes; scope_id_type _M_scope_id; }; /// Exception type thrown on misuse of IPv4 addresses as IPv6 or vice versa. class bad_address_cast : public bad_cast { public: bad_address_cast() { } const char* what() const noexcept { return "bad address cast"; } }; /// An IPv4 or IPv6 address. class address { public: // constructors: constexpr address() noexcept : _M_v4(), _M_is_v4(true) { } #if __cpp_constexpr_dynamic_alloc constexpr #endif address(const address& __a) noexcept : _M_uninit(), _M_is_v4(__a._M_is_v4) { if (_M_is_v4) std::_Construct(std::addressof(_M_v4), __a.to_v4()); else std::_Construct(std::addressof(_M_v6), __a.to_v6()); } constexpr address(const address_v4& __a) noexcept : _M_v4(__a), _M_is_v4(true) { } constexpr address(const address_v6& __a) noexcept : _M_v6(__a), _M_is_v4(false) { } // assignment: address& operator=(const address& __a) noexcept { if (__a._M_is_v4) *this = __a.to_v4(); else *this = __a.to_v6(); return *this; } address& operator=(const address_v4& __a) noexcept { std::_Construct(std::addressof(_M_v4), __a); _M_is_v4 = true; return *this; } address& operator=(const address_v6& __a) noexcept { std::_Construct(std::addressof(_M_v6), __a); _M_is_v4 = false; return *this; } // members: constexpr bool is_v4() const noexcept { return _M_is_v4; } constexpr bool is_v6() const noexcept { return !_M_is_v4; } constexpr address_v4 to_v4() const { if (!is_v4()) _GLIBCXX_THROW_OR_ABORT(bad_address_cast()); return _M_v4; } constexpr address_v6 to_v6() const { if (!is_v6()) _GLIBCXX_THROW_OR_ABORT(bad_address_cast()); return _M_v6; } constexpr bool is_unspecified() const noexcept { return _M_is_v4 ? _M_v4.is_unspecified() : _M_v6.is_unspecified(); } constexpr bool is_loopback() const noexcept { return _M_is_v4 ? _M_v4.is_loopback() : _M_v6.is_loopback(); } constexpr bool is_multicast() const noexcept { return _M_is_v4 ? _M_v4.is_multicast() : _M_v6.is_multicast(); } template> __string_with<_Allocator> to_string(const _Allocator& __a = _Allocator()) const { if (_M_is_v4) return to_v4().to_string(__a); return to_v6().to_string(__a); } private: template friend class basic_endpoint; friend constexpr bool operator==(const address&, const address&) noexcept; friend constexpr bool operator<(const address&, const address&) noexcept; union { address_v4 _M_v4; address_v6 _M_v6; bool _M_uninit; }; bool _M_is_v4; }; /** ip::address_v4 comparisons * @{ */ constexpr bool operator==(const address_v4& __a, const address_v4& __b) noexcept { return __a.to_uint() == __b.to_uint(); } constexpr bool operator!=(const address_v4& __a, const address_v4& __b) noexcept { return !(__a == __b); } constexpr bool operator< (const address_v4& __a, const address_v4& __b) noexcept { return __a.to_uint() < __b.to_uint(); } constexpr bool operator> (const address_v4& __a, const address_v4& __b) noexcept { return __b < __a; } constexpr bool operator<=(const address_v4& __a, const address_v4& __b) noexcept { return !(__b < __a); } constexpr bool operator>=(const address_v4& __a, const address_v4& __b) noexcept { return !(__a < __b); } /// @} /** ip::address_v6 comparisons * @{ */ constexpr bool operator==(const address_v6& __a, const address_v6& __b) noexcept { const auto& __aa = __a._M_bytes; const auto& __bb = __b._M_bytes; int __i = 0; for (; __i < 16 && __aa[__i] == __bb[__i]; ++__i) ; return __i == 16 ? __a.scope_id() == __b.scope_id() : false; } constexpr bool operator!=(const address_v6& __a, const address_v6& __b) noexcept { return !(__a == __b); } constexpr bool operator< (const address_v6& __a, const address_v6& __b) noexcept { const auto& __aa = __a._M_bytes; const auto& __bb = __b._M_bytes; int __i = 0; for (; __i < 16 && __aa[__i] == __bb[__i]; ++__i) ; return __i == 16 ? __a.scope_id() < __b.scope_id() : __aa[__i] < __bb[__i]; } constexpr bool operator> (const address_v6& __a, const address_v6& __b) noexcept { return __b < __a; } constexpr bool operator<=(const address_v6& __a, const address_v6& __b) noexcept { return !(__b < __a); } constexpr bool operator>=(const address_v6& __a, const address_v6& __b) noexcept { return !(__a < __b); } /// @} /** ip::address comparisons * @{ */ constexpr bool operator==(const address& __a, const address& __b) noexcept { if (__a.is_v4()) return __b.is_v4() ? __a._M_v4 == __b._M_v4 : false; return __b.is_v4() ? false : __a._M_v6 == __b._M_v6; } constexpr bool operator!=(const address& __a, const address& __b) noexcept { return !(__a == __b); } constexpr bool operator< (const address& __a, const address& __b) noexcept { if (__a.is_v4()) return __b.is_v4() ? __a._M_v4 < __b._M_v4 : true; return __b.is_v4() ? false : __a._M_v6 < __b._M_v6; } constexpr bool operator> (const address& __a, const address& __b) noexcept { return __b < __a; } constexpr bool operator<=(const address& __a, const address& __b) noexcept { return !(__b < __a); } constexpr bool operator>=(const address& __a, const address& __b) noexcept { return !(__a < __b); } /// @} /** ip::address_v4 creation * @{ */ constexpr address_v4 make_address_v4(const address_v4::bytes_type& __b) { return address_v4{__b}; } constexpr address_v4 make_address_v4(address_v4::uint_type __val) { return address_v4{__val}; } constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& __a) { if (!__a.is_v4_mapped()) _GLIBCXX_THROW_OR_ABORT(bad_address_cast()); const auto __v6b = __a.to_bytes(); return address_v4::bytes_type(__v6b[12], __v6b[13], __v6b[14], __v6b[15]); } inline address_v4 make_address_v4(const char* __str, error_code& __ec) noexcept { #ifdef _GLIBCXX_HAVE_ARPA_INET_H address_v4 __a; const int __res = ::inet_pton(AF_INET, __str, &__a._M_addr); if (__res == 1) { __ec.clear(); return __a; } if (__res == 0) __ec = std::make_error_code(std::errc::invalid_argument); else __ec.assign(errno, generic_category()); #else __ec = std::make_error_code(__unsupported_err()); #endif return {}; } inline address_v4 make_address_v4(const char* __str) { return make_address_v4(__str, __throw_on_error{"make_address_v4"}); } inline address_v4 make_address_v4(const string& __str, error_code& __ec) noexcept { return make_address_v4(__str.c_str(), __ec); } inline address_v4 make_address_v4(const string& __str) { return make_address_v4(__str.c_str()); } inline address_v4 make_address_v4(string_view __str, error_code& __ec) noexcept { char __buf[16]; // INET_ADDRSTRLEN isn't defined on Windows auto __len = __str.copy(__buf, sizeof(__buf)); if (__len == sizeof(__buf)) { __ec = std::make_error_code(std::errc::invalid_argument); return {}; } __ec.clear(); __buf[__len] = '\0'; return make_address_v4(__buf, __ec); } inline address_v4 make_address_v4(string_view __str) { return make_address_v4(__str, __throw_on_error{"make_address_v4"}); } /// @} /** ip::address_v6 creation * @{ */ constexpr address_v6 make_address_v6(const address_v6::bytes_type& __b, scope_id_type __scope = 0) { return address_v6{__b, __scope}; } constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4& __a) noexcept { const address_v4::bytes_type __v4b = __a.to_bytes(); address_v6::bytes_type __v6b(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, __v4b[0], __v4b[1], __v4b[2], __v4b[3]); return address_v6(__v6b); } inline address_v6 __make_address_v6(const char* __addr, const char* __scope, error_code& __ec) { #ifdef _GLIBCXX_HAVE_ARPA_INET_H address_v6::bytes_type __b; const int __res = ::inet_pton(AF_INET6, __addr, __b.data()); if (__res == 1) { __ec.clear(); if (!__scope) { return { __b }; } char* __eptr; unsigned long __val = std::strtoul(__scope, &__eptr, 10); if (__eptr != __scope && !*__eptr && __val <= numeric_limits::max()) { return { __b, static_cast(__val) }; } __ec = std::make_error_code(std::errc::invalid_argument); } else if (__res == 0) __ec = std::make_error_code(std::errc::invalid_argument); else __ec.assign(errno, generic_category()); #else __ec = std::make_error_code(__unsupported_err()); #endif return {}; } inline address_v6 make_address_v6(const char* __str, error_code& __ec) noexcept { auto __p = __builtin_strchr(__str, '%'); if (__p == nullptr) return __make_address_v6(__str, nullptr, __ec); char __buf[64]; char* __out = __buf; bool __skip_leading_zero = true; while (__str < __p && __out < std::end(__buf)) { if (!__skip_leading_zero || *__str != '0') { if (*__str == ':' || *__str == '.') __skip_leading_zero = true; else __skip_leading_zero = false; *__out = *__str; } __str++; } if (__out == std::end(__buf)) { __ec = std::make_error_code(std::errc::invalid_argument); return {}; } else { *__out = '\0'; return __make_address_v6(__buf, __p + 1, __ec); } } inline address_v6 make_address_v6(const char* __str) { return make_address_v6(__str, __throw_on_error{"make_address_v6"}); } inline address_v6 make_address_v6(const string& __str, error_code& __ec) noexcept { auto __pos = __str.find('%'); if (__pos == string::npos) return __make_address_v6(__str.c_str(), nullptr, __ec); char __buf[64]; char* __out = __buf; bool __skip_leading_zero = true; size_t __n = 0; while (__n < __pos && __out < std::end(__buf)) { if (!__skip_leading_zero || __str[__n] != '0') { if (__str[__n] == ':' || __str[__n] == '.') __skip_leading_zero = true; else __skip_leading_zero = false; *__out = __str[__n]; } __n++; } if (__out == std::end(__buf)) { __ec = std::make_error_code(std::errc::invalid_argument); return {}; } else { *__out = '\0'; return __make_address_v6(__buf, __str.c_str() + __pos + 1, __ec); } } inline address_v6 make_address_v6(const string& __str) { return make_address_v6(__str, __throw_on_error{"make_address_v6"}); } inline address_v6 make_address_v6(string_view __str, error_code& __ec) noexcept { char __buf[64]; char* __out = __buf; char* __scope = nullptr; bool __skip_leading_zero = true; size_t __n = 0; while (__n < __str.length() && __out < std::end(__buf)) { if (__str[__n] == '%') { if (__scope) __out = std::end(__buf); else { *__out = '\0'; __scope = ++__out; __skip_leading_zero = true; } } else if (!__skip_leading_zero || __str[__n] != '0') { if (__str[__n] == ':' || __str[__n] == '.') __skip_leading_zero = true; else __skip_leading_zero = false; *__out = __str[__n]; __out++; } __n++; } if (__out == std::end(__buf)) { __ec = std::make_error_code(std::errc::invalid_argument); return {}; } else { *__out = '\0'; return __make_address_v6(__buf, __scope, __ec); } } inline address_v6 make_address_v6(string_view __str) { return make_address_v6(__str, __throw_on_error{"make_address_v6"}); } /// @} /** ip::address creation * @{ */ inline address make_address(const char* __str, error_code& __ec) noexcept { address __a; address_v6 __v6a = make_address_v6(__str, __ec); if (!__ec) __a = __v6a; else { address_v4 __v4a = make_address_v4(__str, __ec); if (!__ec) __a = __v4a; } return __a; } inline address make_address(const char* __str) { return make_address(__str, __throw_on_error{"make_address"}); } inline address make_address(const string& __str, error_code& __ec) noexcept { return make_address(__str.c_str(), __ec); } inline address make_address(const string& __str) { return make_address(__str, __throw_on_error{"make_address"}); } inline address make_address(string_view __str, error_code& __ec) noexcept { if (__str.rfind('\0') != string_view::npos) return make_address(__str.data(), __ec); return make_address(__str.to_string(), __ec); // TODO don't allocate } inline address make_address(string_view __str) { return make_address(__str, __throw_on_error{"make_address"}); } /// @} /// ip::address I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const address& __a) { return __os << __a.to_string(); } /// ip::address_v4 I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const address_v4& __a) { return __os << __a.to_string(); } /// ip::address_v6 I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const address_v6& __a) { return __os << __a.to_string(); } template class basic_address_iterator; // not defined template<> class basic_address_iterator { public: // types: using value_type = address_v4; using difference_type = ptrdiff_t; using pointer = const address_v4*; using reference = const address_v4&; using iterator_category = input_iterator_tag; // constructors: basic_address_iterator(const address_v4& __a) noexcept : _M_address(__a) { } // members: reference operator*() const noexcept { return _M_address; } pointer operator->() const noexcept { return &_M_address; } basic_address_iterator& operator++() noexcept { _M_address = value_type(_M_address.to_uint() + 1); return *this; } basic_address_iterator operator++(int) noexcept { auto __tmp = *this; ++*this; return __tmp; } basic_address_iterator& operator--() noexcept { _M_address = value_type(_M_address.to_uint() - 1); return *this; } basic_address_iterator operator--(int) noexcept { auto __tmp = *this; --*this; return __tmp; } bool operator==(const basic_address_iterator& __rhs) const noexcept { return _M_address == __rhs._M_address; } bool operator!=(const basic_address_iterator& __rhs) const noexcept { return _M_address != __rhs._M_address; } private: address_v4 _M_address; }; using address_v4_iterator = basic_address_iterator; template<> class basic_address_iterator { public: // types: using value_type = address_v6; using difference_type = ptrdiff_t; using pointer = const address_v6*; using reference = const address_v6&; using iterator_category = input_iterator_tag; // constructors: basic_address_iterator(const address_v6& __a) noexcept : _M_address(__a) { } // members: reference operator*() const noexcept { return _M_address; } pointer operator->() const noexcept { return &_M_address; } basic_address_iterator& operator++() noexcept; // TODO basic_address_iterator operator++(int) noexcept { auto __tmp = *this; ++*this; return __tmp; } basic_address_iterator& operator--() noexcept; // TODO basic_address_iterator operator--(int) noexcept { auto __tmp = *this; --*this; return __tmp; } bool operator==(const basic_address_iterator& __rhs) const noexcept { return _M_address == __rhs._M_address; } bool operator!=(const basic_address_iterator& __rhs) const noexcept { return _M_address != __rhs._M_address; } private: address_v6 _M_address; }; using address_v6_iterator = basic_address_iterator; template class basic_address_range; // not defined /** An IPv6 address range. * @{ */ template<> class basic_address_range { public: // types: using iterator = basic_address_iterator; // constructors: basic_address_range() noexcept : _M_begin({}), _M_end({}) { } basic_address_range(const address_v4& __first, const address_v4& __last) noexcept : _M_begin(__first), _M_end(__last) { } // members: iterator begin() const noexcept { return _M_begin; } iterator end() const noexcept { return _M_end; } _GLIBCXX_NODISCARD bool empty() const noexcept { return _M_begin == _M_end; } size_t size() const noexcept { return _M_end->to_uint() - _M_begin->to_uint(); } iterator find(const address_v4& __addr) const noexcept { if (*_M_begin <= __addr && __addr < *_M_end) return iterator{__addr}; return end(); } private: iterator _M_begin; iterator _M_end; }; using address_v4_range = basic_address_range; /// @} /** An IPv6 address range. * @{ */ template<> class basic_address_range { public: // types: using iterator = basic_address_iterator; // constructors: basic_address_range() noexcept : _M_begin({}), _M_end({}) { } basic_address_range(const address_v6& __first, const address_v6& __last) noexcept : _M_begin(__first), _M_end(__last) { } // members: iterator begin() const noexcept { return _M_begin; } iterator end() const noexcept { return _M_end; } _GLIBCXX_NODISCARD bool empty() const noexcept { return _M_begin == _M_end; } iterator find(const address_v6& __addr) const noexcept { if (*_M_begin <= __addr && __addr < *_M_end) return iterator{__addr}; return end(); } private: iterator _M_begin; iterator _M_end; }; using address_v6_range = basic_address_range; /// @} constexpr bool operator==(const network_v4& __a, const network_v4& __b) noexcept; constexpr bool operator==(const network_v6& __a, const network_v6& __b) noexcept; /// An IPv4 network address. class network_v4 { public: // constructors: constexpr network_v4() noexcept : _M_addr(), _M_prefix_len(0) { } constexpr network_v4(const address_v4& __addr, int __prefix_len) : _M_addr(__addr), _M_prefix_len(__prefix_len) { if (_M_prefix_len < 0 || _M_prefix_len > 32) __throw_out_of_range("network_v4: invalid prefix length"); } constexpr network_v4(const address_v4& __addr, const address_v4& __mask) : _M_addr(__addr), _M_prefix_len(__builtin_popcount(__mask.to_uint())) { if (_M_prefix_len != 0) { address_v4::uint_type __mask_uint = __mask.to_uint(); if (__builtin_ctz(__mask_uint) != (32 - _M_prefix_len)) __throw_invalid_argument("network_v4: invalid mask"); if ((__mask_uint & 0x80000000) == 0) __throw_invalid_argument("network_v4: invalid mask"); } } // members: constexpr address_v4 address() const noexcept { return _M_addr; } constexpr int prefix_length() const noexcept { return _M_prefix_len; } constexpr address_v4 netmask() const noexcept { address_v4 __m; if (_M_prefix_len) __m = address_v4(0xFFFFFFFFu << (32 - _M_prefix_len)); return __m; } constexpr address_v4 network() const noexcept { return address_v4{_M_addr.to_uint() & netmask().to_uint()}; } constexpr address_v4 broadcast() const noexcept { auto __b = _M_addr.to_uint(); if (_M_prefix_len < 32) __b |= 0xFFFFFFFFu >> _M_prefix_len; return address_v4{__b}; } address_v4_range hosts() const noexcept { if (is_host()) return { address(), *++address_v4_iterator(address()) }; return { network(), broadcast() }; } constexpr network_v4 canonical() const noexcept { return network_v4(network(), prefix_length()); } constexpr bool is_host() const noexcept { return _M_prefix_len == 32; } constexpr bool is_subnet_of(const network_v4& __other) const noexcept { if (__other.prefix_length() < prefix_length()) { network_v4 __net(address(), __other.prefix_length()); return __net.canonical() == __other.canonical(); } return false; } template> __string_with<_Allocator> to_string(const _Allocator& __a = _Allocator()) const { auto __str = address().to_string(__a); const unsigned __addrlen = __str.length(); const unsigned __preflenlen = _M_prefix_len >= 10 ? 2 : 1; __str.resize(__addrlen + 1 + __preflenlen); __str[__addrlen] = '/'; std::__detail::__to_chars_10_impl(&__str.front() + __addrlen + 1, __preflenlen, (unsigned char)_M_prefix_len); return __str; } private: address_v4 _M_addr; int _M_prefix_len; }; /// An IPv6 network address. class network_v6 { public: // constructors: constexpr network_v6() noexcept : _M_addr(), _M_prefix_len(0) { } constexpr network_v6(const address_v6& __addr, int __prefix_len) : _M_addr(__addr), _M_prefix_len(__prefix_len) { if (_M_prefix_len < 0 || _M_prefix_len > 128) __throw_out_of_range("network_v6: invalid prefix length"); } // members: constexpr address_v6 address() const noexcept { return _M_addr; } constexpr int prefix_length() const noexcept { return _M_prefix_len; } _GLIBCXX17_CONSTEXPR address_v6 network() const noexcept { address_v6::bytes_type __bytes = _M_addr.to_bytes(); int __nbytes = (_M_prefix_len + 7) / 8; for (int __n = __nbytes; __n < 16; ++__n) __bytes[__n] = 0; if (int __zbits = (__nbytes * 8) - _M_prefix_len) __bytes[__nbytes - 1] &= 0xFF << __zbits; return address_v6(__bytes, _M_addr.scope_id()); } address_v6_range hosts() const noexcept { if (is_host()) return { address(), *++address_v6_iterator(address()) }; address_v6::bytes_type __bytes = _M_addr.to_bytes(); int __nbytes = (_M_prefix_len + 7) / 8; for (int __n = __nbytes; __n < 16; ++__n) __bytes[__n] = 0xFF; if (int __bits = (__nbytes * 8) - _M_prefix_len) __bytes[__nbytes - 1] |= (1 << __bits) - 1; address_v6 __last(__bytes, _M_addr.scope_id()); return { network(), *++address_v6_iterator(__last) }; } _GLIBCXX17_CONSTEXPR network_v6 canonical() const noexcept { return network_v6{network(), prefix_length()}; } constexpr bool is_host() const noexcept { return _M_prefix_len == 128; } constexpr bool is_subnet_of(const network_v6& __other) const noexcept { if (__other.prefix_length() < prefix_length()) { network_v6 __net(address(), __other.prefix_length()); return __net.canonical() == __other.canonical(); } return false; } template> __string_with<_Allocator> to_string(const _Allocator& __a = _Allocator()) const { return address().to_string(__a) + '/' + std::to_string(prefix_length()).c_str(); } private: address_v6 _M_addr; int _M_prefix_len; }; /** ip::network_v4 comparisons * @{ */ constexpr bool operator==(const network_v4& __a, const network_v4& __b) noexcept { return __a.address() == __b.address() && __a.prefix_length() == __b.prefix_length(); } constexpr bool operator!=(const network_v4& __a, const network_v4& __b) noexcept { return !(__a == __b); } /// @} /** ip::network_v6 comparisons * @{ */ constexpr bool operator==(const network_v6& __a, const network_v6& __b) noexcept { return __a.address() == __b.address() && __a.prefix_length() == __b.prefix_length(); } constexpr bool operator!=(const network_v6& __a, const network_v6& __b) noexcept { return !(__a == __b); } /// @} /** ip::network_v4 creation * @{ */ inline network_v4 make_network_v4(const address_v4& __a, int __prefix_len) { return network_v4{__a, __prefix_len}; } inline network_v4 make_network_v4(const address_v4& __a, const address_v4& __mask) { return network_v4{ __a, __mask }; } network_v4 make_network_v4(const char*, error_code&) noexcept; // TODO inline network_v4 make_network_v4(const char* __str) { return make_network_v4(__str, __throw_on_error{"make_network_v4"}); } network_v4 make_network_v4(const string&, error_code&) noexcept; // TODO inline network_v4 make_network_v4(const string& __str) { return make_network_v4(__str, __throw_on_error{"make_network_v4"}); } network_v4 make_network_v4(string_view, error_code&) noexcept; // TODO inline network_v4 make_network_v4(string_view __str) { return make_network_v4(__str, __throw_on_error{"make_network_v4"}); } /// @} /** ip::network_v6 creation * @{ */ inline network_v6 make_network_v6(const address_v6& __a, int __prefix_len) { return network_v6{__a, __prefix_len}; } network_v6 make_network_v6(const char*, error_code&) noexcept; // TODO inline network_v6 make_network_v6(const char* __str) { return make_network_v6(__str, __throw_on_error{"make_network_v6"}); } network_v6 make_network_v6(const string&, error_code&) noexcept; // TODO inline network_v6 make_network_v6(const string& __str) { return make_network_v6(__str, __throw_on_error{"make_network_v6"}); } network_v6 make_network_v6(string_view, error_code&) noexcept; // TODO inline network_v6 make_network_v6(string_view __str) { return make_network_v6(__str, __throw_on_error{"make_network_v6"}); } /// @} /// ip::network_v4 I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const network_v4& __net) { return __os << __net.to_string(); } /// ip::network_v6 I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const network_v6& __net) { return __os << __net.to_string(); } #if defined IPPROTO_TCP || defined IPPROTO_UDP /// An IP endpoint. template class basic_endpoint { public: // types: using protocol_type = _InternetProtocol; // constructors: _GLIBCXX20_CONSTEXPR basic_endpoint() noexcept : _M_data() { _M_data._M_v4.sin_family = protocol_type::v4().family(); // If in_addr contains a union, make the correct member active: if (std::__is_constant_evaluated()) std::_Construct(&_M_data._M_v4.sin_addr.s_addr); } _GLIBCXX20_CONSTEXPR basic_endpoint(const protocol_type& __proto, port_type __port_num) noexcept : _M_data() { if (__proto == protocol_type::v4()) { _M_data._M_v4.sin_family = protocol_type::v4().family(); _M_data._M_v4.sin_port = address_v4::_S_hton_16(__port_num); if (std::__is_constant_evaluated()) std::_Construct(&_M_data._M_v4.sin_addr.s_addr); } else if (__proto == protocol_type::v6()) { std::_Construct(&_M_data._M_v6); _M_data._M_v6.sin6_family = __proto.family(); _M_data._M_v6.sin6_port = address_v4::_S_hton_16(__port_num); _M_data._M_v6.sin6_scope_id = 0; if (std::__is_constant_evaluated()) std::_Construct(&_M_data._M_v6.sin6_addr.s6_addr); } else { __glibcxx_assert(__proto == protocol_type::v4() || __proto == protocol_type::v6()); } } _GLIBCXX20_CONSTEXPR basic_endpoint(const ip::address& __addr, port_type __port_num) noexcept : _M_data() { if (__addr.is_v4()) { _M_data._M_v4.sin_family = protocol_type::v4().family(); _M_data._M_v4.sin_port = address_v4::_S_hton_16(__port_num); std::_Construct(&_M_data._M_v4.sin_addr.s_addr, __addr._M_v4._M_addr); } else { std::_Construct(&_M_data._M_v6); _M_data._M_v6.sin6_family = protocol_type::v6().family(); _M_data._M_v6.sin6_port = address_v4::_S_hton_16(__port_num); if (std::__is_constant_evaluated()) std::_Construct(&_M_data._M_v6.sin6_addr.s6_addr); uint8_t* __s6a = _M_data._M_v6.sin6_addr.s6_addr; for (int __i = 0; __i < 16; ++__i) __s6a[__i] = __addr._M_v6._M_bytes[__i]; _M_data._M_v6.sin6_scope_id = __addr._M_v6._M_scope_id; } } // members: constexpr protocol_type protocol() const noexcept { return _M_is_v6() ? protocol_type::v6() : protocol_type::v4(); } constexpr ip::address address() const noexcept { if (_M_is_v6()) { address_v6 __v6; const uint8_t* __s6a = _M_data._M_v6.sin6_addr.s6_addr; for (int __i = 0; __i < 16; ++__i) __v6._M_bytes[__i] = __s6a[__i]; __v6._M_scope_id = _M_data._M_v6.sin6_scope_id; return __v6; } else { address_v4 __v4; __v4._M_addr = _M_data._M_v4.sin_addr.s_addr; return __v4; } } void address(const ip::address& __addr) noexcept { if (__addr.is_v6()) { std::_Construct(&_M_data._M_v6); _M_data._M_v6.sin6_family = protocol_type::v6().family(); __builtin_memcpy(_M_data._M_v6.sin6_addr.s6_addr, __addr._M_v6._M_bytes.data(), 16); _M_data._M_v6.sin6_scope_id = __addr._M_v6._M_scope_id; } else { std::_Construct(&_M_data._M_v4); _M_data._M_v4.sin_family = protocol_type::v4().family(); _M_data._M_v4.sin_addr.s_addr = __addr._M_v4._M_addr; } } constexpr port_type port() const noexcept { port_type __p = 0; if (_M_is_v6()) __p = _M_data._M_v6.sin6_port; else __p = _M_data._M_v4.sin_port; return address_v4::_S_ntoh_16(__p); } void port(port_type __port_num) noexcept { __port_num = address_v4::_S_hton_16(__port_num); if (_M_is_v6()) _M_data._M_v6.sin6_port = __port_num; else _M_data._M_v4.sin_port = __port_num; } void* data() noexcept { return &_M_data; } const void* data() const noexcept { return &_M_data; } constexpr size_t size() const noexcept { return _M_is_v6() ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); } void resize(size_t __s) { if (__s != size()) __throw_length_error("net::ip::basic_endpoint::resize"); } constexpr size_t capacity() const noexcept { return sizeof(_M_data); } private: union { sockaddr_in _M_v4; sockaddr_in6 _M_v6; } _M_data; constexpr bool _M_is_v6() const noexcept { // For constexpr eval we can just detect which union member is active. // i.e. emulate P2641R1's std::is_active_member(&_M_data._M_v6)). if (std::__is_constant_evaluated()) return __builtin_constant_p(_M_data._M_v6.sin6_family); return _M_data._M_v6.sin6_family == AF_INET6; } }; /** basic_endpoint comparisons * @{ */ template constexpr bool operator==(const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return __a.address() == __b.address() && __a.port() == __b.port(); } template constexpr bool operator!=(const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return !(__a == __b); } template constexpr bool operator< (const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return __a.address() < __b.address() || (!(__b.address() < __a.address()) && __a.port() < __b.port()); } template constexpr bool operator> (const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return __b < __a; } template constexpr bool operator<=(const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return !(__b < __a); } template constexpr bool operator>=(const basic_endpoint<_InternetProtocol>& __a, const basic_endpoint<_InternetProtocol>& __b) { return !(__a < __b); } /// @} /// basic_endpoint I/O template inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const basic_endpoint<_InternetProtocol>& __ep) { basic_ostringstream<_CharT, _Traits> __ss; if (__ep.protocol() == basic_endpoint<_InternetProtocol>::protocol_type::v6()) __ss << '[' << __ep.address() << ']'; else __ss << __ep.address(); __ss << ':' << __ep.port(); __os << __ss.str(); return __os; } /** Type representing a single result of name/address resolution. * @{ */ template class basic_resolver_entry { public: // types: using protocol_type = _InternetProtocol; using endpoint_type = typename _InternetProtocol::endpoint; // constructors: basic_resolver_entry() { } basic_resolver_entry(const endpoint_type& __ep, string_view __h, string_view __s) : _M_ep(__ep), _M_host(__h), _M_svc(__s) { } // members: endpoint_type endpoint() const { return _M_ep; } operator endpoint_type() const { return _M_ep; } template> __string_with<_Allocator> host_name(const _Allocator& __a = _Allocator()) const { return { _M_host, __a }; } template> __string_with<_Allocator> service_name(const _Allocator& __a = _Allocator()) const { return { _M_svc, __a }; } private: basic_endpoint<_InternetProtocol> _M_ep; string _M_host; string _M_svc; }; template inline bool operator==(const basic_resolver_entry<_InternetProtocol>& __a, const basic_resolver_entry<_InternetProtocol>& __b) { return __a.endpoint() == __b.endpoint() && __a.host_name() == __b.host_name() && __a.service_name() == __b.service_name(); } template inline bool operator!=(const basic_resolver_entry<_InternetProtocol>& __a, const basic_resolver_entry<_InternetProtocol>& __b) { return !(__a == __b); } /// @} /** Base class defining flags for name/address resolution. * @{ */ class resolver_base { public: enum flags : int { }; static constexpr flags passive = (flags)AI_PASSIVE; static constexpr flags canonical_name = (flags)AI_CANONNAME; static constexpr flags numeric_host = (flags)AI_NUMERICHOST; #ifdef AI_NUMERICSERV static constexpr flags numeric_service = (flags)AI_NUMERICSERV; #endif #ifdef AI_V4MAPPED static constexpr flags v4_mapped = (flags)AI_V4MAPPED; #endif #ifdef AI_ALL static constexpr flags all_matching = (flags)AI_ALL; #endif #ifdef AI_ADDRCONFIG static constexpr flags address_configured = (flags)AI_ADDRCONFIG; #endif friend constexpr flags operator&(flags __f1, flags __f2) noexcept { return flags( int(__f1) & int(__f2) ); } friend constexpr flags operator|(flags __f1, flags __f2) noexcept { return flags( int(__f1) | int(__f2) ); } friend constexpr flags operator^(flags __f1, flags __f2) noexcept { return flags( int(__f1) ^ int(__f2) ); } friend constexpr flags operator~(flags __f) noexcept { return flags( ~int(__f) ); } friend constexpr flags& operator&=(flags& __f1, flags __f2) noexcept { return __f1 = (__f1 & __f2); } friend constexpr flags& operator|=(flags& __f1, flags __f2) noexcept { return __f1 = (__f1 | __f2); } friend constexpr flags& operator^=(flags& __f1, flags __f2) noexcept { return __f1 = (__f1 ^ __f2); } protected: resolver_base() = default; ~resolver_base() = default; }; // TODO define resolver_base::flags static constants in .so for C++14 mode /// @} /** Container for results of name/address resolution. * @{ */ template class basic_resolver_results { public: // types: using protocol_type = _InternetProtocol; using endpoint_type = typename protocol_type::endpoint; using value_type = basic_resolver_entry; using const_reference = const value_type&; using reference = value_type&; using const_iterator = typename forward_list::const_iterator; using iterator = const_iterator; using difference_type = ptrdiff_t; using size_type = size_t; // construct / copy / destroy: basic_resolver_results() = default; basic_resolver_results(const basic_resolver_results&) = default; basic_resolver_results(basic_resolver_results&&) noexcept = default; basic_resolver_results& operator=(const basic_resolver_results&) = default; basic_resolver_results& operator=(basic_resolver_results&&) = default; ~basic_resolver_results() = default; // size: size_type size() const noexcept { return _M_size; } size_type max_size() const noexcept { return _M_results.max_size(); } _GLIBCXX_NODISCARD bool empty() const noexcept { return _M_results.empty(); } // element access: const_iterator begin() const { return _M_results.begin(); } const_iterator end() const { return _M_results.end(); } const_iterator cbegin() const { return _M_results.begin(); } const_iterator cend() const { return _M_results.end(); } // swap: void swap(basic_resolver_results& __that) noexcept { _M_results.swap(__that._M_results); } private: friend class basic_resolver; basic_resolver_results(string_view, string_view, resolver_base::flags, error_code&, protocol_type* = nullptr); basic_resolver_results(const endpoint_type&, error_code&); forward_list _M_results; size_t _M_size = 0; }; template inline bool operator==(const basic_resolver_results<_InternetProtocol>& __a, const basic_resolver_results<_InternetProtocol>& __b) { return __a.size() == __b.size() && std::equal(__a.begin(), __a.end(), __b.begin()); } template inline bool operator!=(const basic_resolver_results<_InternetProtocol>& __a, const basic_resolver_results<_InternetProtocol>& __b) { return !(__a == __b); } /// @} /// Perform name/address resolution. template class basic_resolver : public resolver_base { public: // types: using executor_type = io_context::executor_type; using protocol_type = _InternetProtocol; using endpoint_type = typename _InternetProtocol::endpoint; using results_type = basic_resolver_results<_InternetProtocol>; // construct / copy / destroy: explicit basic_resolver(io_context& __ctx) : _M_ctx(&__ctx) { } basic_resolver(const basic_resolver&) = delete; basic_resolver(basic_resolver&& __rhs) noexcept : _M_ctx(__rhs._M_ctx) { } // TODO move state/tasks etc. ~basic_resolver() { cancel(); } basic_resolver& operator=(const basic_resolver&) = delete; basic_resolver& operator=(basic_resolver&& __rhs) { cancel(); _M_ctx = __rhs._M_ctx; // TODO move state/tasks etc. return *this; } // basic_resolver operations: executor_type get_executor() noexcept { return _M_ctx->get_executor(); } void cancel() { } // TODO results_type resolve(string_view __host_name, string_view __service_name) { return resolve(__host_name, __service_name, resolver_base::flags(), __throw_on_error{"basic_resolver::resolve"}); } results_type resolve(string_view __host_name, string_view __service_name, error_code& __ec) { return resolve(__host_name, __service_name, resolver_base::flags(), __ec); } results_type resolve(string_view __host_name, string_view __service_name, flags __f) { return resolve(__host_name, __service_name, __f, __throw_on_error{"basic_resolver::resolve"}); } results_type resolve(string_view __host_name, string_view __service_name, flags __f, error_code& __ec) { return {__host_name, __service_name, __f, __ec}; } template __deduced_t<_CompletionToken, void(error_code, results_type)> async_resolve(string_view __host_name, string_view __service_name, _CompletionToken&& __token) { return async_resolve(__host_name, __service_name, resolver_base::flags(), forward<_CompletionToken>(__token)); } template __deduced_t<_CompletionToken, void(error_code, results_type)> async_resolve(string_view __host_name, string_view __service_name, flags __f, _CompletionToken&& __token); // TODO results_type resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name) { return resolve(__protocol, __host_name, __service_name, resolver_base::flags(), __throw_on_error{"basic_resolver::resolve"}); } results_type resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name, error_code& __ec) { return resolve(__protocol, __host_name, __service_name, resolver_base::flags(), __ec); } results_type resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name, flags __f) { return resolve(__protocol, __host_name, __service_name, __f, __throw_on_error{"basic_resolver::resolve"}); } results_type resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name, flags __f, error_code& __ec) { return {__host_name, __service_name, __f, __ec, &__protocol}; } template __deduced_t<_CompletionToken, void(error_code, results_type)> async_resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name, _CompletionToken&& __token) { return async_resolve(__protocol, __host_name, __service_name, resolver_base::flags(), forward<_CompletionToken>(__token)); } template __deduced_t<_CompletionToken, void(error_code, results_type)> async_resolve(const protocol_type& __protocol, string_view __host_name, string_view __service_name, flags __f, _CompletionToken&& __token); // TODO results_type resolve(const endpoint_type& __ep) { return resolve(__ep, __throw_on_error{"basic_resolver::resolve"}); } results_type resolve(const endpoint_type& __ep, error_code& __ec) { return { __ep, __ec }; } template // TODO __deduced_t<_CompletionToken, void(error_code, results_type)> async_resolve(const endpoint_type& __ep, _CompletionToken&& __token); private: io_context* _M_ctx; }; /// Private constructor to synchronously resolve host and service names. template basic_resolver_results<_InternetProtocol>:: basic_resolver_results(string_view __host_name, string_view __service_name, resolver_base::flags __f, error_code& __ec, protocol_type* __protocol) { #ifdef _GLIBCXX_HAVE_NETDB_H string __host; const char* __h = __host_name.data() ? (__host = __host_name.to_string()).c_str() : nullptr; string __svc; const char* __s = __service_name.data() ? (__svc = __service_name.to_string()).c_str() : nullptr; ::addrinfo __hints{ }; __hints.ai_flags = static_cast(__f); if (__protocol) { __hints.ai_family = __protocol->family(); __hints.ai_socktype = __protocol->type(); __hints.ai_protocol = __protocol->protocol(); } else { auto __p = endpoint_type{}.protocol(); __hints.ai_family = AF_UNSPEC; __hints.ai_socktype = __p.type(); __hints.ai_protocol = __p.protocol(); } struct __scoped_addrinfo { ~__scoped_addrinfo() { if (_M_p) ::freeaddrinfo(_M_p); } ::addrinfo* _M_p = nullptr; } __sai; if (int __err = ::getaddrinfo(__h, __s, &__hints, &__sai._M_p)) { __ec = ip::__make_resolver_error_code(__err, errno); return; } __ec.clear(); endpoint_type __ep; auto __tail = _M_results.before_begin(); for (auto __ai = __sai._M_p; __ai != nullptr; __ai = __ai->ai_next) { if (__ai->ai_family == AF_INET || __ai->ai_family == AF_INET6) { if (__ai->ai_addrlen <= __ep.capacity()) __builtin_memcpy(__ep.data(), __ai->ai_addr, __ai->ai_addrlen); __ep.resize(__ai->ai_addrlen); __tail = _M_results.emplace_after(__tail, __ep, __host, __svc); _M_size++; } } #else __ec = std::make_error_code(errc::operation_not_supported); #endif } /// Private constructor to synchronously resolve an endpoint. template basic_resolver_results<_InternetProtocol>:: basic_resolver_results(const endpoint_type& __ep, error_code& __ec) { #ifdef _GLIBCXX_HAVE_NETDB_H char __host_name[1025]; // glibc NI_MAXHOST char __service_name[32]; // glibc NI_MAXSERV int __flags = 0; if (__ep.protocol().type() == SOCK_DGRAM) __flags |= NI_DGRAM; auto __sa = static_cast(__ep.data()); int __err = ::getnameinfo(__sa, __ep.size(), __host_name, sizeof(__host_name), __service_name, sizeof(__service_name), __flags); if (__err) { __flags |= NI_NUMERICSERV; __err = ::getnameinfo(__sa, __ep.size(), __host_name, sizeof(__host_name), __service_name, sizeof(__service_name), __flags); } if (__err) __ec = ip::__make_resolver_error_code(__err, errno); else { __ec.clear(); _M_results.emplace_front(__ep, __host_name, __service_name); _M_size = 1; } #else __ec = std::make_error_code(errc::operation_not_supported); #endif } #endif // IPPROTO_TCP || IPPROTO_UDP /** The name of the local host. * @{ */ template __string_with<_Allocator> host_name(const _Allocator& __a, error_code& __ec) { #ifdef HOST_NAME_MAX constexpr size_t __maxlen = HOST_NAME_MAX; #else constexpr size_t __maxlen = 256; #endif char __buf[__maxlen + 1]; if (::gethostname(__buf, __maxlen) == -1) __ec.assign(errno, generic_category()); __buf[__maxlen] = '\0'; return { __buf, __a }; } template inline __string_with<_Allocator> host_name(const _Allocator& __a) { return host_name(__a, __throw_on_error{"host_name"}); } inline string host_name(error_code& __ec) { return host_name(std::allocator{}, __ec); } inline string host_name() { return host_name(std::allocator{}, __throw_on_error{"host_name"}); } /// @} #ifdef IPPROTO_TCP /// The TCP byte-stream protocol. class tcp { public: // types: using endpoint = basic_endpoint; ///< A TCP endpoint. using resolver = basic_resolver; ///< A TCP resolver. using socket = basic_stream_socket; ///< A TCP socket. using acceptor = basic_socket_acceptor; ///< A TCP acceptor. using iostream = basic_socket_iostream; ///< A TCP iostream. #ifdef TCP_NODELAY /// Disable coalescing of small segments (i.e. the Nagle algorithm). struct no_delay : __sockopt_crtp { using __sockopt_crtp::__sockopt_crtp; using __sockopt_crtp::operator=; static const int _S_level = IPPROTO_TCP; static const int _S_name = TCP_NODELAY; }; #endif // static members: /// A protocol object representing IPv4 TCP. static constexpr tcp v4() noexcept { return tcp(AF_INET); } /// A protocol object representing IPv6 TCP. static constexpr tcp v6() noexcept { return tcp(AF_INET6); } tcp() = delete; constexpr int family() const noexcept { return _M_family; } constexpr int type() const noexcept { return SOCK_STREAM; } constexpr int protocol() const noexcept { return IPPROTO_TCP; } private: constexpr explicit tcp(int __family) : _M_family(__family) { } int _M_family; }; /** tcp comparisons * @{ */ constexpr bool operator==(const tcp& __a, const tcp& __b) noexcept { return __a.family() == __b.family(); } constexpr bool operator!=(const tcp& __a, const tcp& __b) noexcept { return !(__a == __b); } /// @} #endif // IPPROTO_TCP #ifdef IPPROTO_UDP /// The UDP datagram protocol. class udp { public: // types: using endpoint = basic_endpoint; using resolver = basic_resolver; using socket = basic_datagram_socket; // static members: static constexpr udp v4() noexcept { return udp(AF_INET); } static constexpr udp v6() noexcept { return udp(AF_INET6); } udp() = delete; constexpr int family() const noexcept { return _M_family; } constexpr int type() const noexcept { return SOCK_DGRAM; } constexpr int protocol() const noexcept { return IPPROTO_UDP; } private: constexpr explicit udp(int __family) : _M_family(__family) { } int _M_family; }; /** udp comparisons * @{ */ constexpr bool operator==(const udp& __a, const udp& __b) noexcept { return __a.family() == __b.family(); } constexpr bool operator!=(const udp& __a, const udp& __b) noexcept { return !(__a == __b); } /// @} #endif // IPPROTO_UDP #if defined IPPROTO_IP && defined IPPROTO_IPV6 /// Restrict a socket created for an IPv6 protocol to IPv6 only. class v6_only : public __sockopt_crtp { public: using __sockopt_crtp::__sockopt_crtp; using __sockopt_crtp::operator=; private: friend __sockopt_crtp; static const int _S_level = IPPROTO_IPV6; static const int _S_name = IPV6_V6ONLY; }; namespace unicast { /// Set the default number of hops (TTL) for outbound datagrams. class hops : public __sockopt_crtp { public: using __sockopt_crtp::__sockopt_crtp; using __sockopt_crtp::operator=; template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } template int name(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPV6_UNICAST_HOPS : IP_TTL; } }; } // namespace unicast namespace multicast { class __mcastopt { public: explicit __mcastopt(const address& __grp) noexcept : __mcastopt(__grp.is_v4() ? __mcastopt(__grp.to_v4()) : __mcastopt(__grp.to_v6())) { } explicit __mcastopt(const address_v4& __grp, const address_v4& __iface = address_v4::any()) noexcept { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ _M_v4.imr_multiaddr.s_addr = __grp.to_uint(); _M_v4.imr_interface.s_addr = __iface.to_uint(); #else _M_v4.imr_multiaddr.s_addr = __builtin_bswap32(__grp.to_uint()); _M_v4.imr_interface.s_addr = __builtin_bswap32(__iface.to_uint()); #endif } explicit __mcastopt(const address_v6& __grp, unsigned int __iface = 0) noexcept { const auto __addr = __grp.to_bytes(); __builtin_memcpy(_M_v6.ipv6mr_multiaddr.s6_addr, __addr.data(), 16); _M_v6.ipv6mr_interface = __iface; } template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } template const void* data(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? &_M_v6 : &_M_v4; } template size_t size(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? sizeof(_M_v6) : sizeof(_M_v4); } private: ipv6_mreq _M_v6 = {}; ip_mreq _M_v4 = {}; }; /// Request that a socket joins a multicast group. class join_group : private __mcastopt { public: using __mcastopt::__mcastopt; using __mcastopt::level; using __mcastopt::data; using __mcastopt::size; template int name(const _Protocol& __p) const noexcept { if (__p.family() == AF_INET6) return IPV6_JOIN_GROUP; return IP_ADD_MEMBERSHIP; } }; /// Request that a socket leaves a multicast group. class leave_group : private __mcastopt { public: using __mcastopt::__mcastopt; using __mcastopt::level; using __mcastopt::data; using __mcastopt::size; template int name(const _Protocol& __p) const noexcept { if (__p.family() == AF_INET6) return IPV6_LEAVE_GROUP; return IP_DROP_MEMBERSHIP; } }; /// Specify the network interface for outgoing multicast datagrams. class outbound_interface { public: explicit outbound_interface(const address_v4& __v4) noexcept { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ _M_v4.s_addr = __v4.to_uint(); #else _M_v4.s_addr = __builtin_bswap32(__v4.to_uint()); #endif } explicit outbound_interface(unsigned int __v6) noexcept : _M_v4(), _M_v6(__v6) { } template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } template int name(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPV6_MULTICAST_IF : IP_MULTICAST_IF; } template const void* data(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? &_M_v6 : &_M_v4; } template size_t size(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? sizeof(_M_v6) : sizeof(_M_v4); } private: in_addr _M_v4; unsigned _M_v6 = 0; }; /// Set the default number of hops (TTL) for outbound datagrams. class hops : public __sockopt_crtp { public: using __sockopt_crtp::__sockopt_crtp; using __sockopt_crtp::operator=; template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } template int name(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL; } }; /// Set whether datagrams are delivered back to the local application. class enable_loopback : public __sockopt_crtp { public: using __sockopt_crtp::__sockopt_crtp; using __sockopt_crtp::operator=; template int level(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP; } template int name(const _Protocol& __p) const noexcept { return __p.family() == AF_INET6 ? IPV6_MULTICAST_LOOP : IP_MULTICAST_LOOP; } }; } // namespace multicast #endif // IPPROTO_IP && IPPROTO_IPV6 /// @} } // namespace ip } // namespace v1 } // namespace net } // namespace experimental template<> struct is_error_condition_enum : public true_type {}; // hash support template struct hash; template<> struct hash : __hash_base { size_t operator()(const experimental::net::v1::ip::address& __a) const { if (__a.is_v4()) return _Hash_impl::hash(__a.to_v4()); else return _Hash_impl::hash(__a.to_v6()); } }; template<> struct hash : __hash_base { size_t operator()(const experimental::net::v1::ip::address_v4& __a) const { return _Hash_impl::hash(__a.to_bytes()); } }; template<> struct hash : __hash_base { size_t operator()(const experimental::net::v1::ip::address_v6& __a) const { return _Hash_impl::hash(__a.to_bytes()); } }; _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // C++14 #endif // _GLIBCXX_EXPERIMENTAL_INTERNET