LCOV - code coverage report
Current view: top level - asiodns - udp_server.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 60 62 96.8 %
Date: 2012-05-15 Functions: 9 10 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 81 176 46.0 %

           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 <unistd.h>             // for some IPC/network system calls
      16                 :            : #include <netinet/in.h>
      17                 :            : #include <sys/socket.h>
      18                 :            : #include <errno.h>
      19                 :            : 
      20                 :            : #include <boost/shared_array.hpp>
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include <log/dummylog.h>
      25                 :            : 
      26                 :            : #include <asio.hpp>
      27                 :            : #include <asio/error.hpp>
      28                 :            : #include <asiolink/dummy_io_cb.h>
      29                 :            : #include <asiolink/udp_endpoint.h>
      30                 :            : #include <asiolink/udp_socket.h>
      31                 :            : #include "udp_server.h"
      32                 :            : #include "logger.h"
      33                 :            : 
      34                 :            : #include <dns/opcode.h>
      35                 :            : 
      36                 :            : using namespace asio;
      37                 :            : using asio::ip::udp;
      38                 :            : using isc::log::dlog;
      39                 :            : 
      40                 :            : using namespace std;
      41                 :            : using namespace isc::dns;
      42                 :            : using namespace isc::util;
      43                 :            : using namespace isc::asiolink;
      44                 :            : 
      45                 :            : namespace isc {
      46                 :            : namespace asiodns {
      47                 :            : 
      48                 :            : /*
      49                 :            :  * Some of the member variables here are shared_ptrs and some are
      50                 :            :  * auto_ptrs. There will be one instance of Data for the lifetime
      51                 :            :  * of packet. The variables that are state only for a single packet
      52                 :            :  * use auto_ptr, as it is more lightweight. In the case of shared
      53                 :            :  * configuration (eg. the callbacks, socket), we use shared_ptrs.
      54                 :            :  */
      55                 :        141 : struct UDPServer::Data {
      56                 :            :     /*
      57                 :            :      * Constructors from parameters passed to UDPServer constructor.
      58                 :            :      * This instance will not be used to retrieve and answer the actual
      59                 :            :      * query, it will only hold parameters until we wait for the
      60                 :            :      * first packet. But we do initialize the socket in here.
      61                 :            :      */
      62                 :            :     Data(io_service& io_service, const ip::address& addr, const uint16_t port,
      63                 :            :         SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
      64                 :            :         io_(io_service), done_(false),
      65                 :            :         checkin_callback_(checkin),lookup_callback_(lookup),
      66                 :            :         answer_callback_(answer)
      67                 :            :     {
      68                 :            :         // We must use different instantiations for v4 and v6;
      69                 :            :         // otherwise ASIO will bind to both
      70                 :            :         udp proto = addr.is_v4() ? udp::v4() : udp::v6();
      71                 :            :         socket_.reset(new udp::socket(io_service, proto));
      72                 :            :         socket_->set_option(socket_base::reuse_address(true));
      73                 :            :         if (addr.is_v6()) {
      74                 :            :             socket_->set_option(asio::ip::v6_only(true));
      75                 :            :         }
      76                 :            :         socket_->bind(udp::endpoint(addr, port));
      77                 :            :     }
      78                 :         68 :     Data(io_service& io_service, int fd, int af, SimpleCallback* checkin,
      79                 :            :          DNSLookup* lookup, DNSAnswer* answer) :
      80                 :            :          io_(io_service), done_(false),
      81                 :            :          checkin_callback_(checkin),lookup_callback_(lookup),
      82         [ +  - ]:         68 :          answer_callback_(answer)
      83                 :            :     {
      84         [ +  + ]:         68 :         if (af != AF_INET && af != AF_INET6) {
      85 [ +  - ][ +  - ]:          2 :             isc_throw(InvalidParameter, "Address family must be either AF_INET "
                 [ +  - ]
      86                 :            :                       "or AF_INET6, not " << af);
      87                 :            :         }
      88 [ +  - ][ +  - ]:         67 :         LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
         [ +  - ][ +  - ]
                 [ +  - ]
      89                 :            :         try {
      90 [ +  - ][ +  - ]:        134 :             socket_.reset(new udp::socket(io_service));
      91         [ +  + ]:        134 :             socket_->assign(af == AF_INET6 ? udp::v6() : udp::v4(), fd);
      92         [ #  # ]:          0 :         } catch (const std::exception& exception) {
      93                 :            :             // Whatever the thing throws, it is something from ASIO and we
      94                 :            :             // convert it
      95 [ #  # ][ #  # ]:          0 :             isc_throw(IOError, exception.what());
      96                 :            :         }
      97                 :         67 :     }
      98                 :            : 
      99                 :            :     /*
     100                 :            :      * Copy constructor. Default one would probably do, but it is unnecessary
     101                 :            :      * to copy many of the member variables every time we fork to handle
     102                 :            :      * another packet.
     103                 :            :      *
     104                 :            :      * We also allocate data for receiving the packet here.
     105                 :            :      */
     106                 :            :     Data(const Data& other) :
     107                 :            :         io_(other.io_), socket_(other.socket_), done_(false),
     108                 :            :         checkin_callback_(other.checkin_callback_),
     109                 :            :         lookup_callback_(other.lookup_callback_),
     110         [ +  - ]:         74 :         answer_callback_(other.answer_callback_)
     111                 :            :     {
     112                 :            :         // Instantiate the data buffer and endpoint that will
     113                 :            :         // be used by the asynchronous receive call.
     114         [ +  - ]:         74 :         data_.reset(new char[MAX_LENGTH]);
     115         [ +  - ]:         74 :         sender_.reset(new udp::endpoint());
     116                 :            :     }
     117                 :            : 
     118                 :            :     // The ASIO service object
     119                 :            :     asio::io_service& io_;
     120                 :            : 
     121                 :            :     // Class member variables which are dynamic, and changes to which
     122                 :            :     // need to accessible from both sides of a coroutine fork or from
     123                 :            :     // outside of the coroutine (i.e., from an asynchronous I/O call),
     124                 :            :     // should be declared here as pointers and allocated in the
     125                 :            :     // constructor or in the coroutine.  This allows state information
     126                 :            :     // to persist when an individual copy of the coroutine falls out
     127                 :            :     // scope while waiting for an event, *so long as* there is another
     128                 :            :     // object that is referencing the same data.  As a side-benefit, using
     129                 :            :     // pointers also reduces copy overhead for coroutine objects.
     130                 :            :     //
     131                 :            :     // Note: Currently these objects are allocated by "new" in the
     132                 :            :     // constructor, or in the function operator while processing a query.
     133                 :            :     // Repeated allocations from the heap for every incoming query is
     134                 :            :     // clearly a performance issue; this must be optimized in the future.
     135                 :            :     // The plan is to have a structure pre-allocate several "Data"
     136                 :            :     // objects which can be pulled off a free list and placed on an in-use
     137                 :            :     // list whenever a query comes in.  This will serve the dual purpose
     138                 :            :     // of improving performance and guaranteeing that state information
     139                 :            :     // will *not* be destroyed when any one instance of the coroutine
     140                 :            :     // falls out of scope while waiting for an event.
     141                 :            :     //
     142                 :            :     // Socket used to for listen for queries.  Created in the
     143                 :            :     // constructor and stored in a shared_ptr because socket objects
     144                 :            :     // are not copyable.
     145                 :            :     boost::shared_ptr<asio::ip::udp::socket> socket_;
     146                 :            : 
     147                 :            :     // The ASIO-internal endpoint object representing the client
     148                 :            :     std::auto_ptr<asio::ip::udp::endpoint> sender_;
     149                 :            : 
     150                 :            :     // \c IOMessage and \c Message objects to be passed to the
     151                 :            :     // DNS lookup and answer providers
     152                 :            :     std::auto_ptr<asiolink::IOMessage> io_message_;
     153                 :            : 
     154                 :            :     // The original query as sent by the client
     155                 :            :     isc::dns::MessagePtr query_message_;
     156                 :            : 
     157                 :            :     // The response message we are building
     158                 :            :     isc::dns::MessagePtr answer_message_;
     159                 :            : 
     160                 :            :     // The buffer into which the response is written
     161                 :            :     isc::util::OutputBufferPtr respbuf_;
     162                 :            : 
     163                 :            :     // The buffer into which the query packet is written
     164                 :            :     boost::shared_array<char> data_;
     165                 :            : 
     166                 :            :     // State information that is entirely internal to a given instance
     167                 :            :     // of the coroutine can be declared here.
     168                 :            :     size_t bytes_;
     169                 :            :     bool done_;
     170                 :            : 
     171                 :            : 
     172                 :            :     // Callback functions provided by the caller
     173                 :            :     const SimpleCallback* checkin_callback_;
     174                 :            :     const DNSLookup* lookup_callback_;
     175                 :            :     const DNSAnswer* answer_callback_;
     176                 :            : 
     177                 :            :     std::auto_ptr<IOEndpoint> peer_;
     178                 :            :     std::auto_ptr<IOSocket> iosock_;
     179                 :            : };
     180                 :            : 
     181                 :            : /// The following functions implement the \c UDPServer class.
     182                 :            : ///
     183                 :            : /// The constructor. It just creates new internal state object
     184                 :            : /// and lets it handle the initialization.
     185                 :         68 : UDPServer::UDPServer(io_service& io_service, int fd, int af,
     186                 :            :                      SimpleCallback* checkin, DNSLookup* lookup,
     187                 :            :                      DNSAnswer* answer) :
     188 [ +  - ][ +  + ]:         68 :     data_(new Data(io_service, fd, af, checkin, lookup, answer))
         [ +  - ][ #  # ]
     189                 :         67 : { }
     190                 :            : 
     191                 :            : /// The function operator is implemented with the "stackless coroutine"
     192                 :            : /// pattern; see internal/coroutine.h for details.
     193                 :            : void
     194                 :        105 : UDPServer::operator()(asio::error_code ec, size_t length) {
     195                 :            :     /// Because the coroutine reentry block is implemented as
     196                 :            :     /// a switch statement, inline variable declarations are not
     197                 :            :     /// permitted.  Certain variables used below can be declared here.
     198                 :            : 
     199   [ -  +  +  +  :        105 :     CORO_REENTER (this) {
             -  +  -  + ]
                 [ #  # ]
     200         [ +  + ]:         26 :         do {
     201                 :            :             /*
     202                 :            :              * This is preparation for receiving a packet. We get a new
     203                 :            :              * state object for the lifetime of the next packet to come.
     204                 :            :              * It allocates the buffers to receive data into.
     205                 :            :              */
     206         [ +  - ]:         74 :             data_.reset(new Data(*data_));
     207                 :            : 
     208 [ +  - ][ +  - ]:         13 :             do {
                 [ -  + ]
     209                 :            :                 // Begin an asynchronous receive, then yield.
     210                 :            :                 // When the receive event is posted, the coroutine
     211                 :            :                 // will resume immediately after this point.
     212         [ +  - ]:        222 :                 CORO_YIELD data_->socket_->async_receive_from(
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     213                 :         74 :                     buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
     214         [ +  - ]:        222 :                     *this);
     215                 :            : 
     216                 :            :                 // Abort on fatal errors
     217                 :            :                 // TODO: add log
     218         [ +  + ]:         19 :                 if (ec) {
     219                 :            :                     using namespace asio::error;
     220 [ +  - ][ +  - ]:          6 :                     if (ec.value() != would_block && ec.value() != try_again &&
         [ -  + ][ -  + ]
     221                 :            :                         ec.value() != interrupted) {
     222                 :            :                         return;
     223                 :            :                     }
     224                 :            :                 }
     225                 :            : 
     226                 :            :             } while (ec || length == 0);
     227                 :            : 
     228                 :         13 :             data_->bytes_ = length;
     229                 :            : 
     230                 :            :             /*
     231                 :            :              * We fork the coroutine now. One (the child) will keep
     232                 :            :              * the current state and handle the packet, then die and
     233                 :            :              * drop ownership of the state. The other (parent) will just
     234                 :            :              * go into the loop again and replace the current state with
     235                 :            :              * a new one for a new object.
     236                 :            :              *
     237                 :            :              * Actually, both of the coroutines will be a copy of this
     238                 :            :              * one, but that's just internal implementation detail.
     239                 :            :              */
     240 [ +  + ][ +  - ]:         26 :             CORO_FORK data_->io_.post(UDPServer(*this));
                 [ +  - ]
     241                 :         26 :         } while (is_parent());
     242                 :            : 
     243                 :            :         // Create an \c IOMessage object to store the query.
     244                 :            :         //
     245                 :            :         // (XXX: It would be good to write a factory function
     246                 :            :         // that would quickly generate an IOMessage object without
     247                 :            :         // all these calls to "new".)
     248         [ +  - ]:         13 :         data_->peer_.reset(new UDPEndpoint(*data_->sender_));
     249                 :            : 
     250                 :            :         // The UDP socket class has been extended with asynchronous functions
     251                 :            :         // and takes as a template parameter a completion callback class.  As
     252                 :            :         // UDPServer does not use these extended functions (only those defined
     253                 :            :         // in the IOSocket base class) - but needs a UDPSocket to get hold of
     254                 :            :         // the underlying Boost UDP socket - DummyIOCallback is used.  This
     255                 :            :         // provides the appropriate operator() but is otherwise functionless.
     256                 :         13 :         data_->iosock_.reset(
     257         [ +  - ]:         26 :             new UDPSocket<DummyIOCallback>(*data_->socket_));
     258                 :            : 
     259                 :         26 :         data_->io_message_.reset(new IOMessage(data_->data_.get(),
     260         [ +  - ]:         13 :             data_->bytes_, *data_->iosock_, *data_->peer_));
     261                 :            : 
     262                 :            :         // Perform any necessary operations prior to processing an incoming
     263                 :            :         // query (e.g., checking for queued configuration messages).
     264                 :            :         //
     265                 :            :         // (XXX: it may be a performance issue to check in for every single
     266                 :            :         // incoming query; we may wish to throttle this in the future.)
     267         [ +  + ]:         13 :         if (data_->checkin_callback_ != NULL) {
     268         [ +  - ]:          9 :             (*data_->checkin_callback_)(*data_->io_message_);
     269                 :            :         }
     270                 :            : 
     271                 :            :         // If we don't have a DNS Lookup provider, there's no point in
     272                 :            :         // continuing; we exit the coroutine permanently.
     273         [ +  + ]:         13 :         if (data_->lookup_callback_ == NULL) {
     274         [ +  - ]:          4 :             CORO_YIELD return;
           [ -  -  -  + ]
         [ #  # ][ #  # ]
     275                 :            :         }
     276                 :            : 
     277                 :            :         // Instantiate objects that will be needed by the
     278                 :            :         // asynchronous DNS lookup and/or by the send call.
     279 [ +  - ][ +  - ]:          9 :         data_->respbuf_.reset(new OutputBuffer(0));
     280 [ +  - ][ +  - ]:          9 :         data_->query_message_.reset(new Message(Message::PARSE));
                 [ +  - ]
     281 [ +  - ][ +  - ]:          9 :         data_->answer_message_.reset(new Message(Message::RENDER));
                 [ +  - ]
     282                 :            : 
     283                 :            :         // Schedule a DNS lookup, and yield.  When the lookup is
     284                 :            :         // finished, the coroutine will resume immediately after
     285                 :            :         // this point.
     286         [ +  - ]:         27 :         CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     287                 :            : 
     288                 :            :         // The 'done_' flag indicates whether we have an answer
     289                 :            :         // to send back.  If not, exit the coroutine permanently.
     290         [ +  + ]:          7 :         if (!data_->done_) {
     291         [ +  - ]:          2 :             CORO_YIELD return;
           [ -  -  -  + ]
         [ #  # ][ #  # ]
     292                 :            :         }
     293                 :            : 
     294                 :            :         // Call the DNS answer provider to render the answer into
     295                 :            :         // wire format
     296                 :         15 :         (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
     297         [ +  - ]:         15 :             data_->answer_message_, data_->respbuf_);
     298                 :            : 
     299                 :            :         // Begin an asynchronous send, and then yield.  When the
     300                 :            :         // send completes, we will resume immediately after this point
     301                 :            :         // (though we have nothing further to do, so the coroutine
     302                 :            :         // will simply exit at that time).
     303         [ +  - ]:         15 :         CORO_YIELD data_->socket_->async_send_to(
           [ -  -  -  + ]
         [ #  # ][ -  + ]
     304                 :          5 :             buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
     305         [ +  - ]:         15 :             *data_->sender_, *this);
     306                 :            :     }
     307                 :            : }
     308                 :            : 
     309                 :            : /// Call the DNS lookup provider.  (Expected to be called by the
     310                 :            : /// AsyncLookup<UDPServer> handler.)
     311                 :            : void
     312                 :          9 : UDPServer::asyncLookup() {
     313                 :         18 :     (*data_->lookup_callback_)(*data_->io_message_,
     314         [ +  - ]:         36 :         data_->query_message_, data_->answer_message_, data_->respbuf_, this);
     315                 :          9 : }
     316                 :            : 
     317                 :            : /// Stop the UDPServer
     318                 :            : void
     319                 :         24 : UDPServer::stop() {
     320                 :            :     /// Using close instead of cancel, because cancel
     321                 :            :     /// will only cancel the asynchornized event already submitted
     322                 :            :     /// to io service, the events post to io service after
     323                 :            :     /// cancel still can be scheduled by io service, if
     324                 :            :     /// the socket is cloesed, all the asynchronized event
     325                 :            :     /// for it won't be scheduled by io service not matter it is
     326                 :            :     /// submit to io serice before or after close call. And we will
     327                 :            :     //. get bad_descriptor error
     328                 :         24 :     data_->socket_->close();
     329                 :         24 : }
     330                 :            : 
     331                 :            : /// Post this coroutine on the ASIO service queue so that it will
     332                 :            : /// resume processing where it left off.  The 'done' parameter indicates
     333                 :            : /// whether there is an answer to return to the client.
     334                 :            : void
     335                 :          9 : UDPServer::resume(const bool done) {
     336                 :          9 :     data_->done_ = done;
     337         [ +  - ]:          9 :     data_->io_.post(*this);
     338                 :          9 : }
     339                 :            : 
     340                 :            : } // namespace asiodns
     341 [ +  - ][ +  - ]:          9 : } // namespace isc

Generated by: LCOV version 1.9