]>
Commit | Line | Data |
---|---|---|
bcf21dff | 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 | |
e8d78143 | 25 | #include "packetcache.hh" |
12c86877 | 26 | #include "utility.hh" |
01fde57c | 27 | #include "base32.hh" |
12c86877 BH |
28 | #include <string> |
29 | #include <sys/types.h> | |
bc28bef8 | 30 | #include <boost/algorithm/string.hpp> |
35fe50c3 BH |
31 | #include "dnssecinfra.hh" |
32 | #include "dnsseckeeper.hh" | |
12c86877 BH |
33 | #include "dns.hh" |
34 | #include "dnsbackend.hh" | |
35 | #include "ueberbackend.hh" | |
36 | #include "dnspacket.hh" | |
37 | #include "nameserver.hh" | |
38 | #include "distributor.hh" | |
39 | #include "logger.hh" | |
40 | #include "arguments.hh" | |
41 | #include "packethandler.hh" | |
42 | #include "statbag.hh" | |
43 | #include "resolver.hh" | |
44 | #include "communicator.hh" | |
45 | #include "dnsproxy.hh" | |
ba1a571d | 46 | #include "version.hh" |
357f6a75 | 47 | #include "common_startup.hh" |
12c86877 | 48 | |
51a3a4d4 | 49 | #if 0 |
2893c412 BH |
50 | #undef DLOG |
51 | #define DLOG(x) x | |
507823d1 | 52 | #endif |
357f6a75 | 53 | |
16f7d28d | 54 | AtomicCounter PacketHandler::s_count; |
d207ad63 | 55 | NetmaskGroup PacketHandler::s_allowNotifyFrom; |
dad0736b RC |
56 | set<string> PacketHandler::s_forwardNotify; |
57 | ||
12c86877 BH |
58 | extern string s_programname; |
59 | ||
e59b5787 | 60 | PacketHandler::PacketHandler():B(s_programname), d_dk(&B) |
12c86877 | 61 | { |
16f7d28d | 62 | ++s_count; |
4dfe94ae | 63 | d_doDNAME=::arg().mustDo("dname-processing"); |
389b7a05 | 64 | d_doExpandALIAS = ::arg().mustDo("expand-alias"); |
e8d78143 BH |
65 | d_logDNSDetails= ::arg().mustDo("log-dns-details"); |
66 | d_doIPv6AdditionalProcessing = ::arg().mustDo("do-ipv6-additional-processing"); | |
5704e107 PD |
67 | string fname= ::arg()["lua-prequery-script"]; |
68 | if(fname.empty()) | |
69 | { | |
70 | d_pdl = NULL; | |
71 | } | |
72 | else | |
73 | { | |
7c99293d AT |
74 | d_pdl = std::unique_ptr<AuthLua4>(new AuthLua4()); |
75 | d_pdl->loadFile(fname); | |
5704e107 | 76 | } |
0ecc1158 AT |
77 | fname = ::arg()["lua-dnsupdate-policy-script"]; |
78 | if (fname.empty()) | |
79 | { | |
80 | d_update_policy_lua = NULL; | |
81 | } | |
82 | else | |
83 | { | |
9694e14f AT |
84 | d_update_policy_lua = std::unique_ptr<AuthLua4>(new AuthLua4()); |
85 | d_update_policy_lua->loadFile(fname); | |
0ecc1158 | 86 | } |
12c86877 BH |
87 | } |
88 | ||
3971cf53 | 89 | UeberBackend *PacketHandler::getBackend() |
12c86877 BH |
90 | { |
91 | return &B; | |
92 | } | |
93 | ||
94 | PacketHandler::~PacketHandler() | |
95 | { | |
96 | --s_count; | |
e6a9dde5 | 97 | DLOG(g_log<<Logger::Error<<"PacketHandler destructor called - "<<s_count<<" left"<<endl); |
12c86877 BH |
98 | } |
99 | ||
088370cd | 100 | /** |
f889ab99 | 101 | * This adds CDNSKEY records to the answer packet. Returns true if one was added. |
088370cd PL |
102 | * |
103 | * @param p Pointer to the DNSPacket containing the original question | |
104 | * @param r Pointer to the DNSPacket where the records should be inserted into | |
f889ab99 | 105 | * @param sd SOAData of the zone for which CDNSKEY records sets should be added |
088370cd PL |
106 | * @return bool that shows if any records were added |
107 | **/ | |
f889ab99 | 108 | bool PacketHandler::addCDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd) |
35fe50c3 | 109 | { |
088370cd | 110 | string publishCDNSKEY; |
0900d2d3 | 111 | d_dk.getFromMeta(p->qdomain, "PUBLISH-CDNSKEY", publishCDNSKEY); |
f889ab99 | 112 | if (publishCDNSKEY != "1") |
088370cd PL |
113 | return false; |
114 | ||
90ba52e0 | 115 | DNSZoneRecord rr; |
35fe50c3 | 116 | bool haveOne=false; |
35fe50c3 | 117 | |
f889ab99 PL |
118 | DNSSECKeeper::keyset_t entryPoints = d_dk.getEntryPoints(p->qdomain); |
119 | for(const auto& value: entryPoints) { | |
90ba52e0 | 120 | rr.dr.d_type=QType::CDNSKEY; |
121 | rr.dr.d_ttl=sd.default_ttl; | |
122 | rr.dr.d_name=p->qdomain; | |
123 | rr.dr.d_content=std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()); | |
f889ab99 PL |
124 | rr.auth=true; |
125 | r->addRecord(rr); | |
126 | haveOne=true; | |
127 | } | |
128 | ||
129 | if(::arg().mustDo("direct-dnskey")) { | |
130 | B.lookup(QType(QType::CDNSKEY), p->qdomain, p, sd.domain_id); | |
131 | ||
132 | while(B.get(rr)) { | |
90ba52e0 | 133 | rr.dr.d_ttl=sd.default_ttl; |
f889ab99 PL |
134 | r->addRecord(rr); |
135 | haveOne=true; | |
136 | } | |
137 | } | |
138 | return haveOne; | |
139 | } | |
140 | ||
141 | /** | |
142 | * This adds DNSKEY records to the answer packet. Returns true if one was added. | |
143 | * | |
144 | * @param p Pointer to the DNSPacket containing the original question | |
145 | * @param r Pointer to the DNSPacket where the records should be inserted into | |
146 | * @param sd SOAData of the zone for which DNSKEY records sets should be added | |
147 | * @return bool that shows if any records were added | |
148 | **/ | |
149 | bool PacketHandler::addDNSKEY(DNSPacket *p, DNSPacket *r, const SOAData& sd) | |
150 | { | |
90ba52e0 | 151 | DNSZoneRecord rr; |
f889ab99 | 152 | bool haveOne=false; |
f889ab99 | 153 | |
ade1b1e9 | 154 | DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain); |
424b92c6 | 155 | for(const auto& value: keyset) { |
90ba52e0 | 156 | rr.dr.d_type=QType::DNSKEY; |
157 | rr.dr.d_ttl=sd.default_ttl; | |
158 | rr.dr.d_name=p->qdomain; | |
159 | rr.dr.d_content=std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()); | |
ade1b1e9 | 160 | rr.auth=true; |
35fe50c3 BH |
161 | r->addRecord(rr); |
162 | haveOne=true; | |
163 | } | |
4a6ea260 | 164 | |
cc8df07f | 165 | if(::arg().mustDo("direct-dnskey")) { |
f889ab99 | 166 | B.lookup(QType(QType::DNSKEY), p->qdomain, p, sd.domain_id); |
088370cd | 167 | |
4a6ea260 | 168 | while(B.get(rr)) { |
90ba52e0 | 169 | rr.dr.d_ttl=sd.default_ttl; |
4a6ea260 PD |
170 | r->addRecord(rr); |
171 | haveOne=true; | |
172 | } | |
173 | } | |
174 | ||
35fe50c3 BH |
175 | return haveOne; |
176 | } | |
177 | ||
ef542223 PL |
178 | /** |
179 | * This adds CDS records to the answer packet r. | |
180 | * | |
181 | * @param p Pointer to the DNSPacket containing the original question. | |
182 | * @param r Pointer to the DNSPacket where the records should be inserted into. | |
183 | * @param sd SOAData of the zone for which CDS records sets should be added, | |
184 | * used to determine record TTL. | |
185 | * @return bool that shows if any records were added. | |
186 | **/ | |
187 | bool PacketHandler::addCDS(DNSPacket *p, DNSPacket *r, const SOAData& sd) | |
188 | { | |
189 | string publishCDS; | |
0900d2d3 | 190 | d_dk.getFromMeta(p->qdomain, "PUBLISH-CDS", publishCDS); |
ef542223 PL |
191 | if (publishCDS.empty()) |
192 | return false; | |
193 | ||
194 | vector<string> digestAlgos; | |
195 | stringtok(digestAlgos, publishCDS, ", "); | |
196 | ||
90ba52e0 | 197 | DNSZoneRecord rr; |
198 | rr.dr.d_type=QType::CDS; | |
199 | rr.dr.d_ttl=sd.default_ttl; | |
200 | rr.dr.d_name=p->qdomain; | |
ef542223 PL |
201 | rr.auth=true; |
202 | ||
203 | bool haveOne=false; | |
ef542223 | 204 | |
f889ab99 | 205 | DNSSECKeeper::keyset_t keyset = d_dk.getEntryPoints(p->qdomain); |
ef542223 | 206 | |
56225bd3 | 207 | for(auto const &value : keyset) { |
56225bd3 | 208 | for(auto const &digestAlgo : digestAlgos){ |
8455425c | 209 | rr.dr.d_content=std::make_shared<DSRecordContent>(makeDSFromDNSKey(p->qdomain, value.first.getDNSKEY(), pdns_stou(digestAlgo))); |
ef542223 PL |
210 | r->addRecord(rr); |
211 | haveOne=true; | |
212 | } | |
213 | } | |
214 | ||
215 | if(::arg().mustDo("direct-dnskey")) { | |
216 | B.lookup(QType(QType::CDS), p->qdomain, p, sd.domain_id); | |
217 | ||
218 | while(B.get(rr)) { | |
90ba52e0 | 219 | rr.dr.d_ttl=sd.default_ttl; |
ef542223 PL |
220 | r->addRecord(rr); |
221 | haveOne=true; | |
222 | } | |
223 | } | |
224 | ||
225 | return haveOne; | |
226 | } | |
35fe50c3 | 227 | |
794c2f92 PD |
228 | /** This adds NSEC3PARAM records. Returns true if one was added */ |
229 | bool PacketHandler::addNSEC3PARAM(DNSPacket *p, DNSPacket *r, const SOAData& sd) | |
c3c89361 | 230 | { |
90ba52e0 | 231 | DNSZoneRecord rr; |
c3c89361 BH |
232 | |
233 | NSEC3PARAMRecordContent ns3prc; | |
e0d84497 | 234 | if(d_dk.getNSEC3PARAM(p->qdomain, &ns3prc)) { |
90ba52e0 | 235 | rr.dr.d_type=QType::NSEC3PARAM; |
236 | rr.dr.d_ttl=sd.default_ttl; | |
237 | rr.dr.d_name=p->qdomain; | |
1c405b6a | 238 | ns3prc.d_flags = 0; // the NSEC3PARAM 'flag' is defined to always be zero in RFC5155. |
90ba52e0 | 239 | rr.dr.d_content=std::make_shared<NSEC3PARAMRecordContent>(ns3prc); |
c3c89361 BH |
240 | rr.auth = true; |
241 | r->addRecord(rr); | |
242 | return true; | |
243 | } | |
244 | return false; | |
245 | } | |
246 | ||
247 | ||
49e8d5d5 | 248 | // This is our chaos class requests handler. Return 1 if content was added, 0 if it wasn't |
675fa24c | 249 | int PacketHandler::doChaosRequest(DNSPacket *p, DNSPacket *r, DNSName &target) |
12c86877 | 250 | { |
90ba52e0 | 251 | DNSZoneRecord rr; |
8ca1a435 | 252 | |
49e8d5d5 | 253 | if(p->qtype.getCode()==QType::TXT) { |
290a083d | 254 | static const DNSName versionbind("version.bind."), versionpdns("version.pdns."), idserver("id.server."); |
00582c16 | 255 | if (target==versionbind || target==versionpdns) { |
49e8d5d5 KM |
256 | // modes: full, powerdns only, anonymous or custom |
257 | const static string mode=::arg()["version-string"]; | |
90ba52e0 | 258 | string content; |
49e8d5d5 | 259 | if(mode.empty() || mode=="full") |
90ba52e0 | 260 | content=fullVersionString(); |
49e8d5d5 | 261 | else if(mode=="powerdns") |
90ba52e0 | 262 | content="Served by PowerDNS - https://www.powerdns.com/"; |
49e8d5d5 | 263 | else if(mode=="anonymous") { |
8ca1a435 | 264 | r->setRcode(RCode::ServFail); |
8ca1a435 DB |
265 | return 0; |
266 | } | |
267 | else | |
90ba52e0 | 268 | content=mode; |
6177a176 | 269 | rr.dr.d_content = DNSRecordContent::mastermake(QType::TXT, 1, "\""+content+"\""); |
49e8d5d5 | 270 | } |
290a083d | 271 | else if (target==idserver) { |
49e8d5d5 KM |
272 | // modes: disabled, hostname or custom |
273 | const static string id=::arg()["server-id"]; | |
274 | ||
275 | if (id == "disabled") { | |
276 | r->setRcode(RCode::Refused); | |
277 | return 0; | |
8ca1a435 | 278 | } |
0e684fda | 279 | string tid=id; |
9a1a24a9 | 280 | if(!tid.empty() && tid[0]!='"') { // see #6010 however |
281 | tid = "\"" + tid + "\""; | |
282 | } | |
0e684fda | 283 | rr.dr.d_content=DNSRecordContent::mastermake(QType::TXT, 1, tid); |
49e8d5d5 KM |
284 | } |
285 | else { | |
286 | r->setRcode(RCode::Refused); | |
287 | return 0; | |
e5d684f9 | 288 | } |
e5d684f9 | 289 | |
90ba52e0 | 290 | rr.dr.d_ttl=5; |
291 | rr.dr.d_name=target; | |
292 | rr.dr.d_type=QType::TXT; | |
293 | rr.dr.d_class=QClass::CHAOS; | |
12c86877 | 294 | r->addRecord(rr); |
12c86877 BH |
295 | return 1; |
296 | } | |
49e8d5d5 KM |
297 | |
298 | r->setRcode(RCode::NotImp); | |
12c86877 BH |
299 | return 0; |
300 | } | |
301 | ||
90ba52e0 | 302 | vector<DNSZoneRecord> PacketHandler::getBestReferralNS(DNSPacket *p, SOAData& sd, const DNSName &target) |
35fe50c3 | 303 | { |
90ba52e0 | 304 | vector<DNSZoneRecord> ret; |
305 | DNSZoneRecord rr; | |
675fa24c | 306 | DNSName subdomain(target); |
35fe50c3 | 307 | do { |
82cc1f71 BH |
308 | if(subdomain == sd.qname) // stop at SOA |
309 | break; | |
35fe50c3 BH |
310 | B.lookup(QType(QType::NS), subdomain, p, sd.domain_id); |
311 | while(B.get(rr)) { | |
0da371d7 | 312 | ret.push_back(rr); // this used to exclude auth NS records for some reason |
35fe50c3 BH |
313 | } |
314 | if(!ret.empty()) | |
315 | return ret; | |
675fa24c | 316 | } while( subdomain.chopOff() ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> '' |
35fe50c3 BH |
317 | return ret; |
318 | } | |
319 | ||
90ba52e0 | 320 | vector<DNSZoneRecord> PacketHandler::getBestDNAMESynth(DNSPacket *p, SOAData& sd, DNSName &target) |
8dee0750 | 321 | { |
90ba52e0 | 322 | vector<DNSZoneRecord> ret; |
323 | DNSZoneRecord rr; | |
561434a6 | 324 | DNSName prefix; |
675fa24c | 325 | DNSName subdomain(target); |
8dee0750 | 326 | do { |
e6a9dde5 | 327 | DLOG(g_log<<"Attempting DNAME lookup for "<<subdomain<<", sd.qname="<<sd.qname<<endl); |
8dee0750 | 328 | |
329 | B.lookup(QType(QType::DNAME), subdomain, p, sd.domain_id); | |
330 | while(B.get(rr)) { | |
331 | ret.push_back(rr); // put in the original | |
90ba52e0 | 332 | rr.dr.d_type = QType::CNAME; |
333 | rr.dr.d_name = prefix + rr.dr.d_name; | |
334 | rr.dr.d_content = std::make_shared<CNAMERecordContent>(CNAMERecordContent(prefix + getRR<DNAMERecordContent>(rr.dr)->d_content)); | |
dce1e90d | 335 | rr.auth = 0; // don't sign CNAME |
90ba52e0 | 336 | target= getRR<CNAMERecordContent>(rr.dr)->getTarget(); |
8dee0750 | 337 | ret.push_back(rr); |
338 | } | |
339 | if(!ret.empty()) | |
340 | return ret; | |
561434a6 | 341 | if(subdomain.countLabels()) |
f48c35c0 | 342 | prefix.appendRawLabel(subdomain.getRawLabels()[0]); // XXX DNSName pain this feels wrong |
8dee0750 | 343 | if(subdomain == sd.qname) // stop at SOA |
344 | break; | |
345 | ||
675fa24c | 346 | } while( subdomain.chopOff() ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> '' |
8dee0750 | 347 | return ret; |
348 | } | |
349 | ||
350 | ||
75a89ce6 | 351 | // Return best matching wildcard or next closer name |
90ba52e0 | 352 | bool PacketHandler::getBestWildcard(DNSPacket *p, SOAData& sd, const DNSName &target, DNSName &wildcard, vector<DNSZoneRecord>* ret) |
35fe50c3 | 353 | { |
e3f388cd | 354 | ret->clear(); |
90ba52e0 | 355 | DNSZoneRecord rr; |
675fa24c | 356 | DNSName subdomain(target); |
75a89ce6 PD |
357 | bool haveSomething=false; |
358 | ||
359 | wildcard=subdomain; | |
675fa24c | 360 | while( subdomain.chopOff() && !haveSomething ) { |
598b152c | 361 | if (subdomain.empty()) { |
12c06211 | 362 | B.lookup(QType(QType::ANY), g_wildcarddnsname, p, sd.domain_id); |
598b152c | 363 | } else { |
12c06211 | 364 | B.lookup(QType(QType::ANY), g_wildcarddnsname+subdomain, p, sd.domain_id); |
598b152c | 365 | } |
35fe50c3 | 366 | while(B.get(rr)) { |
90ba52e0 | 367 | if(rr.dr.d_type == p->qtype.getCode() || rr.dr.d_type == QType::CNAME || (p->qtype.getCode() == QType::ANY && rr.dr.d_type != QType::RRSIG)) |
e3f388cd | 368 | ret->push_back(rr); |
12c06211 | 369 | wildcard=g_wildcarddnsname+subdomain; |
e3f388cd | 370 | haveSomething=true; |
35fe50c3 | 371 | } |
75a89ce6 PD |
372 | |
373 | if ( subdomain == sd.qname || haveSomething ) // stop at SOA or result | |
82cc1f71 | 374 | break; |
35fe50c3 | 375 | |
75a89ce6 PD |
376 | B.lookup(QType(QType::ANY), subdomain, p, sd.domain_id); |
377 | if (B.get(rr)) { | |
e6a9dde5 | 378 | DLOG(g_log<<"No wildcard match, ancestor exists"<<endl); |
75a89ce6 PD |
379 | while (B.get(rr)) ; |
380 | break; | |
381 | } | |
382 | wildcard=subdomain; | |
383 | } | |
384 | ||
385 | return haveSomething; | |
35fe50c3 BH |
386 | } |
387 | ||
12c86877 | 388 | /** dangling is declared true if we were unable to resolve everything */ |
d2323cd0 | 389 | int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r, const SOAData& soadata, bool retargeted) |
12c86877 | 390 | { |
90ba52e0 | 391 | DNSZoneRecord rr; |
fd8bc993 | 392 | SOAData sd; |
313923a8 | 393 | sd.db=0; |
b636533b | 394 | |
b8e0f341 | 395 | if(p->qtype.getCode()!=QType::AXFR) { // this packet needs additional processing |
aa7b2405 | 396 | // we now have a copy, push_back on packet might reallocate! |
397 | auto& records = r->getRRS(); | |
398 | vector<DNSZoneRecord> toAdd; | |
8513f025 | 399 | |
aa7b2405 | 400 | for(auto i = records.cbegin() ; i!= records.cend(); ++i) { |
401 | if(i->dr.d_place==DNSResourceRecord::ADDITIONAL || | |
402 | !(i->dr.d_type==QType::MX || i->dr.d_type==QType::NS || i->dr.d_type==QType::SRV)) | |
403 | continue; | |
8513f025 | 404 | |
94bfa5b6 | 405 | if(r->d.aa && i->dr.d_name.countLabels() && i->dr.d_type==QType::NS && !B.getSOA(i->dr.d_name,sd) && !retargeted) { // drop AA in case of non-SOA-level NS answer, except for root referral |
76bf5f40 | 406 | r->setA(false); |
232f0877 | 407 | // i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME |
f6ba332a | 408 | } |
fd8bc993 | 409 | |
90ba52e0 | 410 | DNSName lookup; |
411 | ||
412 | if(i->dr.d_type == QType::MX) | |
413 | lookup = getRR<MXRecordContent>(i->dr)->d_mxname; | |
414 | else if(i->dr.d_type == QType::SRV) | |
415 | lookup = getRR<SRVRecordContent>(i->dr)->d_target; | |
aa7b2405 | 416 | else if(i->dr.d_type == QType::NS) |
417 | lookup = getRR<NSRecordContent>(i->dr)->getNS(); | |
418 | else | |
419 | continue; | |
90ba52e0 | 420 | B.lookup(QType(d_doIPv6AdditionalProcessing ? QType::ANY : QType::A), lookup, p); |
794c2f92 | 421 | |
0ece3f09 | 422 | while(B.get(rr)) { |
90ba52e0 | 423 | if(rr.dr.d_type != QType::A && rr.dr.d_type!=QType::AAAA) |
0ece3f09 | 424 | continue; |
425 | if(rr.domain_id!=i->domain_id && ::arg()["out-of-zone-additional-processing"]=="no") { | |
e6a9dde5 | 426 | DLOG(g_log<<Logger::Warning<<"Not including out-of-zone additional processing of "<<i->dr.d_name<<" ("<<rr.dr.d_name<<")"<<endl); |
0ece3f09 | 427 | continue; // not adding out-of-zone additional data |
4957a608 | 428 | } |
0ece3f09 | 429 | |
90ba52e0 | 430 | if(rr.auth && !rr.dr.d_name.isPartOf(soadata.qname)) // don't sign out of zone data using the main key |
0ece3f09 | 431 | rr.auth=false; |
90ba52e0 | 432 | rr.dr.d_place=DNSResourceRecord::ADDITIONAL; |
aa7b2405 | 433 | toAdd.push_back(rr); |
b636533b | 434 | } |
12c86877 | 435 | } |
336f11d8 | 436 | for(const auto& rec : toAdd) |
437 | r->addRecord(rec); | |
438 | ||
439 | //records.insert(records.end(), toAdd.cbegin(), toAdd.cend()); // would be faster, but no dedup | |
12c86877 BH |
440 | } |
441 | return 1; | |
442 | } | |
443 | ||
35fe50c3 | 444 | |
ca617317 | 445 | void PacketHandler::emitNSEC(DNSPacket *r, const SOAData& sd, const DNSName& name, const DNSName& next, int mode) |
35fe50c3 | 446 | { |
35fe50c3 | 447 | NSECRecordContent nrc; |
ca617317 KM |
448 | nrc.d_next = next; |
449 | ||
35fe50c3 | 450 | nrc.d_set.insert(QType::NSEC); |
ca617317 | 451 | nrc.d_set.insert(QType::RRSIG); |
576e7e0f KM |
452 | if(sd.qname == name) { |
453 | nrc.d_set.insert(QType::SOA); // 1dfd8ad SOA can live outside the records table | |
9b30cd1a | 454 | nrc.d_set.insert(QType::DNSKEY); |
088370cd | 455 | string publishCDNSKEY; |
0900d2d3 | 456 | d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY); |
088370cd PL |
457 | if (publishCDNSKEY == "1") |
458 | nrc.d_set.insert(QType::CDNSKEY); | |
ef542223 | 459 | string publishCDS; |
0900d2d3 | 460 | d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS); |
ef542223 PL |
461 | if (! publishCDS.empty()) |
462 | nrc.d_set.insert(QType::CDS); | |
576e7e0f | 463 | } |
35fe50c3 | 464 | |
90ba52e0 | 465 | DNSZoneRecord rr; |
576e7e0f | 466 | |
ca617317 | 467 | B.lookup(QType(QType::ANY), name, NULL, sd.domain_id); |
35fe50c3 | 468 | while(B.get(rr)) { |
90ba52e0 | 469 | if(rr.dr.d_type == QType::NS || rr.auth) |
470 | nrc.d_set.insert(rr.dr.d_type); | |
35fe50c3 | 471 | } |
50eb4144 | 472 | |
90ba52e0 | 473 | rr.dr.d_name = name; |
474 | rr.dr.d_ttl = sd.default_ttl; | |
475 | rr.dr.d_type = QType::NSEC; | |
476 | rr.dr.d_content = std::make_shared<NSECRecordContent>(nrc); | |
477 | rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY; | |
35fe50c3 | 478 | rr.auth = true; |
50eb4144 | 479 | |
35fe50c3 BH |
480 | r->addRecord(rr); |
481 | } | |
482 | ||
576e7e0f | 483 | void PacketHandler::emitNSEC3(DNSPacket *r, const SOAData& sd, const NSEC3PARAMRecordContent& ns3prc, const DNSName& name, const string& namehash, const string& nexthash, int mode) |
5c3bf2db | 484 | { |
5c3bf2db | 485 | NSEC3RecordContent n3rc; |
576e7e0f | 486 | n3rc.d_algorithm = ns3prc.d_algorithm; |
b9dba5c1 | 487 | n3rc.d_flags = ns3prc.d_flags; |
c3c89361 | 488 | n3rc.d_iterations = ns3prc.d_iterations; |
576e7e0f KM |
489 | n3rc.d_salt = ns3prc.d_salt; |
490 | n3rc.d_nexthash = nexthash; | |
5c3bf2db | 491 | |
90ba52e0 | 492 | DNSZoneRecord rr; |
c3c89361 | 493 | |
576e7e0f KM |
494 | if(!name.empty()) { |
495 | if (sd.qname == name) { | |
496 | n3rc.d_set.insert(QType::SOA); // 1dfd8ad SOA can live outside the records table | |
b5baefaf PD |
497 | n3rc.d_set.insert(QType::NSEC3PARAM); |
498 | n3rc.d_set.insert(QType::DNSKEY); | |
088370cd | 499 | string publishCDNSKEY; |
0900d2d3 | 500 | d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY); |
088370cd PL |
501 | if (publishCDNSKEY == "1") |
502 | n3rc.d_set.insert(QType::CDNSKEY); | |
ef542223 | 503 | string publishCDS; |
0900d2d3 | 504 | d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS); |
ef542223 PL |
505 | if (! publishCDS.empty()) |
506 | n3rc.d_set.insert(QType::CDS); | |
b5baefaf | 507 | } |
576e7e0f KM |
508 | |
509 | B.lookup(QType(QType::ANY), name, NULL, sd.domain_id); | |
510 | while(B.get(rr)) { | |
90ba52e0 | 511 | if(rr.dr.d_type && (rr.dr.d_type == QType::NS || rr.auth)) // skip empty non-terminals |
512 | n3rc.d_set.insert(rr.dr.d_type); | |
576e7e0f | 513 | } |
c3c89361 | 514 | } |
b5baefaf | 515 | |
b8adb30d | 516 | if (n3rc.d_set.size() && !(n3rc.d_set.size() == 1 && n3rc.d_set.count(QType::NS))) |
b5baefaf | 517 | n3rc.d_set.insert(QType::RRSIG); |
50eb4144 | 518 | |
90ba52e0 | 519 | rr.dr.d_name = DNSName(toBase32Hex(namehash))+sd.qname; |
520 | rr.dr.d_ttl = sd.default_ttl; | |
521 | rr.dr.d_type=QType::NSEC3; | |
522 | rr.dr.d_content=std::make_shared<NSEC3RecordContent>(n3rc); | |
523 | rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY; | |
5c3bf2db | 524 | rr.auth = true; |
50eb4144 | 525 | |
5c3bf2db BH |
526 | r->addRecord(rr); |
527 | } | |
528 | ||
75a89ce6 PD |
529 | /* |
530 | mode 0 = No Data Responses, QTYPE is not DS | |
b5baefaf | 531 | mode 1 = No Data Responses, QTYPE is DS |
75a89ce6 PD |
532 | mode 2 = Wildcard No Data Responses |
533 | mode 3 = Wildcard Answer Responses | |
534 | mode 4 = Name Error Responses | |
dcb8c5d7 | 535 | mode 5 = Direct NSEC request |
35fe50c3 | 536 | */ |
675fa24c | 537 | void PacketHandler::addNSECX(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, int mode) |
01fde57c | 538 | { |
52e0d783 KM |
539 | if(!p->d_dnssecOk && mode != 5) |
540 | return; | |
541 | ||
c3c89361 | 542 | NSEC3PARAMRecordContent ns3rc; |
22c5aa60 BH |
543 | bool narrow; |
544 | if(d_dk.getNSEC3PARAM(auth, &ns3rc, &narrow)) { | |
d31e9b23 | 545 | if (mode != 5) // no direct NSEC3 queries, rfc5155 7.2.8 |
dcb8c5d7 | 546 | addNSEC3(p, r, target, wildcard, auth, ns3rc, narrow, mode); |
9b30cd1a BH |
547 | } |
548 | else { | |
c5c4fbdc | 549 | addNSEC(p, r, target, wildcard, auth, mode); |
9b30cd1a | 550 | } |
01fde57c BH |
551 | } |
552 | ||
29e0008a | 553 | bool getNSEC3Hashes(bool narrow, DNSBackend* db, int id, const std::string& hashed, bool decrement, DNSName& unhashed, std::string& before, std::string& after, int mode) |
22c5aa60 BH |
554 | { |
555 | bool ret; | |
556 | if(narrow) { // nsec3-narrow | |
557 | ret=true; | |
558 | before=hashed; | |
b5baefaf | 559 | if(decrement) { |
af3ffdf1 | 560 | decrementHash(before); |
b5baefaf PD |
561 | unhashed.clear(); |
562 | } | |
22c5aa60 BH |
563 | after=hashed; |
564 | incrementHash(after); | |
565 | } | |
566 | else { | |
29e0008a KM |
567 | DNSName hashedName = DNSName(toBase32Hex(hashed)); |
568 | DNSName beforeName, afterName; | |
569 | if (!decrement && mode >= 2) | |
570 | beforeName = hashedName; | |
571 | ret=db->getBeforeAndAfterNamesAbsolute(id, hashedName, unhashed, beforeName, afterName); | |
572 | before=fromBase32Hex(beforeName.toString()); | |
573 | after=fromBase32Hex(afterName.toString()); | |
22c5aa60 | 574 | } |
22c5aa60 BH |
575 | return ret; |
576 | } | |
577 | ||
561434a6 | 578 | void PacketHandler::addNSEC3(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, const NSEC3PARAMRecordContent& ns3rc, bool narrow, int mode) |
01fde57c | 579 | { |
e6a9dde5 | 580 | DLOG(g_log<<"addNSEC3() mode="<<mode<<" auth="<<auth<<" target="<<target<<" wildcard="<<wildcard<<endl); |
53977f80 | 581 | |
5c3bf2db | 582 | SOAData sd; |
79ba7763 | 583 | if(!B.getSOAUncached(auth, sd)) { |
e6a9dde5 | 584 | DLOG(g_log<<"Could not get SOA for domain"); |
5c3bf2db BH |
585 | return; |
586 | } | |
53977f80 | 587 | |
3a741d7f | 588 | bool doNextcloser = false; |
561434a6 PD |
589 | string before, after, hashed; |
590 | DNSName unhashed, closest; | |
53977f80 | 591 | |
75a89ce6 | 592 | if (mode == 2 || mode == 3 || mode == 4) { |
f0306634 | 593 | closest=wildcard; |
561434a6 | 594 | closest.chopOff(); |
f0306634 KM |
595 | } else |
596 | closest=target; | |
53977f80 | 597 | |
75a89ce6 | 598 | // add matching NSEC3 RR |
b50efd61 | 599 | if (mode != 3) { |
3a741d7f | 600 | unhashed=(mode == 0 || mode == 1 || mode == 5) ? target : closest; |
28e2e78e | 601 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 602 | DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
53977f80 | 603 | |
15c9bf3b | 604 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, false, unhashed, before, after, mode); |
3a741d7f | 605 | |
2e9c8710 | 606 | if (((mode == 0 && ns3rc.d_flags) || mode == 1) && (hashed != before)) { |
e6a9dde5 | 607 | DLOG(g_log<<"No matching NSEC3, do closest (provable) encloser"<<endl); |
3a741d7f | 608 | |
54c9247e | 609 | bool doBreak = false; |
90ba52e0 | 610 | DNSZoneRecord rr; |
561434a6 | 611 | while( closest.chopOff() && (closest != sd.qname)) { // stop at SOA |
3a741d7f | 612 | B.lookup(QType(QType::ANY), closest, p, sd.domain_id); |
54c9247e KM |
613 | while(B.get(rr)) |
614 | if (rr.auth) | |
615 | doBreak = true; | |
616 | if(doBreak) | |
3a741d7f | 617 | break; |
3a741d7f KM |
618 | } |
619 | doNextcloser = true; | |
620 | unhashed=closest; | |
28e2e78e | 621 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 622 | DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
3a741d7f | 623 | |
15c9bf3b | 624 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, false, unhashed, before, after); |
3a741d7f KM |
625 | } |
626 | ||
d88babea | 627 | if (!after.empty()) { |
e6a9dde5 | 628 | DLOG(g_log<<"Done calling for matching, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
576e7e0f | 629 | emitNSEC3(r, sd, ns3rc, unhashed, before, after, mode); |
2010ac95 | 630 | } |
16cf9135 | 631 | } |
75a89ce6 PD |
632 | |
633 | // add covering NSEC3 RR | |
3a741d7f | 634 | if ((mode >= 2 && mode <= 4) || doNextcloser) { |
561434a6 | 635 | DNSName next(target); |
75a89ce6 PD |
636 | do { |
637 | unhashed=next; | |
638 | } | |
e325f20c | 639 | while( next.chopOff() && !(next==closest)); |
75a89ce6 | 640 | |
28e2e78e | 641 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 642 | DLOG(g_log<<"2 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
15c9bf3b KM |
643 | |
644 | getNSEC3Hashes(narrow, sd.db,sd.domain_id, hashed, true, unhashed, before, after); | |
e6a9dde5 | 645 | DLOG(g_log<<"Done calling for covering, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
15c9bf3b | 646 | emitNSEC3( r, sd, ns3rc, unhashed, before, after, mode); |
75a89ce6 | 647 | } |
53977f80 | 648 | |
75a89ce6 | 649 | // wildcard denial |
7bb8e202 | 650 | if (mode == 2 || mode == 4) { |
12c06211 | 651 | unhashed=g_wildcarddnsname+closest; |
75a89ce6 | 652 | |
28e2e78e | 653 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 654 | DLOG(g_log<<"3 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
53977f80 | 655 | |
15c9bf3b | 656 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, (mode != 2), unhashed, before, after); |
e6a9dde5 | 657 | DLOG(g_log<<"Done calling for '*', hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
15c9bf3b | 658 | emitNSEC3( r, sd, ns3rc, unhashed, before, after, mode); |
75a89ce6 | 659 | } |
01fde57c | 660 | } |
35fe50c3 | 661 | |
561434a6 | 662 | void PacketHandler::addNSEC(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, int mode) |
35fe50c3 | 663 | { |
e6a9dde5 | 664 | DLOG(g_log<<"addNSEC() mode="<<mode<<" auth="<<auth<<" target="<<target<<" wildcard="<<wildcard<<endl); |
35fe50c3 | 665 | |
53977f80 | 666 | SOAData sd; |
79ba7763 | 667 | if(!B.getSOAUncached(auth, sd)) { |
e6a9dde5 | 668 | DLOG(g_log<<"Could not get SOA for domain"<<endl); |
35fe50c3 BH |
669 | return; |
670 | } | |
671 | ||
561434a6 | 672 | DNSName before,after; |
15c9bf3b | 673 | sd.db->getBeforeAndAfterNames(sd.domain_id, auth, target, before, after); |
7cd99296 KM |
674 | if (mode != 5 || before == target) |
675 | emitNSEC(r, sd, before, after, mode); | |
b5baefaf | 676 | |
337dd27b KM |
677 | if (mode == 2 || mode == 4) { |
678 | // wildcard NO-DATA or wildcard denial | |
628ab42d | 679 | before.clear(); |
561434a6 | 680 | DNSName closest(wildcard); |
337dd27b | 681 | if (mode == 4) { |
561434a6 PD |
682 | closest.chopOff(); |
683 | closest.prependRawLabel("*"); | |
337dd27b | 684 | } |
15c9bf3b KM |
685 | sd.db->getBeforeAndAfterNames(sd.domain_id, auth, closest, before, after); |
686 | emitNSEC(r, sd, before, after, mode); | |
75a89ce6 | 687 | } |
35fe50c3 BH |
688 | return; |
689 | } | |
690 | ||
12c86877 BH |
691 | /* Semantics: |
692 | ||
693 | - only one backend owns the SOA of a zone | |
694 | - only one AXFR per zone at a time - double startTransaction should fail | |
695 | - backends need to implement transaction semantics | |
696 | ||
697 | ||
698 | How BindBackend would implement this: | |
699 | startTransaction makes a file | |
700 | feedRecord sends everything to that file | |
701 | commitTransaction moves that file atomically over the regular file, and triggers a reload | |
702 | rollbackTransaction removes the file | |
703 | ||
704 | ||
705 | How PostgreSQLBackend would implement this: | |
706 | startTransaction starts a sql transaction, which also deletes all records | |
707 | feedRecord is an insert statement | |
708 | commitTransaction commits the transaction | |
709 | rollbackTransaction aborts it | |
710 | ||
711 | How MySQLBackend would implement this: | |
712 | (good question!) | |
713 | ||
714 | */ | |
715 | ||
6fe866b4 | 716 | int PacketHandler::trySuperMaster(DNSPacket *p, const DNSName& tsigkeyname) |
7108e055 PD |
717 | { |
718 | if(p->d_tcp) | |
719 | { | |
720 | // do it right now if the client is TCP | |
721 | // rarely happens | |
7731e32c | 722 | return trySuperMasterSynchronous(p, tsigkeyname); |
7108e055 PD |
723 | } |
724 | else | |
725 | { | |
726 | // queue it if the client is on UDP | |
727 | Communicator.addTrySuperMasterRequest(p); | |
728 | return 0; | |
729 | } | |
730 | } | |
731 | ||
6fe866b4 | 732 | int PacketHandler::trySuperMasterSynchronous(DNSPacket *p, const DNSName& tsigkeyname) |
12c86877 | 733 | { |
a94fe494 RG |
734 | string remote = p->getRemote().toString(); |
735 | if(p->hasEDNSSubnet() && ::arg().contains("trusted-notification-proxy", remote)) { | |
736 | remote = p->getRealRemote().toStringNoMask(); | |
737 | } | |
738 | ||
12c86877 BH |
739 | Resolver::res_t nsset; |
740 | try { | |
741 | Resolver resolver; | |
092f210a | 742 | uint32_t theirserial; |
a94fe494 RG |
743 | resolver.getSoaSerial(remote,p->qdomain, &theirserial); |
744 | resolver.resolve(remote, p->qdomain, QType::NS, &nsset); | |
12c86877 BH |
745 | } |
746 | catch(ResolverException &re) { | |
e6a9dde5 | 747 | g_log<<Logger::Error<<"Error resolving SOA or NS for "<<p->qdomain<<" at: "<< remote <<": "<<re.reason<<endl; |
12c86877 BH |
748 | return RCode::ServFail; |
749 | } | |
750 | ||
39b23e69 RK |
751 | // check if the returned records are NS records |
752 | bool haveNS=false; | |
424b92c6 | 753 | for(const auto& ns: nsset) { |
90ba52e0 | 754 | if(ns.qtype==QType::NS) |
39b23e69 RK |
755 | haveNS=true; |
756 | } | |
757 | ||
758 | if(!haveNS) { | |
e6a9dde5 | 759 | g_log<<Logger::Error<<"While checking for supermaster, did not find NS for "<<p->qdomain<<" at: "<< remote <<endl; |
39b23e69 RK |
760 | return RCode::ServFail; |
761 | } | |
762 | ||
719f9024 | 763 | string nameserver, account; |
12c86877 | 764 | DNSBackend *db; |
771bb0b0 AT |
765 | |
766 | if (!::arg().mustDo("allow-unsigned-supermaster") && tsigkeyname.empty()) { | |
e6a9dde5 | 767 | g_log<<Logger::Error<<"Received unsigned NOTIFY for "<<p->qdomain<<" from potential supermaster "<<remote<<". Refusing."<<endl; |
771bb0b0 AT |
768 | return RCode::Refused; |
769 | } | |
770 | ||
a94fe494 | 771 | if(!B.superMasterBackend(remote, p->qdomain, nsset, &nameserver, &account, &db)) { |
e6a9dde5 | 772 | g_log<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<remote<<". Remote nameservers: "<<endl; |
424b92c6 | 773 | for(const auto& rr: nsset) { |
90ba52e0 | 774 | if(rr.qtype==QType::NS) |
e6a9dde5 | 775 | g_log<<Logger::Error<<rr.content<<endl; |
a7372c6f | 776 | } |
12c86877 BH |
777 | return RCode::Refused; |
778 | } | |
8c80c4f4 | 779 | try { |
ded6b08d | 780 | db->createSlaveDomain(p->getRemote().toString(), p->qdomain, nameserver, account); |
7731e32c AT |
781 | if (tsigkeyname.empty() == false) { |
782 | vector<string> meta; | |
6fe866b4 | 783 | meta.push_back(tsigkeyname.toStringNoDot()); |
7731e32c AT |
784 | db->setDomainMetadata(p->qdomain, "AXFR-MASTER-TSIG", meta); |
785 | } | |
8c80c4f4 | 786 | } |
3f81d239 | 787 | catch(PDNSException& ae) { |
e6a9dde5 | 788 | g_log<<Logger::Error<<"Database error trying to create "<<p->qdomain<<" for potential supermaster "<<remote<<": "<<ae.reason<<endl; |
8c80c4f4 BH |
789 | return RCode::ServFail; |
790 | } | |
e6a9dde5 | 791 | g_log<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<remote<<endl; |
12c86877 BH |
792 | return RCode::NoError; |
793 | } | |
794 | ||
3777f434 | 795 | int PacketHandler::processNotify(DNSPacket *p) |
12c86877 BH |
796 | { |
797 | /* now what? | |
798 | was this notification from an approved address? | |
c68ab952 | 799 | was this notification approved by TSIG? |
12c86877 BH |
800 | We determine our internal SOA id (via UeberBackend) |
801 | We determine the SOA at our (known) master | |
802 | if master is higher -> do stuff | |
803 | */ | |
c68ab952 | 804 | vector<string> meta; |
c68ab952 | 805 | |
dad0736b | 806 | if(!::arg().mustDo("slave") && s_forwardNotify.empty()) { |
e6a9dde5 | 807 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl; |
12c86877 BH |
808 | return RCode::NotImp; |
809 | } | |
d207ad63 | 810 | |
c68ab952 | 811 | if(!s_allowNotifyFrom.match((ComboAddress *) &p->d_remote ) || p->d_havetsig) { |
7731e32c | 812 | if (p->d_havetsig && p->getTSIGKeyname().empty() == false) { |
e6a9dde5 | 813 | g_log<<Logger::Notice<<"Received secure NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<", allowed by TSIG key '"<<p->getTSIGKeyname()<<"'"<<endl; |
c68ab952 | 814 | } else { |
e6a9dde5 | 815 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but remote is not permitted by TSIG or allow-notify-from"<<endl; |
c68ab952 AT |
816 | return RCode::Refused; |
817 | } | |
d207ad63 RK |
818 | } |
819 | ||
12c86877 BH |
820 | DNSBackend *db=0; |
821 | DomainInfo di; | |
f7fb7022 | 822 | di.serial = 0; |
c38f6509 | 823 | if(!B.getDomainInfo(p->qdomain, di) || !(db=di.backend)) { |
e6a9dde5 | 824 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl; |
7731e32c | 825 | return trySuperMaster(p, p->getTSIGKeyname()); |
12c86877 | 826 | } |
c68ab952 | 827 | |
c68ab952 AT |
828 | meta.clear(); |
829 | if (B.getDomainMetadata(p->qdomain,"AXFR-MASTER-TSIG",meta) && meta.size() > 0) { | |
2fa2a51a | 830 | if (!p->d_havetsig) { |
771bb0b0 | 831 | if (::arg().mustDo("allow-unsigned-notify")) { |
e6a9dde5 | 832 | g_log<<Logger::Warning<<"Received unsigned NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<": permitted because allow-unsigned-notify"; |
771bb0b0 | 833 | } else { |
e6a9dde5 | 834 | g_log<<Logger::Warning<<"Received unsigned NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<": refused"<<endl; |
771bb0b0 AT |
835 | return RCode::Refused; |
836 | } | |
2fa2a51a | 837 | } else if (meta[0] != p->getTSIGKeyname().toStringNoDot()) { |
e6a9dde5 | 838 | g_log<<Logger::Error<<"Received secure NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<": expected TSIG key '"<<meta[0]<<", got '"<<p->getTSIGKeyname()<<"'"<<endl; |
c68ab952 AT |
839 | return RCode::Refused; |
840 | } | |
841 | } | |
842 | ||
ded6b08d | 843 | if(::arg().contains("trusted-notification-proxy", p->getRemote().toString())) { |
e6a9dde5 | 844 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl; |
f7fb7022 | 845 | if(di.masters.empty()) { |
e6a9dde5 | 846 | g_log<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined"<<endl; |
f7fb7022 BH |
847 | return RCode::Refused; |
848 | } | |
f7fb7022 | 849 | } |
d2adca9d | 850 | else if(::arg().mustDo("master") && di.kind == DomainInfo::Master) { |
e6a9dde5 | 851 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but we are master, rejecting"<<endl; |
d2adca9d PL |
852 | return RCode::Refused; |
853 | } | |
ded6b08d | 854 | else if(!db->isMaster(p->qdomain, p->getRemote().toString())) { |
e6a9dde5 | 855 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl; |
12c86877 BH |
856 | return RCode::Refused; |
857 | } | |
e6e4836c | 858 | |
7f3d870e | 859 | // ok, we've done our checks |
a16e8e3a | 860 | di.backend = 0; |
dad0736b RC |
861 | |
862 | if(!s_forwardNotify.empty()) { | |
863 | set<string> forwardNotify(s_forwardNotify); | |
864 | for(set<string>::const_iterator j=forwardNotify.begin();j!=forwardNotify.end();++j) { | |
e6a9dde5 | 865 | g_log<<Logger::Warning<<"Relaying notification of domain "<<p->qdomain<<" from "<<p->getRemote()<<" to "<<*j<<endl; |
dad0736b RC |
866 | Communicator.notify(p->qdomain,*j); |
867 | } | |
868 | } | |
869 | ||
870 | if(::arg().mustDo("slave")) | |
871 | Communicator.addSlaveCheckRequest(di, p->d_remote); | |
7f3d870e | 872 | return 0; |
12c86877 BH |
873 | } |
874 | ||
1d563353 KM |
875 | bool validDNSName(const DNSName &name) |
876 | { | |
fc41a1a6 KM |
877 | if (!g_8bitDNS) { |
878 | string::size_type pos, length; | |
879 | char c; | |
880 | for(const auto& s : name.getRawLabels()) { | |
881 | length=s.length(); | |
882 | for(pos=0; pos < length; ++pos) { | |
883 | c=s[pos]; | |
884 | if(!((c >= 'a' && c <= 'z') || | |
885 | (c >= 'A' && c <= 'Z') || | |
886 | (c >= '0' && c <= '9') || | |
887 | c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) | |
888 | return false; | |
889 | } | |
1d563353 KM |
890 | } |
891 | } | |
892 | return true; | |
893 | } | |
894 | ||
12c86877 BH |
895 | DNSPacket *PacketHandler::question(DNSPacket *p) |
896 | { | |
5704e107 PD |
897 | DNSPacket *ret; |
898 | ||
899 | if(d_pdl) | |
900 | { | |
901 | ret=d_pdl->prequery(p); | |
902 | if(ret) | |
903 | return ret; | |
904 | } | |
905 | ||
bb6e54fe | 906 | if(p->d.rd) { |
1566533a | 907 | static AtomicCounter &rdqueries=*S.getPointer("rd-queries"); |
bb6e54fe | 908 | rdqueries++; |
909 | } | |
910 | ||
da286f66 | 911 | return doQuestion(p); |
ff76e8b4 BH |
912 | } |
913 | ||
90ba52e0 | 914 | |
675fa24c | 915 | void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd) |
35fe50c3 | 916 | { |
90ba52e0 | 917 | DNSZoneRecord rr; |
918 | rr.dr.d_name=sd.qname; | |
919 | rr.dr.d_type=QType::SOA; | |
920 | ||
921 | rr.dr.d_content=makeSOAContent(sd); | |
922 | rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); | |
794c2f92 | 923 | rr.signttl=sd.ttl; |
35fe50c3 | 924 | rr.domain_id=sd.domain_id; |
90ba52e0 | 925 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
0957a99f | 926 | rr.auth = 1; |
af7d3ea6 | 927 | rr.scopeMask = sd.scopeMask; |
35fe50c3 | 928 | r->addRecord(rr); |
6865d5c0 | 929 | |
52e0d783 | 930 | if(d_dk.isSecuredZone(sd.qname)) |
c5c4fbdc | 931 | addNSECX(p, r, target, wildcard, sd.qname, 4); |
6865d5c0 KM |
932 | |
933 | r->setRcode(RCode::NXDomain); | |
35fe50c3 BH |
934 | } |
935 | ||
675fa24c | 936 | void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd, int mode) |
35fe50c3 | 937 | { |
90ba52e0 | 938 | DNSZoneRecord rr; |
939 | rr.dr.d_name=sd.qname; | |
940 | rr.dr.d_type=QType::SOA; | |
941 | rr.dr.d_content=makeSOAContent(sd); | |
942 | rr.dr.d_ttl=sd.ttl; | |
943 | rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); | |
794c2f92 | 944 | rr.signttl=sd.ttl; |
35fe50c3 | 945 | rr.domain_id=sd.domain_id; |
90ba52e0 | 946 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
bccefefa | 947 | rr.auth = 1; |
35fe50c3 | 948 | r->addRecord(rr); |
ed9c3a50 | 949 | |
52e0d783 | 950 | if(d_dk.isSecuredZone(sd.qname)) |
c5c4fbdc | 951 | addNSECX(p, r, target, wildcard, sd.qname, mode); |
9951e2d0 | 952 | |
74f6d6e1 | 953 | S.ringAccount("noerror-queries",p->qdomain.toLogString()+"/"+p->qtype.getName()); |
35fe50c3 BH |
954 | } |
955 | ||
956 | ||
675fa24c | 957 | bool PacketHandler::addDSforNS(DNSPacket* p, DNSPacket* r, SOAData& sd, const DNSName& dsname) |
35fe50c3 | 958 | { |
e4090157 | 959 | //cerr<<"Trying to find a DS for '"<<dsname<<"', domain_id = "<<sd.domain_id<<endl; |
35fe50c3 | 960 | B.lookup(QType(QType::DS), dsname, p, sd.domain_id); |
90ba52e0 | 961 | DNSZoneRecord rr; |
35fe50c3 BH |
962 | bool gotOne=false; |
963 | while(B.get(rr)) { | |
964 | gotOne=true; | |
90ba52e0 | 965 | rr.dr.d_place = DNSResourceRecord::AUTHORITY; |
35fe50c3 BH |
966 | r->addRecord(rr); |
967 | } | |
968 | return gotOne; | |
969 | } | |
970 | ||
675fa24c | 971 | bool PacketHandler::tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const DNSName &target, bool retargeted) |
35fe50c3 | 972 | { |
90ba52e0 | 973 | vector<DNSZoneRecord> rrset = getBestReferralNS(p, sd, target); |
35fe50c3 BH |
974 | if(rrset.empty()) |
975 | return false; | |
976 | ||
424b92c6 | 977 | for(auto& rr: rrset) { |
90ba52e0 | 978 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
35fe50c3 BH |
979 | r->addRecord(rr); |
980 | } | |
d2323cd0 PD |
981 | if(!retargeted) |
982 | r->setA(false); | |
35fe50c3 | 983 | |
90ba52e0 | 984 | if(d_dk.isSecuredZone(sd.qname) && !addDSforNS(p, r, sd, rrset.begin()->dr.d_name)) |
985 | addNSECX(p, r, rrset.begin()->dr.d_name, DNSName(), sd.qname, 1); | |
35fe50c3 BH |
986 | |
987 | return true; | |
988 | } | |
989 | ||
675fa24c | 990 | void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, const DNSName &target) |
35fe50c3 BH |
991 | { |
992 | if(!p->d_dnssecOk) | |
df554502 | 993 | return; // Don't send dnssec info to non validating resolvers. |
52e0d783 | 994 | |
d3e7090c | 995 | if(!d_dk.isSecuredZone(sd.qname)) |
fbcdac7e | 996 | return; |
f889ab99 | 997 | |
290a083d | 998 | addNSECX(p, r, target, DNSName(), sd.qname, 5); |
e325f20c | 999 | if(sd.qname == p->qdomain) { |
794c2f92 | 1000 | addDNSKEY(p, r, sd); |
f889ab99 | 1001 | addCDNSKEY(p, r, sd); |
ef542223 | 1002 | addCDS(p, r, sd); |
794c2f92 | 1003 | addNSEC3PARAM(p, r, sd); |
70b18120 | 1004 | } |
35fe50c3 BH |
1005 | } |
1006 | ||
675fa24c | 1007 | bool PacketHandler::tryDNAME(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName &target) |
8dee0750 | 1008 | { |
1009 | if(!d_doDNAME) | |
1010 | return false; | |
e6a9dde5 | 1011 | DLOG(g_log<<Logger::Warning<<"Let's try DNAME.."<<endl); |
90ba52e0 | 1012 | vector<DNSZoneRecord> rrset = getBestDNAMESynth(p, sd, target); |
8dee0750 | 1013 | if(!rrset.empty()) { |
424b92c6 | 1014 | for(auto& rr: rrset) { |
90ba52e0 | 1015 | rr.dr.d_place = DNSResourceRecord::ANSWER; |
8dee0750 | 1016 | r->addRecord(rr); |
1017 | } | |
1018 | return true; | |
1019 | } | |
1020 | return false; | |
1021 | } | |
675fa24c | 1022 | bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName &target, DNSName &wildcard, bool& retargeted, bool& nodata) |
35fe50c3 | 1023 | { |
e3f388cd | 1024 | retargeted = nodata = false; |
561434a6 | 1025 | DNSName bestmatch; |
35fe50c3 | 1026 | |
90ba52e0 | 1027 | vector<DNSZoneRecord> rrset; |
75a89ce6 | 1028 | if(!getBestWildcard(p, sd, target, wildcard, &rrset)) |
35fe50c3 BH |
1029 | return false; |
1030 | ||
e3f388cd | 1031 | if(rrset.empty()) { |
e6a9dde5 | 1032 | DLOG(g_log<<"Wildcard matched something, but not of the correct type"<<endl); |
e3f388cd BH |
1033 | nodata=true; |
1034 | } | |
1035 | else { | |
424b92c6 | 1036 | for(auto& rr: rrset) { |
90ba52e0 | 1037 | rr.wildcardname = rr.dr.d_name; |
1038 | rr.dr.d_name=bestmatch=target; | |
bcb8aebe | 1039 | |
90ba52e0 | 1040 | if(rr.dr.d_type == QType::CNAME) { |
e3f388cd | 1041 | retargeted=true; |
90ba52e0 | 1042 | target=getRR<CNAMERecordContent>(rr.dr)->getTarget(); |
e3f388cd BH |
1043 | } |
1044 | ||
90ba52e0 | 1045 | rr.dr.d_place=DNSResourceRecord::ANSWER; |
e3f388cd | 1046 | r->addRecord(rr); |
35fe50c3 | 1047 | } |
35fe50c3 | 1048 | } |
52e0d783 | 1049 | if(d_dk.isSecuredZone(sd.qname) && !nodata) { |
c5c4fbdc | 1050 | addNSECX(p, r, bestmatch, wildcard, sd.qname, 3); |
35fe50c3 BH |
1051 | } |
1052 | return true; | |
1053 | } | |
1054 | ||
ff76e8b4 | 1055 | //! Called by the Distributor to ask a question. Returns 0 in case of an error |
e89efca5 | 1056 | DNSPacket *PacketHandler::doQuestion(DNSPacket *p) |
ff76e8b4 | 1057 | { |
90ba52e0 | 1058 | DNSZoneRecord rr; |
12c86877 | 1059 | SOAData sd; |
81c486ad | 1060 | |
12c86877 | 1061 | int retargetcount=0; |
675fa24c | 1062 | set<DNSName> authSet; |
35fe50c3 | 1063 | |
90ba52e0 | 1064 | vector<DNSZoneRecord> rrset; |
35fe50c3 | 1065 | bool weDone=0, weRedirected=0, weHaveUnauth=0; |
561434a6 | 1066 | DNSName haveAlias; |
12c86877 | 1067 | |
0c127168 | 1068 | DNSPacket *r=0; |
78bcb858 | 1069 | bool noCache=false; |
a16e8e3a BH |
1070 | |
1071 | if(p->d.qr) { // QR bit from dns packet (thanks RA from N) | |
641d32aa | 1072 | if(d_logDNSDetails) |
e6a9dde5 | 1073 | g_log<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl; |
a16e8e3a | 1074 | S.inc("corrupt-packets"); |
41aacb6a | 1075 | S.ringAccount("remotes-corrupt", p->d_remote); |
a16e8e3a BH |
1076 | return 0; |
1077 | } | |
1078 | ||
43b50405 CH |
1079 | if(p->d.tc) { // truncated query. MOADNSParser would silently parse this packet in an incomplete way. |
1080 | if(d_logDNSDetails) | |
e6a9dde5 | 1081 | g_log<<Logger::Error<<"Received truncated query packet from "<<p->getRemote()<<", dropping"<<endl; |
43b50405 CH |
1082 | S.inc("corrupt-packets"); |
1083 | S.ringAccount("remotes-corrupt", p->d_remote); | |
1084 | return 0; | |
1085 | } | |
1086 | ||
298fabc3 AT |
1087 | if (p->hasEDNS() && p->getEDNSVersion() > 0) { |
1088 | r = p->replyPacket(); | |
1089 | r->setRcode(16 & 0xF); | |
1090 | r->setEDNSRcode((16 & 0xFFF0)>>4); // set rcode to BADVERS | |
1091 | return r; | |
1092 | } | |
1093 | ||
78bcb858 | 1094 | if(p->d_havetsig) { |
7abbc40f PD |
1095 | DNSName keyname; |
1096 | string secret; | |
78bcb858 | 1097 | TSIGRecordContent trc; |
ea3816cf | 1098 | if(!p->checkForCorrectTSIG(&B, &keyname, &secret, &trc)) { |
0c127168 | 1099 | r=p->replyPacket(); // generate an empty reply packet |
78bcb858 | 1100 | if(d_logDNSDetails) |
e6a9dde5 | 1101 | g_log<<Logger::Error<<"Received a TSIG signed message with a non-validating key"<<endl; |
f7a69a4c RA |
1102 | // RFC3007 describes that a non-secure message should be sending Refused for DNS Updates |
1103 | if (p->d.opcode == Opcode::Update) | |
914353ca KM |
1104 | r->setRcode(RCode::Refused); |
1105 | else | |
f7a69a4c | 1106 | r->setRcode(RCode::NotAuth); |
78bcb858 | 1107 | return r; |
7f9ac49b AT |
1108 | } else { |
1109 | getTSIGHashEnum(trc.d_algoName, p->d_tsig_algo); | |
1110 | if (p->d_tsig_algo == TSIG_GSS) { | |
1635f12b | 1111 | GssContext gssctx(keyname); |
7f9ac49b | 1112 | if (!gssctx.getPeerPrincipal(p->d_peer_principal)) { |
e6a9dde5 | 1113 | g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<keyname<<"'"<<endl; |
7f9ac49b AT |
1114 | } |
1115 | } | |
78bcb858 BH |
1116 | } |
1117 | p->setTSIGDetails(trc, keyname, secret, trc.d_mac); // this will get copied by replyPacket() | |
1118 | noCache=true; | |
1119 | } | |
1120 | ||
0c127168 | 1121 | r=p->replyPacket(); // generate an empty reply packet, possibly with TSIG details inside |
c00d7891 AT |
1122 | |
1123 | if (p->qtype == QType::TKEY) { | |
1124 | this->tkeyHandler(p, r); | |
1125 | return r; | |
1126 | } | |
1127 | ||
12c86877 | 1128 | try { |
12c86877 BH |
1129 | |
1130 | // XXX FIXME do this in DNSPacket::parse ? | |
1131 | ||
1d563353 KM |
1132 | if(!validDNSName(p->qdomain)) { |
1133 | if(d_logDNSDetails) | |
e6a9dde5 | 1134 | g_log<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': sending servfail"<<endl; |
1d563353 KM |
1135 | S.inc("corrupt-packets"); |
1136 | S.ringAccount("remotes-corrupt", p->d_remote); | |
1137 | S.inc("servfail-packets"); | |
1138 | r->setRcode(RCode::ServFail); | |
1139 | return r; | |
1140 | } | |
12c86877 BH |
1141 | if(p->d.opcode) { // non-zero opcode (again thanks RA!) |
1142 | if(p->d.opcode==Opcode::Update) { | |
71f758e0 | 1143 | S.inc("dnsupdate-queries"); |
f7a69a4c | 1144 | int res=processUpdate(p); |
63cb8c10 | 1145 | if (res == RCode::Refused) |
71f758e0 | 1146 | S.inc("dnsupdate-refused"); |
63cb8c10 | 1147 | else if (res != RCode::ServFail) |
71f758e0 | 1148 | S.inc("dnsupdate-answers"); |
f7a69a4c RA |
1149 | r->setRcode(res); |
1150 | r->setOpcode(Opcode::Update); | |
1151 | return r; | |
12c86877 BH |
1152 | } |
1153 | else if(p->d.opcode==Opcode::Notify) { | |
93aecccc | 1154 | S.inc("incoming-notifications"); |
4957a608 BH |
1155 | int res=processNotify(p); |
1156 | if(res>=0) { | |
4957a608 BH |
1157 | r->setRcode(res); |
1158 | r->setOpcode(Opcode::Notify); | |
1159 | return r; | |
1160 | } | |
f3a91936 | 1161 | delete r; |
4957a608 | 1162 | return 0; |
12c86877 BH |
1163 | } |
1164 | ||
e6a9dde5 | 1165 | g_log<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl; |
12c86877 | 1166 | |
12c86877 BH |
1167 | r->setRcode(RCode::NotImp); |
1168 | return r; | |
1169 | } | |
c2413a68 | 1170 | |
e6a9dde5 | 1171 | // g_log<<Logger::Warning<<"Query for '"<<p->qdomain<<"' "<<p->qtype.getName()<<" from "<<p->getRemote()<< " (tcp="<<p->d_tcp<<")"<<endl; |
12c86877 | 1172 | |
dc45a198 | 1173 | if(p->qtype.getCode()==QType::IXFR) { |
8d7543ba | 1174 | r->setRcode(RCode::Refused); |
dc45a198 BH |
1175 | return r; |
1176 | } | |
1177 | ||
561434a6 | 1178 | DNSName target=p->qdomain; |
c76a16b7 | 1179 | |
49e8d5d5 KM |
1180 | // catch chaos qclass requests |
1181 | if(p->qclass == QClass::CHAOS) { | |
1182 | if (doChaosRequest(p,r,target)) | |
1183 | goto sendit; | |
1184 | else | |
1185 | return r; | |
1186 | } | |
12c86877 | 1187 | |
8d7543ba | 1188 | // we only know about qclass IN (and ANY), send Refused for everything else. |
c76a16b7 | 1189 | if(p->qclass != QClass::IN && p->qclass!=QClass::ANY) { |
8d7543ba | 1190 | r->setRcode(RCode::Refused); |
c4ac5865 BH |
1191 | return r; |
1192 | } | |
12c86877 | 1193 | |
ec62f3c0 KM |
1194 | // send TC for udp ANY query if any-to-tcp is enabled. |
1195 | if(p->qtype.getCode() == QType::ANY && !p->d_tcp && g_anyToTcp) { | |
abc8f3f9 | 1196 | r->d.tc = 1; |
1197 | r->commitD(); | |
1198 | return r; | |
1199 | } | |
1200 | ||
49e8d5d5 | 1201 | // for qclass ANY the response should never be authoritative unless the response covers all classes. |
c76a16b7 | 1202 | if(p->qclass==QClass::ANY) |
12c86877 | 1203 | r->setA(false); |
c76a16b7 | 1204 | |
12c86877 BH |
1205 | |
1206 | retargeted:; | |
35fe50c3 | 1207 | if(retargetcount > 10) { // XXX FIXME, retargetcount++? |
e6a9dde5 | 1208 | g_log<<Logger::Warning<<"Abort CNAME chain resolution after "<<--retargetcount<<" redirects, sending out servfail. Initial query: '"<<p->qdomain<<"'"<<endl; |
75c8ebb4 KM |
1209 | delete r; |
1210 | r=p->replyPacket(); | |
12c86877 | 1211 | r->setRcode(RCode::ServFail); |
35fe50c3 | 1212 | return r; |
12c86877 | 1213 | } |
507823d1 | 1214 | |
cec52de6 | 1215 | if(!B.getAuth(target, p->qtype, &sd)) { |
e6a9dde5 | 1216 | DLOG(g_log<<Logger::Error<<"We have no authority over zone '"<<target<<"'"<<endl); |
40fc506f | 1217 | if(!retargetcount) { |
8d3cbffa | 1218 | r->setA(false); // drop AA if we never had a SOA in the first place |
40fc506f | 1219 | r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea' |
82cc1f71 | 1220 | } |
507823d1 BH |
1221 | goto sendit; |
1222 | } | |
e6a9dde5 | 1223 | DLOG(g_log<<Logger::Error<<"We have authority, zone='"<<sd.qname<<"', id="<<sd.domain_id<<endl); |
8d3cbffa | 1224 | authSet.insert(sd.qname); |
12c86877 | 1225 | |
3e8216c8 PD |
1226 | if(!retargetcount) r->qdomainzone=sd.qname; |
1227 | ||
e325f20c | 1228 | if(sd.qname==p->qdomain) { |
794c2f92 PD |
1229 | if(p->qtype.getCode() == QType::DNSKEY) |
1230 | { | |
1231 | if(addDNSKEY(p, r, sd)) | |
1232 | goto sendit; | |
1233 | } | |
088370cd PL |
1234 | else if(p->qtype.getCode() == QType::CDNSKEY) |
1235 | { | |
f889ab99 | 1236 | if(addCDNSKEY(p,r, sd)) |
088370cd PL |
1237 | goto sendit; |
1238 | } | |
ef542223 PL |
1239 | else if(p->qtype.getCode() == QType::CDS) |
1240 | { | |
1241 | if(addCDS(p,r, sd)) | |
1242 | goto sendit; | |
1243 | } | |
dacacb23 | 1244 | else if(p->qtype.getCode() == QType::NSEC3PARAM && d_dk.isSecuredZone(sd.qname)) |
794c2f92 PD |
1245 | { |
1246 | if(addNSEC3PARAM(p,r, sd)) | |
1247 | goto sendit; | |
1248 | } | |
fbcdac7e BH |
1249 | } |
1250 | ||
e325f20c | 1251 | if(p->qtype.getCode() == QType::SOA && sd.qname==p->qdomain) { |
90ba52e0 | 1252 | rr.dr.d_name=sd.qname; |
1253 | rr.dr.d_type=QType::SOA; | |
13f9e280 | 1254 | sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname); |
90ba52e0 | 1255 | rr.dr.d_content=makeSOAContent(sd); |
1256 | rr.dr.d_ttl=sd.ttl; | |
507823d1 | 1257 | rr.domain_id=sd.domain_id; |
90ba52e0 | 1258 | rr.dr.d_place=DNSResourceRecord::ANSWER; |
d24589bc | 1259 | rr.auth = true; |
507823d1 BH |
1260 | r->addRecord(rr); |
1261 | goto sendit; | |
1262 | } | |
12c86877 | 1263 | |
35fe50c3 | 1264 | // this TRUMPS a cname! |
52e0d783 | 1265 | if(p->qtype.getCode() == QType::NSEC && d_dk.isSecuredZone(sd.qname) && !d_dk.getNSEC3PARAM(sd.qname, 0)) { |
290a083d | 1266 | addNSEC(p, r, target, DNSName(), sd.qname, 5); |
7cd99296 KM |
1267 | if (!r->isEmpty()) |
1268 | goto sendit; | |
12c86877 | 1269 | } |
571726e9 | 1270 | |
35fe50c3 | 1271 | // this TRUMPS a cname! |
ec62f3c0 | 1272 | if(p->qtype.getCode() == QType::RRSIG) { |
e6a9dde5 | 1273 | g_log<<Logger::Info<<"Direct RRSIG query for "<<target<<" from "<<p->getRemote()<<endl; |
8d7543ba | 1274 | r->setRcode(RCode::Refused); |
52e0d783 | 1275 | goto sendit; |
cac7e485 | 1276 | } |
35fe50c3 | 1277 | |
e6a9dde5 | 1278 | DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<<endl); |
d2323cd0 | 1279 | if(p->qtype.getCode() != QType::DS && tryReferral(p, r, sd, target, retargetcount)) |
571726e9 PD |
1280 | goto sendit; |
1281 | ||
e6a9dde5 | 1282 | DLOG(g_log<<"Got no referrals, trying ANY"<<endl); |
571726e9 | 1283 | |
35fe50c3 BH |
1284 | // see what we get.. |
1285 | B.lookup(QType(QType::ANY), target, p, sd.domain_id); | |
1286 | rrset.clear(); | |
561434a6 | 1287 | haveAlias.trimToLabels(0); |
8dee0750 | 1288 | weDone = weRedirected = weHaveUnauth = false; |
35fe50c3 BH |
1289 | |
1290 | while(B.get(rr)) { | |
1fda8e87 | 1291 | //cerr<<"got content: ["<<rr.content<<"]"<<endl; |
90ba52e0 | 1292 | if (p->qtype.getCode() == QType::ANY && !p->d_dnssecOk && (rr.dr.d_type == QType:: DNSKEY || rr.dr.d_type == QType::NSEC3PARAM)) |
65538369 | 1293 | continue; // Don't send dnssec info to non validating resolvers. |
90ba52e0 | 1294 | if (rr.dr.d_type == QType::RRSIG) // RRSIGS are added later any way. |
65538369 | 1295 | continue; // TODO: this actually means addRRSig should check if the RRSig is already there |
794c2f92 | 1296 | |
90ba52e0 | 1297 | // cerr<<"Auth: "<<rr.auth<<", "<<(rr.dr.d_type == p->qtype)<<", "<<rr.dr.d_type.getName()<<endl; |
1298 | if((p->qtype.getCode() == QType::ANY || rr.dr.d_type == p->qtype.getCode()) && rr.auth) | |
82cc1f71 | 1299 | weDone=1; |
5b739515 | 1300 | // the line below fakes 'unauth NS' for delegations for non-DNSSEC backends. |
90ba52e0 | 1301 | if((rr.dr.d_type == p->qtype.getCode() && !rr.auth) || (rr.dr.d_type == QType::NS && (!rr.auth || !(sd.qname==rr.dr.d_name)))) |
82cc1f71 | 1302 | weHaveUnauth=1; |
35fe50c3 | 1303 | |
90ba52e0 | 1304 | if(rr.dr.d_type == QType::CNAME && p->qtype.getCode() != QType::CNAME) |
82cc1f71 | 1305 | weRedirected=1; |
1dfd8ada | 1306 | |
90ba52e0 | 1307 | if(DP && rr.dr.d_type == QType::ALIAS && (p->qtype.getCode() == QType::A || p->qtype.getCode() == QType::AAAA || p->qtype.getCode() == QType::ANY)) { |
389b7a05 | 1308 | if (!d_doExpandALIAS) { |
e6a9dde5 | 1309 | g_log<<Logger::Info<<"ALIAS record found for "<<target<<", but ALIAS expansion is disabled."<<endl; |
389b7a05 PL |
1310 | continue; |
1311 | } | |
90ba52e0 | 1312 | haveAlias=getRR<ALIASRecordContent>(rr.dr)->d_content; |
d59b894d | 1313 | } |
1314 | ||
1dfd8ada | 1315 | // Filter out all SOA's and add them in later |
90ba52e0 | 1316 | if(rr.dr.d_type == QType::SOA) |
1dfd8ada MZ |
1317 | continue; |
1318 | ||
35fe50c3 | 1319 | rrset.push_back(rr); |
12c86877 BH |
1320 | } |
1321 | ||
1dfd8ada | 1322 | /* Add in SOA if required */ |
e325f20c | 1323 | if(target==sd.qname) { |
13f9e280 | 1324 | rr.dr.d_name = sd.qname; |
90ba52e0 | 1325 | rr.dr.d_type = QType::SOA; |
13f9e280 | 1326 | sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname); |
90ba52e0 | 1327 | rr.dr.d_content = makeSOAContent(sd); |
90ba52e0 | 1328 | rr.dr.d_ttl = sd.ttl; |
1dfd8ada MZ |
1329 | rr.domain_id = sd.domain_id; |
1330 | rr.auth = true; | |
1331 | rrset.push_back(rr); | |
1332 | } | |
1333 | ||
8dee0750 | 1334 | |
e6a9dde5 | 1335 | DLOG(g_log<<"After first ANY query for '"<<target<<"', id="<<sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<haveAlias<<"'"<<endl); |
849bd7f1 | 1336 | if(p->qtype.getCode() == QType::DS && weHaveUnauth && !weDone && !weRedirected && d_dk.isSecuredZone(sd.qname)) { |
e6a9dde5 | 1337 | DLOG(g_log<<"Q for DS of a name for which we do have NS, but for which we don't have on a zone with DNSSEC need to provide an AUTH answer that proves we don't"<<endl); |
290a083d | 1338 | makeNOError(p, r, target, DNSName(), sd, 1); |
849bd7f1 BH |
1339 | goto sendit; |
1340 | } | |
12c86877 | 1341 | |
65d2032b | 1342 | if(!haveAlias.empty() && (!weDone || p->qtype.getCode() == QType::ANY)) { |
e6a9dde5 | 1343 | DLOG(g_log<<Logger::Warning<<"Found nothing that matched for '"<<target<<"', but did get alias to '"<<haveAlias<<"', referring"<<endl); |
d59b894d | 1344 | DP->completePacket(r, haveAlias, target); |
1345 | return 0; | |
1346 | } | |
1347 | ||
35fe50c3 | 1348 | if(rrset.empty()) { |
e6a9dde5 | 1349 | DLOG(g_log<<"checking qtype.getCode() ["<<(p->qtype.getCode())<<"] against QType::DS ["<<(QType::DS)<<"]"<<endl); |
0a0f4112 PD |
1350 | if(p->qtype.getCode() == QType::DS) |
1351 | { | |
e6a9dde5 | 1352 | DLOG(g_log<<"DS query found no direct result, trying referral now"<<endl); |
d2323cd0 | 1353 | if(tryReferral(p, r, sd, target, retargetcount)) |
0a0f4112 | 1354 | { |
e6a9dde5 | 1355 | DLOG(g_log<<"got referral for DS query"<<endl); |
0a0f4112 PD |
1356 | goto sendit; |
1357 | } | |
1358 | } | |
1359 | ||
d59b894d | 1360 | |
e6a9dde5 | 1361 | DLOG(g_log<<Logger::Warning<<"Found nothing in the by-name ANY, but let's try wildcards.."<<endl); |
e3f388cd | 1362 | bool wereRetargeted(false), nodata(false); |
561434a6 | 1363 | DNSName wildcard; |
75a89ce6 | 1364 | if(tryWildcard(p, r, sd, target, wildcard, wereRetargeted, nodata)) { |
82cc1f71 | 1365 | if(wereRetargeted) { |
3e8216c8 | 1366 | if(!retargetcount) r->qdomainwild=wildcard; |
82cc1f71 BH |
1367 | retargetcount++; |
1368 | goto retargeted; | |
1369 | } | |
c5c4fbdc PD |
1370 | if(nodata) |
1371 | makeNOError(p, r, target, wildcard, sd, 2); | |
1372 | ||
82cc1f71 | 1373 | goto sendit; |
8e50cd4c | 1374 | } |
8dee0750 | 1375 | else if(tryDNAME(p, r, sd, target)) { |
1376 | retargetcount++; | |
1377 | goto retargeted; | |
1378 | } | |
a87b7e3f PD |
1379 | else |
1380 | { | |
c5c4fbdc PD |
1381 | if (!(((p->qtype.getCode() == QType::CNAME) || (p->qtype.getCode() == QType::ANY)) && retargetcount > 0)) |
1382 | makeNXDomain(p, r, target, wildcard, sd); | |
a87b7e3f PD |
1383 | } |
1384 | ||
82cc1f71 BH |
1385 | goto sendit; |
1386 | } | |
232f0877 | 1387 | |
35fe50c3 | 1388 | if(weRedirected) { |
2010ac95 RG |
1389 | for(auto& loopRR: rrset) { |
1390 | if(loopRR.dr.d_type == QType::CNAME) { | |
1391 | r->addRecord(loopRR); | |
1392 | target = getRR<CNAMERecordContent>(loopRR.dr)->getTarget(); | |
82cc1f71 BH |
1393 | retargetcount++; |
1394 | goto retargeted; | |
4957a608 | 1395 | } |
82cc1f71 | 1396 | } |
82cc1f71 | 1397 | } |
35fe50c3 | 1398 | else if(weDone) { |
b5baefaf | 1399 | bool haveRecords = false; |
2010ac95 RG |
1400 | for(const auto& loopRR: rrset) { |
1401 | if((p->qtype.getCode() == QType::ANY || loopRR.dr.d_type == p->qtype.getCode()) && loopRR.dr.d_type && loopRR.dr.d_type != QType::ALIAS && loopRR.auth) { | |
1402 | r->addRecord(loopRR); | |
b5baefaf PD |
1403 | haveRecords = true; |
1404 | } | |
82cc1f71 | 1405 | } |
4957a608 | 1406 | |
b5baefaf PD |
1407 | if (haveRecords) { |
1408 | if(p->qtype.getCode() == QType::ANY) | |
1409 | completeANYRecords(p, r, sd, target); | |
82cc1f71 | 1410 | } |
b5baefaf | 1411 | else |
b9817f2b | 1412 | makeNOError(p, r, target, DNSName(), sd, 0); |
4957a608 | 1413 | |
35fe50c3 | 1414 | goto sendit; |
82cc1f71 | 1415 | } |
35fe50c3 | 1416 | else if(weHaveUnauth) { |
e6a9dde5 | 1417 | DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<<endl); |
d2323cd0 | 1418 | if(tryReferral(p, r, sd, target, retargetcount)) |
82cc1f71 | 1419 | goto sendit; |
2b18bcf3 | 1420 | // check whether this could be fixed easily |
90ba52e0 | 1421 | // if (*(rr.dr.d_name.rbegin()) == '.') { |
e6a9dde5 | 1422 | // g_log<<Logger::Error<<"Should not get here ("<<p->qdomain<<"|"<<p->qtype.getCode()<<"): you have a trailing dot, this could be the problem (or run pdnsutil rectify-zone " <<sd.qname<<")"<<endl; |
561434a6 | 1423 | // } else { |
e6a9dde5 | 1424 | g_log<<Logger::Error<<"Should not get here ("<<p->qdomain<<"|"<<p->qtype.getCode()<<"): please run pdnsutil rectify-zone "<<sd.qname<<endl; |
561434a6 | 1425 | // } |
82cc1f71 BH |
1426 | } |
1427 | else { | |
e6a9dde5 | 1428 | DLOG(g_log<<"Have some data, but not the right data"<<endl); |
290a083d | 1429 | makeNOError(p, r, target, DNSName(), sd, 0); |
82cc1f71 | 1430 | } |
12c86877 BH |
1431 | |
1432 | sendit:; | |
d2323cd0 | 1433 | if(doAdditionalProcessingAndDropAA(p, r, sd, retargetcount)<0) { |
f3a91936 | 1434 | delete r; |
12c86877 | 1435 | return 0; |
f3a91936 | 1436 | } |
8ea10bfc | 1437 | |
2010ac95 RG |
1438 | for(const auto& loopRR: r->getRRS()) { |
1439 | if(loopRR.scopeMask) { | |
bf269e28 | 1440 | noCache=true; |
c28bf199 | 1441 | break; |
606018f2 | 1442 | } |
1443 | } | |
e02d0a59 | 1444 | if(p->d_dnssecOk) |
8d3cbffa | 1445 | addRRSigs(d_dk, B, authSet, r->getRRS()); |
f3a91936 | 1446 | |
e02d0a59 | 1447 | r->wrapup(); // needed for inserting in cache |
bf269e28 | 1448 | if(!noCache && p->couldBeCached()) |
071d2d90 | 1449 | PC.insert(p, r, r->getMinTTL()); // in the packet cache |
12c86877 BH |
1450 | } |
1451 | catch(DBException &e) { | |
e6a9dde5 | 1452 | g_log<<Logger::Error<<"Backend reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl; |
25e7af37 KM |
1453 | delete r; |
1454 | r=p->replyPacket(); // generate an empty reply packet | |
12c86877 | 1455 | r->setRcode(RCode::ServFail); |
eefd15f9 | 1456 | S.inc("servfail-packets"); |
74f6d6e1 | 1457 | S.ringAccount("servfail-queries",p->qdomain.toLogString()); |
12c86877 | 1458 | } |
3f81d239 | 1459 | catch(PDNSException &e) { |
e6a9dde5 | 1460 | g_log<<Logger::Error<<"Backend reported permanent error which prevented lookup ("+e.reason+"), aborting"<<endl; |
31d9bb01 | 1461 | throw; // we WANT to die at this point |
86113ac9 | 1462 | } |
5172cb78 | 1463 | catch(std::exception &e) { |
e6a9dde5 | 1464 | g_log<<Logger::Error<<"Exception building answer packet for "<<p->qdomain<<"/"<<p->qtype.getName()<<" ("<<e.what()<<") sending out servfail"<<endl; |
8ea10bfc | 1465 | delete r; |
25e7af37 | 1466 | r=p->replyPacket(); // generate an empty reply packet |
8ea10bfc BH |
1467 | r->setRcode(RCode::ServFail); |
1468 | S.inc("servfail-packets"); | |
74f6d6e1 | 1469 | S.ringAccount("servfail-queries",p->qdomain.toLogString()); |
8ea10bfc | 1470 | } |
12c86877 BH |
1471 | return r; |
1472 | ||
1473 | } |