123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef __SOCKETSESSION_H_
- #define __SOCKETSESSION_H_ 1
- #include <boost/noncopyable.hpp>
- #include <exceptions/exceptions.h>
- #include <string>
- #include <sys/socket.h>
- 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 SocketSessionReceiver is the receiver (this interface assumes
- /// one direction of forwarding); \c SocketSession represents a single
- /// socket session.
- ///
- /// \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
- /// "(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 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.
- ///
- /// 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 receiver have sufficient resources
- /// to handle the forwarding process since the communication is local.
- /// 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
- /// 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 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 receiver implementation
- /// throws an exception when it encounters an incomplete session. Like the
- /// 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 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 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 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
- /// 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 "base" class of \c SocketSessionForwarder
- ///
- /// This class defines abstract interfaces of the \c SocketSessionForwarder
- /// class. Although \c SocketSessionForwarder is not intended to be used in
- /// a polymorphic way, it's not easy to use in tests because it will require
- /// various low level network operations. So it would be useful if we
- /// provide a framework for defining a fake or mock version of it.
- /// An application that needs to use \c SocketSessionForwarder would actually
- /// refer to this base class, and tests for the application would define
- /// and use a fake version of the forwarder class.
- ///
- /// Normal applications are not expected to define and use their own derived
- /// version of this base class, while it's not prohibited at the API level.
- ///
- /// See description of \c SocketSessionForwarder for the expected interface.
- class BaseSocketSessionForwarder {
- protected:
- BaseSocketSessionForwarder() {}
- public:
- virtual ~BaseSocketSessionForwarder() {}
- virtual void connectToReceiver() = 0;
- virtual void close() = 0;
- virtual 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) = 0;
- };
- /// The forwarder of socket sessions
- ///
- /// An object of this class maintains a UNIX domain socket (normally expected
- /// 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.
- class SocketSessionForwarder : boost::noncopyable,
- public BaseSocketSessionForwarder
- {
- public:
- /// The constructor.
- ///
- /// 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 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 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
- /// 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 receiver.
- explicit SocketSessionForwarder(const std::string& unix_file);
- /// The destructor.
- ///
- /// If a connection has been established, it's automatically closed in
- /// the destructor.
- virtual ~SocketSessionForwarder();
- /// Establish a connection to the receiver.
- ///
- /// 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.
- virtual void connectToReceiver();
- /// Close the connection to the receiver.
- ///
- /// 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.
- virtual void close();
- /// 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 receiver through the UNIX domain connection.
- ///
- /// 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:
- /// - 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.
- virtual 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);
- private:
- struct ForwarderImpl;
- 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 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 SocketSessionReceiver::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,
- 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:
- const int sock_;
- const int family_;
- const int type_;
- const int protocol_;
- const sockaddr* local_end_;
- const sockaddr* remote_end_;
- 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 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
- /// SocketSessionUtility for more details).
- ///
- /// See the description of \ref SocketSessionUtility for other details of how
- /// the session forwarding works.
- class SocketSessionReceiver : 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 SocketSessionReceiver(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).
- ~SocketSessionReceiver();
- /// 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 SocketSessionReceiver object is
- /// destructed.
- ///
- /// The caller is responsible for closing the received socket (whose
- /// file descriptor is accessible via \c SocketSession::getSocket()).
- /// If the caller copies the returned \c SocketSession object, it's also
- /// responsible for making sure the descriptor is closed at most once.
- /// On the other hand, the caller is not responsible for freeing the
- /// socket session data (accessible via \c SocketSession::getData());
- /// the \c SocketSessionReceiver object will clean it up automatically.
- ///
- /// 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:
- struct ReceiverImpl;
- ReceiverImpl* impl_;
- };
- }
- }
- }
- #endif // __SOCKETSESSION_H_
- // Local Variables:
- // mode: c++
- // End:
|