]>
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 | ||
8900e2e3 | 359 | #ifdef HAVE_LUA_RECORDS |
cb6bd1a9 | 360 | bool doLua=g_doLuaRecord; |
25bcfaec | 361 | if(!doLua) { |
362 | string val; | |
27a630b4 | 363 | d_dk.getFromMeta(sd.qname, "ENABLE-LUA-RECORDS", val); |
25bcfaec | 364 | doLua = (val=="1"); |
365 | } | |
8900e2e3 | 366 | #endif |
25bcfaec | 367 | |
75a89ce6 | 368 | wildcard=subdomain; |
675fa24c | 369 | while( subdomain.chopOff() && !haveSomething ) { |
598b152c | 370 | if (subdomain.empty()) { |
12c06211 | 371 | B.lookup(QType(QType::ANY), g_wildcarddnsname, p, sd.domain_id); |
598b152c | 372 | } else { |
12c06211 | 373 | B.lookup(QType(QType::ANY), g_wildcarddnsname+subdomain, p, sd.domain_id); |
598b152c | 374 | } |
35fe50c3 | 375 | while(B.get(rr)) { |
8900e2e3 | 376 | #ifdef HAVE_LUA_RECORDS |
b7edebf8 | 377 | if(rr.dr.d_type == QType::LUA) { |
25bcfaec | 378 | if(!doLua) { |
f5f3d7f5 | 379 | DLOG(g_log<<"Have a wildcard LUA match, but not doing LUA record for this zone"<<endl); |
25bcfaec | 380 | continue; |
381 | } | |
382 | ||
f5f3d7f5 | 383 | DLOG(g_log<<"Have a wildcard LUA match"<<endl); |
25bcfaec | 384 | |
b7edebf8 | 385 | auto rec=getRR<LUARecordContent>(rr.dr); |
98fa3598 RG |
386 | if (!rec) { |
387 | continue; | |
388 | } | |
389 | if(rec->d_type == QType::CNAME || rec->d_type == p->qtype.getCode() || (p->qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) { | |
b7edebf8 | 390 | // noCache=true; |
f5f3d7f5 | 391 | DLOG(g_log<<"Executing Lua: '"<<rec->getCode()<<"'"<<endl); |
cc5c4f6b CHB |
392 | auto recvec=luaSynth(rec->getCode(), target, sd.qname, sd.domain_id, *p, rec->d_type); |
393 | for(const auto& r : recvec) { | |
394 | rr.dr.d_type = rec->d_type; // might be CNAME | |
395 | rr.dr.d_content = r; | |
396 | rr.scopeMask = p->getRealRemote().getBits(); // this makes sure answer is a specific as your question | |
397 | ret->push_back(rr); | |
b7edebf8 | 398 | } |
399 | } | |
400 | } | |
8900e2e3 CHB |
401 | else |
402 | #endif | |
403 | 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 | 404 | ret->push_back(rr); |
8900e2e3 | 405 | } |
b7edebf8 | 406 | |
12c06211 | 407 | wildcard=g_wildcarddnsname+subdomain; |
e3f388cd | 408 | haveSomething=true; |
35fe50c3 | 409 | } |
75a89ce6 PD |
410 | |
411 | if ( subdomain == sd.qname || haveSomething ) // stop at SOA or result | |
82cc1f71 | 412 | break; |
35fe50c3 | 413 | |
75a89ce6 PD |
414 | B.lookup(QType(QType::ANY), subdomain, p, sd.domain_id); |
415 | if (B.get(rr)) { | |
e6a9dde5 | 416 | DLOG(g_log<<"No wildcard match, ancestor exists"<<endl); |
75a89ce6 PD |
417 | while (B.get(rr)) ; |
418 | break; | |
419 | } | |
420 | wildcard=subdomain; | |
421 | } | |
422 | ||
423 | return haveSomething; | |
35fe50c3 BH |
424 | } |
425 | ||
12c86877 | 426 | /** dangling is declared true if we were unable to resolve everything */ |
d2323cd0 | 427 | int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r, const SOAData& soadata, bool retargeted) |
12c86877 | 428 | { |
90ba52e0 | 429 | DNSZoneRecord rr; |
fd8bc993 | 430 | SOAData sd; |
313923a8 | 431 | sd.db=0; |
b636533b | 432 | |
b8e0f341 | 433 | if(p->qtype.getCode()!=QType::AXFR) { // this packet needs additional processing |
aa7b2405 | 434 | // we now have a copy, push_back on packet might reallocate! |
435 | auto& records = r->getRRS(); | |
436 | vector<DNSZoneRecord> toAdd; | |
8513f025 | 437 | |
aa7b2405 | 438 | for(auto i = records.cbegin() ; i!= records.cend(); ++i) { |
439 | if(i->dr.d_place==DNSResourceRecord::ADDITIONAL || | |
440 | !(i->dr.d_type==QType::MX || i->dr.d_type==QType::NS || i->dr.d_type==QType::SRV)) | |
441 | continue; | |
8513f025 | 442 | |
94bfa5b6 | 443 | 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 | 444 | r->setA(false); |
232f0877 | 445 | // i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME |
f6ba332a | 446 | } |
fd8bc993 | 447 | |
90ba52e0 | 448 | DNSName lookup; |
449 | ||
450 | if(i->dr.d_type == QType::MX) | |
451 | lookup = getRR<MXRecordContent>(i->dr)->d_mxname; | |
452 | else if(i->dr.d_type == QType::SRV) | |
453 | lookup = getRR<SRVRecordContent>(i->dr)->d_target; | |
aa7b2405 | 454 | else if(i->dr.d_type == QType::NS) |
455 | lookup = getRR<NSRecordContent>(i->dr)->getNS(); | |
456 | else | |
457 | continue; | |
75676790 | 458 | |
137d738a | 459 | B.lookup(QType(d_doIPv6AdditionalProcessing ? QType::ANY : QType::A), lookup, p, soadata.domain_id); |
794c2f92 | 460 | |
0ece3f09 | 461 | while(B.get(rr)) { |
90ba52e0 | 462 | if(rr.dr.d_type != QType::A && rr.dr.d_type!=QType::AAAA) |
0ece3f09 | 463 | continue; |
87c261bc PL |
464 | if(!rr.dr.d_name.isPartOf(soadata.qname)) { |
465 | // FIXME we might still pass on the record if it is occluded and the | |
466 | // backend uses a single id for all zones | |
467 | continue; | |
468 | } | |
90ba52e0 | 469 | rr.dr.d_place=DNSResourceRecord::ADDITIONAL; |
aa7b2405 | 470 | toAdd.push_back(rr); |
b636533b | 471 | } |
12c86877 | 472 | } |
336f11d8 | 473 | for(const auto& rec : toAdd) |
474 | r->addRecord(rec); | |
475 | ||
476 | //records.insert(records.end(), toAdd.cbegin(), toAdd.cend()); // would be faster, but no dedup | |
12c86877 BH |
477 | } |
478 | return 1; | |
479 | } | |
480 | ||
35fe50c3 | 481 | |
ca617317 | 482 | void PacketHandler::emitNSEC(DNSPacket *r, const SOAData& sd, const DNSName& name, const DNSName& next, int mode) |
35fe50c3 | 483 | { |
35fe50c3 | 484 | NSECRecordContent nrc; |
ca617317 KM |
485 | nrc.d_next = next; |
486 | ||
27d4a65b RG |
487 | nrc.set(QType::NSEC); |
488 | nrc.set(QType::RRSIG); | |
576e7e0f | 489 | if(sd.qname == name) { |
27d4a65b RG |
490 | nrc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table |
491 | nrc.set(QType::DNSKEY); | |
088370cd | 492 | string publishCDNSKEY; |
0900d2d3 | 493 | d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY); |
088370cd | 494 | if (publishCDNSKEY == "1") |
27d4a65b | 495 | nrc.set(QType::CDNSKEY); |
ef542223 | 496 | string publishCDS; |
0900d2d3 | 497 | d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS); |
ef542223 | 498 | if (! publishCDS.empty()) |
27d4a65b | 499 | nrc.set(QType::CDS); |
576e7e0f | 500 | } |
35fe50c3 | 501 | |
90ba52e0 | 502 | DNSZoneRecord rr; |
576e7e0f | 503 | |
ca617317 | 504 | B.lookup(QType(QType::ANY), name, NULL, sd.domain_id); |
35fe50c3 | 505 | while(B.get(rr)) { |
8900e2e3 | 506 | #ifdef HAVE_LUA_RECORDS |
d0c7ecf1 | 507 | if(rr.dr.d_type == QType::LUA) |
27d4a65b | 508 | nrc.set(getRR<LUARecordContent>(rr.dr)->d_type); |
8900e2e3 CHB |
509 | else |
510 | #endif | |
511 | if(rr.dr.d_type == QType::NS || rr.auth) | |
27d4a65b | 512 | nrc.set(rr.dr.d_type); |
35fe50c3 | 513 | } |
50eb4144 | 514 | |
90ba52e0 | 515 | rr.dr.d_name = name; |
516 | rr.dr.d_ttl = sd.default_ttl; | |
517 | rr.dr.d_type = QType::NSEC; | |
27d4a65b | 518 | rr.dr.d_content = std::make_shared<NSECRecordContent>(std::move(nrc)); |
90ba52e0 | 519 | rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY; |
35fe50c3 | 520 | rr.auth = true; |
50eb4144 | 521 | |
35fe50c3 BH |
522 | r->addRecord(rr); |
523 | } | |
524 | ||
576e7e0f | 525 | void PacketHandler::emitNSEC3(DNSPacket *r, const SOAData& sd, const NSEC3PARAMRecordContent& ns3prc, const DNSName& name, const string& namehash, const string& nexthash, int mode) |
5c3bf2db | 526 | { |
5c3bf2db | 527 | NSEC3RecordContent n3rc; |
576e7e0f | 528 | n3rc.d_algorithm = ns3prc.d_algorithm; |
b9dba5c1 | 529 | n3rc.d_flags = ns3prc.d_flags; |
c3c89361 | 530 | n3rc.d_iterations = ns3prc.d_iterations; |
576e7e0f KM |
531 | n3rc.d_salt = ns3prc.d_salt; |
532 | n3rc.d_nexthash = nexthash; | |
5c3bf2db | 533 | |
90ba52e0 | 534 | DNSZoneRecord rr; |
c3c89361 | 535 | |
576e7e0f KM |
536 | if(!name.empty()) { |
537 | if (sd.qname == name) { | |
27d4a65b RG |
538 | n3rc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table |
539 | n3rc.set(QType::NSEC3PARAM); | |
540 | n3rc.set(QType::DNSKEY); | |
088370cd | 541 | string publishCDNSKEY; |
0900d2d3 | 542 | d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY); |
088370cd | 543 | if (publishCDNSKEY == "1") |
27d4a65b | 544 | n3rc.set(QType::CDNSKEY); |
ef542223 | 545 | string publishCDS; |
0900d2d3 | 546 | d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS); |
ef542223 | 547 | if (! publishCDS.empty()) |
27d4a65b | 548 | n3rc.set(QType::CDS); |
b5baefaf | 549 | } |
576e7e0f KM |
550 | |
551 | B.lookup(QType(QType::ANY), name, NULL, sd.domain_id); | |
552 | while(B.get(rr)) { | |
8900e2e3 | 553 | #ifdef HAVE_LUA_RECORDS |
d0c7ecf1 | 554 | if(rr.dr.d_type == QType::LUA) |
27d4a65b | 555 | n3rc.set(getRR<LUARecordContent>(rr.dr)->d_type); |
8900e2e3 CHB |
556 | else |
557 | #endif | |
558 | if(rr.dr.d_type && (rr.dr.d_type == QType::NS || rr.auth)) // skip empty non-terminals | |
27d4a65b | 559 | n3rc.set(rr.dr.d_type); |
576e7e0f | 560 | } |
c3c89361 | 561 | } |
b5baefaf | 562 | |
27d4a65b RG |
563 | const auto numberOfTypesSet = n3rc.numberOfTypesSet(); |
564 | if (numberOfTypesSet != 0 && !(numberOfTypesSet == 1 && n3rc.isSet(QType::NS))) { | |
565 | n3rc.set(QType::RRSIG); | |
566 | } | |
50eb4144 | 567 | |
90ba52e0 | 568 | rr.dr.d_name = DNSName(toBase32Hex(namehash))+sd.qname; |
569 | rr.dr.d_ttl = sd.default_ttl; | |
570 | rr.dr.d_type=QType::NSEC3; | |
27d4a65b | 571 | rr.dr.d_content=std::make_shared<NSEC3RecordContent>(std::move(n3rc)); |
90ba52e0 | 572 | rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY; |
5c3bf2db | 573 | rr.auth = true; |
50eb4144 | 574 | |
5c3bf2db BH |
575 | r->addRecord(rr); |
576 | } | |
577 | ||
75a89ce6 PD |
578 | /* |
579 | mode 0 = No Data Responses, QTYPE is not DS | |
b5baefaf | 580 | mode 1 = No Data Responses, QTYPE is DS |
75a89ce6 PD |
581 | mode 2 = Wildcard No Data Responses |
582 | mode 3 = Wildcard Answer Responses | |
583 | mode 4 = Name Error Responses | |
dcb8c5d7 | 584 | mode 5 = Direct NSEC request |
35fe50c3 | 585 | */ |
675fa24c | 586 | void PacketHandler::addNSECX(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, int mode) |
01fde57c | 587 | { |
52e0d783 KM |
588 | if(!p->d_dnssecOk && mode != 5) |
589 | return; | |
590 | ||
c3c89361 | 591 | NSEC3PARAMRecordContent ns3rc; |
22c5aa60 BH |
592 | bool narrow; |
593 | if(d_dk.getNSEC3PARAM(auth, &ns3rc, &narrow)) { | |
d31e9b23 | 594 | if (mode != 5) // no direct NSEC3 queries, rfc5155 7.2.8 |
dcb8c5d7 | 595 | addNSEC3(p, r, target, wildcard, auth, ns3rc, narrow, mode); |
9b30cd1a BH |
596 | } |
597 | else { | |
c5c4fbdc | 598 | addNSEC(p, r, target, wildcard, auth, mode); |
9b30cd1a | 599 | } |
01fde57c BH |
600 | } |
601 | ||
29e0008a | 602 | 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 |
603 | { |
604 | bool ret; | |
605 | if(narrow) { // nsec3-narrow | |
606 | ret=true; | |
607 | before=hashed; | |
b5baefaf | 608 | if(decrement) { |
af3ffdf1 | 609 | decrementHash(before); |
b5baefaf PD |
610 | unhashed.clear(); |
611 | } | |
22c5aa60 BH |
612 | after=hashed; |
613 | incrementHash(after); | |
614 | } | |
615 | else { | |
29e0008a KM |
616 | DNSName hashedName = DNSName(toBase32Hex(hashed)); |
617 | DNSName beforeName, afterName; | |
618 | if (!decrement && mode >= 2) | |
619 | beforeName = hashedName; | |
620 | ret=db->getBeforeAndAfterNamesAbsolute(id, hashedName, unhashed, beforeName, afterName); | |
621 | before=fromBase32Hex(beforeName.toString()); | |
622 | after=fromBase32Hex(afterName.toString()); | |
22c5aa60 | 623 | } |
22c5aa60 BH |
624 | return ret; |
625 | } | |
626 | ||
561434a6 | 627 | void PacketHandler::addNSEC3(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, const NSEC3PARAMRecordContent& ns3rc, bool narrow, int mode) |
01fde57c | 628 | { |
e6a9dde5 | 629 | DLOG(g_log<<"addNSEC3() mode="<<mode<<" auth="<<auth<<" target="<<target<<" wildcard="<<wildcard<<endl); |
53977f80 | 630 | |
5c3bf2db | 631 | SOAData sd; |
79ba7763 | 632 | if(!B.getSOAUncached(auth, sd)) { |
e6a9dde5 | 633 | DLOG(g_log<<"Could not get SOA for domain"); |
5c3bf2db BH |
634 | return; |
635 | } | |
53977f80 | 636 | |
3a741d7f | 637 | bool doNextcloser = false; |
561434a6 PD |
638 | string before, after, hashed; |
639 | DNSName unhashed, closest; | |
53977f80 | 640 | |
75a89ce6 | 641 | if (mode == 2 || mode == 3 || mode == 4) { |
f0306634 | 642 | closest=wildcard; |
561434a6 | 643 | closest.chopOff(); |
f0306634 KM |
644 | } else |
645 | closest=target; | |
53977f80 | 646 | |
75a89ce6 | 647 | // add matching NSEC3 RR |
b50efd61 | 648 | if (mode != 3) { |
3a741d7f | 649 | unhashed=(mode == 0 || mode == 1 || mode == 5) ? target : closest; |
28e2e78e | 650 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 651 | DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
53977f80 | 652 | |
15c9bf3b | 653 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, false, unhashed, before, after, mode); |
3a741d7f | 654 | |
2e9c8710 | 655 | if (((mode == 0 && ns3rc.d_flags) || mode == 1) && (hashed != before)) { |
e6a9dde5 | 656 | DLOG(g_log<<"No matching NSEC3, do closest (provable) encloser"<<endl); |
3a741d7f | 657 | |
54c9247e | 658 | bool doBreak = false; |
90ba52e0 | 659 | DNSZoneRecord rr; |
561434a6 | 660 | while( closest.chopOff() && (closest != sd.qname)) { // stop at SOA |
3a741d7f | 661 | B.lookup(QType(QType::ANY), closest, p, sd.domain_id); |
54c9247e KM |
662 | while(B.get(rr)) |
663 | if (rr.auth) | |
664 | doBreak = true; | |
665 | if(doBreak) | |
3a741d7f | 666 | break; |
3a741d7f KM |
667 | } |
668 | doNextcloser = true; | |
669 | unhashed=closest; | |
28e2e78e | 670 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 671 | DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
3a741d7f | 672 | |
15c9bf3b | 673 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, false, unhashed, before, after); |
3a741d7f KM |
674 | } |
675 | ||
d88babea | 676 | if (!after.empty()) { |
e6a9dde5 | 677 | DLOG(g_log<<"Done calling for matching, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
576e7e0f | 678 | emitNSEC3(r, sd, ns3rc, unhashed, before, after, mode); |
2010ac95 | 679 | } |
16cf9135 | 680 | } |
75a89ce6 PD |
681 | |
682 | // add covering NSEC3 RR | |
3a741d7f | 683 | if ((mode >= 2 && mode <= 4) || doNextcloser) { |
561434a6 | 684 | DNSName next(target); |
75a89ce6 PD |
685 | do { |
686 | unhashed=next; | |
687 | } | |
e325f20c | 688 | while( next.chopOff() && !(next==closest)); |
75a89ce6 | 689 | |
28e2e78e | 690 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 691 | DLOG(g_log<<"2 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
15c9bf3b KM |
692 | |
693 | getNSEC3Hashes(narrow, sd.db,sd.domain_id, hashed, true, unhashed, before, after); | |
e6a9dde5 | 694 | DLOG(g_log<<"Done calling for covering, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
15c9bf3b | 695 | emitNSEC3( r, sd, ns3rc, unhashed, before, after, mode); |
75a89ce6 | 696 | } |
53977f80 | 697 | |
75a89ce6 | 698 | // wildcard denial |
7bb8e202 | 699 | if (mode == 2 || mode == 4) { |
12c06211 | 700 | unhashed=g_wildcarddnsname+closest; |
75a89ce6 | 701 | |
28e2e78e | 702 | hashed=hashQNameWithSalt(ns3rc, unhashed); |
e6a9dde5 | 703 | DLOG(g_log<<"3 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl); |
53977f80 | 704 | |
15c9bf3b | 705 | getNSEC3Hashes(narrow, sd.db, sd.domain_id, hashed, (mode != 2), unhashed, before, after); |
e6a9dde5 | 706 | DLOG(g_log<<"Done calling for '*', hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl); |
15c9bf3b | 707 | emitNSEC3( r, sd, ns3rc, unhashed, before, after, mode); |
75a89ce6 | 708 | } |
01fde57c | 709 | } |
35fe50c3 | 710 | |
561434a6 | 711 | void PacketHandler::addNSEC(DNSPacket *p, DNSPacket *r, const DNSName& target, const DNSName& wildcard, const DNSName& auth, int mode) |
35fe50c3 | 712 | { |
e6a9dde5 | 713 | DLOG(g_log<<"addNSEC() mode="<<mode<<" auth="<<auth<<" target="<<target<<" wildcard="<<wildcard<<endl); |
35fe50c3 | 714 | |
53977f80 | 715 | SOAData sd; |
79ba7763 | 716 | if(!B.getSOAUncached(auth, sd)) { |
e6a9dde5 | 717 | DLOG(g_log<<"Could not get SOA for domain"<<endl); |
35fe50c3 BH |
718 | return; |
719 | } | |
720 | ||
561434a6 | 721 | DNSName before,after; |
15c9bf3b | 722 | sd.db->getBeforeAndAfterNames(sd.domain_id, auth, target, before, after); |
7cd99296 KM |
723 | if (mode != 5 || before == target) |
724 | emitNSEC(r, sd, before, after, mode); | |
b5baefaf | 725 | |
337dd27b KM |
726 | if (mode == 2 || mode == 4) { |
727 | // wildcard NO-DATA or wildcard denial | |
628ab42d | 728 | before.clear(); |
561434a6 | 729 | DNSName closest(wildcard); |
337dd27b | 730 | if (mode == 4) { |
561434a6 PD |
731 | closest.chopOff(); |
732 | closest.prependRawLabel("*"); | |
337dd27b | 733 | } |
15c9bf3b KM |
734 | sd.db->getBeforeAndAfterNames(sd.domain_id, auth, closest, before, after); |
735 | emitNSEC(r, sd, before, after, mode); | |
75a89ce6 | 736 | } |
35fe50c3 BH |
737 | return; |
738 | } | |
739 | ||
12c86877 BH |
740 | /* Semantics: |
741 | ||
742 | - only one backend owns the SOA of a zone | |
743 | - only one AXFR per zone at a time - double startTransaction should fail | |
744 | - backends need to implement transaction semantics | |
745 | ||
746 | ||
747 | How BindBackend would implement this: | |
748 | startTransaction makes a file | |
749 | feedRecord sends everything to that file | |
750 | commitTransaction moves that file atomically over the regular file, and triggers a reload | |
751 | rollbackTransaction removes the file | |
752 | ||
753 | ||
754 | How PostgreSQLBackend would implement this: | |
755 | startTransaction starts a sql transaction, which also deletes all records | |
756 | feedRecord is an insert statement | |
757 | commitTransaction commits the transaction | |
758 | rollbackTransaction aborts it | |
759 | ||
760 | How MySQLBackend would implement this: | |
761 | (good question!) | |
762 | ||
763 | */ | |
764 | ||
6fe866b4 | 765 | int PacketHandler::trySuperMaster(DNSPacket *p, const DNSName& tsigkeyname) |
7108e055 PD |
766 | { |
767 | if(p->d_tcp) | |
768 | { | |
769 | // do it right now if the client is TCP | |
770 | // rarely happens | |
7731e32c | 771 | return trySuperMasterSynchronous(p, tsigkeyname); |
7108e055 PD |
772 | } |
773 | else | |
774 | { | |
775 | // queue it if the client is on UDP | |
776 | Communicator.addTrySuperMasterRequest(p); | |
777 | return 0; | |
778 | } | |
779 | } | |
780 | ||
02980dc2 | 781 | int PacketHandler::trySuperMasterSynchronous(const DNSPacket *p, const DNSName& tsigkeyname) |
12c86877 | 782 | { |
f43e6a40 | 783 | ComboAddress remote = p->getRemote().setPort(53); |
d622042f | 784 | if(p->hasEDNSSubnet() && ::arg().contains("trusted-notification-proxy", remote.toString())) { |
785 | remote = p->getRealRemote().getNetwork(); | |
a94fe494 RG |
786 | } |
787 | ||
12c86877 BH |
788 | Resolver::res_t nsset; |
789 | try { | |
790 | Resolver resolver; | |
092f210a | 791 | uint32_t theirserial; |
d622042f | 792 | resolver.getSoaSerial(remote, p->qdomain, &theirserial); |
a94fe494 | 793 | resolver.resolve(remote, p->qdomain, QType::NS, &nsset); |
12c86877 BH |
794 | } |
795 | catch(ResolverException &re) { | |
e6a9dde5 | 796 | g_log<<Logger::Error<<"Error resolving SOA or NS for "<<p->qdomain<<" at: "<< remote <<": "<<re.reason<<endl; |
12c86877 BH |
797 | return RCode::ServFail; |
798 | } | |
799 | ||
39b23e69 RK |
800 | // check if the returned records are NS records |
801 | bool haveNS=false; | |
424b92c6 | 802 | for(const auto& ns: nsset) { |
90ba52e0 | 803 | if(ns.qtype==QType::NS) |
39b23e69 RK |
804 | haveNS=true; |
805 | } | |
806 | ||
807 | if(!haveNS) { | |
e6a9dde5 | 808 | g_log<<Logger::Error<<"While checking for supermaster, did not find NS for "<<p->qdomain<<" at: "<< remote <<endl; |
39b23e69 RK |
809 | return RCode::ServFail; |
810 | } | |
811 | ||
719f9024 | 812 | string nameserver, account; |
12c86877 | 813 | DNSBackend *db; |
771bb0b0 AT |
814 | |
815 | if (!::arg().mustDo("allow-unsigned-supermaster") && tsigkeyname.empty()) { | |
e6a9dde5 | 816 | g_log<<Logger::Error<<"Received unsigned NOTIFY for "<<p->qdomain<<" from potential supermaster "<<remote<<". Refusing."<<endl; |
771bb0b0 AT |
817 | return RCode::Refused; |
818 | } | |
819 | ||
d622042f | 820 | if(!B.superMasterBackend(remote.toString(), p->qdomain, nsset, &nameserver, &account, &db)) { |
e6a9dde5 | 821 | g_log<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<remote<<". Remote nameservers: "<<endl; |
424b92c6 | 822 | for(const auto& rr: nsset) { |
90ba52e0 | 823 | if(rr.qtype==QType::NS) |
e6a9dde5 | 824 | g_log<<Logger::Error<<rr.content<<endl; |
a7372c6f | 825 | } |
12c86877 BH |
826 | return RCode::Refused; |
827 | } | |
8c80c4f4 | 828 | try { |
ded6b08d | 829 | db->createSlaveDomain(p->getRemote().toString(), p->qdomain, nameserver, account); |
7731e32c AT |
830 | if (tsigkeyname.empty() == false) { |
831 | vector<string> meta; | |
6fe866b4 | 832 | meta.push_back(tsigkeyname.toStringNoDot()); |
7731e32c AT |
833 | db->setDomainMetadata(p->qdomain, "AXFR-MASTER-TSIG", meta); |
834 | } | |
8c80c4f4 | 835 | } |
3f81d239 | 836 | catch(PDNSException& ae) { |
e6a9dde5 | 837 | g_log<<Logger::Error<<"Database error trying to create "<<p->qdomain<<" for potential supermaster "<<remote<<": "<<ae.reason<<endl; |
8c80c4f4 BH |
838 | return RCode::ServFail; |
839 | } | |
e6a9dde5 | 840 | g_log<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<remote<<endl; |
12c86877 BH |
841 | return RCode::NoError; |
842 | } | |
843 | ||
3777f434 | 844 | int PacketHandler::processNotify(DNSPacket *p) |
12c86877 BH |
845 | { |
846 | /* now what? | |
847 | was this notification from an approved address? | |
c68ab952 | 848 | was this notification approved by TSIG? |
12c86877 BH |
849 | We determine our internal SOA id (via UeberBackend) |
850 | We determine the SOA at our (known) master | |
851 | if master is higher -> do stuff | |
852 | */ | |
c68ab952 | 853 | |
1793df78 | 854 | g_log<<Logger::Debug<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<endl; |
c68ab952 | 855 | |
dad0736b | 856 | if(!::arg().mustDo("slave") && s_forwardNotify.empty()) { |
5b07dc82 KM |
857 | g_log<<Logger::Warning<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl; |
858 | return RCode::Refused; | |
12c86877 | 859 | } |
d207ad63 | 860 | |
5b07dc82 KM |
861 | // Sender verification |
862 | // | |
c68ab952 | 863 | if(!s_allowNotifyFrom.match((ComboAddress *) &p->d_remote ) || p->d_havetsig) { |
7731e32c | 864 | if (p->d_havetsig && p->getTSIGKeyname().empty() == false) { |
5b07dc82 | 865 | g_log<<Logger::Notice<<"Received secure NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<", with TSIG key '"<<p->getTSIGKeyname()<<"'"<<endl; |
c68ab952 | 866 | } else { |
5b07dc82 | 867 | g_log<<Logger::Warning<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but the remote is not providing a TSIG key or in allow-notify-from (Refused)"<<endl; |
c68ab952 AT |
868 | return RCode::Refused; |
869 | } | |
d207ad63 RK |
870 | } |
871 | ||
5b07dc82 | 872 | if ((!::arg().mustDo("allow-unsigned-notify") && !p->d_havetsig) || p->d_havetsig) { |
2fa2a51a | 873 | if (!p->d_havetsig) { |
5b07dc82 KM |
874 | g_log<<Logger::Warning<<"Received unsigned NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" while a TSIG key was required (Refused)"<<endl; |
875 | return RCode::Refused; | |
876 | } | |
877 | vector<string> meta; | |
878 | if (B.getDomainMetadata(p->qdomain,"AXFR-MASTER-TSIG",meta) && meta.size() > 0) { | |
879 | if (!pdns_iequals(meta[0], p->getTSIGKeyname().toStringNoDot())) { | |
880 | g_log<<Logger::Warning<<"Received secure NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<": expected TSIG key '"<<meta[0]<<", got '"<<p->getTSIGKeyname()<<"' (Refused)"<<endl; | |
771bb0b0 AT |
881 | return RCode::Refused; |
882 | } | |
c68ab952 AT |
883 | } |
884 | } | |
885 | ||
5b07dc82 KM |
886 | // Domain verification |
887 | // | |
888 | DomainInfo di; | |
ce7dc7e9 | 889 | if(!B.getDomainInfo(p->qdomain, di, false) || !di.backend) { |
b8013977 KM |
890 | if(::arg().mustDo("supermaster")) { |
891 | g_log<<Logger::Warning<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative, trying supermaster"<<endl; | |
892 | return trySuperMaster(p, p->getTSIGKeyname()); | |
893 | } | |
894 | g_log<<Logger::Notice<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative (Refused)"<<endl; | |
895 | return RCode::Refused; | |
5b07dc82 KM |
896 | } |
897 | ||
ded6b08d | 898 | if(::arg().contains("trusted-notification-proxy", p->getRemote().toString())) { |
e6a9dde5 | 899 | g_log<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl; |
f7fb7022 | 900 | if(di.masters.empty()) { |
5b07dc82 | 901 | g_log<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined (Refused)"<<endl; |
f7fb7022 BH |
902 | return RCode::Refused; |
903 | } | |
f7fb7022 | 904 | } |
d2adca9d | 905 | else if(::arg().mustDo("master") && di.kind == DomainInfo::Master) { |
5b07dc82 | 906 | g_log<<Logger::Warning<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but we are master (Refused)"<<endl; |
d2adca9d PL |
907 | return RCode::Refused; |
908 | } | |
a2dfc9ea | 909 | else if(!di.isMaster(p->getRemote())) { |
5b07dc82 | 910 | g_log<<Logger::Warning<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master (Refused)"<<endl; |
12c86877 BH |
911 | return RCode::Refused; |
912 | } | |
dad0736b RC |
913 | |
914 | if(!s_forwardNotify.empty()) { | |
915 | set<string> forwardNotify(s_forwardNotify); | |
916 | for(set<string>::const_iterator j=forwardNotify.begin();j!=forwardNotify.end();++j) { | |
5b07dc82 | 917 | g_log<<Logger::Notice<<"Relaying notification of domain "<<p->qdomain<<" from "<<p->getRemote()<<" to "<<*j<<endl; |
dad0736b RC |
918 | Communicator.notify(p->qdomain,*j); |
919 | } | |
920 | } | |
921 | ||
1793df78 | 922 | if(::arg().mustDo("slave")) { |
ce7dc7e9 | 923 | g_log<<Logger::Debug<<"Queueing slave check for "<<p->qdomain<<endl; |
dad0736b | 924 | Communicator.addSlaveCheckRequest(di, p->d_remote); |
1793df78 | 925 | } |
7f3d870e | 926 | return 0; |
12c86877 BH |
927 | } |
928 | ||
1d563353 KM |
929 | bool validDNSName(const DNSName &name) |
930 | { | |
fc41a1a6 KM |
931 | if (!g_8bitDNS) { |
932 | string::size_type pos, length; | |
933 | char c; | |
934 | for(const auto& s : name.getRawLabels()) { | |
935 | length=s.length(); | |
936 | for(pos=0; pos < length; ++pos) { | |
937 | c=s[pos]; | |
938 | if(!((c >= 'a' && c <= 'z') || | |
939 | (c >= 'A' && c <= 'Z') || | |
940 | (c >= '0' && c <= '9') || | |
941 | c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) | |
942 | return false; | |
943 | } | |
1d563353 KM |
944 | } |
945 | } | |
946 | return true; | |
947 | } | |
948 | ||
12c86877 BH |
949 | DNSPacket *PacketHandler::question(DNSPacket *p) |
950 | { | |
5704e107 PD |
951 | DNSPacket *ret; |
952 | ||
953 | if(d_pdl) | |
954 | { | |
955 | ret=d_pdl->prequery(p); | |
956 | if(ret) | |
957 | return ret; | |
958 | } | |
959 | ||
bb6e54fe | 960 | if(p->d.rd) { |
1566533a | 961 | static AtomicCounter &rdqueries=*S.getPointer("rd-queries"); |
bb6e54fe | 962 | rdqueries++; |
963 | } | |
964 | ||
da286f66 | 965 | return doQuestion(p); |
ff76e8b4 BH |
966 | } |
967 | ||
90ba52e0 | 968 | |
675fa24c | 969 | void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd) |
35fe50c3 | 970 | { |
90ba52e0 | 971 | DNSZoneRecord rr; |
972 | rr.dr.d_name=sd.qname; | |
973 | rr.dr.d_type=QType::SOA; | |
974 | ||
975 | rr.dr.d_content=makeSOAContent(sd); | |
976 | rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); | |
794c2f92 | 977 | rr.signttl=sd.ttl; |
35fe50c3 | 978 | rr.domain_id=sd.domain_id; |
90ba52e0 | 979 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
0957a99f | 980 | rr.auth = 1; |
35fe50c3 | 981 | r->addRecord(rr); |
6865d5c0 | 982 | |
52e0d783 | 983 | if(d_dk.isSecuredZone(sd.qname)) |
c5c4fbdc | 984 | addNSECX(p, r, target, wildcard, sd.qname, 4); |
6865d5c0 KM |
985 | |
986 | r->setRcode(RCode::NXDomain); | |
35fe50c3 BH |
987 | } |
988 | ||
675fa24c | 989 | void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd, int mode) |
35fe50c3 | 990 | { |
90ba52e0 | 991 | DNSZoneRecord rr; |
992 | rr.dr.d_name=sd.qname; | |
993 | rr.dr.d_type=QType::SOA; | |
994 | rr.dr.d_content=makeSOAContent(sd); | |
995 | rr.dr.d_ttl=sd.ttl; | |
996 | rr.dr.d_ttl=min(sd.ttl, sd.default_ttl); | |
794c2f92 | 997 | rr.signttl=sd.ttl; |
35fe50c3 | 998 | rr.domain_id=sd.domain_id; |
90ba52e0 | 999 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
bccefefa | 1000 | rr.auth = 1; |
35fe50c3 | 1001 | r->addRecord(rr); |
ed9c3a50 | 1002 | |
52e0d783 | 1003 | if(d_dk.isSecuredZone(sd.qname)) |
c5c4fbdc | 1004 | addNSECX(p, r, target, wildcard, sd.qname, mode); |
9951e2d0 | 1005 | |
eb029b8e | 1006 | S.ringAccount("noerror-queries", p->qdomain, p->qtype); |
35fe50c3 BH |
1007 | } |
1008 | ||
1009 | ||
675fa24c | 1010 | bool PacketHandler::addDSforNS(DNSPacket* p, DNSPacket* r, SOAData& sd, const DNSName& dsname) |
35fe50c3 | 1011 | { |
e4090157 | 1012 | //cerr<<"Trying to find a DS for '"<<dsname<<"', domain_id = "<<sd.domain_id<<endl; |
35fe50c3 | 1013 | B.lookup(QType(QType::DS), dsname, p, sd.domain_id); |
90ba52e0 | 1014 | DNSZoneRecord rr; |
35fe50c3 BH |
1015 | bool gotOne=false; |
1016 | while(B.get(rr)) { | |
1017 | gotOne=true; | |
90ba52e0 | 1018 | rr.dr.d_place = DNSResourceRecord::AUTHORITY; |
35fe50c3 BH |
1019 | r->addRecord(rr); |
1020 | } | |
1021 | return gotOne; | |
1022 | } | |
1023 | ||
675fa24c | 1024 | bool PacketHandler::tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const DNSName &target, bool retargeted) |
35fe50c3 | 1025 | { |
90ba52e0 | 1026 | vector<DNSZoneRecord> rrset = getBestReferralNS(p, sd, target); |
35fe50c3 BH |
1027 | if(rrset.empty()) |
1028 | return false; | |
1029 | ||
424b92c6 | 1030 | for(auto& rr: rrset) { |
90ba52e0 | 1031 | rr.dr.d_place=DNSResourceRecord::AUTHORITY; |
35fe50c3 BH |
1032 | r->addRecord(rr); |
1033 | } | |
d2323cd0 PD |
1034 | if(!retargeted) |
1035 | r->setA(false); | |
35fe50c3 | 1036 | |
90ba52e0 | 1037 | if(d_dk.isSecuredZone(sd.qname) && !addDSforNS(p, r, sd, rrset.begin()->dr.d_name)) |
1038 | addNSECX(p, r, rrset.begin()->dr.d_name, DNSName(), sd.qname, 1); | |
35fe50c3 BH |
1039 | |
1040 | return true; | |
1041 | } | |
1042 | ||
675fa24c | 1043 | void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, const DNSName &target) |
35fe50c3 BH |
1044 | { |
1045 | if(!p->d_dnssecOk) | |
df554502 | 1046 | return; // Don't send dnssec info to non validating resolvers. |
52e0d783 | 1047 | |
d3e7090c | 1048 | if(!d_dk.isSecuredZone(sd.qname)) |
fbcdac7e | 1049 | return; |
f889ab99 | 1050 | |
290a083d | 1051 | addNSECX(p, r, target, DNSName(), sd.qname, 5); |
e325f20c | 1052 | if(sd.qname == p->qdomain) { |
794c2f92 | 1053 | addDNSKEY(p, r, sd); |
f889ab99 | 1054 | addCDNSKEY(p, r, sd); |
ef542223 | 1055 | addCDS(p, r, sd); |
794c2f92 | 1056 | addNSEC3PARAM(p, r, sd); |
70b18120 | 1057 | } |
35fe50c3 BH |
1058 | } |
1059 | ||
675fa24c | 1060 | bool PacketHandler::tryDNAME(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName &target) |
8dee0750 | 1061 | { |
1062 | if(!d_doDNAME) | |
1063 | return false; | |
e6a9dde5 | 1064 | DLOG(g_log<<Logger::Warning<<"Let's try DNAME.."<<endl); |
90ba52e0 | 1065 | vector<DNSZoneRecord> rrset = getBestDNAMESynth(p, sd, target); |
8dee0750 | 1066 | if(!rrset.empty()) { |
424b92c6 | 1067 | for(auto& rr: rrset) { |
90ba52e0 | 1068 | rr.dr.d_place = DNSResourceRecord::ANSWER; |
8dee0750 | 1069 | r->addRecord(rr); |
1070 | } | |
1071 | return true; | |
1072 | } | |
1073 | return false; | |
1074 | } | |
675fa24c | 1075 | bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName &target, DNSName &wildcard, bool& retargeted, bool& nodata) |
35fe50c3 | 1076 | { |
e3f388cd | 1077 | retargeted = nodata = false; |
561434a6 | 1078 | DNSName bestmatch; |
35fe50c3 | 1079 | |
90ba52e0 | 1080 | vector<DNSZoneRecord> rrset; |
75a89ce6 | 1081 | if(!getBestWildcard(p, sd, target, wildcard, &rrset)) |
35fe50c3 BH |
1082 | return false; |
1083 | ||
e3f388cd | 1084 | if(rrset.empty()) { |
e6a9dde5 | 1085 | DLOG(g_log<<"Wildcard matched something, but not of the correct type"<<endl); |
e3f388cd BH |
1086 | nodata=true; |
1087 | } | |
1088 | else { | |
424b92c6 | 1089 | for(auto& rr: rrset) { |
90ba52e0 | 1090 | rr.wildcardname = rr.dr.d_name; |
1091 | rr.dr.d_name=bestmatch=target; | |
bcb8aebe | 1092 | |
90ba52e0 | 1093 | if(rr.dr.d_type == QType::CNAME) { |
e3f388cd | 1094 | retargeted=true; |
90ba52e0 | 1095 | target=getRR<CNAMERecordContent>(rr.dr)->getTarget(); |
e3f388cd BH |
1096 | } |
1097 | ||
90ba52e0 | 1098 | rr.dr.d_place=DNSResourceRecord::ANSWER; |
e3f388cd | 1099 | r->addRecord(rr); |
35fe50c3 | 1100 | } |
35fe50c3 | 1101 | } |
52e0d783 | 1102 | if(d_dk.isSecuredZone(sd.qname) && !nodata) { |
c5c4fbdc | 1103 | addNSECX(p, r, bestmatch, wildcard, sd.qname, 3); |
35fe50c3 BH |
1104 | } |
1105 | return true; | |
1106 | } | |
1107 | ||
ff76e8b4 | 1108 | //! Called by the Distributor to ask a question. Returns 0 in case of an error |
e89efca5 | 1109 | DNSPacket *PacketHandler::doQuestion(DNSPacket *p) |
ff76e8b4 | 1110 | { |
90ba52e0 | 1111 | DNSZoneRecord rr; |
12c86877 | 1112 | SOAData sd; |
81c486ad | 1113 | |
12c86877 | 1114 | int retargetcount=0; |
675fa24c | 1115 | set<DNSName> authSet; |
35fe50c3 | 1116 | |
90ba52e0 | 1117 | vector<DNSZoneRecord> rrset; |
35fe50c3 | 1118 | bool weDone=0, weRedirected=0, weHaveUnauth=0; |
561434a6 | 1119 | DNSName haveAlias; |
0abea1ca | 1120 | uint8_t aliasScopeMask; |
12c86877 | 1121 | |
0c127168 | 1122 | DNSPacket *r=0; |
78bcb858 | 1123 | bool noCache=false; |
8900e2e3 CHB |
1124 | |
1125 | #ifdef HAVE_LUA_RECORDS | |
cb6bd1a9 | 1126 | bool doLua=g_doLuaRecord; |
8900e2e3 | 1127 | #endif |
a16e8e3a BH |
1128 | |
1129 | if(p->d.qr) { // QR bit from dns packet (thanks RA from N) | |
641d32aa | 1130 | if(d_logDNSDetails) |
e6a9dde5 | 1131 | g_log<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl; |
a16e8e3a | 1132 | S.inc("corrupt-packets"); |
41aacb6a | 1133 | S.ringAccount("remotes-corrupt", p->d_remote); |
a16e8e3a BH |
1134 | return 0; |
1135 | } | |
1136 | ||
43b50405 CH |
1137 | if(p->d.tc) { // truncated query. MOADNSParser would silently parse this packet in an incomplete way. |
1138 | if(d_logDNSDetails) | |
e6a9dde5 | 1139 | g_log<<Logger::Error<<"Received truncated query packet from "<<p->getRemote()<<", dropping"<<endl; |
43b50405 CH |
1140 | S.inc("corrupt-packets"); |
1141 | S.ringAccount("remotes-corrupt", p->d_remote); | |
1142 | return 0; | |
1143 | } | |
1144 | ||
298fabc3 AT |
1145 | if (p->hasEDNS() && p->getEDNSVersion() > 0) { |
1146 | r = p->replyPacket(); | |
5d21450e PL |
1147 | |
1148 | // PacketWriter::addOpt will take care of setting this correctly in the packet | |
1149 | r->setEDNSRcode(ERCode::BADVERS); | |
298fabc3 AT |
1150 | return r; |
1151 | } | |
1152 | ||
78bcb858 | 1153 | if(p->d_havetsig) { |
7abbc40f PD |
1154 | DNSName keyname; |
1155 | string secret; | |
78bcb858 | 1156 | TSIGRecordContent trc; |
ea3816cf | 1157 | if(!p->checkForCorrectTSIG(&B, &keyname, &secret, &trc)) { |
0c127168 | 1158 | r=p->replyPacket(); // generate an empty reply packet |
78bcb858 | 1159 | if(d_logDNSDetails) |
e6a9dde5 | 1160 | g_log<<Logger::Error<<"Received a TSIG signed message with a non-validating key"<<endl; |
f7a69a4c RA |
1161 | // RFC3007 describes that a non-secure message should be sending Refused for DNS Updates |
1162 | if (p->d.opcode == Opcode::Update) | |
914353ca KM |
1163 | r->setRcode(RCode::Refused); |
1164 | else | |
f7a69a4c | 1165 | r->setRcode(RCode::NotAuth); |
78bcb858 | 1166 | return r; |
7f9ac49b AT |
1167 | } else { |
1168 | getTSIGHashEnum(trc.d_algoName, p->d_tsig_algo); | |
1169 | if (p->d_tsig_algo == TSIG_GSS) { | |
1635f12b | 1170 | GssContext gssctx(keyname); |
7f9ac49b | 1171 | if (!gssctx.getPeerPrincipal(p->d_peer_principal)) { |
e6a9dde5 | 1172 | g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<keyname<<"'"<<endl; |
7f9ac49b AT |
1173 | } |
1174 | } | |
78bcb858 BH |
1175 | } |
1176 | p->setTSIGDetails(trc, keyname, secret, trc.d_mac); // this will get copied by replyPacket() | |
1177 | noCache=true; | |
1178 | } | |
1179 | ||
0c127168 | 1180 | r=p->replyPacket(); // generate an empty reply packet, possibly with TSIG details inside |
c00d7891 AT |
1181 | |
1182 | if (p->qtype == QType::TKEY) { | |
1183 | this->tkeyHandler(p, r); | |
1184 | return r; | |
1185 | } | |
1186 | ||
12c86877 | 1187 | try { |
12c86877 BH |
1188 | |
1189 | // XXX FIXME do this in DNSPacket::parse ? | |
1190 | ||
1d563353 KM |
1191 | if(!validDNSName(p->qdomain)) { |
1192 | if(d_logDNSDetails) | |
e6a9dde5 | 1193 | g_log<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': sending servfail"<<endl; |
1d563353 KM |
1194 | S.inc("corrupt-packets"); |
1195 | S.ringAccount("remotes-corrupt", p->d_remote); | |
1196 | S.inc("servfail-packets"); | |
1197 | r->setRcode(RCode::ServFail); | |
1198 | return r; | |
1199 | } | |
12c86877 BH |
1200 | if(p->d.opcode) { // non-zero opcode (again thanks RA!) |
1201 | if(p->d.opcode==Opcode::Update) { | |
71f758e0 | 1202 | S.inc("dnsupdate-queries"); |
f7a69a4c | 1203 | int res=processUpdate(p); |
63cb8c10 | 1204 | if (res == RCode::Refused) |
71f758e0 | 1205 | S.inc("dnsupdate-refused"); |
63cb8c10 | 1206 | else if (res != RCode::ServFail) |
71f758e0 | 1207 | S.inc("dnsupdate-answers"); |
f7a69a4c RA |
1208 | r->setRcode(res); |
1209 | r->setOpcode(Opcode::Update); | |
1210 | return r; | |
12c86877 BH |
1211 | } |
1212 | else if(p->d.opcode==Opcode::Notify) { | |
93aecccc | 1213 | S.inc("incoming-notifications"); |
4957a608 BH |
1214 | int res=processNotify(p); |
1215 | if(res>=0) { | |
4957a608 BH |
1216 | r->setRcode(res); |
1217 | r->setOpcode(Opcode::Notify); | |
1218 | return r; | |
1219 | } | |
f3a91936 | 1220 | delete r; |
4957a608 | 1221 | return 0; |
12c86877 BH |
1222 | } |
1223 | ||
e6a9dde5 | 1224 | g_log<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl; |
12c86877 | 1225 | |
12c86877 BH |
1226 | r->setRcode(RCode::NotImp); |
1227 | return r; | |
1228 | } | |
c2413a68 | 1229 | |
e6a9dde5 | 1230 | // g_log<<Logger::Warning<<"Query for '"<<p->qdomain<<"' "<<p->qtype.getName()<<" from "<<p->getRemote()<< " (tcp="<<p->d_tcp<<")"<<endl; |
12c86877 | 1231 | |
dc45a198 | 1232 | if(p->qtype.getCode()==QType::IXFR) { |
8d7543ba | 1233 | r->setRcode(RCode::Refused); |
dc45a198 BH |
1234 | return r; |
1235 | } | |
1236 | ||
561434a6 | 1237 | DNSName target=p->qdomain; |
c76a16b7 | 1238 | |
49e8d5d5 KM |
1239 | // catch chaos qclass requests |
1240 | if(p->qclass == QClass::CHAOS) { | |
1241 | if (doChaosRequest(p,r,target)) | |
1242 | goto sendit; | |
1243 | else | |
1244 | return r; | |
1245 | } | |
12c86877 | 1246 | |
8d7543ba | 1247 | // we only know about qclass IN (and ANY), send Refused for everything else. |
c76a16b7 | 1248 | if(p->qclass != QClass::IN && p->qclass!=QClass::ANY) { |
8d7543ba | 1249 | r->setRcode(RCode::Refused); |
c4ac5865 BH |
1250 | return r; |
1251 | } | |
12c86877 | 1252 | |
ec62f3c0 KM |
1253 | // send TC for udp ANY query if any-to-tcp is enabled. |
1254 | if(p->qtype.getCode() == QType::ANY && !p->d_tcp && g_anyToTcp) { | |
abc8f3f9 | 1255 | r->d.tc = 1; |
1256 | r->commitD(); | |
1257 | return r; | |
1258 | } | |
1259 | ||
49e8d5d5 | 1260 | // for qclass ANY the response should never be authoritative unless the response covers all classes. |
c76a16b7 | 1261 | if(p->qclass==QClass::ANY) |
12c86877 | 1262 | r->setA(false); |
c76a16b7 | 1263 | |
12c86877 BH |
1264 | |
1265 | retargeted:; | |
35fe50c3 | 1266 | if(retargetcount > 10) { // XXX FIXME, retargetcount++? |
e6a9dde5 | 1267 | g_log<<Logger::Warning<<"Abort CNAME chain resolution after "<<--retargetcount<<" redirects, sending out servfail. Initial query: '"<<p->qdomain<<"'"<<endl; |
75c8ebb4 KM |
1268 | delete r; |
1269 | r=p->replyPacket(); | |
12c86877 | 1270 | r->setRcode(RCode::ServFail); |
35fe50c3 | 1271 | return r; |
12c86877 | 1272 | } |
507823d1 | 1273 | |
cec52de6 | 1274 | if(!B.getAuth(target, p->qtype, &sd)) { |
e6a9dde5 | 1275 | DLOG(g_log<<Logger::Error<<"We have no authority over zone '"<<target<<"'"<<endl); |
40fc506f | 1276 | if(!retargetcount) { |
8d3cbffa | 1277 | r->setA(false); // drop AA if we never had a SOA in the first place |
40fc506f | 1278 | r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea' |
82cc1f71 | 1279 | } |
507823d1 BH |
1280 | goto sendit; |
1281 | } | |
e6a9dde5 | 1282 | DLOG(g_log<<Logger::Error<<"We have authority, zone='"<<sd.qname<<"', id="<<sd.domain_id<<endl); |
8d3cbffa | 1283 | authSet.insert(sd.qname); |
12c86877 | 1284 | |
3e8216c8 PD |
1285 | if(!retargetcount) r->qdomainzone=sd.qname; |
1286 | ||
e325f20c | 1287 | if(sd.qname==p->qdomain) { |
794c2f92 PD |
1288 | if(p->qtype.getCode() == QType::DNSKEY) |
1289 | { | |
1290 | if(addDNSKEY(p, r, sd)) | |
1291 | goto sendit; | |
1292 | } | |
088370cd PL |
1293 | else if(p->qtype.getCode() == QType::CDNSKEY) |
1294 | { | |
f889ab99 | 1295 | if(addCDNSKEY(p,r, sd)) |
088370cd PL |
1296 | goto sendit; |
1297 | } | |
ef542223 PL |
1298 | else if(p->qtype.getCode() == QType::CDS) |
1299 | { | |
1300 | if(addCDS(p,r, sd)) | |
1301 | goto sendit; | |
1302 | } | |
dacacb23 | 1303 | else if(p->qtype.getCode() == QType::NSEC3PARAM && d_dk.isSecuredZone(sd.qname)) |
794c2f92 PD |
1304 | { |
1305 | if(addNSEC3PARAM(p,r, sd)) | |
1306 | goto sendit; | |
1307 | } | |
fbcdac7e BH |
1308 | } |
1309 | ||
e325f20c | 1310 | if(p->qtype.getCode() == QType::SOA && sd.qname==p->qdomain) { |
90ba52e0 | 1311 | rr.dr.d_name=sd.qname; |
1312 | rr.dr.d_type=QType::SOA; | |
13f9e280 | 1313 | sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname); |
90ba52e0 | 1314 | rr.dr.d_content=makeSOAContent(sd); |
1315 | rr.dr.d_ttl=sd.ttl; | |
507823d1 | 1316 | rr.domain_id=sd.domain_id; |
90ba52e0 | 1317 | rr.dr.d_place=DNSResourceRecord::ANSWER; |
d24589bc | 1318 | rr.auth = true; |
507823d1 BH |
1319 | r->addRecord(rr); |
1320 | goto sendit; | |
1321 | } | |
12c86877 | 1322 | |
35fe50c3 | 1323 | // this TRUMPS a cname! |
52e0d783 | 1324 | if(p->qtype.getCode() == QType::NSEC && d_dk.isSecuredZone(sd.qname) && !d_dk.getNSEC3PARAM(sd.qname, 0)) { |
290a083d | 1325 | addNSEC(p, r, target, DNSName(), sd.qname, 5); |
7cd99296 KM |
1326 | if (!r->isEmpty()) |
1327 | goto sendit; | |
12c86877 | 1328 | } |
571726e9 | 1329 | |
35fe50c3 | 1330 | // this TRUMPS a cname! |
ec62f3c0 | 1331 | if(p->qtype.getCode() == QType::RRSIG) { |
e6a9dde5 | 1332 | g_log<<Logger::Info<<"Direct RRSIG query for "<<target<<" from "<<p->getRemote()<<endl; |
8d7543ba | 1333 | r->setRcode(RCode::Refused); |
52e0d783 | 1334 | goto sendit; |
cac7e485 | 1335 | } |
35fe50c3 | 1336 | |
e6a9dde5 | 1337 | DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<<endl); |
d2323cd0 | 1338 | if(p->qtype.getCode() != QType::DS && tryReferral(p, r, sd, target, retargetcount)) |
571726e9 PD |
1339 | goto sendit; |
1340 | ||
e6a9dde5 | 1341 | DLOG(g_log<<"Got no referrals, trying ANY"<<endl); |
571726e9 | 1342 | |
8900e2e3 | 1343 | #ifdef HAVE_LUA_RECORDS |
25bcfaec | 1344 | if(!doLua) { |
1345 | string val; | |
27a630b4 | 1346 | d_dk.getFromMeta(sd.qname, "ENABLE-LUA-RECORDS", val); |
25bcfaec | 1347 | doLua = (val=="1"); |
1348 | } | |
8900e2e3 | 1349 | #endif |
ff2e84e9 PD |
1350 | |
1351 | // see what we get.. | |
1352 | B.lookup(QType(QType::ANY), target, p, sd.domain_id); | |
1353 | rrset.clear(); | |
1354 | haveAlias.trimToLabels(0); | |
0abea1ca | 1355 | aliasScopeMask = 0; |
ff2e84e9 | 1356 | weDone = weRedirected = weHaveUnauth = false; |
35fe50c3 BH |
1357 | |
1358 | while(B.get(rr)) { | |
8900e2e3 | 1359 | #ifdef HAVE_LUA_RECORDS |
6b547a53 | 1360 | if(rr.dr.d_type == QType::LUA) { |
25bcfaec | 1361 | if(!doLua) |
1362 | continue; | |
5dbd408c | 1363 | auto rec=getRR<LUARecordContent>(rr.dr); |
98fa3598 RG |
1364 | if (!rec) { |
1365 | continue; | |
1366 | } | |
1367 | if(rec->d_type == QType::CNAME || rec->d_type == p->qtype.getCode() || (p->qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) { | |
bce078d4 | 1368 | noCache=true; |
1bc56192 CHB |
1369 | try { |
1370 | auto recvec=luaSynth(rec->getCode(), target, sd.qname, sd.domain_id, *p, rec->d_type); | |
1371 | if(!recvec.empty()) { | |
1372 | for(const auto& r : recvec) { | |
1373 | rr.dr.d_type = rec->d_type; // might be CNAME | |
1374 | rr.dr.d_content = r; | |
1375 | rr.scopeMask = p->getRealRemote().getBits(); // this makes sure answer is a specific as your question | |
1376 | rrset.push_back(rr); | |
1377 | } | |
1378 | if(rec->d_type == QType::CNAME && p->qtype.getCode() != QType::CNAME) | |
1379 | weRedirected = 1; | |
1380 | else | |
1381 | weDone = 1; | |
bce078d4 | 1382 | } |
1bc56192 CHB |
1383 | } |
1384 | catch(std::exception &e) { | |
1385 | r=p->replyPacket(); | |
1386 | r->setRcode(RCode::ServFail); | |
1387 | ||
1388 | return r; | |
6b547a53 | 1389 | } |
6b547a53 | 1390 | } |
1391 | } | |
8900e2e3 | 1392 | #endif |
1fda8e87 | 1393 | //cerr<<"got content: ["<<rr.content<<"]"<<endl; |
90ba52e0 | 1394 | if (p->qtype.getCode() == QType::ANY && !p->d_dnssecOk && (rr.dr.d_type == QType:: DNSKEY || rr.dr.d_type == QType::NSEC3PARAM)) |
65538369 | 1395 | continue; // Don't send dnssec info to non validating resolvers. |
90ba52e0 | 1396 | if (rr.dr.d_type == QType::RRSIG) // RRSIGS are added later any way. |
65538369 | 1397 | continue; // TODO: this actually means addRRSig should check if the RRSig is already there |
794c2f92 | 1398 | |
90ba52e0 | 1399 | // cerr<<"Auth: "<<rr.auth<<", "<<(rr.dr.d_type == p->qtype)<<", "<<rr.dr.d_type.getName()<<endl; |
1400 | if((p->qtype.getCode() == QType::ANY || rr.dr.d_type == p->qtype.getCode()) && rr.auth) | |
82cc1f71 | 1401 | weDone=1; |
5b739515 | 1402 | // the line below fakes 'unauth NS' for delegations for non-DNSSEC backends. |
90ba52e0 | 1403 | 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 | 1404 | weHaveUnauth=1; |
35fe50c3 | 1405 | |
90ba52e0 | 1406 | if(rr.dr.d_type == QType::CNAME && p->qtype.getCode() != QType::CNAME) |
82cc1f71 | 1407 | weRedirected=1; |
1dfd8ada | 1408 | |
90ba52e0 | 1409 | if(DP && rr.dr.d_type == QType::ALIAS && (p->qtype.getCode() == QType::A || p->qtype.getCode() == QType::AAAA || p->qtype.getCode() == QType::ANY)) { |
389b7a05 | 1410 | if (!d_doExpandALIAS) { |
e6a9dde5 | 1411 | g_log<<Logger::Info<<"ALIAS record found for "<<target<<", but ALIAS expansion is disabled."<<endl; |
389b7a05 PL |
1412 | continue; |
1413 | } | |
90ba52e0 | 1414 | haveAlias=getRR<ALIASRecordContent>(rr.dr)->d_content; |
0abea1ca | 1415 | aliasScopeMask=rr.scopeMask; |
d59b894d | 1416 | } |
1417 | ||
1dfd8ada | 1418 | // Filter out all SOA's and add them in later |
90ba52e0 | 1419 | if(rr.dr.d_type == QType::SOA) |
1dfd8ada MZ |
1420 | continue; |
1421 | ||
35fe50c3 | 1422 | rrset.push_back(rr); |
12c86877 BH |
1423 | } |
1424 | ||
1dfd8ada | 1425 | /* Add in SOA if required */ |
e325f20c | 1426 | if(target==sd.qname) { |
13f9e280 | 1427 | rr.dr.d_name = sd.qname; |
90ba52e0 | 1428 | rr.dr.d_type = QType::SOA; |
13f9e280 | 1429 | sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname); |
90ba52e0 | 1430 | rr.dr.d_content = makeSOAContent(sd); |
90ba52e0 | 1431 | rr.dr.d_ttl = sd.ttl; |
1dfd8ada MZ |
1432 | rr.domain_id = sd.domain_id; |
1433 | rr.auth = true; | |
1434 | rrset.push_back(rr); | |
1435 | } | |
1436 | ||
8dee0750 | 1437 | |
e6a9dde5 | 1438 | DLOG(g_log<<"After first ANY query for '"<<target<<"', id="<<sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<haveAlias<<"'"<<endl); |
32d24117 PD |
1439 | if(p->qtype.getCode() == QType::DS && weHaveUnauth && !weDone && !weRedirected) { |
1440 | DLOG(g_log<<"Q for DS of a name for which we do have NS, but for which we don't have DS; need to provide an AUTH answer that shows we don't"<<endl); | |
290a083d | 1441 | makeNOError(p, r, target, DNSName(), sd, 1); |
849bd7f1 BH |
1442 | goto sendit; |
1443 | } | |
12c86877 | 1444 | |
65d2032b | 1445 | if(!haveAlias.empty() && (!weDone || p->qtype.getCode() == QType::ANY)) { |
e6a9dde5 | 1446 | DLOG(g_log<<Logger::Warning<<"Found nothing that matched for '"<<target<<"', but did get alias to '"<<haveAlias<<"', referring"<<endl); |
0abea1ca | 1447 | DP->completePacket(r, haveAlias, target, aliasScopeMask); |
d59b894d | 1448 | return 0; |
1449 | } | |
1450 | ||
35fe50c3 | 1451 | if(rrset.empty()) { |
725070e7 | 1452 | DLOG(g_log<<"checking if qtype is DS"<<endl); |
0a0f4112 PD |
1453 | if(p->qtype.getCode() == QType::DS) |
1454 | { | |
e6a9dde5 | 1455 | DLOG(g_log<<"DS query found no direct result, trying referral now"<<endl); |
d2323cd0 | 1456 | if(tryReferral(p, r, sd, target, retargetcount)) |
0a0f4112 | 1457 | { |
e6a9dde5 | 1458 | DLOG(g_log<<"got referral for DS query"<<endl); |
0a0f4112 PD |
1459 | goto sendit; |
1460 | } | |
1461 | } | |
1462 | ||
d59b894d | 1463 | |
e6a9dde5 | 1464 | DLOG(g_log<<Logger::Warning<<"Found nothing in the by-name ANY, but let's try wildcards.."<<endl); |
e3f388cd | 1465 | bool wereRetargeted(false), nodata(false); |
561434a6 | 1466 | DNSName wildcard; |
75a89ce6 | 1467 | if(tryWildcard(p, r, sd, target, wildcard, wereRetargeted, nodata)) { |
82cc1f71 | 1468 | if(wereRetargeted) { |
3e8216c8 | 1469 | if(!retargetcount) r->qdomainwild=wildcard; |
82cc1f71 BH |
1470 | retargetcount++; |
1471 | goto retargeted; | |
1472 | } | |
c5c4fbdc PD |
1473 | if(nodata) |
1474 | makeNOError(p, r, target, wildcard, sd, 2); | |
1475 | ||
82cc1f71 | 1476 | goto sendit; |
8e50cd4c | 1477 | } |
8dee0750 | 1478 | else if(tryDNAME(p, r, sd, target)) { |
1479 | retargetcount++; | |
1480 | goto retargeted; | |
1481 | } | |
a87b7e3f PD |
1482 | else |
1483 | { | |
c5c4fbdc PD |
1484 | if (!(((p->qtype.getCode() == QType::CNAME) || (p->qtype.getCode() == QType::ANY)) && retargetcount > 0)) |
1485 | makeNXDomain(p, r, target, wildcard, sd); | |
a87b7e3f PD |
1486 | } |
1487 | ||
82cc1f71 BH |
1488 | goto sendit; |
1489 | } | |
232f0877 | 1490 | |
35fe50c3 | 1491 | if(weRedirected) { |
2010ac95 RG |
1492 | for(auto& loopRR: rrset) { |
1493 | if(loopRR.dr.d_type == QType::CNAME) { | |
1494 | r->addRecord(loopRR); | |
1495 | target = getRR<CNAMERecordContent>(loopRR.dr)->getTarget(); | |
82cc1f71 BH |
1496 | retargetcount++; |
1497 | goto retargeted; | |
4957a608 | 1498 | } |
82cc1f71 | 1499 | } |
82cc1f71 | 1500 | } |
35fe50c3 | 1501 | else if(weDone) { |
b5baefaf | 1502 | bool haveRecords = false; |
2010ac95 | 1503 | for(const auto& loopRR: rrset) { |
8900e2e3 | 1504 | #ifdef HAVE_LUA_RECORDS |
bce078d4 | 1505 | if(loopRR.dr.d_type == QType::LUA) |
1506 | continue; | |
8900e2e3 | 1507 | #endif |
2010ac95 RG |
1508 | 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) { |
1509 | r->addRecord(loopRR); | |
b5baefaf PD |
1510 | haveRecords = true; |
1511 | } | |
82cc1f71 | 1512 | } |
4957a608 | 1513 | |
b5baefaf PD |
1514 | if (haveRecords) { |
1515 | if(p->qtype.getCode() == QType::ANY) | |
1516 | completeANYRecords(p, r, sd, target); | |
82cc1f71 | 1517 | } |
b5baefaf | 1518 | else |
b9817f2b | 1519 | makeNOError(p, r, target, DNSName(), sd, 0); |
4957a608 | 1520 | |
35fe50c3 | 1521 | goto sendit; |
82cc1f71 | 1522 | } |
35fe50c3 | 1523 | else if(weHaveUnauth) { |
e6a9dde5 | 1524 | DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<<endl); |
d2323cd0 | 1525 | if(tryReferral(p, r, sd, target, retargetcount)) |
82cc1f71 | 1526 | goto sendit; |
2b18bcf3 | 1527 | // check whether this could be fixed easily |
90ba52e0 | 1528 | // if (*(rr.dr.d_name.rbegin()) == '.') { |
e6a9dde5 | 1529 | // 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 | 1530 | // } else { |
e6a9dde5 | 1531 | g_log<<Logger::Error<<"Should not get here ("<<p->qdomain<<"|"<<p->qtype.getCode()<<"): please run pdnsutil rectify-zone "<<sd.qname<<endl; |
561434a6 | 1532 | // } |
82cc1f71 BH |
1533 | } |
1534 | else { | |
e6a9dde5 | 1535 | DLOG(g_log<<"Have some data, but not the right data"<<endl); |
290a083d | 1536 | makeNOError(p, r, target, DNSName(), sd, 0); |
82cc1f71 | 1537 | } |
12c86877 BH |
1538 | |
1539 | sendit:; | |
d2323cd0 | 1540 | if(doAdditionalProcessingAndDropAA(p, r, sd, retargetcount)<0) { |
f3a91936 | 1541 | delete r; |
12c86877 | 1542 | return 0; |
f3a91936 | 1543 | } |
8ea10bfc | 1544 | |
2010ac95 RG |
1545 | for(const auto& loopRR: r->getRRS()) { |
1546 | if(loopRR.scopeMask) { | |
bf269e28 | 1547 | noCache=true; |
c28bf199 | 1548 | break; |
606018f2 | 1549 | } |
1550 | } | |
e02d0a59 | 1551 | if(p->d_dnssecOk) |
8d3cbffa | 1552 | addRRSigs(d_dk, B, authSet, r->getRRS()); |
f3a91936 | 1553 | |
e02d0a59 | 1554 | r->wrapup(); // needed for inserting in cache |
bf269e28 | 1555 | if(!noCache && p->couldBeCached()) |
071d2d90 | 1556 | PC.insert(p, r, r->getMinTTL()); // in the packet cache |
12c86877 BH |
1557 | } |
1558 | catch(DBException &e) { | |
e6a9dde5 | 1559 | g_log<<Logger::Error<<"Backend reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl; |
25e7af37 KM |
1560 | delete r; |
1561 | r=p->replyPacket(); // generate an empty reply packet | |
12c86877 | 1562 | r->setRcode(RCode::ServFail); |
eefd15f9 | 1563 | S.inc("servfail-packets"); |
eb029b8e | 1564 | S.ringAccount("servfail-queries", p->qdomain, p->qtype); |
12c86877 | 1565 | } |
3f81d239 | 1566 | catch(PDNSException &e) { |
e6a9dde5 | 1567 | g_log<<Logger::Error<<"Backend reported permanent error which prevented lookup ("+e.reason+"), aborting"<<endl; |
31d9bb01 | 1568 | throw; // we WANT to die at this point |
86113ac9 | 1569 | } |
5172cb78 | 1570 | catch(std::exception &e) { |
e6a9dde5 | 1571 | g_log<<Logger::Error<<"Exception building answer packet for "<<p->qdomain<<"/"<<p->qtype.getName()<<" ("<<e.what()<<") sending out servfail"<<endl; |
8ea10bfc | 1572 | delete r; |
25e7af37 | 1573 | r=p->replyPacket(); // generate an empty reply packet |
8ea10bfc BH |
1574 | r->setRcode(RCode::ServFail); |
1575 | S.inc("servfail-packets"); | |
eb029b8e | 1576 | S.ringAccount("servfail-queries", p->qdomain, p->qtype); |
8ea10bfc | 1577 | } |
12c86877 BH |
1578 | return r; |
1579 | ||
1580 | } |