LCOV - code coverage report
Current view: top level - asiolink - udp_socket.h (source / functions) Hit Total Coverage
Test: report.info Lines: 52 58 89.7 %
Date: 2012-05-15 Functions: 21 35 60.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 20 42 47.6 %

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

Generated by: LCOV version 1.9