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