///
/// 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
+/// while \c SocketSessionReceiver 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
+/// \c SocketSessionForwarder and \c SocketSessionReceiver 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
/// 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
+/// forwarder and the receiver 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.
/// 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
+/// as long as both the forwarder and receiver 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
+/// But a forward attempt could still block if the receiver 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
/// 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
+/// connection with a new receiver, possibly after confirming the receiving
+/// side is still alive). On the other hand, the receiver 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
+/// entire session and gives up the connection). The receiver implementation
/// throws an exception when it encounters an incomplete session. Like the
-/// case of the forwarder application, the receptor application is expected
+/// case of the forwarder application, the receiver 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
+/// Note that the receiver 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
+/// in the connection when the receiver 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.
+/// and keeps the connection, the receiver 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
/// 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.
+/// to be connected to a \c SocketSessionReceiver object) and forwards
+/// socket sessions to the receiver.
///
/// See the description of \ref SocketSessionUtility for other details of how
/// the session forwarding works.
public:
/// 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
+ /// It's constructed with path information of the intended receiver,
+ /// but does not immediately establish a connection to the receiver;
+ /// \c connectToReceiver() 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
+ /// knowing the receiver 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
+ /// socket, if the receiver (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
/// \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.
+ /// \param unix_file Path name of the receiver.
explicit SocketSessionForwarder(const std::string& unix_file);
/// The destructor.
/// the destructor.
~SocketSessionForwarder();
- /// Establish a connection to the receptor.
+ /// Establish a connection to the receiver.
///
- /// This method establishes a connection to the receptor at the path
+ /// This method establishes a connection to the receiver 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 connectToReceiver();
- /// Close the connection to the receptor.
+ /// Close the connection to the receiver.
///
- /// The connection must have been established by \c connectToReceptor().
+ /// The connection must have been established by \c connectToReceiver().
/// As long as it's met this method is exception free.
///
/// \exception BadValue The connection hasn't been established.
void close();
- /// Forward a socket session to the receptor.
+ /// Forward a socket session to the receiver.
///
/// 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 receiver through the UNIX domain connection.
///
- /// The connection must have been established by \c connectToReceptor().
+ /// The connection must have been established by \c connectToReceiver().
///
/// For simplicity and for the convenience of detecting application
/// errors, this method imposes some restrictions on the parameters:
/// 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
+/// of \c SocketSessionReceiver::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.
+/// \c SocketSessionReceiver::pop() for the specific case of that usage.
class SocketSession {
public:
/// The constructor.
/// 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
+/// established, the application would construct a \c SocketSessionReceiver
/// 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
///
/// See the description of \ref SocketSessionUtility for other details of how
/// the session forwarding works.
-class SocketSessionReceptor : boost::noncopyable {
+class SocketSessionReceiver : boost::noncopyable {
public:
/// The constructor.
///
///
/// \param fd A UNIX domain socket for an established connection with
/// a forwarder.
- explicit SocketSessionReceptor(int fd);
+ explicit SocketSessionReceiver(int fd);
/// The destructor.
///
/// 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();
+ ~SocketSessionReceiver();
/// Receive a socket session from the forwarder.
///
/// 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
+ /// this method is called or until the \c SocketSessionReceiver object is
/// destructed.
///
/// It ensures the following:
SocketSession pop();
private:
- struct ReceptorImpl;
- ReceptorImpl* impl_;
+ struct ReceiverImpl;
+ ReceiverImpl* impl_;
};
}
// A helper method to push some (normally bogus) socket session header
// via a Unix domain socket that pretends to be a valid
// SocketSessionForwarder. It first opens the Unix domain socket,
- // and connect to the test receptor server (startListen() is expected to
+ // and connect to the test receiver server (startListen() is expected to
// be called beforehand), forwards a valid file descriptor ("stdin" is
// used for simplicity), the pushed a 2-byte header length field of the
- // session header. The internal receptor_ pointer will be set to a
- // newly created receptor object for the connection.
+ // session header. The internal receiver_ pointer will be set to a
+ // newly created receiver object for the connection.
//
// \param hdrlen: The header length to be pushed. It may or may not be
// valid.
}
}
accept_sock_.reset(acceptForwarder());
- receptor_.reset(new SocketSessionReceptor(accept_sock_.fd));
+ receiver_.reset(new SocketSessionReceiver(accept_sock_.fd));
}
// A helper method to push some (normally bogus) socket session via a
int listen_fd_;
SocketSessionForwarder forwarder_;
ScopedSocket dummy_forwarder_; // forwarder "like" socket to pass bad data
- scoped_ptr<SocketSessionReceptor> receptor_;
+ scoped_ptr<SocketSessionReceiver> receiver_;
ScopedSocket accept_sock_;
const string large_text_;
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);
+ EXPECT_THROW(forwarder.connectToReceiver(), SocketSessionError);
// The socket should be closed internally, so close() should result in
// error.
EXPECT_THROW(forwarder.close(), BadValue);
- // Set up the receptor and connect. It should succeed.
+ // Set up the receiver and connect. It should succeed.
SocketSessionForwarder forwarder2(TEST_UNIX_FILE);
startListen();
- forwarder2.connectToReceptor();
+ forwarder2.connectToReceiver();
// And it can be closed successfully.
forwarder2.close();
// Duplicate close should fail
EXPECT_THROW(forwarder2.close(), BadValue);
// Once closed, reconnect is okay.
- forwarder2.connectToReceptor();
+ forwarder2.connectToReceiver();
forwarder2.close();
// Duplicate connect should be rejected
- forwarder2.connectToReceptor();
- EXPECT_THROW(forwarder2.connectToReceptor(), BadValue);
+ forwarder2.connectToReceiver();
+ EXPECT_THROW(forwarder2.connectToReceiver(), BadValue);
// Connect then destroy. Should be internally closed, but unfortunately
// it's not easy to test it directly. We only check no disruption happens.
SocketSessionForwarder* forwarderp =
new SocketSessionForwarder(TEST_UNIX_FILE);
- forwarderp->connectToReceptor();
+ forwarderp->connectToReceiver();
delete forwarderp;
}
// session passing. It first creates a "local" socket (which is supposed
// to act as a "server") bound to the 'local' parameter. It then forwards
// the descriptor of the FD of the local socket along with given data.
-// Next, it creates an Receptor object to receive the forwarded FD itself,
+// Next, it creates an Receiver object to receive the forwarded FD itself,
// receives the FD, and sends test data from the received FD. The
// test finally checks if it can receive the test data from the local socket
// at the Forwarder side. In the case of TCP it's a bit complicated because
// scenario is the same. See the diagram below for more details.
//
// UDP:
-// Forwarder Receptor
+// Forwarder Receiver
// sock -- (pass) --> passed_sock
// (check) <-------- send TEST_DATA
//
// TCP:
-// Forwarder Receptor
+// Forwarder Receiver
// server_sock---(pass)--->passed_sock
// ^ |
// |(connect) |
// internal forwarder connect to it, and then internally accept it.
if (new_connection) {
startListen();
- forwarder_.connectToReceptor();
+ forwarder_.connectToReceiver();
accept_sock_.reset(acceptForwarder());
}
forwarder_.push(fwd_fd, family, type, protocol, *local.first,
*remote.first, data, data_len);
- // Pop the socket session we just pushed from a local receptor, and
- // check the content. Since we do blocking read on the receptor's socket,
+ // Pop the socket session we just pushed from a local receiver, and
+ // check the content. Since we do blocking read on the receiver's socket,
// we set up an alarm to prevent hangup in case there's a bug that really
// makes the blocking happen.
- SocketSessionReceptor receptor(accept_sock_.fd);
+ SocketSessionReceiver receiver(accept_sock_.fd);
alarm(1); // set up 1-sec timer, an arbitrary choice.
- const SocketSession sock_session = receptor.pop();
+ const SocketSession sock_session = receiver.pop();
alarm(0); // then cancel it.
const ScopedSocket passed_sock(sock_session.getSocket());
EXPECT_LE(0, passed_sock.fd);
}
// Pass a UDP/IPv4 session. This reuses the same pair of forwarder and
- // receptor, which should be usable for multiple attempts of passing,
+ // receiver, which should be usable for multiple attempts of passing,
// regardless of family of the passed session
const SockAddrInfo sai_local4(getSockAddr("127.0.0.1", TEST_PORT));
const SockAddrInfo sai_remote4(getSockAddr("192.0.2.2", "5300"));
// Now connect the forwarder for the rest of tests
startListen();
- forwarder_.connectToReceptor();
+ forwarder_.connectToReceiver();
// Invalid address family
struct sockaddr sockaddr_unspec;
string(65536, 'd').c_str(), 65536),
BadValue);
- // Close the receptor before push. It will result in SIGPIPE (should be
+ // Close the receiver before push. It will result in SIGPIPE (should be
// ignored) and EPIPE, which will be converted to SocketSessionError.
- const int receptor_fd = acceptForwarder();
- close(receptor_fd);
+ const int receiver_fd = acceptForwarder();
+ close(receiver_fd);
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,
// Emulate the situation where the forwarder is pushing sessions too fast.
// It should eventually fail without blocking.
startListen();
- forwarder_.connectToReceptor();
+ forwarder_.connectToReceiver();
EXPECT_THROW(multiPush(forwarder_, *getSockAddr("192.0.2.1", "53").first,
large_text_.c_str(), large_text_.length()),
SocketSessionError);
// Close the forwarder socket before pop() without sending anything.
pushSessionHeader(0, 0, false);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Pretending to be a forwarder but don't actually pass FD.
pushSessionHeader(0, 1, false);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Pass a valid FD (stdin), but provide short data for the hdrlen
pushSessionHeader(0, 1);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Pass a valid FD, but provides too large hdrlen
pushSessionHeader(0xffff);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Don't provide full header
pushSessionHeader(sizeof(uint32_t));
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Pushed header is too short
const uint8_t dummy_data = 0;
pushSessionHeader(1);
send(dummy_forwarder_.fd, &dummy_data, 1, 0);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// socket addresses commonly used below (the values don't matter).
const SockAddrInfo sai_local(getSockAddr("192.0.2.1", "53535"));
pushSession(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
*sai_local.first, sai_remote.second, *sai_remote.first);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Pass inconsistent address family for local
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai6.second,
*sai6.first, sai_remote.second, *sai_remote.first);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Same for remote
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
sizeof(struct sockaddr_storage) + 1, *sai_local.first,
sai_remote.second, *sai_remote.first);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Same for remote
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
*sai_local.first, sizeof(struct sockaddr_storage) + 1,
*sai_remote.first);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Data length is too large
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
*sai_local.first, sai_remote.second,
*sai_remote.first, 65536);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Empty data
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
*sai_local.first, sai_remote.second,
*sai_remote.first, 0);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
// Not full data are passed
pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
*sai_local.first, sai_remote.second,
*sai_remote.first, sizeof(TEST_DATA) + 1);
dummy_forwarder_.reset(-1);
- EXPECT_THROW(receptor_->pop(), SocketSessionError);
+ EXPECT_THROW(receiver_->pop(), SocketSessionError);
}
TEST(SocketSessionTest, badValue) {