]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mastercommunicator.cc
clang-tidy: modernize deprecated header invarious places
[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"
9931f46f 26#include "auth-zonecache.hh"
3696224d 27#include "utility.hh"
dc6aa7f5 28#include <cerrno>
3696224d
BH
29#include "communicator.hh"
30#include <set>
31#include <boost/utility.hpp>
fa8fd4d2 32
3696224d
BH
33#include "dnsbackend.hh"
34#include "ueberbackend.hh"
35#include "packethandler.hh"
8db49a64 36#include "nameserver.hh"
3696224d
BH
37#include "resolver.hh"
38#include "logger.hh"
39#include "dns.hh"
40#include "arguments.hh"
3696224d 41#include "packetcache.hh"
7014a23d 42#include "base64.hh"
3696224d 43#include "namespaces.hh"
20829585 44#include "query-local-address.hh"
3696224d 45
3696224d 46
abcd36a1 47void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B)
3696224d 48{
088c3342 49 bool hasQueuedItem=false;
1fd89ec2
PD
50 set<string> ips;
51 set<DNSName> nsset;
66157226 52 DNSZoneRecord rr;
eac85b18
KM
53 FindNS fns;
54
410f8bd3 55 try {
99844905 56 if (d_onlyNotify.size()) {
4bd5eb80 57 B->lookup(QType(QType::NS), di.zone, di.id);
99844905 58 while(B->get(rr))
1fd89ec2 59 nsset.insert(getRR<NSRecordContent>(rr.dr)->getNS());
eac85b18 60
1fd89ec2
PD
61 for(const auto & ns : nsset) {
62 vector<string> nsips=fns.lookup(ns, B);
99844905 63 if(nsips.empty())
1fd89ec2 64 g_log<<Logger::Warning<<"Unable to queue notification of domain '"<<di.zone<<"' to nameserver '"<<ns<<"': nameserver does not resolve!"<<endl;
99844905 65 else
d7f67000
RP
66 for(const auto & nsip : nsips) {
67 const ComboAddress caIp(nsip, 53);
99844905
KD
68 if(!d_preventSelfNotification || !AddressIsUs(caIp)) {
69 if(!d_onlyNotify.match(&caIp))
1fd89ec2 70 g_log<<Logger::Notice<<"Skipped notification of domain '"<<di.zone<<"' to "<<ns<<" because "<<caIp<<" does not match only-notify."<<endl;
99844905
KD
71 else
72 ips.insert(caIp.toStringWithPort());
73 }
68bb0e57 74 }
99844905 75 }
eac85b18 76
d7f67000
RP
77 for(const auto & ip : ips) {
78 g_log<<Logger::Notice<<"Queued notification of domain '"<<di.zone<<"' to "<<ip<<endl;
79 d_nq.add(di.zone,ip);
99844905
KD
80 hasQueuedItem=true;
81 }
3696224d 82 }
410f8bd3
PD
83 }
84 catch (PDNSException &ae) {
85 g_log << Logger::Error << "Error looking up name servers for " << di.zone << ", cannot notify: " << ae.reason << endl;
86 return;
87 }
88 catch (std::exception &e) {
89 g_log << Logger::Error << "Error looking up name servers for " << di.zone << ", cannot notify: " << e.what() << endl;
90 return;
91 }
92
eac85b18 93
24d3239e 94 set<string> alsoNotify(d_alsoNotify);
abcd36a1 95 B->alsoNotifies(di.zone, &alsoNotify);
eac85b18 96
d7f67000 97 for(const auto & j : alsoNotify) {
79a454ef 98 try {
d7f67000 99 const ComboAddress caIp(j, 53);
c2923b81 100 g_log<<Logger::Notice<<"Queued also-notification of domain '"<<di.zone<<"' to "<<caIp.toStringWithPort()<<endl;
79a454ef
KM
101 if (!ips.count(caIp.toStringWithPort())) {
102 ips.insert(caIp.toStringWithPort());
abcd36a1 103 d_nq.add(di.zone, caIp.toStringWithPort());
79a454ef
KM
104 }
105 hasQueuedItem=true;
106 }
107 catch(PDNSException &e) {
e6a9dde5 108 g_log<<Logger::Warning<<"Unparseable IP in ALSO-NOTIFY metadata of domain '"<<di.zone<<"'. Warning: "<<e.reason<<endl;
24d3239e 109 }
3696224d 110 }
eac85b18 111
088c3342 112 if (!hasQueuedItem)
e6a9dde5 113 g_log<<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
114}
115
eac85b18 116
3497eb18 117bool CommunicatorClass::notifyDomain(const DNSName &domain, UeberBackend* B)
3696224d
BH
118{
119 DomainInfo di;
3497eb18 120 if(!B->getDomainInfo(domain, di)) {
c2923b81 121 g_log<<Logger::Warning<<"No such domain '"<<domain<<"' in our database"<<endl;
3696224d
BH
122 return false;
123 }
3497eb18 124 queueNotifyDomain(di, B);
f159e137
KM
125 // call backend and tell them we sent out the notification - even though that is premature
126 if (di.serial != di.notified_serial)
127 di.backend->setNotified(di.id, di.serial);
3696224d 128
de42d2df 129 return true;
3696224d
BH
130}
131
2d00c43d
BH
132void NotificationQueue::dump()
133{
134 cerr<<"Waiting for notification responses: "<<endl;
ef7cd021 135 for(NotificationRequest& nr : d_nqueue) {
af729f37 136 cerr<<nr.domain<<", "<<nr.ip<<endl;
2d00c43d
BH
137 }
138}
3696224d 139
ddeea7a6
KM
140void CommunicatorClass::getUpdatedProducers(UeberBackend* B, vector<DomainInfo>& domains, const std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
141{
142 std::string metaHash;
143 std::string mapHash;
144 for (auto& ch : catalogHashes) {
ddeea7a6
KM
145 if (!catalogs.count(ch.first)) {
146 g_log << Logger::Warning << "orphaned member zones found with catalog '" << ch.first << "'" << endl;
147 continue;
148 }
149
150 if (!B->getDomainMetadata(ch.first, "CATALOG-HASH", metaHash)) {
151 metaHash.clear();
152 }
153
154 mapHash = Base64Encode(ch.second.digest());
155 if (mapHash != metaHash) {
156 DomainInfo di;
157 if (B->getDomainInfo(ch.first, di)) {
158 if (di.kind != DomainInfo::Producer) {
159 g_log << Logger::Warning << "zone '" << di.zone << "' is no producer zone" << endl;
160 continue;
161 }
162
163 B->setDomainMetadata(di.zone, "CATALOG-HASH", mapHash);
164
98d26417 165 g_log << Logger::Warning << "new CATALOG-HASH '" << mapHash << "' for zone '" << di.zone << "'" << endl;
ddeea7a6
KM
166
167 SOAData sd;
168 if (!B->getSOAUncached(di.zone, sd)) {
169 g_log << Logger::Warning << "SOA lookup failed for producer zone '" << di.zone << "'" << endl;
170 continue;
171 }
172
173 DNSResourceRecord rr;
174 makeIncreasedSOARecord(sd, "EPOCH", "", rr);
175 di.backend->startTransaction(sd.qname, -1);
176 if (!di.backend->replaceRRSet(di.id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
177 di.backend->abortTransaction();
178 throw PDNSException("backend hosting producer zone '" + sd.qname.toLogString() + "' does not support editing records");
179 }
180 di.backend->commitTransaction();
181
182 domains.emplace_back(di);
183 }
184 }
185 }
186}
187
3696224d
BH
188void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
189{
2c70d169 190 if(!::arg().mustDo("primary"))
ddeea7a6 191 return;
3696224d 192
3971cf53 193 UeberBackend *B=P->getBackend();
3696224d 194 vector<DomainInfo> cmdomains;
ddeea7a6
KM
195 std::unordered_set<DNSName> catalogs;
196 CatalogHashMap catalogHashes;
197 B->getUpdatedMasters(cmdomains, catalogs, catalogHashes);
198 getUpdatedProducers(B, cmdomains, catalogs, catalogHashes);
199
3696224d 200 if(cmdomains.empty()) {
ddeea7a6 201 g_log << Logger::Info << "no primary or producer domains need notifications" << endl;
3696224d
BH
202 }
203 else {
ddeea7a6 204 g_log << Logger::Info << cmdomains.size() << " domain" << addS(cmdomains.size()) << " for which we are primary or consumer need" << addS(cmdomains.size()) << " notifications" << endl;
3696224d
BH
205 }
206
abcd36a1 207 for(auto& di : cmdomains) {
bf269e28 208 purgeAuthCachesExact(di.zone);
9931f46f 209 g_zoneCache.add(di.zone, di.id);
abcd36a1
CH
210 queueNotifyDomain(di, B);
211 di.backend->setNotified(di.id, di.serial);
3696224d
BH
212 }
213}
214
de42d2df 215time_t CommunicatorClass::doNotifications(PacketHandler* P)
3696224d 216{
de42d2df 217 UeberBackend* B = P->getBackend();
3696224d 218 ComboAddress from;
3696224d 219 char buffer[1500];
a683e8bd 220 int sock;
d529011f 221 set<int> fds = {d_nsock4, d_nsock6};
b9ec63fa 222
a71bee29 223 // receive incoming notifications on the nonblocking socket and take them off the list
de42d2df 224 while (waitForMultiData(fds, 0, 0, &sock) > 0) {
bd6223db
FM
225 Utility::socklen_t fromlen = sizeof(from);
226 const auto size = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&from, &fromlen);
227 if (size < 0) {
0c01dd7c 228 break;
bd6223db 229 }
27c0050c 230 DNSPacket p(true);
3696224d
BH
231
232 p.setRemote(&from);
233
de42d2df
FM
234 if (p.parse(buffer, (size_t)size) < 0) {
235 g_log << Logger::Warning << "Unable to parse SOA notification answer from " << p.getRemote() << endl;
3696224d
BH
236 continue;
237 }
238
bd6223db 239 if (p.d.rcode) {
de42d2df 240 g_log << Logger::Warning << "Received unsuccessful notification report for '" << p.qdomain << "' from " << from.toStringWithPort() << ", error: " << RCode::to_s(p.d.rcode) << endl;
bd6223db 241 }
33966bfb 242
792548c4 243 if (d_nq.removeIf(from, p.d.id, p.qdomain)) {
de42d2df 244 g_log << Logger::Notice << "Removed from notification list: '" << p.qdomain << "' to " << from.toStringWithPort() << " " << (p.d.rcode ? RCode::to_s(p.d.rcode) : "(was acknowledged)") << endl;
bd6223db 245 }
2d00c43d 246 else {
de42d2df
FM
247 g_log << Logger::Warning << "Received spurious notify answer for '" << p.qdomain << "' from " << from.toStringWithPort() << endl;
248 // d_nq.dump();
2d00c43d 249 }
3696224d
BH
250 }
251
252 // send out possible new notifications
675fa24c
PD
253 DNSName domain;
254 string ip;
de42d2df 255 uint16_t id = 0;
3696224d
BH
256
257 bool purged;
de42d2df
FM
258 while (d_nq.getOne(domain, ip, &id, purged)) {
259 if (!purged) {
3696224d 260 try {
0c01dd7c 261 ComboAddress remote(ip, 53); // default to 53
de42d2df
FM
262 if ((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) || (d_nsock4 < 0 && remote.sin4.sin_family == AF_INET)) {
263 g_log << Logger::Warning << "Unable to notify " << remote.toStringWithPort() << " for domain '" << domain << "', address family is disabled. Is an IPv" << (remote.sin4.sin_family == AF_INET ? "4" : "6") << " address set in query-local-address?" << endl;
792548c4 264 d_nq.removeIf(remote, id, domain); // Remove, we'll never be able to notify
de42d2df 265 continue; // don't try to notify what we can't!
9d36589a 266 }
bd6223db 267 if (d_preventSelfNotification && AddressIsUs(remote)) {
232f0877 268 continue;
bd6223db 269 }
8db49a64 270
3497eb18 271 sendNotification(remote.sin4.sin_family == AF_INET ? d_nsock4 : d_nsock6, domain, remote, id, B);
4957a608 272 drillHole(domain, ip);
3696224d 273 }
de42d2df
FM
274 catch (ResolverException& re) {
275 g_log << Logger::Warning << "Error trying to resolve '" << ip << "' for notifying '" << domain << "' to server: " << re.reason << endl;
3696224d
BH
276 }
277 }
bd6223db 278 else {
de42d2df 279 g_log << Logger::Warning << "Notification for " << domain << " to " << ip << " failed after retries" << endl;
bd6223db 280 }
3696224d
BH
281 }
282
283 return d_nq.earliest();
284}
285
3497eb18 286void CommunicatorClass::sendNotification(int sock, const DNSName& domain, const ComboAddress& remote, uint16_t id, UeberBackend *B)
a71bee29 287{
7014a23d 288 vector<string> meta;
6fe866b4
AT
289 DNSName tsigkeyname;
290 DNSName tsigalgorithm;
7014a23d
AT
291 string tsigsecret64;
292 string tsigsecret;
293
3497eb18 294 if (::arg().mustDo("send-signed-notify") && B->getDomainMetadata(domain, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
9d423514 295 tsigkeyname = DNSName(meta[0]);
7014a23d
AT
296 }
297
a71bee29
PD
298 vector<uint8_t> packet;
299 DNSPacketWriter pw(packet, domain, QType::SOA, 1, Opcode::Notify);
300 pw.getHeader()->id = id;
de42d2df 301 pw.getHeader()->aa = true;
a71bee29 302
7014a23d 303 if (tsigkeyname.empty() == false) {
40361bf2
KM
304 if (!B->getTSIGKey(tsigkeyname, tsigalgorithm, tsigsecret64)) {
305 g_log << Logger::Error << "TSIG key '" << tsigkeyname << "' for domain '" << domain << "' not found" << endl;
fba65bff
PL
306 return;
307 }
7014a23d 308 TSIGRecordContent trc;
9d423514
AT
309 if (tsigalgorithm.toStringNoDot() == "hmac-md5")
310 trc.d_algoName = DNSName(tsigalgorithm.toStringNoDot() + ".sig-alg.reg.int.");
7014a23d
AT
311 else
312 trc.d_algoName = tsigalgorithm;
4646277d 313 trc.d_time = time(nullptr);
7014a23d
AT
314 trc.d_fudge = 300;
315 trc.d_origID=ntohs(id);
316 trc.d_eRcode=0;
fba65bff 317 if (B64Decode(tsigsecret64, tsigsecret) == -1) {
e6a9dde5 318 g_log<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<tsigkeyname<<"' for domain '"<<domain<<"'"<<endl;
fba65bff
PL
319 return;
320 }
ea3816cf 321 addTSIG(pw, trc, tsigkeyname, tsigsecret, "", false);
7014a23d
AT
322 }
323
a71bee29
PD
324 if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
325 throw ResolverException("Unable to send notify to "+remote.toStringWithPort()+": "+stringerror());
326 }
327}
328
5fca2e23 329void CommunicatorClass::drillHole(const DNSName &domain, const string &ip)
3696224d 330{
faa05786 331 (*d_holes.lock())[pair(domain,ip)]=time(nullptr);
3696224d
BH
332}
333
5fca2e23 334bool CommunicatorClass::justNotified(const DNSName &domain, const string &ip)
3696224d 335{
01e96795 336 auto holes = d_holes.lock();
faa05786 337 auto it = holes->find(pair(domain,ip));
01e96795
RG
338 if (it == holes->end()) {
339 // no hole
3696224d 340 return false;
01e96795 341 }
3696224d 342
01e96795
RG
343 if (it->second > time(nullptr)-900) {
344 // recent hole
3696224d 345 return true;
01e96795 346 }
3696224d 347
de42d2df 348 // do we want to purge this? XXX FIXME
3696224d
BH
349 return false;
350}
351
0c01dd7c 352void CommunicatorClass::makeNotifySockets()
3696224d 353{
20829585
PL
354 if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
355 d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
9d36589a
PL
356 } else {
357 d_nsock4 = -1;
358 }
20829585
PL
359 if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
360 d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
9d36589a 361 } else {
0c01dd7c 362 d_nsock6 = -1;
9d36589a 363 }
3696224d
BH
364}
365
5fca2e23 366void CommunicatorClass::notify(const DNSName &domain, const string &ip)
3696224d
BH
367{
368 d_nq.add(domain, ip);
3696224d
BH
369 d_any_sem.post();
370}