Kea 1.5.0
unix_domain_socket.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2018 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/bind.hpp>
12#include <boost/enable_shared_from_this.hpp>
13#include <iostream>
14using namespace boost::asio::local;
15
16namespace isc {
17namespace asiolink {
18
20class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> {
21public:
22
27 : socket_(io_service.get_io_service()) {
28 }
29
34 close();
35 }
36
45 void asyncConnect(const stream_protocol::endpoint& endpoint,
47
60 void connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
61 const boost::system::error_code& ec);
62
72 void asyncSend(const void* data, const size_t length,
73 const UnixDomainSocket::Handler& handler);
74
84 void doSend(const boost::asio::const_buffers_1& buffer,
85 const UnixDomainSocket::Handler& handler);
86
87
103 void sendHandler(const UnixDomainSocket::Handler& remote_handler,
104 const boost::asio::const_buffers_1& buffer,
105 const boost::system::error_code& ec,
106 size_t length);
107
117 void asyncReceive(void* data, const size_t length,
118 const UnixDomainSocket::Handler& handler);
119
128 void doReceive(const boost::asio::mutable_buffers_1& buffer,
129 const UnixDomainSocket::Handler& handler);
130
146 void receiveHandler(const UnixDomainSocket::Handler& remote_handler,
147 const boost::asio::mutable_buffers_1& buffer,
148 const boost::system::error_code& ec,
149 size_t length);
150
152 void shutdown();
153
155 void cancel();
156
158 void close();
159
161 stream_protocol::socket socket_;
162};
163
164void
165UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint,
166 const UnixDomainSocket::ConnectHandler& handler) {
167 auto local_handler = boost::bind(&UnixDomainSocketImpl::connectHandler, shared_from_this(),
168 handler, _1);
169 socket_.async_connect(endpoint, local_handler);
170}
171
172void
174 const boost::system::error_code& ec) {
175 // It was observed on Debian and Fedora that asynchronous connect may result
176 // in EINPROGRESS error. This doesn't really indicate a problem with a
177 // connection. If we continue transmitting data over the socket it will
178 // succeed. So we suppress this error and return 'success' to the user's
179 // handler.
180 if (ec.value() == boost::asio::error::in_progress) {
181 remote_handler(boost::system::error_code());
182 } else {
183 remote_handler(ec);
184 }
185}
186
187void
188UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
189 const UnixDomainSocket::Handler& handler) {
190 doSend(boost::asio::buffer(data, length), handler);
191}
192
193void
194UnixDomainSocketImpl::doSend(const boost::asio::const_buffers_1& buffer,
195 const UnixDomainSocket::Handler& handler) {
196 auto local_handler = boost::bind(&UnixDomainSocketImpl::sendHandler, shared_from_this(),
197 handler, buffer, _1, _2);
198 socket_.async_send(buffer, local_handler);
199}
200
201void
203 const boost::asio::const_buffers_1& buffer,
204 const boost::system::error_code& ec,
205 size_t length) {
206 // The asynchronous send may return EWOULDBLOCK or EAGAIN on some
207 // operating systems. In this case, we simply retry hoping that it
208 // will succeed next time. The user's callback never sees these
209 // errors.
210 if ((ec.value() == boost::asio::error::would_block) ||
211 (ec.value() == boost::asio::error::try_again)) {
212 doSend(buffer, remote_handler);
213
214 } else {
215 remote_handler(ec, length);
216 }
217}
218
219void
220UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
221 const UnixDomainSocket::Handler& handler) {
222 doReceive(boost::asio::buffer(data, length), handler);
223}
224
225void
226UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffers_1& buffer,
227 const UnixDomainSocket::Handler& handler) {
228 auto local_handler = boost::bind(&UnixDomainSocketImpl::receiveHandler, shared_from_this(),
229 handler, buffer, _1, _2);
230 socket_.async_receive(buffer, 0, local_handler);
231}
232
233void
235 const boost::asio::mutable_buffers_1& buffer,
236 const boost::system::error_code& ec,
237 size_t length) {
238 // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some
239 // operating systems. In this case, we simply retry hoping that it
240 // will succeed next time. The user's callback never sees these
241 // errors.
242 if ((ec.value() == boost::asio::error::would_block) ||
243 (ec.value() == boost::asio::error::try_again)) {
244 doReceive(buffer, remote_handler);
245
246 } else {
247 remote_handler(ec, length);
248 }
249}
250
251void
253 boost::system::error_code ec;
254 static_cast<void>(socket_.shutdown(stream_protocol::socket::shutdown_both, ec));
255 if (ec) {
256 isc_throw(UnixDomainSocketError, ec.message());
257 }
258}
259
260void
262 boost::system::error_code ec;
263 static_cast<void>(socket_.cancel(ec));
264 if (ec) {
265 isc_throw(UnixDomainSocketError, ec.message());
266 }
267}
268
269void
271 boost::system::error_code ec;
272 static_cast<void>(socket_.close(ec));
273 if (ec) {
274 isc_throw(UnixDomainSocketError, ec.message());
275 }
276}
277
279 : impl_(new UnixDomainSocketImpl(io_service)) {
280}
281
282int
284#if BOOST_VERSION < 106600
285 return (impl_->socket_.native());
286#else
287 return (impl_->socket_.native_handle());
288#endif
289}
290
291int
293 return (0);
294}
295
296void
297UnixDomainSocket::connect(const std::string& path) {
298 boost::system::error_code ec;
299 impl_->socket_.connect(stream_protocol::endpoint(path.c_str()), ec);
300 if (ec) {
301 isc_throw(UnixDomainSocketError, ec.message());
302 }
303}
304
305void
306UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
307 impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
308}
309
310size_t
311UnixDomainSocket::write(const void* data, size_t length) {
312 boost::system::error_code ec;
313 size_t res = boost::asio::write(impl_->socket_,
314 boost::asio::buffer(data, length),
315 boost::asio::transfer_all(),
316 ec);
317 if (ec) {
318 isc_throw(UnixDomainSocketError, ec.message());
319 }
320 return (res);
321}
322
323void
324UnixDomainSocket::asyncSend(const void* data, const size_t length,
325 const Handler& handler) {
326 impl_->asyncSend(data, length, handler);
327}
328
329size_t
330UnixDomainSocket::receive(void* data, size_t length) {
331 boost::system::error_code ec;
332 size_t res = impl_->socket_.receive(boost::asio::buffer(data, length), 0, ec);
333 if (ec) {
334 isc_throw(UnixDomainSocketError, ec.message());
335 }
336 return (res);
337}
338
339void
340UnixDomainSocket::asyncReceive(void* data, const size_t length,
341 const Handler& handler) {
342 impl_->asyncReceive(data, length, handler);
343}
344
345void
347 impl_->shutdown();
348}
349
350void
352 impl_->cancel();
353}
354
355void
357 impl_->close();
358}
359
360boost::asio::local::stream_protocol::socket&
362 return (impl_->socket_);
363}
364
365} // end of namespace asiolink
366} // 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-dhcp-ddns.