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