2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[1452] overall documentation update

This commit is contained in:
JINMEI Tatuya
2011-12-14 15:49:25 -08:00
parent 4d5f96b4d0
commit ed5fa95326
4 changed files with 437 additions and 63 deletions

View File

@@ -573,7 +573,7 @@ INPUT = ../src/lib/exceptions ../src/lib/cc \
../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
../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
# This tag can be used to specify the character encoding of the source files

View File

@@ -73,7 +73,8 @@ struct SocketSessionForwarder::ForwarderImpl {
SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
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) {
isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
}
@@ -108,7 +109,7 @@ SocketSessionForwarder::~SocketSessionForwarder() {
void
SocketSessionForwarder::connectToReceptor() {
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);
}
@@ -146,41 +147,40 @@ SocketSessionForwarder::connectToReceptor() {
void
SocketSessionForwarder::close() {
if (impl_->fd_ == -1) {
isc_throw(SocketSessionError, "Attempt of close before connect");
isc_throw(BadValue, "Attempt of close before connect");
}
::close(impl_->fd_);
impl_->fd_ = -1;
}
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& remote_end,
const void* data, size_t data_len)
{
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) ||
(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; " <<
static_cast<int>(local_end.sa_family) << ", " <<
static_cast<int>(remote_end.sa_family) << " given");
}
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>(local_end.sa_family) << ", "
<< static_cast<int>(remote_end.sa_family) << " given");
}
if (data_len == 0 || data == NULL) {
isc_throw(SocketSessionError,
"Data for a socket session must not be empty");
isc_throw(BadValue, "Data for a socket session must not be empty");
}
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);
}
@@ -194,7 +194,7 @@ SocketSessionForwarder::push(int sock, int family, int sock_type, int protocol,
impl_->buf_.skip(sizeof(uint16_t));
// Socket properties: family, type, protocol
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));
// Local endpoint
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,
const sockaddr* local_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),
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) {
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* const sa_remote_;
vector<char> header_buf_;
vector<char> data_buf_;
vector<uint8_t> header_buf_;
vector<uint8_t> data_buf_;
};
SocketSessionReceptor::SocketSessionReceptor(int fd) :
@@ -363,8 +363,8 @@ SocketSessionReceptor::pop() {
}
return (SocketSession(passed_fd, family, type, protocol,
impl_->sa_local_, impl_->sa_remote_, data_len,
&impl_->data_buf_[0]));
impl_->sa_local_, impl_->sa_remote_,
&impl_->data_buf_[0], data_len));
} catch (const InvalidBufferPosition& ex) {
// We catch the case where the given header is too short and convert
// the exception to SocketSessionError.

View File

@@ -25,26 +25,258 @@ namespace isc {
namespace util {
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 {
public:
SocketSessionError(const char *file, size_t line, const char *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 {
public:
// Note about SIGPIPE. Assuming this class is not often instantiated
// (so the overhead of signal setting should be marginal) and could also be
// instantiated by multiple threads, it always set the filter.
/// The constructor.
///
/// 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);
/// The destructor.
///
/// If a connection has been established, it's automatically closed in
/// the destructor.
~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();
/// 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 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& remote_end,
const void* data, size_t data_len);
@@ -54,18 +286,78 @@ private:
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 {
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,
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_); }
/// Return the address family (such as AF_INET6) of the socket.
int getFamily() const { return (family_); }
/// Return the socket type (such as SOCK_DGRAM) of the socket.
int getType() const { return (type_); }
/// Return the transport protocol (such as IPPROTO_UDP) of the socket.
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_); }
/// Return the remote end point of the session in the form of \c sockaddr.
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_); }
/// 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_); }
private:
@@ -75,14 +367,79 @@ private:
const int protocol_;
const sockaddr* local_end_;
const sockaddr* remote_end_;
const size_t data_len_;
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 {
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);
/// 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();
/// 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();
private:

View File

@@ -147,11 +147,11 @@ private:
vector<struct addrinfo*> addrinfo_list_;
};
class ForwarderTest : public ::testing::Test {
class ForwardTest : public ::testing::Test {
protected:
ForwarderTest() : listen_fd_(-1), forwarder_(TEST_UNIX_FILE),
large_text_(65535, 'a'),
test_un_len_(2 + strlen(TEST_UNIX_FILE))
ForwardTest() : listen_fd_(-1), forwarder_(TEST_UNIX_FILE),
large_text_(65535, 'a'),
test_un_len_(2 + strlen(TEST_UNIX_FILE))
{
unlink(TEST_UNIX_FILE);
test_un_.sun_family = AF_UNIX;
@@ -161,7 +161,7 @@ protected:
#endif
}
~ForwarderTest() {
~ForwardTest() {
if (listen_fd_ != -1) {
close(listen_fd_);
}
@@ -287,12 +287,24 @@ protected:
}
obuffer.writeUint16(hdrlen);
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());
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,
const sockaddr& local, socklen_t remote_len,
const sockaddr& remote,
@@ -308,8 +320,14 @@ protected:
obuffer.writeData(&remote, getSALength(remote));
obuffer.writeUint32(static_cast<uint32_t>(data_len));
pushSessionHeader(obuffer.getLength());
send(dummy_forwarder_.fd, obuffer.getData(), obuffer.getLength(), 0);
send(dummy_forwarder_.fd, TEST_DATA, sizeof(TEST_DATA), 0);
if (send(dummy_forwarder_.fd, obuffer.getData(), obuffer.getLength(),
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
@@ -332,7 +350,7 @@ private:
SockAddrCreator addr_creator_;
};
TEST_F(ForwarderTest, construct) {
TEST_F(ForwardTest, construct) {
// On construction the existence of the file doesn't matter.
SocketSessionForwarder("some_file");
@@ -344,13 +362,13 @@ TEST_F(ForwarderTest, construct) {
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)
SocketSessionForwarder forwarder("no_such_file");
EXPECT_THROW(forwarder.connectToReceptor(), SocketSessionError);
// The socket should be closed internally, so close() should result in
// error.
EXPECT_THROW(forwarder.close(), SocketSessionError);
EXPECT_THROW(forwarder.close(), BadValue);
// Set up the receptor and connect. It should succeed.
SocketSessionForwarder forwarder2(TEST_UNIX_FILE);
@@ -359,14 +377,14 @@ TEST_F(ForwarderTest, connect) {
// And it can be closed successfully.
forwarder2.close();
// Duplicate close should fail
EXPECT_THROW(forwarder2.close(), SocketSessionError);
EXPECT_THROW(forwarder2.close(), BadValue);
// Once closed, reconnect is okay.
forwarder2.connectToReceptor();
forwarder2.close();
// Duplicate connect should be rejected
forwarder2.connectToReceptor();
EXPECT_THROW(forwarder2.connectToReceptor(), SocketSessionError);
EXPECT_THROW(forwarder2.connectToReceptor(), BadValue);
// Connect then destroy. Should be internally closed, but unfortunately
// it's not easy to test it directly. We only check no disruption happens.
@@ -376,10 +394,9 @@ TEST_F(ForwarderTest, connect) {
delete forwarderp;
}
TEST_F(ForwarderTest, close) {
TEST_F(ForwardTest, close) {
// can't close before connect
EXPECT_THROW(SocketSessionForwarder(TEST_UNIX_FILE).close(),
SocketSessionError);
EXPECT_THROW(SocketSessionForwarder(TEST_UNIX_FILE).close(), BadValue);
}
void
@@ -422,11 +439,11 @@ checkSockAddrs(const sockaddr& expected, const sockaddr& actual) {
// client_sock |
// (check)<---------send TEST_DATA
void
ForwarderTest::checkPushAndPop(int family, int type, int protocol,
const SockAddrInfo& local,
const SockAddrInfo& remote,
const void* const data,
size_t data_len, bool new_connection)
ForwardTest::checkPushAndPop(int family, int type, int protocol,
const SockAddrInfo& local,
const SockAddrInfo& remote,
const void* const data,
size_t data_len, bool new_connection)
{
// Create an original socket to be passed
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));
}
TEST_F(ForwarderTest, pushAndPop) {
TEST_F(ForwardTest, pushAndPop) {
// Pass a UDP/IPv6 session.
const SockAddrInfo sai_local6(getSockAddr("::1", TEST_PORT));
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
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("192.0.2.1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
TEST_DATA, sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
// Now connect the forwarder for the rest of tests
startListen();
@@ -595,43 +612,43 @@ TEST_F(ForwarderTest, badPush) {
sockaddr_unspec,
*getSockAddr("192.0.2.2", "53").first,
TEST_DATA, sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("192.0.2.2", "53").first,
sockaddr_unspec, TEST_DATA,
sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
// Inconsistent address family
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("2001:db8::1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
TEST_DATA, sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
EXPECT_THROW(forwarder_.push(1, AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("2001:db8::1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
TEST_DATA, sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
// Empty data: we reject them at least for now
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("192.0.2.1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
TEST_DATA, 0),
SocketSessionError);
BadValue);
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("192.0.2.1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
NULL, sizeof(TEST_DATA)),
SocketSessionError);
BadValue);
// Too big data: we reject them at least for now
EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
*getSockAddr("192.0.2.1", "53").first,
*getSockAddr("192.0.2.2", "53").first,
string(65536, 'd').c_str(), 65536),
SocketSessionError);
BadValue);
// Close the receptor before push. It will result in SIGPIPE (should be
// 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.
// It should eventually fail without blocking.
startListen();
@@ -668,7 +685,7 @@ TEST_F(ForwarderTest, pushTooFast) {
SocketSessionError);
}
TEST_F(ForwarderTest, badPop) {
TEST_F(ForwardTest, badPop) {
startListen();
// Close the forwarder socket before pop() without sending anything.
@@ -761,26 +778,26 @@ TEST_F(ForwarderTest, badPop) {
EXPECT_THROW(receptor_->pop(), SocketSessionError);
}
TEST(SocketSession, badValue) {
// normal cases are confirmed in ForwarderTest. We only check some
TEST(SocketSessionTest, badValue) {
// normal cases are confirmed in ForwardTest. We only check some
// abnormal cases here.
SockAddrCreator addr_creator;
EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
addr_creator.get("192.0.2.1", "53").first,
sizeof(TEST_DATA), TEST_DATA),
TEST_DATA, sizeof(TEST_DATA)),
BadValue);
EXPECT_THROW(SocketSession(42, AF_INET6, SOCK_STREAM, IPPROTO_TCP,
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,
addr_creator.get("192.0.2.1", "53").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,
addr_creator.get("192.0.2.1", "53").first,
addr_creator.get("192.0.2.2", "5300").first,
sizeof(TEST_DATA), NULL), BadValue);
NULL, sizeof(TEST_DATA)), BadValue);
}
}