LCOV - code coverage report
Current view: top level - asiodns - tcp_server.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 67 74 90.5 %
Date: 2012-05-15 Functions: 7 8 87.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 103 302 34.1 %

           Branch data     Line data    Source code
       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                 :            : 
      15                 :            : #include <config.h>
      16                 :            : 
      17                 :            : #include <unistd.h>             // for some IPC/network system calls
      18                 :            : #include <netinet/in.h>
      19                 :            : #include <sys/socket.h>
      20                 :            : #include <errno.h>
      21                 :            : 
      22                 :            : #include <boost/shared_array.hpp>
      23                 :            : 
      24                 :            : #include <log/dummylog.h>
      25                 :            : 
      26                 :            : #include <util/buffer.h>
      27                 :            : 
      28                 :            : #include <asio.hpp>
      29                 :            : #include <asiolink/dummy_io_cb.h>
      30                 :            : #include <asiolink/tcp_endpoint.h>
      31                 :            : #include <asiolink/tcp_socket.h>
      32                 :            : #include <asiodns/tcp_server.h>
      33                 :            : #include <asiodns/logger.h>
      34                 :            : 
      35                 :            : using namespace asio;
      36                 :            : using asio::ip::udp;
      37                 :            : using asio::ip::tcp;
      38                 :            : 
      39                 :            : using namespace std;
      40                 :            : using namespace isc::dns;
      41                 :            : using namespace isc::util;
      42                 :            : using namespace isc::asiolink;
      43                 :            : 
      44                 :            : namespace isc {
      45                 :            : namespace asiodns {
      46                 :            : 
      47                 :            : /// The following functions implement the \c TCPServer class.
      48                 :            : ///
      49                 :            : /// The constructor
      50                 :         85 : TCPServer::TCPServer(io_service& io_service, int fd, int af,
      51                 :            :                      const SimpleCallback* checkin,
      52                 :            :                      const DNSLookup* lookup,
      53                 :            :                      const DNSAnswer* answer) :
      54                 :            :     io_(io_service), done_(false),
      55                 :            :     checkin_callback_(checkin), lookup_callback_(lookup),
      56 [ +  - ][ #  # ]:         85 :     answer_callback_(answer)
      57                 :            : {
      58 [ +  + ][ #  # ]:         85 :     if (af != AF_INET && af != AF_INET6) {
      59 [ +  - ][ +  - ]:          4 :         isc_throw(InvalidParameter, "Address family must be either AF_INET "
         [ +  - ][ #  # ]
         [ #  # ][ #  # ]
      60                 :            :                   "or AF_INET6, not " << af);
      61                 :            :     }
      62 [ +  - ][ +  - ]:         83 :     LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_TCP).arg(fd);
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
      63                 :            : 
      64                 :            :     try {
      65 [ +  - ][ +  - ]:         83 :         acceptor_.reset(new tcp::acceptor(io_service));
         [ +  - ][ #  # ]
         [ #  # ][ #  # ]
      66 [ +  + ][ +  + ]:        136 :         acceptor_->assign(af == AF_INET6 ? tcp::v6() : tcp::v4(), fd);
         [ #  # ][ #  # ]
      67 [ +  - ][ #  # ]:         81 :         acceptor_->listen();
      68 [ -  + ][ #  # ]:          4 :     } catch (const std::exception& exception) {
      69                 :            :         // Whatever the thing throws, it is something from ASIO and we convert
      70                 :            :         // it
      71 [ -  + ][ -  + ]:          4 :         isc_throw(IOError, exception.what());
         [ #  # ][ #  # ]
      72                 :            :     }
      73                 :         81 : }
      74                 :            : 
      75                 :            : void
      76                 :        163 : TCPServer::operator()(asio::error_code ec, size_t length) {
      77                 :            :     /// Because the coroutine reentry block is implemented as
      78                 :            :     /// a switch statement, inline variable declarations are not
      79                 :            :     /// permitted.  Certain variables used below can be declared here.
      80                 :            : 
      81                 :            :     boost::array<const_buffer,2> bufs;
      82                 :        163 :     OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
      83                 :            : 
      84   [ -  +  +  +  :        163 :     CORO_REENTER (this) {
          +  -  +  -  -  
             +  -  -  +  
              - ][ #  # ]
      85         [ +  + ]:         32 :         do {
      86                 :            :             /// Create a socket to listen for connections
      87 [ +  - ][ +  - ]:        166 :             socket_.reset(new tcp::socket(acceptor_->get_io_service()));
      88                 :            : 
      89                 :            :             /// Wait for new connections. In the event of non-fatal error,
      90                 :            :             /// try again
      91         [ -  + ]:         16 :             do {
      92         [ +  - ]:        166 :                 CORO_YIELD acceptor_->async_accept(*socket_, *this);
           [ -  -  -  + ]
         [ #  # ][ -  + ]
                 [ +  - ]
      93                 :            : 
      94                 :            :                 // Abort on fatal errors
      95                 :            :                 // TODO: Log error?
      96         [ +  + ]:         28 :                 if (ec) {
      97                 :            :                     using namespace asio::error;
      98 [ +  - ][ +  - ]:         12 :                     if (ec.value() != would_block && ec.value() != try_again &&
         [ +  - ][ -  + ]
                 [ -  + ]
      99                 :            :                         ec.value() != connection_aborted &&
     100                 :            :                         ec.value() != interrupted) {
     101                 :            :                         return;
     102                 :            :                     }
     103                 :            :                 }
     104                 :            :             } while (ec);
     105                 :            : 
     106                 :            :             /// Fork the coroutine by creating a copy of this one and
     107                 :            :             /// scheduling it on the ASIO service queue.  The parent
     108                 :            :             /// will continue listening for DNS connections while the
     109                 :            :             /// handles the one that has just arrived.
     110 [ +  + ][ +  - ]:         32 :             CORO_FORK io_.post(TCPServer(*this));
                 [ +  - ]
     111                 :         32 :         } while (is_parent());
     112                 :            : 
     113                 :            :         /// Instantiate the data buffer that will be used by the
     114                 :            :         /// asynchronous read call.
     115         [ +  - ]:         16 :         data_.reset(new char[MAX_LENGTH]);
     116                 :            : 
     117                 :            :         /// Read the message, in two parts.  First, the message length:
     118         [ +  - ]:         48 :         CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     119 [ +  - ][ +  - ]:         32 :                               TCP_MESSAGE_LENGTHSIZE), *this);
     120         [ -  + ]:         16 :         if (ec) {
     121         [ #  # ]:          0 :             socket_->close();
     122         [ #  # ]:          0 :             CORO_YIELD return;
           [ #  #  #  # ]
         [ #  # ][ #  # ]
     123                 :            :         }
     124                 :            : 
     125                 :            :         /// Now read the message itself. (This is done in a different scope
     126                 :            :         /// to allow inline variable declarations.)
     127         [ +  - ]:         32 :         CORO_YIELD {
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     128                 :         16 :             InputBuffer dnsbuffer(data_.get(), length);
     129                 :         16 :             uint16_t msglen = dnsbuffer.readUint16();
     130 [ +  - ][ +  - ]:         16 :             async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
     131                 :            :         }
     132                 :            : 
     133         [ -  + ]:         16 :         if (ec) {
     134         [ #  # ]:          0 :             socket_->close();
     135         [ #  # ]:          0 :             CORO_YIELD return;
           [ #  #  #  # ]
         [ #  # ][ #  # ]
     136                 :            :         }
     137                 :            : 
     138                 :            : 
     139                 :            :         // Create an \c IOMessage object to store the query.
     140                 :            :         //
     141                 :            :         // (XXX: It would be good to write a factory function
     142                 :            :         // that would quickly generate an IOMessage object without
     143                 :            :         // all these calls to "new".)
     144 [ +  - ][ +  - ]:         32 :         peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
                 [ +  - ]
     145                 :            : 
     146                 :            :         // The TCP socket class has been extended with asynchronous functions
     147                 :            :         // and takes as a template parameter a completion callback class.  As
     148                 :            :         // TCPServer does not use these extended functions (only those defined
     149                 :            :         // in the IOSocket base class) - but needs a TCPSocket to get hold of
     150                 :            :         // the underlying Boost TCP socket - DummyIOCallback is used.  This
     151                 :            :         // provides the appropriate operator() but is otherwise functionless.
     152 [ +  - ][ +  - ]:         16 :         iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
     153 [ +  - ][ +  - ]:         16 :         io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
     154                 :         16 :         bytes_ = length;
     155                 :            : 
     156                 :            :         // Perform any necessary operations prior to processing the incoming
     157                 :            :         // packet (e.g., checking for queued configuration messages).
     158                 :            :         //
     159                 :            :         // (XXX: it may be a performance issue to have this called for
     160                 :            :         // every single incoming packet; we may wish to throttle it somehow
     161                 :            :         // in the future.)
     162         [ +  - ]:         16 :         if (checkin_callback_ != NULL) {
     163         [ +  - ]:         16 :             (*checkin_callback_)(*io_message_);
     164                 :            :         }
     165                 :            : 
     166                 :            :         // If we don't have a DNS Lookup provider, there's no point in
     167                 :            :         // continuing; we exit the coroutine permanently.
     168         [ +  + ]:         16 :         if (lookup_callback_ == NULL) {
     169         [ +  - ]:          6 :             socket_->close();
     170         [ +  - ]:          6 :             CORO_YIELD return;
           [ -  -  -  + ]
         [ #  # ][ #  # ]
     171                 :            :         }
     172                 :            : 
     173                 :            :         // Reset or instantiate objects that will be needed by the
     174                 :            :         // DNS lookup and the write call.
     175 [ +  - ][ +  - ]:         10 :         respbuf_.reset(new OutputBuffer(0));
                 [ +  - ]
     176 [ +  - ][ +  - ]:         10 :         query_message_.reset(new Message(Message::PARSE));
                 [ +  - ]
     177 [ +  - ][ +  - ]:         10 :         answer_message_.reset(new Message(Message::RENDER));
                 [ +  - ]
     178                 :            : 
     179                 :            :         // Schedule a DNS lookup, and yield.  When the lookup is
     180                 :            :         // finished, the coroutine will resume immediately after
     181                 :            :         // this point.
     182         [ +  - ]:         20 :         CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     183                 :            : 
     184                 :            :         // The 'done_' flag indicates whether we have an answer
     185                 :            :         // to send back.  If not, exit the coroutine permanently.
     186         [ -  + ]:         10 :         if (!done_) {
     187                 :            :             // TODO: should we keep the connection open for a short time
     188                 :            :             // to see if new requests come in?
     189         [ #  # ]:          0 :             socket_->close();
     190         [ #  # ]:          0 :             CORO_YIELD return;
           [ #  #  #  # ]
         [ #  # ][ #  # ]
     191                 :            :         }
     192                 :            : 
     193         [ -  + ]:         10 :         if (ec) {
     194         [ #  # ]:          0 :             CORO_YIELD return;
           [ #  #  #  # ]
         [ #  # ][ #  # ]
     195                 :            :         }
     196                 :            :         // Call the DNS answer provider to render the answer into
     197                 :            :         // wire format
     198                 :         10 :         (*answer_callback_)(*io_message_, query_message_,
     199         [ +  - ]:         10 :                             answer_message_, respbuf_);
     200                 :            : 
     201                 :            :         // Set up the response, beginning with two length bytes.
     202         [ +  - ]:         10 :         lenbuf.writeUint16(respbuf_->getLength());
     203                 :         10 :         bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
     204                 :         10 :         bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
     205                 :            : 
     206                 :            :         // Begin an asynchronous send, and then yield.  When the
     207                 :            :         // send completes, we will resume immediately after this point
     208                 :            :         // (though we have nothing further to do, so the coroutine
     209                 :            :         // will simply exit at that time).
     210         [ +  - ]:         20 :         CORO_YIELD async_write(*socket_, bufs, *this);
           [ -  -  -  + ]
         [ #  # ][ -  + ]
                 [ +  - ]
     211                 :            : 
     212                 :            :         // TODO: should we keep the connection open for a short time
     213                 :            :         // to see if new requests come in?
     214         [ +  - ]:         10 :         socket_->close();
     215                 :            :     }
     216                 :            : }
     217                 :            : 
     218                 :            : /// Call the DNS lookup provider.  (Expected to be called by the
     219                 :            : /// AsyncLookup<TCPServer> handler.)
     220                 :            : void
     221                 :         10 : TCPServer::asyncLookup() {
     222                 :         10 :     (*lookup_callback_)(*io_message_, query_message_,
     223         [ +  - ]:         10 :                         answer_message_, respbuf_, this);
     224                 :         10 : }
     225                 :            : 
     226                 :         44 : void TCPServer::stop() {
     227                 :            :     /// we use close instead of cancel, with the same reason
     228                 :            :     /// with udp server stop, refer to the udp server code
     229                 :            : 
     230                 :         44 :     acceptor_->close();
     231                 :            :     // User may stop the server even when it hasn't started to
     232                 :            :     // run, in that that socket_ is empty
     233         [ +  + ]:         44 :     if (socket_) {
     234                 :         28 :         socket_->close();
     235                 :            :     }
     236                 :         44 : }
     237                 :            : /// Post this coroutine on the ASIO service queue so that it will
     238                 :            : /// resume processing where it left off.  The 'done' parameter indicates
     239                 :            : /// whether there is an answer to return to the client.
     240                 :            : void
     241                 :         10 : TCPServer::resume(const bool done) {
     242                 :         10 :     done_ = done;
     243         [ +  - ]:         10 :     io_.post(*this);
     244                 :         10 : }
     245                 :            : 
     246                 :            : } // namespace asiodns
     247 [ +  - ][ +  - ]:         25 : } // namespace isc

Generated by: LCOV version 1.9