Kea  1.5.0
udp_socket.h
Go to the documentation of this file.
1 // Copyright (C) 2011-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 #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 namespace isc {
26 namespace asiolink {
27 
32 template <typename C>
33 class UDPSocket : public IOAsioSocket<C> {
34 private:
36  UDPSocket(const UDPSocket&);
37  UDPSocket& operator=(const UDPSocket&);
38 
39 public:
40  enum {
41  MIN_SIZE = 4096 // Minimum send and receive size
42  };
43 
49  UDPSocket(boost::asio::ip::udp::socket& socket);
50 
57  UDPSocket(IOService& service);
58 
60  virtual ~UDPSocket();
61 
63  virtual int getNative() const {
64 #if BOOST_VERSION < 106600
65  return (socket_.native());
66 #else
67  return (socket_.native_handle());
68 #endif
69  }
70 
72  virtual int getProtocol() const {
73  return (IPPROTO_UDP);
74  }
75 
79  virtual bool isOpenSynchronous() const {
80  return true;
81  }
82 
91  virtual void open(const IOEndpoint* endpoint, C& callback);
92 
103  virtual void asyncSend(const void* data, size_t length,
104  const IOEndpoint* endpoint, C& callback);
105 
117  virtual void asyncReceive(void* data, size_t length, size_t offset,
118  IOEndpoint* endpoint, C& callback);
119 
135  virtual bool processReceivedData(const void* staging, size_t length,
136  size_t& cumulative, size_t& offset,
137  size_t& expected,
138  isc::util::OutputBufferPtr& outbuff);
139 
141  virtual void cancel();
142 
144  virtual void close();
145 
146 
147 private:
148  // Two variables to hold the socket - a socket and a pointer to it. This
149  // handles the case where a socket is passed to the UDPSocket on
150  // construction, or where it is asked to manage its own socket.
151  boost::asio::ip::udp::socket* socket_ptr_;
152  boost::asio::ip::udp::socket& socket_;
153  bool isopen_;
154 };
155 
156 // Constructor - caller manages socket
157 
158 template <typename C>
159 UDPSocket<C>::UDPSocket(boost::asio::ip::udp::socket& socket) :
160  socket_ptr_(NULL), socket_(socket), isopen_(true)
161 {
162 }
163 
164 // Constructor - create socket on the fly
165 
166 template <typename C>
168  socket_ptr_(new boost::asio::ip::udp::socket(service.get_io_service())),
169  socket_(*socket_ptr_), isopen_(false)
170 {
171 }
172 
173 // Destructor. Only delete the socket if we are managing it.
174 
175 template <typename C>
177 {
178  delete socket_ptr_;
179 }
180 
181 // Open the socket.
182 
183 template <typename C> void
184 UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
185 
186  // Ignore opens on already-open socket. (Don't throw a failure because
187  // of uncertainties as to what precedes whan when using asynchronous I/O.)
188  // It also allows us a treat a passed-in socket in exactly the same way as
189  // a self-managed socket (in that we can call the open() and close() methods
190  // of this class).
191  if (!isopen_) {
192  if (endpoint->getFamily() == AF_INET) {
193  socket_.open(boost::asio::ip::udp::v4());
194  }
195  else {
196  socket_.open(boost::asio::ip::udp::v6());
197  }
198  isopen_ = true;
199 
200  // Ensure it can send and receive at least 4K buffers.
201  boost::asio::ip::udp::socket::send_buffer_size snd_size;
202  socket_.get_option(snd_size);
203  if (snd_size.value() < MIN_SIZE) {
204  snd_size = MIN_SIZE;
205  socket_.set_option(snd_size);
206  }
207 
208  boost::asio::ip::udp::socket::receive_buffer_size rcv_size;
209  socket_.get_option(rcv_size);
210  if (rcv_size.value() < MIN_SIZE) {
211  rcv_size = MIN_SIZE;
212  socket_.set_option(rcv_size);
213  }
214  }
215 }
216 
217 // Send a message. Should never do this if the socket is not open, so throw
218 // an exception if this is the case.
219 
220 template <typename C> void
221 UDPSocket<C>::asyncSend(const void* data, size_t length,
222  const IOEndpoint* endpoint, C& callback)
223 {
224  if (isopen_) {
225 
226  // Upconvert to a UDPEndpoint. We need to do this because although
227  // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
228  // does not contain a method for getting at the underlying endpoint
229  // type - that is in the derived class and the two classes differ on
230  // return type.
231  assert(endpoint->getProtocol() == IPPROTO_UDP);
232  const UDPEndpoint* udp_endpoint =
233  static_cast<const UDPEndpoint*>(endpoint);
234 
235  // ... and send the message.
236  socket_.async_send_to(boost::asio::buffer(data, length),
237  udp_endpoint->getASIOEndpoint(), callback);
238  } else {
240  "attempt to send on a UDP socket that is not open");
241  }
242 }
243 
244 // Receive a message. Should never do this if the socket is not open, so throw
245 // an exception if this is the case.
246 
247 template <typename C> void
248 UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
249  IOEndpoint* endpoint, C& callback)
250 {
251  if (isopen_) {
252 
253  // Upconvert the endpoint again.
254  assert(endpoint->getProtocol() == IPPROTO_UDP);
255  UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
256 
257  // Ensure we can write into the buffer
258  if (offset >= length) {
259  isc_throw(BufferOverflow, "attempt to read into area beyond end of "
260  "UDP receive buffer");
261  }
262  void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
263 
264  // Issue the read
265  socket_.async_receive_from(boost::asio::buffer(buffer_start, length - offset),
266  udp_endpoint->getASIOEndpoint(), callback);
267  } else {
269  "attempt to receive from a UDP socket that is not open");
270  }
271 }
272 
273 // Receive complete. Just copy the data across to the output buffer and
274 // update arguments as appropriate.
275 
276 template <typename C> bool
277 UDPSocket<C>::processReceivedData(const void* staging, size_t length,
278  size_t& cumulative, size_t& offset,
279  size_t& expected,
281 {
282  // Set return values to what we should expect.
283  cumulative = length;
284  expected = length;
285  offset = 0;
286 
287  // Copy data across
288  outbuff->writeData(staging, length);
289 
290  // ... and mark that we have everything.
291  return (true);
292 }
293 
294 // Cancel I/O on the socket. No-op if the socket is not open.
295 
296 template <typename C> void
298  if (isopen_) {
299  socket_.cancel();
300  }
301 }
302 
303 // Close the socket down. Can only do this if the socket is open and we are
304 // managing it ourself.
305 
306 template <typename C> void
308  if (isopen_ && socket_ptr_) {
309  socket_.close();
310  isopen_ = false;
311  }
312 }
313 
314 } // namespace asiolink
315 } // namespace isc
316 
317 #endif // UDP_SOCKET_H
io_endpoint.h
boost
Definition: io_service.h:12
io_service.h
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
udp_endpoint.h
io_asio_socket.h
isc::util::OutputBufferPtr
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Definition: buffer.h:596