%PDF- %PDF-
| Direktori : /backups/router/usr/local/include/kea/dhcp_ddns/ |
| Current File : //backups/router/usr/local/include/kea/dhcp_ddns/ncr_udp.h |
// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#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 boost::system::error_code error_code,
/// const size_t bytes_transferred);
/// @endcode
///
/// Upon completion of the service, the callback instance's operator() is
/// invoked by the asio 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 <asiolink/asio_wrapper.h>
#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <dhcp_ddns/ncr_io.h>
#include <util/buffer.h>
#include <util/watch_socket.h>
#include <boost/shared_array.hpp>
#include <boost/enable_shared_from_this.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 dhcp_ddns {
/// @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 std::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, const 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.
boost::system::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 NcrUDPError if either the handler or buffer pointers
/// are invalid.
UDPCallback(RawBufferPtr& buffer, const 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 boost::system::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.
boost::system::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 boost::system::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.
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 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.
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.
const 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 UDP 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
/// 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,
RequestReceiveHandlerPtr 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 NcrUDPError if the open fails.
virtual void open(const isc::asiolink::IOServicePtr& 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 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 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 receiveCompletionHandler(const bool successful,
const UDPCallback* recv_callback);
private:
/// @brief The IO service used to handle events.
isc::asiolink::IOServicePtr io_service_;
/// @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<boost::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 Flag which enables the reuse address socket option if true.
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_QUEUE_DEFAULT
/// @param reuse_address enables IP address sharing when true
/// It defaults to false.
///
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,
RequestSendHandlerPtr ncr_send_handler,
const size_t send_que_max = NameChangeSender::MAX_QUEUE_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 NcrUDPError if the open fails.
virtual void open(const isc::asiolink::IOServicePtr& 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 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.
/// @param ncr NameChangeRequest to send.
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 sendCompletionHandler(const bool successful,
const UDPCallback* send_callback);
/// @brief Returns a file descriptor suitable for use with select
///
/// The value returned is an open file descriptor which can be used with
/// select() system call to monitor the sender for IO events. This allows
/// NameChangeUDPSenders to be used in applications which use select,
/// rather than IOService to wait for IO events to occur.
///
/// @warning Attempting other use of this value may lead to unpredictable
/// behavior in the sender.
///
/// @return Returns an "open" file descriptor
///
/// @throw NcrSenderError if the sender is not in send mode,
virtual int getSelectFd();
/// @brief Returns whether or not the sender has IO ready to process.
///
/// @return true if the sender has at IO ready, false otherwise.
virtual bool ioReady();
private:
/// @brief Closes watch socket if the socket is open.
///
/// This method closes watch socket if its instance exists and if the
/// socket is open. An error message is logged when this operation fails.
void closeWatchSocket();
/// @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<boost::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 Flag which enables the reuse address socket option if true.
bool reuse_address_;
/// @brief Pointer to WatchSocket instance supplying the "select-fd".
util::WatchSocketPtr watch_socket_;
};
} // namespace isc::dhcp_ddns
} // namespace isc
#endif