tcp_socket.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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 __TCP_SOCKET_H
  15. #define __TCP_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 <algorithm>
  24. #include <cassert>
  25. #include <cstddef>
  26. #include <boost/bind.hpp>
  27. #include <boost/numeric/conversion/cast.hpp>
  28. #include <config.h>
  29. #include <dns/buffer.h>
  30. #include <asiolink/asiolink_utilities.h>
  31. #include <asiolink/io_asio_socket.h>
  32. #include <asiolink/io_endpoint.h>
  33. #include <asiolink/io_service.h>
  34. #include <asiolink/tcp_endpoint.h>
  35. namespace asiolink {
  36. /// \brief Buffer Too Large
  37. ///
  38. /// Thrown on an attempt to send a buffer > 64k
  39. class BufferTooLarge : public IOError {
  40. public:
  41. BufferTooLarge(const char* file, size_t line, const char* what) :
  42. IOError(file, line, what) {}
  43. };
  44. /// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket
  45. /// that represents a TCP socket.
  46. ///
  47. /// \param C Callback type
  48. template <typename C>
  49. class TCPSocket : public IOAsioSocket<C> {
  50. private:
  51. /// \brief Class is non-copyable
  52. TCPSocket(const TCPSocket&);
  53. TCPSocket& operator=(const TCPSocket&);
  54. public:
  55. /// \brief Constructor from an ASIO TCP socket.
  56. ///
  57. /// \param socket The ASIO representation of the TCP 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. TCPSocket(asio::ip::tcp::socket& socket);
  61. /// \brief Constructor
  62. ///
  63. /// Used when the TCPSocket is being asked to manage its own internal
  64. /// socket. In this case, the open() and close() methods are used.
  65. ///
  66. /// \param service I/O Service object used to manage the socket.
  67. TCPSocket(IOService& service);
  68. /// \brief Destructor
  69. virtual ~TCPSocket();
  70. /// \brief Return file descriptor of underlying socket
  71. virtual int getNative() const {
  72. return (socket_.native());
  73. }
  74. /// \brief Return protocol of socket
  75. virtual int getProtocol() const {
  76. return (IPPROTO_TCP);
  77. }
  78. /// \brief Is "open()" synchronous?
  79. ///
  80. /// Indicates that the opening of a TCP socket is asynchronous.
  81. virtual bool isOpenSynchronous() const {
  82. return (false);
  83. }
  84. /// \brief Open Socket
  85. ///
  86. /// Opens the TCP socket. This is an asynchronous operation, completion of
  87. /// which will be signalled via a call to the callback function.
  88. ///
  89. /// \param endpoint Endpoint to which the socket will connect.
  90. /// \param callback Callback object.
  91. virtual void open(const IOEndpoint* endpoint, C& callback);
  92. /// \brief Send Asynchronously
  93. ///
  94. /// Calls the underlying socket's async_send() method to send a packet of
  95. /// data asynchronously to the remote endpoint. The callback will be called
  96. /// on completion.
  97. ///
  98. /// \param data Data to send
  99. /// \param length Length of data to send
  100. /// \param endpoint Target of the send. (Unused for a TCP socket because
  101. /// that was determined when the connection was opened.)
  102. /// \param callback Callback object.
  103. virtual void asyncSend(const void* data, size_t length,
  104. const IOEndpoint* endpoint, C& callback);
  105. /// \brief Receive Asynchronously
  106. ///
  107. /// Calls the underlying socket's async_receive() method to read a packet
  108. /// of data from a remote endpoint. Arrival of the data is signalled via a
  109. /// call to the callback function.
  110. ///
  111. /// \param data Buffer to receive incoming message
  112. /// \param length Length of the data buffer
  113. /// \param offset Offset into buffer where data is to be put
  114. /// \param endpoint Source of the communication
  115. /// \param callback Callback object
  116. virtual void asyncReceive(void* data, size_t length, size_t offset,
  117. IOEndpoint* endpoint, C& callback);
  118. /// \brief Process received data packet
  119. ///
  120. /// See the description of IOAsioSocket::receiveComplete for a complete
  121. /// description of this method.
  122. ///
  123. /// \param staging Pointer to the start of the staging buffer.
  124. /// \param length Amount of data in the staging buffer.
  125. /// \param cumulative Amount of data received before the staging buffer is
  126. /// processed.
  127. /// \param offset Unused.
  128. /// \param expected unused.
  129. /// \param outbuff Output buffer. Data in the staging buffer is be copied
  130. /// to this output buffer in the call.
  131. ///
  132. /// \return Always true
  133. virtual bool processReceivedData(const void* staging, size_t length,
  134. size_t& cumulative, size_t& offset,
  135. size_t& expected,
  136. isc::dns::OutputBufferPtr& outbuff);
  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 TCPSocket on
  144. // construction, or where it is asked to manage its own socket.
  145. asio::ip::tcp::socket* socket_ptr_; ///< Pointer to own socket
  146. asio::ip::tcp::socket& socket_; ///< Socket
  147. bool isopen_; ///< true when socket is open
  148. // TODO: Remove temporary buffer
  149. // The current implementation copies the buffer passed to asyncSend() into
  150. // a temporary buffer and precedes it with a two-byte count field. As
  151. // ASIO should really be just about sendiong and receiving data, the TCP
  152. // code should not do this. If the protocol using this requires a two-byte
  153. // count, it should add it before calling this code. (This may be best
  154. // achieved by altering isc::dns::buffer to have pairs of methods:
  155. // getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx()
  156. // methods taking into account a two-byte count field.)
  157. //
  158. // The option of sending the data in two operations, the count followed by
  159. // the data was discounted as that would lead to two callbacks which would
  160. // cause problems with the stackless coroutine code.
  161. isc::dns::OutputBufferPtr send_buffer_; ///< Send buffer
  162. };
  163. // Constructor - caller manages socket
  164. template <typename C>
  165. TCPSocket<C>::TCPSocket(asio::ip::tcp::socket& socket) :
  166. socket_ptr_(NULL), socket_(socket), isopen_(true), send_buffer_()
  167. {
  168. }
  169. // Constructor - create socket on the fly
  170. template <typename C>
  171. TCPSocket<C>::TCPSocket(IOService& service) :
  172. socket_ptr_(new asio::ip::tcp::socket(service.get_io_service())),
  173. socket_(*socket_ptr_), isopen_(false)
  174. {
  175. }
  176. // Destructor. Only delete the socket if we are managing it.
  177. template <typename C>
  178. TCPSocket<C>::~TCPSocket()
  179. {
  180. delete socket_ptr_;
  181. }
  182. // Open the socket.
  183. template <typename C> void
  184. TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
  185. // Ignore opens on already-open socket. Don't throw a failure because
  186. // of uncertainties as to what precedes whan when using asynchronous I/O.
  187. // At also allows us a treat a passed-in socket as a self-managed socket.
  188. if (!isopen_) {
  189. if (endpoint->getFamily() == AF_INET) {
  190. socket_.open(asio::ip::tcp::v4());
  191. }
  192. else {
  193. socket_.open(asio::ip::tcp::v6());
  194. }
  195. isopen_ = true;
  196. // Set options on the socket:
  197. // Reuse address - allow the socket to bind to a port even if the port
  198. // is in the TIMED_WAIT state.
  199. socket_.set_option(asio::socket_base::reuse_address(true));
  200. }
  201. // Upconvert to a TCPEndpoint. We need to do this because although
  202. // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not
  203. // contain a method for getting at the underlying endpoint type - that is in
  204. /// the derived class and the two classes differ on return type.
  205. assert(endpoint->getProtocol() == IPPROTO_TCP);
  206. const TCPEndpoint* tcp_endpoint =
  207. static_cast<const TCPEndpoint*>(endpoint);
  208. // Connect to the remote endpoint. On success, the handler will be
  209. // called (with one argument - the length argument will default to
  210. // zero).
  211. socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback);
  212. }
  213. // Send a message. Should never do this if the socket is not open, so throw
  214. // an exception if this is the case.
  215. template <typename C> void
  216. TCPSocket<C>::asyncSend(const void* data, size_t length, const IOEndpoint*,
  217. C& callback)
  218. {
  219. if (isopen_) {
  220. // Need to copy the data into a temporary buffer and precede it with
  221. // a two-byte count field.
  222. // TODO: arrange for the buffer passed to be preceded by the count
  223. try {
  224. // Ensure it fits into 16 bits
  225. uint16_t count = boost::numeric_cast<uint16_t>(length);
  226. // Copy data into a buffer preceded by the count field.
  227. send_buffer_.reset(new isc::dns::OutputBuffer(length + 2));
  228. send_buffer_->writeUint16(count);
  229. send_buffer_->writeData(data, length);
  230. // ... and send it
  231. socket_.async_send(asio::buffer(send_buffer_->getData(),
  232. send_buffer_->getLength()), callback);
  233. } catch (boost::numeric::bad_numeric_cast& e) {
  234. isc_throw(BufferTooLarge,
  235. "attempt to send buffer larger than 64kB");
  236. }
  237. } else {
  238. isc_throw(SocketNotOpen,
  239. "attempt to send on a TCP socket that is not open");
  240. }
  241. }
  242. // Receive a message. Note that the "offset" argument is used as an index
  243. // into the buffer in order to decide where to put the data. It is up to the
  244. // caller to initialize the data to zero
  245. template <typename C> void
  246. TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
  247. IOEndpoint* endpoint, C& callback)
  248. {
  249. if (isopen_) {
  250. // Upconvert to a TCPEndpoint. We need to do this because although
  251. // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
  252. // does not contain a method for getting at the underlying endpoint
  253. // type - that is in the derived class and the two classes differ on
  254. // return type.
  255. assert(endpoint->getProtocol() == IPPROTO_TCP);
  256. TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint);
  257. // Write the endpoint details from the comminications link. Ideally
  258. // we should make IOEndpoint assignable, but this runs in to all sorts
  259. // of problems concerning the management of the underlying Boost
  260. // endpoint (e.g. if it is not self-managed, is the copied one
  261. // self-managed?) The most pragmatic solution is to let Boost take care
  262. // of everything and copy details of the underlying endpoint.
  263. tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint();
  264. // Ensure we can write into the buffer and if so, set the pointer to
  265. // where the data will be written.
  266. if (offset >= length) {
  267. isc_throw(BufferOverflow, "attempt to read into area beyond end of "
  268. "TCP receive buffer");
  269. }
  270. void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
  271. // ... and kick off the read.
  272. socket_.async_receive(asio::buffer(buffer_start, length - offset), callback);
  273. } else {
  274. isc_throw(SocketNotOpen,
  275. "attempt to receive from a TCP socket that is not open");
  276. }
  277. }
  278. // Is the receive complete?
  279. template <typename C> bool
  280. TCPSocket<C>::processReceivedData(const void* staging, size_t length,
  281. size_t& cumulative, size_t& offset,
  282. size_t& expected,
  283. isc::dns::OutputBufferPtr& outbuff)
  284. {
  285. // Point to the data in the staging buffer and note how much there is.
  286. const uint8_t* data = static_cast<const uint8_t*>(staging);
  287. size_t data_length = length;
  288. // Is the number is "expected" valid? It won't be unless we have received
  289. // at least two bytes of data in total for this set of receives.
  290. if (cumulative < 2) {
  291. // "expected" is not valid. Did this read give us enough data to
  292. // work it out?
  293. cumulative += length;
  294. if (cumulative < 2) {
  295. // Nope, still not valid. This must have been the first packet and
  296. // was only one byte long. Tell the fetch code to read the next
  297. // packet into the staging buffer beyond the data that is already
  298. // there so that the next time we are called we have a complete
  299. // TCP count.
  300. offset = cumulative;
  301. return (false);
  302. }
  303. // Have enough data to interpret the packet count, so do so now.
  304. expected = readUint16(data);
  305. // We have two bytes less of data to process. Point to the start of the
  306. // data and adjust the packet size. Note that at this point,
  307. // "cumulative" is the true amount of data in the staging buffer, not
  308. // "length".
  309. data += 2;
  310. data_length = cumulative - 2;
  311. } else {
  312. // Update total amount of data received.
  313. cumulative += length;
  314. }
  315. // Regardless of anything else, the next read goes into the start of the
  316. // staging buffer.
  317. offset = 0;
  318. // Work out how much data we still have to put in the output buffer. (This
  319. // could be zero if we have just interpreted the TCP count and that was
  320. // set to zero.)
  321. if (expected >= outbuff->getLength()) {
  322. // Still need data in the output packet. Copy what we can from the
  323. // staging buffer to the output buffer.
  324. size_t copy_amount = std::min(expected - outbuff->getLength(), data_length);
  325. outbuff->writeData(data, copy_amount);
  326. }
  327. // We can now say if we have all the data.
  328. return (expected == outbuff->getLength());
  329. }
  330. // Cancel I/O on the socket. No-op if the socket is not open.
  331. template <typename C> void
  332. TCPSocket<C>::cancel() {
  333. if (isopen_) {
  334. socket_.cancel();
  335. }
  336. }
  337. // Close the socket down. Can only do this if the socket is open and we are
  338. // managing it ourself.
  339. template <typename C> void
  340. TCPSocket<C>::close() {
  341. if (isopen_ && socket_ptr_) {
  342. socket_.close();
  343. isopen_ = false;
  344. }
  345. }
  346. } // namespace asiolink
  347. #endif // __TCP_SOCKET_H