]>
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());
35 static void NetworkErr(const boost::format
& fmt
)
37 throw NetworkError(fmt
.str());
40 int SSocket(int family
, int type
, int flags
)
42 int ret
= socket(family
, type
, flags
);
44 RuntimeError(boost::format("creating socket of type %d: %s") % family
% strerror(errno
));
48 int SConnect(int sockfd
, const ComboAddress
& remote
)
50 int ret
= connect(sockfd
, (struct sockaddr
*)&remote
, remote
.getSocklen());
52 int savederrno
= errno
;
53 RuntimeError(boost::format("connecting socket to %s: %s") % remote
.toStringWithPort() % strerror(savederrno
));
58 int SConnectWithTimeout(int sockfd
, const ComboAddress
& remote
, int timeout
)
60 int ret
= connect(sockfd
, (struct sockaddr
*)&remote
, remote
.getSocklen());
62 int savederrno
= errno
;
63 if (savederrno
== EINPROGRESS
) {
68 /* we wait until the connection has been established */
70 bool disconnected
= false;
71 int res
= waitForRWData(sockfd
, false, timeout
, 0, &error
, &disconnected
);
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
)));
80 NetworkErr(boost::format("connecting to %s failed") % remote
.toStringWithPort());
84 NetworkErr(boost::format("%s closed the connection") % remote
.toStringWithPort());
89 NetworkErr(boost::format("timeout while connecting to %s") % remote
.toStringWithPort());
92 NetworkErr(boost::format("waiting to connect to %s: %s") % remote
.toStringWithPort() % string(strerror(savederrno
)));
96 NetworkErr(boost::format("connecting to %s: %s") % remote
.toStringWithPort() % string(strerror(savederrno
)));
103 int SBind(int sockfd
, const ComboAddress
& local
)
105 int ret
= bind(sockfd
, (struct sockaddr
*)&local
, local
.getSocklen());
107 int savederrno
= errno
;
108 RuntimeError(boost::format("binding socket to %s: %s") % local
.toStringWithPort() % strerror(savederrno
));
113 int SAccept(int sockfd
, ComboAddress
& remote
)
115 socklen_t remlen
= remote
.getSocklen();
117 int ret
= accept(sockfd
, (struct sockaddr
*)&remote
, &remlen
);
119 RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno
));
123 int SListen(int sockfd
, int limit
)
125 int ret
= listen(sockfd
, limit
);
127 RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno
));
131 int SSetsockopt(int sockfd
, int level
, int opname
, int value
)
133 int ret
= setsockopt(sockfd
, level
, opname
, &value
, sizeof(value
));
135 RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level
% opname
% value
% strerror(errno
));
140 bool HarvestTimestamp(struct msghdr
* msgh
, struct timeval
* tv
)
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
));
154 bool HarvestDestinationAddress(const struct msghdr
* msgh
, ComboAddress
* destination
)
156 destination
->reset();
158 struct cmsghdr
* cmsg
;
160 const struct cmsghdr
* cmsg
;
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
;
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
;
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
;
189 bool IsAnyAddress(const ComboAddress
& addr
)
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
));
199 ssize_t
sendfromto(int sock
, const char* data
, size_t len
, int flags
, const ComboAddress
& from
, const ComboAddress
& to
)
205 /* Set up iov and msgh structures. */
206 memset(&msgh
, 0, sizeof(struct msghdr
));
207 iov
.iov_base
= (void*)data
;
211 msgh
.msg_name
= (struct sockaddr
*)&to
;
212 msgh
.msg_namelen
= to
.getSocklen();
214 if(from
.sin4
.sin_family
) {
215 addCMsgSrcAddr(&msgh
, cbuf
, &from
, 0);
218 msgh
.msg_control
=NULL
;
220 return sendmsg(sock
, &msgh
, flags
);
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
)
228 iov
->iov_base
= data
;
229 iov
->iov_len
= datalen
;
231 memset(msgh
, 0, sizeof(struct msghdr
));
233 msgh
->msg_control
= cbuf
;
234 msgh
->msg_controllen
= cbufsize
;
235 msgh
->msg_name
= addr
;
236 msgh
->msg_namelen
= addr
->getSocklen();
238 msgh
->msg_iovlen
= 1;
242 // warning: various parts of PowerDNS assume 'truncate' will never throw
243 void ComboAddress::truncate(unsigned int bits
) noexcept
247 if(sin4
.sin_family
==AF_INET
) {
250 start
= (uint8_t*)&sin4
.sin_addr
.s_addr
;
256 start
= (uint8_t*)&sin6
.sin6_addr
.s6_addr
;
260 auto tozero
= len
*8 - bits
; // if set to 22, this will clear 1 byte, as it should
262 memset(start
+ len
- tozero
/8, 0, tozero
/8); // blot out the whole bytes on the right
264 auto bitsleft
=tozero
% 8; // 2 bits left to clear
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));
272 ssize_t
sendMsgWithTimeout(int fd
, const char* buffer
, size_t len
, int timeout
, ComboAddress
& dest
, const ComboAddress
& local
, unsigned int localItf
)
277 bool firstTry
= true;
278 fillMSGHdr(&msgh
, &iov
, cbuf
, sizeof(cbuf
), const_cast<char*>(buffer
), len
, &dest
);
279 addCMsgSrcAddr(&msgh
, cbuf
, &local
, localItf
);
282 ssize_t written
= sendmsg(fd
, &msgh
, 0);
287 if (errno
== EAGAIN
) {
289 int res
= waitForRWData(fd
, false, timeout
, 0);
291 /* there is room available */
295 throw runtime_error("Timeout while waiting to write data");
297 throw runtime_error("Error while waiting for room to write data");
301 throw runtime_error("Timeout while waiting to write data");
305 unixDie("failed in write2WithTimeout");
313 template class NetmaskTree
<bool>;
315 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
)
317 uint16_t size
= htons(bufferLen
);
321 int remainingTime
= totalTimeout
;
327 /* Set up iov and msgh structures. */
328 memset(&msgh
, 0, sizeof(struct msghdr
));
329 msgh
.msg_control
= nullptr;
330 msgh
.msg_controllen
= 0;
332 msgh
.msg_name
= reinterpret_cast<void*>(const_cast<ComboAddress
*>(dest
));
333 msgh
.msg_namelen
= dest
->getSocklen();
336 msgh
.msg_name
= nullptr;
337 msgh
.msg_namelen
= 0;
342 if (localItf
!= 0 && local
) {
343 addCMsgSrcAddr(&msgh
, cbuf
, local
, localItf
);
346 iov
[0].iov_base
= &size
;
347 iov
[0].iov_len
= sizeof(size
);
348 iov
[1].iov_base
= reinterpret_cast<void*>(const_cast<char*>(buffer
));
349 iov
[1].iov_len
= bufferLen
;
353 size_t nbElements
= sizeof(iov
)/sizeof(*iov
);
355 msgh
.msg_iov
= &iov
[pos
];
356 msgh
.msg_iovlen
= nbElements
- pos
;
358 ssize_t res
= sendmsg(sock
, &msgh
, flags
);
360 size_t written
= static_cast<size_t>(res
);
363 if (sent
== (sizeof(size
) + bufferLen
)) {
366 /* partial write, we need to keep only the (parts of) elements
367 that have not been written.
370 if (written
< iov
[pos
].iov_len
) {
371 iov
[pos
].iov_len
-= written
;
372 iov
[pos
].iov_base
= reinterpret_cast<void*>(reinterpret_cast<char*>(iov
[pos
].iov_base
) + written
);
376 written
-= iov
[pos
].iov_len
;
377 iov
[pos
].iov_len
= 0;
381 while (written
> 0 && pos
< nbElements
);
383 else if (res
== -1) {
384 if (errno
== EINTR
) {
387 else if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) {
388 /* EINPROGRESS might happen with non blocking socket,
389 especially with TCP Fast Open */
390 int ret
= waitForRWData(sock
, false, (totalTimeout
== 0 || idleTimeout
<= remainingTime
) ? idleTimeout
: remainingTime
, 0);
392 /* there is room available */
395 throw runtime_error("Timeout while waiting to send data");
397 throw runtime_error("Error while waiting for room to send data");
401 unixDie("failed in sendSizeAndMsgWithTimeout");
405 time_t now
= time(NULL
);
406 int elapsed
= now
- start
;
407 if (elapsed
>= remainingTime
) {
408 throw runtime_error("Timeout while sending data");
411 remainingTime
-= elapsed
;
418 /* requires a non-blocking socket.
419 On Linux, we could use MSG_DONTWAIT on a blocking socket
420 but this is not portable.
422 bool isTCPSocketUsable(int sock
)
426 size_t buf_size
= sizeof(buf
);
429 ssize_t got
= recv(sock
, &buf
, buf_size
, MSG_PEEK
);
432 /* socket is usable, some data is even waiting to be read */
436 /* other end has closed the socket */
442 if (err
== EAGAIN
|| err
== EWOULDBLOCK
) {
443 /* socket is usable, no data waiting */
448 /* something is wrong, could be ECONNRESET,
449 ENOTCONN, EPIPE, but anyway this socket is
455 } while (err
== EINTR
);