Kea 2.2.0
unix_domain_socket.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2020 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 : socket_(io_service.get_io_service()) {
30 }
31
36 close();
37 }
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
163 stream_protocol::socket socket_;
164};
165
166void
167UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint,
168 const UnixDomainSocket::ConnectHandler& handler) {
169 auto local_handler = std::bind(&UnixDomainSocketImpl::connectHandler,
170 shared_from_this(),
171 handler, ph::_1);
172 socket_.async_connect(endpoint, local_handler);
173}
174
175void
177 const boost::system::error_code& ec) {
178 // It was observed on Debian and Fedora that asynchronous connect may result
179 // in EINPROGRESS error. This doesn't really indicate a problem with a
180 // connection. If we continue transmitting data over the socket it will
181 // succeed. So we suppress this error and return 'success' to the user's
182 // handler.
183 if (ec.value() == boost::asio::error::in_progress) {
184 remote_handler(boost::system::error_code());
185 } else {
186 remote_handler(ec);
187 }
188}
189
190void
191UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
192 const UnixDomainSocket::Handler& handler) {
193 doSend(boost::asio::buffer(data, length), handler);
194}
195
196void
197UnixDomainSocketImpl::doSend(const boost::asio::const_buffers_1& buffer,
198 const UnixDomainSocket::Handler& handler) {
199 auto local_handler = std::bind(&UnixDomainSocketImpl::sendHandler,
200 shared_from_this(),
201 handler, buffer, ph::_1, ph::_2);
202 socket_.async_send(buffer, local_handler);
203}
204
205void
207 const boost::asio::const_buffers_1& buffer,
208 const boost::system::error_code& ec,
209 size_t length) {
210 // The asynchronous send may return EWOULDBLOCK or EAGAIN on some
211 // operating systems. In this case, we simply retry hoping that it
212 // will succeed next time. The user's callback never sees these
213 // errors.
214 if ((ec.value() == boost::asio::error::would_block) ||
215 (ec.value() == boost::asio::error::try_again)) {
216 doSend(buffer, remote_handler);
217
218 } else {
219 remote_handler(ec, length);
220 }
221}
222
223void
224UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
225 const UnixDomainSocket::Handler& handler) {
226 doReceive(boost::asio::buffer(data, length), handler);
227}
228
229void
230UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffers_1& buffer,
231 const UnixDomainSocket::Handler& handler) {
232 auto local_handler = std::bind(&UnixDomainSocketImpl::receiveHandler,
233 shared_from_this(),
234 handler, buffer, ph::_1, ph::_2);
235 socket_.async_receive(buffer, 0, local_handler);
236}
237
238void
240 const boost::asio::mutable_buffers_1& buffer,
241 const boost::system::error_code& ec,
242 size_t length) {
243 // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some
244 // operating systems. In this case, we simply retry hoping that it
245 // will succeed next time. The user's callback never sees these
246 // errors.
247 if ((ec.value() == boost::asio::error::would_block) ||
248 (ec.value() == boost::asio::error::try_again)) {
249 doReceive(buffer, remote_handler);
250
251 } else {
252 remote_handler(ec, length);
253 }
254}
255
256void
258 boost::system::error_code ec;
259 static_cast<void>(socket_.shutdown(stream_protocol::socket::shutdown_both, ec));
260 if (ec) {
261 isc_throw(UnixDomainSocketError, ec.message());
262 }
263}
264
265void
267 boost::system::error_code ec;
268 static_cast<void>(socket_.cancel(ec));
269 if (ec) {
270 isc_throw(UnixDomainSocketError, ec.message());
271 }
272}
273
274void
276 boost::system::error_code ec;
277 static_cast<void>(socket_.close(ec));
278 if (ec) {
279 isc_throw(UnixDomainSocketError, ec.message());
280 }
281}
282
284 : impl_(new UnixDomainSocketImpl(io_service)) {
285}
286
287int
289#if BOOST_VERSION < 106600
290 return (impl_->socket_.native());
291#else
292 return (impl_->socket_.native_handle());
293#endif
294}
295
296int
298 return (0);
299}
300
301void
302UnixDomainSocket::connect(const std::string& path) {
303 boost::system::error_code ec;
304 impl_->socket_.connect(stream_protocol::endpoint(path.c_str()), ec);
305 if (ec) {
306 isc_throw(UnixDomainSocketError, ec.message());
307 }
308}
309
310void
311UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
312 impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
313}
314
315size_t
316UnixDomainSocket::write(const void* data, size_t length) {
317 boost::system::error_code ec;
318 size_t res = boost::asio::write(impl_->socket_,
319 boost::asio::buffer(data, length),
320 boost::asio::transfer_all(),
321 ec);
322 if (ec) {
323 isc_throw(UnixDomainSocketError, ec.message());
324 }
325 return (res);
326}
327
328void
329UnixDomainSocket::asyncSend(const void* data, const size_t length,
330 const Handler& handler) {
331 impl_->asyncSend(data, length, handler);
332}
333
334size_t
335UnixDomainSocket::receive(void* data, size_t length) {
336 boost::system::error_code ec;
337 size_t res = impl_->socket_.receive(boost::asio::buffer(data, length), 0, ec);
338 if (ec) {
339 isc_throw(UnixDomainSocketError, ec.message());
340 }
341 return (res);
342}
343
344void
345UnixDomainSocket::asyncReceive(void* data, const size_t length,
346 const Handler& handler) {
347 impl_->asyncReceive(data, length, handler);
348}
349
350void
352 impl_->shutdown();
353}
354
355void
357 impl_->cancel();
358}
359
360void
362 impl_->close();
363}
364
365boost::asio::local::stream_protocol::socket&
367 return (impl_->socket_);
368}
369
370} // end of namespace asiolink
371} // 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.