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>
14 using namespace boost::asio::local;
15 
16 namespace isc {
17 namespace asiolink {
18 
20 class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> {
21 public:
22 
27  : socket_(io_service.get_io_service()) {
28  }
29 
34  close();
35  }
36 
45  void asyncConnect(const stream_protocol::endpoint& endpoint,
46  const UnixDomainSocket::ConnectHandler& handler);
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 
164 void
165 UnixDomainSocketImpl::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 
172 void
173 UnixDomainSocketImpl::connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
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 
187 void
188 UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
189  const UnixDomainSocket::Handler& handler) {
190  doSend(boost::asio::buffer(data, length), handler);
191 }
192 
193 void
194 UnixDomainSocketImpl::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 
201 void
202 UnixDomainSocketImpl::sendHandler(const UnixDomainSocket::Handler& remote_handler,
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 
219 void
220 UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
221  const UnixDomainSocket::Handler& handler) {
222  doReceive(boost::asio::buffer(data, length), handler);
223 }
224 
225 void
226 UnixDomainSocketImpl::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 
233 void
234 UnixDomainSocketImpl::receiveHandler(const UnixDomainSocket::Handler& remote_handler,
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 
251 void
252 UnixDomainSocketImpl::shutdown() {
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 
260 void
261 UnixDomainSocketImpl::cancel() {
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 
269 void
270 UnixDomainSocketImpl::close() {
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 
278 UnixDomainSocket::UnixDomainSocket(IOService& io_service)
279  : impl_(new UnixDomainSocketImpl(io_service)) {
280 }
281 
282 int
284 #if BOOST_VERSION < 106600
285  return (impl_->socket_.native());
286 #else
287  return (impl_->socket_.native_handle());
288 #endif
289 }
290 
291 int
293  return (0);
294 }
295 
296 void
297 UnixDomainSocket::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 
305 void
306 UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
307  impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
308 }
309 
310 size_t
311 UnixDomainSocket::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 
323 void
324 UnixDomainSocket::asyncSend(const void* data, const size_t length,
325  const Handler& handler) {
326  impl_->asyncSend(data, length, handler);
327 }
328 
329 size_t
330 UnixDomainSocket::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 
339 void
340 UnixDomainSocket::asyncReceive(void* data, const size_t length,
341  const Handler& handler) {
342  impl_->asyncReceive(data, length, handler);
343 }
344 
345 void
347  impl_->shutdown();
348 }
349 
350 void
352  impl_->cancel();
353 }
354 
355 void
357  impl_->close();
358 }
359 
360 boost::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.