]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/iputils.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
26 #include <sys/socket.h>
28 /** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
30 static void RuntimeError(const boost::format
& fmt
)
32 throw runtime_error(fmt
.str());
36 int SSocket(int family
, int type
, int flags
)
38 int ret
= socket(family
, type
, flags
);
40 RuntimeError(boost::format("creating socket of type %d: %s") % family
% strerror(errno
));
44 int SConnect(int sockfd
, const ComboAddress
& remote
)
46 int ret
= connect(sockfd
, (struct sockaddr
*)&remote
, remote
.getSocklen());
48 int savederrno
= errno
;
49 RuntimeError(boost::format("connecting socket to %s: %s") % remote
.toStringWithPort() % strerror(savederrno
));
54 int SConnectWithTimeout(int sockfd
, const ComboAddress
& remote
, int timeout
)
56 int ret
= connect(sockfd
, (struct sockaddr
*)&remote
, remote
.getSocklen());
58 int savederrno
= errno
;
59 if (savederrno
== EINPROGRESS
) {
60 /* we wait until the connection has been established */
62 bool disconnected
= false;
63 int res
= waitForRWData(sockfd
, false, timeout
, 0, &error
, &disconnected
);
67 socklen_t errlen
= sizeof(savederrno
);
68 if (getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, (void *)&savederrno
, &errlen
) == 0) {
69 RuntimeError(boost::format("connecting to %s failed: %s") % remote
.toStringWithPort() % string(strerror(savederrno
)));
72 RuntimeError(boost::format("connecting to %s failed") % remote
.toStringWithPort());
76 RuntimeError(boost::format("%s closed the connection") % remote
.toStringWithPort());
81 RuntimeError(boost::format("timeout while connecting to %s") % remote
.toStringWithPort());
84 RuntimeError(boost::format("waiting to connect to %s: %s") % remote
.toStringWithPort() % string(strerror(savederrno
)));
88 RuntimeError(boost::format("connecting to %s: %s") % remote
.toStringWithPort() % string(strerror(savederrno
)));
95 int SBind(int sockfd
, const ComboAddress
& local
)
97 int ret
= bind(sockfd
, (struct sockaddr
*)&local
, local
.getSocklen());
99 int savederrno
= errno
;
100 RuntimeError(boost::format("binding socket to %s: %s") % local
.toStringWithPort() % strerror(savederrno
));
105 int SAccept(int sockfd
, ComboAddress
& remote
)
107 socklen_t remlen
= remote
.getSocklen();
109 int ret
= accept(sockfd
, (struct sockaddr
*)&remote
, &remlen
);
111 RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno
));
115 int SListen(int sockfd
, int limit
)
117 int ret
= listen(sockfd
, limit
);
119 RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno
));
123 int SSetsockopt(int sockfd
, int level
, int opname
, int value
)
125 int ret
= setsockopt(sockfd
, level
, opname
, &value
, sizeof(value
));
127 RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level
% opname
% value
% strerror(errno
));
132 bool HarvestTimestamp(struct msghdr
* msgh
, struct timeval
* tv
)
135 struct cmsghdr
*cmsg
;
136 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(msgh
,cmsg
)) {
137 if ((cmsg
->cmsg_level
== SOL_SOCKET
) && (cmsg
->cmsg_type
== SO_TIMESTAMP
|| cmsg
->cmsg_type
== SCM_TIMESTAMP
) &&
138 CMSG_LEN(sizeof(*tv
)) == cmsg
->cmsg_len
) {
139 memcpy(tv
, CMSG_DATA(cmsg
), sizeof(*tv
));
146 bool HarvestDestinationAddress(const struct msghdr
* msgh
, ComboAddress
* destination
)
148 memset(destination
, 0, sizeof(*destination
));
149 const struct cmsghdr
* cmsg
;
150 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(const_cast<struct msghdr
*>(msgh
), const_cast<struct cmsghdr
*>(cmsg
))) {
151 #if defined(IP_PKTINFO)
152 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_PKTINFO
)) {
153 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
154 destination
->sin4
.sin_addr
= i
->ipi_addr
;
155 destination
->sin4
.sin_family
= AF_INET
;
158 #elif defined(IP_RECVDSTADDR)
159 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
160 struct in_addr
*i
= (struct in_addr
*) CMSG_DATA(cmsg
);
161 destination
->sin4
.sin_addr
= *i
;
162 destination
->sin4
.sin_family
= AF_INET
;
167 if ((cmsg
->cmsg_level
== IPPROTO_IPV6
) && (cmsg
->cmsg_type
== IPV6_PKTINFO
)) {
168 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
169 destination
->sin6
.sin6_addr
= i
->ipi6_addr
;
170 destination
->sin4
.sin_family
= AF_INET6
;
177 bool IsAnyAddress(const ComboAddress
& addr
)
179 if(addr
.sin4
.sin_family
== AF_INET
)
180 return addr
.sin4
.sin_addr
.s_addr
== 0;
181 else if(addr
.sin4
.sin_family
== AF_INET6
)
182 return !memcmp(&addr
.sin6
.sin6_addr
, &in6addr_any
, sizeof(addr
.sin6
.sin6_addr
));
187 ssize_t
sendfromto(int sock
, const char* data
, size_t len
, int flags
, const ComboAddress
& from
, const ComboAddress
& to
)
193 /* Set up iov and msgh structures. */
194 memset(&msgh
, 0, sizeof(struct msghdr
));
195 iov
.iov_base
= (void*)data
;
199 msgh
.msg_name
= (struct sockaddr
*)&to
;
200 msgh
.msg_namelen
= to
.getSocklen();
202 if(from
.sin4
.sin_family
) {
203 addCMsgSrcAddr(&msgh
, cbuf
, &from
, 0);
206 msgh
.msg_control
=NULL
;
208 return sendmsg(sock
, &msgh
, flags
);
211 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
212 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
213 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
214 void fillMSGHdr(struct msghdr
* msgh
, struct iovec
* iov
, char* cbuf
, size_t cbufsize
, char* data
, size_t datalen
, ComboAddress
* addr
)
216 iov
->iov_base
= data
;
217 iov
->iov_len
= datalen
;
219 memset(msgh
, 0, sizeof(struct msghdr
));
221 msgh
->msg_control
= cbuf
;
222 msgh
->msg_controllen
= cbufsize
;
223 msgh
->msg_name
= addr
;
224 msgh
->msg_namelen
= addr
->getSocklen();
226 msgh
->msg_iovlen
= 1;
230 // warning: various parts of PowerDNS assume 'truncate' will never throw
231 void ComboAddress::truncate(unsigned int bits
) noexcept
235 if(sin4
.sin_family
==AF_INET
) {
238 start
= (uint8_t*)&sin4
.sin_addr
.s_addr
;
244 start
= (uint8_t*)&sin6
.sin6_addr
.s6_addr
;
248 auto tozero
= len
*8 - bits
; // if set to 22, this will clear 1 byte, as it should
250 memset(start
+ len
- tozero
/8, 0, tozero
/8); // blot out the whole bytes on the right
252 auto bitsleft
=tozero
% 8; // 2 bits left to clear
254 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
255 // so and by '11111100', which is ~((1<<2)-1) = ~3
256 uint8_t* place
= start
+ len
- 1 - tozero
/8;
257 *place
&= (~((1<<bitsleft
)-1));
260 ssize_t
sendMsgWithTimeout(int fd
, const char* buffer
, size_t len
, int timeout
, ComboAddress
& dest
, const ComboAddress
& local
, unsigned int localItf
)
265 bool firstTry
= true;
266 fillMSGHdr(&msgh
, &iov
, cbuf
, sizeof(cbuf
), const_cast<char*>(buffer
), len
, &dest
);
267 addCMsgSrcAddr(&msgh
, cbuf
, &local
, localItf
);
270 ssize_t written
= sendmsg(fd
, &msgh
, 0);
275 if (errno
== EAGAIN
) {
277 int res
= waitForRWData(fd
, false, timeout
, 0);
279 /* there is room available */
283 throw runtime_error("Timeout while waiting to write data");
285 throw runtime_error("Error while waiting for room to write data");
289 throw runtime_error("Timeout while waiting to write data");
293 unixDie("failed in write2WithTimeout");
301 template class NetmaskTree
<bool>;
303 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
)
305 uint16_t size
= htons(bufferLen
);
309 int remainingTime
= totalTimeout
;
315 /* Set up iov and msgh structures. */
316 memset(&msgh
, 0, sizeof(struct msghdr
));
317 msgh
.msg_control
= nullptr;
318 msgh
.msg_controllen
= 0;
320 msgh
.msg_name
= reinterpret_cast<void*>(const_cast<ComboAddress
*>(dest
));
321 msgh
.msg_namelen
= dest
->getSocklen();
324 msgh
.msg_name
= nullptr;
325 msgh
.msg_namelen
= 0;
330 if (localItf
!= 0 && local
) {
331 addCMsgSrcAddr(&msgh
, cbuf
, local
, localItf
);
334 iov
[0].iov_base
= &size
;
335 iov
[0].iov_len
= sizeof(size
);
336 iov
[1].iov_base
= reinterpret_cast<void*>(const_cast<char*>(buffer
));
337 iov
[1].iov_len
= bufferLen
;
341 size_t nbElements
= sizeof(iov
)/sizeof(*iov
);
343 msgh
.msg_iov
= &iov
[pos
];
344 msgh
.msg_iovlen
= nbElements
- pos
;
346 ssize_t res
= sendmsg(sock
, &msgh
, flags
);
348 size_t written
= static_cast<size_t>(res
);
351 if (sent
== (sizeof(size
) + bufferLen
)) {
354 /* partial write, we need to keep only the (parts of) elements
355 that have not been written.
358 if (written
< iov
[pos
].iov_len
) {
359 iov
[pos
].iov_len
-= written
;
360 iov
[pos
].iov_base
= reinterpret_cast<void*>(reinterpret_cast<char*>(iov
[pos
].iov_base
) + written
);
364 written
-= iov
[pos
].iov_len
;
365 iov
[pos
].iov_len
= 0;
369 while (written
> 0 && pos
< nbElements
);
371 else if (res
== -1) {
372 if (errno
== EINTR
) {
375 else if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) {
376 /* EINPROGRESS might happen with non blocking socket,
377 especially with TCP Fast Open */
378 int ret
= waitForRWData(sock
, false, (totalTimeout
== 0 || idleTimeout
<= remainingTime
) ? idleTimeout
: remainingTime
, 0);
380 /* there is room available */
383 throw runtime_error("Timeout while waiting to send data");
385 throw runtime_error("Error while waiting for room to send data");
389 unixDie("failed in sendSizeAndMsgWithTimeout");
393 time_t now
= time(NULL
);
394 int elapsed
= now
- start
;
395 if (elapsed
>= remainingTime
) {
396 throw runtime_error("Timeout while sending data");
399 remainingTime
-= elapsed
;
406 /* requires a non-blocking socket.
407 On Linux, we could use MSG_DONTWAIT on a blocking socket
408 but this is not portable.
410 bool isTCPSocketUsable(int sock
)
414 size_t buf_size
= sizeof(buf
);
417 ssize_t got
= recv(sock
, &buf
, buf_size
, MSG_PEEK
);
420 /* socket is usable, some data is even waiting to be read */
424 /* other end has closed the socket */
430 if (err
== EAGAIN
|| err
== EWOULDBLOCK
) {
431 /* socket is usable, no data waiting */
436 /* something is wrong, could be ECONNRESET,
437 ENOTCONN, EPIPE, but anyway this socket is
443 } while (err
== EINTR
);