tcp_socket.h 16 KB

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