]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/mastercommunicator.cc
Include config.h only in .cc files
[thirdparty/pdns.git] / pdns / mastercommunicator.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2011 PowerDNS.COM BV
4
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;
8
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.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "packetcache.hh"
26 #include "utility.hh"
27 #include <errno.h>
28 #include "communicator.hh"
29 #include <set>
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"
37 #include "logger.hh"
38 #include "dns.hh"
39 #include "arguments.hh"
40 #include "packetcache.hh"
41 #include <boost/lexical_cast.hpp>
42
43 #include "namespaces.hh"
44
45
46 void CommunicatorClass::queueNotifyDomain(const string &domain, UeberBackend *B)
47 {
48 bool hasQueuedItem=false;
49 set<string> nsset, ips;
50 DNSResourceRecord rr;
51 FindNS fns;
52
53 B->lookup(QType(QType::NS),domain);
54 while(B->get(rr))
55 nsset.insert(rr.content);
56
57 for(set<string>::const_iterator j=nsset.begin();j!=nsset.end();++j) {
58 vector<string> nsips=fns.lookup(*j, B);
59 if(nsips.empty())
60 L<<Logger::Warning<<"Unable to queue notification of domain '"<<domain<<"': nameservers do not resolve!"<<endl;
61 else
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;
67 else
68 ips.insert(caIp.toStringWithPort());
69 }
70 }
71 }
72
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;
75 d_nq.add(domain,*j);
76 hasQueuedItem=true;
77 }
78
79 set<string> alsoNotify(d_alsoNotify);
80 B->alsoNotifies(domain, &alsoNotify);
81
82 for(set<string>::const_iterator j=alsoNotify.begin();j!=alsoNotify.end();++j) {
83 try {
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());
89 }
90 hasQueuedItem=true;
91 }
92 catch(PDNSException &e) {
93 L<<Logger::Warning<<"Unparseable IP in ALSO-NOTIFY metadata of domain '"<<domain<<"'. Warning: "<<e.reason<<endl;
94 }
95 }
96
97 if (!hasQueuedItem)
98 L<<Logger::Warning<<"Request to queue notification for domain '"<<domain<<"' was processed, but no valid nameservers or ALSO-NOTIFYs found. Not notifying!"<<endl;
99 }
100
101
102 bool CommunicatorClass::notifyDomain(const string &domain)
103 {
104 DomainInfo di;
105 UeberBackend B;
106 if(!B.getDomainInfo(domain, di)) {
107 L<<Logger::Error<<"No such domain '"<<domain<<"' in our database"<<endl;
108 return false;
109 }
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);
113
114 return true;
115 }
116
117 void NotificationQueue::dump()
118 {
119 cerr<<"Waiting for notification responses: "<<endl;
120 BOOST_FOREACH(NotificationRequest& nr, d_nqueue) {
121 cerr<<nr.domain<<", "<<nr.ip<<endl;
122 }
123 }
124
125 void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
126 {
127 if(!::arg().mustDo("master"))
128 return;
129
130 UeberBackend *B=P->getBackend();
131 vector<DomainInfo> cmdomains;
132 B->getUpdatedMasters(&cmdomains);
133
134 if(cmdomains.empty()) {
135 if(d_masterschanged)
136 L<<Logger::Warning<<"No master domains need notifications"<<endl;
137 d_masterschanged=false;
138 }
139 else {
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;
144 }
145
146 // figure out A records of everybody needing notification
147 // do this via the FindNS class, d_fns
148
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);
154 }
155 }
156
157 time_t CommunicatorClass::doNotifications()
158 {
159 ComboAddress from;
160 Utility::socklen_t fromlen;
161 char buffer[1500];
162 int size, sock;
163
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);
168 if(size < 0)
169 break;
170 DNSPacket p;
171
172 p.setRemote(&from);
173
174 if(p.parse(buffer,size)<0) {
175 L<<Logger::Warning<<"Unable to parse SOA notification answer from "<<p.getRemote()<<endl;
176 continue;
177 }
178
179 if(p.d.rcode)
180 L<<Logger::Warning<<"Received unsuccessful notification report for '"<<p.qdomain<<"' from "<<from.toStringWithPort()<<", error: "<<RCode::to_s(p.d.rcode)<<endl;
181
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;
184 else {
185 L<<Logger::Warning<<"Received spurious notify answer for '"<<p.qdomain<<"' from "<< from.toStringWithPort()<<endl;
186 //d_nq.dump();
187 }
188 }
189
190 // send out possible new notifications
191 string domain, ip;
192 uint16_t id;
193
194 bool purged;
195 while(d_nq.getOne(domain, ip, &id, purged)) {
196 if(!purged) {
197 try {
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))
203 continue;
204
205 sendNotification(remote.sin4.sin_family == AF_INET ? d_nsock4 : d_nsock6, domain, remote, id);
206 drillHole(domain, ip);
207 }
208 catch(ResolverException &re) {
209 L<<Logger::Error<<"Error trying to resolve '"+ip+"' for notifying '"+domain+"' to server: "+re.reason<<endl;
210 }
211 }
212 else
213 L<<Logger::Error<<Logger::NTLog<<"Notification for "<<domain<<" to "<<ip<<" failed after retries"<<endl;
214 }
215
216 return d_nq.earliest();
217 }
218
219 void CommunicatorClass::sendNotification(int sock, const string& domain, const ComboAddress& remote, uint16_t id)
220 {
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;
225
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());
228 }
229 }
230
231 void CommunicatorClass::drillHole(const string &domain, const string &ip)
232 {
233 Lock l(&d_holelock);
234 d_holes[make_pair(domain,ip)]=time(0);
235 }
236
237 bool CommunicatorClass::justNotified(const string &domain, const string &ip)
238 {
239 Lock l(&d_holelock);
240 if(d_holes.find(make_pair(domain,ip))==d_holes.end()) // no hole
241 return false;
242
243 if(d_holes[make_pair(domain,ip)]>time(0)-900) // recent hole
244 return true;
245
246 // do we want to purge this? XXX FIXME
247 return false;
248 }
249
250 void CommunicatorClass::makeNotifySockets()
251 {
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);
255 else
256 d_nsock6 = -1;
257 }
258
259 void CommunicatorClass::notify(const string &domain, const string &ip)
260 {
261 d_nq.add(domain, ip);
262 d_any_sem.post();
263 }
264