udp_socket.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. MIN_SIZE = 4096 // Minimum 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 is assumed
  47. /// that the caller will open and close the socket, so these
  48. /// 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. In this case, the open() and close() methods are 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 Is "open()" synchronous?
  68. ///
  69. /// Indicates that the opening of a UDP socket is synchronous.
  70. virtual bool isOpenSynchronous() const {
  71. return true;
  72. }
  73. /// \brief Open Socket
  74. ///
  75. /// Opens the UDP socket. This is a synchronous operation.
  76. ///
  77. /// \param endpoint Endpoint to which the socket will send data. This is
  78. /// used to determine the address family trhat should be used for the
  79. /// underlying socket.
  80. /// \param callback Unused as the operation is synchronous.
  81. virtual void open(const IOEndpoint* endpoint, C& callback);
  82. /// \brief Send Asynchronously
  83. ///
  84. /// Calls the underlying socket's async_send_to() method to send a packet of
  85. /// data asynchronously to the remote endpoint. The callback will be called
  86. /// on completion.
  87. ///
  88. /// \param data Data to send
  89. /// \param length Length of data to send
  90. /// \param endpoint Target of the send
  91. /// \param callback Callback object.
  92. virtual void asyncSend(const void* data, size_t length,
  93. const IOEndpoint* endpoint, C& callback);
  94. /// \brief Receive Asynchronously
  95. ///
  96. /// Calls the underlying socket's async_receive_from() method to read a
  97. /// packet of data from a remote endpoint. Arrival of the data is signalled
  98. /// via a call to the callback function.
  99. ///
  100. /// \param data Buffer to receive incoming message
  101. /// \param length Length of the data buffer
  102. /// \param offset Offset into buffer where data is to be put
  103. /// \param endpoint Source of the communication
  104. /// \param callback Callback object
  105. virtual void asyncReceive(void* data, size_t length, size_t offset,
  106. IOEndpoint* endpoint, C& callback);
  107. /// \brief Checks if the data received is complete.
  108. ///
  109. /// For a UDP socket all the data is received in one I/O, so this is
  110. /// effectively a no-op (although it does update the amount of data
  111. /// received).
  112. ///
  113. /// \param data Data buffer containing data to date (ignored)
  114. /// \param length Amount of data in the buffer.
  115. ///
  116. /// \return Always true
  117. virtual bool receiveComplete(const void*, size_t) {
  118. return (true);
  119. }
  120. /// \brief Append Normalized Data
  121. ///
  122. /// When a UDP buffer is received, the entire buffer contains the data.
  123. /// When a TCP buffer is received, the first two bytes of the buffer hold
  124. /// a length count. This method removes those bytes from the buffer.
  125. ///
  126. /// \param inbuf Input buffer. This contains the data received over the
  127. /// network connection.
  128. /// \param length Amount of data in the input buffer. If TCP, this includes
  129. /// the two-byte count field.
  130. /// \param outbuf Pointer to output buffer to which the data will be
  131. /// appended
  132. virtual void appendNormalizedData(const void* inbuf, size_t length,
  133. isc::dns::OutputBufferPtr outbuf)
  134. {
  135. outbuf->writeData(inbuf, length);
  136. }
  137. /// \brief Cancel I/O On Socket
  138. virtual void cancel();
  139. /// \brief Close socket
  140. virtual void close();
  141. private:
  142. // Two variables to hold the socket - a socket and a pointer to it. This
  143. // handles the case where a socket is passed to the UDPSocket on
  144. // construction, or where it is asked to manage its own socket.
  145. asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
  146. asio::ip::udp::socket& socket_; ///< Socket
  147. bool isopen_; ///< true when socket is open
  148. };
  149. // Constructor - caller manages socket
  150. template <typename C>
  151. UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
  152. socket_ptr_(NULL), socket_(socket), isopen_(true)
  153. {
  154. }
  155. // Constructor - create socket on the fly
  156. template <typename C>
  157. UDPSocket<C>::UDPSocket(IOService& service) :
  158. socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
  159. socket_(*socket_ptr_), isopen_(false)
  160. {
  161. }
  162. // Destructor. Only delete the socket if we are managing it.
  163. template <typename C>
  164. UDPSocket<C>::~UDPSocket()
  165. {
  166. delete socket_ptr_;
  167. }
  168. // Open the socket.
  169. template <typename C> void
  170. UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
  171. // Ignore opens on already-open socket. (Don't throw a failure because
  172. // of uncertainties as to what precedes whan when using asynchronous I/O.)
  173. // It also allows us a treat a passed-in socket in exactly the same way as
  174. // a self-managed socket (in that we can call the open() and close() methods
  175. // of this class).
  176. if (!isopen_) {
  177. if (endpoint->getFamily() == AF_INET) {
  178. socket_.open(asio::ip::udp::v4());
  179. }
  180. else {
  181. socket_.open(asio::ip::udp::v6());
  182. }
  183. isopen_ = true;
  184. // Ensure it can send and receive at least 4K buffers.
  185. asio::ip::udp::socket::send_buffer_size snd_size;
  186. socket_.get_option(snd_size);
  187. if (snd_size.value() < MIN_SIZE) {
  188. snd_size = MIN_SIZE;
  189. socket_.set_option(snd_size);
  190. }
  191. asio::ip::udp::socket::receive_buffer_size rcv_size;
  192. socket_.get_option(rcv_size);
  193. if (rcv_size.value() < MIN_SIZE) {
  194. rcv_size = MIN_SIZE;
  195. socket_.set_option(rcv_size);
  196. }
  197. }
  198. }
  199. // Send a message. Should never do this if the socket is not open, so throw
  200. // an exception if this is the case.
  201. template <typename C> void
  202. UDPSocket<C>::asyncSend(const void* data, size_t length,
  203. const IOEndpoint* endpoint, C& callback)
  204. {
  205. if (isopen_) {
  206. // Upconvert to a UDPEndpoint. We need to do this because although
  207. // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
  208. // does not contain a method for getting at the underlying endpoint
  209. // type - that is in the derived class and the two classes differ on
  210. // return type.
  211. assert(endpoint->getProtocol() == IPPROTO_UDP);
  212. const UDPEndpoint* udp_endpoint =
  213. static_cast<const UDPEndpoint*>(endpoint);
  214. // ... and send the message.
  215. socket_.async_send_to(asio::buffer(data, length),
  216. udp_endpoint->getASIOEndpoint(), callback);
  217. } else {
  218. isc_throw(SocketNotOpen,
  219. "attempt to send on a UDP socket that is not open");
  220. }
  221. }
  222. // Receive a message. Should never do this if the socket is not open, so throw
  223. // an exception if this is the case.
  224. template <typename C> void
  225. UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
  226. IOEndpoint* endpoint, C& callback)
  227. {
  228. if (isopen_) {
  229. // Upconvert the endpoint again.
  230. assert(endpoint->getProtocol() == IPPROTO_UDP);
  231. UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
  232. // Ensure we can write into the buffer
  233. if (offset >= length) {
  234. isc_throw(BufferOverflow, "attempt to read into area beyond end of "
  235. "UDP receive buffer");
  236. }
  237. void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
  238. // Issue the read
  239. socket_.async_receive_from(asio::buffer(buffer_start, length - offset),
  240. udp_endpoint->getASIOEndpoint(), callback);
  241. } else {
  242. isc_throw(SocketNotOpen,
  243. "attempt to receive from a UDP socket that is not open");
  244. }
  245. }
  246. // Cancel I/O on the socket. No-op if the socket is not open.
  247. template <typename C> void
  248. UDPSocket<C>::cancel() {
  249. if (isopen_) {
  250. socket_.cancel();
  251. }
  252. }
  253. // Close the socket down. Can only do this if the socket is open and we are
  254. // managing it ourself.
  255. template <typename C> void
  256. UDPSocket<C>::close() {
  257. if (isopen_ && socket_ptr_) {
  258. socket_.close();
  259. isopen_ = false;
  260. }
  261. }
  262. } // namespace asiolink
  263. #endif // __UDP_SOCKET_H