Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <sys/types.h>
18 : : #include <sys/socket.h>
19 : : #include <sys/uio.h>
20 : : #include <sys/un.h>
21 : :
22 : : #include <netinet/in.h>
23 : :
24 : : #include <fcntl.h>
25 : : #include <stdint.h>
26 : :
27 : : #include <cerrno>
28 : : #include <csignal>
29 : : #include <cstddef>
30 : : #include <cstring>
31 : : #include <cassert>
32 : :
33 : : #include <string>
34 : : #include <vector>
35 : :
36 : : #include <boost/noncopyable.hpp>
37 : :
38 : : #include <exceptions/exceptions.h>
39 : :
40 : : #include <util/buffer.h>
41 : :
42 : : #include "fd_share.h"
43 : : #include "socketsession.h"
44 : : #include "sockaddr_util.h"
45 : :
46 : : using namespace std;
47 : :
48 : : namespace isc {
49 : : namespace util {
50 : : namespace io {
51 : :
52 : : using namespace internal;
53 : :
54 : : // The expected max size of the session header: 2-byte header length,
55 : : // 6 32-bit fields, and 2 sockaddr structure. (see the SocketSessionUtility
56 : : // overview description in the header file). sizeof sockaddr_storage
57 : : // should be the possible max of any sockaddr structure
58 : : const size_t DEFAULT_HEADER_BUFLEN = sizeof(uint16_t) + sizeof(uint32_t) * 6 +
59 : : sizeof(struct sockaddr_storage) * 2;
60 : :
61 : : // The allowable maximum size of data passed with the socket FD. For now
62 : : // we use a fixed value of 65535, the largest possible size of valid DNS
63 : : // messages. We may enlarge it or make it configurable as we see the need
64 : : // for more flexibility.
65 : : const int MAX_DATASIZE = 65535;
66 : :
67 : : // The initial buffer size for receiving socket session data in the receiver.
68 : : // This value is the maximum message size of DNS messages carried over UDP
69 : : // (without EDNS). In our expected usage (at the moment) this should be
70 : : // sufficiently large (the expected data is AXFR/IXFR query or an UPDATE
71 : : // requests. The former should be generally quite small. While the latter
72 : : // could be large, it would often be small enough for a single UDP message).
73 : : // If it turns out that there are many exceptions, we may want to extend
74 : : // the class so that this value can be customized. Note that the buffer
75 : : // will be automatically extended for longer data and this is only about
76 : : // efficiency.
77 : : const size_t INITIAL_BUFSIZE = 512;
78 : :
79 : : // The (default) socket buffer size for the forwarder and receiver. This is
80 : : // chosen to be sufficiently large to store two full-size DNS messages. We
81 : : // may want to customize this value in future.
82 : : const int SOCKSESSION_BUFSIZE = (DEFAULT_HEADER_BUFLEN + MAX_DATASIZE) * 2;
83 : :
84 : 18 : struct SocketSessionForwarder::ForwarderImpl {
85 : 37 : ForwarderImpl() : buf_(DEFAULT_HEADER_BUFLEN) {}
86 : : struct sockaddr_un sock_un_;
87 : : socklen_t sock_un_len_;
88 : : int fd_;
89 : : OutputBuffer buf_;
90 : : };
91 : :
92 : 19 : SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
93 : 19 : impl_(NULL)
94 : : {
95 : : // We need to filter SIGPIPE for subsequent push(). See the class
96 : : // description.
97 [ - + ]: 19 : if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
98 [ # # ][ # # ]: 0 : isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
99 : : }
100 : :
101 : 19 : ForwarderImpl impl;
102 [ + + ]: 19 : if (sizeof(impl.sock_un_.sun_path) - 1 < unix_file.length()) {
103 [ + - ][ + - ]: 2 : isc_throw(SocketSessionError,
[ + - ][ + - ]
104 : : "File name for a UNIX domain socket is too long: " <<
105 : : unix_file);
106 : : }
107 : 18 : impl.sock_un_.sun_family = AF_UNIX;
108 : : // the copy should be safe due to the above check, but we'd be rather
109 : : // paranoid about making it 100% sure even if the check has a bug (with
110 : : // triggering the assertion in the worse case)
111 : : strncpy(impl.sock_un_.sun_path, unix_file.c_str(),
112 : : sizeof(impl.sock_un_.sun_path));
113 [ - + ]: 18 : assert(impl.sock_un_.sun_path[sizeof(impl.sock_un_.sun_path) - 1] == '\0');
114 : : impl.sock_un_len_ = offsetof(struct sockaddr_un, sun_path) +
115 : 18 : unix_file.length();
116 : : #ifdef HAVE_SA_LEN
117 : : impl.sock_un_.sun_len = impl.sock_un_len_;
118 : : #endif
119 : 18 : impl.fd_ = -1;
120 : :
121 [ + - ][ + - ]: 18 : impl_ = new ForwarderImpl;
122 : 18 : *impl_ = impl;
123 : 18 : }
124 : :
125 : 18 : SocketSessionForwarder::~SocketSessionForwarder() {
126 [ + + ]: 18 : if (impl_->fd_ != -1) {
127 : 8 : close();
128 : : }
129 [ + - ]: 36 : delete impl_;
130 : 18 : }
131 : :
132 : : void
133 : 12 : SocketSessionForwarder::connectToReceiver() {
134 [ + + ]: 12 : if (impl_->fd_ != -1) {
135 [ + - ][ + - ]: 2 : isc_throw(BadValue, "Duplicate connect to UNIX domain "
[ + - ]
136 : : "endpoint " << impl_->sock_un_.sun_path);
137 : : }
138 : :
139 : 11 : impl_->fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
140 [ - + ]: 11 : if (impl_->fd_ == -1) {
141 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError, "Failed to create a UNIX domain socket: "
[ # # ]
142 : : << strerror(errno));
143 : : }
144 : : // Make the socket non blocking
145 : 11 : int fcntl_flags = fcntl(impl_->fd_, F_GETFL, 0);
146 [ + - ]: 11 : if (fcntl_flags != -1) {
147 : 11 : fcntl_flags |= O_NONBLOCK;
148 : 11 : fcntl_flags = fcntl(impl_->fd_, F_SETFL, fcntl_flags);
149 : : }
150 [ - + ]: 11 : if (fcntl_flags == -1) {
151 : 0 : close(); // note: this is the internal method, not ::close()
152 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError,
[ # # ]
153 : : "Failed to make UNIX domain socket non blocking: " <<
154 : : strerror(errno));
155 : : }
156 : : // Ensure the socket send buffer is large enough. If we can't get the
157 : : // current size, simply set the sufficient size.
158 : : int sndbuf_size;
159 : 11 : socklen_t sndbuf_size_len = sizeof(sndbuf_size);
160 [ + - ][ - + ]: 11 : if (getsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &sndbuf_size,
[ + - ]
161 : 11 : &sndbuf_size_len) == -1 ||
162 : : sndbuf_size < SOCKSESSION_BUFSIZE) {
163 [ - + ]: 11 : if (setsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &SOCKSESSION_BUFSIZE,
164 : 11 : sizeof(SOCKSESSION_BUFSIZE)) == -1) {
165 : 0 : close();
166 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError, "Failed to set send buffer size");
167 : : }
168 : : }
169 [ + + ]: 11 : if (connect(impl_->fd_, convertSockAddr(&impl_->sock_un_),
170 : 11 : impl_->sock_un_len_) == -1) {
171 : 1 : close();
172 [ + - ][ + - ]: 2 : isc_throw(SocketSessionError, "Failed to connect to UNIX domain "
[ + - ][ + - ]
[ + - ]
173 : : "endpoint " << impl_->sock_un_.sun_path << ": " <<
174 : : strerror(errno));
175 : : }
176 : 10 : }
177 : :
178 : : void
179 : 14 : SocketSessionForwarder::close() {
180 [ + + ]: 14 : if (impl_->fd_ == -1) {
181 [ + - ][ + - ]: 6 : isc_throw(BadValue, "Attempt of close before connect");
182 : : }
183 : 11 : ::close(impl_->fd_);
184 : 11 : impl_->fd_ = -1;
185 : 11 : }
186 : :
187 : : void
188 : 40 : SocketSessionForwarder::push(int sock, int family, int type, int protocol,
189 : : const struct sockaddr& local_end,
190 : : const struct sockaddr& remote_end,
191 : : const void* data, size_t data_len)
192 : : {
193 [ + + ]: 40 : if (impl_->fd_ == -1) {
194 [ + - ][ + - ]: 4 : isc_throw(BadValue, "Attempt of push before connect");
195 : : }
196 [ + + ][ + + ]: 38 : if ((local_end.sa_family != AF_INET && local_end.sa_family != AF_INET6) ||
197 : : (remote_end.sa_family != AF_INET && remote_end.sa_family != AF_INET6))
198 : : {
199 [ + - ][ + - ]: 4 : isc_throw(BadValue, "Invalid address family: must be "
[ + - ][ + - ]
[ + - ][ + - ]
200 : : "AF_INET or AF_INET6; " <<
201 : : static_cast<int>(local_end.sa_family) << ", " <<
202 : : static_cast<int>(remote_end.sa_family) << " given");
203 : : }
204 [ + + ][ + + ]: 36 : if (family != local_end.sa_family || family != remote_end.sa_family) {
205 [ + - ][ + - ]: 8 : isc_throw(BadValue, "Inconsistent address family: must be "
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
206 : : << static_cast<int>(family) << "; "
207 : : << static_cast<int>(local_end.sa_family) << ", "
208 : : << static_cast<int>(remote_end.sa_family) << " given");
209 : : }
210 [ + + ]: 32 : if (data_len == 0 || data == NULL) {
211 [ + - ][ + - ]: 6 : isc_throw(BadValue, "Data for a socket session must not be empty");
212 : : }
213 [ + + ]: 29 : if (data_len > MAX_DATASIZE) {
214 [ + - ][ + - ]: 6 : isc_throw(BadValue, "Invalid socket session data size: " <<
[ + - ][ + - ]
215 : : data_len << ", must not exceed " << MAX_DATASIZE);
216 : : }
217 : :
218 [ + + ]: 27 : if (send_fd(impl_->fd_, sock) != 0) {
219 [ + - ][ + - ]: 4 : isc_throw(SocketSessionError, "FD passing failed: " <<
[ + - ]
220 : : strerror(errno));
221 : : }
222 : :
223 : 25 : impl_->buf_.clear();
224 : : // Leave the space for the header length
225 : 25 : impl_->buf_.skip(sizeof(uint16_t));
226 : : // Socket properties: family, type, protocol
227 : 25 : impl_->buf_.writeUint32(static_cast<uint32_t>(family));
228 : 25 : impl_->buf_.writeUint32(static_cast<uint32_t>(type));
229 : 25 : impl_->buf_.writeUint32(static_cast<uint32_t>(protocol));
230 : : // Local endpoint
231 : 25 : impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end)));
232 : 25 : impl_->buf_.writeData(&local_end, getSALength(local_end));
233 : : // Remote endpoint
234 : 25 : impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(remote_end)));
235 : 25 : impl_->buf_.writeData(&remote_end, getSALength(remote_end));
236 : : // Data length. Must be fit uint32 due to the range check above.
237 : 25 : const uint32_t data_len32 = static_cast<uint32_t>(data_len);
238 [ - + ]: 25 : assert(data_len == data_len32); // shouldn't cause overflow.
239 : 25 : impl_->buf_.writeUint32(data_len32);
240 : : // Write the resulting header length at the beginning of the buffer
241 : 25 : impl_->buf_.writeUint16At(impl_->buf_.getLength() - sizeof(uint16_t), 0);
242 : :
243 : : const struct iovec iov[2] = {
244 : 50 : { const_cast<void*>(impl_->buf_.getData()), impl_->buf_.getLength() },
245 : : { const_cast<void*>(data), data_len }
246 : 50 : };
247 : 25 : const int cc = writev(impl_->fd_, iov, 2);
248 [ + + ]: 25 : if (cc != impl_->buf_.getLength() + data_len) {
249 [ - + ]: 2 : if (cc < 0) {
250 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError,
[ # # ]
251 : : "Write failed in forwarding a socket session: " <<
252 : : strerror(errno));
253 : : }
254 [ + - ][ + - ]: 6 : isc_throw(SocketSessionError,
[ + - ][ + - ]
255 : : "Incomplete write in forwarding a socket session: " << cc <<
256 : : "/" << (impl_->buf_.getLength() + data_len));
257 : : }
258 : 23 : }
259 : :
260 : 21 : SocketSession::SocketSession(int sock, int family, int type, int protocol,
261 : : const sockaddr* local_end,
262 : : const sockaddr* remote_end,
263 : : const void* data, size_t data_len) :
264 : : sock_(sock), family_(family), type_(type), protocol_(protocol),
265 : : local_end_(local_end), remote_end_(remote_end),
266 : 21 : data_(data), data_len_(data_len)
267 : : {
268 [ + + ]: 21 : if (local_end == NULL || remote_end == NULL) {
269 [ + - ][ + - ]: 4 : isc_throw(BadValue, "sockaddr must be non NULL for SocketSession");
270 : : }
271 [ + + ]: 19 : if (data_len == 0) {
272 [ + - ][ + - ]: 2 : isc_throw(BadValue, "data_len must be non 0 for SocketSession");
273 : : }
274 [ + + ]: 18 : if (data == NULL) {
275 [ + - ][ + - ]: 2 : isc_throw(BadValue, "data must be non NULL for SocketSession");
276 : : }
277 : 17 : }
278 : :
279 : 35 : struct SocketSessionReceiver::ReceiverImpl {
280 : 35 : ReceiverImpl(int fd) : fd_(fd),
281 : 70 : sa_local_(convertSockAddr(&ss_local_)),
282 : 70 : sa_remote_(convertSockAddr(&ss_remote_)),
283 : : header_buf_(DEFAULT_HEADER_BUFLEN),
284 [ + - ]: 35 : data_buf_(INITIAL_BUFSIZE)
285 : : {
286 [ - + ]: 35 : if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &SOCKSESSION_BUFSIZE,
287 : 35 : sizeof(SOCKSESSION_BUFSIZE)) == -1) {
288 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError,
289 : : "Failed to set receive buffer size");
290 : : }
291 : 35 : }
292 : :
293 : : const int fd_;
294 : : struct sockaddr_storage ss_local_; // placeholder for local endpoint
295 : : struct sockaddr* const sa_local_;
296 : : struct sockaddr_storage ss_remote_; // placeholder for remote endpoint
297 : : struct sockaddr* const sa_remote_;
298 : :
299 : : // placeholder for session header and data
300 : : vector<uint8_t> header_buf_;
301 : : vector<uint8_t> data_buf_;
302 : : };
303 : :
304 : 35 : SocketSessionReceiver::SocketSessionReceiver(int fd) :
305 [ + - ]: 35 : impl_(new ReceiverImpl(fd))
306 : : {
307 : 35 : }
308 : :
309 : 35 : SocketSessionReceiver::~SocketSessionReceiver() {
310 [ + - ]: 70 : delete impl_;
311 : 35 : }
312 : :
313 : : namespace {
314 : : // A shortcut to throw common exception on failure of recv(2)
315 : : void
316 : 4 : readFail(int actual_len, int expected_len) {
317 [ - + ]: 4 : if (expected_len < 0) {
318 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError, "Failed to receive data from "
[ # # ]
319 : : "SocketSessionForwarder: " << strerror(errno));
320 : : }
321 [ + - ][ + - ]: 8 : isc_throw(SocketSessionError, "Incomplete data from "
[ + - ][ + - ]
[ + - ]
322 : : "SocketSessionForwarder: " << actual_len << "/" <<
323 : : expected_len);
324 : : }
325 : :
326 : : // A helper container for a (socket) file descriptor used in
327 : : // SocketSessionReceiver::pop that ensures the socket is closed unless it
328 : : // can be safely passed to the caller via release().
329 : : struct ScopedSocket : boost::noncopyable {
330 : 35 : ScopedSocket(int fd) : fd_(fd) {}
331 : : ~ScopedSocket() {
332 [ - + ][ + + ]: 35 : if (fd_ >= 0) {
333 : 18 : close(fd_);
334 : : }
335 : : }
336 : : int release() {
337 : 17 : const int fd = fd_;
338 : 17 : fd_ = -1;
339 : : return (fd);
340 : : }
341 : : int fd_;
342 : : };
343 : : }
344 : :
345 : : SocketSession
346 : 35 : SocketSessionReceiver::pop() {
347 : 35 : ScopedSocket passed_sock(recv_fd(impl_->fd_));
348 [ + + ]: 35 : if (passed_sock.fd_ == FD_SYSTEM_ERROR) {
349 [ + - ][ + - ]: 6 : isc_throw(SocketSessionError, "Receiving a forwarded FD failed: " <<
[ + - ][ + - ]
350 : : strerror(errno));
351 [ - + ]: 32 : } else if (passed_sock.fd_ < 0) {
352 [ # # ][ # # ]: 0 : isc_throw(SocketSessionError, "No FD forwarded");
[ # # ]
353 : : }
354 : :
355 : : uint16_t header_len;
356 : : const int cc_hlen = recv(impl_->fd_, &header_len, sizeof(header_len),
357 : 64 : MSG_WAITALL);
358 [ + + ]: 32 : if (cc_hlen < sizeof(header_len)) {
359 : 2 : readFail(cc_hlen, sizeof(header_len));
360 : : }
361 : 30 : header_len = InputBuffer(&header_len, sizeof(header_len)).readUint16();
362 [ + + ]: 30 : if (header_len > DEFAULT_HEADER_BUFLEN) {
363 [ + - ][ + - ]: 3 : isc_throw(SocketSessionError, "Too large header length: " <<
[ + - ]
364 : : header_len);
365 : : }
366 : 29 : impl_->header_buf_.clear();
367 [ + - ]: 29 : impl_->header_buf_.resize(header_len);
368 : 29 : const int cc_hdr = recv(impl_->fd_, &impl_->header_buf_[0], header_len,
369 : 87 : MSG_WAITALL);
370 [ + + ]: 29 : if (cc_hdr < header_len) {
371 : 1 : readFail(cc_hdr, header_len);
372 : : }
373 : :
374 : 56 : InputBuffer ibuffer(&impl_->header_buf_[0], header_len);
375 : : try {
376 [ + + ]: 28 : const int family = static_cast<int>(ibuffer.readUint32());
377 [ + + ]: 27 : if (family != AF_INET && family != AF_INET6) {
378 [ + - ][ + - ]: 2 : isc_throw(SocketSessionError,
[ + - ][ + - ]
379 : : "Unsupported address family is passed: " << family);
380 : : }
381 [ + - ]: 26 : const int type = static_cast<int>(ibuffer.readUint32());
382 [ + - ]: 26 : const int protocol = static_cast<int>(ibuffer.readUint32());
383 [ + - ]: 26 : const socklen_t local_end_len = ibuffer.readUint32();
384 : : const socklen_t endpoint_minlen = (family == AF_INET) ?
385 [ + + ]: 26 : sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
386 [ + + ]: 26 : if (local_end_len < endpoint_minlen ||
387 : : local_end_len > sizeof(impl_->ss_local_)) {
388 [ + - ][ + - ]: 6 : isc_throw(SocketSessionError, "Invalid local SA length: " <<
[ + - ]
389 : : local_end_len);
390 : : }
391 [ + - ]: 24 : ibuffer.readData(&impl_->ss_local_, local_end_len);
392 [ + - ]: 24 : const socklen_t remote_end_len = ibuffer.readUint32();
393 [ + + ]: 24 : if (remote_end_len < endpoint_minlen ||
394 : : remote_end_len > sizeof(impl_->ss_remote_)) {
395 [ + - ][ + - ]: 6 : isc_throw(SocketSessionError, "Invalid remote SA length: " <<
[ + - ]
396 : : remote_end_len);
397 : : }
398 [ + - ]: 22 : ibuffer.readData(&impl_->ss_remote_, remote_end_len);
399 [ + + ][ + + ]: 22 : if (family != impl_->sa_local_->sa_family ||
400 : : family != impl_->sa_remote_->sa_family) {
401 [ + - ][ + - ]: 4 : isc_throw(SocketSessionError, "SA family inconsistent: " <<
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
402 : : static_cast<int>(impl_->sa_local_->sa_family) << ", " <<
403 : : static_cast<int>(impl_->sa_remote_->sa_family) <<
404 : : " given, must be " << family);
405 : : }
406 [ + - ]: 20 : const size_t data_len = ibuffer.readUint32();
407 [ + + ]: 20 : if (data_len == 0 || data_len > MAX_DATASIZE) {
408 [ + - ][ + - ]: 6 : isc_throw(SocketSessionError,
[ + - ][ + - ]
[ + - ]
409 : : "Invalid socket session data size: " << data_len <<
410 : : ", must be > 0 and <= " << MAX_DATASIZE);
411 : : }
412 : :
413 : 18 : impl_->data_buf_.clear();
414 [ + - ]: 18 : impl_->data_buf_.resize(data_len);
415 : 18 : const int cc_data = recv(impl_->fd_, &impl_->data_buf_[0], data_len,
416 : 36 : MSG_WAITALL);
417 [ + + ]: 18 : if (cc_data < data_len) {
418 : 1 : readFail(cc_data, data_len);
419 : : }
420 : :
421 : : return (SocketSession(passed_sock.release(), family, type, protocol,
422 : : impl_->sa_local_, impl_->sa_remote_,
423 [ + - ]: 34 : &impl_->data_buf_[0], data_len));
424 [ + + ]: 12 : } catch (const InvalidBufferPosition& ex) {
425 : : // We catch the case where the given header is too short and convert
426 : : // the exception to SocketSessionError.
427 [ - + ][ - + ]: 2 : isc_throw(SocketSessionError, "bogus socket session header: " <<
[ - + ][ - + ]
428 : : ex.what());
429 : : }
430 : : }
431 : :
432 : : }
433 : : }
434 : 1030 : }
|