Kea 2.7.7
unix_domain_socket.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11#include <boost/enable_shared_from_this.hpp>
12#include <functional>
13#include <iostream>
14
15using namespace boost::asio::local;
16namespace ph = std::placeholders;
17
18namespace isc {
19namespace asiolink {
20
22class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> {
23public:
24
29 : io_service_(io_service), socket_(io_service_->getInternalIOService()) {
30 }
31
38
47 void asyncConnect(const stream_protocol::endpoint& endpoint,
49
62 void connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
63 const boost::system::error_code& ec);
64
74 void asyncSend(const void* data, const size_t length,
75 const UnixDomainSocket::Handler& handler);
76
86 void doSend(const boost::asio::const_buffer& buffer,
87 const UnixDomainSocket::Handler& handler);
88
89
105 void sendHandler(const UnixDomainSocket::Handler& remote_handler,
106 const boost::asio::const_buffer& buffer,
107 const boost::system::error_code& ec,
108 size_t length);
109
119 void asyncReceive(void* data, const size_t length,
120 const UnixDomainSocket::Handler& handler);
121
130 void doReceive(const boost::asio::mutable_buffer& buffer,
131 const UnixDomainSocket::Handler& handler);
132
148 void receiveHandler(const UnixDomainSocket::Handler& remote_handler,
149 const boost::asio::mutable_buffer& buffer,
150 const boost::system::error_code& ec,
151 size_t length);
152
154 void shutdown();
155
157 void cancel();
158
160 void close();
161
164
166 stream_protocol::socket socket_;
167};
168
169void
170UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint,
171 const UnixDomainSocket::ConnectHandler& handler) {
172 auto local_handler = std::bind(&UnixDomainSocketImpl::connectHandler,
173 shared_from_this(),
174 handler, ph::_1);
175 socket_.async_connect(endpoint, local_handler);
176}
177
178void
180 const boost::system::error_code& ec) {
181 // It was observed on Debian and Fedora that asynchronous connect may result
182 // in EINPROGRESS error. This doesn't really indicate a problem with a
183 // connection. If we continue transmitting data over the socket it will
184 // succeed. So we suppress this error and return 'success' to the user's
185 // handler.
186 if (ec.value() == boost::asio::error::in_progress) {
187 remote_handler(boost::system::error_code());
188 } else {
189 remote_handler(ec);
190 }
191}
192
193void
194UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
195 const UnixDomainSocket::Handler& handler) {
196 doSend(boost::asio::buffer(data, length), handler);
197}
198
199void
200UnixDomainSocketImpl::doSend(const boost::asio::const_buffer& buffer,
201 const UnixDomainSocket::Handler& handler) {
202 auto local_handler = std::bind(&UnixDomainSocketImpl::sendHandler,
203 shared_from_this(),
204 handler, buffer, ph::_1, ph::_2);
205 socket_.async_send(buffer, local_handler);
206}
207
208void
210 const boost::asio::const_buffer& buffer,
211 const boost::system::error_code& ec,
212 size_t length) {
213 // The asynchronous send may return EWOULDBLOCK or EAGAIN on some
214 // operating systems. In this case, we simply retry hoping that it
215 // will succeed next time. The user's callback never sees these
216 // errors.
217 if ((ec.value() == boost::asio::error::would_block) ||
218 (ec.value() == boost::asio::error::try_again)) {
219 doSend(buffer, remote_handler);
220
221 } else {
222 remote_handler(ec, length);
223 }
224}
225
226void
227UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
228 const UnixDomainSocket::Handler& handler) {
229 doReceive(boost::asio::buffer(data, length), handler);
230}
231
232void
233UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffer& buffer,
234 const UnixDomainSocket::Handler& handler) {
235 auto local_handler = std::bind(&UnixDomainSocketImpl::receiveHandler,
236 shared_from_this(),
237 handler, buffer, ph::_1, ph::_2);
238 socket_.async_receive(buffer, 0, local_handler);
239}
240
241void
243 const boost::asio::mutable_buffer& buffer,
244 const boost::system::error_code& ec,
245 size_t length) {
246 // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some
247 // operating systems. In this case, we simply retry hoping that it
248 // will succeed next time. The user's callback never sees these
249 // errors.
250 if ((ec.value() == boost::asio::error::would_block) ||
251 (ec.value() == boost::asio::error::try_again)) {
252 doReceive(buffer, remote_handler);
253
254 } else {
255 remote_handler(ec, length);
256 }
257}
258
259void
261 boost::system::error_code ec;
262 static_cast<void>(socket_.shutdown(stream_protocol::socket::shutdown_both, ec));
263 if (ec) {
264 isc_throw(UnixDomainSocketError, ec.message());
265 }
266}
267
268void
270 boost::system::error_code ec;
271 static_cast<void>(socket_.cancel(ec));
272 if (ec) {
273 isc_throw(UnixDomainSocketError, ec.message());
274 }
275}
276
277void
279 boost::system::error_code ec;
280 static_cast<void>(socket_.close(ec));
281 if (ec) {
282 isc_throw(UnixDomainSocketError, ec.message());
283 }
284}
285
287 : impl_(new UnixDomainSocketImpl(io_service)) {
288}
289
290int
292 return (impl_->socket_.native_handle());
293}
294
295int
297 return (0);
298}
299
300void
301UnixDomainSocket::connect(const std::string& path) {
302 boost::system::error_code ec;
303 impl_->socket_.connect(stream_protocol::endpoint(path.c_str()), ec);
304 if (ec) {
305 isc_throw(UnixDomainSocketError, ec.message());
306 }
307}
308
309void
310UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
311 impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
312}
313
314size_t
315UnixDomainSocket::write(const void* data, size_t length) {
316 boost::system::error_code ec;
317 size_t res = boost::asio::write(impl_->socket_,
318 boost::asio::buffer(data, length),
319 boost::asio::transfer_all(),
320 ec);
321 if (ec) {
322 isc_throw(UnixDomainSocketError, ec.message());
323 }
324 return (res);
325}
326
327void
328UnixDomainSocket::asyncSend(const void* data, const size_t length,
329 const Handler& handler) {
330 impl_->asyncSend(data, length, handler);
331}
332
333size_t
334UnixDomainSocket::receive(void* data, size_t length) {
335 boost::system::error_code ec;
336 size_t res = impl_->socket_.receive(boost::asio::buffer(data, length), 0, ec);
337 if (ec) {
338 isc_throw(UnixDomainSocketError, ec.message());
339 }
340 return (res);
341}
342
343void
344UnixDomainSocket::asyncReceive(void* data, const size_t length,
345 const Handler& handler) {
346 impl_->asyncReceive(data, length, handler);
347}
348
349void
351 impl_->shutdown();
352}
353
354void
356 impl_->cancel();
357}
358
359void
361 impl_->close();
362}
363
364boost::asio::local::stream_protocol::socket&
366 return (impl_->socket_);
367}
368
369} // end of namespace asiolink
370} // end of namespace isc
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Defines the logger used by the top-level component of kea-lfc.