]>
Commit | Line | Data |
---|---|---|
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 | 45 | void 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 | 104 | bool 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 |
119 | void 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 | |
127 | void 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 | ||
158 | time_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 | 222 | void 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 | 260 | void 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 | 266 | bool 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 | 279 | void 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 | 288 | void 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 | } |