Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #ifndef __UDP_SOCKET_H
16 : : #define __UDP_SOCKET_H 1
17 : :
18 : : #ifndef ASIO_HPP
19 : : #error "asio.hpp must be included before including this, see asiolink.h as to why"
20 : : #endif
21 : :
22 : : #include <log/dummylog.h>
23 : : #include <netinet/in.h>
24 : : #include <sys/socket.h>
25 : : #include <unistd.h> // for some IPC/network system calls
26 : :
27 : : #include <cstddef>
28 : :
29 : : #include <config.h>
30 : :
31 : : #include <asiolink/io_asio_socket.h>
32 : : #include <asiolink/io_endpoint.h>
33 : : #include <asiolink/io_service.h>
34 : : #include <asiolink/udp_endpoint.h>
35 : :
36 : : namespace isc {
37 : : namespace asiolink {
38 : :
39 : : /// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
40 : : /// that represents a UDP socket.
41 : : ///
42 : : /// \param C Callback type
43 : : template <typename C>
44 : : class UDPSocket : public IOAsioSocket<C> {
45 : : private:
46 : : /// \brief Class is non-copyable
47 : : UDPSocket(const UDPSocket&);
48 : : UDPSocket& operator=(const UDPSocket&);
49 : :
50 : : public:
51 : : enum {
52 : : MIN_SIZE = 4096 // Minimum send and receive size
53 : : };
54 : :
55 : : /// \brief Constructor from an ASIO UDP socket.
56 : : ///
57 : : /// \param socket The ASIO representation of the UDP socket. It is assumed
58 : : /// that the caller will open and close the socket, so these
59 : : /// operations are a no-op for that socket.
60 : : UDPSocket(asio::ip::udp::socket& socket);
61 : :
62 : : /// \brief Constructor
63 : : ///
64 : : /// Used when the UDPSocket is being asked to manage its own internal
65 : : /// socket. In this case, the open() and close() methods are used.
66 : : ///
67 : : /// \param service I/O Service object used to manage the socket.
68 : : UDPSocket(IOService& service);
69 : :
70 : : /// \brief Destructor
71 : : virtual ~UDPSocket();
72 : :
73 : : /// \brief Return file descriptor of underlying socket
74 : 4 : virtual int getNative() const {
75 : 4 : return (socket_.native());
76 : : }
77 : :
78 : : /// \brief Return protocol of socket
79 : 4 : virtual int getProtocol() const {
80 : 4 : return (IPPROTO_UDP);
81 : : }
82 : :
83 : : /// \brief Is "open()" synchronous?
84 : : ///
85 : : /// Indicates that the opening of a UDP socket is synchronous.
86 : 16 : virtual bool isOpenSynchronous() const {
87 : 16 : return true;
88 : : }
89 : :
90 : : /// \brief Open Socket
91 : : ///
92 : : /// Opens the UDP socket. This is a synchronous operation.
93 : : ///
94 : : /// \param endpoint Endpoint to which the socket will send data. This is
95 : : /// used to determine the address family trhat should be used for the
96 : : /// underlying socket.
97 : : /// \param callback Unused as the operation is synchronous.
98 : : virtual void open(const IOEndpoint* endpoint, C& callback);
99 : :
100 : : /// \brief Send Asynchronously
101 : : ///
102 : : /// Calls the underlying socket's async_send_to() method to send a packet of
103 : : /// data asynchronously to the remote endpoint. The callback will be called
104 : : /// on completion.
105 : : ///
106 : : /// \param data Data to send
107 : : /// \param length Length of data to send
108 : : /// \param endpoint Target of the send
109 : : /// \param callback Callback object.
110 : : virtual void asyncSend(const void* data, size_t length,
111 : : const IOEndpoint* endpoint, C& callback);
112 : :
113 : : /// \brief Receive Asynchronously
114 : : ///
115 : : /// Calls the underlying socket's async_receive_from() method to read a
116 : : /// packet of data from a remote endpoint. Arrival of the data is signalled
117 : : /// via a call to the callback function.
118 : : ///
119 : : /// \param data Buffer to receive incoming message
120 : : /// \param length Length of the data buffer
121 : : /// \param offset Offset into buffer where data is to be put
122 : : /// \param endpoint Source of the communication
123 : : /// \param callback Callback object
124 : : virtual void asyncReceive(void* data, size_t length, size_t offset,
125 : : IOEndpoint* endpoint, C& callback);
126 : :
127 : : /// \brief Process received data
128 : : ///
129 : : /// See the description of IOAsioSocket::receiveComplete for a complete
130 : : /// description of this method.
131 : : ///
132 : : /// \param staging Pointer to the start of the staging buffer.
133 : : /// \param length Amount of data in the staging buffer.
134 : : /// \param cumulative Amount of data received before the staging buffer is
135 : : /// processed.
136 : : /// \param offset Unused.
137 : : /// \param expected unused.
138 : : /// \param outbuff Output buffer. Data in the staging buffer is be copied
139 : : /// to this output buffer in the call.
140 : : ///
141 : : /// \return Always true
142 : : virtual bool processReceivedData(const void* staging, size_t length,
143 : : size_t& cumulative, size_t& offset,
144 : : size_t& expected,
145 : : isc::util::OutputBufferPtr& outbuff);
146 : :
147 : : /// \brief Cancel I/O On Socket
148 : : virtual void cancel();
149 : :
150 : : /// \brief Close socket
151 : : virtual void close();
152 : :
153 : :
154 : : private:
155 : : // Two variables to hold the socket - a socket and a pointer to it. This
156 : : // handles the case where a socket is passed to the UDPSocket on
157 : : // construction, or where it is asked to manage its own socket.
158 : : asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
159 : : asio::ip::udp::socket& socket_; ///< Socket
160 : : bool isopen_; ///< true when socket is open
161 : : };
162 : :
163 : : // Constructor - caller manages socket
164 : :
165 : : template <typename C>
166 : : UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
167 : 42 : socket_ptr_(NULL), socket_(socket), isopen_(true)
168 : : {
169 : : }
170 : :
171 : : // Constructor - create socket on the fly
172 : :
173 : : template <typename C>
174 : 39 : UDPSocket<C>::UDPSocket(IOService& service) :
175 : : socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
176 [ + - ][ + - ]: 39 : socket_(*socket_ptr_), isopen_(false)
177 : : {
178 : 39 : }
179 : :
180 : : // Destructor. Only delete the socket if we are managing it.
181 : :
182 : : template <typename C>
183 : 110 : UDPSocket<C>::~UDPSocket()
184 : : {
185 [ + + ][ # # ]: 99 : delete socket_ptr_;
186 : 110 : }
187 : :
188 : : // Open the socket.
189 : :
190 : : template <typename C> void
191 : 17 : 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 whan 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 [ + - ]: 17 : if (!isopen_) {
199 [ + - ]: 17 : if (endpoint->getFamily() == AF_INET) {
200 : 17 : socket_.open(asio::ip::udp::v4());
201 : : }
202 : : else {
203 : 0 : socket_.open(asio::ip::udp::v6());
204 : : }
205 : 17 : isopen_ = true;
206 : :
207 : : // Ensure it can send and receive at least 4K buffers.
208 : : asio::ip::udp::socket::send_buffer_size snd_size;
209 : 17 : socket_.get_option(snd_size);
210 [ - + ]: 17 : if (snd_size.value() < MIN_SIZE) {
211 : : snd_size = MIN_SIZE;
212 : 0 : socket_.set_option(snd_size);
213 : : }
214 : :
215 : : asio::ip::udp::socket::receive_buffer_size rcv_size;
216 : 17 : socket_.get_option(rcv_size);
217 [ - + ]: 17 : if (rcv_size.value() < MIN_SIZE) {
218 : : rcv_size = MIN_SIZE;
219 : 0 : socket_.set_option(rcv_size);
220 : : }
221 : : }
222 : 17 : }
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 : 19 : UDPSocket<C>::asyncSend(const void* data, size_t length,
229 : : const IOEndpoint* endpoint, C& callback)
230 : : {
231 [ + - ]: 19 : 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 [ - + ]: 19 : assert(endpoint->getProtocol() == IPPROTO_UDP);
239 : : const UDPEndpoint* udp_endpoint =
240 : 19 : static_cast<const UDPEndpoint*>(endpoint);
241 : :
242 : : // ... and send the message.
243 [ + - ]: 19 : socket_.async_send_to(asio::buffer(data, length),
244 : : udp_endpoint->getASIOEndpoint(), callback);
245 : : } else {
246 [ # # ]: 0 : isc_throw(SocketNotOpen,
247 : : "attempt to send on a UDP socket that is not open");
248 : : }
249 : 19 : }
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 : 18 : UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
256 : : IOEndpoint* endpoint, C& callback)
257 : : {
258 [ + - ]: 18 : if (isopen_) {
259 : :
260 : : // Upconvert the endpoint again.
261 [ - + ]: 18 : assert(endpoint->getProtocol() == IPPROTO_UDP);
262 : 18 : UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
263 : :
264 : : // Ensure we can write into the buffer
265 [ - + ]: 18 : if (offset >= length) {
266 [ # # ]: 0 : isc_throw(BufferOverflow, "attempt to read into area beyond end of "
267 : : "UDP receive buffer");
268 : : }
269 : 18 : void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
270 : :
271 : : // Issue the read
272 [ + - ]: 18 : socket_.async_receive_from(asio::buffer(buffer_start, length - offset),
273 : : udp_endpoint->getASIOEndpoint(), callback);
274 : : } else {
275 [ # # ]: 0 : isc_throw(SocketNotOpen,
276 : : "attempt to receive from a UDP socket that is not open");
277 : : }
278 : 18 : }
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 : 131083 : UDPSocket<C>::processReceivedData(const void* staging, size_t length,
285 : : size_t& cumulative, size_t& offset,
286 : : size_t& expected,
287 : : isc::util::OutputBufferPtr& outbuff)
288 : : {
289 : : // Set return values to what we should expect.
290 : 131083 : cumulative = length;
291 : 131083 : expected = length;
292 : 131083 : offset = 0;
293 : :
294 : : // Copy data across
295 : 131083 : outbuff->writeData(staging, length);
296 : :
297 : : // ... and mark that we have everything.
298 : 131083 : 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
304 : 13 : UDPSocket<C>::cancel() {
305 [ + + ]: 13 : if (isopen_) {
306 : 4 : socket_.cancel();
307 : : }
308 : 13 : }
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
314 : 22 : UDPSocket<C>::close() {
315 [ + + ][ + - ]: 22 : if (isopen_ && socket_ptr_) {
316 : 13 : socket_.close();
317 : 13 : isopen_ = false;
318 : : }
319 : 22 : }
320 : :
321 : : } // namespace asiolink
322 : : } // namespace isc
323 : :
324 : : #endif // __UDP_SOCKET_H
|