Parcourir la source

[1452] overall documentation update

JINMEI Tatuya il y a 13 ans
Parent
commit
ed5fa95326

+ 1 - 1
doc/Doxyfile

@@ -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

+ 17 - 17
src/lib/util/io/socketsession.cc

@@ -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.

+ 363 - 6
src/lib/util/io/socketsession.h

@@ -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:

+ 56 - 39
src/lib/util/tests/socketsession_unittest.cc

@@ -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);
 }
 }