]>
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
% stringerror());
48 int SConnect(int sockfd
, const ComboAddress
& remote
)
50 int ret
= connect(sockfd
, reinterpret_cast<const 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
, reinterpret_cast<const 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") % stringerror());
123 int SListen(int sockfd
, int limit
)
125 int ret
= listen(sockfd
, limit
);
127 RuntimeError(boost::format("setting socket to listen: %s") % stringerror());
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
% stringerror());
139 void setSocketIgnorePMTU(int sockfd
)
141 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
142 #ifdef IP_PMTUDISC_OMIT
143 /* Linux 3.15+ has IP_PMTUDISC_OMIT, which discards PMTU information to prevent
144 poisoning, but still allows fragmentation if the packet size exceeds the
145 outgoing interface MTU, which is good.
148 SSetsockopt(sockfd
, IPPROTO_IP
, IP_MTU_DISCOVER
, IP_PMTUDISC_OMIT
);
151 catch(const std::exception
& e
) {
152 /* failed, let's try IP_PMTUDISC_DONT instead */
154 #endif /* IP_PMTUDISC_OMIT */
156 /* IP_PMTUDISC_DONT disables Path MTU discovery */
157 SSetsockopt(sockfd
, IPPROTO_IP
, IP_MTU_DISCOVER
, IP_PMTUDISC_DONT
);
158 #endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
161 bool HarvestTimestamp(struct msghdr
* msgh
, struct timeval
* tv
)
164 struct cmsghdr
*cmsg
;
165 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(msgh
,cmsg
)) {
166 if ((cmsg
->cmsg_level
== SOL_SOCKET
) && (cmsg
->cmsg_type
== SO_TIMESTAMP
|| cmsg
->cmsg_type
== SCM_TIMESTAMP
) &&
167 CMSG_LEN(sizeof(*tv
)) == cmsg
->cmsg_len
) {
168 memcpy(tv
, CMSG_DATA(cmsg
), sizeof(*tv
));
175 bool HarvestDestinationAddress(const struct msghdr
* msgh
, ComboAddress
* destination
)
177 destination
->reset();
179 struct cmsghdr
* cmsg
;
181 const struct cmsghdr
* cmsg
;
183 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(const_cast<struct msghdr
*>(msgh
), const_cast<struct cmsghdr
*>(cmsg
))) {
184 #if defined(IP_PKTINFO)
185 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_PKTINFO
)) {
186 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
187 destination
->sin4
.sin_addr
= i
->ipi_addr
;
188 destination
->sin4
.sin_family
= AF_INET
;
191 #elif defined(IP_RECVDSTADDR)
192 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
193 struct in_addr
*i
= (struct in_addr
*) CMSG_DATA(cmsg
);
194 destination
->sin4
.sin_addr
= *i
;
195 destination
->sin4
.sin_family
= AF_INET
;
200 if ((cmsg
->cmsg_level
== IPPROTO_IPV6
) && (cmsg
->cmsg_type
== IPV6_PKTINFO
)) {
201 struct in6_pktinfo
*i
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
202 destination
->sin6
.sin6_addr
= i
->ipi6_addr
;
203 destination
->sin4
.sin_family
= AF_INET6
;
210 bool IsAnyAddress(const ComboAddress
& addr
)
212 if(addr
.sin4
.sin_family
== AF_INET
)
213 return addr
.sin4
.sin_addr
.s_addr
== 0;
214 else if(addr
.sin4
.sin_family
== AF_INET6
)
215 return !memcmp(&addr
.sin6
.sin6_addr
, &in6addr_any
, sizeof(addr
.sin6
.sin6_addr
));
220 ssize_t
sendfromto(int sock
, const char* data
, size_t len
, int flags
, const ComboAddress
& from
, const ComboAddress
& to
)
224 cmsgbuf_aligned cbuf
;
226 /* Set up iov and msgh structures. */
227 memset(&msgh
, 0, sizeof(struct msghdr
));
228 iov
.iov_base
= (void*)data
;
232 msgh
.msg_name
= (struct sockaddr
*)&to
;
233 msgh
.msg_namelen
= to
.getSocklen();
235 if(from
.sin4
.sin_family
) {
236 addCMsgSrcAddr(&msgh
, &cbuf
, &from
, 0);
239 msgh
.msg_control
=NULL
;
241 return sendmsg(sock
, &msgh
, flags
);
244 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
245 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
246 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
247 void fillMSGHdr(struct msghdr
* msgh
, struct iovec
* iov
, cmsgbuf_aligned
* cbuf
, size_t cbufsize
, char* data
, size_t datalen
, ComboAddress
* addr
)
249 iov
->iov_base
= data
;
250 iov
->iov_len
= datalen
;
252 memset(msgh
, 0, sizeof(struct msghdr
));
254 msgh
->msg_control
= cbuf
;
255 msgh
->msg_controllen
= cbufsize
;
256 msgh
->msg_name
= addr
;
257 msgh
->msg_namelen
= addr
->getSocklen();
259 msgh
->msg_iovlen
= 1;
263 // warning: various parts of PowerDNS assume 'truncate' will never throw
264 void ComboAddress::truncate(unsigned int bits
) noexcept
268 if(sin4
.sin_family
==AF_INET
) {
271 start
= (uint8_t*)&sin4
.sin_addr
.s_addr
;
277 start
= (uint8_t*)&sin6
.sin6_addr
.s6_addr
;
281 auto tozero
= len
*8 - bits
; // if set to 22, this will clear 1 byte, as it should
283 memset(start
+ len
- tozero
/8, 0, tozero
/8); // blot out the whole bytes on the right
285 auto bitsleft
=tozero
% 8; // 2 bits left to clear
287 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
288 // so and by '11111100', which is ~((1<<2)-1) = ~3
289 uint8_t* place
= start
+ len
- 1 - tozero
/8;
290 *place
&= (~((1<<bitsleft
)-1));
293 size_t sendMsgWithOptions(int fd
, const char* buffer
, size_t len
, const ComboAddress
* dest
, const ComboAddress
* local
, unsigned int localItf
, int flags
)
297 cmsgbuf_aligned cbuf
;
299 /* Set up iov and msgh structures. */
300 memset(&msgh
, 0, sizeof(struct msghdr
));
301 msgh
.msg_control
= nullptr;
302 msgh
.msg_controllen
= 0;
304 msgh
.msg_name
= reinterpret_cast<void*>(const_cast<ComboAddress
*>(dest
));
305 msgh
.msg_namelen
= dest
->getSocklen();
308 msgh
.msg_name
= nullptr;
309 msgh
.msg_namelen
= 0;
314 if (localItf
!= 0 && local
) {
315 addCMsgSrcAddr(&msgh
, &cbuf
, local
, localItf
);
318 iov
.iov_base
= reinterpret_cast<void*>(const_cast<char*>(buffer
));
325 bool firstTry
= true;
330 if (flags
& MSG_FASTOPEN
&& firstTry
== false) {
331 flags
&= ~MSG_FASTOPEN
;
333 #endif /* MSG_FASTOPEN */
335 ssize_t res
= sendmsg(fd
, &msgh
, flags
);
338 size_t written
= static_cast<size_t>(res
);
347 iov
.iov_len
-= written
;
348 iov
.iov_base
= reinterpret_cast<void*>(reinterpret_cast<char*>(iov
.iov_base
) + written
);
354 else if (res
== -1) {
359 else if (err
== EAGAIN
|| err
== EWOULDBLOCK
|| err
== EINPROGRESS
|| err
== ENOTCONN
) {
360 /* EINPROGRESS might happen with non blocking socket,
361 especially with TCP Fast Open */
365 unixDie("failed in sendMsgWithTimeout");
374 template class NetmaskTree
<bool>;
376 /* requires a non-blocking socket.
377 On Linux, we could use MSG_DONTWAIT on a blocking socket
378 but this is not portable.
380 bool isTCPSocketUsable(int sock
)
384 size_t buf_size
= sizeof(buf
);
387 ssize_t got
= recv(sock
, &buf
, buf_size
, MSG_PEEK
);
390 /* socket is usable, some data is even waiting to be read */
394 /* other end has closed the socket */
400 if (err
== EAGAIN
|| err
== EWOULDBLOCK
) {
401 /* socket is usable, no data waiting */
406 /* something is wrong, could be ECONNRESET,
407 ENOTCONN, EPIPE, but anyway this socket is
413 } while (err
== EINTR
);