]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/nameserver.cc
bf1a2c6a94d3ebf726f94987e55959ade65c9fa3
2 Copyright (C) 2002 - 2012 PowerDNS.COM BV
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License version 2 as
6 published by the Free Software Foundation.
8 Additionally, the license of this program contains a special
9 exception which allows to distribute the program in binary form when
10 it is linked against OpenSSL.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/types.h>
33 #include "responsestats.hh"
36 #include "dnsbackend.hh"
37 #include "dnspacket.hh"
38 #include "nameserver.hh"
39 #include "distributor.hh"
41 #include "arguments.hh"
44 #include "namespaces.hh"
49 PowerDNS is a very versatile nameserver that can answer questions from different backends. To implement your
50 own backend, see the documentation for the DNSBackend class.
52 \section copyright Copyright and License
53 PowerDNS is (C) 2001-2008 PowerDNS.COM BV. It is distributed according to the terms of the General Public License version 2.
55 \section overview High level overview
57 The Distributor contains a configurable number of PacketHandler instances, each in its own thread, for connection pooling.
58 PacketHandler instances are recycled of they let escape an PDNSException.
60 The PacketHandler implements the RFC1034 algorithm and converts question packets into DNSBackend queries.
62 A DNSBackend is an entity that returns DNSResourceRecord objects in return to explicit questions for domains with a specified QType
64 PowerDNS uses the UeberBackend as its DNSBackend. The UeberBackend by default has no DNSBackends within itself, those are loaded
65 using the pdns_control tool. This way DNSBackend implementations can be kept completely separate (but they often aren't).s
67 If one or more DNSBackends are loaded, the UeberBackend fields the queries to all of them until one answers.
69 \section TCP TCP Operations
71 The TCP operation runs within a single thread called tcpreceiver(), that also queries the PacketHandler.
73 \section Cache Caching
75 On its own, this setup is not suitable for high performance operations. A single DNS query can turn into many DNSBackend questions,
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
77 asking this question. If so, the entire Distributor is shunted, and the answer is sent back *directly*, within a few microseconds.
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.
84 The main() of PowerDNS can be found in receiver.cc - start reading there for further insights into the operation of the nameserver
87 vector
<ComboAddress
> g_localaddresses
; // not static, our unit tests need to poke this
89 void UDPNameserver::bindIPv4()
92 stringtok(locals
,::arg()["local-address"]," ,");
96 throw PDNSException("No local address specified");
99 for(vector
<string
>::const_iterator i
=locals
.begin();i
!=locals
.end();++i
) {
100 string
localname(*i
);
103 s
=socket(AF_INET
,SOCK_DGRAM
,0);
106 L
<<Logger::Error
<<"Unable to acquire UDP socket: "+string(strerror(errno
)) << endl
;
107 throw PDNSException("Unable to acquire a UDP socket: "+string(strerror(errno
)));
112 if(!setNonBlocking(s
))
113 throw PDNSException("Unable to set UDP socket to non-blocking: "+stringerror());
115 memset(&locala
,0,sizeof(locala
));
116 locala
.sin4
.sin_family
=AF_INET
;
118 if(localname
=="0.0.0.0")
119 setsockopt(s
, IPPROTO_IP
, GEN_IP_PKTINFO
, &one
, sizeof(one
));
121 if (!setSocketTimestamps(s
))
122 L
<<Logger::Warning
<<"Unable to enable timestamp reporting for socket"<<endl
;
125 if( d_can_reuseport
)
126 if( setsockopt(s
, SOL_SOCKET
, SO_REUSEPORT
, &one
, sizeof(one
)) )
127 d_can_reuseport
= false;
130 if( ::arg().mustDo("non-local-bind") )
131 Utility::setBindAny(AF_INET
, s
);
133 locala
=ComboAddress(localname
, ::arg().asNum("local-port"));
134 if(locala
.sin4
.sin_family
!= AF_INET
)
135 throw PDNSException("Attempting to bind IPv4 socket to IPv6 address");
137 if( !d_additional_socket
)
138 g_localaddresses
.push_back(locala
);
140 if(::bind(s
, (sockaddr
*)&locala
, locala
.getSocklen()) < 0) {
141 string binderror
= strerror(errno
);
143 if( errno
== EADDRNOTAVAIL
&& ! ::arg().mustDo("local-address-nonexist-fail") ) {
144 L
<<Logger::Error
<<"IPv4 Address " << localname
<< " does not exist on this server - skipping UDP bind" << endl
;
147 L
<<Logger::Error
<<"Unable to bind UDP socket to '"+locala
.toStringWithPort()+"': "<<binderror
<<endl
;
148 throw PDNSException("Unable to bind to UDP socket");
151 d_sockets
.push_back(s
);
152 L
<<Logger::Error
<<"UDP server bound to "<<locala
.toStringWithPort()<<endl
;
157 d_rfds
.push_back(pfd
);
161 bool AddressIsUs(const ComboAddress
& remote
)
163 for(const ComboAddress
& us
: g_localaddresses
) {
166 if(IsAnyAddress(us
)) {
167 int s
= socket(remote
.sin4
.sin_family
, SOCK_DGRAM
, 0);
171 if(connect(s
, (struct sockaddr
*)&remote
, remote
.getSocklen()) < 0) {
176 ComboAddress actualLocal
;
177 actualLocal
.sin4
.sin_family
= remote
.sin4
.sin_family
;
178 socklen_t socklen
= actualLocal
.getSocklen();
180 if(getsockname(s
, (struct sockaddr
*) &actualLocal
, &socklen
) < 0) {
185 actualLocal
.sin4
.sin_port
= us
.sin4
.sin_port
;
186 if(actualLocal
== remote
)
194 void UDPNameserver::bindIPv6()
196 vector
<string
> locals
;
197 stringtok(locals
,::arg()["local-ipv6"]," ,");
204 for(vector
<string
>::const_iterator i
=locals
.begin();i
!=locals
.end();++i
) {
205 string
localname(*i
);
207 s
=socket(AF_INET6
,SOCK_DGRAM
,0);
209 if( errno
== EAFNOSUPPORT
) {
210 L
<<Logger::Error
<<"IPv6 Address Family is not supported - skipping UDPv6 bind" << endl
;
213 L
<<Logger::Error
<<"Unable to acquire a UDPv6 socket: "+string(strerror(errno
)) << endl
;
214 throw PDNSException("Unable to acquire a UDPv6 socket: "+string(strerror(errno
)));
219 if(!setNonBlocking(s
))
220 throw PDNSException("Unable to set UDPv6 socket to non-blocking: "+stringerror());
222 ComboAddress
locala(localname
, ::arg().asNum("local-port"));
224 if(IsAnyAddress(locala
)) {
225 setsockopt(s
, IPPROTO_IP
, GEN_IP_PKTINFO
, &one
, sizeof(one
)); // linux supports this, so why not - might fail on other systems
226 #ifdef IPV6_RECVPKTINFO
227 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
229 setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
)); // if this fails, we report an error in tcpreceiver too
232 if (!setSocketTimestamps(s
))
233 L
<<Logger::Warning
<<"Unable to enable timestamp reporting for socket"<<endl
;
236 if( d_can_reuseport
)
237 if( setsockopt(s
, SOL_SOCKET
, SO_REUSEPORT
, &one
, sizeof(one
)) )
238 d_can_reuseport
= false;
241 if( ::arg().mustDo("non-local-bind") )
242 Utility::setBindAny(AF_INET6
, s
);
244 if( !d_additional_socket
)
245 g_localaddresses
.push_back(locala
);
246 if(::bind(s
, (sockaddr
*)&locala
, sizeof(locala
))<0) {
248 if( errno
== EADDRNOTAVAIL
&& ! ::arg().mustDo("local-ipv6-nonexist-fail") ) {
249 L
<<Logger::Error
<<"IPv6 Address " << localname
<< " does not exist on this server - skipping UDP bind" << endl
;
252 L
<<Logger::Error
<<"Unable to bind to UDPv6 socket "<< localname
<<": "<<strerror(errno
)<<endl
;
253 throw PDNSException("Unable to bind to UDPv6 socket");
256 d_sockets
.push_back(s
);
261 d_rfds
.push_back(pfd
);
262 L
<<Logger::Error
<<"UDPv6 server bound to "<<locala
.toStringWithPort()<<endl
;
266 UDPNameserver::UDPNameserver( bool additional_socket
)
269 d_can_reuseport
= ::arg().mustDo("reuseport");
271 // Are we the main socket (false) or a rebinding using SO_REUSEPORT ?
272 d_additional_socket
= additional_socket
;
274 if(!::arg()["local-address"].empty())
276 if(!::arg()["local-ipv6"].empty())
279 if(::arg()["local-address"].empty() && ::arg()["local-ipv6"].empty())
280 L
<<Logger::Critical
<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl
;
283 void UDPNameserver::send(DNSPacket
*p
)
285 string buffer
=p
->getString();
286 g_rs
.submitResponse(*p
, true);
292 fillMSGHdr(&msgh
, &iov
, cbuf
, 0, (char*)buffer
.c_str(), buffer
.length(), &p
->d_remote
);
294 msgh
.msg_control
=NULL
;
296 addCMsgSrcAddr(&msgh
, cbuf
, p
->d_anyLocal
.get_ptr(), 0);
298 DLOG(L
<<Logger::Notice
<<"Sending a packet to "<< p
->getRemote() <<" ("<< buffer
.length()<<" octets)"<<endl
);
299 if(buffer
.length() > p
->getMaxReplyLen()) {
300 L
<<Logger::Error
<<"Weird, trying to send a message that needs truncation, "<< buffer
.length()<<" > "<<p
->getMaxReplyLen()<<endl
;
302 if(sendmsg(p
->getSocket(), &msgh
, 0) < 0)
303 L
<<Logger::Error
<<"Error sending reply with sendmsg (socket="<<p
->getSocket()<<", dest="<<p
->d_remote
.toStringWithPort()<<"): "<<strerror(errno
)<<endl
;
306 DNSPacket
*UDPNameserver::receive(DNSPacket
*prefilled
)
311 char mesg
[DNSPacket::s_udpTruncationThreshold
];
312 Utility::sock_t sock
=-1;
318 remote
.sin6
.sin6_family
=AF_INET6
; // make sure it is big enough
319 fillMSGHdr(&msgh
, &iov
, cbuf
, sizeof(cbuf
), mesg
, sizeof(mesg
), &remote
);
322 vector
<struct pollfd
> rfds
= d_rfds
;
324 for(struct pollfd
&pfd
: rfds
) {
331 err
= poll(&rfds
[0], rfds
.size(), -1);
335 unixDie("Unable to poll for new UDP events");
338 for(struct pollfd
&pfd
: rfds
) {
339 if(pfd
.revents
& POLLIN
) {
341 if((len
=recvmsg(sock
, &msgh
, 0)) < 0 ) {
343 L
<<Logger::Error
<<"recvfrom gave error, ignoring: "<<strerror(errno
)<<endl
;
350 throw PDNSException("poll betrayed us! (should not happen)");
352 DLOG(L
<<"Received a packet " << len
<<" bytes long from "<< remote
.toString()<<endl
);
354 BOOST_STATIC_ASSERT(offsetof(sockaddr_in
, sin_port
) == offsetof(sockaddr_in6
, sin6_port
));
356 if(remote
.sin4
.sin_port
== 0) // would generate error on responding. sin4 also works for ipv6
360 if(prefilled
) // they gave us a preallocated packet
363 packet
=new DNSPacket
; // don't forget to free it!
365 packet
->setSocket(sock
);
366 packet
->setRemote(&remote
);
369 if(HarvestDestinationAddress(&msgh
, &dest
)) {
370 // cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl;
371 packet
->d_anyLocal
= dest
;
374 struct timeval recvtv
;
375 if(HarvestTimestamp(&msgh
, &recvtv
)) {
376 packet
->d_dt
.setTimeval(recvtv
);
379 packet
->d_dt
.set(); // timing
381 if(packet
->parse(mesg
, (size_t) len
)<0) {
382 S
.inc("corrupt-packets");
383 S
.ringAccount("remotes-corrupt", packet
->d_remote
);
387 return 0; // unable to parse