]>
Commit | Line | Data |
---|---|---|
12c86877 | 1 | /* |
12471842 PL |
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 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
12c86877 BH |
25 | #include "utility.hh" |
26 | #include <cstdio> | |
27 | #include <cstring> | |
28 | #include <cstdlib> | |
29 | #include <cerrno> | |
30 | #include <iostream> | |
31 | #include <string> | |
32 | #include <sys/types.h> | |
46c6975c | 33 | #include "responsestats.hh" |
dd7da6cd | 34 | |
12c86877 BH |
35 | #include "dns.hh" |
36 | #include "dnsbackend.hh" | |
37 | #include "dnspacket.hh" | |
38 | #include "nameserver.hh" | |
39 | #include "distributor.hh" | |
40 | #include "logger.hh" | |
41 | #include "arguments.hh" | |
42 | #include "statbag.hh" | |
43 | ||
61b26744 | 44 | #include "namespaces.hh" |
30b4da87 | 45 | |
12c86877 BH |
46 | extern StatBag S; |
47 | ||
48 | /** \mainpage | |
f28307ad | 49 | PowerDNS is a very versatile nameserver that can answer questions from different backends. To implement your |
12c86877 BH |
50 | own backend, see the documentation for the DNSBackend class. |
51 | ||
52 | \section copyright Copyright and License | |
657e9124 | 53 | PowerDNS is (C) 2001-2008 PowerDNS.COM BV. It is distributed according to the terms of the General Public License version 2. |
12c86877 BH |
54 | |
55 | \section overview High level overview | |
56 | ||
bdc9f8d2 | 57 | The Distributor contains a configurable number of PacketHandler instances, each in its own thread, for connection pooling. |
3f81d239 | 58 | PacketHandler instances are recycled of they let escape an PDNSException. |
12c86877 BH |
59 | |
60 | The PacketHandler implements the RFC1034 algorithm and converts question packets into DNSBackend queries. | |
61 | ||
62 | A DNSBackend is an entity that returns DNSResourceRecord objects in return to explicit questions for domains with a specified QType | |
63 | ||
35520716 CH |
64 | PowerDNS uses the UeberBackend, which hosts DNSBackends. By default it has no DNSBackends within itself, those are loaded |
65 | by setting --load=<list of backends>. This way DNSBackend implementations can be kept completely separate, but most aren't. | |
12c86877 BH |
66 | |
67 | If one or more DNSBackends are loaded, the UeberBackend fields the queries to all of them until one answers. | |
68 | ||
69 | \section TCP TCP Operations | |
70 | ||
71 | The TCP operation runs within a single thread called tcpreceiver(), that also queries the PacketHandler. | |
72 | ||
73 | \section Cache Caching | |
74 | ||
75 | On its own, this setup is not suitable for high performance operations. A single DNS query can turn into many DNSBackend questions, | |
abc1d928 | 76 | each taking many milliseconds to complete. This is why the qthread() first checks the PacketCache to see if an answer is known to a packet |
12c86877 BH |
77 | asking this question. If so, the entire Distributor is shunted, and the answer is sent back *directly*, within a few microseconds. |
78 | ||
12c86877 BH |
79 | \section misc Miscellaneous |
80 | Configuration details are available via the ArgvMap instance arg. Statistics are created by making calls to the StatBag object called S. | |
81 | These statistics are made available via the UeberBackend on the same socket that is used for dynamic module commands. | |
82 | ||
83 | \section Main Main | |
bdc9f8d2 | 84 | The main() of PowerDNS can be found in receiver.cc - start reading there for further insights into the operation of the nameserver |
12c86877 BH |
85 | */ |
86 | ||
d0854a6c | 87 | vector<ComboAddress> g_localaddresses; // not static, our unit tests need to poke this |
2b6f1436 | 88 | |
12c86877 BH |
89 | void UDPNameserver::bindIPv4() |
90 | { | |
91 | vector<string>locals; | |
43d347d3 | 92 | stringtok(locals,::arg()["local-address"]," ,"); |
4ee30c66 | 93 | int one = 1; |
12c86877 BH |
94 | |
95 | if(locals.empty()) | |
3f81d239 | 96 | throw PDNSException("No local address specified"); |
12c86877 BH |
97 | |
98 | int s; | |
99 | for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) { | |
100 | string localname(*i); | |
8db49a64 | 101 | ComboAddress locala; |
12c86877 BH |
102 | |
103 | s=socket(AF_INET,SOCK_DGRAM,0); | |
42c235e5 | 104 | |
a738556b | 105 | if(s<0) { |
e6a9dde5 | 106 | g_log<<Logger::Error<<"Unable to acquire UDP socket: "+string(strerror(errno)) << endl; |
3f81d239 | 107 | throw PDNSException("Unable to acquire a UDP socket: "+string(strerror(errno))); |
a738556b | 108 | } |
91dd9c00 | 109 | |
3897b9e1 | 110 | setCloseOnExec(s); |
2b6f1436 | 111 | |
3897b9e1 | 112 | if(!setNonBlocking(s)) |
3f81d239 | 113 | throw PDNSException("Unable to set UDP socket to non-blocking: "+stringerror()); |
d38e2ba9 | 114 | |
36025a51 | 115 | locala=ComboAddress(localname, ::arg().asNum("local-port")); |
116 | ||
117 | if(locala.sin4.sin_family != AF_INET) | |
118 | throw PDNSException("Attempting to bind IPv4 socket to IPv6 address"); | |
12c86877 | 119 | |
36025a51 | 120 | if(IsAnyAddress(locala)) |
4ee30c66 MZ |
121 | setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); |
122 | ||
915b0c39 | 123 | if (!setSocketTimestamps(s)) |
e6a9dde5 | 124 | g_log<<Logger::Warning<<"Unable to enable timestamp reporting for socket"<<endl; |
9ae194e2 | 125 | |
90f9fbc0 RG |
126 | if (locala.isIPv4()) { |
127 | try { | |
128 | setSocketIgnorePMTU(s); | |
129 | } | |
130 | catch(const std::exception& e) { | |
131 | g_log<<Logger::Warning<<"Failed to set IP_MTU_DISCOVER on UDP server socket: "<<e.what()<<endl; | |
132 | } | |
133 | } | |
134 | ||
4ee30c66 | 135 | #ifdef SO_REUSEPORT |
3a56adcc MZ |
136 | if( d_can_reuseport ) |
137 | if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) ) | |
138 | d_can_reuseport = false; | |
4ee30c66 MZ |
139 | #endif |
140 | ||
fec7dd5a SS |
141 | if( ::arg().mustDo("non-local-bind") ) |
142 | Utility::setBindAny(AF_INET, s); | |
143 | ||
12c86877 | 144 | |
3a56adcc MZ |
145 | if( !d_additional_socket ) |
146 | g_localaddresses.push_back(locala); | |
1d586b37 | 147 | |
8db49a64 | 148 | if(::bind(s, (sockaddr*)&locala, locala.getSocklen()) < 0) { |
ed3afdfc | 149 | string binderror = strerror(errno); |
2c896042 | 150 | close(s); |
5ecb2885 | 151 | if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-address-nonexist-fail") ) { |
e6a9dde5 | 152 | g_log<<Logger::Error<<"IPv4 Address " << localname << " does not exist on this server - skipping UDP bind" << endl; |
5ecb2885 MZ |
153 | continue; |
154 | } else { | |
e6a9dde5 | 155 | g_log<<Logger::Error<<"Unable to bind UDP socket to '"+locala.toStringWithPort()+"': "<<binderror<<endl; |
5ecb2885 MZ |
156 | throw PDNSException("Unable to bind to UDP socket"); |
157 | } | |
12c86877 | 158 | } |
12c86877 | 159 | d_sockets.push_back(s); |
e6a9dde5 | 160 | g_log<<Logger::Error<<"UDP server bound to "<<locala.toStringWithPort()<<endl; |
6e242246 BH |
161 | struct pollfd pfd; |
162 | pfd.fd = s; | |
cd5e1584 | 163 | pfd.events = POLLIN; |
6e242246 BH |
164 | pfd.revents = 0; |
165 | d_rfds.push_back(pfd); | |
12c86877 BH |
166 | } |
167 | } | |
168 | ||
8db49a64 | 169 | bool AddressIsUs(const ComboAddress& remote) |
170 | { | |
ef7cd021 | 171 | for(const ComboAddress& us : g_localaddresses) { |
8db49a64 | 172 | if(remote == us) |
173 | return true; | |
174 | if(IsAnyAddress(us)) { | |
d0854a6c | 175 | int s = socket(remote.sin4.sin_family, SOCK_DGRAM, 0); |
8db49a64 | 176 | if(s < 0) |
232f0877 | 177 | continue; |
8db49a64 | 178 | |
179 | if(connect(s, (struct sockaddr*)&remote, remote.getSocklen()) < 0) { | |
232f0877 CH |
180 | close(s); |
181 | continue; | |
8db49a64 | 182 | } |
183 | ||
184 | ComboAddress actualLocal; | |
185 | actualLocal.sin4.sin_family = remote.sin4.sin_family; | |
186 | socklen_t socklen = actualLocal.getSocklen(); | |
187 | ||
188 | if(getsockname(s, (struct sockaddr*) &actualLocal, &socklen) < 0) { | |
232f0877 CH |
189 | close(s); |
190 | continue; | |
8db49a64 | 191 | } |
192 | close(s); | |
193 | actualLocal.sin4.sin_port = us.sin4.sin_port; | |
194 | if(actualLocal == remote) | |
232f0877 | 195 | return true; |
8db49a64 | 196 | } |
197 | } | |
198 | return false; | |
199 | } | |
200 | ||
201 | ||
12c86877 BH |
202 | void UDPNameserver::bindIPv6() |
203 | { | |
2b6f1436 | 204 | vector<string> locals; |
43d347d3 | 205 | stringtok(locals,::arg()["local-ipv6"]," ,"); |
4ee30c66 | 206 | int one=1; |
12c86877 BH |
207 | |
208 | if(locals.empty()) | |
209 | return; | |
210 | ||
12c86877 BH |
211 | int s; |
212 | for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) { | |
213 | string localname(*i); | |
12c86877 BH |
214 | |
215 | s=socket(AF_INET6,SOCK_DGRAM,0); | |
a738556b | 216 | if(s<0) { |
883ad072 | 217 | if( errno == EAFNOSUPPORT ) { |
e6a9dde5 | 218 | g_log<<Logger::Error<<"IPv6 Address Family is not supported - skipping UDPv6 bind" << endl; |
883ad072 YG |
219 | return; |
220 | } else { | |
e6a9dde5 | 221 | g_log<<Logger::Error<<"Unable to acquire a UDPv6 socket: "+string(strerror(errno)) << endl; |
883ad072 YG |
222 | throw PDNSException("Unable to acquire a UDPv6 socket: "+string(strerror(errno))); |
223 | } | |
a738556b | 224 | } |
2b6f1436 | 225 | |
3897b9e1 | 226 | setCloseOnExec(s); |
227 | if(!setNonBlocking(s)) | |
3f81d239 | 228 | throw PDNSException("Unable to set UDPv6 socket to non-blocking: "+stringerror()); |
315dd2e6 | 229 | |
2b6f1436 BH |
230 | ComboAddress locala(localname, ::arg().asNum("local-port")); |
231 | ||
232 | if(IsAnyAddress(locala)) { | |
4ee30c66 | 233 | setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems |
199f29bf | 234 | #ifdef IPV6_RECVPKTINFO |
4ee30c66 | 235 | setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); |
199f29bf | 236 | #endif |
4ee30c66 | 237 | setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); // if this fails, we report an error in tcpreceiver too |
12c86877 | 238 | } |
4ee30c66 | 239 | |
c2e30378 | 240 | if (!setSocketTimestamps(s)) |
e6a9dde5 | 241 | g_log<<Logger::Warning<<"Unable to enable timestamp reporting for socket"<<endl; |
9ae194e2 | 242 | |
4ee30c66 | 243 | #ifdef SO_REUSEPORT |
3a56adcc MZ |
244 | if( d_can_reuseport ) |
245 | if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) ) | |
246 | d_can_reuseport = false; | |
4ee30c66 MZ |
247 | #endif |
248 | ||
fec7dd5a SS |
249 | if( ::arg().mustDo("non-local-bind") ) |
250 | Utility::setBindAny(AF_INET6, s); | |
251 | ||
3a56adcc MZ |
252 | if( !d_additional_socket ) |
253 | g_localaddresses.push_back(locala); | |
4324d44e | 254 | if(::bind(s, (sockaddr*)&locala, locala.getSocklen())<0) { |
2c896042 | 255 | close(s); |
5ecb2885 | 256 | if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-ipv6-nonexist-fail") ) { |
e6a9dde5 | 257 | g_log<<Logger::Error<<"IPv6 Address " << localname << " does not exist on this server - skipping UDP bind" << endl; |
5ecb2885 MZ |
258 | continue; |
259 | } else { | |
e6a9dde5 | 260 | g_log<<Logger::Error<<"Unable to bind to UDPv6 socket "<< localname <<": "<<strerror(errno)<<endl; |
c057bfaa | 261 | throw PDNSException("Unable to bind to UDPv6 socket"); |
5ecb2885 | 262 | } |
12c86877 | 263 | } |
12c86877 | 264 | d_sockets.push_back(s); |
6e242246 BH |
265 | struct pollfd pfd; |
266 | pfd.fd = s; | |
cd5e1584 | 267 | pfd.events = POLLIN; |
6e242246 BH |
268 | pfd.revents = 0; |
269 | d_rfds.push_back(pfd); | |
e6a9dde5 | 270 | g_log<<Logger::Error<<"UDPv6 server bound to "<<locala.toStringWithPort()<<endl; |
12c86877 | 271 | } |
12c86877 BH |
272 | } |
273 | ||
3a56adcc | 274 | UDPNameserver::UDPNameserver( bool additional_socket ) |
12c86877 | 275 | { |
4ee30c66 | 276 | #ifdef SO_REUSEPORT |
3a56adcc | 277 | d_can_reuseport = ::arg().mustDo("reuseport"); |
4ee30c66 | 278 | #endif |
3a56adcc MZ |
279 | // Are we the main socket (false) or a rebinding using SO_REUSEPORT ? |
280 | d_additional_socket = additional_socket; | |
4ee30c66 | 281 | |
43d347d3 | 282 | if(!::arg()["local-address"].empty()) |
12c86877 | 283 | bindIPv4(); |
43d347d3 | 284 | if(!::arg()["local-ipv6"].empty()) |
12c86877 BH |
285 | bindIPv6(); |
286 | ||
43d347d3 | 287 | if(::arg()["local-address"].empty() && ::arg()["local-ipv6"].empty()) |
e6a9dde5 | 288 | g_log<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl; |
12c86877 | 289 | } |
46c6975c | 290 | |
12c86877 BH |
291 | void UDPNameserver::send(DNSPacket *p) |
292 | { | |
b552d7b1 PL |
293 | string buffer=p->getString(); |
294 | g_rs.submitResponse(*p, true); | |
46c6975c | 295 | |
2b6f1436 | 296 | struct msghdr msgh; |
2b6f1436 BH |
297 | struct iovec iov; |
298 | char cbuf[256]; | |
6865d5c0 | 299 | |
b71b60ee | 300 | fillMSGHdr(&msgh, &iov, cbuf, 0, (char*)buffer.c_str(), buffer.length(), &p->d_remote); |
2b6f1436 | 301 | |
3eae691f | 302 | msgh.msg_control=NULL; |
2b6f1436 | 303 | if(p->d_anyLocal) { |
fbe2a2e0 | 304 | addCMsgSrcAddr(&msgh, cbuf, p->d_anyLocal.get_ptr(), 0); |
2b6f1436 | 305 | } |
e6a9dde5 | 306 | DLOG(g_log<<Logger::Notice<<"Sending a packet to "<< p->getRemote() <<" ("<< buffer.length()<<" octets)"<<endl); |
1bd5361b | 307 | if(buffer.length() > p->getMaxReplyLen()) { |
dfa4c4a6 | 308 | g_log<<Logger::Error<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<<p->getMaxReplyLen()<<". Question was for "<<p->qdomain<<"|"<<p->qtype.getName()<<endl; |
12c86877 | 309 | } |
2b6f1436 | 310 | if(sendmsg(p->getSocket(), &msgh, 0) < 0) |
e6a9dde5 | 311 | g_log<<Logger::Error<<"Error sending reply with sendmsg (socket="<<p->getSocket()<<", dest="<<p->d_remote.toStringWithPort()<<"): "<<strerror(errno)<<endl; |
12c86877 BH |
312 | } |
313 | ||
2c001eb6 | 314 | DNSPacket *UDPNameserver::receive(DNSPacket *prefilled, std::string& buffer) |
2b6f1436 BH |
315 | { |
316 | ComboAddress remote; | |
317 | extern StatBag S; | |
a683e8bd | 318 | ssize_t len=-1; |
2b6f1436 | 319 | Utility::sock_t sock=-1; |
1cd66b8e | 320 | |
2b6f1436 BH |
321 | struct msghdr msgh; |
322 | struct iovec iov; | |
323 | char cbuf[256]; | |
324 | ||
b71b60ee | 325 | remote.sin6.sin6_family=AF_INET6; // make sure it is big enough |
2c001eb6 | 326 | fillMSGHdr(&msgh, &iov, cbuf, sizeof(cbuf), &buffer.at(0), buffer.size(), &remote); |
2b6f1436 BH |
327 | |
328 | int err; | |
223066c6 | 329 | vector<struct pollfd> rfds= d_rfds; |
315dd2e6 | 330 | |
28c266da | 331 | for(auto &pfd : rfds) { |
315dd2e6 | 332 | pfd.events = POLLIN; |
333 | pfd.revents = 0; | |
334 | } | |
2b6f1436 | 335 | |
ee79512a | 336 | retry:; |
337 | ||
315dd2e6 | 338 | err = poll(&rfds[0], rfds.size(), -1); |
ee79512a | 339 | if(err < 0) { |
340 | if(errno==EINTR) | |
341 | goto retry; | |
315dd2e6 | 342 | unixDie("Unable to poll for new UDP events"); |
ee79512a | 343 | } |
2b6f1436 | 344 | |
28c266da | 345 | for(auto &pfd : rfds) { |
315dd2e6 | 346 | if(pfd.revents & POLLIN) { |
347 | sock=pfd.fd; | |
315dd2e6 | 348 | if((len=recvmsg(sock, &msgh, 0)) < 0 ) { |
232f0877 | 349 | if(errno != EAGAIN) |
e6a9dde5 | 350 | g_log<<Logger::Error<<"recvfrom gave error, ignoring: "<<strerror(errno)<<endl; |
232f0877 | 351 | return 0; |
2b6f1436 | 352 | } |
315dd2e6 | 353 | break; |
2b6f1436 BH |
354 | } |
355 | } | |
315dd2e6 | 356 | if(sock==-1) |
3f81d239 | 357 | throw PDNSException("poll betrayed us! (should not happen)"); |
2b6f1436 | 358 | |
e6a9dde5 | 359 | DLOG(g_log<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl); |
81859ba5 | 360 | |
361 | BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port)); | |
362 | ||
363 | if(remote.sin4.sin_port == 0) // would generate error on responding. sin4 also works for ipv6 | |
364 | return 0; | |
2b6f1436 BH |
365 | |
366 | DNSPacket *packet; | |
367 | if(prefilled) // they gave us a preallocated packet | |
368 | packet=prefilled; | |
369 | else | |
27c0050c | 370 | packet=new DNSPacket(true); // don't forget to free it! |
9ae194e2 | 371 | |
2b6f1436 BH |
372 | packet->setSocket(sock); |
373 | packet->setRemote(&remote); | |
374 | ||
375 | ComboAddress dest; | |
376 | if(HarvestDestinationAddress(&msgh, &dest)) { | |
377 | // cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl; | |
378 | packet->d_anyLocal = dest; | |
232f0877 | 379 | } |
2b6f1436 | 380 | |
9ae194e2 | 381 | struct timeval recvtv; |
382 | if(HarvestTimestamp(&msgh, &recvtv)) { | |
383 | packet->d_dt.setTimeval(recvtv); | |
384 | } | |
385 | else | |
386 | packet->d_dt.set(); // timing | |
387 | ||
2c001eb6 | 388 | if(packet->parse(&buffer.at(0), (size_t) len)<0) { |
2b6f1436 | 389 | S.inc("corrupt-packets"); |
4eb26e0f | 390 | S.ringAccount("remotes-corrupt", packet->d_remote); |
2b6f1436 BH |
391 | |
392 | if(!prefilled) | |
393 | delete packet; | |
394 | return 0; // unable to parse | |
395 | } | |
396 | ||
397 | return packet; | |
398 | } |