udp_socket.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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 isc {
  30. namespace asiolink {
  31. /// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
  32. /// that represents a UDP socket.
  33. ///
  34. /// \param C Callback type
  35. template <typename C>
  36. class UDPSocket : public IOAsioSocket<C> {
  37. private:
  38. /// \brief Class is non-copyable
  39. UDPSocket(const UDPSocket&);
  40. UDPSocket& operator=(const UDPSocket&);
  41. public:
  42. enum {
  43. MIN_SIZE = 4096 // Minimum send and receive size
  44. };
  45. /// \brief Constructor from an ASIO UDP socket.
  46. ///
  47. /// \param socket The ASIO representation of the UDP socket. It is assumed
  48. /// that the caller will open and close the socket, so these
  49. /// operations are a no-op for that socket.
  50. UDPSocket(asio::ip::udp::socket& socket);
  51. /// \brief Constructor
  52. ///
  53. /// Used when the UDPSocket is being asked to manage its own internal
  54. /// socket. In this case, the open() and close() methods are used.
  55. ///
  56. /// \param service I/O Service object used to manage the socket.
  57. UDPSocket(IOService& service);
  58. /// \brief Destructor
  59. virtual ~UDPSocket();
  60. /// \brief Return file descriptor of underlying socket
  61. virtual int getNative() const {
  62. return (socket_.native());
  63. }
  64. /// \brief Return protocol of socket
  65. virtual int getProtocol() const {
  66. return (IPPROTO_UDP);
  67. }
  68. /// \brief Is "open()" synchronous?
  69. ///
  70. /// Indicates that the opening of a UDP socket is synchronous.
  71. virtual bool isOpenSynchronous() const {
  72. return true;
  73. }
  74. /// \brief Open Socket
  75. ///
  76. /// Opens the UDP socket. This is a synchronous operation.
  77. ///
  78. /// \param endpoint Endpoint to which the socket will send data. This is
  79. /// used to determine the address family trhat should be used for the
  80. /// underlying socket.
  81. /// \param callback Unused as the operation is synchronous.
  82. virtual void open(const IOEndpoint* endpoint, C& callback);
  83. /// \brief Send Asynchronously
  84. ///
  85. /// Calls the underlying socket's async_send_to() method to send a packet of
  86. /// data asynchronously to the remote endpoint. The callback will be called
  87. /// on completion.
  88. ///
  89. /// \param data Data to send
  90. /// \param length Length of data to send
  91. /// \param endpoint Target of the send
  92. /// \param callback Callback object.
  93. virtual void asyncSend(const void* data, size_t length,
  94. const IOEndpoint* endpoint, C& callback);
  95. /// \brief Receive Asynchronously
  96. ///
  97. /// Calls the underlying socket's async_receive_from() method to read a
  98. /// packet of data from a remote endpoint. Arrival of the data is signalled
  99. /// via a call to the callback function.
  100. ///
  101. /// \param data Buffer to receive incoming message
  102. /// \param length Length of the data buffer
  103. /// \param offset Offset into buffer where data is to be put
  104. /// \param endpoint Source of the communication
  105. /// \param callback Callback object
  106. virtual void asyncReceive(void* data, size_t length, size_t offset,
  107. IOEndpoint* endpoint, C& callback);
  108. /// \brief Process received data
  109. ///
  110. /// See the description of IOAsioSocket::receiveComplete for a complete
  111. /// description of this method.
  112. ///
  113. /// \param staging Pointer to the start of the staging buffer.
  114. /// \param length Amount of data in the staging buffer.
  115. /// \param cumulative Amount of data received before the staging buffer is
  116. /// processed.
  117. /// \param offset Unused.
  118. /// \param expected unused.
  119. /// \param outbuff Output buffer. Data in the staging buffer is be copied
  120. /// to this output buffer in the call.
  121. ///
  122. /// \return Always true
  123. virtual bool processReceivedData(const void* staging, size_t length,
  124. size_t& cumulative, size_t& offset,
  125. size_t& expected,
  126. isc::util::OutputBufferPtr& outbuff);
  127. /// \brief Cancel I/O On Socket
  128. virtual void cancel();
  129. /// \brief Close socket
  130. virtual void close();
  131. private:
  132. // Two variables to hold the socket - a socket and a pointer to it. This
  133. // handles the case where a socket is passed to the UDPSocket on
  134. // construction, or where it is asked to manage its own socket.
  135. asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
  136. asio::ip::udp::socket& socket_; ///< Socket
  137. bool isopen_; ///< true when socket is open
  138. };
  139. // Constructor - caller manages socket
  140. template <typename C>
  141. UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
  142. socket_ptr_(NULL), socket_(socket), isopen_(true)
  143. {
  144. }
  145. // Constructor - create socket on the fly
  146. template <typename C>
  147. UDPSocket<C>::UDPSocket(IOService& service) :
  148. socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
  149. socket_(*socket_ptr_), isopen_(false)
  150. {
  151. }
  152. // Destructor. Only delete the socket if we are managing it.
  153. template <typename C>
  154. UDPSocket<C>::~UDPSocket()
  155. {
  156. delete socket_ptr_;
  157. }
  158. // Open the socket.
  159. template <typename C> void
  160. UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
  161. // Ignore opens on already-open socket. (Don't throw a failure because
  162. // of uncertainties as to what precedes whan when using asynchronous I/O.)
  163. // It also allows us a treat a passed-in socket in exactly the same way as
  164. // a self-managed socket (in that we can call the open() and close() methods
  165. // of this class).
  166. if (!isopen_) {
  167. if (endpoint->getFamily() == AF_INET) {
  168. socket_.open(asio::ip::udp::v4());
  169. }
  170. else {
  171. socket_.open(asio::ip::udp::v6());
  172. }
  173. isopen_ = true;
  174. // Ensure it can send and receive at least 4K buffers.
  175. asio::ip::udp::socket::send_buffer_size snd_size;
  176. socket_.get_option(snd_size);
  177. if (snd_size.value() < MIN_SIZE) {
  178. snd_size = MIN_SIZE;
  179. socket_.set_option(snd_size);
  180. }
  181. asio::ip::udp::socket::receive_buffer_size rcv_size;
  182. socket_.get_option(rcv_size);
  183. if (rcv_size.value() < MIN_SIZE) {
  184. rcv_size = MIN_SIZE;
  185. socket_.set_option(rcv_size);
  186. }
  187. }
  188. }
  189. // Send a message. Should never do this if the socket is not open, so throw
  190. // an exception if this is the case.
  191. template <typename C> void
  192. UDPSocket<C>::asyncSend(const void* data, size_t length,
  193. const IOEndpoint* endpoint, C& callback)
  194. {
  195. if (isopen_) {
  196. // Upconvert to a UDPEndpoint. We need to do this because although
  197. // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
  198. // does not contain a method for getting at the underlying endpoint
  199. // type - that is in the derived class and the two classes differ on
  200. // return type.
  201. assert(endpoint->getProtocol() == IPPROTO_UDP);
  202. const UDPEndpoint* udp_endpoint =
  203. static_cast<const UDPEndpoint*>(endpoint);
  204. // ... and send the message.
  205. socket_.async_send_to(asio::buffer(data, length),
  206. udp_endpoint->getASIOEndpoint(), callback);
  207. } else {
  208. isc_throw(SocketNotOpen,
  209. "attempt to send on a UDP socket that is not open");
  210. }
  211. }
  212. // Receive a message. Should never do this if the socket is not open, so throw
  213. // an exception if this is the case.
  214. template <typename C> void
  215. UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
  216. IOEndpoint* endpoint, C& callback)
  217. {
  218. if (isopen_) {
  219. // Upconvert the endpoint again.
  220. assert(endpoint->getProtocol() == IPPROTO_UDP);
  221. UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
  222. // Ensure we can write into the buffer
  223. if (offset >= length) {
  224. isc_throw(BufferOverflow, "attempt to read into area beyond end of "
  225. "UDP receive buffer");
  226. }
  227. void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
  228. // Issue the read
  229. socket_.async_receive_from(asio::buffer(buffer_start, length - offset),
  230. udp_endpoint->getASIOEndpoint(), callback);
  231. } else {
  232. isc_throw(SocketNotOpen,
  233. "attempt to receive from a UDP socket that is not open");
  234. }
  235. }
  236. // Receive complete. Just copy the data across to the output buffer and
  237. // update arguments as appropriate.
  238. template <typename C> bool
  239. UDPSocket<C>::processReceivedData(const void* staging, size_t length,
  240. size_t& cumulative, size_t& offset,
  241. size_t& expected,
  242. isc::util::OutputBufferPtr& outbuff)
  243. {
  244. // Set return values to what we should expect.
  245. cumulative = length;
  246. expected = length;
  247. offset = 0;
  248. // Copy data across
  249. outbuff->writeData(staging, length);
  250. // ... and mark that we have everything.
  251. return (true);
  252. }
  253. // Cancel I/O on the socket. No-op if the socket is not open.
  254. template <typename C> void
  255. UDPSocket<C>::cancel() {
  256. if (isopen_) {
  257. socket_.cancel();
  258. }
  259. }
  260. // Close the socket down. Can only do this if the socket is open and we are
  261. // managing it ourself.
  262. template <typename C> void
  263. UDPSocket<C>::close() {
  264. if (isopen_ && socket_ptr_) {
  265. socket_.close();
  266. isopen_ = false;
  267. }
  268. }
  269. } // namespace asiolink
  270. } // namespace isc
  271. #endif // UDP_SOCKET_H