mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 22:15:23 +00:00
[1452] overall documentation update
This commit is contained in:
@@ -573,7 +573,7 @@ INPUT = ../src/lib/exceptions ../src/lib/cc \
|
|||||||
../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
|
../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
|
||||||
../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
|
../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
|
||||||
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
|
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
|
||||||
../src/bin/sockcreator/ ../src/lib/util/ \
|
../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
|
||||||
../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp
|
../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
|
@@ -73,7 +73,8 @@ struct SocketSessionForwarder::ForwarderImpl {
|
|||||||
SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
|
SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
|
||||||
impl_(NULL)
|
impl_(NULL)
|
||||||
{
|
{
|
||||||
// We need to filter SIGPIPE for subsequent push(). See the description.
|
// We need to filter SIGPIPE for subsequent push(). See the class
|
||||||
|
// description.
|
||||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||||
isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
|
isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
|
||||||
}
|
}
|
||||||
@@ -108,7 +109,7 @@ SocketSessionForwarder::~SocketSessionForwarder() {
|
|||||||
void
|
void
|
||||||
SocketSessionForwarder::connectToReceptor() {
|
SocketSessionForwarder::connectToReceptor() {
|
||||||
if (impl_->fd_ != -1) {
|
if (impl_->fd_ != -1) {
|
||||||
isc_throw(SocketSessionError, "Duplicate connect to UNIX domain "
|
isc_throw(BadValue, "Duplicate connect to UNIX domain "
|
||||||
"endpoint " << impl_->sock_un_.sun_path);
|
"endpoint " << impl_->sock_un_.sun_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,41 +147,40 @@ SocketSessionForwarder::connectToReceptor() {
|
|||||||
void
|
void
|
||||||
SocketSessionForwarder::close() {
|
SocketSessionForwarder::close() {
|
||||||
if (impl_->fd_ == -1) {
|
if (impl_->fd_ == -1) {
|
||||||
isc_throw(SocketSessionError, "Attempt of close before connect");
|
isc_throw(BadValue, "Attempt of close before connect");
|
||||||
}
|
}
|
||||||
::close(impl_->fd_);
|
::close(impl_->fd_);
|
||||||
impl_->fd_ = -1;
|
impl_->fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SocketSessionForwarder::push(int sock, int family, int sock_type, int protocol,
|
SocketSessionForwarder::push(int sock, int family, int type, int protocol,
|
||||||
const struct sockaddr& local_end,
|
const struct sockaddr& local_end,
|
||||||
const struct sockaddr& remote_end,
|
const struct sockaddr& remote_end,
|
||||||
const void* data, size_t data_len)
|
const void* data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (impl_->fd_ == -1) {
|
if (impl_->fd_ == -1) {
|
||||||
isc_throw(SocketSessionError, "Attempt of push before connect");
|
isc_throw(BadValue, "Attempt of push before connect");
|
||||||
}
|
}
|
||||||
if ((local_end.sa_family != AF_INET && local_end.sa_family != AF_INET6) ||
|
if ((local_end.sa_family != AF_INET && local_end.sa_family != AF_INET6) ||
|
||||||
(remote_end.sa_family != AF_INET && remote_end.sa_family != AF_INET6))
|
(remote_end.sa_family != AF_INET && remote_end.sa_family != AF_INET6))
|
||||||
{
|
{
|
||||||
isc_throw(SocketSessionError, "Invalid address family: must be "
|
isc_throw(BadValue, "Invalid address family: must be "
|
||||||
"AF_INET or AF_INET6; " <<
|
"AF_INET or AF_INET6; " <<
|
||||||
static_cast<int>(local_end.sa_family) << ", " <<
|
static_cast<int>(local_end.sa_family) << ", " <<
|
||||||
static_cast<int>(remote_end.sa_family) << " given");
|
static_cast<int>(remote_end.sa_family) << " given");
|
||||||
}
|
}
|
||||||
if (family != local_end.sa_family || family != remote_end.sa_family) {
|
if (family != local_end.sa_family || family != remote_end.sa_family) {
|
||||||
isc_throw(SocketSessionError, "Inconsistent address family: must be "
|
isc_throw(BadValue, "Inconsistent address family: must be "
|
||||||
<< static_cast<int>(family) << "; "
|
<< static_cast<int>(family) << "; "
|
||||||
<< static_cast<int>(local_end.sa_family) << ", "
|
<< static_cast<int>(local_end.sa_family) << ", "
|
||||||
<< static_cast<int>(remote_end.sa_family) << " given");
|
<< static_cast<int>(remote_end.sa_family) << " given");
|
||||||
}
|
}
|
||||||
if (data_len == 0 || data == NULL) {
|
if (data_len == 0 || data == NULL) {
|
||||||
isc_throw(SocketSessionError,
|
isc_throw(BadValue, "Data for a socket session must not be empty");
|
||||||
"Data for a socket session must not be empty");
|
|
||||||
}
|
}
|
||||||
if (data_len > MAX_DATASIZE) {
|
if (data_len > MAX_DATASIZE) {
|
||||||
isc_throw(SocketSessionError, "Invalid socket session data size: " <<
|
isc_throw(BadValue, "Invalid socket session data size: " <<
|
||||||
data_len << ", must not exceed " << MAX_DATASIZE);
|
data_len << ", must not exceed " << MAX_DATASIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ SocketSessionForwarder::push(int sock, int family, int sock_type, int protocol,
|
|||||||
impl_->buf_.skip(sizeof(uint16_t));
|
impl_->buf_.skip(sizeof(uint16_t));
|
||||||
// Socket properties: family, type, protocol
|
// Socket properties: family, type, protocol
|
||||||
impl_->buf_.writeUint32(static_cast<uint32_t>(family));
|
impl_->buf_.writeUint32(static_cast<uint32_t>(family));
|
||||||
impl_->buf_.writeUint32(static_cast<uint32_t>(sock_type));
|
impl_->buf_.writeUint32(static_cast<uint32_t>(type));
|
||||||
impl_->buf_.writeUint32(static_cast<uint32_t>(protocol));
|
impl_->buf_.writeUint32(static_cast<uint32_t>(protocol));
|
||||||
// Local endpoint
|
// Local endpoint
|
||||||
impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end)));
|
impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end)));
|
||||||
@@ -229,10 +229,10 @@ SocketSessionForwarder::push(int sock, int family, int sock_type, int protocol,
|
|||||||
SocketSession::SocketSession(int sock, int family, int type, int protocol,
|
SocketSession::SocketSession(int sock, int family, int type, int protocol,
|
||||||
const sockaddr* local_end,
|
const sockaddr* local_end,
|
||||||
const sockaddr* remote_end,
|
const sockaddr* remote_end,
|
||||||
size_t data_len, const void* data) :
|
const void* data, size_t data_len) :
|
||||||
sock_(sock), family_(family), type_(type), protocol_(protocol),
|
sock_(sock), family_(family), type_(type), protocol_(protocol),
|
||||||
local_end_(local_end), remote_end_(remote_end),
|
local_end_(local_end), remote_end_(remote_end),
|
||||||
data_len_(data_len), data_(data)
|
data_(data), data_len_(data_len)
|
||||||
{
|
{
|
||||||
if (local_end == NULL || remote_end == NULL) {
|
if (local_end == NULL || remote_end == NULL) {
|
||||||
isc_throw(BadValue, "sockaddr must be non NULL for SocketSession");
|
isc_throw(BadValue, "sockaddr must be non NULL for SocketSession");
|
||||||
@@ -264,8 +264,8 @@ struct SocketSessionReceptor::ReceptorImpl {
|
|||||||
struct sockaddr_storage ss_remote_; // placeholder
|
struct sockaddr_storage ss_remote_; // placeholder
|
||||||
struct sockaddr* const sa_remote_;
|
struct sockaddr* const sa_remote_;
|
||||||
|
|
||||||
vector<char> header_buf_;
|
vector<uint8_t> header_buf_;
|
||||||
vector<char> data_buf_;
|
vector<uint8_t> data_buf_;
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketSessionReceptor::SocketSessionReceptor(int fd) :
|
SocketSessionReceptor::SocketSessionReceptor(int fd) :
|
||||||
@@ -363,8 +363,8 @@ SocketSessionReceptor::pop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (SocketSession(passed_fd, family, type, protocol,
|
return (SocketSession(passed_fd, family, type, protocol,
|
||||||
impl_->sa_local_, impl_->sa_remote_, data_len,
|
impl_->sa_local_, impl_->sa_remote_,
|
||||||
&impl_->data_buf_[0]));
|
&impl_->data_buf_[0], data_len));
|
||||||
} catch (const InvalidBufferPosition& ex) {
|
} catch (const InvalidBufferPosition& ex) {
|
||||||
// We catch the case where the given header is too short and convert
|
// We catch the case where the given header is too short and convert
|
||||||
// the exception to SocketSessionError.
|
// the exception to SocketSessionError.
|
||||||
|
@@ -25,26 +25,258 @@ namespace isc {
|
|||||||
namespace util {
|
namespace util {
|
||||||
namespace io {
|
namespace io {
|
||||||
|
|
||||||
|
/// \page SocketSessionUtility Socket session utility
|
||||||
|
///
|
||||||
|
/// This utility defines a set of classes that support forwarding a
|
||||||
|
/// "socket session" from one process to another. A socket session is a
|
||||||
|
/// conceptual tuple of the following elements:
|
||||||
|
/// - A network socket
|
||||||
|
/// - The local and remote endpoints of a (IP) communication taking place on
|
||||||
|
/// the socket. In practice an endpoint is a pair of an IP address and
|
||||||
|
/// TCP or UDP port number.
|
||||||
|
/// - Some amount of data sent from the remote endpoint and received on the
|
||||||
|
/// socket. We call it (socket) session data in this documentation.
|
||||||
|
///
|
||||||
|
/// Note that this is a conceptual definition. Depending on the underlying
|
||||||
|
/// implementation and/or the network protocol, some of the elements could be
|
||||||
|
/// part of others; for example, if it's an established TCP connection,
|
||||||
|
/// the local and remote endpoints would be able to be retrieved from the
|
||||||
|
/// socket using the standard \c getsockname() and \c getpeername() system
|
||||||
|
/// calls. But in this definition we separate these to be more generic.
|
||||||
|
/// Also, as a matter of fact our intended usage includes non-connected UDP
|
||||||
|
/// communications, in which case at least the remote endpoint should be
|
||||||
|
/// provided separately from the socket.
|
||||||
|
///
|
||||||
|
/// In the actual implementation we represent a socket as a tuple of
|
||||||
|
/// socket's file descriptor, address family (e.g. \c AF_INET6),
|
||||||
|
/// socket type (e.g. \c SOCK_STREAM), and protocol (e.g. \c IPPROTO_TCP).
|
||||||
|
/// The latter three are included in the representation of a socket in order
|
||||||
|
/// to provide complete information of how the socket would be created
|
||||||
|
/// by the \c socket(2) system call. More specifically in practice, these
|
||||||
|
/// parameters could be used to construct a Python socket object from the
|
||||||
|
/// file descriptor.
|
||||||
|
///
|
||||||
|
/// We use the standard \c sockaddr structure to represent endpoints.
|
||||||
|
///
|
||||||
|
/// Socket session data is an opaque memory region of an arbitrary length
|
||||||
|
/// (possibly with some reasonable upper limit).
|
||||||
|
///
|
||||||
|
/// To forward a socket session between processes, we use connected UNIX
|
||||||
|
/// domain sockets established between the processes. The file descriptor
|
||||||
|
/// will be forwarded through the sockets as an ancillary data item of
|
||||||
|
/// type \c SCM_RIGHTS. Other elements of the session will be transferred
|
||||||
|
/// as normal data over the connection.
|
||||||
|
///
|
||||||
|
/// We provide three classes to help applications forward socket sessions:
|
||||||
|
/// \c SocketSessionForwarder is the sender of the UNIX domain connection,
|
||||||
|
/// while \c SocketSessionReceptor is the receiver (this interface assumes
|
||||||
|
/// one direction of forwarding); \c SocketSession represents a single
|
||||||
|
/// socket session.
|
||||||
|
///
|
||||||
|
/// \c SocketSessionForwarder and \c SocketSessionReceptor objects use a
|
||||||
|
/// straightforward protocol to pass elements of socket sessions.
|
||||||
|
/// Once the connection is established, the forwarder object first forwards
|
||||||
|
/// the file descriptor with 1-byte dummy data. It then forwards a
|
||||||
|
/// "(socket) session header", which contains all other elements of the session
|
||||||
|
/// except the file descriptor (already forwarded) and session data.
|
||||||
|
/// The wire format of the header is as follows:
|
||||||
|
/// - The length of the header (16-bit unsigned integer)
|
||||||
|
/// - Address family
|
||||||
|
/// - Socket type
|
||||||
|
/// - Protocol
|
||||||
|
/// - Size of the local endpoint in bytes
|
||||||
|
/// - Local endpoint (a copy of the memory image of the corresponding
|
||||||
|
/// \c sockaddr)
|
||||||
|
/// - Size of the remote endpoint in bytes
|
||||||
|
/// - Remote endpoint (same as local endpoint)
|
||||||
|
/// - Size of session data in bytes
|
||||||
|
///
|
||||||
|
/// The type of the fields is 32-bit unsigned integer unless explicitly
|
||||||
|
/// noted, and all fields are formatted in the network byte order.
|
||||||
|
///
|
||||||
|
/// The socket session data immediately follows the session header.
|
||||||
|
///
|
||||||
|
/// Note that the fields do not necessarily be in the network byte order
|
||||||
|
/// because they are expected to be exchanged on the same machine. Likewise,
|
||||||
|
/// integer elements such as address family do not necessarily be represented
|
||||||
|
/// as an fixed-size value (i.e., 32-bit). But fixed size fields are used
|
||||||
|
/// in order to ensure maximum portability in such a (rare) case where the
|
||||||
|
/// forwarder and the receptor are built with different compilers that have
|
||||||
|
/// different definitions of \c int. Also, since \c sockaddr fields are
|
||||||
|
/// generally formatted in the network byte order, other fields are defined
|
||||||
|
/// so to be consistent.
|
||||||
|
///
|
||||||
|
/// One basic assumption in the API of this utility is socket sessions should
|
||||||
|
/// be forwarded without blocking, thus eliminating the need for incremental
|
||||||
|
/// read/write or blocking other important services such as responding to
|
||||||
|
/// requests from the application's clients. This assumption should be held
|
||||||
|
/// as long as both the forwarder and receptor have sufficient resources
|
||||||
|
/// to handle the forwarding process since the communication is local.
|
||||||
|
/// But a forward attempt could still block if the receptor is busy (or even
|
||||||
|
/// hang up) and cannot keep up with the volume of incoming sessions.
|
||||||
|
///
|
||||||
|
/// So, in this implementation, the forwarder uses non blocking writes to
|
||||||
|
/// forward sessions. If a write attempt could block, it immediately gives
|
||||||
|
/// up the operation with an exception. The corresponding application is
|
||||||
|
/// expected to catch it, close the connection, and perform any necessary
|
||||||
|
/// recovery for that application (that would normally be re-establish the
|
||||||
|
/// connection with a new receptor, possibly after confirming the receiving
|
||||||
|
/// side is still alive). On the other hand, the receptor implementation
|
||||||
|
/// assumes it's possible that it only receive incomplete elements of a
|
||||||
|
/// session (such as in the case where the forwarder writes part of the
|
||||||
|
/// entire session and gives up the connection). The receptor implementation
|
||||||
|
/// throws an exception when it encounters an incomplete session. Like the
|
||||||
|
/// case of the forwarder application, the receptor application is expected
|
||||||
|
/// to catch it, close the connection, and perform any necessary recovery
|
||||||
|
/// steps.
|
||||||
|
///
|
||||||
|
/// Note that the receptor implementation uses blocking read. So it's
|
||||||
|
/// application's responsibility to ensure that there's at least some data
|
||||||
|
/// in the connection when the receptor object is requested to receive a
|
||||||
|
/// session (unless this operation can be blocking, e.g., by the use of
|
||||||
|
/// a separate thread). Also, if the forwarder implementation or application
|
||||||
|
/// is malicious or extremely buggy and intentionally sends partial session
|
||||||
|
/// and keeps the connection, the receptor could block in receiving a session.
|
||||||
|
/// In general, we assume the forwarder doesn't do intentional blocking
|
||||||
|
/// as it's a local node and is generally a module of the same (BIND 10)
|
||||||
|
/// system. The minimum requirement for the forwarder implementation (and
|
||||||
|
/// application) is to make sure the connection is closed once it detects
|
||||||
|
/// an error on it. Even a naive implementation that simply dies due to
|
||||||
|
/// the exception will meet this requirement.
|
||||||
|
|
||||||
|
/// An exception indicating general errors that takes place in the
|
||||||
|
/// socket session related class objects.
|
||||||
|
///
|
||||||
|
/// In general the errors are unusual but possible failures such as unexpected
|
||||||
|
/// connection reset, and suggest the application to close the connection and
|
||||||
|
/// (if necessary) reestablish it.
|
||||||
class SocketSessionError: public Exception {
|
class SocketSessionError: public Exception {
|
||||||
public:
|
public:
|
||||||
SocketSessionError(const char *file, size_t line, const char *what):
|
SocketSessionError(const char *file, size_t line, const char *what):
|
||||||
isc::Exception(file, line, what) {}
|
isc::Exception(file, line, what) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The forwarder of socket sessions
|
||||||
|
///
|
||||||
|
/// An object of this class maintains a UNIX domain socket (normally expected
|
||||||
|
/// to be connected to a \c SocketSessionReceptor object) and forwards
|
||||||
|
/// socket sessions to the receptor.
|
||||||
|
///
|
||||||
|
/// See the description of \ref SocketSessionUtility for other details of how
|
||||||
|
/// the session forwarding works.
|
||||||
class SocketSessionForwarder : boost::noncopyable {
|
class SocketSessionForwarder : boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
// Note about SIGPIPE. Assuming this class is not often instantiated
|
/// The constructor.
|
||||||
// (so the overhead of signal setting should be marginal) and could also be
|
///
|
||||||
// instantiated by multiple threads, it always set the filter.
|
/// It's constructed with path information of the intended receptor,
|
||||||
|
/// but does not immediately establish a connection to the receptor;
|
||||||
|
/// \c connectToReceptor() must be called to establish it. These are
|
||||||
|
/// separated so that an object of class can be initialized (possibly
|
||||||
|
/// as an attribute of a higher level application class object) without
|
||||||
|
/// knowing the receptor is ready for accepting new forwarders. The
|
||||||
|
/// separate connect interface allows the object to be reused when it
|
||||||
|
/// detects connection failure and tries to re-establish it after closing
|
||||||
|
/// the failed one.
|
||||||
|
///
|
||||||
|
/// On construction, it also installs a signal filter for SIGPIPE to
|
||||||
|
/// ignore it. Since this class uses a stream-type connected UNIX domain
|
||||||
|
/// socket, if the receptor (abruptly) closes the connection a subsequent
|
||||||
|
/// write operation on the socket would trigger a SIGPIPE signal, which
|
||||||
|
/// kills the caller process by default. This behavior would be
|
||||||
|
/// undesirable in many cases, so this implementation always disables
|
||||||
|
/// the signal.
|
||||||
|
///
|
||||||
|
/// This approach has some drawbacks, however; first, since signal handling
|
||||||
|
/// is process (or thread) wide, ignoring it may not what the application
|
||||||
|
/// wants. On the other hand, if the application changes how the signal is
|
||||||
|
/// handled after instantiating this class, the new behavior affects the
|
||||||
|
/// class operation. Secondly, even if ignoring the signal is the desired
|
||||||
|
/// operation, it's a waste to set the filter every time this class object
|
||||||
|
/// is constructed. It's sufficient to do it once. We still adopt this
|
||||||
|
/// behavior based on the observation that in most cases applications would
|
||||||
|
/// like to ignore SIGPIPE (or simply doesn't care about it) and that this
|
||||||
|
/// class is not instantiated so often (so the wasteful setting overhead
|
||||||
|
/// should be marginal). On the other hand, doing it every time is
|
||||||
|
/// beneficial if the application is threaded and different threads
|
||||||
|
/// create different forwarder objects (and if signals work per thread).
|
||||||
|
///
|
||||||
|
/// \exception SocketSessionError \c unix_file is invalid as a path name
|
||||||
|
/// of a UNIX domain socket.
|
||||||
|
/// \exception Unexpected Error in setting a filter for SIGPIPE (see above)
|
||||||
|
/// \exception std::bad_alloc resource allocation failure
|
||||||
|
///
|
||||||
|
/// \param unix_file Path name of the receptor.
|
||||||
explicit SocketSessionForwarder(const std::string& unix_file);
|
explicit SocketSessionForwarder(const std::string& unix_file);
|
||||||
|
|
||||||
|
/// The destructor.
|
||||||
|
///
|
||||||
|
/// If a connection has been established, it's automatically closed in
|
||||||
|
/// the destructor.
|
||||||
~SocketSessionForwarder();
|
~SocketSessionForwarder();
|
||||||
|
|
||||||
|
/// Establish a connection to the receptor.
|
||||||
|
///
|
||||||
|
/// This method establishes a connection to the receptor at the path
|
||||||
|
/// given on construction. It makes the underlying UNIX domain socket
|
||||||
|
/// non blocking, so this method (or subsequent \c push() calls) does not
|
||||||
|
/// block.
|
||||||
|
///
|
||||||
|
/// \exception BadValue The method is called while an already
|
||||||
|
/// established connection is still active.
|
||||||
|
/// \exception SocketSessionError A system error in socket operation.
|
||||||
void connectToReceptor();
|
void connectToReceptor();
|
||||||
|
|
||||||
|
/// Close the connection to the receptor.
|
||||||
|
///
|
||||||
|
/// The connection must have been established by \c connectToReceptor().
|
||||||
|
/// As long as it's met this method is exception free.
|
||||||
|
///
|
||||||
|
/// \exception BadValue The connection hasn't been established.
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void push(int sock, int family, int sock_type, int protocol,
|
/// Forward a socket session to the receptor.
|
||||||
|
///
|
||||||
|
/// This method takes a set of parameters that represent a single socket
|
||||||
|
/// session, renders them in the "wire" format according to the internal
|
||||||
|
/// protocol (see \ref SocketSessionUtility) and forwards them to
|
||||||
|
/// the receptor through the UNIX domain connection.
|
||||||
|
///
|
||||||
|
/// The connection must have been established by \c connectToReceptor().
|
||||||
|
///
|
||||||
|
/// For simplicity and for the convenience of detecting application
|
||||||
|
/// errors, this method imposes some restrictions on the parameters:
|
||||||
|
/// - Socket family must be either \c AF_INET or \c AF_INET6
|
||||||
|
/// - The address family (\c sa_family) member of the local and remote
|
||||||
|
/// end points must be equal to the \c family parameter
|
||||||
|
/// - Socket session data must not be empty (\c data_len must not be 0
|
||||||
|
/// and \c data must not be NULL)
|
||||||
|
/// - Data length must not exceed 65535
|
||||||
|
/// These are not architectural limitation, and might be loosened in
|
||||||
|
/// future versions as we see the need for flexibility.
|
||||||
|
///
|
||||||
|
/// Since the underlying UNIX domain socket is non blocking
|
||||||
|
/// (see the description for the constructor), a call to this method
|
||||||
|
/// should either return immediately or result in exception (in case of
|
||||||
|
/// "would block").
|
||||||
|
///
|
||||||
|
/// \exception BadValue The method is called before establishing a
|
||||||
|
/// connection or given parameters are invalid.
|
||||||
|
/// \exception SocketSessionError A system error in socket operation,
|
||||||
|
/// including the case where the write operation would block.
|
||||||
|
///
|
||||||
|
/// \param sock The socket file descriptor
|
||||||
|
/// \param family The address family (such as AF_INET6) of the socket
|
||||||
|
/// \param type The socket type (such as SOCK_DGRAM) of the socket
|
||||||
|
/// \param protocol The transport protocol (such as IPPROTO_UDP) of the
|
||||||
|
/// socket
|
||||||
|
/// \param local_end The local end point of the session in the form of
|
||||||
|
/// \c sockaddr.
|
||||||
|
/// \param remote_end The remote end point of the session in the form of
|
||||||
|
/// \c sockaddr.
|
||||||
|
/// \param data A pointer to the beginning of the memory region for the
|
||||||
|
/// session data
|
||||||
|
/// \param data_len The size of the session data in bytes.
|
||||||
|
void push(int sock, int family, int type, int protocol,
|
||||||
const struct sockaddr& local_end,
|
const struct sockaddr& local_end,
|
||||||
const struct sockaddr& remote_end,
|
const struct sockaddr& remote_end,
|
||||||
const void* data, size_t data_len);
|
const void* data, size_t data_len);
|
||||||
@@ -54,18 +286,78 @@ private:
|
|||||||
ForwarderImpl* impl_;
|
ForwarderImpl* impl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Socket session object.
|
||||||
|
///
|
||||||
|
/// The \c SocketSession class provides a convenient encapsulation
|
||||||
|
/// for the notion of a socket session. It's instantiated with straightforward
|
||||||
|
/// parameters corresponding to a socket session, and provides read only
|
||||||
|
/// accessors to the parameters to ensure data integrity.
|
||||||
|
///
|
||||||
|
/// In the initial design and implementation it's only used as a return type
|
||||||
|
/// of \c SocketSessionReceptor::pop(), but it could also be used by
|
||||||
|
/// the \c SocketSessionForwarder class or for other purposes.
|
||||||
|
///
|
||||||
|
/// It is assumed that the original owner of a \c SocketSession object
|
||||||
|
/// (e.g. a class or a function that constructs it) is responsible for validity
|
||||||
|
/// of the data passed to the object. See the description of
|
||||||
|
/// \c SocketSessionReceptor::pop() for the specific case of that usage.
|
||||||
class SocketSession {
|
class SocketSession {
|
||||||
public:
|
public:
|
||||||
|
/// The constructor.
|
||||||
|
///
|
||||||
|
/// This is a trivial constructor, taking a straightforward representation
|
||||||
|
/// of session parameters and storing them internally to ensure integrity.
|
||||||
|
///
|
||||||
|
/// As long as the given parameters are valid it never throws an exception.
|
||||||
|
///
|
||||||
|
/// \exception BadValue Given parameters don't meet the requirement
|
||||||
|
/// (see the parameter descriptions).
|
||||||
|
///
|
||||||
|
/// \param sock The socket file descriptor
|
||||||
|
/// \param family The address family (such as AF_INET6) of the socket
|
||||||
|
/// \param type The socket type (such as SOCK_DGRAM) of the socket
|
||||||
|
/// \param protocol The transport protocol (such as IPPROTO_UDP) of the
|
||||||
|
/// socket.
|
||||||
|
/// \param local_end The local end point of the session in the form of
|
||||||
|
/// \c sockaddr. Must not be NULL.
|
||||||
|
/// \param remote_end The remote end point of the session in the form of
|
||||||
|
/// \c sockaddr. Must not be NULL.
|
||||||
|
/// \param data A pointer to the beginning of the memory region for the
|
||||||
|
/// session data. Must not be NULL, and the subsequent \c data_len bytes
|
||||||
|
/// must be valid.
|
||||||
|
/// \param data_len The size of the session data in bytes. Must not be 0.
|
||||||
SocketSession(int sock, int family, int type, int protocol,
|
SocketSession(int sock, int family, int type, int protocol,
|
||||||
const sockaddr* local_end, const sockaddr* remote_end,
|
const sockaddr* local_end, const sockaddr* remote_end,
|
||||||
size_t data_len, const void* data);
|
const void* data, size_t data_len);
|
||||||
|
|
||||||
|
/// Return the socket file descriptor.
|
||||||
int getSocket() const { return (sock_); }
|
int getSocket() const { return (sock_); }
|
||||||
|
|
||||||
|
/// Return the address family (such as AF_INET6) of the socket.
|
||||||
int getFamily() const { return (family_); }
|
int getFamily() const { return (family_); }
|
||||||
|
|
||||||
|
/// Return the socket type (such as SOCK_DGRAM) of the socket.
|
||||||
int getType() const { return (type_); }
|
int getType() const { return (type_); }
|
||||||
|
|
||||||
|
/// Return the transport protocol (such as IPPROTO_UDP) of the socket.
|
||||||
int getProtocol() const { return (protocol_); }
|
int getProtocol() const { return (protocol_); }
|
||||||
|
|
||||||
|
/// Return the local end point of the session in the form of \c sockaddr.
|
||||||
const sockaddr& getLocalEndpoint() const { return (*local_end_); }
|
const sockaddr& getLocalEndpoint() const { return (*local_end_); }
|
||||||
|
|
||||||
|
/// Return the remote end point of the session in the form of \c sockaddr.
|
||||||
const sockaddr& getRemoteEndpoint() const { return (*remote_end_); }
|
const sockaddr& getRemoteEndpoint() const { return (*remote_end_); }
|
||||||
|
|
||||||
|
/// Return a pointer to the beginning of the memory region for the session
|
||||||
|
/// data.
|
||||||
|
///
|
||||||
|
/// In the current implementation it should never be NULL, and the region
|
||||||
|
/// of the size returned by \c getDataLength() is expected to be valid.
|
||||||
const void* getData() const { return (data_); }
|
const void* getData() const { return (data_); }
|
||||||
|
|
||||||
|
/// Return the size of the session data in bytes.
|
||||||
|
///
|
||||||
|
/// In the current implementation it should be always larger than 0.
|
||||||
size_t getDataLength() const { return (data_len_); }
|
size_t getDataLength() const { return (data_len_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -75,14 +367,79 @@ private:
|
|||||||
const int protocol_;
|
const int protocol_;
|
||||||
const sockaddr* local_end_;
|
const sockaddr* local_end_;
|
||||||
const sockaddr* remote_end_;
|
const sockaddr* remote_end_;
|
||||||
const size_t data_len_;
|
|
||||||
const void* const data_;
|
const void* const data_;
|
||||||
|
const size_t data_len_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The receiver of socket sessions
|
||||||
|
///
|
||||||
|
/// An object of this class holds a UNIX domain socket for an
|
||||||
|
/// <em>established connection</em>, receives socket sessions from
|
||||||
|
/// the remote forwarder, and provides the session to the application
|
||||||
|
/// in the form of a \c SocketSession object.
|
||||||
|
///
|
||||||
|
/// Note that this class is instantiated with an already connected socket;
|
||||||
|
/// it's not a listening socket that is accepting connection requests from
|
||||||
|
/// forwarders. It's application's responsibility to create the listening
|
||||||
|
/// socket, listen on it and accept connections. Once the connection is
|
||||||
|
/// established, the application would construct a \c SocketSessionReceptor
|
||||||
|
/// object with the socket for the newly established connection.
|
||||||
|
/// This behavior is based on the design decision that the application should
|
||||||
|
/// decide when it performs (possibly) blocking operations (see \ref
|
||||||
|
/// SocketSessionUtility for more details).
|
||||||
|
///
|
||||||
|
/// See the description of \ref SocketSessionUtility for other details of how
|
||||||
|
/// the session forwarding works.
|
||||||
class SocketSessionReceptor : boost::noncopyable {
|
class SocketSessionReceptor : boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
|
/// The constructor.
|
||||||
|
///
|
||||||
|
/// \exception SocketSessionError Any error on an operation that is
|
||||||
|
/// performed on the given socket as part of initialization.
|
||||||
|
/// \exception std::bad_alloc Resource allocation failure
|
||||||
|
///
|
||||||
|
/// \param fd A UNIX domain socket for an established connection with
|
||||||
|
/// a forwarder.
|
||||||
explicit SocketSessionReceptor(int fd);
|
explicit SocketSessionReceptor(int fd);
|
||||||
|
|
||||||
|
/// The destructor.
|
||||||
|
///
|
||||||
|
/// The destructor does \c not close the socket given on construction.
|
||||||
|
/// It's up to the application what to do with it (note that the
|
||||||
|
/// application would have to maintain the socket itself for detecting
|
||||||
|
/// the existence of a new socket session asynchronously).
|
||||||
~SocketSessionReceptor();
|
~SocketSessionReceptor();
|
||||||
|
|
||||||
|
/// Receive a socket session from the forwarder.
|
||||||
|
///
|
||||||
|
/// This method receives wire-format data (see \ref SocketSessionUtility)
|
||||||
|
/// for a socket session on the UNIX domain socket, performs some
|
||||||
|
/// validation on the data, and returns the session information in the
|
||||||
|
/// form of a \c SocketSession object.
|
||||||
|
///
|
||||||
|
/// The returned SocketSession object is valid only until the next time
|
||||||
|
/// this method is called or until the \c SocketSessionReceptor object is
|
||||||
|
/// destructed.
|
||||||
|
///
|
||||||
|
/// It ensures the following:
|
||||||
|
/// - The address family is either \c AF_INET or \c AF_INET6
|
||||||
|
/// - The address family (\c sa_family) member of the local and remote
|
||||||
|
/// end points must be equal to the \c family parameter
|
||||||
|
/// - The socket session data is not empty and does not exceed 65535
|
||||||
|
/// bytes.
|
||||||
|
/// If the validation fails or an unexpected system error happens
|
||||||
|
/// (including a connection close in the meddle of reception), it throws
|
||||||
|
/// an SocketSessionError exception. When this happens, it's very
|
||||||
|
/// unlikely that a subsequent call to this method succeeds, so in reality
|
||||||
|
/// the application is expected to destruct it and close the socket in
|
||||||
|
/// such a case.
|
||||||
|
///
|
||||||
|
/// \exception SocketSessionError Invalid data is received or a system
|
||||||
|
/// error on socket operation happens.
|
||||||
|
/// \exception std::bad_alloc Resource allocation failure
|
||||||
|
///
|
||||||
|
/// \return A \c SocketSession object corresponding to the extracted
|
||||||
|
/// socket session.
|
||||||
SocketSession pop();
|
SocketSession pop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -147,11 +147,11 @@ private:
|
|||||||
vector<struct addrinfo*> addrinfo_list_;
|
vector<struct addrinfo*> addrinfo_list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ForwarderTest : public ::testing::Test {
|
class ForwardTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
ForwarderTest() : listen_fd_(-1), forwarder_(TEST_UNIX_FILE),
|
ForwardTest() : listen_fd_(-1), forwarder_(TEST_UNIX_FILE),
|
||||||
large_text_(65535, 'a'),
|
large_text_(65535, 'a'),
|
||||||
test_un_len_(2 + strlen(TEST_UNIX_FILE))
|
test_un_len_(2 + strlen(TEST_UNIX_FILE))
|
||||||
{
|
{
|
||||||
unlink(TEST_UNIX_FILE);
|
unlink(TEST_UNIX_FILE);
|
||||||
test_un_.sun_family = AF_UNIX;
|
test_un_.sun_family = AF_UNIX;
|
||||||
@@ -161,7 +161,7 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~ForwarderTest() {
|
~ForwardTest() {
|
||||||
if (listen_fd_ != -1) {
|
if (listen_fd_ != -1) {
|
||||||
close(listen_fd_);
|
close(listen_fd_);
|
||||||
}
|
}
|
||||||
@@ -287,12 +287,24 @@ protected:
|
|||||||
}
|
}
|
||||||
obuffer.writeUint16(hdrlen);
|
obuffer.writeUint16(hdrlen);
|
||||||
if (hdrlen_len > 0) {
|
if (hdrlen_len > 0) {
|
||||||
send(dummy_forwarder_.fd, obuffer.getData(), hdrlen_len, 0);
|
if (send(dummy_forwarder_.fd, obuffer.getData(), hdrlen_len, 0) !=
|
||||||
|
hdrlen_len) {
|
||||||
|
isc_throw(isc::Unexpected,
|
||||||
|
"Failed to pass session header len");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
accept_sock_.reset(acceptForwarder());
|
accept_sock_.reset(acceptForwarder());
|
||||||
receptor_.reset(new SocketSessionReceptor(accept_sock_.fd));
|
receptor_.reset(new SocketSessionReceptor(accept_sock_.fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper method to push some (normally bogus) socket session via a
|
||||||
|
// Unix domain socket pretending to be a valid SocketSessionForwarder.
|
||||||
|
// It internally calls pushSessionHeader() for setup and pushing the
|
||||||
|
// header, and pass (often bogus) header data and session data based
|
||||||
|
// on the function parameters. The parameters are generally compatible
|
||||||
|
// to those for SocketSessionForwarder::push, but could be invalid for
|
||||||
|
// testing purposes. For session data, we use TEST_DATA and its size
|
||||||
|
// by default for simplicity, but the size can be tweaked for testing.
|
||||||
void pushSession(int family, int type, int protocol, socklen_t local_len,
|
void pushSession(int family, int type, int protocol, socklen_t local_len,
|
||||||
const sockaddr& local, socklen_t remote_len,
|
const sockaddr& local, socklen_t remote_len,
|
||||||
const sockaddr& remote,
|
const sockaddr& remote,
|
||||||
@@ -308,8 +320,14 @@ protected:
|
|||||||
obuffer.writeData(&remote, getSALength(remote));
|
obuffer.writeData(&remote, getSALength(remote));
|
||||||
obuffer.writeUint32(static_cast<uint32_t>(data_len));
|
obuffer.writeUint32(static_cast<uint32_t>(data_len));
|
||||||
pushSessionHeader(obuffer.getLength());
|
pushSessionHeader(obuffer.getLength());
|
||||||
send(dummy_forwarder_.fd, obuffer.getData(), obuffer.getLength(), 0);
|
if (send(dummy_forwarder_.fd, obuffer.getData(), obuffer.getLength(),
|
||||||
send(dummy_forwarder_.fd, TEST_DATA, sizeof(TEST_DATA), 0);
|
0) != obuffer.getLength()) {
|
||||||
|
isc_throw(isc::Unexpected, "Failed to pass session header");
|
||||||
|
}
|
||||||
|
if (send(dummy_forwarder_.fd, TEST_DATA, sizeof(TEST_DATA), 0) !=
|
||||||
|
sizeof(TEST_DATA)) {
|
||||||
|
isc_throw(isc::Unexpected, "Failed to pass session data");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See below
|
// See below
|
||||||
@@ -332,7 +350,7 @@ private:
|
|||||||
SockAddrCreator addr_creator_;
|
SockAddrCreator addr_creator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ForwarderTest, construct) {
|
TEST_F(ForwardTest, construct) {
|
||||||
// On construction the existence of the file doesn't matter.
|
// On construction the existence of the file doesn't matter.
|
||||||
SocketSessionForwarder("some_file");
|
SocketSessionForwarder("some_file");
|
||||||
|
|
||||||
@@ -344,13 +362,13 @@ TEST_F(ForwarderTest, construct) {
|
|||||||
SocketSessionForwarder(string(sizeof(s.sun_path) - 1, 'x'));
|
SocketSessionForwarder(string(sizeof(s.sun_path) - 1, 'x'));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, connect) {
|
TEST_F(ForwardTest, connect) {
|
||||||
// File doesn't exist (we assume the file "no_such_file" doesn't exist)
|
// File doesn't exist (we assume the file "no_such_file" doesn't exist)
|
||||||
SocketSessionForwarder forwarder("no_such_file");
|
SocketSessionForwarder forwarder("no_such_file");
|
||||||
EXPECT_THROW(forwarder.connectToReceptor(), SocketSessionError);
|
EXPECT_THROW(forwarder.connectToReceptor(), SocketSessionError);
|
||||||
// The socket should be closed internally, so close() should result in
|
// The socket should be closed internally, so close() should result in
|
||||||
// error.
|
// error.
|
||||||
EXPECT_THROW(forwarder.close(), SocketSessionError);
|
EXPECT_THROW(forwarder.close(), BadValue);
|
||||||
|
|
||||||
// Set up the receptor and connect. It should succeed.
|
// Set up the receptor and connect. It should succeed.
|
||||||
SocketSessionForwarder forwarder2(TEST_UNIX_FILE);
|
SocketSessionForwarder forwarder2(TEST_UNIX_FILE);
|
||||||
@@ -359,14 +377,14 @@ TEST_F(ForwarderTest, connect) {
|
|||||||
// And it can be closed successfully.
|
// And it can be closed successfully.
|
||||||
forwarder2.close();
|
forwarder2.close();
|
||||||
// Duplicate close should fail
|
// Duplicate close should fail
|
||||||
EXPECT_THROW(forwarder2.close(), SocketSessionError);
|
EXPECT_THROW(forwarder2.close(), BadValue);
|
||||||
// Once closed, reconnect is okay.
|
// Once closed, reconnect is okay.
|
||||||
forwarder2.connectToReceptor();
|
forwarder2.connectToReceptor();
|
||||||
forwarder2.close();
|
forwarder2.close();
|
||||||
|
|
||||||
// Duplicate connect should be rejected
|
// Duplicate connect should be rejected
|
||||||
forwarder2.connectToReceptor();
|
forwarder2.connectToReceptor();
|
||||||
EXPECT_THROW(forwarder2.connectToReceptor(), SocketSessionError);
|
EXPECT_THROW(forwarder2.connectToReceptor(), BadValue);
|
||||||
|
|
||||||
// Connect then destroy. Should be internally closed, but unfortunately
|
// Connect then destroy. Should be internally closed, but unfortunately
|
||||||
// it's not easy to test it directly. We only check no disruption happens.
|
// it's not easy to test it directly. We only check no disruption happens.
|
||||||
@@ -376,10 +394,9 @@ TEST_F(ForwarderTest, connect) {
|
|||||||
delete forwarderp;
|
delete forwarderp;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, close) {
|
TEST_F(ForwardTest, close) {
|
||||||
// can't close before connect
|
// can't close before connect
|
||||||
EXPECT_THROW(SocketSessionForwarder(TEST_UNIX_FILE).close(),
|
EXPECT_THROW(SocketSessionForwarder(TEST_UNIX_FILE).close(), BadValue);
|
||||||
SocketSessionError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -422,11 +439,11 @@ checkSockAddrs(const sockaddr& expected, const sockaddr& actual) {
|
|||||||
// client_sock |
|
// client_sock |
|
||||||
// (check)<---------send TEST_DATA
|
// (check)<---------send TEST_DATA
|
||||||
void
|
void
|
||||||
ForwarderTest::checkPushAndPop(int family, int type, int protocol,
|
ForwardTest::checkPushAndPop(int family, int type, int protocol,
|
||||||
const SockAddrInfo& local,
|
const SockAddrInfo& local,
|
||||||
const SockAddrInfo& remote,
|
const SockAddrInfo& remote,
|
||||||
const void* const data,
|
const void* const data,
|
||||||
size_t data_len, bool new_connection)
|
size_t data_len, bool new_connection)
|
||||||
{
|
{
|
||||||
// Create an original socket to be passed
|
// Create an original socket to be passed
|
||||||
const ScopedSocket sock(createSocket(family, type, protocol, local, true));
|
const ScopedSocket sock(createSocket(family, type, protocol, local, true));
|
||||||
@@ -516,7 +533,7 @@ ForwarderTest::checkPushAndPop(int family, int type, int protocol,
|
|||||||
EXPECT_EQ(string(TEST_DATA), string(recvbuf));
|
EXPECT_EQ(string(TEST_DATA), string(recvbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, pushAndPop) {
|
TEST_F(ForwardTest, pushAndPop) {
|
||||||
// Pass a UDP/IPv6 session.
|
// Pass a UDP/IPv6 session.
|
||||||
const SockAddrInfo sai_local6(getSockAddr("::1", TEST_PORT));
|
const SockAddrInfo sai_local6(getSockAddr("::1", TEST_PORT));
|
||||||
const SockAddrInfo sai_remote6(getSockAddr("2001:db8::1", "5300"));
|
const SockAddrInfo sai_remote6(getSockAddr("2001:db8::1", "5300"));
|
||||||
@@ -576,13 +593,13 @@ TEST_F(ForwarderTest, pushAndPop) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, badPush) {
|
TEST_F(ForwardTest, badPush) {
|
||||||
// push before connect
|
// push before connect
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("192.0.2.1", "53").first,
|
*getSockAddr("192.0.2.1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
TEST_DATA, sizeof(TEST_DATA)),
|
TEST_DATA, sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
|
|
||||||
// Now connect the forwarder for the rest of tests
|
// Now connect the forwarder for the rest of tests
|
||||||
startListen();
|
startListen();
|
||||||
@@ -595,43 +612,43 @@ TEST_F(ForwarderTest, badPush) {
|
|||||||
sockaddr_unspec,
|
sockaddr_unspec,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
TEST_DATA, sizeof(TEST_DATA)),
|
TEST_DATA, sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
sockaddr_unspec, TEST_DATA,
|
sockaddr_unspec, TEST_DATA,
|
||||||
sizeof(TEST_DATA)),
|
sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
|
|
||||||
// Inconsistent address family
|
// Inconsistent address family
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("2001:db8::1", "53").first,
|
*getSockAddr("2001:db8::1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
TEST_DATA, sizeof(TEST_DATA)),
|
TEST_DATA, sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("2001:db8::1", "53").first,
|
*getSockAddr("2001:db8::1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
TEST_DATA, sizeof(TEST_DATA)),
|
TEST_DATA, sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
|
|
||||||
// Empty data: we reject them at least for now
|
// Empty data: we reject them at least for now
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("192.0.2.1", "53").first,
|
*getSockAddr("192.0.2.1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
TEST_DATA, 0),
|
TEST_DATA, 0),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("192.0.2.1", "53").first,
|
*getSockAddr("192.0.2.1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
NULL, sizeof(TEST_DATA)),
|
NULL, sizeof(TEST_DATA)),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
|
|
||||||
// Too big data: we reject them at least for now
|
// Too big data: we reject them at least for now
|
||||||
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
*getSockAddr("192.0.2.1", "53").first,
|
*getSockAddr("192.0.2.1", "53").first,
|
||||||
*getSockAddr("192.0.2.2", "53").first,
|
*getSockAddr("192.0.2.2", "53").first,
|
||||||
string(65536, 'd').c_str(), 65536),
|
string(65536, 'd').c_str(), 65536),
|
||||||
SocketSessionError);
|
BadValue);
|
||||||
|
|
||||||
// Close the receptor before push. It will result in SIGPIPE (should be
|
// Close the receptor before push. It will result in SIGPIPE (should be
|
||||||
// ignored) and EPIPE, which will be converted to SocketSessionError.
|
// ignored) and EPIPE, which will be converted to SocketSessionError.
|
||||||
@@ -658,7 +675,7 @@ multiPush(SocketSessionForwarder& forwarder, const struct sockaddr& sa,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, pushTooFast) {
|
TEST_F(ForwardTest, pushTooFast) {
|
||||||
// Emulate the situation where the forwarder is pushing sessions too fast.
|
// Emulate the situation where the forwarder is pushing sessions too fast.
|
||||||
// It should eventually fail without blocking.
|
// It should eventually fail without blocking.
|
||||||
startListen();
|
startListen();
|
||||||
@@ -668,7 +685,7 @@ TEST_F(ForwarderTest, pushTooFast) {
|
|||||||
SocketSessionError);
|
SocketSessionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ForwarderTest, badPop) {
|
TEST_F(ForwardTest, badPop) {
|
||||||
startListen();
|
startListen();
|
||||||
|
|
||||||
// Close the forwarder socket before pop() without sending anything.
|
// Close the forwarder socket before pop() without sending anything.
|
||||||
@@ -761,26 +778,26 @@ TEST_F(ForwarderTest, badPop) {
|
|||||||
EXPECT_THROW(receptor_->pop(), SocketSessionError);
|
EXPECT_THROW(receptor_->pop(), SocketSessionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SocketSession, badValue) {
|
TEST(SocketSessionTest, badValue) {
|
||||||
// normal cases are confirmed in ForwarderTest. We only check some
|
// normal cases are confirmed in ForwardTest. We only check some
|
||||||
// abnormal cases here.
|
// abnormal cases here.
|
||||||
|
|
||||||
SockAddrCreator addr_creator;
|
SockAddrCreator addr_creator;
|
||||||
|
|
||||||
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
|
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
|
||||||
addr_creator.get("192.0.2.1", "53").first,
|
addr_creator.get("192.0.2.1", "53").first,
|
||||||
sizeof(TEST_DATA), TEST_DATA),
|
TEST_DATA, sizeof(TEST_DATA)),
|
||||||
BadValue);
|
BadValue);
|
||||||
EXPECT_THROW(SocketSession(42, AF_INET6, SOCK_STREAM, IPPROTO_TCP,
|
EXPECT_THROW(SocketSession(42, AF_INET6, SOCK_STREAM, IPPROTO_TCP,
|
||||||
addr_creator.get("2001:db8::1", "53").first,
|
addr_creator.get("2001:db8::1", "53").first,
|
||||||
NULL, sizeof(TEST_DATA), TEST_DATA), BadValue);
|
NULL, TEST_DATA , sizeof(TEST_DATA)), BadValue);
|
||||||
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
addr_creator.get("192.0.2.1", "53").first,
|
addr_creator.get("192.0.2.1", "53").first,
|
||||||
addr_creator.get("192.0.2.2", "5300").first,
|
addr_creator.get("192.0.2.2", "5300").first,
|
||||||
0, TEST_DATA), BadValue);
|
TEST_DATA, 0), BadValue);
|
||||||
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||||
addr_creator.get("192.0.2.1", "53").first,
|
addr_creator.get("192.0.2.1", "53").first,
|
||||||
addr_creator.get("192.0.2.2", "5300").first,
|
addr_creator.get("192.0.2.2", "5300").first,
|
||||||
sizeof(TEST_DATA), NULL), BadValue);
|
NULL, sizeof(TEST_DATA)), BadValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user