]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/iputils.cc
rec: allow exception to proxy protocal usage for specific listen addresses
[thirdparty/pdns.git] / pdns / iputils.cc
CommitLineData
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
a48e03da 25
002c970a 26#include "iputils.hh"
a48e03da 27
2489b946 28#include <fstream>
a48e03da
OM
29#include <sys/socket.h>
30#include <boost/format.hpp>
22cf1fda 31
ee773bbc 32#ifdef HAVE_GETIFADDRS
6ec09d51
RG
33#include <ifaddrs.h>
34#endif
35
002c970a 36/** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
37
6da6832b 38static void RuntimeError(const std::string& error)
002c970a 39{
6da6832b 40 throw runtime_error(error);
002c970a 41}
42
6da6832b 43static void NetworkErr(const std::string& error)
73ba5999 44{
6da6832b 45 throw NetworkError(error);
73ba5999 46}
002c970a 47
48int SSocket(int family, int type, int flags)
49{
50 int ret = socket(family, type, flags);
4e596bb2
RG
51 if (ret < 0) {
52 RuntimeError("creating socket of type " + std::to_string(family) + ": " + stringerror());
53 }
002c970a 54 return ret;
55}
56
57int SConnect(int sockfd, const ComboAddress& remote)
58{
bdf8277d 59 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
4e596bb2 60 if (ret < 0) {
ccb4b5e2 61 int savederrno = errno;
4e596bb2 62 RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
ccb4b5e2 63 }
002c970a 64 return ret;
65}
66
50111728 67int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout)
51959320 68{
bdf8277d 69 int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen());
51959320
RG
70 if(ret < 0) {
71 int savederrno = errno;
72 if (savederrno == EINPROGRESS) {
50111728 73 if (timeout <= timeval{0,0}) {
bdf8277d 74 return savederrno;
399fd947
RG
75 }
76
51959320
RG
77 /* we wait until the connection has been established */
78 bool error = false;
79 bool disconnected = false;
50111728 80 int res = waitForRWData(sockfd, false, timeout.tv_sec, timeout.tv_usec, &error, &disconnected);
51959320
RG
81 if (res == 1) {
82 if (error) {
83 savederrno = 0;
84 socklen_t errlen = sizeof(savederrno);
85 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&savederrno, &errlen) == 0) {
4e596bb2 86 NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
51959320
RG
87 }
88 else {
4e596bb2 89 NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
51959320
RG
90 }
91 }
92 if (disconnected) {
4e596bb2 93 NetworkErr(remote.toStringWithPort() + " closed the connection");
51959320
RG
94 }
95 return 0;
96 }
97 else if (res == 0) {
4e596bb2 98 NetworkErr("timeout while connecting to " + remote.toStringWithPort());
51959320
RG
99 } else if (res < 0) {
100 savederrno = errno;
4e596bb2 101 NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
51959320
RG
102 }
103 }
104 else {
4e596bb2 105 NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
51959320
RG
106 }
107 }
108
bdf8277d 109 return 0;
51959320
RG
110}
111
002c970a 112int SBind(int sockfd, const ComboAddress& local)
113{
114 int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen());
4e596bb2 115 if (ret < 0) {
ccb4b5e2 116 int savederrno = errno;
4e596bb2 117 RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
ccb4b5e2 118 }
002c970a 119 return ret;
120}
121
122int SAccept(int sockfd, ComboAddress& remote)
123{
124 socklen_t remlen = remote.getSocklen();
125
126 int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen);
4e596bb2
RG
127 if (ret < 0) {
128 RuntimeError("accepting new connection on socket: " + stringerror());
129 }
002c970a 130 return ret;
131}
132
133int SListen(int sockfd, int limit)
134{
135 int ret = listen(sockfd, limit);
4e596bb2
RG
136 if (ret < 0) {
137 RuntimeError("setting socket to listen: " + stringerror());
138 }
002c970a 139 return ret;
140}
141
142int SSetsockopt(int sockfd, int level, int opname, int value)
143{
144 int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
4e596bb2
RG
145 if (ret < 0) {
146 RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
147 }
002c970a 148 return ret;
149}
150
3bb98d97 151void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
90f9fbc0 152{
db63b4b6 153 if (family == AF_INET) {
b7076725 154#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
90f9fbc0
RG
155#ifdef IP_PMTUDISC_OMIT
156 /* Linux 3.15+ has IP_PMTUDISC_OMIT, which discards PMTU information to prevent
157 poisoning, but still allows fragmentation if the packet size exceeds the
158 outgoing interface MTU, which is good.
159 */
db63b4b6
O
160 try {
161 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
162 return;
163 }
164 catch(const std::exception& e) {
165 /* failed, let's try IP_PMTUDISC_DONT instead */
166 }
90f9fbc0
RG
167#endif /* IP_PMTUDISC_OMIT */
168
169 /* IP_PMTUDISC_DONT disables Path MTU discovery */
db63b4b6 170 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
b7076725 171#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
db63b4b6
O
172 }
173 else {
174 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
175#ifdef IPV6_PMTUDISC_OMIT
176 /* Linux 3.15+ has IPV6_PMTUDISC_OMIT, which discards PMTU information to prevent
177 poisoning, but still allows fragmentation if the packet size exceeds the
178 outgoing interface MTU, which is good.
179 */
180 try {
181 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
182 return;
183 }
184 catch(const std::exception& e) {
185 /* failed, let's try IP_PMTUDISC_DONT instead */
186 }
187#endif /* IPV6_PMTUDISC_OMIT */
188
189 /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
190 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
191#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
192 }
90f9fbc0 193}
002c970a 194
3198b2c3
RG
195void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
196{
197 if (family == AF_INET) {
198#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
199 /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
200 SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
201#elif defined(IP_DONTFRAG)
202 /* at least this prevents fragmentation */
203 SSetsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, 1);
204#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) */
205 }
206 else {
207#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
208 /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
209 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
210#elif defined(IPV6_DONTFRAG)
211 /* at least this prevents fragmentation */
212 SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, 1);
213#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) */
214 }
215}
db63b4b6 216
665821e1
RG
217bool setReusePort(int sockfd)
218{
219#if defined(SO_REUSEPORT_LB)
220 try {
221 SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
222 return true;
223 }
224 catch (const std::exception& e) {
225 return false;
226 }
227#elif defined(SO_REUSEPORT)
228 try {
229 SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
230 return true;
231 }
232 catch (const std::exception& e) {
233 return false;
234 }
235#endif
236 return false;
237}
238
1e05b07c 239bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
3e3f0358 240{
241#ifdef SO_TIMESTAMP
242 struct cmsghdr *cmsg;
4646277d 243 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
1e05b07c 244 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) &&
3e3f0358 245 CMSG_LEN(sizeof(*tv)) == cmsg->cmsg_len) {
246 memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv));
247 return true;
248 }
249 }
250#endif
251 return false;
252}
2b3eefc3 253bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
3e3f0358 254{
d38e2ba9 255 destination->reset();
4d39d7f3
TIH
256#ifdef __NetBSD__
257 struct cmsghdr* cmsg;
258#else
2b3eefc3 259 const struct cmsghdr* cmsg;
4d39d7f3 260#endif
4646277d 261 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
3e3f0358 262#if defined(IP_PKTINFO)
263 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
264 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
265 destination->sin4.sin_addr = i->ipi_addr;
266 destination->sin4.sin_family = AF_INET;
267 return true;
268 }
269#elif defined(IP_RECVDSTADDR)
270 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
271 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
272 destination->sin4.sin_addr = *i;
1e05b07c 273 destination->sin4.sin_family = AF_INET;
3e3f0358 274 return true;
275 }
276#endif
277
278 if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
279 struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
280 destination->sin6.sin6_addr = i->ipi6_addr;
281 destination->sin4.sin_family = AF_INET6;
282 return true;
283 }
284 }
285 return false;
286}
287
288bool IsAnyAddress(const ComboAddress& addr)
289{
290 if(addr.sin4.sin_family == AF_INET)
291 return addr.sin4.sin_addr.s_addr == 0;
292 else if(addr.sin4.sin_family == AF_INET6)
293 return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
1e05b07c 294
3e3f0358 295 return false;
296}
2ea1d2c0
OM
297int sendOnNBSocket(int fd, const struct msghdr *msgh)
298{
299 int sendErr = 0;
300#ifdef __OpenBSD__
819d97d2 301 // OpenBSD can and does return EAGAIN on non-blocking datagram sockets
d2fd2f8c 302 for (int i = 0; i < 10; i++) { // Arbitrary upper bound
2ea1d2c0
OM
303 if (sendmsg(fd, msgh, 0) != -1) {
304 sendErr = 0;
305 break;
306 }
307 sendErr = errno;
308 if (sendErr != EAGAIN) {
309 break;
310 }
311 }
312#else
313 if (sendmsg(fd, msgh, 0) == -1) {
314 sendErr = errno;
315 }
316#endif
317 return sendErr;
318}
3e3f0358 319
b71b60ee 320// be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
321// be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
579cae19 322// be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
7bec330a 323void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
b71b60ee 324{
325 iov->iov_base = data;
326 iov->iov_len = datalen;
327
328 memset(msgh, 0, sizeof(struct msghdr));
1e05b07c 329
b71b60ee 330 msgh->msg_control = cbuf;
331 msgh->msg_controllen = cbufsize;
332 msgh->msg_name = addr;
333 msgh->msg_namelen = addr->getSocklen();
334 msgh->msg_iov = iov;
335 msgh->msg_iovlen = 1;
336 msgh->msg_flags = 0;
337}
22779196 338
5b6099b2 339// warning: various parts of PowerDNS assume 'truncate' will never throw
340void ComboAddress::truncate(unsigned int bits) noexcept
22779196 341{
342 uint8_t* start;
343 int len=4;
344 if(sin4.sin_family==AF_INET) {
ecd43f08 345 if(bits >= 32)
22779196 346 return;
347 start = (uint8_t*)&sin4.sin_addr.s_addr;
348 len=4;
349 }
350 else {
ecd43f08 351 if(bits >= 128)
22779196 352 return;
353 start = (uint8_t*)&sin6.sin6_addr.s6_addr;
354 len=16;
355 }
356
22779196 357 auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should
358
359 memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right
1e05b07c 360
22779196 361 auto bitsleft=tozero % 8; // 2 bits left to clear
362
363 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
364 // so and by '11111100', which is ~((1<<2)-1) = ~3
1e05b07c 365 uint8_t* place = start + len - 1 - tozero/8;
22779196 366 *place &= (~((1<<bitsleft)-1));
367}
f9f9592e 368
6714b6ac 369size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
fbe2a2e0
RG
370{
371 struct msghdr msgh;
372 struct iovec iov;
7bec330a 373 cmsgbuf_aligned cbuf;
d0ae6360
RG
374
375 /* Set up iov and msgh structures. */
376 memset(&msgh, 0, sizeof(struct msghdr));
377 msgh.msg_control = nullptr;
378 msgh.msg_controllen = 0;
379 if (dest) {
380 msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
381 msgh.msg_namelen = dest->getSocklen();
382 }
383 else {
384 msgh.msg_name = nullptr;
385 msgh.msg_namelen = 0;
386 }
387
388 msgh.msg_flags = 0;
389
390 if (localItf != 0 && local) {
7bec330a 391 addCMsgSrcAddr(&msgh, &cbuf, local, localItf);
d0ae6360
RG
392 }
393
d0ae6360
RG
394 iov.iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
395 iov.iov_len = len;
396 msgh.msg_iov = &iov;
397 msgh.msg_iovlen = 1;
398 msgh.msg_flags = 0;
399
400 size_t sent = 0;
93497bef 401#ifdef MSG_FASTOPEN
fbe2a2e0 402 bool firstTry = true;
93497bef 403#endif
fbe2a2e0
RG
404
405 do {
fbe2a2e0 406
d0ae6360
RG
407#ifdef MSG_FASTOPEN
408 if (flags & MSG_FASTOPEN && firstTry == false) {
409 flags &= ~MSG_FASTOPEN;
410 }
411#endif /* MSG_FASTOPEN */
412
413 ssize_t res = sendmsg(fd, &msgh, flags);
fbe2a2e0 414
d0ae6360
RG
415 if (res > 0) {
416 size_t written = static_cast<size_t>(res);
417 sent += written;
418
419 if (sent == len) {
420 return sent;
421 }
422
423 /* partial write */
93497bef 424 #ifdef MSG_FASTOPEN
6714b6ac 425 firstTry = false;
93497bef 426 #endif
d0ae6360
RG
427 iov.iov_len -= written;
428 iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
d0ae6360 429 }
6714b6ac
RG
430 else if (res == 0) {
431 return res;
432 }
d0ae6360 433 else if (res == -1) {
a2a81d42
OM
434 int err = errno;
435 if (err == EINTR) {
d0ae6360
RG
436 continue;
437 }
a2a81d42 438 else if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
d0ae6360
RG
439 /* EINPROGRESS might happen with non blocking socket,
440 especially with TCP Fast Open */
6714b6ac 441 return sent;
fbe2a2e0
RG
442 }
443 else {
d0ae6360 444 unixDie("failed in sendMsgWithTimeout");
fbe2a2e0
RG
445 }
446 }
fbe2a2e0 447 }
6714b6ac 448 while (true);
fbe2a2e0
RG
449
450 return 0;
451}
452
77e08b20 453template class NetmaskTree<bool, Netmask>;
fbe2a2e0 454
840ed663
RG
455/* requires a non-blocking socket.
456 On Linux, we could use MSG_DONTWAIT on a blocking socket
457 but this is not portable.
458*/
459bool isTCPSocketUsable(int sock)
460{
461 int err = 0;
462 char buf = '\0';
463 size_t buf_size = sizeof(buf);
464
465 do {
466 ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
467
468 if (got > 0) {
469 /* socket is usable, some data is even waiting to be read */
470 return true;
471 }
472 else if (got == 0) {
473 /* other end has closed the socket */
474 return false;
475 }
476 else {
88479ac5 477 err = errno;
840ed663
RG
478
479 if (err == EAGAIN || err == EWOULDBLOCK) {
480 /* socket is usable, no data waiting */
481 return true;
482 }
483 else {
484 if (err != EINTR) {
485 /* something is wrong, could be ECONNRESET,
486 ENOTCONN, EPIPE, but anyway this socket is
487 not usable. */
488 return false;
489 }
490 }
491 }
492 } while (err == EINTR);
493
494 return false;
495}
f06d11c8 496/* mission in life: parse four cases
7f3906f6
OM
497 1) [2002::1]:53
498 2) 1.2.3.4
499 3) 1.2.3.4:5300
500 4) 2001::1 no port allowed
7234d9fc
OM
501*/
502
503ComboAddress parseIPAndPort(const std::string& input, uint16_t port)
504{
7f3906f6 505 if (input[0] == '[') { // case 1
f06d11c8 506 auto both = splitField(input.substr(1), ']');
a0383aad 507 return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
7234d9fc
OM
508 }
509
f06d11c8
OM
510 string::size_type count = 0;
511 for (char c : input) {
512 if (c == ':') {
513 count++;
514 }
515 if (count > 1) {
516 break;
517 }
518 }
519 switch (count) {
7f3906f6 520 case 0: // case 2
f06d11c8 521 return ComboAddress(input, port);
7f3906f6 522 case 1: { // case 3
7234d9fc 523 string::size_type cpos = input.rfind(':');
f06d11c8 524 pair<std::string,std::string> both;
7234d9fc
OM
525 both.first = input.substr(0, cpos);
526 both.second = input.substr(cpos + 1);
527
a0383aad 528 auto newport = pdns::checked_stoi<uint16_t>(both.second);
7234d9fc
OM
529 return ComboAddress(both.first, newport);
530 }
7f3906f6 531 default: // case 4
f06d11c8 532 return ComboAddress(input, port);
7234d9fc 533 }
7234d9fc 534}
f402f388
RG
535
536void setSocketBuffer(int fd, int optname, uint32_t size)
537{
538 uint32_t psize = 0;
539 socklen_t len = sizeof(psize);
540
fe65dec0
OM
541 if (getsockopt(fd, SOL_SOCKET, optname, &psize, &len) != 0) {
542 throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
f402f388 543 }
fe65dec0
OM
544 if (psize >= size) {
545 return;
546 }
547 if (setsockopt(fd, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
f402f388
RG
548 throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
549 }
550}
551
552void setSocketReceiveBuffer(int fd, uint32_t size)
553{
554 setSocketBuffer(fd, SO_RCVBUF, size);
555}
556
557void setSocketSendBuffer(int fd, uint32_t size)
558{
559 setSocketBuffer(fd, SO_SNDBUF, size);
560}
6ec09d51 561
4bf9c998 562#ifdef __linux__
37088b76 563static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
2489b946
CHB
564{
565 std::ifstream ifs(readMaxFromFile);
566 if (ifs) {
567 std::string line;
568 if (getline(ifs, line)) {
569 auto max = pdns::checked_stoi<uint32_t>(line);
37088b76 570 setSocketBuffer(socket, optname, max);
2489b946
CHB
571 return max;
572 }
573 }
574 return 0;
575}
4bf9c998 576#endif
2489b946 577
4bf9c998 578uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
2489b946
CHB
579{
580#ifdef __linux__
37088b76 581 return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
2489b946
CHB
582#else
583 return 0;
584#endif
585}
586
4bf9c998 587uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
2489b946
CHB
588{
589#ifdef __linux__
37088b76 590 return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
2489b946
CHB
591#else
592 return 0;
593#endif
594}
595
6ec09d51
RG
596std::set<std::string> getListOfNetworkInterfaces()
597{
598 std::set<std::string> result;
ee773bbc 599#ifdef HAVE_GETIFADDRS
6ec09d51
RG
600 struct ifaddrs *ifaddr;
601 if (getifaddrs(&ifaddr) == -1) {
602 return result;
603 }
604
605 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
606 if (ifa->ifa_name == nullptr) {
607 continue;
608 }
609 result.insert(ifa->ifa_name);
610 }
611
612 freeifaddrs(ifaddr);
613#endif
614 return result;
615}
616
ee773bbc 617#ifdef HAVE_GETIFADDRS
6ec09d51
RG
618std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
619{
620 std::vector<ComboAddress> result;
ee773bbc 621 struct ifaddrs *ifaddr = nullptr;
6ec09d51
RG
622 if (getifaddrs(&ifaddr) == -1) {
623 return result;
624 }
625
626 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
627 if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
628 continue;
629 }
630 if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
631 continue;
632 }
633 ComboAddress addr;
634 try {
635 addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
636 }
637 catch (...) {
638 continue;
639 }
640
641 result.push_back(addr);
642 }
643
644 freeifaddrs(ifaddr);
6ec09d51
RG
645 return result;
646}
ee773bbc
FM
647#else
648std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& /* itf */)
649{
650 std::vector<ComboAddress> result;
651 return result;
652}
653#endif // HAVE_GETIFADDRS
6f0bf6e8 654
ee773bbc 655#ifdef HAVE_GETIFADDRS
a9be9813 656static uint8_t convertNetmaskToBits(const uint8_t* mask, socklen_t len)
6f0bf6e8 657{
705f9468 658 if (mask == nullptr || len > 16) {
dbe1fe7b
RG
659 throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
660 }
661
6f0bf6e8
RG
662 uint8_t result = 0;
663 // for all bytes in the address (4 for IPv4, 16 for IPv6)
664 for (size_t idx = 0; idx < len; idx++) {
a9be9813 665 uint8_t byte = *(mask + idx);
6f0bf6e8
RG
666 // count the number of bits set
667 while (byte > 0) {
668 result += (byte & 1);
669 byte >>= 1;
670 }
671 }
672 return result;
673}
674#endif /* HAVE_GETIFADDRS */
675
ee773bbc 676#ifdef HAVE_GETIFADDRS
6f0bf6e8
RG
677std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
678{
679 std::vector<Netmask> result;
ee773bbc 680 struct ifaddrs *ifaddr = nullptr;
6f0bf6e8
RG
681 if (getifaddrs(&ifaddr) == -1) {
682 return result;
683 }
684
685 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
686 if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
687 continue;
688 }
689 if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
690 continue;
691 }
692 ComboAddress addr;
693 try {
694 addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
695 }
696 catch (...) {
697 continue;
698 }
699
a9be9813
RG
700 if (ifa->ifa_addr->sa_family == AF_INET) {
701 auto netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
705f9468 702 uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
a9be9813
RG
703 result.emplace_back(addr, maskBits);
704 }
705 else if (ifa->ifa_addr->sa_family == AF_INET6) {
706 auto netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
705f9468 707 uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
a9be9813
RG
708 result.emplace_back(addr, maskBits);
709 }
6f0bf6e8
RG
710 }
711
712 freeifaddrs(ifaddr);
6f0bf6e8
RG
713 return result;
714}
ee773bbc
FM
715#else
716std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& /* itf */)
717{
718 std::vector<Netmask> result;
719 return result;
720}
721#endif // HAVE_GETIFADDRS