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