Kea  2.3.5-git
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 
20 namespace isc {
21 namespace util {
22 namespace io {
23 
24 namespace {
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.
30 inline socklen_t
31 cmsg_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 
42 inline socklen_t
43 cmsg_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 
70 int
71 recv_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 
133 int
134 send_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 } // End for namespace io
165 } // End for namespace util
166 } // End for namespace isc
int recv_fd(const int sock)
Receives a file descriptor.
Definition: fd_share.cc:71
Support to transfer file descriptors between processes.
int send_fd(const int sock, const int fd)
Sends a file descriptor.
Definition: fd_share.cc:134
Defines the logger used by the top-level component of kea-lfc.
const int FD_OTHER_ERROR
Definition: fd_share.h:21
const int FD_SYSTEM_ERROR
Definition: fd_share.h:20