Kea 2.7.0
unix_domain_socket.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2024 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_buffers_1& buffer,
87 const UnixDomainSocket::Handler& handler);
88
89
105 void sendHandler(const UnixDomainSocket::Handler& remote_handler,
106 const boost::asio::const_buffers_1& 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_buffers_1& buffer,
131 const UnixDomainSocket::Handler& handler);
132
148 void receiveHandler(const UnixDomainSocket::Handler& remote_handler,
149 const boost::asio::mutable_buffers_1& 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_buffers_1& 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_buffers_1& 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_buffers_1& 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_buffers_1& 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#if BOOST_VERSION < 106600
293 return (impl_->socket_.native());
294#else
295 return (impl_->socket_.native_handle());
296#endif
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.