Kea  1.9.9-git
socketsession.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2020 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 <unistd.h>
10 
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/uio.h>
14 #include <sys/un.h>
15 
16 #include <netinet/in.h>
17 
18 #include <fcntl.h>
19 #include <stdint.h>
20 
21 #include <cerrno>
22 #include <csignal>
23 #include <cstddef>
24 #include <cstring>
25 
26 #include <string>
27 #include <vector>
28 
29 #include <boost/noncopyable.hpp>
30 
31 #include <exceptions/exceptions.h>
32 #include <exceptions/isc_assert.h>
33 
34 #include <util/buffer.h>
35 
36 #include <util/io/fd_share.h>
37 #include <util/io/socketsession.h>
38 #include <util/io/sockaddr_util.h>
39 
40 using namespace std;
41 
42 namespace isc {
43 namespace util {
44 namespace io {
45 
46 using namespace internal;
47 
48 // The expected max size of the session header: 2-byte header length,
49 // 6 32-bit fields, and 2 sockaddr structure. (see the SocketSessionUtility
50 // overview description in the header file). sizeof sockaddr_storage
51 // should be the possible max of any sockaddr structure
52 const size_t DEFAULT_HEADER_BUFLEN = sizeof(uint16_t) + sizeof(uint32_t) * 6 +
53  sizeof(struct sockaddr_storage) * 2;
54 
55 // The allowable maximum size of data passed with the socket FD. For now
56 // we use a fixed value of 65535, the largest possible size of valid DNS
57 // messages. We may enlarge it or make it configurable as we see the need
58 // for more flexibility.
59 const int MAX_DATASIZE = 65535;
60 
61 // The initial buffer size for receiving socket session data in the receiver.
62 // This value is the maximum message size of DNS messages carried over UDP
63 // (without EDNS). In our expected usage (at the moment) this should be
64 // sufficiently large (the expected data is AXFR/IXFR query or an UPDATE
65 // requests. The former should be generally quite small. While the latter
66 // could be large, it would often be small enough for a single UDP message).
67 // If it turns out that there are many exceptions, we may want to extend
68 // the class so that this value can be customized. Note that the buffer
69 // will be automatically extended for longer data and this is only about
70 // efficiency.
71 const size_t INITIAL_BUFSIZE = 512;
72 
73 // The (default) socket buffer size for the forwarder and receiver. This is
74 // chosen to be sufficiently large to store two full-size DNS messages. We
75 // may want to customize this value in future.
76 const int SOCKSESSION_BUFSIZE = (DEFAULT_HEADER_BUFLEN + MAX_DATASIZE) * 2;
77 
79  ForwarderImpl() : fd_(-1), buf_(DEFAULT_HEADER_BUFLEN) {}
80  struct sockaddr_un sock_un_;
81  socklen_t sock_un_len_;
82  int fd_;
84 };
85 
86 SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
87  impl_(NULL)
88 {
89  // We need to filter SIGPIPE for subsequent push(). See the class
90  // description.
91  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
92  isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
93  }
94 
96  if (sizeof(impl.sock_un_.sun_path) - 1 < unix_file.length()) {
98  "File name for a UNIX domain socket is too long: " <<
99  unix_file);
100  }
101  impl.sock_un_.sun_family = AF_UNIX;
102  // the copy should be safe due to the above check, but we'd be rather
103  // paranoid about making it 100% sure even if the check has a bug (with
104  // triggering the assertion in the worse case)
105  memset(&impl.sock_un_.sun_path, 0, sizeof(impl.sock_un_.sun_path));
106  strncpy(impl.sock_un_.sun_path, unix_file.c_str(),
107  sizeof(impl.sock_un_.sun_path) - 1);
108  isc_throw_assert(impl.sock_un_.sun_path[sizeof(impl.sock_un_.sun_path) - 1] == '\0');
109  impl.sock_un_len_ = offsetof(struct sockaddr_un, sun_path) +
110  unix_file.length();
111 #ifdef HAVE_SA_LEN
112  impl.sock_un_.sun_len = impl.sock_un_len_;
113 #endif
114  impl.fd_ = -1;
115 
116  impl_ = new ForwarderImpl;
117  *impl_ = impl;
118 }
119 
121  if (impl_->fd_ != -1) {
122  close();
123  }
124  delete impl_;
125 }
126 
127 void
129  if (impl_->fd_ != -1) {
130  isc_throw(BadValue, "Duplicate connect to UNIX domain "
131  "endpoint " << impl_->sock_un_.sun_path);
132  }
133 
134  impl_->fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
135  if (impl_->fd_ == -1) {
136  isc_throw(SocketSessionError, "Failed to create a UNIX domain socket: "
137  << strerror(errno));
138  }
139  // Make the socket non blocking
140  int fcntl_flags = fcntl(impl_->fd_, F_GETFL, 0);
141  if (fcntl_flags != -1) {
142  fcntl_flags |= O_NONBLOCK;
143  fcntl_flags = fcntl(impl_->fd_, F_SETFL, fcntl_flags);
144  }
145  if (fcntl_flags == -1) {
146  close(); // note: this is the internal method, not ::close()
148  "Failed to make UNIX domain socket non blocking: " <<
149  strerror(errno));
150  }
151  // Ensure the socket send buffer is large enough. If we can't get the
152  // current size, simply set the sufficient size.
153  int sndbuf_size;
154  socklen_t sndbuf_size_len = sizeof(sndbuf_size);
155  if (getsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &sndbuf_size,
156  &sndbuf_size_len) == -1 ||
157  sndbuf_size < SOCKSESSION_BUFSIZE) {
158  if (setsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &SOCKSESSION_BUFSIZE,
159  sizeof(SOCKSESSION_BUFSIZE)) == -1) {
160  close();
162  "Failed to set send buffer size to " <<
163  SOCKSESSION_BUFSIZE);
164  }
165  }
166  if (connect(impl_->fd_, convertSockAddr(&impl_->sock_un_),
167  impl_->sock_un_len_) == -1) {
168  close();
169  isc_throw(SocketSessionError, "Failed to connect to UNIX domain "
170  "endpoint " << impl_->sock_un_.sun_path << ": " <<
171  strerror(errno));
172  }
173 }
174 
175 void
177  if (impl_->fd_ == -1) {
178  isc_throw(BadValue, "Attempt of close before connect");
179  }
180  ::close(impl_->fd_);
181  impl_->fd_ = -1;
182 }
183 
184 void
185 SocketSessionForwarder::push(int sock, int family, int type, int protocol,
186  const struct sockaddr& local_end,
187  const struct sockaddr& remote_end,
188  const void* data, size_t data_len)
189 {
190  if (impl_->fd_ == -1) {
191  isc_throw(BadValue, "Attempt of push before connect");
192  }
193  if ((local_end.sa_family != AF_INET && local_end.sa_family != AF_INET6) ||
194  (remote_end.sa_family != AF_INET && remote_end.sa_family != AF_INET6))
195  {
196  isc_throw(BadValue, "Invalid address family: must be "
197  "AF_INET or AF_INET6; " <<
198  static_cast<int>(local_end.sa_family) << ", " <<
199  static_cast<int>(remote_end.sa_family) << " given");
200  }
201  if (family != local_end.sa_family || family != remote_end.sa_family) {
202  isc_throw(BadValue, "Inconsistent address family: must be "
203  << static_cast<int>(family) << "; "
204  << static_cast<int>(local_end.sa_family) << ", "
205  << static_cast<int>(remote_end.sa_family) << " given");
206  }
207  if (data_len == 0 || data == NULL) {
208  isc_throw(BadValue, "Data for a socket session must not be empty");
209  }
210  if (data_len > MAX_DATASIZE) {
211  isc_throw(BadValue, "Invalid socket session data size: " <<
212  data_len << ", must not exceed " << MAX_DATASIZE);
213  }
214 
215  if (send_fd(impl_->fd_, sock) != 0) {
216  isc_throw(SocketSessionError, "FD passing failed: " <<
217  strerror(errno));
218  }
219 
220  impl_->buf_.clear();
221  // Leave the space for the header length
222  impl_->buf_.skip(sizeof(uint16_t));
223  // Socket properties: family, type, protocol
224  impl_->buf_.writeUint32(static_cast<uint32_t>(family));
225  impl_->buf_.writeUint32(static_cast<uint32_t>(type));
226  impl_->buf_.writeUint32(static_cast<uint32_t>(protocol));
227  // Local endpoint
228  impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end)));
229  impl_->buf_.writeData(&local_end, getSALength(local_end));
230  // Remote endpoint
231  impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(remote_end)));
232  impl_->buf_.writeData(&remote_end, getSALength(remote_end));
233  // Data length. Must be fit uint32 due to the range check above.
234  const uint32_t data_len32 = static_cast<uint32_t>(data_len);
235  isc_throw_assert(data_len == data_len32); // shouldn't cause overflow.
236  impl_->buf_.writeUint32(data_len32);
237  // Write the resulting header length at the beginning of the buffer
238  impl_->buf_.writeUint16At(impl_->buf_.getLength() - sizeof(uint16_t), 0);
239 
240  const struct iovec iov[2] = {
241  { const_cast<void*>(impl_->buf_.getData()), impl_->buf_.getLength() },
242  { const_cast<void*>(data), data_len }
243  };
244  const int cc = writev(impl_->fd_, iov, 2);
245  if (cc != impl_->buf_.getLength() + data_len) {
246  if (cc < 0) {
248  "Write failed in forwarding a socket session: " <<
249  strerror(errno));
250  }
252  "Incomplete write in forwarding a socket session: " << cc <<
253  "/" << (impl_->buf_.getLength() + data_len));
254  }
255 }
256 
257 SocketSession::SocketSession(int sock, int family, int type, int protocol,
258  const sockaddr* local_end,
259  const sockaddr* remote_end,
260  const void* data, size_t data_len) :
261  sock_(sock), family_(family), type_(type), protocol_(protocol),
262  local_end_(local_end), remote_end_(remote_end),
263  data_(data), data_len_(data_len)
264 {
265  if (local_end == NULL || remote_end == NULL) {
266  isc_throw(BadValue, "sockaddr must be non NULL for SocketSession");
267  }
268  if (data_len == 0) {
269  isc_throw(BadValue, "data_len must be non 0 for SocketSession");
270  }
271  if (data == NULL) {
272  isc_throw(BadValue, "data must be non NULL for SocketSession");
273  }
274 }
275 
277  ReceiverImpl(int fd) : fd_(fd),
280  header_buf_(DEFAULT_HEADER_BUFLEN),
281  data_buf_(INITIAL_BUFSIZE)
282  {
283  memset(&ss_local_, 0, sizeof(ss_local_));
284  memset(&ss_remote_, 0, sizeof(ss_remote_));
285 
286  if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &SOCKSESSION_BUFSIZE,
287  sizeof(SOCKSESSION_BUFSIZE)) == -1) {
289  "Failed to set receive buffer size to " <<
290  SOCKSESSION_BUFSIZE);
291  }
292  }
293 
294  const int fd_;
295  struct sockaddr_storage ss_local_; // placeholder for local endpoint
296  struct sockaddr* const sa_local_;
297  struct sockaddr_storage ss_remote_; // placeholder for remote endpoint
298  struct sockaddr* const sa_remote_;
299 
300  // placeholder for session header and data
301  vector<uint8_t> header_buf_;
302  vector<uint8_t> data_buf_;
303 };
304 
306  impl_(new ReceiverImpl(fd))
307 {
308 }
309 
311  delete impl_;
312 }
313 
314 namespace {
315 // A shortcut to throw common exception on failure of recv(2)
316 void
317 readFail(int actual_len, int expected_len) {
318  if (expected_len < 0) {
319  isc_throw(SocketSessionError, "Failed to receive data from "
320  "SocketSessionForwarder: " << strerror(errno));
321  }
322  isc_throw(SocketSessionError, "Incomplete data from "
323  "SocketSessionForwarder: " << actual_len << "/" <<
324  expected_len);
325 }
326 
327 // A helper container for a (socket) file descriptor used in
328 // SocketSessionReceiver::pop that ensures the socket is closed unless it
329 // can be safely passed to the caller via release().
330 struct ScopedSocket : boost::noncopyable {
331  ScopedSocket(int fd) : fd_(fd) {}
332  ~ScopedSocket() {
333  if (fd_ >= 0) {
334  close(fd_);
335  }
336  }
337  int release() {
338  const int fd = fd_;
339  fd_ = -1;
340  return (fd);
341  }
342  int fd_;
343 };
344 }
345 
346 SocketSession
348  ScopedSocket passed_sock(recv_fd(impl_->fd_));
349  if (passed_sock.fd_ == FD_SYSTEM_ERROR) {
350  isc_throw(SocketSessionError, "Receiving a forwarded FD failed: " <<
351  strerror(errno));
352  } else if (passed_sock.fd_ < 0) {
353  isc_throw(SocketSessionError, "No FD forwarded");
354  }
355 
356  uint16_t header_len;
357  const int cc_hlen = recv(impl_->fd_, &header_len, sizeof(header_len),
358  MSG_WAITALL);
359  if (cc_hlen < sizeof(header_len)) {
360  readFail(cc_hlen, sizeof(header_len));
361  }
362  header_len = InputBuffer(&header_len, sizeof(header_len)).readUint16();
363  if (header_len > DEFAULT_HEADER_BUFLEN) {
364  isc_throw(SocketSessionError, "Too large header length: " <<
365  header_len);
366  }
367  impl_->header_buf_.clear();
368  impl_->header_buf_.resize(header_len);
369  const int cc_hdr = recv(impl_->fd_, &impl_->header_buf_[0], header_len,
370  MSG_WAITALL);
371  if (cc_hdr < header_len) {
372  readFail(cc_hdr, header_len);
373  }
374 
375  InputBuffer ibuffer(&impl_->header_buf_[0], header_len);
376  try {
377  const int family = static_cast<int>(ibuffer.readUint32());
378  if (family != AF_INET && family != AF_INET6) {
380  "Unsupported address family is passed: " << family);
381  }
382  const int type = static_cast<int>(ibuffer.readUint32());
383  const int protocol = static_cast<int>(ibuffer.readUint32());
384  const socklen_t local_end_len = ibuffer.readUint32();
385  const socklen_t endpoint_minlen = (family == AF_INET) ?
386  sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
387  if (local_end_len < endpoint_minlen ||
388  local_end_len > sizeof(impl_->ss_local_)) {
389  isc_throw(SocketSessionError, "Invalid local SA length: " <<
390  local_end_len);
391  }
392  ibuffer.readData(&impl_->ss_local_, local_end_len);
393  const socklen_t remote_end_len = ibuffer.readUint32();
394  if (remote_end_len < endpoint_minlen ||
395  remote_end_len > sizeof(impl_->ss_remote_)) {
396  isc_throw(SocketSessionError, "Invalid remote SA length: " <<
397  remote_end_len);
398  }
399  ibuffer.readData(&impl_->ss_remote_, remote_end_len);
400  if (family != impl_->sa_local_->sa_family ||
401  family != impl_->sa_remote_->sa_family) {
402  isc_throw(SocketSessionError, "SA family inconsistent: " <<
403  static_cast<int>(impl_->sa_local_->sa_family) << ", " <<
404  static_cast<int>(impl_->sa_remote_->sa_family) <<
405  " given, must be " << family);
406  }
407  const size_t data_len = ibuffer.readUint32();
408  if (data_len == 0 || data_len > MAX_DATASIZE) {
410  "Invalid socket session data size: " << data_len <<
411  ", must be > 0 and <= " << MAX_DATASIZE);
412  }
413 
414  impl_->data_buf_.clear();
415  impl_->data_buf_.resize(data_len);
416  const int cc_data = recv(impl_->fd_, &impl_->data_buf_[0], data_len,
417  MSG_WAITALL);
418  if (cc_data < data_len) {
419  readFail(cc_data, data_len);
420  }
421 
422  return (SocketSession(passed_sock.release(), family, type, protocol,
423  impl_->sa_local_, impl_->sa_remote_,
424  &impl_->data_buf_[0], data_len));
425  } catch (const InvalidBufferPosition& ex) {
426  // We catch the case where the given header is too short and convert
427  // the exception to SocketSessionError.
428  isc_throw(SocketSessionError, "bogus socket session header: " <<
429  ex.what());
430  }
431 }
432 
433 }
434 }
435 }
int recv_fd(const int sock)
Receives a file descriptor.
Definition: fd_share.cc:71
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition: isc_assert.h:18
socklen_t getSALength(const struct sockaddr &sa)
Definition: sockaddr_util.h:26
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
void skip(size_t len)
Insert a specified length of gap at the end of the buffer.
Definition: buffer.h:429
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)
Forward a socket session to the receiver.
STL namespace.
virtual ~SocketSessionForwarder()
The destructor.
Support to transfer file descriptors between processes.
SocketSession(int sock, int family, int type, int protocol, const sockaddr *local_end, const sockaddr *remote_end, const void *data, size_t data_len)
The constructor.
An exception indicating general errors that takes place in the socket session related class objects...
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void writeUint16At(uint16_t data, size_t pos)
Write an unsigned 16-bit integer in host byte order at the specified position of the buffer in networ...
Definition: buffer.h:507
A generic exception that is thrown when an unexpected error condition occurs.
const size_t INITIAL_BUFSIZE
const size_t DEFAULT_HEADER_BUFLEN
int send_fd(const int sock, const int fd)
Sends a file descriptor.
Definition: fd_share.cc:134
void clear()
Clear buffer content.
Definition: buffer.h:451
A standard DNS module exception that is thrown if an out-of-range buffer operation is being performed...
Definition: buffer.h:27
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
uint16_t readUint16()
Read an unsigned 16-bit integer in network byte order from the buffer, convert it to host byte order...
Definition: buffer.h:142
void writeUint32(uint32_t data)
Write an unsigned 32-bit integer in host byte order into the buffer in network byte order...
Definition: buffer.h:520
Defines the logger used by the top-level component of kea-dhcp-ddns.
const int MAX_DATASIZE
uint32_t readUint32()
Read an unsigned 32-bit integer in network byte order from the buffer, convert it to host byte order...
Definition: buffer.h:162
FlexOptionImplPtr impl
void readData(void *data, size_t len)
Read data of the specified length from the buffer and copy it to the caller supplied buffer...
Definition: buffer.h:186
const struct sockaddr * convertSockAddr(const SAType *sa)
Definition: sockaddr_util.h:41
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
SocketSession pop()
Receive a socket session from the forwarder.
const int SOCKSESSION_BUFSIZE
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
Socket session object.
virtual void connectToReceiver()
Establish a connection to the receiver.
SocketSessionReceiver(int fd)
The constructor.
virtual void close()
Close the connection to the receiver.
int fd_
const int FD_SYSTEM_ERROR
Definition: fd_share.h:20