]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/iputils.cc
Merge pull request #5699 from ahupowerdns/rec-dyn-cache-entries
[thirdparty/pdns.git] / pdns / iputils.cc
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "iputils.hh"
26 #include <sys/socket.h>
27
28 /** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
29
30 static void RuntimeError(const boost::format& fmt)
31 {
32 throw runtime_error(fmt.str());
33 }
34
35
36 int SSocket(int family, int type, int flags)
37 {
38 int ret = socket(family, type, flags);
39 if(ret < 0)
40 RuntimeError(boost::format("creating socket of type %d: %s") % family % strerror(errno));
41 return ret;
42 }
43
44 int SConnect(int sockfd, const ComboAddress& remote)
45 {
46 int ret = connect(sockfd, (struct sockaddr*)&remote, remote.getSocklen());
47 if(ret < 0) {
48 int savederrno = errno;
49 RuntimeError(boost::format("connecting socket to %s: %s") % remote.toStringWithPort() % strerror(savederrno));
50 }
51 return ret;
52 }
53
54 int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout)
55 {
56 int ret = connect(sockfd, (struct sockaddr*)&remote, remote.getSocklen());
57 if(ret < 0) {
58 int savederrno = errno;
59 if (savederrno == EINPROGRESS) {
60 /* we wait until the connection has been established */
61 bool error = false;
62 bool disconnected = false;
63 int res = waitForRWData(sockfd, false, timeout, 0, &error, &disconnected);
64 if (res == 1) {
65 if (error) {
66 savederrno = 0;
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)));
70 }
71 else {
72 RuntimeError(boost::format("connecting to %s failed") % remote.toStringWithPort());
73 }
74 }
75 if (disconnected) {
76 RuntimeError(boost::format("%s closed the connection") % remote.toStringWithPort());
77 }
78 return 0;
79 }
80 else if (res == 0) {
81 RuntimeError(boost::format("timeout while connecting to %s") % remote.toStringWithPort());
82 } else if (res < 0) {
83 savederrno = errno;
84 RuntimeError(boost::format("waiting to connect to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno)));
85 }
86 }
87 else {
88 RuntimeError(boost::format("connecting to %s: %s") % remote.toStringWithPort() % string(strerror(savederrno)));
89 }
90 }
91
92 return ret;
93 }
94
95 int SBind(int sockfd, const ComboAddress& local)
96 {
97 int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen());
98 if(ret < 0) {
99 int savederrno = errno;
100 RuntimeError(boost::format("binding socket to %s: %s") % local.toStringWithPort() % strerror(savederrno));
101 }
102 return ret;
103 }
104
105 int SAccept(int sockfd, ComboAddress& remote)
106 {
107 socklen_t remlen = remote.getSocklen();
108
109 int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen);
110 if(ret < 0)
111 RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno));
112 return ret;
113 }
114
115 int SListen(int sockfd, int limit)
116 {
117 int ret = listen(sockfd, limit);
118 if(ret < 0)
119 RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno));
120 return ret;
121 }
122
123 int SSetsockopt(int sockfd, int level, int opname, int value)
124 {
125 int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
126 if(ret < 0)
127 RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level % opname % value % strerror(errno));
128 return ret;
129 }
130
131
132 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
133 {
134 #ifdef SO_TIMESTAMP
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));
140 return true;
141 }
142 }
143 #endif
144 return false;
145 }
146 bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
147 {
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;
156 return true;
157 }
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;
163 return true;
164 }
165 #endif
166
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;
171 return true;
172 }
173 }
174 return false;
175 }
176
177 bool IsAnyAddress(const ComboAddress& addr)
178 {
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));
183
184 return false;
185 }
186
187 ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to)
188 {
189 struct msghdr msgh;
190 struct iovec iov;
191 char cbuf[256];
192
193 /* Set up iov and msgh structures. */
194 memset(&msgh, 0, sizeof(struct msghdr));
195 iov.iov_base = (void*)data;
196 iov.iov_len = len;
197 msgh.msg_iov = &iov;
198 msgh.msg_iovlen = 1;
199 msgh.msg_name = (struct sockaddr*)&to;
200 msgh.msg_namelen = to.getSocklen();
201
202 if(from.sin4.sin_family) {
203 addCMsgSrcAddr(&msgh, cbuf, &from, 0);
204 }
205 else {
206 msgh.msg_control=NULL;
207 }
208 return sendmsg(sock, &msgh, flags);
209 }
210
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)
215 {
216 iov->iov_base = data;
217 iov->iov_len = datalen;
218
219 memset(msgh, 0, sizeof(struct msghdr));
220
221 msgh->msg_control = cbuf;
222 msgh->msg_controllen = cbufsize;
223 msgh->msg_name = addr;
224 msgh->msg_namelen = addr->getSocklen();
225 msgh->msg_iov = iov;
226 msgh->msg_iovlen = 1;
227 msgh->msg_flags = 0;
228 }
229
230 // warning: various parts of PowerDNS assume 'truncate' will never throw
231 void ComboAddress::truncate(unsigned int bits) noexcept
232 {
233 uint8_t* start;
234 int len=4;
235 if(sin4.sin_family==AF_INET) {
236 if(bits >= 32)
237 return;
238 start = (uint8_t*)&sin4.sin_addr.s_addr;
239 len=4;
240 }
241 else {
242 if(bits >= 128)
243 return;
244 start = (uint8_t*)&sin6.sin6_addr.s6_addr;
245 len=16;
246 }
247
248 auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should
249
250 memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right
251
252 auto bitsleft=tozero % 8; // 2 bits left to clear
253
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));
258 }
259
260 ssize_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int timeout, ComboAddress& dest, const ComboAddress& local, unsigned int localItf)
261 {
262 struct msghdr msgh;
263 struct iovec iov;
264 char cbuf[256];
265 bool firstTry = true;
266 fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), const_cast<char*>(buffer), len, &dest);
267 addCMsgSrcAddr(&msgh, cbuf, &local, localItf);
268
269 do {
270 ssize_t written = sendmsg(fd, &msgh, 0);
271
272 if (written > 0)
273 return written;
274
275 if (errno == EAGAIN) {
276 if (firstTry) {
277 int res = waitForRWData(fd, false, timeout, 0);
278 if (res > 0) {
279 /* there is room available */
280 firstTry = false;
281 }
282 else if (res == 0) {
283 throw runtime_error("Timeout while waiting to write data");
284 } else {
285 throw runtime_error("Error while waiting for room to write data");
286 }
287 }
288 else {
289 throw runtime_error("Timeout while waiting to write data");
290 }
291 }
292 else {
293 unixDie("failed in write2WithTimeout");
294 }
295 }
296 while (firstTry);
297
298 return 0;
299 }
300
301 template class NetmaskTree<bool>;
302
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)
304 {
305 uint16_t size = htons(bufferLen);
306 char cbuf[256];
307 struct msghdr msgh;
308 struct iovec iov[2];
309 int remainingTime = totalTimeout;
310 time_t start = 0;
311 if (totalTimeout) {
312 start = time(NULL);
313 }
314
315 /* Set up iov and msgh structures. */
316 memset(&msgh, 0, sizeof(struct msghdr));
317 msgh.msg_control = nullptr;
318 msgh.msg_controllen = 0;
319 if (dest) {
320 msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
321 msgh.msg_namelen = dest->getSocklen();
322 }
323 else {
324 msgh.msg_name = nullptr;
325 msgh.msg_namelen = 0;
326 }
327
328 msgh.msg_flags = 0;
329
330 if (localItf != 0 && local) {
331 addCMsgSrcAddr(&msgh, cbuf, local, localItf);
332 }
333
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;
338
339 size_t pos = 0;
340 size_t sent = 0;
341 size_t nbElements = sizeof(iov)/sizeof(*iov);
342 while (true) {
343 msgh.msg_iov = &iov[pos];
344 msgh.msg_iovlen = nbElements - pos;
345
346 ssize_t res = sendmsg(sock, &msgh, flags);
347 if (res > 0) {
348 size_t written = static_cast<size_t>(res);
349 sent += written;
350
351 if (sent == (sizeof(size) + bufferLen)) {
352 return true;
353 }
354 /* partial write, we need to keep only the (parts of) elements
355 that have not been written.
356 */
357 do {
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);
361 written = 0;
362 }
363 else {
364 written -= iov[pos].iov_len;
365 iov[pos].iov_len = 0;
366 pos++;
367 }
368 }
369 while (written > 0 && pos < nbElements);
370 }
371 else if (res == -1) {
372 if (errno == EINTR) {
373 continue;
374 }
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);
379 if (ret > 0) {
380 /* there is room available */
381 }
382 else if (ret == 0) {
383 throw runtime_error("Timeout while waiting to send data");
384 } else {
385 throw runtime_error("Error while waiting for room to send data");
386 }
387 }
388 else {
389 unixDie("failed in sendSizeAndMsgWithTimeout");
390 }
391 }
392 if (totalTimeout) {
393 time_t now = time(NULL);
394 int elapsed = now - start;
395 if (elapsed >= remainingTime) {
396 throw runtime_error("Timeout while sending data");
397 }
398 start = now;
399 remainingTime -= elapsed;
400 }
401 }
402
403 return false;
404 }
405
406 /* requires a non-blocking socket.
407 On Linux, we could use MSG_DONTWAIT on a blocking socket
408 but this is not portable.
409 */
410 bool isTCPSocketUsable(int sock)
411 {
412 int err = 0;
413 char buf = '\0';
414 size_t buf_size = sizeof(buf);
415
416 do {
417 ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
418
419 if (got > 0) {
420 /* socket is usable, some data is even waiting to be read */
421 return true;
422 }
423 else if (got == 0) {
424 /* other end has closed the socket */
425 return false;
426 }
427 else {
428 int err = errno;
429
430 if (err == EAGAIN || err == EWOULDBLOCK) {
431 /* socket is usable, no data waiting */
432 return true;
433 }
434 else {
435 if (err != EINTR) {
436 /* something is wrong, could be ECONNRESET,
437 ENOTCONN, EPIPE, but anyway this socket is
438 not usable. */
439 return false;
440 }
441 }
442 }
443 } while (err == EINTR);
444
445 return false;
446 }