]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/iputils.cc
84425e66cb08514ecbacfdb92e716f266b77d91f
[thirdparty/pdns.git] / pdns / iputils.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "iputils.hh"
26 #include <sys/socket.h>
27
28 /** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
29
30 static void RuntimeError(const boost::format& fmt)
31 {
32 throw runtime_error(fmt.str());
33 }
34
35 static void NetworkErr(const boost::format& fmt)
36 {
37 throw NetworkError(fmt.str());
38 }
39
40 int SSocket(int family, int type, int flags)
41 {
42 int ret = socket(family, type, flags);
43 if(ret < 0)
44 RuntimeError(boost::format("creating socket of type %d: %s") % family % strerror(errno));
45 return ret;
46 }
47
48 int SConnect(int sockfd, const ComboAddress& remote)
49 {
50 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
51 if(ret < 0) {
52 int savederrno = errno;
53 RuntimeError(boost::format("connecting socket to %s: %s") % remote.toStringWithPort() % strerror(savederrno));
54 }
55 return ret;
56 }
57
58 int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout)
59 {
60 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
61 if(ret < 0) {
62 int savederrno = errno;
63 if (savederrno == EINPROGRESS) {
64 if (timeout <= 0) {
65 return savederrno;
66 }
67
68 /* we wait until the connection has been established */
69 bool error = false;
70 bool disconnected = false;
71 int res = waitForRWData(sockfd, false, timeout, 0, &error, &disconnected);
72 if (res == 1) {
73 if (error) {
74 savederrno = 0;
75 socklen_t errlen = sizeof(savederrno);
76 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&savederrno, &errlen) == 0) {
77 NetworkErr(boost::format("connecting to %s failed: %s") % remote.toStringWithPort() % string(strerror(savederrno)));
78 }
79 else {
80 NetworkErr(boost::format("connecting to %s failed") % remote.toStringWithPort());
81 }
82 }
83 if (disconnected) {
84 NetworkErr(boost::format("%s closed the connection") % remote.toStringWithPort());
85 }
86 return 0;
87 }
88 else if (res == 0) {
89 NetworkErr(boost::format("timeout while connecting to %s") % remote.toStringWithPort());
90 } else if (res < 0) {
91 savederrno = errno;
92 NetworkErr(boost::format("waiting to connect to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno)));
93 }
94 }
95 else {
96 NetworkErr(boost::format("connecting to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno)));
97 }
98 }
99
100 return 0;
101 }
102
103 int SBind(int sockfd, const ComboAddress& local)
104 {
105 int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen());
106 if(ret < 0) {
107 int savederrno = errno;
108 RuntimeError(boost::format("binding socket to %s: %s") % local.toStringWithPort() % strerror(savederrno));
109 }
110 return ret;
111 }
112
113 int SAccept(int sockfd, ComboAddress& remote)
114 {
115 socklen_t remlen = remote.getSocklen();
116
117 int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen);
118 if(ret < 0)
119 RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno));
120 return ret;
121 }
122
123 int SListen(int sockfd, int limit)
124 {
125 int ret = listen(sockfd, limit);
126 if(ret < 0)
127 RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno));
128 return ret;
129 }
130
131 int SSetsockopt(int sockfd, int level, int opname, int value)
132 {
133 int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
134 if(ret < 0)
135 RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level % opname % value % strerror(errno));
136 return ret;
137 }
138
139
140 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
141 {
142 #ifdef SO_TIMESTAMP
143 struct cmsghdr *cmsg;
144 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
145 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) &&
146 CMSG_LEN(sizeof(*tv)) == cmsg->cmsg_len) {
147 memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv));
148 return true;
149 }
150 }
151 #endif
152 return false;
153 }
154 bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
155 {
156 destination->reset();
157 #ifdef __NetBSD__
158 struct cmsghdr* cmsg;
159 #else
160 const struct cmsghdr* cmsg;
161 #endif
162 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
163 #if defined(IP_PKTINFO)
164 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
165 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
166 destination->sin4.sin_addr = i->ipi_addr;
167 destination->sin4.sin_family = AF_INET;
168 return true;
169 }
170 #elif defined(IP_RECVDSTADDR)
171 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
172 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
173 destination->sin4.sin_addr = *i;
174 destination->sin4.sin_family = AF_INET;
175 return true;
176 }
177 #endif
178
179 if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
180 struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
181 destination->sin6.sin6_addr = i->ipi6_addr;
182 destination->sin4.sin_family = AF_INET6;
183 return true;
184 }
185 }
186 return false;
187 }
188
189 bool IsAnyAddress(const ComboAddress& addr)
190 {
191 if(addr.sin4.sin_family == AF_INET)
192 return addr.sin4.sin_addr.s_addr == 0;
193 else if(addr.sin4.sin_family == AF_INET6)
194 return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
195
196 return false;
197 }
198
199 ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to)
200 {
201 struct msghdr msgh;
202 struct iovec iov;
203 char cbuf[256];
204
205 /* Set up iov and msgh structures. */
206 memset(&msgh, 0, sizeof(struct msghdr));
207 iov.iov_base = (void*)data;
208 iov.iov_len = len;
209 msgh.msg_iov = &iov;
210 msgh.msg_iovlen = 1;
211 msgh.msg_name = (struct sockaddr*)&to;
212 msgh.msg_namelen = to.getSocklen();
213
214 if(from.sin4.sin_family) {
215 addCMsgSrcAddr(&msgh, cbuf, &from, 0);
216 }
217 else {
218 msgh.msg_control=NULL;
219 }
220 return sendmsg(sock, &msgh, flags);
221 }
222
223 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
224 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
225 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
226 void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
227 {
228 iov->iov_base = data;
229 iov->iov_len = datalen;
230
231 memset(msgh, 0, sizeof(struct msghdr));
232
233 msgh->msg_control = cbuf;
234 msgh->msg_controllen = cbufsize;
235 msgh->msg_name = addr;
236 msgh->msg_namelen = addr->getSocklen();
237 msgh->msg_iov = iov;
238 msgh->msg_iovlen = 1;
239 msgh->msg_flags = 0;
240 }
241
242 // warning: various parts of PowerDNS assume 'truncate' will never throw
243 void ComboAddress::truncate(unsigned int bits) noexcept
244 {
245 uint8_t* start;
246 int len=4;
247 if(sin4.sin_family==AF_INET) {
248 if(bits >= 32)
249 return;
250 start = (uint8_t*)&sin4.sin_addr.s_addr;
251 len=4;
252 }
253 else {
254 if(bits >= 128)
255 return;
256 start = (uint8_t*)&sin6.sin6_addr.s6_addr;
257 len=16;
258 }
259
260 auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should
261
262 memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right
263
264 auto bitsleft=tozero % 8; // 2 bits left to clear
265
266 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
267 // so and by '11111100', which is ~((1<<2)-1) = ~3
268 uint8_t* place = start + len - 1 - tozero/8;
269 *place &= (~((1<<bitsleft)-1));
270 }
271
272 size_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags)
273 {
274 int remainingTime = totalTimeout;
275 time_t start = 0;
276 if (totalTimeout) {
277 start = time(nullptr);
278 }
279
280 struct msghdr msgh;
281 struct iovec iov;
282 char cbuf[256];
283
284 /* Set up iov and msgh structures. */
285 memset(&msgh, 0, sizeof(struct msghdr));
286 msgh.msg_control = nullptr;
287 msgh.msg_controllen = 0;
288 if (dest) {
289 msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
290 msgh.msg_namelen = dest->getSocklen();
291 }
292 else {
293 msgh.msg_name = nullptr;
294 msgh.msg_namelen = 0;
295 }
296
297 msgh.msg_flags = 0;
298
299 if (localItf != 0 && local) {
300 addCMsgSrcAddr(&msgh, cbuf, local, localItf);
301 }
302
303 iov.iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
304 iov.iov_len = len;
305 msgh.msg_iov = &iov;
306 msgh.msg_iovlen = 1;
307 msgh.msg_flags = 0;
308
309 size_t sent = 0;
310 bool firstTry = true;
311
312 do {
313
314 #ifdef MSG_FASTOPEN
315 if (flags & MSG_FASTOPEN && firstTry == false) {
316 flags &= ~MSG_FASTOPEN;
317 }
318 #endif /* MSG_FASTOPEN */
319
320 ssize_t res = sendmsg(fd, &msgh, flags);
321
322 if (res > 0) {
323 size_t written = static_cast<size_t>(res);
324 sent += written;
325
326 if (sent == len) {
327 return sent;
328 }
329
330 /* partial write */
331 iov.iov_len -= written;
332 iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
333 written = 0;
334 }
335 else if (res == -1) {
336 if (errno == EINTR) {
337 continue;
338 }
339 else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == ENOTCONN) {
340 /* EINPROGRESS might happen with non blocking socket,
341 especially with TCP Fast Open */
342 if (totalTimeout <= 0 && idleTimeout <= 0) {
343 return sent;
344 }
345
346 if (firstTry) {
347 int res = waitForRWData(fd, false, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime, 0);
348 if (res > 0) {
349 /* there is room available */
350 firstTry = false;
351 }
352 else if (res == 0) {
353 throw runtime_error("Timeout while waiting to write data");
354 } else {
355 throw runtime_error("Error while waiting for room to write data");
356 }
357 }
358 else {
359 throw runtime_error("Timeout while waiting to write data");
360 }
361 }
362 else {
363 unixDie("failed in sendMsgWithTimeout");
364 }
365 }
366 if (totalTimeout) {
367 time_t now = time(nullptr);
368 int elapsed = now - start;
369 if (elapsed >= remainingTime) {
370 throw runtime_error("Timeout while sending data");
371 }
372 start = now;
373 remainingTime -= elapsed;
374 }
375 }
376 while (firstTry);
377
378 return 0;
379 }
380
381 template class NetmaskTree<bool>;
382
383 bool sendSizeAndMsgWithTimeout(int sock, uint16_t bufferLen, const char* buffer, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags)
384 {
385 uint16_t size = htons(bufferLen);
386 char cbuf[256];
387 struct msghdr msgh;
388 struct iovec iov[2];
389 int remainingTime = totalTimeout;
390 time_t start = 0;
391 if (totalTimeout) {
392 start = time(NULL);
393 }
394
395 /* Set up iov and msgh structures. */
396 memset(&msgh, 0, sizeof(struct msghdr));
397 msgh.msg_control = nullptr;
398 msgh.msg_controllen = 0;
399 if (dest) {
400 msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
401 msgh.msg_namelen = dest->getSocklen();
402 }
403 else {
404 msgh.msg_name = nullptr;
405 msgh.msg_namelen = 0;
406 }
407
408 msgh.msg_flags = 0;
409
410 if (localItf != 0 && local) {
411 addCMsgSrcAddr(&msgh, cbuf, local, localItf);
412 }
413
414 iov[0].iov_base = &size;
415 iov[0].iov_len = sizeof(size);
416 iov[1].iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
417 iov[1].iov_len = bufferLen;
418
419 size_t pos = 0;
420 size_t sent = 0;
421 size_t nbElements = sizeof(iov)/sizeof(*iov);
422 while (true) {
423 msgh.msg_iov = &iov[pos];
424 msgh.msg_iovlen = nbElements - pos;
425
426 ssize_t res = sendmsg(sock, &msgh, flags);
427 if (res > 0) {
428 size_t written = static_cast<size_t>(res);
429 sent += written;
430
431 if (sent == (sizeof(size) + bufferLen)) {
432 return true;
433 }
434 /* partial write, we need to keep only the (parts of) elements
435 that have not been written.
436 */
437 do {
438 if (written < iov[pos].iov_len) {
439 iov[pos].iov_len -= written;
440 iov[pos].iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov[pos].iov_base) + written);
441 written = 0;
442 }
443 else {
444 written -= iov[pos].iov_len;
445 iov[pos].iov_len = 0;
446 pos++;
447 }
448 }
449 while (written > 0 && pos < nbElements);
450 }
451 else if (res == -1) {
452 if (errno == EINTR) {
453 continue;
454 }
455 else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
456 /* EINPROGRESS might happen with non blocking socket,
457 especially with TCP Fast Open */
458 int ret = waitForRWData(sock, false, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime, 0);
459 if (ret > 0) {
460 /* there is room available */
461 }
462 else if (ret == 0) {
463 throw runtime_error("Timeout while waiting to send data");
464 } else {
465 throw runtime_error("Error while waiting for room to send data");
466 }
467 }
468 else {
469 unixDie("failed in sendSizeAndMsgWithTimeout");
470 }
471 }
472 if (totalTimeout) {
473 time_t now = time(NULL);
474 int elapsed = now - start;
475 if (elapsed >= remainingTime) {
476 throw runtime_error("Timeout while sending data");
477 }
478 start = now;
479 remainingTime -= elapsed;
480 }
481 }
482
483 return false;
484 }
485
486 /* requires a non-blocking socket.
487 On Linux, we could use MSG_DONTWAIT on a blocking socket
488 but this is not portable.
489 */
490 bool isTCPSocketUsable(int sock)
491 {
492 int err = 0;
493 char buf = '\0';
494 size_t buf_size = sizeof(buf);
495
496 do {
497 ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
498
499 if (got > 0) {
500 /* socket is usable, some data is even waiting to be read */
501 return true;
502 }
503 else if (got == 0) {
504 /* other end has closed the socket */
505 return false;
506 }
507 else {
508 err = errno;
509
510 if (err == EAGAIN || err == EWOULDBLOCK) {
511 /* socket is usable, no data waiting */
512 return true;
513 }
514 else {
515 if (err != EINTR) {
516 /* something is wrong, could be ECONNRESET,
517 ENOTCONN, EPIPE, but anyway this socket is
518 not usable. */
519 return false;
520 }
521 }
522 }
523 } while (err == EINTR);
524
525 return false;
526 }