]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/iputils.cc
Standardize license text in all PDNS files
[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 SBind(int sockfd, const ComboAddress& local)
55 {
56 int ret = bind(sockfd, (struct sockaddr*)&local, local.getSocklen());
57 if(ret < 0) {
58 int savederrno = errno;
59 RuntimeError(boost::format("binding socket to %s: %s") % local.toStringWithPort() % strerror(savederrno));
60 }
61 return ret;
62 }
63
64 int SAccept(int sockfd, ComboAddress& remote)
65 {
66 socklen_t remlen = remote.getSocklen();
67
68 int ret = accept(sockfd, (struct sockaddr*)&remote, &remlen);
69 if(ret < 0)
70 RuntimeError(boost::format("accepting new connection on socket: %s") % strerror(errno));
71 return ret;
72 }
73
74 int SListen(int sockfd, int limit)
75 {
76 int ret = listen(sockfd, limit);
77 if(ret < 0)
78 RuntimeError(boost::format("setting socket to listen: %s") % strerror(errno));
79 return ret;
80 }
81
82 int SSetsockopt(int sockfd, int level, int opname, int value)
83 {
84 int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
85 if(ret < 0)
86 RuntimeError(boost::format("setsockopt for level %d and opname %d to %d failed: %s") % level % opname % value % strerror(errno));
87 return ret;
88 }
89
90
91 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
92 {
93 #ifdef SO_TIMESTAMP
94 struct cmsghdr *cmsg;
95 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
96 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) &&
97 CMSG_LEN(sizeof(*tv)) == cmsg->cmsg_len) {
98 memcpy(tv, CMSG_DATA(cmsg), sizeof(*tv));
99 return true;
100 }
101 }
102 #endif
103 return false;
104 }
105 bool HarvestDestinationAddress(struct msghdr* msgh, ComboAddress* destination)
106 {
107 memset(destination, 0, sizeof(*destination));
108 struct cmsghdr *cmsg;
109 for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh,cmsg)) {
110 #if defined(IP_PKTINFO)
111 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
112 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
113 destination->sin4.sin_addr = i->ipi_addr;
114 destination->sin4.sin_family = AF_INET;
115 return true;
116 }
117 #elif defined(IP_RECVDSTADDR)
118 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
119 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
120 destination->sin4.sin_addr = *i;
121 destination->sin4.sin_family = AF_INET;
122 return true;
123 }
124 #endif
125
126 if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
127 struct in6_pktinfo *i = (struct in6_pktinfo *) CMSG_DATA(cmsg);
128 destination->sin6.sin6_addr = i->ipi6_addr;
129 destination->sin4.sin_family = AF_INET6;
130 return true;
131 }
132 }
133 return false;
134 }
135
136 bool IsAnyAddress(const ComboAddress& addr)
137 {
138 if(addr.sin4.sin_family == AF_INET)
139 return addr.sin4.sin_addr.s_addr == 0;
140 else if(addr.sin4.sin_family == AF_INET6)
141 return !memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr));
142
143 return false;
144 }
145
146 ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to)
147 {
148 struct msghdr msgh;
149 struct iovec iov;
150 char cbuf[256];
151
152 /* Set up iov and msgh structures. */
153 memset(&msgh, 0, sizeof(struct msghdr));
154 iov.iov_base = (void*)data;
155 iov.iov_len = len;
156 msgh.msg_iov = &iov;
157 msgh.msg_iovlen = 1;
158 msgh.msg_name = (struct sockaddr*)&to;
159 msgh.msg_namelen = to.getSocklen();
160
161 if(from.sin4.sin_family) {
162 addCMsgSrcAddr(&msgh, cbuf, &from, 0);
163 }
164 else {
165 msgh.msg_control=NULL;
166 }
167 return sendmsg(sock, &msgh, flags);
168 }
169
170 // be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
171 // be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
172 // be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
173 void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
174 {
175 iov->iov_base = data;
176 iov->iov_len = datalen;
177
178 memset(msgh, 0, sizeof(struct msghdr));
179
180 msgh->msg_control = cbuf;
181 msgh->msg_controllen = cbufsize;
182 msgh->msg_name = addr;
183 msgh->msg_namelen = addr->getSocklen();
184 msgh->msg_iov = iov;
185 msgh->msg_iovlen = 1;
186 msgh->msg_flags = 0;
187 }
188
189 void ComboAddress::truncate(unsigned int bits)
190 {
191 uint8_t* start;
192 int len=4;
193 if(sin4.sin_family==AF_INET) {
194 if(bits >= 32)
195 return;
196 start = (uint8_t*)&sin4.sin_addr.s_addr;
197 len=4;
198 }
199 else {
200 if(bits >= 128)
201 return;
202 start = (uint8_t*)&sin6.sin6_addr.s6_addr;
203 len=16;
204 }
205
206 auto tozero= len*8 - bits; // if set to 22, this will clear 1 byte, as it should
207
208 memset(start + len - tozero/8, 0, tozero/8); // blot out the whole bytes on the right
209
210 auto bitsleft=tozero % 8; // 2 bits left to clear
211
212 // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
213 // so and by '11111100', which is ~((1<<2)-1) = ~3
214 uint8_t* place = start + len - 1 - tozero/8;
215 *place &= (~((1<<bitsleft)-1));
216 }
217
218 ssize_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int timeout, ComboAddress& dest, const ComboAddress& local, unsigned int localItf)
219 {
220 struct msghdr msgh;
221 struct iovec iov;
222 char cbuf[256];
223 bool firstTry = true;
224 fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), const_cast<char*>(buffer), len, &dest);
225 addCMsgSrcAddr(&msgh, cbuf, &local, localItf);
226
227 do {
228 ssize_t written = sendmsg(fd, &msgh, 0);
229
230 if (written > 0)
231 return written;
232
233 if (errno == EAGAIN) {
234 if (firstTry) {
235 int res = waitForRWData(fd, false, timeout, 0);
236 if (res > 0) {
237 /* there is room available */
238 firstTry = false;
239 }
240 else if (res == 0) {
241 throw runtime_error("Timeout while waiting to write data");
242 } else {
243 throw runtime_error("Error while waiting for room to write data");
244 }
245 }
246 else {
247 throw runtime_error("Timeout while waiting to write data");
248 }
249 }
250 else {
251 unixDie("failed in write2WithTimeout");
252 }
253 }
254 while (firstTry);
255
256 return 0;
257 }
258
259 template class NetmaskTree<bool>;
260