]>
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
));
150 struct cmsghdr
* cmsg
;
152 const struct cmsghdr
* cmsg
;
154 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(const_cast<struct msghdr
*>(msgh
), const_cast<struct cmsghdr
*>(cmsg
))) {
155 #if defined(IP_PKTINFO)
156 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_PKTINFO
)) {
157 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
158 destination
->sin4
.sin_addr
= i
->ipi_addr
;
159 destination
->sin4
.sin_family
= AF_INET
;
162 #elif defined(IP_RECVDSTADDR)
163 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
164 struct in_addr
*i
= (struct in_addr
*) CMSG_DATA(cmsg
);
165 destination
->sin4
.sin_addr
= *i
;
166 destination
->sin4
.sin_family
= AF_INET
;
171 if ((cmsg
->cmsg_level
== IPPROTO_IPV6
) && (cmsg
->cmsg_type
== IPV6_PKTINFO
)) {
172 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
173 destination
->sin6
.sin6_addr
= i
->ipi6_addr
;
174 destination
->sin4
.sin_family
= AF_INET6
;
181 bool IsAnyAddress(const ComboAddress
& addr
)
183 if(addr
.sin4
.sin_family
== AF_INET
)
184 return addr
.sin4
.sin_addr
.s_addr
== 0;
185 else if(addr
.sin4
.sin_family
== AF_INET6
)
186 return !memcmp(&addr
.sin6
.sin6_addr
, &in6addr_any
, sizeof(addr
.sin6
.sin6_addr
));
191 ssize_t
sendfromto(int sock
, const char* data
, size_t len
, int flags
, const ComboAddress
& from
, const ComboAddress
& to
)
197 /* Set up iov and msgh structures. */
198 memset(&msgh
, 0, sizeof(struct msghdr
));
199 iov
.iov_base
= (void*)data
;
203 msgh
.msg_name
= (struct sockaddr
*)&to
;
204 msgh
.msg_namelen
= to
.getSocklen();
206 if(from
.sin4
.sin_family
) {
207 addCMsgSrcAddr(&msgh
, cbuf
, &from
, 0);
210 msgh
.msg_control
=NULL
;
212 return sendmsg(sock
, &msgh
, flags
);
215 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
216 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
217 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
218 void fillMSGHdr(struct msghdr
* msgh
, struct iovec
* iov
, char* cbuf
, size_t cbufsize
, char* data
, size_t datalen
, ComboAddress
* addr
)
220 iov
->iov_base
= data
;
221 iov
->iov_len
= datalen
;
223 memset(msgh
, 0, sizeof(struct msghdr
));
225 msgh
->msg_control
= cbuf
;
226 msgh
->msg_controllen
= cbufsize
;
227 msgh
->msg_name
= addr
;
228 msgh
->msg_namelen
= addr
->getSocklen();
230 msgh
->msg_iovlen
= 1;
234 // warning: various parts of PowerDNS assume 'truncate' will never throw
235 void ComboAddress::truncate(unsigned int bits
) noexcept
239 if(sin4
.sin_family
==AF_INET
) {
242 start
= (uint8_t*)&sin4
.sin_addr
.s_addr
;
248 start
= (uint8_t*)&sin6
.sin6_addr
.s6_addr
;
252 auto tozero
= len
*8 - bits
; // if set to 22, this will clear 1 byte, as it should
254 memset(start
+ len
- tozero
/8, 0, tozero
/8); // blot out the whole bytes on the right
256 auto bitsleft
=tozero
% 8; // 2 bits left to clear
258 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
259 // so and by '11111100', which is ~((1<<2)-1) = ~3
260 uint8_t* place
= start
+ len
- 1 - tozero
/8;
261 *place
&= (~((1<<bitsleft
)-1));
264 ssize_t
sendMsgWithTimeout(int fd
, const char* buffer
, size_t len
, int timeout
, ComboAddress
& dest
, const ComboAddress
& local
, unsigned int localItf
)
269 bool firstTry
= true;
270 fillMSGHdr(&msgh
, &iov
, cbuf
, sizeof(cbuf
), const_cast<char*>(buffer
), len
, &dest
);
271 addCMsgSrcAddr(&msgh
, cbuf
, &local
, localItf
);
274 ssize_t written
= sendmsg(fd
, &msgh
, 0);
279 if (errno
== EAGAIN
) {
281 int res
= waitForRWData(fd
, false, timeout
, 0);
283 /* there is room available */
287 throw runtime_error("Timeout while waiting to write data");
289 throw runtime_error("Error while waiting for room to write data");
293 throw runtime_error("Timeout while waiting to write data");
297 unixDie("failed in write2WithTimeout");
305 template class NetmaskTree
<bool>;
307 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
)
309 uint16_t size
= htons(bufferLen
);
313 int remainingTime
= totalTimeout
;
319 /* Set up iov and msgh structures. */
320 memset(&msgh
, 0, sizeof(struct msghdr
));
321 msgh
.msg_control
= nullptr;
322 msgh
.msg_controllen
= 0;
324 msgh
.msg_name
= reinterpret_cast<void*>(const_cast<ComboAddress
*>(dest
));
325 msgh
.msg_namelen
= dest
->getSocklen();
328 msgh
.msg_name
= nullptr;
329 msgh
.msg_namelen
= 0;
334 if (localItf
!= 0 && local
) {
335 addCMsgSrcAddr(&msgh
, cbuf
, local
, localItf
);
338 iov
[0].iov_base
= &size
;
339 iov
[0].iov_len
= sizeof(size
);
340 iov
[1].iov_base
= reinterpret_cast<void*>(const_cast<char*>(buffer
));
341 iov
[1].iov_len
= bufferLen
;
345 size_t nbElements
= sizeof(iov
)/sizeof(*iov
);
347 msgh
.msg_iov
= &iov
[pos
];
348 msgh
.msg_iovlen
= nbElements
- pos
;
350 ssize_t res
= sendmsg(sock
, &msgh
, flags
);
352 size_t written
= static_cast<size_t>(res
);
355 if (sent
== (sizeof(size
) + bufferLen
)) {
358 /* partial write, we need to keep only the (parts of) elements
359 that have not been written.
362 if (written
< iov
[pos
].iov_len
) {
363 iov
[pos
].iov_len
-= written
;
364 iov
[pos
].iov_base
= reinterpret_cast<void*>(reinterpret_cast<char*>(iov
[pos
].iov_base
) + written
);
368 written
-= iov
[pos
].iov_len
;
369 iov
[pos
].iov_len
= 0;
373 while (written
> 0 && pos
< nbElements
);
375 else if (res
== -1) {
376 if (errno
== EINTR
) {
379 else if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) {
380 /* EINPROGRESS might happen with non blocking socket,
381 especially with TCP Fast Open */
382 int ret
= waitForRWData(sock
, false, (totalTimeout
== 0 || idleTimeout
<= remainingTime
) ? idleTimeout
: remainingTime
, 0);
384 /* there is room available */
387 throw runtime_error("Timeout while waiting to send data");
389 throw runtime_error("Error while waiting for room to send data");
393 unixDie("failed in sendSizeAndMsgWithTimeout");
397 time_t now
= time(NULL
);
398 int elapsed
= now
- start
;
399 if (elapsed
>= remainingTime
) {
400 throw runtime_error("Timeout while sending data");
403 remainingTime
-= elapsed
;
410 /* requires a non-blocking socket.
411 On Linux, we could use MSG_DONTWAIT on a blocking socket
412 but this is not portable.
414 bool isTCPSocketUsable(int sock
)
418 size_t buf_size
= sizeof(buf
);
421 ssize_t got
= recv(sock
, &buf
, buf_size
, MSG_PEEK
);
424 /* socket is usable, some data is even waiting to be read */
428 /* other end has closed the socket */
434 if (err
== EAGAIN
|| err
== EWOULDBLOCK
) {
435 /* socket is usable, no data waiting */
440 /* something is wrong, could be ECONNRESET,
441 ENOTCONN, EPIPE, but anyway this socket is
447 } while (err
== EINTR
);