udp_socket.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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. #ifndef __UDP_SOCKET_H
  15. #define __UDP_SOCKET_H 1
  16. #ifndef ASIO_HPP
  17. #error "asio.hpp must be included before including this, see asiolink.h as to why"
  18. #endif
  19. #include <log/dummylog.h>
  20. #include <netinet/in.h>
  21. #include <sys/socket.h>
  22. #include <unistd.h> // for some IPC/network system calls
  23. #include <cstddef>
  24. #include <config.h>
  25. #include <asiolink/io_asio_socket.h>
  26. #include <asiolink/io_endpoint.h>
  27. #include <asiolink/io_service.h>
  28. #include <asiolink/udp_endpoint.h>
  29. namespace asiolink {
  30. /// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
  31. /// that represents a UDP socket.
  32. ///
  33. /// \param C Callback type
  34. template <typename C>
  35. class UDPSocket : public IOAsioSocket<C> {
  36. private:
  37. /// \brief Class is non-copyable
  38. UDPSocket(const UDPSocket&);
  39. UDPSocket& operator=(const UDPSocket&);
  40. public:
  41. enum {
  42. MAX_SIZE = 4096 // Send and receive size
  43. };
  44. /// \brief Constructor from an ASIO UDP socket.
  45. ///
  46. /// \param socket The ASIO representation of the UDP socket. It
  47. /// is assumed that the caller will open and close the socket, so
  48. /// these operations are a no-op for that socket.
  49. UDPSocket(asio::ip::udp::socket& socket);
  50. /// \brief Constructor
  51. ///
  52. /// Used when the UDPSocket is being asked to manage its own internal
  53. /// socket. It is assumed that open() and close() will not be used.
  54. ///
  55. /// \param service I/O Service object used to manage the socket.
  56. UDPSocket(IOService& service);
  57. /// \brief Destructor
  58. virtual ~UDPSocket();
  59. /// \brief Return file descriptor of underlying socket
  60. virtual int getNative() const {
  61. return (socket_.native());
  62. }
  63. /// \brief Return protocol of socket
  64. virtual int getProtocol() const {
  65. return (IPPROTO_UDP);
  66. }
  67. /// \brief Open Socket
  68. ///
  69. /// Opens the UDP socket. In the model for transport-layer agnostic I/O,
  70. /// an "open" operation includes a connection to the remote end (which
  71. /// may take time). This does not happen for UDP, so the method returns
  72. /// "false" to indicate that the operation completed synchronously.
  73. ///
  74. /// \param endpoint Endpoint to which the socket will connect to.
  75. /// \param callback Unused.
  76. ///
  77. /// \return false to indicate that the "operation" completed synchronously.
  78. virtual bool open(const IOEndpoint* endpoint, C&);
  79. /// \brief Send Asynchronously
  80. ///
  81. /// This corresponds to async_send_to() for UDP sockets and async_send()
  82. /// for TCP. In both cases an endpoint argument is supplied indicating the
  83. /// target of the send - this is ignored for TCP.
  84. ///
  85. /// \param data Data to send
  86. /// \param length Length of data to send
  87. /// \param endpoint Target of the send
  88. /// \param callback Callback object.
  89. virtual void asyncSend(const void* data, size_t length,
  90. const IOEndpoint* endpoint, C& callback);
  91. /// \brief Receive Asynchronously
  92. ///
  93. /// This correstponds to async_receive_from() for UDP sockets and
  94. /// async_receive() for TCP. In both cases, an endpoint argument is
  95. /// supplied to receive the source of the communication. For TCP it will
  96. /// be filled in with details of the connection.
  97. ///
  98. /// \param data Buffer to receive incoming message
  99. /// \param length Length of the data buffer
  100. /// \param cumulative Amount of data that should already be in the buffer.
  101. /// (This is ignored - every UPD receive fills the buffer from the start.)
  102. /// \param endpoint Source of the communication
  103. /// \param callback Callback object
  104. virtual void asyncReceive(void* data, size_t length, size_t cumulative,
  105. IOEndpoint* endpoint, C& callback);
  106. /// \brief Checks if the data received is complete.
  107. ///
  108. /// As all the data is received in one I/O, so this is, this is effectively
  109. /// a no-op (although it does update the amount of data received).
  110. ///
  111. /// \param data Data buffer containing data to date. (This is ignored
  112. /// for UDP receives.)
  113. /// \param length Amount of data received in last asynchronous I/O
  114. /// \param cumulative On input, amount of data received before the last
  115. /// I/O. On output, the total amount of data received to date.
  116. ///
  117. /// \return true if the receive is complete, false if another receive is
  118. /// needed.
  119. virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
  120. cumulative = length;
  121. return (true);
  122. }
  123. /// \brief Cancel I/O On Socket
  124. virtual void cancel();
  125. /// \brief Close socket
  126. virtual void close();
  127. private:
  128. // Two variables to hold the socket - a socket and a pointer to it. This
  129. // handles the case where a socket is passed to the UDPSocket on
  130. // construction, or where it is asked to manage its own socket.
  131. asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
  132. asio::ip::udp::socket& socket_; ///< Socket
  133. bool isopen_; ///< true when socket is open
  134. };
  135. // Constructor - caller manages socket
  136. template <typename C>
  137. UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
  138. socket_ptr_(NULL), socket_(socket), isopen_(true)
  139. {
  140. }
  141. // Constructor - create socket on the fly
  142. template <typename C>
  143. UDPSocket<C>::UDPSocket(IOService& service) :
  144. socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
  145. socket_(*socket_ptr_), isopen_(false)
  146. {
  147. }
  148. // Destructor. Only delete the socket if we are managing it.
  149. template <typename C>
  150. UDPSocket<C>::~UDPSocket()
  151. {
  152. delete socket_ptr_;
  153. }
  154. // Open the socket. Throws an error on failure
  155. // TODO: Make the open more resilient
  156. template <typename C> bool
  157. UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
  158. // Ignore opens on already-open socket. Don't throw a failure because
  159. // of uncertainties as to what precedes whan when using asynchronous I/O.
  160. // At also allows us a treat a passed-in socket as a self-managed socket.
  161. if (!isopen_) {
  162. if (endpoint->getFamily() == AF_INET) {
  163. socket_.open(asio::ip::udp::v4());
  164. }
  165. else {
  166. socket_.open(asio::ip::udp::v6());
  167. }
  168. isopen_ = true;
  169. // Ensure it can send and receive 4K buffers.
  170. socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
  171. socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
  172. ;
  173. // Allow reuse of an existing port/address
  174. socket_.set_option(asio::socket_base::reuse_address(true));
  175. }
  176. return (false);
  177. }
  178. // Send a message. Should never do this if the socket is not open, so throw
  179. // an exception if this is the case.
  180. template <typename C> void
  181. UDPSocket<C>::asyncSend(const void* data, size_t length,
  182. const IOEndpoint* endpoint, C& callback)
  183. {
  184. if (isopen_) {
  185. // Upconvert to a UDPEndpoint. We need to do this because although
  186. // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
  187. // doing cont contain a method for getting at the underlying endpoint
  188. // type - those are in the derived class and the two classes differ on
  189. // return type.
  190. assert(endpoint->getProtocol() == IPPROTO_UDP);
  191. const UDPEndpoint* udp_endpoint =
  192. static_cast<const UDPEndpoint*>(endpoint);
  193. socket_.async_send_to(asio::buffer(data, length),
  194. udp_endpoint->getASIOEndpoint(), callback);
  195. } else {
  196. isc_throw(SocketNotOpen,
  197. "attempt to send on a UDP socket that is not open");
  198. }
  199. }
  200. // Receive a message. Note that the "cumulative" argument is ignored - every UDP
  201. // receive is put into the buffer beginning at the start - there is no concept
  202. // receiving a subsequent part of a message. Same critera as before concerning
  203. // the need for the socket to be open.
  204. template <typename C> void
  205. UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
  206. IOEndpoint* endpoint, C& callback)
  207. {
  208. if (isopen_) {
  209. // Upconvert the endpoint again.
  210. assert(endpoint->getProtocol() == IPPROTO_UDP);
  211. UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
  212. socket_.async_receive_from(asio::buffer(data, length),
  213. udp_endpoint->getASIOEndpoint(), callback);
  214. } else {
  215. isc_throw(SocketNotOpen,
  216. "attempt to receive from a UDP socket that is not open");
  217. }
  218. }
  219. // Cancel I/O on the socket. No-op if the socket is not open.
  220. template <typename C> void
  221. UDPSocket<C>::cancel() {
  222. if (isopen_) {
  223. socket_.cancel();
  224. }
  225. }
  226. // Close the socket down. Can only do this if the socket is open and we are
  227. // managing it ourself.
  228. template <typename C> void
  229. UDPSocket<C>::close() {
  230. if (isopen_ && socket_ptr_) {
  231. socket_.close();
  232. isopen_ = false;
  233. }
  234. }
  235. } // namespace asiolink
  236. #endif // __UDP_SOCKET_H