123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef __UDP_SOCKET_H
- #define __UDP_SOCKET_H 1
- #ifndef ASIO_HPP
- #error "asio.hpp must be included before including this, see asiolink.h as to why"
- #endif
- #include <log/dummylog.h>
- #include <netinet/in.h>
- #include <sys/socket.h>
- #include <unistd.h> // for some IPC/network system calls
- #include <cstddef>
- #include <config.h>
- #include <asiolink/io_asio_socket.h>
- #include <asiolink/io_endpoint.h>
- #include <asiolink/io_service.h>
- #include <asiolink/udp_endpoint.h>
- namespace asiolink {
- /// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
- /// that represents a UDP socket.
- ///
- /// \param C Callback type
- template <typename C>
- class UDPSocket : public IOAsioSocket<C> {
- private:
- /// \brief Class is non-copyable
- UDPSocket(const UDPSocket&);
- UDPSocket& operator=(const UDPSocket&);
- public:
- enum {
- MIN_SIZE = 4096 // Minimum send and receive size
- };
- /// \brief Constructor from an ASIO UDP socket.
- ///
- /// \param socket The ASIO representation of the UDP socket. It is assumed
- /// that the caller will open and close the socket, so these
- /// operations are a no-op for that socket.
- UDPSocket(asio::ip::udp::socket& socket);
- /// \brief Constructor
- ///
- /// Used when the UDPSocket is being asked to manage its own internal
- /// socket. In this case, the open() and close() methods are used.
- ///
- /// \param service I/O Service object used to manage the socket.
- UDPSocket(IOService& service);
- /// \brief Destructor
- virtual ~UDPSocket();
- /// \brief Return file descriptor of underlying socket
- virtual int getNative() const {
- return (socket_.native());
- }
- /// \brief Return protocol of socket
- virtual int getProtocol() const {
- return (IPPROTO_UDP);
- }
- /// \brief Is "open()" synchronous?
- ///
- /// Indicates that the opening of a UDP socket is synchronous.
- virtual bool isOpenSynchronous() const {
- return true;
- }
- /// \brief Open Socket
- ///
- /// Opens the UDP socket. This is a synchronous operation.
- ///
- /// \param endpoint Endpoint to which the socket will send data. This is
- /// used to determine the address family trhat should be used for the
- /// underlying socket.
- /// \param callback Unused as the operation is synchronous.
- virtual void open(const IOEndpoint* endpoint, C& callback);
- /// \brief Send Asynchronously
- ///
- /// Calls the underlying socket's async_send_to() method to send a packet of
- /// data asynchronously to the remote endpoint. The callback will be called
- /// on completion.
- ///
- /// \param data Data to send
- /// \param length Length of data to send
- /// \param endpoint Target of the send
- /// \param callback Callback object.
- virtual void asyncSend(const void* data, size_t length,
- const IOEndpoint* endpoint, C& callback);
- /// \brief Receive Asynchronously
- ///
- /// Calls the underlying socket's async_receive_from() method to read a
- /// packet of data from a remote endpoint. Arrival of the data is signalled
- /// via a call to the callback function.
- ///
- /// \param data Buffer to receive incoming message
- /// \param length Length of the data buffer
- /// \param offset Offset into buffer where data is to be put
- /// \param endpoint Source of the communication
- /// \param callback Callback object
- virtual void asyncReceive(void* data, size_t length, size_t offset,
- IOEndpoint* endpoint, C& callback);
- /// \brief Checks if the data received is complete.
- ///
- /// For a UDP socket all the data is received in one I/O, so this is
- /// effectively a no-op (although it does update the amount of data
- /// received).
- ///
- /// \param data Data buffer containing data to date (ignored)
- /// \param length Amount of data in the buffer.
- ///
- /// \return Always true
- virtual bool receiveComplete(const void*, size_t) {
- return (true);
- }
- /// \brief Append Normalized Data
- ///
- /// When a UDP buffer is received, the entire buffer contains the data.
- /// When a TCP buffer is received, the first two bytes of the buffer hold
- /// a length count. This method removes those bytes from the buffer.
- ///
- /// \param inbuf Input buffer. This contains the data received over the
- /// network connection.
- /// \param length Amount of data in the input buffer. If TCP, this includes
- /// the two-byte count field.
- /// \param outbuf Pointer to output buffer to which the data will be
- /// appended
- virtual void appendNormalizedData(const void* inbuf, size_t length,
- isc::dns::OutputBufferPtr outbuf)
- {
- outbuf->writeData(inbuf, length);
- }
- /// \brief Cancel I/O On Socket
- virtual void cancel();
- /// \brief Close socket
- virtual void close();
- private:
- // Two variables to hold the socket - a socket and a pointer to it. This
- // handles the case where a socket is passed to the UDPSocket on
- // construction, or where it is asked to manage its own socket.
- asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
- asio::ip::udp::socket& socket_; ///< Socket
- bool isopen_; ///< true when socket is open
- };
- // Constructor - caller manages socket
- template <typename C>
- UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
- socket_ptr_(NULL), socket_(socket), isopen_(true)
- {
- }
- // Constructor - create socket on the fly
- template <typename C>
- UDPSocket<C>::UDPSocket(IOService& service) :
- socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
- socket_(*socket_ptr_), isopen_(false)
- {
- }
- // Destructor. Only delete the socket if we are managing it.
- template <typename C>
- UDPSocket<C>::~UDPSocket()
- {
- delete socket_ptr_;
- }
- // Open the socket.
- template <typename C> void
- UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
- // Ignore opens on already-open socket. (Don't throw a failure because
- // of uncertainties as to what precedes whan when using asynchronous I/O.)
- // It also allows us a treat a passed-in socket in exactly the same way as
- // a self-managed socket (in that we can call the open() and close() methods
- // of this class).
- if (!isopen_) {
- if (endpoint->getFamily() == AF_INET) {
- socket_.open(asio::ip::udp::v4());
- }
- else {
- socket_.open(asio::ip::udp::v6());
- }
- isopen_ = true;
- // Ensure it can send and receive at least 4K buffers.
- asio::ip::udp::socket::send_buffer_size snd_size;
- socket_.get_option(snd_size);
- if (snd_size.value() < MIN_SIZE) {
- snd_size = MIN_SIZE;
- socket_.set_option(snd_size);
- }
- asio::ip::udp::socket::receive_buffer_size rcv_size;
- socket_.get_option(rcv_size);
- if (rcv_size.value() < MIN_SIZE) {
- rcv_size = MIN_SIZE;
- socket_.set_option(rcv_size);
- }
- }
- }
- // Send a message. Should never do this if the socket is not open, so throw
- // an exception if this is the case.
- template <typename C> void
- UDPSocket<C>::asyncSend(const void* data, size_t length,
- const IOEndpoint* endpoint, C& callback)
- {
- if (isopen_) {
- // Upconvert to a UDPEndpoint. We need to do this because although
- // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
- // does not contain a method for getting at the underlying endpoint
- // type - that is in the derived class and the two classes differ on
- // return type.
- assert(endpoint->getProtocol() == IPPROTO_UDP);
- const UDPEndpoint* udp_endpoint =
- static_cast<const UDPEndpoint*>(endpoint);
- // ... and send the message.
- socket_.async_send_to(asio::buffer(data, length),
- udp_endpoint->getASIOEndpoint(), callback);
- } else {
- isc_throw(SocketNotOpen,
- "attempt to send on a UDP socket that is not open");
- }
- }
- // Receive a message. Should never do this if the socket is not open, so throw
- // an exception if this is the case.
- template <typename C> void
- UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
- IOEndpoint* endpoint, C& callback)
- {
- if (isopen_) {
- // Upconvert the endpoint again.
- assert(endpoint->getProtocol() == IPPROTO_UDP);
- UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
- // Ensure we can write into the buffer
- if (offset >= length) {
- isc_throw(BufferOverflow, "attempt to read into area beyond end of "
- "UDP receive buffer");
- }
- void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
- // Issue the read
- socket_.async_receive_from(asio::buffer(buffer_start, length - offset),
- udp_endpoint->getASIOEndpoint(), callback);
- } else {
- isc_throw(SocketNotOpen,
- "attempt to receive from a UDP socket that is not open");
- }
- }
- // Cancel I/O on the socket. No-op if the socket is not open.
- template <typename C> void
- UDPSocket<C>::cancel() {
- if (isopen_) {
- socket_.cancel();
- }
- }
- // Close the socket down. Can only do this if the socket is open and we are
- // managing it ourself.
- template <typename C> void
- UDPSocket<C>::close() {
- if (isopen_ && socket_ptr_) {
- socket_.close();
- isopen_ = false;
- }
- }
- } // namespace asiolink
- #endif // __UDP_SOCKET_H
|