Kea  1.9.9-git
udp_socket.h
Go to the documentation of this file.
1 // Copyright (C) 2011-2021 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 #ifndef UDP_SOCKET_H
8 #define UDP_SOCKET_H 1
9 
10 #ifndef BOOST_ASIO_HPP
11 #error "asio.hpp must be included before including this, see asiolink.h as to why"
12 #endif
13 
14 #include <netinet/in.h>
15 #include <sys/socket.h>
16 #include <unistd.h> // for some IPC/network system calls
17 
18 #include <cstddef>
19 
21 #include <asiolink/io_endpoint.h>
22 #include <asiolink/io_service.h>
23 #include <asiolink/udp_endpoint.h>
24 
25 #include <exceptions/isc_assert.h>
26 
27 namespace isc {
28 namespace asiolink {
29 
34 template <typename C>
35 class UDPSocket : public IOAsioSocket<C> {
36 private:
38  UDPSocket(const UDPSocket&);
39  UDPSocket& operator=(const UDPSocket&);
40 
41 public:
42  enum {
43  MIN_SIZE = 4096 // Minimum send and receive size
44  };
45 
51  UDPSocket(boost::asio::ip::udp::socket& socket);
52 
59  UDPSocket(IOService& service);
60 
62  virtual ~UDPSocket();
63 
65  virtual int getNative() const {
66 #if BOOST_VERSION < 106600
67  return (socket_.native());
68 #else
69  return (socket_.native_handle());
70 #endif
71  }
72 
74  virtual int getProtocol() const {
75  return (IPPROTO_UDP);
76  }
77 
81  virtual bool isOpenSynchronous() const {
82  return true;
83  }
84 
93  virtual void open(const IOEndpoint* endpoint, C& callback);
94 
105  virtual void asyncSend(const void* data, size_t length,
106  const IOEndpoint* endpoint, C& callback);
107 
119  virtual void asyncReceive(void* data, size_t length, size_t offset,
120  IOEndpoint* endpoint, C& callback);
121 
137  virtual bool processReceivedData(const void* staging, size_t length,
138  size_t& cumulative, size_t& offset,
139  size_t& expected,
140  isc::util::OutputBufferPtr& outbuff);
141 
143  virtual void cancel();
144 
146  virtual void close();
147 
148 
149 private:
150  // Two variables to hold the socket - a socket and a pointer to it. This
151  // handles the case where a socket is passed to the UDPSocket on
152  // construction, or where it is asked to manage its own socket.
153 
155  std::unique_ptr<boost::asio::ip::udp::socket> socket_ptr_;
156 
157  // Socket
158  boost::asio::ip::udp::socket& socket_;
159 
160  // True when socket is open
161  bool isopen_;
162 };
163 
164 // Constructor - caller manages socket
165 
166 template <typename C>
167 UDPSocket<C>::UDPSocket(boost::asio::ip::udp::socket& socket) :
168  socket_ptr_(), socket_(socket), isopen_(true)
169 {
170 }
171 
172 // Constructor - create socket on the fly
173 
174 template <typename C>
176  socket_ptr_(new boost::asio::ip::udp::socket(service.get_io_service())),
177  socket_(*socket_ptr_), isopen_(false)
178 {
179 }
180 
181 // Destructor.
182 
183 template <typename C>
185 {
186 }
187 
188 // Open the socket.
189 
190 template <typename C> void
191 UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
192 
193  // Ignore opens on already-open socket. (Don't throw a failure because
194  // of uncertainties as to what precedes when using asynchronous I/O.)
195  // It also allows us a treat a passed-in socket in exactly the same way as
196  // a self-managed socket (in that we can call the open() and close() methods
197  // of this class).
198  if (!isopen_) {
199  if (endpoint->getFamily() == AF_INET) {
200  socket_.open(boost::asio::ip::udp::v4());
201  }
202  else {
203  socket_.open(boost::asio::ip::udp::v6());
204  }
205  isopen_ = true;
206 
207  // Ensure it can send and receive at least 4K buffers.
208  boost::asio::ip::udp::socket::send_buffer_size snd_size;
209  socket_.get_option(snd_size);
210  if (snd_size.value() < MIN_SIZE) {
211  snd_size = MIN_SIZE;
212  socket_.set_option(snd_size);
213  }
214 
215  boost::asio::ip::udp::socket::receive_buffer_size rcv_size;
216  socket_.get_option(rcv_size);
217  if (rcv_size.value() < MIN_SIZE) {
218  rcv_size = MIN_SIZE;
219  socket_.set_option(rcv_size);
220  }
221  }
222 }
223 
224 // Send a message. Should never do this if the socket is not open, so throw
225 // an exception if this is the case.
226 
227 template <typename C> void
228 UDPSocket<C>::asyncSend(const void* data, size_t length,
229  const IOEndpoint* endpoint, C& callback)
230 {
231  if (isopen_) {
232 
233  // Upconvert to a UDPEndpoint. We need to do this because although
234  // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
235  // does not contain a method for getting at the underlying endpoint
236  // type - that is in the derived class and the two classes differ on
237  // return type.
238  isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
239  const UDPEndpoint* udp_endpoint =
240  static_cast<const UDPEndpoint*>(endpoint);
241 
242  // ... and send the message.
243  socket_.async_send_to(boost::asio::buffer(data, length),
244  udp_endpoint->getASIOEndpoint(), callback);
245  } else {
247  "attempt to send on a UDP socket that is not open");
248  }
249 }
250 
251 // Receive a message. Should never do this if the socket is not open, so throw
252 // an exception if this is the case.
253 
254 template <typename C> void
255 UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
256  IOEndpoint* endpoint, C& callback)
257 {
258  if (isopen_) {
259 
260  // Upconvert the endpoint again.
261  isc_throw_assert(endpoint->getProtocol() == IPPROTO_UDP);
262  UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
263 
264  // Ensure we can write into the buffer
265  if (offset >= length) {
266  isc_throw(BufferOverflow, "attempt to read into area beyond end of "
267  "UDP receive buffer");
268  }
269  void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
270 
271  // Issue the read
272  socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset),
273  udp_endpoint->getASIOEndpoint(), callback);
274  } else {
276  "attempt to receive from a UDP socket that is not open");
277  }
278 }
279 
280 // Receive complete. Just copy the data across to the output buffer and
281 // update arguments as appropriate.
282 
283 template <typename C> bool
284 UDPSocket<C>::processReceivedData(const void* staging, size_t length,
285  size_t& cumulative, size_t& offset,
286  size_t& expected,
288 {
289  // Set return values to what we should expect.
290  cumulative = length;
291  expected = length;
292  offset = 0;
293 
294  // Copy data across
295  outbuff->writeData(staging, length);
296 
297  // ... and mark that we have everything.
298  return (true);
299 }
300 
301 // Cancel I/O on the socket. No-op if the socket is not open.
302 
303 template <typename C> void
305  if (isopen_) {
306  socket_.cancel();
307  }
308 }
309 
310 // Close the socket down. Can only do this if the socket is open and we are
311 // managing it ourself.
312 
313 template <typename C> void
315  if (isopen_ && socket_ptr_) {
316  socket_.close();
317  isopen_ = false;
318  }
319 }
320 
321 } // namespace asiolink
322 } // namespace isc
323 
324 #endif // UDP_SOCKET_H
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition: isc_assert.h:18
#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.
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Definition: buffer.h:599