diff options
Diffstat (limited to 'src/lib/util/io/fd_share.cc')
-rw-r--r-- | src/lib/util/io/fd_share.cc | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc new file mode 100644 index 0000000..6edffd3 --- /dev/null +++ b/src/lib/util/io/fd_share.cc @@ -0,0 +1,166 @@ +// 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); +} + +} // End for namespace io +} // End for namespace util +} // End for namespace isc |