]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mastercommunicator.cc
Merge pull request #5523 from rubenk/fix-typos-in-logmessage
[thirdparty/pdns.git] / pdns / mastercommunicator.cc
CommitLineData
3696224d 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
bf269e28 25#include "auth-caches.hh"
3696224d
BH
26#include "utility.hh"
27#include <errno.h>
28#include "communicator.hh"
29#include <set>
30#include <boost/utility.hpp>
fa8fd4d2 31
3696224d
BH
32#include "dnsbackend.hh"
33#include "ueberbackend.hh"
34#include "packethandler.hh"
8db49a64 35#include "nameserver.hh"
3696224d
BH
36#include "resolver.hh"
37#include "logger.hh"
38#include "dns.hh"
39#include "arguments.hh"
3696224d 40#include "packetcache.hh"
7014a23d 41#include "base64.hh"
3696224d
BH
42#include "namespaces.hh"
43
3696224d 44
abcd36a1 45void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B)
3696224d 46{
088c3342 47 bool hasQueuedItem=false;
eac85b18 48 set<string> nsset, ips;
66157226 49 DNSZoneRecord rr;
eac85b18
KM
50 FindNS fns;
51
afad964a 52
99844905 53 if (d_onlyNotify.size()) {
66157226 54 B->lookup(QType(QType::NS), di.zone);
99844905 55 while(B->get(rr))
66157226 56 nsset.insert(getRR<NSRecordContent>(rr.dr)->getNS().toString());
eac85b18 57
99844905
KD
58 for(set<string>::const_iterator j=nsset.begin();j!=nsset.end();++j) {
59 vector<string> nsips=fns.lookup(DNSName(*j), B);
60 if(nsips.empty())
66157226 61 L<<Logger::Warning<<"Unable to queue notification of domain '"<<di.zone<<"': nameservers do not resolve!"<<endl;
99844905
KD
62 else
63 for(vector<string>::const_iterator k=nsips.begin();k!=nsips.end();++k) {
64 const ComboAddress caIp(*k, 53);
65 if(!d_preventSelfNotification || !AddressIsUs(caIp)) {
66 if(!d_onlyNotify.match(&caIp))
66157226 67 L<<Logger::Info<<"Skipped notification of domain '"<<di.zone<<"' to "<<*j<<" because it does not match only-notify."<<endl;
99844905
KD
68 else
69 ips.insert(caIp.toStringWithPort());
70 }
68bb0e57 71 }
99844905 72 }
eac85b18 73
99844905 74 for(set<string>::const_iterator j=ips.begin();j!=ips.end();++j) {
66157226 75 L<<Logger::Warning<<"Queued notification of domain '"<<di.zone<<"' to "<<*j<<endl;
76 d_nq.add(di.zone,*j);
99844905
KD
77 hasQueuedItem=true;
78 }
3696224d 79 }
eac85b18 80
24d3239e 81 set<string> alsoNotify(d_alsoNotify);
abcd36a1 82 B->alsoNotifies(di.zone, &alsoNotify);
eac85b18 83
3696224d 84 for(set<string>::const_iterator j=alsoNotify.begin();j!=alsoNotify.end();++j) {
79a454ef
KM
85 try {
86 const ComboAddress caIp(*j, 53);
abcd36a1 87 L<<Logger::Warning<<"Queued also-notification of domain '"<<di.zone<<"' to "<<caIp.toStringWithPort()<<endl;
79a454ef
KM
88 if (!ips.count(caIp.toStringWithPort())) {
89 ips.insert(caIp.toStringWithPort());
abcd36a1 90 d_nq.add(di.zone, caIp.toStringWithPort());
79a454ef
KM
91 }
92 hasQueuedItem=true;
93 }
94 catch(PDNSException &e) {
abcd36a1 95 L<<Logger::Warning<<"Unparseable IP in ALSO-NOTIFY metadata of domain '"<<di.zone<<"'. Warning: "<<e.reason<<endl;
24d3239e 96 }
3696224d 97 }
eac85b18 98
088c3342 99 if (!hasQueuedItem)
abcd36a1 100 L<<Logger::Warning<<"Request to queue notification for domain '"<<di.zone<<"' was processed, but no valid nameservers or ALSO-NOTIFYs found. Not notifying!"<<endl;
3696224d
BH
101}
102
eac85b18 103
5fca2e23 104bool CommunicatorClass::notifyDomain(const DNSName &domain)
3696224d
BH
105{
106 DomainInfo di;
295c4a00
CH
107 UeberBackend B;
108 if(!B.getDomainInfo(domain, di)) {
f43c4448 109 L<<Logger::Error<<"No such domain '"<<domain<<"' in our database"<<endl;
3696224d
BH
110 return false;
111 }
abcd36a1 112 queueNotifyDomain(di, &B);
3696224d
BH
113 // call backend and tell them we sent out the notification - even though that is premature
114 di.backend->setNotified(di.id, di.serial);
115
116 return true;
117}
118
2d00c43d
BH
119void NotificationQueue::dump()
120{
121 cerr<<"Waiting for notification responses: "<<endl;
ef7cd021 122 for(NotificationRequest& nr : d_nqueue) {
af729f37 123 cerr<<nr.domain<<", "<<nr.ip<<endl;
2d00c43d
BH
124 }
125}
3696224d
BH
126
127void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
128{
129 if(!::arg().mustDo("master"))
130 return;
131
3971cf53 132 UeberBackend *B=P->getBackend();
3696224d
BH
133 vector<DomainInfo> cmdomains;
134 B->getUpdatedMasters(&cmdomains);
135
136 if(cmdomains.empty()) {
137 if(d_masterschanged)
138 L<<Logger::Warning<<"No master domains need notifications"<<endl;
139 d_masterschanged=false;
140 }
141 else {
142 d_masterschanged=true;
143 L<<Logger::Error<<cmdomains.size()<<" domain"<<(cmdomains.size()>1 ? "s" : "")<<" for which we are master need"<<
144 (cmdomains.size()>1 ? "" : "s")<<
145 " notifications"<<endl;
146 }
147
148 // figure out A records of everybody needing notification
149 // do this via the FindNS class, d_fns
150
abcd36a1 151 for(auto& di : cmdomains) {
bf269e28 152 purgeAuthCachesExact(di.zone);
abcd36a1
CH
153 queueNotifyDomain(di, B);
154 di.backend->setNotified(di.id, di.serial);
3696224d
BH
155 }
156}
157
158time_t CommunicatorClass::doNotifications()
159{
160 ComboAddress from;
33966bfb 161 Utility::socklen_t fromlen;
3696224d 162 char buffer[1500];
a683e8bd
RG
163 int sock;
164 ssize_t size;
b9ec63fa 165
a71bee29 166 // receive incoming notifications on the nonblocking socket and take them off the list
b9ec63fa 167 while(waitFor2Data(d_nsock4, d_nsock6, 0, 0, &sock) > 0) {
33966bfb 168 fromlen=sizeof(from);
0c01dd7c
BH
169 size=recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen);
170 if(size < 0)
171 break;
27c0050c 172 DNSPacket p(true);
3696224d
BH
173
174 p.setRemote(&from);
175
a683e8bd 176 if(p.parse(buffer,(size_t)size)<0) {
3696224d
BH
177 L<<Logger::Warning<<"Unable to parse SOA notification answer from "<<p.getRemote()<<endl;
178 continue;
179 }
180
181 if(p.d.rcode)
f43c4448 182 L<<Logger::Warning<<"Received unsuccessful notification report for '"<<p.qdomain<<"' from "<<from.toStringWithPort()<<", error: "<<RCode::to_s(p.d.rcode)<<endl;
33966bfb 183
c541cccb 184 if(d_nq.removeIf(from.toStringWithPort(), p.d.id, p.qdomain))
f43c4448 185 L<<Logger::Warning<<"Removed from notification list: '"<<p.qdomain<<"' to "<<from.toStringWithPort()<<" "<< (p.d.rcode ? RCode::to_s(p.d.rcode) : "(was acknowledged)")<<endl;
2d00c43d 186 else {
f43c4448 187 L<<Logger::Warning<<"Received spurious notify answer for '"<<p.qdomain<<"' from "<< from.toStringWithPort()<<endl;
2d00c43d
BH
188 //d_nq.dump();
189 }
3696224d
BH
190 }
191
192 // send out possible new notifications
675fa24c
PD
193 DNSName domain;
194 string ip;
3696224d
BH
195 uint16_t id;
196
197 bool purged;
198 while(d_nq.getOne(domain, ip, &id, purged)) {
199 if(!purged) {
200 try {
0c01dd7c 201 ComboAddress remote(ip, 53); // default to 53
16d1f609
BH
202 if((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) ||
203 (d_nsock4 < 0 && remote.sin4.sin_family == AF_INET))
204 continue; // don't try to notify what we can't!
232f0877
CH
205 if(d_preventSelfNotification && AddressIsUs(remote))
206 continue;
8db49a64 207
c541cccb 208 sendNotification(remote.sin4.sin_family == AF_INET ? d_nsock4 : d_nsock6, domain, remote, id);
4957a608 209 drillHole(domain, ip);
3696224d
BH
210 }
211 catch(ResolverException &re) {
af729f37 212 L<<Logger::Error<<"Error trying to resolve '"<<ip<<"' for notifying '"<<domain<<"' to server: "<<re.reason<<endl;
3696224d
BH
213 }
214 }
215 else
b34510a3 216 L<<Logger::Error<<"Notification for "<<domain<<" to "<<ip<<" failed after retries"<<endl;
3696224d
BH
217 }
218
219 return d_nq.earliest();
220}
221
675fa24c 222void CommunicatorClass::sendNotification(int sock, const DNSName& domain, const ComboAddress& remote, uint16_t id)
a71bee29 223{
7014a23d
AT
224 UeberBackend B;
225 vector<string> meta;
6fe866b4
AT
226 DNSName tsigkeyname;
227 DNSName tsigalgorithm;
7014a23d
AT
228 string tsigsecret64;
229 string tsigsecret;
230
231 if (B.getDomainMetadata(domain, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
9d423514 232 tsigkeyname = DNSName(meta[0]);
7014a23d
AT
233 }
234
a71bee29
PD
235 vector<uint8_t> packet;
236 DNSPacketWriter pw(packet, domain, QType::SOA, 1, Opcode::Notify);
237 pw.getHeader()->id = id;
238 pw.getHeader()->aa = true;
239
7014a23d
AT
240 if (tsigkeyname.empty() == false) {
241 B.getTSIGKey(tsigkeyname, &tsigalgorithm, &tsigsecret64);
242 TSIGRecordContent trc;
9d423514
AT
243 if (tsigalgorithm.toStringNoDot() == "hmac-md5")
244 trc.d_algoName = DNSName(tsigalgorithm.toStringNoDot() + ".sig-alg.reg.int.");
7014a23d
AT
245 else
246 trc.d_algoName = tsigalgorithm;
247 trc.d_time = time(0);
248 trc.d_fudge = 300;
249 trc.d_origID=ntohs(id);
250 trc.d_eRcode=0;
251 B64Decode(tsigsecret64, tsigsecret);
ea3816cf 252 addTSIG(pw, trc, tsigkeyname, tsigsecret, "", false);
7014a23d
AT
253 }
254
a71bee29
PD
255 if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
256 throw ResolverException("Unable to send notify to "+remote.toStringWithPort()+": "+stringerror());
257 }
258}
259
5fca2e23 260void CommunicatorClass::drillHole(const DNSName &domain, const string &ip)
3696224d
BH
261{
262 Lock l(&d_holelock);
263 d_holes[make_pair(domain,ip)]=time(0);
264}
265
5fca2e23 266bool CommunicatorClass::justNotified(const DNSName &domain, const string &ip)
3696224d
BH
267{
268 Lock l(&d_holelock);
269 if(d_holes.find(make_pair(domain,ip))==d_holes.end()) // no hole
270 return false;
271
272 if(d_holes[make_pair(domain,ip)]>time(0)-900) // recent hole
273 return true;
274
275 // do we want to purge this? XXX FIXME
276 return false;
277}
278
0c01dd7c 279void CommunicatorClass::makeNotifySockets()
3696224d 280{
f688119e 281 d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
0a2915e2 282 if(!::arg()["query-local-address6"].empty())
f688119e 283 d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
3696224d 284 else
0c01dd7c 285 d_nsock6 = -1;
3696224d
BH
286}
287
5fca2e23 288void CommunicatorClass::notify(const DNSName &domain, const string &ip)
3696224d
BH
289{
290 d_nq.add(domain, ip);
3696224d
BH
291 d_any_sem.post();
292}