Kea  2.1.7-git
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 
15 using namespace boost::asio::local;
16 namespace ph = std::placeholders;
17 
18 namespace isc {
19 namespace asiolink {
20 
22 class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> {
23 public:
24 
29  : socket_(io_service.get_io_service()) {
30  }
31 
36  close();
37  }
38 
47  void asyncConnect(const stream_protocol::endpoint& endpoint,
48  const UnixDomainSocket::ConnectHandler& handler);
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 
166 void
167 UnixDomainSocketImpl::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 
175 void
176 UnixDomainSocketImpl::connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
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 
190 void
191 UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
192  const UnixDomainSocket::Handler& handler) {
193  doSend(boost::asio::buffer(data, length), handler);
194 }
195 
196 void
197 UnixDomainSocketImpl::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 
205 void
206 UnixDomainSocketImpl::sendHandler(const UnixDomainSocket::Handler& remote_handler,
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 
223 void
224 UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
225  const UnixDomainSocket::Handler& handler) {
226  doReceive(boost::asio::buffer(data, length), handler);
227 }
228 
229 void
230 UnixDomainSocketImpl::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 
238 void
239 UnixDomainSocketImpl::receiveHandler(const UnixDomainSocket::Handler& remote_handler,
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 
256 void
257 UnixDomainSocketImpl::shutdown() {
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 
265 void
266 UnixDomainSocketImpl::cancel() {
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 
274 void
275 UnixDomainSocketImpl::close() {
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 
283 UnixDomainSocket::UnixDomainSocket(IOService& io_service)
284  : impl_(new UnixDomainSocketImpl(io_service)) {
285 }
286 
287 int
289 #if BOOST_VERSION < 106600
290  return (impl_->socket_.native());
291 #else
292  return (impl_->socket_.native_handle());
293 #endif
294 }
295 
296 int
298  return (0);
299 }
300 
301 void
302 UnixDomainSocket::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 
310 void
311 UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
312  impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
313 }
314 
315 size_t
316 UnixDomainSocket::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 
328 void
329 UnixDomainSocket::asyncSend(const void* data, const size_t length,
330  const Handler& handler) {
331  impl_->asyncSend(data, length, handler);
332 }
333 
334 size_t
335 UnixDomainSocket::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 
344 void
345 UnixDomainSocket::asyncReceive(void* data, const size_t length,
346  const Handler& handler) {
347  impl_->asyncReceive(data, length, handler);
348 }
349 
350 void
352  impl_->shutdown();
353 }
354 
355 void
357  impl_->cancel();
358 }
359 
360 void
362  impl_->close();
363 }
364 
365 boost::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.