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
22#include <asiolink/io_service.h>
24
25namespace isc {
26namespace asiolink {
27
32template <typename C>
33class UDPSocket : public IOAsioSocket<C> {
34private:
36 UDPSocket(const UDPSocket&);
37 UDPSocket& operator=(const UDPSocket&);
38
39public:
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,
139
141 virtual void cancel();
142
144 virtual void close();
145
146
147private:
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
158template <typename C>
159UDPSocket<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
166template <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
175template <typename C>
177{
178 delete socket_ptr_;
179}
180
181// Open the socket.
182
183template <typename C> void
184UDPSocket<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
220template <typename C> void
221UDPSocket<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
247template <typename C> void
248UDPSocket<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
276template <typename C> bool
277UDPSocket<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
296template <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
306template <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
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Definition: buffer.h:596
Defines the logger used by the top-level component of kea-dhcp-ddns.