TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
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 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
11 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
12 :
13 : /* Parameterized final service types for reactor backends.
14 :
15 : One service template per protocol (TCP, local stream, UDP, local
16 : datagram, TCP acceptor, local stream acceptor) because each abstract
17 : service base declares different virtual methods.
18 : */
19 :
20 : #include <boost/corosio/native/detail/reactor/reactor_socket_finals.hpp>
21 : #include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
22 : #include <boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp>
23 : #include <boost/corosio/detail/tcp_service.hpp>
24 : #include <boost/corosio/detail/udp_service.hpp>
25 : #include <boost/corosio/detail/local_stream_service.hpp>
26 : #include <boost/corosio/detail/local_datagram_service.hpp>
27 :
28 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
29 : #include <boost/corosio/native/detail/make_err.hpp>
30 :
31 : #include <system_error>
32 : #include <type_traits>
33 :
34 : #include <sys/socket.h>
35 : #include <unistd.h>
36 :
37 : namespace boost::corosio::detail {
38 :
39 : // ============================================================
40 : // Shared socket creation helpers
41 : // ============================================================
42 :
43 : template<class Traits, class SocketFinal>
44 : std::error_code
45 HIT 8135 : do_open_socket(
46 : SocketFinal* socket_impl,
47 : int family, int type, int protocol,
48 : bool is_ip) noexcept
49 : {
50 8135 : socket_impl->close_socket();
51 :
52 8135 : int fd = Traits::create_socket(family, type, protocol);
53 8135 : if (fd < 0)
54 MIS 0 : return make_err(errno);
55 :
56 : std::error_code ec = is_ip
57 HIT 8135 : ? Traits::configure_ip_socket(fd, family)
58 28 : : Traits::configure_local_socket(fd);
59 :
60 8135 : if (ec)
61 : {
62 MIS 0 : ::close(fd);
63 0 : return ec;
64 : }
65 :
66 HIT 8135 : socket_impl->init_and_register(fd);
67 8135 : return {};
68 : }
69 :
70 : template<class Traits, class SocketFinal>
71 : std::error_code
72 32 : do_assign_fd(
73 : SocketFinal* socket_impl,
74 : int fd,
75 : int expected_type) noexcept
76 : {
77 32 : socket_impl->close_socket();
78 :
79 : // Validate that fd is actually an AF_UNIX socket of the expected
80 : // type BEFORE applying any backend setup. Otherwise we could
81 : // adopt (or worse, mutate flags on) a foreign fd — e.g., an
82 : // AF_INET SOCK_STREAM fd passed into local_stream_socket::assign,
83 : // or a SOCK_DGRAM fd passed into a stream socket. The caller
84 : // retains ownership of fd on any error from this function, so
85 : // doing the checks first leaves a rejected fd unmodified.
86 : {
87 32 : sockaddr_storage st{};
88 32 : socklen_t st_len = sizeof(st);
89 32 : if (::getsockname(
90 32 : fd, reinterpret_cast<sockaddr*>(&st), &st_len) != 0)
91 MIS 0 : return make_err(errno);
92 HIT 32 : if (st.ss_family != AF_UNIX)
93 MIS 0 : return make_err(EAFNOSUPPORT);
94 :
95 HIT 32 : int sock_type = 0;
96 32 : socklen_t opt_len = sizeof(sock_type);
97 32 : if (::getsockopt(
98 32 : fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) != 0)
99 MIS 0 : return make_err(errno);
100 HIT 32 : if (sock_type != expected_type)
101 MIS 0 : return make_err(EPROTOTYPE);
102 : }
103 :
104 : // Apply backend fd setup (O_NONBLOCK, FD_CLOEXEC, SO_NOSIGPIPE on kqueue,
105 : // FD_SETSIZE validation on select). On failure, the caller retains
106 : // ownership of fd — assign_socket reports the error and assign() throws
107 : // without closing.
108 HIT 32 : std::error_code ec = Traits::configure_local_socket(fd);
109 32 : if (ec)
110 MIS 0 : return ec;
111 :
112 HIT 32 : socket_impl->init_and_register(fd);
113 :
114 : // Best-effort: refresh endpoint caches so local_endpoint() and
115 : // remote_endpoint() reflect the actual fd state. Failures (e.g.
116 : // ENOTCONN from getpeername on an unconnected socket) are ignored;
117 : // for unnamed socketpair() fds the queries succeed but yield empty
118 : // endpoints, which is the correct cached state.
119 : using endpoint_type = std::remove_cvref_t<
120 : decltype(socket_impl->local_endpoint())>;
121 :
122 32 : endpoint_type local_ep{};
123 32 : sockaddr_storage local_storage{};
124 32 : socklen_t local_len = sizeof(local_storage);
125 32 : if (::getsockname(
126 32 : fd, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
127 32 : local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{});
128 :
129 32 : endpoint_type remote_ep{};
130 32 : sockaddr_storage peer_storage{};
131 32 : socklen_t peer_len = sizeof(peer_storage);
132 32 : if (::getpeername(
133 32 : fd, reinterpret_cast<sockaddr*>(&peer_storage), &peer_len) == 0)
134 32 : remote_ep = from_sockaddr_as(peer_storage, peer_len, endpoint_type{});
135 :
136 32 : socket_impl->set_endpoints(local_ep, remote_ep);
137 :
138 32 : return {};
139 : }
140 :
141 : template<class Traits, class AccFinal>
142 : std::error_code
143 158 : do_open_acceptor(
144 : AccFinal* acc_impl,
145 : int family, int type, int protocol,
146 : bool is_ip) noexcept
147 : {
148 158 : acc_impl->close_socket();
149 :
150 158 : int fd = Traits::create_socket(family, type, protocol);
151 158 : if (fd < 0)
152 MIS 0 : return make_err(errno);
153 :
154 : std::error_code ec = is_ip
155 HIT 158 : ? Traits::configure_ip_acceptor(fd, family)
156 12 : : Traits::configure_local_socket(fd);
157 :
158 158 : if (ec)
159 : {
160 MIS 0 : ::close(fd);
161 0 : return ec;
162 : }
163 :
164 HIT 158 : acc_impl->init_acceptor_fd(fd);
165 158 : return {};
166 : }
167 :
168 : // ============================================================
169 : // TCP service
170 : // ============================================================
171 :
172 : template<class Traits, class SocketFinal>
173 : class reactor_tcp_service_final final
174 : : public reactor_socket_service<
175 : reactor_tcp_service_final<Traits, SocketFinal>,
176 : tcp_service,
177 : typename Traits::scheduler_type,
178 : SocketFinal>
179 : {
180 : using base_service = reactor_socket_service<
181 : reactor_tcp_service_final, tcp_service,
182 : typename Traits::scheduler_type, SocketFinal>;
183 : friend base_service;
184 :
185 : public:
186 591 : explicit reactor_tcp_service_final(capy::execution_context& ctx)
187 591 : : base_service(ctx) {}
188 :
189 8023 : std::error_code open_socket(
190 : tcp_socket::implementation& impl,
191 : int family, int type, int protocol) override
192 : {
193 8023 : return do_open_socket<Traits>(
194 : static_cast<SocketFinal*>(&impl),
195 8023 : family, type, protocol, true);
196 : }
197 :
198 12 : std::error_code bind_socket(
199 : tcp_socket::implementation& impl, endpoint ep) override
200 : {
201 12 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
202 : }
203 :
204 MIS 0 : void pre_shutdown(SocketFinal* impl) noexcept
205 : {
206 0 : impl->hook_.pre_shutdown(impl->native_handle());
207 0 : }
208 :
209 HIT 24038 : void pre_destroy(SocketFinal* impl) noexcept
210 : {
211 24038 : impl->hook_.pre_destroy(impl->native_handle());
212 24038 : }
213 : };
214 :
215 : // ============================================================
216 : // Local stream service
217 : // ============================================================
218 :
219 : template<class Traits, class SocketFinal>
220 : class reactor_local_stream_service_final final
221 : : public reactor_socket_service<
222 : reactor_local_stream_service_final<Traits, SocketFinal>,
223 : local_stream_service,
224 : typename Traits::scheduler_type,
225 : SocketFinal>
226 : {
227 : using base_service = reactor_socket_service<
228 : reactor_local_stream_service_final, local_stream_service,
229 : typename Traits::scheduler_type, SocketFinal>;
230 : friend base_service;
231 :
232 : public:
233 591 : explicit reactor_local_stream_service_final(capy::execution_context& ctx)
234 591 : : base_service(ctx) {}
235 :
236 8 : std::error_code open_socket(
237 : local_stream_socket::implementation& impl,
238 : int family, int type, int protocol) override
239 : {
240 8 : return do_open_socket<Traits>(
241 : static_cast<SocketFinal*>(&impl),
242 8 : family, type, protocol, false);
243 : }
244 :
245 16 : std::error_code assign_socket(
246 : local_stream_socket::implementation& impl, int fd) override
247 : {
248 16 : return do_assign_fd<Traits>(
249 16 : static_cast<SocketFinal*>(&impl), fd, SOCK_STREAM);
250 : }
251 : };
252 :
253 : // ============================================================
254 : // UDP service
255 : // ============================================================
256 :
257 : template<class Traits, class SocketFinal>
258 : class reactor_udp_service_final final
259 : : public reactor_socket_service<
260 : reactor_udp_service_final<Traits, SocketFinal>,
261 : udp_service,
262 : typename Traits::scheduler_type,
263 : SocketFinal>
264 : {
265 : using base_service = reactor_socket_service<
266 : reactor_udp_service_final, udp_service,
267 : typename Traits::scheduler_type, SocketFinal>;
268 : friend base_service;
269 :
270 : public:
271 591 : explicit reactor_udp_service_final(capy::execution_context& ctx)
272 591 : : base_service(ctx) {}
273 :
274 84 : std::error_code open_datagram_socket(
275 : udp_socket::implementation& impl,
276 : int family, int type, int protocol) override
277 : {
278 84 : return do_open_socket<Traits>(
279 : static_cast<SocketFinal*>(&impl),
280 84 : family, type, protocol, true);
281 : }
282 :
283 52 : std::error_code bind_datagram(
284 : udp_socket::implementation& impl, endpoint ep) override
285 : {
286 52 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
287 : }
288 : };
289 :
290 : // ============================================================
291 : // Local datagram service
292 : // ============================================================
293 :
294 : template<class Traits, class SocketFinal>
295 : class reactor_local_dgram_service_final final
296 : : public reactor_socket_service<
297 : reactor_local_dgram_service_final<Traits, SocketFinal>,
298 : local_datagram_service,
299 : typename Traits::scheduler_type,
300 : SocketFinal>
301 : {
302 : using base_service = reactor_socket_service<
303 : reactor_local_dgram_service_final, local_datagram_service,
304 : typename Traits::scheduler_type, SocketFinal>;
305 : friend base_service;
306 :
307 : public:
308 591 : explicit reactor_local_dgram_service_final(capy::execution_context& ctx)
309 591 : : base_service(ctx) {}
310 :
311 20 : std::error_code open_socket(
312 : local_datagram_socket::implementation& impl,
313 : int family, int type, int protocol) override
314 : {
315 20 : return do_open_socket<Traits>(
316 : static_cast<SocketFinal*>(&impl),
317 20 : family, type, protocol, false);
318 : }
319 :
320 16 : std::error_code assign_socket(
321 : local_datagram_socket::implementation& impl, int fd) override
322 : {
323 16 : return do_assign_fd<Traits>(
324 16 : static_cast<SocketFinal*>(&impl), fd, SOCK_DGRAM);
325 : }
326 :
327 16 : std::error_code bind_socket(
328 : local_datagram_socket::implementation& impl,
329 : corosio::local_endpoint ep) override
330 : {
331 16 : return static_cast<SocketFinal*>(&impl)->do_bind(ep);
332 : }
333 : };
334 :
335 : // ============================================================
336 : // Acceptor service
337 : // ============================================================
338 :
339 : template<class Traits, class ServiceBase, class AccFinal,
340 : class StreamServiceFinal, class Endpoint>
341 : class reactor_acceptor_service_final final
342 : : public reactor_acceptor_service<
343 : reactor_acceptor_service_final<Traits, ServiceBase, AccFinal,
344 : StreamServiceFinal, Endpoint>,
345 : ServiceBase,
346 : typename Traits::scheduler_type,
347 : AccFinal,
348 : StreamServiceFinal>
349 : {
350 : using base_service = reactor_acceptor_service<
351 : reactor_acceptor_service_final,
352 : ServiceBase,
353 : typename Traits::scheduler_type,
354 : AccFinal,
355 : StreamServiceFinal>;
356 : friend base_service;
357 :
358 : public:
359 1182 : explicit reactor_acceptor_service_final(capy::execution_context& ctx)
360 1182 : : base_service(ctx)
361 : {
362 : // Look up the concrete stream service directly by its type.
363 : // Avoids dynamic_cast which can fail across template boundaries
364 : // on some platforms (FreeBSD clang RTTI/visibility).
365 1182 : this->stream_svc_ =
366 1182 : this->ctx_.template find_service<StreamServiceFinal>();
367 1182 : }
368 :
369 158 : std::error_code open_acceptor_socket(
370 : typename AccFinal::impl_base_type& impl,
371 : int family, int type, int protocol) override
372 : {
373 158 : return do_open_acceptor<Traits>(
374 : static_cast<AccFinal*>(&impl),
375 : family, type, protocol,
376 158 : std::is_same_v<Endpoint, endpoint>);
377 : }
378 :
379 156 : std::error_code bind_acceptor(
380 : typename AccFinal::impl_base_type& impl,
381 : Endpoint ep) override
382 : {
383 156 : return static_cast<AccFinal*>(&impl)->do_bind(ep);
384 : }
385 :
386 140 : std::error_code listen_acceptor(
387 : typename AccFinal::impl_base_type& impl,
388 : int backlog) override
389 : {
390 140 : return static_cast<AccFinal*>(&impl)->do_listen(backlog);
391 : }
392 : };
393 :
394 : } // namespace boost::corosio::detail
395 :
396 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
|