src/corosio/src/local_socket_pair.cpp

61.0% Lines (25/41) 100.0% List of functions (3/3)
local_socket_pair.cpp
f(x) Functions (3)
Line TLA Hits 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 #include <boost/corosio/local_socket_pair.hpp>
11 #include <boost/corosio/io_context.hpp>
12 #include <boost/corosio/detail/platform.hpp>
13
14 #if BOOST_COROSIO_POSIX
15
16 #include <stdexcept>
17 #include <system_error>
18 #include <utility>
19
20 #include <fcntl.h>
21 #include <sys/socket.h>
22 #include <unistd.h>
23
24 namespace boost::corosio {
25
26 namespace {
27
28 #ifndef SOCK_NONBLOCK
29 void
30 make_nonblock_cloexec(int fd)
31 {
32 int fl = ::fcntl(fd, F_GETFL, 0);
33 if (fl < 0)
34 throw std::system_error(
35 std::error_code(errno, std::system_category()),
36 "fcntl(F_GETFL)");
37 if (::fcntl(fd, F_SETFL, fl | O_NONBLOCK) < 0)
38 throw std::system_error(
39 std::error_code(errno, std::system_category()),
40 "fcntl(F_SETFL)");
41 if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
42 throw std::system_error(
43 std::error_code(errno, std::system_category()),
44 "fcntl(F_SETFD)");
45 }
46 #endif
47
48 void
49 14x create_pair(int type, int fds[2])
50 {
51 14x int flags = type;
52 #ifdef SOCK_NONBLOCK
53 14x flags |= SOCK_NONBLOCK | SOCK_CLOEXEC;
54 #endif
55 14x if (::socketpair(AF_UNIX, flags, 0, fds) != 0)
56 throw std::system_error(
57 std::error_code(errno, std::system_category()),
58 "socketpair");
59 #ifndef SOCK_NONBLOCK
60 try
61 {
62 make_nonblock_cloexec(fds[0]);
63 make_nonblock_cloexec(fds[1]);
64 }
65 catch (...)
66 {
67 ::close(fds[0]);
68 ::close(fds[1]);
69 throw;
70 }
71 #endif
72 14x }
73
74 } // namespace
75
76 std::pair<local_stream_socket, local_stream_socket>
77 8x make_local_stream_pair(io_context& ctx)
78 {
79 int fds[2];
80 8x create_pair(SOCK_STREAM, fds);
81
82 try
83 {
84 8x local_stream_socket s1(ctx);
85 8x local_stream_socket s2(ctx);
86
87 8x s1.assign(fds[0]);
88 8x fds[0] = -1;
89 8x s2.assign(fds[1]);
90 8x fds[1] = -1;
91
92 16x return {std::move(s1), std::move(s2)};
93 8x }
94 catch (...)
95 {
96 if (fds[0] >= 0)
97 ::close(fds[0]);
98 if (fds[1] >= 0)
99 ::close(fds[1]);
100 throw;
101 }
102 }
103
104 std::pair<local_datagram_socket, local_datagram_socket>
105 6x make_local_datagram_pair(io_context& ctx)
106 {
107 int fds[2];
108 6x create_pair(SOCK_DGRAM, fds);
109
110 try
111 {
112 6x local_datagram_socket s1(ctx);
113 6x local_datagram_socket s2(ctx);
114
115 6x s1.assign(fds[0]);
116 6x fds[0] = -1;
117 6x s2.assign(fds[1]);
118 6x fds[1] = -1;
119
120 12x return {std::move(s1), std::move(s2)};
121 6x }
122 catch (...)
123 {
124 if (fds[0] >= 0)
125 ::close(fds[0]);
126 if (fds[1] >= 0)
127 ::close(fds[1]);
128 throw;
129 }
130 }
131
132 } // namespace boost::corosio
133
134 #elif BOOST_COROSIO_HAS_IOCP
135
136 // Windows: emulate socketpair(AF_UNIX, SOCK_STREAM) via a temporary
137 // listener socket. Create a listening socket bound to a unique temp
138 // path, connect a client, accept the peer, then close the listener
139 // and remove the temp path.
140
141 #include <boost/corosio/io_context.hpp>
142 #include <boost/corosio/test/temp_path.hpp>
143 #include <boost/corosio/local_endpoint.hpp>
144 #include <boost/corosio/native/detail/endpoint_convert.hpp>
145
146 #ifndef WIN32_LEAN_AND_MEAN
147 #define WIN32_LEAN_AND_MEAN
148 #endif
149 #ifndef NOMINMAX
150 #define NOMINMAX
151 #endif
152 #include <WinSock2.h>
153
154 #include <system_error>
155 #include <utility>
156
157 namespace boost::corosio {
158
159 namespace {
160
161 std::error_code
162 make_wsa_error()
163 {
164 return std::error_code(::WSAGetLastError(), std::system_category());
165 }
166
167 } // namespace
168
169 std::pair<local_stream_socket, local_stream_socket>
170 make_local_stream_pair(io_context& ctx)
171 {
172 // Create a unique temp directory + path for the listener.
173 test::temp_socket_dir tmp;
174 auto path = tmp.path();
175 local_endpoint ep(path);
176
177 // Build the sockaddr.
178 sockaddr_storage storage{};
179 socklen_t addrlen = detail::to_sockaddr(ep, storage);
180
181 // Create listener socket.
182 SOCKET listener = ::socket(AF_UNIX, SOCK_STREAM, 0);
183 if (listener == INVALID_SOCKET)
184 throw std::system_error(make_wsa_error(), "socket(listener)");
185
186 // bind + listen
187 if (::bind(listener, reinterpret_cast<sockaddr*>(&storage),
188 static_cast<int>(addrlen)) == SOCKET_ERROR)
189 {
190 auto ec = make_wsa_error();
191 ::closesocket(listener);
192 throw std::system_error(ec, "bind(listener)");
193 }
194
195 if (::listen(listener, 1) == SOCKET_ERROR)
196 {
197 auto ec = make_wsa_error();
198 ::closesocket(listener);
199 throw std::system_error(ec, "listen");
200 }
201
202 // Create client socket and connect.
203 SOCKET client = ::socket(AF_UNIX, SOCK_STREAM, 0);
204 if (client == INVALID_SOCKET)
205 {
206 auto ec = make_wsa_error();
207 ::closesocket(listener);
208 throw std::system_error(ec, "socket(client)");
209 }
210
211 if (::connect(client, reinterpret_cast<sockaddr*>(&storage),
212 static_cast<int>(addrlen)) == SOCKET_ERROR)
213 {
214 auto ec = make_wsa_error();
215 ::closesocket(client);
216 ::closesocket(listener);
217 throw std::system_error(ec, "connect");
218 }
219
220 // Accept the peer.
221 SOCKET server = ::accept(listener, nullptr, nullptr);
222 if (server == INVALID_SOCKET)
223 {
224 auto ec = make_wsa_error();
225 ::closesocket(client);
226 ::closesocket(listener);
227 throw std::system_error(ec, "accept");
228 }
229
230 // Listener is no longer needed.
231 ::closesocket(listener);
232
233 // Wrap the raw SOCKETs into local_stream_socket objects.
234 // assign() registers the handle with the IOCP port.
235 try
236 {
237 local_stream_socket s1(ctx);
238 local_stream_socket s2(ctx);
239
240 s1.assign(static_cast<native_handle_type>(client));
241 client = INVALID_SOCKET;
242 s2.assign(static_cast<native_handle_type>(server));
243 server = INVALID_SOCKET;
244
245 return {std::move(s1), std::move(s2)};
246 }
247 catch (...)
248 {
249 if (client != INVALID_SOCKET)
250 ::closesocket(client);
251 if (server != INVALID_SOCKET)
252 ::closesocket(server);
253 throw;
254 }
255 }
256
257 } // namespace boost::corosio
258
259 #endif // BOOST_COROSIO_HAS_IOCP
260