// // Copyright (c) 2025 Klemens Morgenstern (klemens.morgenstern@gmx.net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_COBALT_IO_ENDPOINT_HPP #define BOOST_COBALT_IO_ENDPOINT_HPP #include #include #include #include #include #include #include #include #include #if defined(BOOST_COBALT_NO_PMR) #include #else #include #endif #include namespace boost::cobalt::detail { BOOST_COBALT_IO_DECL BOOST_NORETURN void throw_bad_endpoint_access( boost::source_location const& loc); } namespace boost::cobalt::io { struct endpoint; struct stream_socket; #if __GNUC__ && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsubobject-linkage" #endif struct protocol_type { using family_t = int; using type_t = int; using protocol_t = int; constexpr family_t family() const noexcept {return family_;}; constexpr type_t type() const noexcept {return type_;}; constexpr protocol_t protocol() const noexcept {return protocol_;}; constexpr explicit protocol_type(family_t family = static_cast(0), type_t type = static_cast(0), protocol_t protocol = static_cast(0)) noexcept : family_(family), type_(type), protocol_(protocol) {} template requires requires (const OtherProtocol & op) { {static_cast(op.family())}; {static_cast(op.type())}; {static_cast(op.protocol())}; } constexpr protocol_type(const OtherProtocol & op) noexcept : family_(static_cast(op.family())) , type_(static_cast(op.type())) , protocol_(static_cast(op.protocol())) {} friend constexpr auto operator<=>(const protocol_type & , const protocol_type &) noexcept = default; using endpoint = io::endpoint; // for the asio acceptor using socket = stream_socket; private: family_t family_ = static_cast(0); type_t type_ = static_cast(0); protocol_t protocol_ = static_cast(0); }; template(0), protocol_type::type_t Type = static_cast(0), protocol_type::protocol_t Protocol = static_cast(0)> struct static_protocol { using family_t = protocol_type::family_t ; using type_t = protocol_type::type_t ; using protocol_t = protocol_type::protocol_t; constexpr family_t family() const noexcept {return family_t(Family); }; constexpr type_t type() const noexcept {return type_t(Type); }; constexpr protocol_t protocol() const noexcept {return protocol_t(Protocol); }; using endpoint = io::endpoint; }; constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip {}; constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v4 {}; constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v6 {}; constexpr static_protocol tcp {}; constexpr static_protocol tcp_v4{}; constexpr static_protocol tcp_v6{}; constexpr static_protocol udp {}; constexpr static_protocol udp_v4{}; constexpr static_protocol udp_v6{}; constexpr static_protocol icmp {}; constexpr static_protocol local_stream {}; constexpr static_protocol local_datagram {}; constexpr static_protocol local_seqpacket{}; constexpr static_protocol local_protocol {}; #if defined(IPPROTO_SCTP) constexpr static_protocol sctp {}; constexpr static_protocol sctp_v4{}; constexpr static_protocol sctp_v6{}; #endif template struct make_endpoint_tag {}; template struct get_endpoint_tag {}; struct endpoint { using storage_type = asio::detail::sockaddr_storage_type; using addr_type = asio::detail::socket_addr_type; void resize(std::size_t size) { BOOST_ASSERT(size < sizeof(storage_)); size_ = size; } void * data() {return &storage_; } const void * data() const {return &storage_; } std::size_t size() const {return size_;} std::size_t capacity() const {return sizeof(storage_);} void set_type (protocol_type::type_t type) { type_ = type;} void set_protocol(protocol_type::protocol_t protocol) { protocol_ = protocol;} protocol_type protocol() const { return protocol_type{static_cast(base_.sa_family), type_, protocol_}; } endpoint() = default; endpoint(const endpoint & ep) : storage_(ep.storage_), size_(ep.size_), protocol_(ep.protocol_), type_(ep.type_) { } template requires requires (make_endpoint_tag proto, addr_type* addr, Args && ... args) { {tag_invoke(proto, addr, std::forward(args)...)} -> std::convertible_to; } endpoint(static_protocol proto, Args && ... args) : base_{}, protocol_(Protocol), type_(Type) { size_ = tag_invoke(make_endpoint_tag{}, &base_, std::forward(args)...); } template requires requires (OtherEndpoint oe) { {oe.protocol()} -> std::convertible_to; {oe.data()} -> std::convertible_to; {oe.size()} -> std::convertible_to; } endpoint(OtherEndpoint && oe) : base_{}, protocol_(static_cast(oe.protocol().protocol())), type_(static_cast(oe.protocol().type())) { resize(oe.size()); memcpy(data(), oe.data(), size()); } template requires requires (get_endpoint_tag tag, protocol_type actual, const addr_type * addr) {{tag_invoke(tag, actual, addr)};} friend auto get_if(const endpoint * ep) -> decltype(tag_invoke(get_endpoint_tag{}, protocol_type{}, static_cast(nullptr))) { const auto actual = ep->protocol(); if (Protocol.type() != 0 && actual.type() != 0 && actual.type() != Protocol.type()) return nullptr; if (Protocol.protocol() != 0 && actual.protocol() != 0 && actual.protocol() != Protocol.protocol()) return nullptr; return tag_invoke(get_endpoint_tag{}, ep->protocol(), &ep->base_); } private: union { asio::detail::socket_addr_type base_{}; storage_type storage_; }; std::size_t size_{sizeof(base_)}; protocol_type::protocol_t protocol_ = static_cast(0); protocol_type::type_t type_ = static_cast(0); }; #if __GNUC__ && !defined(__clang__) #pragma GCC diagnostic pop #endif #if defined(BOOST_COBALT_NO_PMR) using endpoint_sequence = std::vector; #else using endpoint_sequence = pmr::vector; #endif class bad_endpoint_access : public std::exception { public: bad_endpoint_access() noexcept = default; char const * what() const noexcept { return "bad_endpoint_access"; } }; template requires requires (get_endpoint_tag tag, protocol_type actual, endpoint::addr_type * addr) {{tag_invoke(tag, actual, addr)};} auto get(const endpoint & ep, const boost::source_location & loc = BOOST_CURRENT_LOCATION) { auto e = get_if(&ep); if (!e) cobalt::detail::throw_bad_endpoint_access(loc); return *e; } struct local_endpoint { std::string_view path() const { return unix_.sun_path;} private: union { asio::detail::sockaddr_storage_type addr_; asio::detail::sockaddr_un_type unix_; }; }; BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::string_view sv); BOOST_COBALT_IO_DECL const local_endpoint* tag_invoke(get_endpoint_tag, protocol_type actual, const endpoint::addr_type * addr); struct ip_address_v4 { std::uint16_t port() const {return boost::endian::big_to_native(in_.sin_port);} std::uint32_t addr() const {return in_.sin_addr.s_addr;} BOOST_COBALT_IO_DECL boost::static_string<15> addr_str() const; private: union { asio::detail::sockaddr_storage_type addr_{}; asio::detail::sockaddr_in4_type in_; }; }; BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::uint32_t address, std::uint16_t port); BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::string_view address, std::uint16_t port); BOOST_COBALT_IO_DECL const ip_address_v4* tag_invoke(get_endpoint_tag, protocol_type actual, const endpoint::addr_type * addr); struct ip_address_v6 { std::uint16_t port() const {return boost::endian::big_to_native(in_.sin6_port);} std::array addr() const { std::array res; const auto & in = in_.sin6_addr.s6_addr; std::copy(std::begin(in), std::end(in), res.begin()); return res; } BOOST_COBALT_IO_DECL boost::static_string<45> addr_str() const; private: union { asio::detail::sockaddr_storage_type addr_{}; asio::detail::sockaddr_in6_type in_; }; }; BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::span address, std::uint16_t port); BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::string_view address, std::uint16_t port); BOOST_COBALT_IO_DECL const ip_address_v6* tag_invoke(get_endpoint_tag, protocol_type actual, const endpoint::addr_type * addr); struct BOOST_SYMBOL_VISIBLE ip_address { bool is_ipv6() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET6); } bool is_ipv4() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET); } std::uint16_t port() const {return boost::endian::big_to_native(addr_.ss_family == AF_INET ? in_.sin_port : in6_.sin6_port);} BOOST_COBALT_IO_DECL std::array addr() const; BOOST_COBALT_IO_DECL boost::static_string<45> addr_str() const; private: union { asio::detail::sockaddr_storage_type addr_{}; asio::detail::sockaddr_in4_type in_; asio::detail::sockaddr_in6_type in6_; }; }; BOOST_COBALT_IO_DECL std::size_t tag_invoke(make_endpoint_tag, asio::detail::socket_addr_type* base, std::string_view address, std::uint16_t port); BOOST_COBALT_IO_DECL const ip_address* tag_invoke(get_endpoint_tag, protocol_type actual, const endpoint::addr_type * addr); } #endif //BOOST_COBALT_IO_ENDPOINT_HPP