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