endpoint.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. //
  2. // Copyright (c) 2025 Klemens Morgenstern (klemens.morgenstern@gmx.net)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_COBALT_IO_ENDPOINT_HPP
  8. #define BOOST_COBALT_IO_ENDPOINT_HPP
  9. #include <boost/cobalt/io/detail/config.hpp>
  10. #include <boost/cobalt/config.hpp>
  11. #include <boost/cobalt/detail/exception.hpp>
  12. #include <boost/asio/detail/socket_types.hpp>
  13. #include <boost/endian/conversion.hpp>
  14. #include <boost/static_string.hpp>
  15. #include <boost/system/result.hpp>
  16. #include <array>
  17. #include <algorithm>
  18. #if defined(BOOST_COBALT_NO_PMR)
  19. #include <boost/container/pmr/vector.hpp>
  20. #else
  21. #include <vector>
  22. #endif
  23. #include <span>
  24. namespace boost::cobalt::detail
  25. {
  26. BOOST_COBALT_IO_DECL BOOST_NORETURN void
  27. throw_bad_endpoint_access(
  28. boost::source_location const& loc);
  29. }
  30. namespace boost::cobalt::io
  31. {
  32. struct endpoint;
  33. struct stream_socket;
  34. #if __GNUC__ && !defined(__clang__)
  35. #pragma GCC diagnostic push
  36. #pragma GCC diagnostic ignored "-Wsubobject-linkage"
  37. #endif
  38. struct protocol_type
  39. {
  40. using family_t = int;
  41. using type_t = int;
  42. using protocol_t = int;
  43. constexpr family_t family() const noexcept {return family_;};
  44. constexpr type_t type() const noexcept {return type_;};
  45. constexpr protocol_t protocol() const noexcept {return protocol_;};
  46. constexpr explicit
  47. protocol_type(family_t family = static_cast<family_t>(0),
  48. type_t type = static_cast<type_t>(0),
  49. protocol_t protocol = static_cast<protocol_t>(0)) noexcept
  50. : family_(family), type_(type), protocol_(protocol)
  51. {}
  52. template<typename OtherProtocol>
  53. requires requires (const OtherProtocol & op)
  54. {
  55. {static_cast<family_t>(op.family())};
  56. {static_cast<type_t>(op.type())};
  57. {static_cast<protocol_t>(op.protocol())};
  58. }
  59. constexpr protocol_type(const OtherProtocol & op) noexcept
  60. : family_(static_cast<family_t>(op.family()))
  61. , type_(static_cast<type_t>(op.type()))
  62. , protocol_(static_cast<protocol_t>(op.protocol()))
  63. {}
  64. friend
  65. constexpr auto operator<=>(const protocol_type & , const protocol_type &) noexcept = default;
  66. using endpoint = io::endpoint;
  67. // for the asio acceptor
  68. using socket = stream_socket;
  69. private:
  70. family_t family_ = static_cast<family_t>(0);
  71. type_t type_ = static_cast<type_t>(0);
  72. protocol_t protocol_ = static_cast<protocol_t>(0);
  73. };
  74. template<protocol_type::family_t Family = static_cast<protocol_type::family_t>(0),
  75. protocol_type::type_t Type = static_cast<protocol_type::type_t>(0),
  76. protocol_type::protocol_t Protocol = static_cast<protocol_type::protocol_t>(0)>
  77. struct static_protocol
  78. {
  79. using family_t = protocol_type::family_t ;
  80. using type_t = protocol_type::type_t ;
  81. using protocol_t = protocol_type::protocol_t;
  82. constexpr family_t family() const noexcept {return family_t(Family); };
  83. constexpr type_t type() const noexcept {return type_t(Type); };
  84. constexpr protocol_t protocol() const noexcept {return protocol_t(Protocol); };
  85. using endpoint = io::endpoint;
  86. };
  87. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_UNSPEC), static_cast<protocol_type::type_t>(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip {};
  88. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET), static_cast<protocol_type::type_t>(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v4 {};
  89. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET6), static_cast<protocol_type::type_t>(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v6 {};
  90. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_UNSPEC), BOOST_ASIO_OS_DEF(SOCK_STREAM), BOOST_ASIO_OS_DEF(IPPROTO_TCP)> tcp {};
  91. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET), BOOST_ASIO_OS_DEF(SOCK_STREAM), BOOST_ASIO_OS_DEF(IPPROTO_TCP)> tcp_v4{};
  92. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET6), BOOST_ASIO_OS_DEF(SOCK_STREAM), BOOST_ASIO_OS_DEF(IPPROTO_TCP)> tcp_v6{};
  93. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_UNSPEC), BOOST_ASIO_OS_DEF(SOCK_DGRAM), BOOST_ASIO_OS_DEF(IPPROTO_UDP)> udp {};
  94. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET), BOOST_ASIO_OS_DEF(SOCK_DGRAM), BOOST_ASIO_OS_DEF(IPPROTO_UDP)> udp_v4{};
  95. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET6), BOOST_ASIO_OS_DEF(SOCK_DGRAM), BOOST_ASIO_OS_DEF(IPPROTO_ICMP)> udp_v6{};
  96. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_UNSPEC), BOOST_ASIO_OS_DEF(SOCK_DGRAM), BOOST_ASIO_OS_DEF(IPPROTO_ICMP)> icmp {};
  97. constexpr static_protocol<AF_UNIX, BOOST_ASIO_OS_DEF(SOCK_STREAM)> local_stream {};
  98. constexpr static_protocol<AF_UNIX, BOOST_ASIO_OS_DEF(SOCK_DGRAM)> local_datagram {};
  99. constexpr static_protocol<AF_UNIX, BOOST_ASIO_OS_DEF(SOCK_SEQPACKET)> local_seqpacket{};
  100. constexpr static_protocol<AF_UNIX> local_protocol {};
  101. #if defined(IPPROTO_SCTP)
  102. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_UNSPEC), BOOST_ASIO_OS_DEF(SOCK_SEQPACKET), IPPROTO_SCTP> sctp {};
  103. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET), BOOST_ASIO_OS_DEF(SOCK_SEQPACKET), IPPROTO_SCTP> sctp_v4{};
  104. constexpr static_protocol<BOOST_ASIO_OS_DEF(AF_INET6), BOOST_ASIO_OS_DEF(SOCK_SEQPACKET), IPPROTO_SCTP> sctp_v6{};
  105. #endif
  106. template<protocol_type::family_t Family>
  107. struct make_endpoint_tag {};
  108. template<protocol_type::family_t Family>
  109. struct get_endpoint_tag {};
  110. struct endpoint
  111. {
  112. using storage_type = asio::detail::sockaddr_storage_type;
  113. using addr_type = asio::detail::socket_addr_type;
  114. void resize(std::size_t size)
  115. {
  116. BOOST_ASSERT(size < sizeof(storage_));
  117. size_ = size;
  118. }
  119. void * data() {return &storage_; }
  120. const void * data() const {return &storage_; }
  121. std::size_t size() const {return size_;}
  122. std::size_t capacity() const {return sizeof(storage_);}
  123. void set_type (protocol_type::type_t type) { type_ = type;}
  124. void set_protocol(protocol_type::protocol_t protocol) { protocol_ = protocol;}
  125. protocol_type protocol() const
  126. {
  127. return protocol_type{static_cast<protocol_type::family_t>(base_.sa_family), type_, protocol_};
  128. }
  129. endpoint() = default;
  130. endpoint(const endpoint & ep) : storage_(ep.storage_), size_(ep.size_), protocol_(ep.protocol_), type_(ep.type_)
  131. {
  132. }
  133. template<protocol_type::family_t Family,
  134. protocol_type::type_t Type,
  135. protocol_type::protocol_t Protocol,
  136. typename ... Args>
  137. requires requires (make_endpoint_tag<Family> proto,
  138. addr_type* addr, Args && ... args)
  139. {
  140. {tag_invoke(proto, addr, std::forward<Args>(args)...)} -> std::convertible_to<std::size_t>;
  141. }
  142. endpoint(static_protocol<Family, Type, Protocol> proto, Args && ... args)
  143. : base_{},
  144. protocol_(Protocol), type_(Type)
  145. {
  146. size_ = tag_invoke(make_endpoint_tag<Family>{}, &base_, std::forward<Args>(args)...);
  147. }
  148. template<typename OtherEndpoint>
  149. requires requires (OtherEndpoint oe)
  150. {
  151. {oe.protocol()} -> std::convertible_to<protocol_type>;
  152. {oe.data()} -> std::convertible_to<void*>;
  153. {oe.size()} -> std::convertible_to<std::size_t>;
  154. }
  155. endpoint(OtherEndpoint && oe) :
  156. base_{},
  157. protocol_(static_cast<protocol_type::protocol_t>(oe.protocol().protocol())),
  158. type_(static_cast<protocol_type::type_t>(oe.protocol().type()))
  159. {
  160. resize(oe.size());
  161. memcpy(data(), oe.data(), size());
  162. }
  163. template<static_protocol Protocol>
  164. requires requires (get_endpoint_tag<Protocol.family()> tag,
  165. protocol_type actual,
  166. const addr_type * addr) {{tag_invoke(tag, actual, addr)};}
  167. friend auto get_if(const endpoint * ep)
  168. -> decltype(tag_invoke(get_endpoint_tag<Protocol.family()>{}, protocol_type{},
  169. static_cast<const addr_type *>(nullptr)))
  170. {
  171. const auto actual = ep->protocol();
  172. if (Protocol.type() != 0 && actual.type() != 0 && actual.type() != Protocol.type())
  173. return nullptr;
  174. if (Protocol.protocol() != 0 && actual.protocol() != 0 && actual.protocol() != Protocol.protocol())
  175. return nullptr;
  176. return tag_invoke(get_endpoint_tag<Protocol.family()>{}, ep->protocol(), &ep->base_);
  177. }
  178. private:
  179. union {
  180. asio::detail::socket_addr_type base_{};
  181. storage_type storage_;
  182. };
  183. std::size_t size_{sizeof(base_)};
  184. protocol_type::protocol_t protocol_ = static_cast<protocol_type::protocol_t>(0);
  185. protocol_type::type_t type_ = static_cast<protocol_type::type_t>(0);
  186. };
  187. #if __GNUC__ && !defined(__clang__)
  188. #pragma GCC diagnostic pop
  189. #endif
  190. #if defined(BOOST_COBALT_NO_PMR)
  191. using endpoint_sequence = std::vector<endpoint>;
  192. #else
  193. using endpoint_sequence = pmr::vector<endpoint>;
  194. #endif
  195. class bad_endpoint_access : public std::exception
  196. {
  197. public:
  198. bad_endpoint_access() noexcept = default;
  199. char const * what() const noexcept
  200. {
  201. return "bad_endpoint_access";
  202. }
  203. };
  204. template<static_protocol Protocol>
  205. requires requires (get_endpoint_tag<Protocol.family()> tag,
  206. protocol_type actual,
  207. endpoint::addr_type * addr) {{tag_invoke(tag, actual, addr)};}
  208. auto get(const endpoint & ep, const boost::source_location & loc = BOOST_CURRENT_LOCATION)
  209. {
  210. auto e = get_if<Protocol>(&ep);
  211. if (!e)
  212. cobalt::detail::throw_bad_endpoint_access(loc);
  213. return *e;
  214. }
  215. struct local_endpoint
  216. {
  217. std::string_view path() const { return unix_.sun_path;}
  218. private:
  219. union {
  220. asio::detail::sockaddr_storage_type addr_;
  221. asio::detail::sockaddr_un_type unix_;
  222. };
  223. };
  224. BOOST_COBALT_IO_DECL
  225. std::size_t tag_invoke(make_endpoint_tag<AF_UNIX>,
  226. asio::detail::socket_addr_type* base,
  227. std::string_view sv);
  228. BOOST_COBALT_IO_DECL
  229. const local_endpoint* tag_invoke(get_endpoint_tag<AF_UNIX>,
  230. protocol_type actual,
  231. const endpoint::addr_type * addr);
  232. struct ip_address_v4
  233. {
  234. std::uint16_t port() const {return boost::endian::big_to_native(in_.sin_port);}
  235. std::uint32_t addr() const {return in_.sin_addr.s_addr;}
  236. BOOST_COBALT_IO_DECL boost::static_string<15> addr_str() const;
  237. private:
  238. union {
  239. asio::detail::sockaddr_storage_type addr_{};
  240. asio::detail::sockaddr_in4_type in_;
  241. };
  242. };
  243. BOOST_COBALT_IO_DECL
  244. std::size_t tag_invoke(make_endpoint_tag<AF_INET>,
  245. asio::detail::socket_addr_type* base,
  246. std::uint32_t address,
  247. std::uint16_t port);
  248. BOOST_COBALT_IO_DECL
  249. std::size_t tag_invoke(make_endpoint_tag<AF_INET>,
  250. asio::detail::socket_addr_type* base,
  251. std::string_view address,
  252. std::uint16_t port);
  253. BOOST_COBALT_IO_DECL
  254. const ip_address_v4* tag_invoke(get_endpoint_tag<AF_INET>,
  255. protocol_type actual,
  256. const endpoint::addr_type * addr);
  257. struct ip_address_v6
  258. {
  259. std::uint16_t port() const {return boost::endian::big_to_native(in_.sin6_port);}
  260. std::array<std::uint8_t, 16u> addr() const
  261. {
  262. std::array<std::uint8_t, 16u> res;
  263. const auto & in = in_.sin6_addr.s6_addr;
  264. std::copy(std::begin(in), std::end(in), res.begin());
  265. return res;
  266. }
  267. BOOST_COBALT_IO_DECL boost::static_string<45> addr_str() const;
  268. private:
  269. union {
  270. asio::detail::sockaddr_storage_type addr_{};
  271. asio::detail::sockaddr_in6_type in_;
  272. };
  273. };
  274. BOOST_COBALT_IO_DECL
  275. std::size_t tag_invoke(make_endpoint_tag<AF_INET6>,
  276. asio::detail::socket_addr_type* base,
  277. std::span<std::uint8_t, 16> address,
  278. std::uint16_t port);
  279. BOOST_COBALT_IO_DECL
  280. std::size_t tag_invoke(make_endpoint_tag<AF_INET6>,
  281. asio::detail::socket_addr_type* base,
  282. std::string_view address,
  283. std::uint16_t port);
  284. BOOST_COBALT_IO_DECL
  285. const ip_address_v6* tag_invoke(get_endpoint_tag<AF_INET6>,
  286. protocol_type actual,
  287. const endpoint::addr_type * addr);
  288. struct BOOST_SYMBOL_VISIBLE ip_address
  289. {
  290. bool is_ipv6() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET6); }
  291. bool is_ipv4() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET); }
  292. std::uint16_t port() const {return boost::endian::big_to_native(addr_.ss_family == AF_INET ? in_.sin_port : in6_.sin6_port);}
  293. BOOST_COBALT_IO_DECL std::array<std::uint8_t, 16u> addr() const;
  294. BOOST_COBALT_IO_DECL boost::static_string<45> addr_str() const;
  295. private:
  296. union {
  297. asio::detail::sockaddr_storage_type addr_{};
  298. asio::detail::sockaddr_in4_type in_;
  299. asio::detail::sockaddr_in6_type in6_;
  300. };
  301. };
  302. BOOST_COBALT_IO_DECL
  303. std::size_t tag_invoke(make_endpoint_tag<AF_UNSPEC>,
  304. asio::detail::socket_addr_type* base,
  305. std::string_view address,
  306. std::uint16_t port);
  307. BOOST_COBALT_IO_DECL
  308. const ip_address* tag_invoke(get_endpoint_tag<AF_UNSPEC>,
  309. protocol_type actual,
  310. const endpoint::addr_type * addr);
  311. }
  312. #endif //BOOST_COBALT_IO_ENDPOINT_HPP