Kea 3.1.10
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
36 try {
37 close();
38 } catch (...) {
39 // Catch and ignore all exceptions.
40 }
41 }
42
51 void asyncConnect(const stream_protocol::endpoint& endpoint,
53
66 void connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
67 const boost::system::error_code& ec);
68
78 void asyncSend(const void* data, const size_t length,
79 const UnixDomainSocket::Handler& handler);
80
90 void doSend(const boost::asio::const_buffer& buffer,
91 const UnixDomainSocket::Handler& handler);
92
93
109 void sendHandler(const UnixDomainSocket::Handler& remote_handler,
110 const boost::asio::const_buffer& buffer,
111 const boost::system::error_code& ec,
112 size_t length);
113
123 void asyncReceive(void* data, const size_t length,
124 const UnixDomainSocket::Handler& handler);
125
134 void doReceive(const boost::asio::mutable_buffer& buffer,
135 const UnixDomainSocket::Handler& handler);
136
152 void receiveHandler(const UnixDomainSocket::Handler& remote_handler,
153 const boost::asio::mutable_buffer& buffer,
154 const boost::system::error_code& ec,
155 size_t length);
156
158 void shutdown();
159
161 void cancel();
162
164 void close();
165
168
170 stream_protocol::socket socket_;
171};
172
173void
174UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint,
175 const UnixDomainSocket::ConnectHandler& handler) {
176 auto local_handler = std::bind(&UnixDomainSocketImpl::connectHandler,
177 shared_from_this(),
178 handler, ph::_1);
179 socket_.async_connect(endpoint, local_handler);
180}
181
182void
184 const boost::system::error_code& ec) {
185 // It was observed on Debian and Fedora that asynchronous connect may result
186 // in EINPROGRESS error. This doesn't really indicate a problem with a
187 // connection. If we continue transmitting data over the socket it will
188 // succeed. So we suppress this error and return 'success' to the user's
189 // handler.
190 if (ec.value() == boost::asio::error::in_progress) {
191 remote_handler(boost::system::error_code());
192 } else {
193 remote_handler(ec);
194 }
195}
196
197void
198UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
199 const UnixDomainSocket::Handler& handler) {
200 doSend(boost::asio::buffer(data, length), handler);
201}
202
203void
204UnixDomainSocketImpl::doSend(const boost::asio::const_buffer& buffer,
205 const UnixDomainSocket::Handler& handler) {
206 auto local_handler = std::bind(&UnixDomainSocketImpl::sendHandler,
207 shared_from_this(),
208 handler, buffer, ph::_1, ph::_2);
209 socket_.async_send(buffer, local_handler);
210}
211
212void
214 const boost::asio::const_buffer& buffer,
215 const boost::system::error_code& ec,
216 size_t length) {
217 // The asynchronous send may return EWOULDBLOCK or EAGAIN on some
218 // operating systems. In this case, we simply retry hoping that it
219 // will succeed next time. The user's callback never sees these
220 // errors.
221 if ((ec.value() == boost::asio::error::would_block) ||
222 (ec.value() == boost::asio::error::try_again)) {
223 doSend(buffer, remote_handler);
224
225 } else {
226 remote_handler(ec, length);
227 }
228}
229
230void
231UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
232 const UnixDomainSocket::Handler& handler) {
233 doReceive(boost::asio::buffer(data, length), handler);
234}
235
236void
237UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffer& buffer,
238 const UnixDomainSocket::Handler& handler) {
239 auto local_handler = std::bind(&UnixDomainSocketImpl::receiveHandler,
240 shared_from_this(),
241 handler, buffer, ph::_1, ph::_2);
242 socket_.async_receive(buffer, 0, local_handler);
243}
244
245void
247 const boost::asio::mutable_buffer& buffer,
248 const boost::system::error_code& ec,
249 size_t length) {
250 // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some
251 // operating systems. In this case, we simply retry hoping that it
252 // will succeed next time. The user's callback never sees these
253 // errors.
254 if ((ec.value() == boost::asio::error::would_block) ||
255 (ec.value() == boost::asio::error::try_again)) {
256 doReceive(buffer, remote_handler);
257
258 } else {
259 remote_handler(ec, length);
260 }
261}
262
263void
265 boost::system::error_code ec;
266 static_cast<void>(socket_.shutdown(stream_protocol::socket::shutdown_both, ec));
267 if (ec) {
268 isc_throw(UnixDomainSocketError, ec.message());
269 }
270}
271
272void
274 boost::system::error_code ec;
275 static_cast<void>(socket_.cancel(ec));
276 if (ec) {
277 isc_throw(UnixDomainSocketError, ec.message());
278 }
279}
280
281void
283 boost::system::error_code ec;
284 static_cast<void>(socket_.close(ec));
285 if (ec) {
286 isc_throw(UnixDomainSocketError, ec.message());
287 }
288}
289
291 : impl_(new UnixDomainSocketImpl(io_service)) {
292}
293
294int
296 return (impl_->socket_.native_handle());
297}
298
299int
301 return (0);
302}
303
304void
305UnixDomainSocket::connect(const std::string& path) {
306 boost::system::error_code ec;
307 impl_->socket_.connect(stream_protocol::endpoint(path.c_str()), ec);
308 if (ec) {
309 isc_throw(UnixDomainSocketError, ec.message());
310 }
311}
312
313void
314UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
315 impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
316}
317
318size_t
319UnixDomainSocket::write(const void* data, size_t length) {
320 boost::system::error_code ec;
321 size_t res = boost::asio::write(impl_->socket_,
322 boost::asio::buffer(data, length),
323 boost::asio::transfer_all(),
324 ec);
325 if (ec) {
326 isc_throw(UnixDomainSocketError, ec.message());
327 }
328 return (res);
329}
330
331void
332UnixDomainSocket::asyncSend(const void* data, const size_t length,
333 const Handler& handler) {
334 impl_->asyncSend(data, length, handler);
335}
336
337size_t
338UnixDomainSocket::receive(void* data, size_t length) {
339 boost::system::error_code ec;
340 size_t res = impl_->socket_.receive(boost::asio::buffer(data, length), 0, ec);
341 if (ec) {
342 isc_throw(UnixDomainSocketError, ec.message());
343 }
344 return (res);
345}
346
347void
348UnixDomainSocket::asyncReceive(void* data, const size_t length,
349 const Handler& handler) {
350 impl_->asyncReceive(data, length, handler);
351}
352
353void
355 impl_->shutdown();
356}
357
358void
360 impl_->cancel();
361}
362
363void
365 impl_->close();
366}
367
368boost::asio::local::stream_protocol::socket&
370 return (impl_->socket_);
371}
372
373} // end of namespace asiolink
374} // 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.