diff options
author | Tomek Mrugalski <tomek@isc.org> | 2024-04-18 13:39:36 +0200 |
---|---|---|
committer | Tomek Mrugalski <tomek@isc.org> | 2024-04-23 17:42:20 +0200 |
commit | 4577c80bd1ff243032a6542353468d30c08a9f0b (patch) | |
tree | 18c5d635d1c075c6e88e2f2b85cdd64bd5abe251 | |
parent | [#3347] Updated ChangeLog (diff) | |
download | kea-4577c80bd1ff243032a6542353468d30c08a9f0b.tar.xz kea-4577c80bd1ff243032a6542353468d30c08a9f0b.zip |
[#3321] legacy code removed
-rw-r--r-- | doc/devel/mainpage.dox | 1 | ||||
-rw-r--r-- | src/lib/util/Makefile.am | 5 | ||||
-rw-r--r-- | src/lib/util/io/Makefile.am | 4 | ||||
-rw-r--r-- | src/lib/util/io/fd_share.cc | 166 | ||||
-rw-r--r-- | src/lib/util/io/fd_share.h | 57 | ||||
-rw-r--r-- | src/lib/util/io/socketsession.cc | 439 | ||||
-rw-r--r-- | src/lib/util/io/socketsession.h | 491 | ||||
-rw-r--r-- | src/lib/util/tests/Makefile.am | 1 | ||||
-rw-r--r-- | src/lib/util/tests/fd_share_tests.cc | 72 | ||||
-rw-r--r-- | src/lib/util/unittests/Makefile.am | 3 | ||||
-rw-r--r-- | src/lib/util/unittests/mock_socketsession.h | 148 |
11 files changed, 3 insertions, 1384 deletions
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox index 5151e15980..e15b8a590a 100644 --- a/doc/devel/mainpage.dox +++ b/doc/devel/mainpage.dox @@ -151,7 +151,6 @@ * - @subpage logDeveloperUse * - @subpage logNotes * - @subpage LoggingApi - * - @subpage SocketSessionUtility * - @subpage crossCompile * - @subpage debug * - @subpage docs diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index de3104ae86..526cac331e 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -92,8 +92,5 @@ libkea_util_encode_include_HEADERS = \ libkea_util_io_includedir = $(pkgincludedir)/util/io libkea_util_io_include_HEADERS = \ - io/fd.h \ - io/fd_share.h \ io/pktinfo_utilities.h \ - io/sockaddr_util.h \ - io/socketsession.h + io/sockaddr_util.h diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am index b5659b9218..9756d9d60e 100644 --- a/src/lib/util/io/Makefile.am +++ b/src/lib/util/io/Makefile.am @@ -5,8 +5,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) lib_LTLIBRARIES = libkea-util-io.la -libkea_util_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc -libkea_util_io_la_SOURCES += socketsession.h socketsession.cc sockaddr_util.h +libkea_util_io_la_SOURCES = fd.h fd.cc +libkea_util_io_la_SOURCES += sockaddr_util.h libkea_util_io_la_SOURCES += pktinfo_utilities.h libkea_util_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la libkea_util_io_la_LDFLAGS = -no-undefined -version-info 0:1:0 diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc deleted file mode 100644 index 7ab265953a..0000000000 --- a/src/lib/util/io/fd_share.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) 2010-2019 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <cstring> -#include <cstdlib> - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <errno.h> -#include <stdlib.h> // for malloc and free -#include <unistd.h> -#include <util/io/fd_share.h> - -namespace isc { -namespace util { -namespace io { - -namespace { -// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE. -// In order to ensure as much portability as possible, we provide wrapper -// functions of these macros. -// Note that cmsg_space() could run slow on OSes that do not have -// CMSG_SPACE. -inline socklen_t -cmsg_len(const socklen_t len) { -#ifdef CMSG_LEN - return (CMSG_LEN(len)); -#else - // Cast NULL so that any pointer arithmetic performed by CMSG_DATA - // is correct. - const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL)); - return (hdrlen + len); -#endif -} - -inline socklen_t -cmsg_space(const socklen_t len) { -#ifdef CMSG_SPACE - return (CMSG_SPACE(len)); -#else - struct msghdr msg; - struct cmsghdr* cmsgp; - // XXX: The buffer length is an ad hoc value, but should be enough - // in a practical sense. - char dummybuf[sizeof(struct cmsghdr) + 1024]; - - memset(&msg, 0, sizeof(msg)); - msg.msg_control = dummybuf; - msg.msg_controllen = sizeof(dummybuf); - - cmsgp = (struct cmsghdr*)dummybuf; - cmsgp->cmsg_len = cmsg_len(len); - - cmsgp = CMSG_NXTHDR(&msg, cmsgp); - if (cmsgp != NULL) { - return ((char*)cmsgp - (char*)msg.msg_control); - } else { - return (0); - } -#endif // CMSG_SPACE -} -} - -int -recv_fd(const int sock) { - struct msghdr msghdr; - struct iovec iov_dummy; - unsigned char dummy_data; - - iov_dummy.iov_base = &dummy_data; - iov_dummy.iov_len = sizeof(dummy_data); - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - msghdr.msg_iov = &iov_dummy; - msghdr.msg_iovlen = 1; - msghdr.msg_flags = 0; - msghdr.msg_controllen = cmsg_space(sizeof(int)); - msghdr.msg_control = malloc(msghdr.msg_controllen); - if (msghdr.msg_control == NULL) { - return (FD_SYSTEM_ERROR); - } - - const int cc = recvmsg(sock, &msghdr, 0); - if (cc <= 0) { - free(msghdr.msg_control); - if (cc == 0) { - errno = ECONNRESET; - } - return (FD_SYSTEM_ERROR); - } - const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr); - int fd = FD_OTHER_ERROR; - if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) && - cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { -// Some systems (e.g. recent NetBSD) converted all CMSG access macros -// to static_cast when used in C++ code. As cmsg is declared const -// this makes the CMSG_DATA macro to not compile. But fortunately -// these systems provide a const alternative named CCMSG_DATA. -#ifdef CCMSG_DATA - std::memcpy(&fd, CCMSG_DATA(cmsg), sizeof(int)); -#else - std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); -#endif - } - free(msghdr.msg_control); - int new_fd = -1; - int close_error = -1; - if (fd >= 0) { - // It is strange, but the call can return the same file descriptor as - // one returned previously, even if that one is not closed yet. So, - // we just re-number every one we get, so they are unique. - new_fd = dup(fd); - close_error = close(fd); - } - if (close_error == -1 || new_fd == -1) { - // We need to return an error, because something failed. But in case - // it was the previous close, we at least try to close the duped FD. - if (new_fd != -1) { - close(new_fd); // If this fails, nothing but returning error can't - // be done and we are doing that anyway. - } - return (FD_SYSTEM_ERROR); - } - return (new_fd); -} - -int -send_fd(const int sock, const int fd) { - struct msghdr msghdr; - struct iovec iov_dummy; - unsigned char dummy_data = 0; - - iov_dummy.iov_base = &dummy_data; - iov_dummy.iov_len = sizeof(dummy_data); - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - msghdr.msg_iov = &iov_dummy; - msghdr.msg_iovlen = 1; - msghdr.msg_flags = 0; - msghdr.msg_controllen = cmsg_space(sizeof(int)); - msghdr.msg_control = malloc(msghdr.msg_controllen); - if (msghdr.msg_control == NULL) { - return (FD_OTHER_ERROR); - } - std::memset(msghdr.msg_control, 0, msghdr.msg_controllen); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg->cmsg_len = cmsg_len(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - std::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - const int ret = sendmsg(sock, &msghdr, 0); - free(msghdr.msg_control); - return (ret >= 0 ? 0 : FD_SYSTEM_ERROR); -} - -} // namespace io -} // namespace util -} // namespace isc diff --git a/src/lib/util/io/fd_share.h b/src/lib/util/io/fd_share.h deleted file mode 100644 index 600606f820..0000000000 --- a/src/lib/util/io/fd_share.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef FD_SHARE_H -#define FD_SHARE_H - -/** - * \file fd_share.h - * \short Support to transfer file descriptors between processes. - * \todo This interface is very C-ish. Should we have some kind of exceptions? - */ - -namespace isc { -namespace util { -namespace io { - -const int FD_SYSTEM_ERROR = -2; -const int FD_OTHER_ERROR = -1; - -/** - * \short Receives a file descriptor. - * This receives a file descriptor sent over an unix domain socket. This - * is the counterpart of send_fd(). - * - * \return FD_SYSTEM_ERROR when there's an error at the operating system - * level (such as a system call failure). The global 'errno' variable - * indicates the specific error. FD_OTHER_ERROR when there's a different - * error. - * - * \param sock The unix domain socket to read from. Tested and it does - * not work with a pipe. - */ -int recv_fd(const int sock); - -/** - * \short Sends a file descriptor. - * This sends a file descriptor over an unix domain socket. This is the - * counterpart of recv_fd(). - * - * \return FD_SYSTEM_ERROR when there's an error at the operating system - * level (such as a system call failure). The global 'errno' variable - * indicates the specific error. - * \param sock The unix domain socket to send to. Tested and it does not - * work with a pipe. - * \param fd The file descriptor to send. It should work with any valid - * file descriptor. - */ -int send_fd(const int sock, const int fd); - -} // namespace io -} // namespace util -} // namespace isc - -#endif // FD_SHARE_H diff --git a/src/lib/util/io/socketsession.cc b/src/lib/util/io/socketsession.cc deleted file mode 100644 index db93820a4f..0000000000 --- a/src/lib/util/io/socketsession.cc +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <unistd.h> - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <sys/un.h> - -#include <netinet/in.h> - -#include <fcntl.h> -#include <stdint.h> - -#include <cerrno> -#include <csignal> -#include <cstddef> -#include <cstring> - -#include <string> -#include <vector> - -#include <boost/noncopyable.hpp> - -#include <exceptions/exceptions.h> -#include <exceptions/isc_assert.h> - -#include <util/buffer.h> - -#include <util/io/fd_share.h> -#include <util/io/socketsession.h> -#include <util/io/sockaddr_util.h> - -using namespace std; - -namespace isc { -namespace util { -namespace io { - -using namespace internal; - -// The expected max size of the session header: 2-byte header length, -// 6 32-bit fields, and 2 sockaddr structure. (see the SocketSessionUtility -// overview description in the header file). sizeof sockaddr_storage -// should be the possible max of any sockaddr structure -const size_t DEFAULT_HEADER_BUFLEN = sizeof(uint16_t) + sizeof(uint32_t) * 6 + - sizeof(struct sockaddr_storage) * 2; - -// The allowable maximum size of data passed with the socket FD. For now -// we use a fixed value of 65535, the largest possible size of valid DNS -// messages. We may enlarge it or make it configurable as we see the need -// for more flexibility. -const int MAX_DATASIZE = 65535; - -// The initial buffer size for receiving socket session data in the receiver. -// This value is the maximum message size of DNS messages carried over UDP -// (without EDNS). In our expected usage (at the moment) this should be -// sufficiently large (the expected data is AXFR/IXFR query or an UPDATE -// requests. The former should be generally quite small. While the latter -// could be large, it would often be small enough for a single UDP message). -// If it turns out that there are many exceptions, we may want to extend -// the class so that this value can be customized. Note that the buffer -// will be automatically extended for longer data and this is only about -// efficiency. -const size_t INITIAL_BUFSIZE = 512; - -// The (default) socket buffer size for the forwarder and receiver. This is -// chosen to be sufficiently large to store two full-size DNS messages. We -// may want to customize this value in future. -const int SOCKSESSION_BUFSIZE = (DEFAULT_HEADER_BUFLEN + MAX_DATASIZE) * 2; - -struct SocketSessionForwarder::ForwarderImpl { - ForwarderImpl() : sock_un_len_(0), fd_(-1), buf_(DEFAULT_HEADER_BUFLEN) { - memset(&sock_un_, 0, sizeof(sock_un_)); - } - - struct sockaddr_un sock_un_; - socklen_t sock_un_len_; - int fd_; - OutputBuffer buf_; -}; - -SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) : - impl_(NULL) -{ - // 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)); - } - - ForwarderImpl impl; - if (sizeof(impl.sock_un_.sun_path) - 1 < unix_file.length()) { - isc_throw(SocketSessionError, - "File name for a UNIX domain socket is too long: " << - unix_file); - } - impl.sock_un_.sun_family = AF_UNIX; - // the copy should be safe due to the above check, but we'd be rather - // paranoid about making it 100% sure even if the check has a bug (with - // triggering the assertion in the worse case) - memset(&impl.sock_un_.sun_path, 0, sizeof(impl.sock_un_.sun_path)); - strncpy(impl.sock_un_.sun_path, unix_file.c_str(), - sizeof(impl.sock_un_.sun_path) - 1); - isc_throw_assert(impl.sock_un_.sun_path[sizeof(impl.sock_un_.sun_path) - 1] == '\0'); - impl.sock_un_len_ = offsetof(struct sockaddr_un, sun_path) + - unix_file.length(); -#ifdef HAVE_SA_LEN - impl.sock_un_.sun_len = impl.sock_un_len_; -#endif - impl.fd_ = -1; - - impl_ = new ForwarderImpl; - *impl_ = impl; -} - -SocketSessionForwarder::~SocketSessionForwarder() { - if (impl_->fd_ != -1) { - close(); - } - delete impl_; -} - -void -SocketSessionForwarder::connectToReceiver() { - if (impl_->fd_ != -1) { - isc_throw(BadValue, "Duplicate connect to UNIX domain " - "endpoint " << impl_->sock_un_.sun_path); - } - - impl_->fd_ = socket(AF_UNIX, SOCK_STREAM, 0); - if (impl_->fd_ == -1) { - isc_throw(SocketSessionError, "Failed to create a UNIX domain socket: " - << strerror(errno)); - } - // Make the socket non blocking - int fcntl_flags = fcntl(impl_->fd_, F_GETFL, 0); - if (fcntl_flags != -1) { - fcntl_flags |= O_NONBLOCK; - fcntl_flags = fcntl(impl_->fd_, F_SETFL, fcntl_flags); - } - if (fcntl_flags == -1) { - close(); // note: this is the internal method, not ::close() - isc_throw(SocketSessionError, - "Failed to make UNIX domain socket non blocking: " << - strerror(errno)); - } - // Ensure the socket send buffer is large enough. If we can't get the - // current size, simply set the sufficient size. - int sndbuf_size; - socklen_t sndbuf_size_len = sizeof(sndbuf_size); - if (getsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, - &sndbuf_size_len) == -1 || - sndbuf_size < SOCKSESSION_BUFSIZE) { - if (setsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &SOCKSESSION_BUFSIZE, - sizeof(SOCKSESSION_BUFSIZE)) == -1) { - close(); - isc_throw(SocketSessionError, - "Failed to set send buffer size to " << - SOCKSESSION_BUFSIZE); - } - } - if (connect(impl_->fd_, convertSockAddr(&impl_->sock_un_), - impl_->sock_un_len_) == -1) { - close(); - isc_throw(SocketSessionError, "Failed to connect to UNIX domain " - "endpoint " << impl_->sock_un_.sun_path << ": " << - strerror(errno)); - } -} - -void -SocketSessionForwarder::close() { - if (impl_->fd_ == -1) { - isc_throw(BadValue, "Attempt of close before connect"); - } - ::close(impl_->fd_); - impl_->fd_ = -1; -} - -void -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(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(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(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(BadValue, "Data for a socket session must not be empty"); - } - if (data_len > MAX_DATASIZE) { - isc_throw(BadValue, "Invalid socket session data size: " << - data_len << ", must not exceed " << MAX_DATASIZE); - } - - if (send_fd(impl_->fd_, sock) != 0) { - isc_throw(SocketSessionError, "FD passing failed: " << - strerror(errno)); - } - - impl_->buf_.clear(); - // Leave the space for the header length - 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>(type)); - impl_->buf_.writeUint32(static_cast<uint32_t>(protocol)); - // Local endpoint - impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end))); - impl_->buf_.writeData(&local_end, getSALength(local_end)); - // Remote endpoint - impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(remote_end))); - impl_->buf_.writeData(&remote_end, getSALength(remote_end)); - // Data length. Must be fit uint32 due to the range check above. - const uint32_t data_len32 = static_cast<uint32_t>(data_len); - isc_throw_assert(data_len == data_len32); // shouldn't cause overflow. - impl_->buf_.writeUint32(data_len32); - // Write the resulting header length at the beginning of the buffer - impl_->buf_.writeUint16At(impl_->buf_.getLength() - sizeof(uint16_t), 0); - - const struct iovec iov[2] = { - { const_cast<void*>(impl_->buf_.getDataAsVoidPtr()), - impl_->buf_.getLength() }, - { const_cast<void*>(data), data_len } - }; - const int cc = writev(impl_->fd_, iov, 2); - if (cc != impl_->buf_.getLength() + data_len) { - if (cc < 0) { - isc_throw(SocketSessionError, - "Write failed in forwarding a socket session: " << - strerror(errno)); - } - isc_throw(SocketSessionError, - "Incomplete write in forwarding a socket session: " << cc << - "/" << (impl_->buf_.getLength() + data_len)); - } -} - -SocketSession::SocketSession(int sock, int family, int type, int protocol, - const sockaddr* local_end, - const sockaddr* remote_end, - const void* data, size_t data_len) : - sock_(sock), family_(family), type_(type), protocol_(protocol), - local_end_(local_end), remote_end_(remote_end), - data_(data), data_len_(data_len) -{ - if (local_end == NULL || remote_end == NULL) { - isc_throw(BadValue, "sockaddr must be non NULL for SocketSession"); - } - if (data_len == 0) { - isc_throw(BadValue, "data_len must be non 0 for SocketSession"); - } - if (data == NULL) { - isc_throw(BadValue, "data must be non NULL for SocketSession"); - } -} - -struct SocketSessionReceiver::ReceiverImpl { - ReceiverImpl(int fd) : fd_(fd), - sa_local_(convertSockAddr(&ss_local_)), - sa_remote_(convertSockAddr(&ss_remote_)), - header_buf_(DEFAULT_HEADER_BUFLEN), - data_buf_(INITIAL_BUFSIZE) - { - memset(&ss_local_, 0, sizeof(ss_local_)); - memset(&ss_remote_, 0, sizeof(ss_remote_)); - - if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &SOCKSESSION_BUFSIZE, - sizeof(SOCKSESSION_BUFSIZE)) == -1) { - isc_throw(SocketSessionError, - "Failed to set receive buffer size to " << - SOCKSESSION_BUFSIZE); - } - } - - const int fd_; - struct sockaddr_storage ss_local_; // placeholder for local endpoint - struct sockaddr* const sa_local_; - struct sockaddr_storage ss_remote_; // placeholder for remote endpoint - struct sockaddr* const sa_remote_; - - // placeholder for session header and data - vector<uint8_t> header_buf_; - vector<uint8_t> data_buf_; -}; - -SocketSessionReceiver::SocketSessionReceiver(int fd) : - impl_(new ReceiverImpl(fd)) -{ -} - -SocketSessionReceiver::~SocketSessionReceiver() { - delete impl_; -} - -namespace { -// A shortcut to throw common exception on failure of recv(2) -void -readFail(int actual_len, int expected_len) { - if (expected_len < 0) { - isc_throw(SocketSessionError, "Failed to receive data from " - "SocketSessionForwarder: " << strerror(errno)); - } - isc_throw(SocketSessionError, "Incomplete data from " - "SocketSessionForwarder: " << actual_len << "/" << - expected_len); -} - -// A helper container for a (socket) file descriptor used in -// SocketSessionReceiver::pop that ensures the socket is closed unless it -// can be safely passed to the caller via release(). -struct ScopedSocket : boost::noncopyable { - ScopedSocket(int fd) : fd_(fd) {} - ~ScopedSocket() { - if (fd_ >= 0) { - close(fd_); - } - } - int release() { - const int fd = fd_; - fd_ = -1; - return (fd); - } - int fd_; -}; -} - -SocketSession -SocketSessionReceiver::pop() { - ScopedSocket passed_sock(recv_fd(impl_->fd_)); - if (passed_sock.fd_ == FD_SYSTEM_ERROR) { - isc_throw(SocketSessionError, "Receiving a forwarded FD failed: " << - strerror(errno)); - } else if (passed_sock.fd_ < 0) { - isc_throw(SocketSessionError, "No FD forwarded"); - } - - uint16_t header_len; - const int cc_hlen = recv(impl_->fd_, &header_len, sizeof(header_len), - MSG_WAITALL); - if (cc_hlen < sizeof(header_len)) { - readFail(cc_hlen, sizeof(header_len)); - } - header_len = InputBuffer(&header_len, sizeof(header_len)).readUint16(); - if (header_len > DEFAULT_HEADER_BUFLEN) { - isc_throw(SocketSessionError, "Too large header length: " << - header_len); - } - impl_->header_buf_.clear(); - impl_->header_buf_.resize(header_len); - const int cc_hdr = recv(impl_->fd_, &impl_->header_buf_[0], header_len, - MSG_WAITALL); - if (cc_hdr < header_len) { - readFail(cc_hdr, header_len); - } - - InputBuffer ibuffer(&impl_->header_buf_[0], header_len); - try { - const int family = static_cast<int>(ibuffer.readUint32()); - if (family != AF_INET && family != AF_INET6) { - isc_throw(SocketSessionError, - "Unsupported address family is passed: " << family); - } - const int type = static_cast<int>(ibuffer.readUint32()); - const int protocol = static_cast<int>(ibuffer.readUint32()); - const socklen_t local_end_len = ibuffer.readUint32(); - const socklen_t endpoint_minlen = (family == AF_INET) ? - sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - if (local_end_len < endpoint_minlen || - local_end_len > sizeof(impl_->ss_local_)) { - isc_throw(SocketSessionError, "Invalid local SA length: " << - local_end_len); - } - ibuffer.readData(&impl_->ss_local_, local_end_len); - const socklen_t remote_end_len = ibuffer.readUint32(); - if (remote_end_len < endpoint_minlen || - remote_end_len > sizeof(impl_->ss_remote_)) { - isc_throw(SocketSessionError, "Invalid remote SA length: " << - remote_end_len); - } - ibuffer.readData(&impl_->ss_remote_, remote_end_len); - if (family != impl_->sa_local_->sa_family || - family != impl_->sa_remote_->sa_family) { - isc_throw(SocketSessionError, "SA family inconsistent: " << - static_cast<int>(impl_->sa_local_->sa_family) << ", " << - static_cast<int>(impl_->sa_remote_->sa_family) << - " given, must be " << family); - } - const size_t data_len = ibuffer.readUint32(); - if (data_len == 0 || data_len > MAX_DATASIZE) { - isc_throw(SocketSessionError, - "Invalid socket session data size: " << data_len << - ", must be > 0 and <= " << MAX_DATASIZE); - } - - impl_->data_buf_.clear(); - impl_->data_buf_.resize(data_len); - const int cc_data = recv(impl_->fd_, &impl_->data_buf_[0], data_len, - MSG_WAITALL); - if (cc_data < data_len) { - readFail(cc_data, data_len); - } - - return (SocketSession(passed_sock.release(), family, type, protocol, - impl_->sa_local_, impl_->sa_remote_, - &impl_->data_buf_[0], data_len)); - } catch (const OutOfRange& ex) { - // We catch the case where the given header is too short and convert - // the exception to SocketSessionError. - isc_throw(SocketSessionError, "bogus socket session header: " << - ex.what()); - } -} - -} -} -} diff --git a/src/lib/util/io/socketsession.h b/src/lib/util/io/socketsession.h deleted file mode 100644 index 6c0fac74bd..0000000000 --- a/src/lib/util/io/socketsession.h +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#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 -/// -/// \note This class is currently unused. Once we get to the implementation -/// of the remote parts of the management API, we will evaluate whether -/// this code is useful or not and either start using it or remove it. -/// -/// 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 (Kea) -/// 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 - /// destroyed. - /// - /// 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 diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am index 430c53db91..6dfe681ba5 100644 --- a/src/lib/util/tests/Makefile.am +++ b/src/lib/util/tests/Makefile.am @@ -29,7 +29,6 @@ run_unittests_SOURCES += csv_file_unittest.cc run_unittests_SOURCES += dhcp_space_unittest.cc run_unittests_SOURCES += doubles_unittest.cc run_unittests_SOURCES += encode_unittest.cc -run_unittests_SOURCES += fd_share_tests.cc run_unittests_SOURCES += fd_tests.cc run_unittests_SOURCES += filesystem_unittests.cc run_unittests_SOURCES += hash_unittest.cc diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc deleted file mode 100644 index f870ec2329..0000000000 --- a/src/lib/util/tests/fd_share_tests.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <util/io/fd.h> -#include <util/io/fd_share.h> - -#include <util/unittests/check_valgrind.h> -#include <util/unittests/fork.h> - -#include <gtest/gtest.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <cstdio> - -using namespace isc::util::io; -using namespace isc::util::unittests; - -namespace { - -// We test that we can transfer a pipe over other pipe -TEST(FDShare, transfer) { - - if (!isc::util::unittests::runningOnValgrind()) { - // Get a pipe and fork - int pipes[2]; - ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes)); - const pid_t sender(fork()); - ASSERT_NE(-1, sender); - if (sender) { // We are in parent - // Close the other side of pipe, we want only writable one - EXPECT_NE(-1, close(pipes[0])); - // Get a process to check data - int fd(0); - const pid_t checker(check_output(&fd, "data", 4)); - ASSERT_NE(-1, checker); - // Now, send the file descriptor, close it and close the pipe - EXPECT_NE(-1, send_fd(pipes[1], fd)); - EXPECT_NE(-1, close(pipes[1])); - EXPECT_NE(-1, close(fd)); - // Check both subprocesses ended well - EXPECT_TRUE(process_ok(sender)); - EXPECT_TRUE(process_ok(checker)); - } else { // We are in child. We do not use ASSERT here - // Close the write end, we only read - if (close(pipes[1])) { - exit(1); - } - // Get the file descriptor - const int fd(recv_fd(pipes[0])); - if (fd == -1) { - exit(1); - } - // This pipe is not needed - if (close(pipes[0])) { - exit(1); - } - // Send "data" through the received fd, close it and be done - if (!write_data(fd, "data", 4) || close(fd) == -1) { - exit(1); - } - exit(0); - } - } -} - -} diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am index bd02a5139e..494ceae3ef 100644 --- a/src/lib/util/unittests/Makefile.am +++ b/src/lib/util/unittests/Makefile.am @@ -22,7 +22,4 @@ libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-excepti libutil_unittests_la_LIBADD += $(GTEST_LDADD) endif -# For now, this isn't needed for libutil_unittests -EXTRA_DIST = mock_socketsession.h - CLEANFILES = *.gcno *.gcda diff --git a/src/lib/util/unittests/mock_socketsession.h b/src/lib/util/unittests/mock_socketsession.h deleted file mode 100644 index 2fc2bb2d3b..0000000000 --- a/src/lib/util/unittests/mock_socketsession.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef UTIL_UNITTESTS_MOCKSOCKETSESSION_H -#define UTIL_UNITTESTS_MOCKSOCKETSESSION_H 1 - -#include <exceptions/exceptions.h> - -#include <util/io/socketsession.h> -#include <util/io/sockaddr_util.h> - -#include <cassert> -#include <cstring> -#include <vector> - -#include <sys/socket.h> -#include <stdint.h> - -namespace isc { -namespace util { -namespace unittests { - -/// \brief Mock socket session forwarder. -/// -/// It emulates the behavior of SocketSessionForwarder without involving -/// network communication, and allowing the tester to customize the behavior -/// and to examine forwarded data afterwards. -class MockSocketSessionForwarder : - public isc::util::io::BaseSocketSessionForwarder -{ -public: - MockSocketSessionForwarder() : - is_connected_(false), connect_ok_(true), push_ok_(true), - close_ok_(true), - // These are not used until set, but we set them anyway here, - // partly to silence cppcheck, and partly to be cleaner. Put some - // invalid values in. - pushed_sock_(-1), pushed_family_(-1), pushed_type_(-1), - pushed_protocol_(-1) - {} - - virtual void connectToReceiver() { - if (!connect_ok_) { - isc_throw(isc::util::io::SocketSessionError, "socket session " - "forwarding connection disabled for test"); - } - if (is_connected_) { - isc_throw(isc::util::io::SocketSessionError, "duplicate connect"); - } - is_connected_ = true; - } - virtual void close() { - if (!is_connected_) { - isc_throw(isc::util::io::SocketSessionError, "duplicate close"); - } - is_connected_ = false; - } - - // Pushing a socket session. It copies the given session data - // so that the test code can check the values later via the getter - // methods. Complete deep copy will be created, so the caller doesn't - // have to keep the parameters valid after the call to this method. - 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) - { - if (!push_ok_) { - isc_throw(isc::util::io::SocketSessionError, - "socket session forwarding is disabled for test"); - } - if (!is_connected_) { - isc_throw(isc::util::io::SocketSessionError, - "socket session is being pushed before connected"); - } - - // Copy parameters for later checks - pushed_sock_ = sock; - pushed_family_ = family; - pushed_type_ = type; - pushed_protocol_ = protocol; - assert(io::internal::getSALength(local_end) <= - sizeof(pushed_local_end_ss_)); - std::memcpy(&pushed_local_end_ss_, &local_end, - io::internal::getSALength(local_end)); - assert(io::internal::getSALength(remote_end) <= - sizeof(pushed_remote_end_ss_)); - std::memcpy(&pushed_remote_end_ss_, &remote_end, - io::internal::getSALength(remote_end)); - pushed_data_.resize(data_len); - std::memcpy(&pushed_data_[0], data, data_len); - } - - // Allow the test code to check if the connection is established. - bool isConnected() const { return (is_connected_); } - - // Allow the test code to customize the forwarder behavior wrt whether - // a specific operation should succeed or fail. - void disableConnect() { connect_ok_ = false; } - void enableConnect() { connect_ok_ = true; } - void disableClose() { close_ok_ = false; } - void disablePush() { push_ok_ = false; } - void enablePush() { push_ok_ = true; } - - // Read-only accessors to recorded parameters to the previous successful - // call to push(). Return values are undefined if there has been no - // successful call to push(). - // Note that we use convertSockAddr() to convert sockaddr_storage to - // sockaddr. It should be safe since we use the storage in its literal - // sense; it was originally filled with the binary image of another - // sockaddr structure, and we are going to return the image opaquely - // as a sockaddr structure without touching the data. - int getPushedSock() const { return (pushed_sock_); } - int getPushedFamily() const { return (pushed_family_); } - int getPushedType() const { return (pushed_type_); } - int getPushedProtocol() const { return (pushed_protocol_); } - const struct sockaddr& getPushedLocalend() const { - return (*io::internal::convertSockAddr(&pushed_local_end_ss_)); - } - const struct sockaddr& getPushedRemoteend() const { - return (*io::internal::convertSockAddr(&pushed_remote_end_ss_)); - } - const std::vector<uint8_t>& getPushedData() const { - return (pushed_data_); - } - -private: - bool is_connected_; - bool connect_ok_; - bool push_ok_; - bool close_ok_; - int pushed_sock_; - int pushed_family_; - int pushed_type_; - int pushed_protocol_; - struct sockaddr_storage pushed_local_end_ss_; - struct sockaddr_storage pushed_remote_end_ss_; - std::vector<uint8_t> pushed_data_; -}; - -} // end of unittests -} // end of util -} // end of isc -#endif // UTIL_UNITTESTS_MOCKSOCKETSESSION_H - |