123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- // Copyright (C) 2013 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 NCR_UDP_LISTENER_H
- #define NCR_UDP_LISTENER_H
- /// @file ncr_udp.h
- /// @brief This file provides UDP socket based implementation for sending and
- /// receiving NameChangeRequests
- ///
- /// These classes are derived from the abstract classes, NameChangeListener
- /// and NameChangeSender (see ncr_io.h).
- ///
- /// The following discussion will refer to three layers of communications:
- ///
- /// * Application layer - This is the business layer which needs to
- /// transport NameChangeRequests, and is unaware of the means by which
- /// they are transported.
- ///
- /// * IO layer - This is the low-level layer that is directly responsible
- /// for sending and receiving data asynchronously and is supplied through
- /// other libraries. This layer is largely unaware of the nature of the
- /// data being transmitted. In other words, it doesn't know beans about
- /// NCRs.
- ///
- /// * NameChangeRequest layer - This is the layer which acts as the
- /// intermediary between the Application layer and the IO layer. It must
- /// be able to move NameChangeRequests to the IO layer as raw data and move
- /// raw data from the IO layer in the Application layer as
- /// NameChangeRequests.
- ///
- /// This file defines NameChangeUDPListener class for receiving NCRs, and
- /// NameChangeUDPSender for sending NCRs.
- ///
- /// Both the listener and sender implementations utilize the same underlying
- /// construct to move NCRs to and from a UDP socket. This construct consists
- /// of a set of classes centered around isc::asiolink::UDPSocket. UDPSocket
- /// is a templated class that supports asio asynchronous event processing; and
- /// which accepts as its parameter, the name of a callback class.
- ///
- /// The asynchronous services provided by UDPSocket typically accept a buffer
- /// for transferring data (either in or out depending on the service direction)
- /// and an object which supplies a callback to invoke upon completion of the
- /// service.
- ///
- /// The callback class must provide an operator() with the following signature:
- /// @code
- /// void operator ()(const asio::error_code error_code,
- /// const size_t bytes_transferred);
- /// @endcode
- ///
- /// Upon completion of the service, the callback instance's operator() is
- /// invoked by the aiso layer. It is given both a outcome result and the
- /// number of bytes either read or written, to or from the buffer supplied
- /// to the service.
- ///
- /// Typically, an asiolink based implementation would simply implement the
- /// callback operator directly. However, the nature of the asiolink library
- /// is such that the callback object may be copied several times during course
- /// of a service invocation. This implies that any class being used as a
- /// callback class must be copyable. This is not always desirable. In order
- /// to separate the callback class from the NameChangeRequest, the construct
- /// defines the UDPCallback class for use as a copyable, callback object.
- ///
- /// The UDPCallback class provides the asiolink layer callback operator(),
- /// which is invoked by the asiolink layer upon service completion. It
- /// contains:
- /// * a pointer to the transfer buffer
- /// * the capacity of the transfer buffer
- /// * a IO layer outcome result
- /// * the number of bytes transferred
- /// * a method pointer to a NameChangeRequest layer completion handler
- ///
- /// This last item, is critical. It points to an instance method that
- /// will be invoked by the UDPCallback operator. This provides access to
- /// the outcome of the service call to the NameChangeRequest layer without
- /// that layer being used as the actual callback object.
- ///
- /// The completion handler method signature is codified in the typedef,
- /// UDPCompletionHandler, and must be as follows:
- ///
- /// @code
- /// void(const bool, const UDPCallback*)
- /// @endcode
- ///
- /// Note that is accepts two parameters. The first is a boolean indicator
- /// which indicates if the service call completed successfully or not. The
- /// second is a pointer to the callback object invoked by the IOService upon
- /// completion of the service. The callback instance will contain all of the
- /// pertinent information about the invocation and outcome of the service.
- ///
- /// Using the contents of the callback, it is the responsibility of the
- /// UDPCompletionHandler to interpret the results of the service invocation and
- /// pass the interpretation to the application layer via either
- /// NameChangeListener::invokeRecvHandler in the case of the UDP listener, or
- /// NameChangeSender::invokeSendHandler in the case of UDP sender.
- ///
- #include <asio.hpp>
- #include <asiolink/io_address.h>
- #include <asiolink/io_service.h>
- #include <asiolink/udp_endpoint.h>
- #include <asiolink/udp_socket.h>
- #include <d2/ncr_io.h>
- #include <util/buffer.h>
- #include <boost/shared_array.hpp>
- /// responsibility of the completion handler to perform the steps necessary
- /// to interpret the raw data provided by the service outcome. The
- /// UDPCallback operator implementation is mostly a pass through.
- ///
- namespace isc {
- namespace d2 {
- /// @brief Thrown when a UDP level exception occurs.
- class NcrUDPError : public isc::Exception {
- public:
- NcrUDPError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) { };
- };
- class UDPCallback;
- /// @brief Defines a function pointer for NameChangeRequest completion handlers.
- typedef boost::function<void(const bool, const UDPCallback*)>
- UDPCompletionHandler;
- /// @brief Defines a dynamically allocated shared array.
- typedef boost::shared_array<uint8_t> RawBufferPtr;
- typedef boost::shared_ptr<asiolink::UDPEndpoint> UDPEndpointPtr;
- /// @brief Implements the callback class passed into UDPSocket calls.
- ///
- /// It serves as the link between the asiolink::UDPSocket asynchronous services
- /// and the NameChangeRequest layer. The class provides the asiolink layer
- /// callback operator(), which is invoked by the asiolink layer upon service
- /// completion. It contains all of the data pertinent to both the invocation
- /// and completion of a service, as well as a pointer to NameChangeRequest
- /// layer completion handler to invoke.
- ///
- class UDPCallback {
- public:
- /// @brief Container class which stores service invocation related data.
- ///
- /// Because the callback object may be copied numerous times during the
- /// course of service invocation, it does not directly contain data values.
- /// Rather it will retain a shared pointer to an instance of this structure
- /// thus ensuring that all copies of the callback object, ultimately refer
- /// to the same data values.
- struct Data {
- /// @brief Constructor
- ///
- /// @param buffer is a pointer to the data transfer buffer. This is
- /// the buffer data will be written to on a read, or read from on a
- /// send.
- /// @param buf_size is the capacity of the buffer
- /// @param data_source storage for UDP endpoint which supplied the data
- Data(RawBufferPtr buffer, size_t buf_size, UDPEndpointPtr data_source)
- : buffer_(buffer), buf_size_(buf_size), data_source_(data_source),
- put_len_(0), error_code_(), bytes_transferred_(0) {
- };
- /// @brief A pointer to the data transfer buffer.
- RawBufferPtr buffer_;
- /// @brief Storage capacity of the buffer.
- size_t buf_size_;
- /// @brief The UDP endpoint that is the origin of the data transferred.
- UDPEndpointPtr data_source_;
- /// @brief Stores this size of the data within the buffer when written
- /// there manually. (See UDPCallback::putData()) .
- size_t put_len_;
- /// @brief Stores the IO layer result code of the completed IO service.
- asio::error_code error_code_;
- /// @brief Stores the number of bytes transferred by completed IO
- /// service.
- /// For a read it is the number of bytes written into the
- /// buffer. For a write it is the number of bytes read from the
- /// buffer.
- size_t bytes_transferred_;
- };
- /// @brief Used as the callback object for UDPSocket services.
- ///
- /// @param buffer is a pointer to the data transfer buffer. This is
- /// the buffer data will be written to on a read, or read from on a
- /// send.
- /// @param buf_size is the capacity of the buffer
- /// @param data_source storage for UDP endpoint which supplied the data
- /// @param handler is a method pointer to the completion handler that
- /// is to be called by the operator() implementation.
- ///
- /// @throw throws a NcrUDPError if either the handler or buffer pointers
- /// are invalid.
- UDPCallback (RawBufferPtr buffer, size_t buf_size,
- UDPEndpointPtr data_source,
- const UDPCompletionHandler& handler);
- /// @brief Operator that will be invoked by the asiolink layer.
- ///
- /// @param error_code is the IO layer result code of the
- /// completed IO service.
- /// @param bytes_transferred is the number of bytes transferred by
- /// completed IO.
- /// For a read it is the number of bytes written into the
- /// buffer. For a write it is the number of bytes read from the
- /// buffer.
- void operator ()(const asio::error_code error_code,
- const size_t bytes_transferred);
- /// @brief Returns the number of bytes transferred by the completed IO
- /// service.
- ///
- /// For a read it is the number of bytes written into the
- /// buffer. For a write it is the number of bytes read from the
- /// buffer.
- size_t getBytesTransferred() const {
- return (data_->bytes_transferred_);
- }
- /// @brief Sets the number of bytes transferred.
- ///
- /// @param value is the new value to assign to bytes transferred.
- void setBytesTransferred(const size_t value) {
- data_->bytes_transferred_ = value;
- }
- /// @brief Returns the completed IO layer service outcome status.
- asio::error_code getErrorCode() const {
- return (data_->error_code_);
- }
- /// @brief Sets the completed IO layer service outcome status.
- ///
- /// @param value is the new value to assign to outcome status.
- void setErrorCode(const asio::error_code value) {
- data_->error_code_ = value;
- }
- /// @brief Returns the data transfer buffer.
- RawBufferPtr getBuffer() const {
- return (data_->buffer_);
- }
- /// @brief Returns the data transfer buffer capacity.
- const size_t getBufferSize() const {
- return (data_->buf_size_);
- }
- /// @brief Returns a pointer the data transfer buffer content.
- const uint8_t* getData() const {
- return (data_->buffer_.get());
- }
- /// @brief Copies data into the data transfer buffer.
- ///
- /// Copies the given number of bytes from the given source buffer
- /// into the data transfer buffer, and updates the value of put length.
- /// This method may be used when performing sends to make a copy of
- /// the "raw data" that was shipped (or attempted) accessible to the
- /// upstream callback.
- ///
- /// @param src is a pointer to the data source from which to copy
- /// @param len is the number of bytes to copy
- ///
- /// @throw throws a NcrUDPError if the number of bytes to copy exceeds
- /// the buffer capacity or if the source pointer is invalid.
- void putData(const uint8_t* src, size_t len);
- /// @brief Returns the number of bytes manually written into the
- /// transfer buffer.
- const size_t getPutLen() const {
- return (data_->put_len_);
- }
- /// @brief Sets the data source to the given endpoint.
- ///
- /// @param endpoint is the new value to assign to data source.
- void setDataSource(UDPEndpointPtr endpoint) {
- data_->data_source_ = endpoint;
- }
- /// @brief Returns the UDP endpoint that provided the transferred data.
- UDPEndpointPtr getDataSource() {
- return (data_->data_source_);
- }
- private:
- /// @brief NameChangeRequest layer completion handler to invoke.
- UDPCompletionHandler handler_;
- /// @brief Shared pointer to the service data container.
- boost::shared_ptr<Data> data_;
- };
- /// @brief Convenience type for UDP socket based listener
- typedef isc::asiolink::UDPSocket<UDPCallback> NameChangeUDPSocket;
- /// @brief Provides the ability to receive NameChangeRequests via UDP socket
- ///
- /// This class is a derivation of the NameChangeListener which is capable of
- /// receiving NameChangeRequests through a UDP socket. The caller need only
- /// supply network addressing and a RequestReceiveHandler instance to receive
- /// NameChangeRequests asynchronously.
- class NameChangeUDPListener : public NameChangeListener {
- public:
- /// @brief Defines the maximum size packet that can be received.
- static const size_t RECV_BUF_MAX = isc::asiolink::
- UDPSocket<UDPCallback>::MIN_SIZE;
- /// @brief Constructor
- ///
- /// @param ip_address is the network address on which to listen
- /// @param port is the IP port on which to listen
- /// @param format is the wire format of the inbound requests. Currently
- /// only JSON is supported
- /// @param ncr_recv_handler the receive handler object to notify when
- /// when a receive completes.
- /// @param reuse_address enables IP address sharing when true
- /// It defaults to false.
- ///
- /// @throw base class throws NcrListenerError if handler is invalid.
- NameChangeUDPListener(const isc::asiolink::IOAddress& ip_address,
- const uint32_t port,
- const NameChangeFormat format,
- const RequestReceiveHandler* ncr_recv_handler,
- const bool reuse_address = false);
- /// @brief Destructor.
- virtual ~NameChangeUDPListener();
- /// @brief Opens a UDP socket using the given IOService.
- ///
- /// Creates a NameChangeUDPSocket bound to the listener's ip address
- /// and port, that is monitored by the given IOService instance.
- ///
- /// @param io_service the IOService which will monitor the socket.
- ///
- /// @throw throws a NcrUDPError if the open fails.
- virtual void open(isc::asiolink::IOService& io_service);
- /// @brief Closes the UDPSocket.
- ///
- /// It first invokes the socket's cancel method which should stop any
- /// pending read and remove the socket callback from the IOService. It
- /// then calls the socket's close method to actually close the socket.
- ///
- /// @throw throws a NcrUDPError if the open fails.
- virtual void close();
- /// @brief Initiates an asynchronous read on the socket.
- ///
- /// Invokes the asyncReceive() method on the socket passing in the
- /// recv_callback_ member's transfer buffer as the receive buffer, and
- /// recv_callback_ itself as the callback object.
- ///
- /// @throw throws a NcrUDPError if the open fails.
- void doReceive();
- /// @brief Implements the NameChangeRequest level receive completion
- /// handler.
- ///
- /// This method is invoked by the UPDCallback operator() implementation,
- /// passing in the boolean success indicator and pointer to itself.
- ///
- /// If the indicator denotes success, then the method will attempt to
- /// to construct a NameChangeRequest from the received data. If the
- /// construction was successful, it will send the new NCR to the
- /// application layer by calling invokeRecvHandler() with a success
- /// status and a pointer to the new NCR.
- ///
- /// If the buffer contains invalid data such that construction fails,
- /// the method will log the failure and then call doReceive() to start a
- /// initiate the next receive.
- ///
- /// If the indicator denotes failure the method will log the failure and
- /// notify the application layer by calling invokeRecvHandler() with
- /// an error status and an empty pointer.
- ///
- /// @param successful boolean indicator that should be true if the
- /// socket receive completed without error, false otherwise.
- /// @param recv_callback pointer to the callback instance which handled
- /// the socket receive completion.
- void recv_completion_handler(bool successful,
- const UDPCallback* recv_callback);
- private:
- /// @brief IP address on which to listen for requests.
- isc::asiolink::IOAddress ip_address_;
- /// @brief Port number on which to listen for requests.
- uint32_t port_;
- /// @brief Wire format of the inbound requests.
- NameChangeFormat format_;
- /// @brief Low level socket underneath the listening socket
- boost::shared_ptr<asio::ip::udp::socket> asio_socket_;
- /// @brief NameChangeUDPSocket listening socket
- boost::shared_ptr<NameChangeUDPSocket> socket_;
- /// @brief Pointer to the receive callback
- boost::shared_ptr<UDPCallback> recv_callback_;
- /// @brief indicator that signifies listener is being used
- /// in test mode
- bool reuse_address_;
- ///
- /// @name Copy and constructor assignment operator
- ///
- /// The copy constructor and assignment operator are private to avoid
- /// potential issues with multiple listeners attempting to share sockets
- /// and callbacks.
- private:
- NameChangeUDPListener(const NameChangeUDPListener& source);
- NameChangeUDPListener& operator=(const NameChangeUDPListener& source);
- //@}
- };
- /// @brief Provides the ability to send NameChangeRequests via UDP socket
- ///
- /// This class is a derivation of the NameChangeSender which is capable of
- /// sending NameChangeRequests through a UDP socket. The caller need only
- /// supply network addressing and a RequestSendHandler instance to send
- /// NameChangeRequests asynchronously.
- class NameChangeUDPSender : public NameChangeSender {
- public:
- /// @brief Defines the maximum size packet that can be sent.
- static const size_t SEND_BUF_MAX = NameChangeUDPListener::RECV_BUF_MAX;
- /// @brief Constructor
- ///
- /// @param ip_address the IP address from which to send
- /// @param port the port from which to send
- /// @param server_address the IP address of the target listener
- /// @param server_port is the IP port of the target listener
- /// @param format is the wire format of the outbound requests.
- /// @param ncr_send_handler the send handler object to notify when
- /// when a send completes.
- /// @param send_que_max sets the maximum number of entries allowed in
- /// the send queue.
- /// It defaults to NameChangeSender::MAX_QUE_DEFAULT
- /// @param reuse_address enables IP address sharing when true
- /// It defaults to false.
- ///
- /// @throw base class throws NcrSenderError if handler is invalid.
- NameChangeUDPSender(const isc::asiolink::IOAddress& ip_address,
- const uint32_t port, const isc::asiolink::IOAddress& server_address,
- const uint32_t server_port, const NameChangeFormat format,
- RequestSendHandler * ncr_send_handler,
- const size_t send_que_max = NameChangeSender::MAX_QUE_DEFAULT,
- const bool reuse_address = false);
- /// @brief Destructor
- virtual ~NameChangeUDPSender();
- /// @brief Opens a UDP socket using the given IOService.
- ///
- /// Creates a NameChangeUDPSocket bound to the sender's IP address
- /// and port, that is monitored by the given IOService instance.
- ///
- /// @param io_service the IOService which will monitor the socket.
- ///
- /// @throw throws a NcrUDPError if the open fails.
- virtual void open(isc::asiolink::IOService & io_service);
- /// @brief Closes the UDPSocket.
- ///
- /// It first invokes the socket's cancel method which should stop any
- /// pending send and remove the socket callback from the IOService. It
- /// then calls the socket's close method to actually close the socket.
- ///
- /// @throw throws a NcrUDPError if the open fails.
- virtual void close();
- /// @brief Sends a given request asynchronously over the socket
- ///
- /// The given NameChangeRequest is converted to wire format and copied
- /// into the send callback's transfer buffer. Then the socket's
- /// asyncSend() method is called, passing in send_callback_ member's
- /// transfer buffer as the send buffer and the send_callback_ itself
- /// as the callback object.
- virtual void doSend(NameChangeRequestPtr ncr);
- /// @brief Implements the NameChangeRequest level send completion handler.
- ///
- /// This method is invoked by the UDPCallback operator() implementation,
- /// passing in the boolean success indicator and pointer to itself.
- ///
- /// If the indicator denotes success, then the method will notify the
- /// application layer by calling invokeSendHandler() with a success
- /// status.
- ///
- /// If the indicator denotes failure the method will log the failure and
- /// notify the application layer by calling invokeRecvHandler() with
- /// an error status.
- ///
- /// @param successful boolean indicator that should be true if the
- /// socket send completed without error, false otherwise.
- /// @param send_callback pointer to the callback instance which handled
- /// the socket receive completion.
- void send_completion_handler(const bool successful,
- const UDPCallback* send_callback);
- private:
- /// @brief IP address from which to send.
- isc::asiolink::IOAddress ip_address_;
- /// @brief Port from which to send.
- uint32_t port_;
- /// @brief IP address of the target listener.
- isc::asiolink::IOAddress server_address_;
- /// @brief Port of the target listener.
- uint32_t server_port_;
- /// @brief Wire format of the outbound requests.
- NameChangeFormat format_;
- /// @brief Low level socket underneath the sending socket.
- boost::shared_ptr<asio::ip::udp::socket> asio_socket_;
- /// @brief NameChangeUDPSocket sending socket.
- boost::shared_ptr<NameChangeUDPSocket> socket_;
- /// @brief Endpoint of the target listener.
- boost::shared_ptr<isc::asiolink::UDPEndpoint> server_endpoint_;
- /// @brief Pointer to the send callback
- boost::shared_ptr<UDPCallback> send_callback_;
- /// @brief boolean indicator that signifies sender is being used
- /// in test mode
- bool reuse_address_;
- };
- } // namespace isc::d2
- } // namespace isc
- #endif
|