2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2011 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "packetcache.hh"
28 #include "communicator.hh"
30 #include <boost/utility.hpp>
31 #include <boost/foreach.hpp>
32 #include "dnsbackend.hh"
33 #include "ueberbackend.hh"
34 #include "packethandler.hh"
35 #include "nameserver.hh"
36 #include "resolver.hh"
39 #include "arguments.hh"
40 #include "packetcache.hh"
41 #include <boost/lexical_cast.hpp>
43 #include "namespaces.hh"
46 void CommunicatorClass::queueNotifyDomain(const string
&domain
, UeberBackend
*B
)
48 bool hasQueuedItem
=false;
49 set
<string
> nsset
, ips
;
53 B
->lookup(QType(QType::NS
),domain
);
55 nsset
.insert(rr
.content
);
57 for(set
<string
>::const_iterator j
=nsset
.begin();j
!=nsset
.end();++j
) {
58 vector
<string
> nsips
=fns
.lookup(*j
, B
);
60 L
<<Logger::Warning
<<"Unable to queue notification of domain '"<<domain
<<"': nameservers do not resolve!"<<endl
;
62 for(vector
<string
>::const_iterator k
=nsips
.begin();k
!=nsips
.end();++k
) {
63 const ComboAddress
caIp(*k
, 53);
64 if(!d_preventSelfNotification
|| !AddressIsUs(caIp
)) {
65 if(!d_onlyNotify
.match(&caIp
))
66 L
<<Logger::Info
<<"Skipped notification of domain '"<<domain
<<"' to "<<*j
<<" because it does not match only-notify."<<endl
;
68 ips
.insert(caIp
.toStringWithPort());
73 for(set
<string
>::const_iterator j
=ips
.begin();j
!=ips
.end();++j
) {
74 L
<<Logger::Warning
<<"Queued notification of domain '"<<domain
<<"' to "<<*j
<<endl
;
79 set
<string
> alsoNotify(d_alsoNotify
);
80 B
->alsoNotifies(domain
, &alsoNotify
);
82 for(set
<string
>::const_iterator j
=alsoNotify
.begin();j
!=alsoNotify
.end();++j
) {
84 const ComboAddress
caIp(*j
, 53);
85 L
<<Logger::Warning
<<"Queued also-notification of domain '"<<domain
<<"' to "<<caIp
.toStringWithPort()<<endl
;
86 if (!ips
.count(caIp
.toStringWithPort())) {
87 ips
.insert(caIp
.toStringWithPort());
88 d_nq
.add(domain
, caIp
.toStringWithPort());
92 catch(PDNSException
&e
) {
93 L
<<Logger::Warning
<<"Unparseable IP in ALSO-NOTIFY metadata of domain '"<<domain
<<"'. Warning: "<<e
.reason
<<endl
;
98 L
<<Logger::Warning
<<"Request to queue notification for domain '"<<domain
<<"' was processed, but no valid nameservers or ALSO-NOTIFYs found. Not notifying!"<<endl
;
102 bool CommunicatorClass::notifyDomain(const string
&domain
)
106 if(!B
.getDomainInfo(domain
, di
)) {
107 L
<<Logger::Error
<<"No such domain '"<<domain
<<"' in our database"<<endl
;
110 queueNotifyDomain(domain
, &B
);
111 // call backend and tell them we sent out the notification - even though that is premature
112 di
.backend
->setNotified(di
.id
, di
.serial
);
117 void NotificationQueue::dump()
119 cerr
<<"Waiting for notification responses: "<<endl
;
120 BOOST_FOREACH(NotificationRequest
& nr
, d_nqueue
) {
121 cerr
<<nr
.domain
<<", "<<nr
.ip
<<endl
;
125 void CommunicatorClass::masterUpdateCheck(PacketHandler
*P
)
127 if(!::arg().mustDo("master"))
130 UeberBackend
*B
=P
->getBackend();
131 vector
<DomainInfo
> cmdomains
;
132 B
->getUpdatedMasters(&cmdomains
);
134 if(cmdomains
.empty()) {
136 L
<<Logger::Warning
<<"No master domains need notifications"<<endl
;
137 d_masterschanged
=false;
140 d_masterschanged
=true;
141 L
<<Logger::Error
<<cmdomains
.size()<<" domain"<<(cmdomains
.size()>1 ? "s" : "")<<" for which we are master need"<<
142 (cmdomains
.size()>1 ? "" : "s")<<
143 " notifications"<<endl
;
146 // figure out A records of everybody needing notification
147 // do this via the FindNS class, d_fns
149 for(vector
<DomainInfo
>::const_iterator i
=cmdomains
.begin();i
!=cmdomains
.end();++i
) {
150 extern PacketCache PC
;
151 PC
.purge(i
->zone
); // fixes cvstrac ticket #30
152 queueNotifyDomain(i
->zone
,P
->getBackend());
153 i
->backend
->setNotified(i
->id
,i
->serial
);
157 time_t CommunicatorClass::doNotifications()
160 Utility::socklen_t fromlen
;
164 // receive incoming notifications on the nonblocking socket and take them off the list
165 while(waitFor2Data(d_nsock4
, d_nsock6
, 0, 0, &sock
) > 0) {
166 fromlen
=sizeof(from
);
167 size
=recvfrom(sock
,buffer
,sizeof(buffer
),0,(struct sockaddr
*)&from
,&fromlen
);
174 if(p
.parse(buffer
,size
)<0) {
175 L
<<Logger::Warning
<<"Unable to parse SOA notification answer from "<<p
.getRemote()<<endl
;
180 L
<<Logger::Warning
<<"Received unsuccessful notification report for '"<<p
.qdomain
<<"' from "<<from
.toStringWithPort()<<", error: "<<RCode::to_s(p
.d
.rcode
)<<endl
;
182 if(d_nq
.removeIf(from
.toStringWithPort(), p
.d
.id
, p
.qdomain
))
183 L
<<Logger::Warning
<<"Removed from notification list: '"<<p
.qdomain
<<"' to "<<from
.toStringWithPort()<<" "<< (p
.d
.rcode
? RCode::to_s(p
.d
.rcode
) : "(was acknowledged)")<<endl
;
185 L
<<Logger::Warning
<<"Received spurious notify answer for '"<<p
.qdomain
<<"' from "<< from
.toStringWithPort()<<endl
;
190 // send out possible new notifications
195 while(d_nq
.getOne(domain
, ip
, &id
, purged
)) {
198 ComboAddress
remote(ip
, 53); // default to 53
199 if((d_nsock6
< 0 && remote
.sin4
.sin_family
== AF_INET6
) ||
200 (d_nsock4
< 0 && remote
.sin4
.sin_family
== AF_INET
))
201 continue; // don't try to notify what we can't!
202 if(d_preventSelfNotification
&& AddressIsUs(remote
))
205 sendNotification(remote
.sin4
.sin_family
== AF_INET
? d_nsock4
: d_nsock6
, domain
, remote
, id
);
206 drillHole(domain
, ip
);
208 catch(ResolverException
&re
) {
209 L
<<Logger::Error
<<"Error trying to resolve '"+ip
+"' for notifying '"+domain
+"' to server: "+re
.reason
<<endl
;
213 L
<<Logger::Error
<<Logger::NTLog
<<"Notification for "<<domain
<<" to "<<ip
<<" failed after retries"<<endl
;
216 return d_nq
.earliest();
219 void CommunicatorClass::sendNotification(int sock
, const string
& domain
, const ComboAddress
& remote
, uint16_t id
)
221 vector
<uint8_t> packet
;
222 DNSPacketWriter
pw(packet
, domain
, QType::SOA
, 1, Opcode::Notify
);
223 pw
.getHeader()->id
= id
;
224 pw
.getHeader()->aa
= true;
226 if(sendto(sock
, &packet
[0], packet
.size(), 0, (struct sockaddr
*)(&remote
), remote
.getSocklen()) < 0) {
227 throw ResolverException("Unable to send notify to "+remote
.toStringWithPort()+": "+stringerror());
231 void CommunicatorClass::drillHole(const string
&domain
, const string
&ip
)
234 d_holes
[make_pair(domain
,ip
)]=time(0);
237 bool CommunicatorClass::justNotified(const string
&domain
, const string
&ip
)
240 if(d_holes
.find(make_pair(domain
,ip
))==d_holes
.end()) // no hole
243 if(d_holes
[make_pair(domain
,ip
)]>time(0)-900) // recent hole
246 // do we want to purge this? XXX FIXME
250 void CommunicatorClass::makeNotifySockets()
252 d_nsock4
= makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
253 if(!::arg()["query-local-address6"].empty())
254 d_nsock6
= makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
259 void CommunicatorClass::notify(const string
&domain
, const string
&ip
)
261 d_nq
.add(domain
, ip
);