Kea 2.5.8
fd_share.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2019 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cstring>
10#include <cstdlib>
11
12#include <sys/types.h>
13#include <sys/socket.h>
14#include <sys/uio.h>
15#include <errno.h>
16#include <stdlib.h> // for malloc and free
17#include <unistd.h>
18#include <util/io/fd_share.h>
19
20namespace isc {
21namespace util {
22namespace io {
23
24namespace {
25// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE.
26// In order to ensure as much portability as possible, we provide wrapper
27// functions of these macros.
28// Note that cmsg_space() could run slow on OSes that do not have
29// CMSG_SPACE.
30inline socklen_t
31cmsg_len(const socklen_t len) {
32#ifdef CMSG_LEN
33 return (CMSG_LEN(len));
34#else
35 // Cast NULL so that any pointer arithmetic performed by CMSG_DATA
36 // is correct.
37 const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL));
38 return (hdrlen + len);
39#endif
40}
41
42inline socklen_t
43cmsg_space(const socklen_t len) {
44#ifdef CMSG_SPACE
45 return (CMSG_SPACE(len));
46#else
47 struct msghdr msg;
48 struct cmsghdr* cmsgp;
49 // XXX: The buffer length is an ad hoc value, but should be enough
50 // in a practical sense.
51 char dummybuf[sizeof(struct cmsghdr) + 1024];
52
53 memset(&msg, 0, sizeof(msg));
54 msg.msg_control = dummybuf;
55 msg.msg_controllen = sizeof(dummybuf);
56
57 cmsgp = (struct cmsghdr*)dummybuf;
58 cmsgp->cmsg_len = cmsg_len(len);
59
60 cmsgp = CMSG_NXTHDR(&msg, cmsgp);
61 if (cmsgp != NULL) {
62 return ((char*)cmsgp - (char*)msg.msg_control);
63 } else {
64 return (0);
65 }
66#endif // CMSG_SPACE
67}
68}
69
70int
71recv_fd(const int sock) {
72 struct msghdr msghdr;
73 struct iovec iov_dummy;
74 unsigned char dummy_data;
75
76 iov_dummy.iov_base = &dummy_data;
77 iov_dummy.iov_len = sizeof(dummy_data);
78 msghdr.msg_name = NULL;
79 msghdr.msg_namelen = 0;
80 msghdr.msg_iov = &iov_dummy;
81 msghdr.msg_iovlen = 1;
82 msghdr.msg_flags = 0;
83 msghdr.msg_controllen = cmsg_space(sizeof(int));
84 msghdr.msg_control = malloc(msghdr.msg_controllen);
85 if (msghdr.msg_control == NULL) {
86 return (FD_SYSTEM_ERROR);
87 }
88
89 const int cc = recvmsg(sock, &msghdr, 0);
90 if (cc <= 0) {
91 free(msghdr.msg_control);
92 if (cc == 0) {
93 errno = ECONNRESET;
94 }
95 return (FD_SYSTEM_ERROR);
96 }
97 const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
98 int fd = FD_OTHER_ERROR;
99 if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
100 cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
101// Some systems (e.g. recent NetBSD) converted all CMSG access macros
102// to static_cast when used in C++ code. As cmsg is declared const
103// this makes the CMSG_DATA macro to not compile. But fortunately
104// these systems provide a const alternative named CCMSG_DATA.
105#ifdef CCMSG_DATA
106 std::memcpy(&fd, CCMSG_DATA(cmsg), sizeof(int));
107#else
108 std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
109#endif
110 }
111 free(msghdr.msg_control);
112 int new_fd = -1;
113 int close_error = -1;
114 if (fd >= 0) {
115 // It is strange, but the call can return the same file descriptor as
116 // one returned previously, even if that one is not closed yet. So,
117 // we just re-number every one we get, so they are unique.
118 new_fd = dup(fd);
119 close_error = close(fd);
120 }
121 if (close_error == -1 || new_fd == -1) {
122 // We need to return an error, because something failed. But in case
123 // it was the previous close, we at least try to close the duped FD.
124 if (new_fd != -1) {
125 close(new_fd); // If this fails, nothing but returning error can't
126 // be done and we are doing that anyway.
127 }
128 return (FD_SYSTEM_ERROR);
129 }
130 return (new_fd);
131}
132
133int
134send_fd(const int sock, const int fd) {
135 struct msghdr msghdr;
136 struct iovec iov_dummy;
137 unsigned char dummy_data = 0;
138
139 iov_dummy.iov_base = &dummy_data;
140 iov_dummy.iov_len = sizeof(dummy_data);
141 msghdr.msg_name = NULL;
142 msghdr.msg_namelen = 0;
143 msghdr.msg_iov = &iov_dummy;
144 msghdr.msg_iovlen = 1;
145 msghdr.msg_flags = 0;
146 msghdr.msg_controllen = cmsg_space(sizeof(int));
147 msghdr.msg_control = malloc(msghdr.msg_controllen);
148 if (msghdr.msg_control == NULL) {
149 return (FD_OTHER_ERROR);
150 }
151 std::memset(msghdr.msg_control, 0, msghdr.msg_controllen);
152
153 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
154 cmsg->cmsg_len = cmsg_len(sizeof(int));
155 cmsg->cmsg_level = SOL_SOCKET;
156 cmsg->cmsg_type = SCM_RIGHTS;
157 std::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
158
159 const int ret = sendmsg(sock, &msghdr, 0);
160 free(msghdr.msg_control);
161 return (ret >= 0 ? 0 : FD_SYSTEM_ERROR);
162}
163
164} // namespace io
165} // namespace util
166} // namespace isc
Support to transfer file descriptors between processes.
const int FD_SYSTEM_ERROR
Definition: fd_share.h:20
int recv_fd(const int sock)
Receives a file descriptor.
Definition: fd_share.cc:71
int send_fd(const int sock, const int fd)
Sends a file descriptor.
Definition: fd_share.cc:134
const int FD_OTHER_ERROR
Definition: fd_share.h:21
Defines the logger used by the top-level component of kea-lfc.