Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <dns/message.h>
16 : : #include <dns/rcode.h>
17 : : #include <dns/rrtype.h>
18 : : #include <dns/rrset.h>
19 : : #include <dns/rdataclass.h>
20 : :
21 : : #include <datasrc/client.h>
22 : :
23 : : #include <auth/query.h>
24 : :
25 : : #include <boost/foreach.hpp>
26 : : #include <boost/bind.hpp>
27 : : #include <boost/function.hpp>
28 : :
29 : : #include <cassert>
30 : : #include <algorithm> // for std::max
31 : : #include <functional>
32 : : #include <vector>
33 : :
34 : : using namespace std;
35 : : using namespace isc::dns;
36 : : using namespace isc::datasrc;
37 : : using namespace isc::dns::rdata;
38 : :
39 : : // This is a "constant" vector storing desired RR types for the additional
40 : : // section. The vector is filled first time it's used.
41 : : namespace {
42 : : const vector<RRType>&
43 : 73 : A_AND_AAAA() {
44 [ + + ][ + - ]: 73 : static vector<RRType> needed_types;
45 [ + + ]: 73 : if (needed_types.empty()) {
46 : 1 : needed_types.push_back(RRType::A());
47 : 1 : needed_types.push_back(RRType::AAAA());
48 : : }
49 : 73 : return (needed_types);
50 : : }
51 : : }
52 : :
53 : : namespace isc {
54 : : namespace auth {
55 : :
56 : : void
57 : 245 : Query::ResponseCreator::addRRset(isc::dns::Message& message,
58 : : const isc::dns::Message::Section section,
59 : : const ConstRRsetPtr& rrset, const bool dnssec)
60 : : {
61 : : /// Is this RRset already in the list of RRsets added to the message?
62 : : const std::vector<const AbstractRRset*>::const_iterator i =
63 : 490 : std::find_if(added_.begin(), added_.end(),
64 : : std::bind1st(Query::ResponseCreator::IsSameKind(),
65 : 245 : rrset.get()));
66 [ + + ]: 245 : if (i == added_.end()) {
67 : : // No - add it to both the message and the list of RRsets processed.
68 : : // The const-cast is wrong, but the message interface seems to insist.
69 : : message.addRRset(section,
70 : : boost::const_pointer_cast<AbstractRRset>(rrset),
71 [ + - ]: 231 : dnssec);
72 : 231 : added_.push_back(rrset.get());
73 : : }
74 : 245 : }
75 : :
76 : : void
77 : 72 : Query::ResponseCreator::create(Message& response,
78 : : const vector<ConstRRsetPtr>& answers,
79 : : const vector<ConstRRsetPtr>& authorities,
80 : : const vector<ConstRRsetPtr>& additionals,
81 : : const bool dnssec)
82 : : {
83 : : // Inserter should be reset each time the query is reset, so should be
84 : : // empty at this point.
85 [ - + ]: 72 : assert(added_.empty());
86 : :
87 : : // Add the RRsets to the message. The order of sections is important,
88 : : // as the ResponseCreator remembers RRsets added and will not add
89 : : // duplicates. Adding in the order answer, authory, additional will
90 : : // guarantee that if there are duplicates, the single RRset added will
91 : : // appear in the most important section.
92 [ + + ][ + - ]: 170 : BOOST_FOREACH(const ConstRRsetPtr& rrset, answers) {
[ + - ][ + + ]
[ + + ]
93 : 49 : addRRset(response, Message::SECTION_ANSWER, rrset, dnssec);
94 : : }
95 [ + + ][ + - ]: 284 : BOOST_FOREACH(const ConstRRsetPtr& rrset, authorities) {
[ + - ][ + + ]
[ + + ]
96 : 106 : addRRset(response, Message::SECTION_AUTHORITY, rrset, dnssec);
97 : : }
98 [ + + ][ + - ]: 252 : BOOST_FOREACH(const ConstRRsetPtr& rrset, additionals) {
[ + - ][ + + ]
[ + + ]
99 : 90 : addRRset(response, Message::SECTION_ADDITIONAL, rrset, dnssec);
100 : : }
101 : 72 : }
102 : :
103 : : void
104 : 40 : Query::addSOA(ZoneFinder& finder) {
105 : 38 : ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
106 [ + - ]: 40 : RRType::SOA(), dnssec_opt_);
107 [ + + ]: 38 : if (soa_ctx->code != ZoneFinder::SUCCESS) {
108 [ + - ][ + - ]: 4 : isc_throw(NoSOA, "There's no SOA record in zone " <<
[ + - ][ + - ]
[ + - ]
109 : : finder.getOrigin().toText());
110 : : } else {
111 [ + - ]: 36 : authorities_.push_back(soa_ctx->rrset);
112 : : }
113 : 36 : }
114 : :
115 : : // Note: unless the data source client implementation or the zone content
116 : : // is broken, 'nsec' should be a valid NSEC RR. Likewise, the call to
117 : : // find() in this method should result in NXDOMAIN and an NSEC RR that proves
118 : : // the non existent of matching wildcard. If these assumptions aren't met
119 : : // due to a buggy data source implementation or a broken zone, we'll let
120 : : // underlying libdns++ modules throw an exception, which would result in
121 : : // either an SERVFAIL response or just ignoring the query. We at least prevent
122 : : // a complete crash due to such broken behavior.
123 : : void
124 : 9 : Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
125 [ + + ]: 9 : if (nsec->getRdataCount() == 0) {
126 [ + - ]: 2 : isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
127 : : }
128 : :
129 : : // Add the NSEC proving NXDOMAIN to the authority section.
130 : 8 : authorities_.push_back(nsec);
131 : :
132 : : // Next, identify the best possible wildcard name that would match
133 : : // the query name. It's the longer common suffix with the qname
134 : : // between the owner or the next domain of the NSEC that proves NXDOMAIN,
135 : : // prefixed by the wildcard label, "*". For example, for query name
136 : : // a.b.example.com, if the NXDOMAIN NSEC is
137 : : // b.example.com. NSEC c.example.com., the longer suffix is b.example.com.,
138 : : // and the best possible wildcard is *.b.example.com. If the NXDOMAIN
139 : : // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
140 : : // is the next domain of the NSEC, and we get the same wildcard name.
141 : 16 : const int qlabels = qname_->getLabelCount();
142 : 8 : const int olabels = qname_->compare(nsec->getName()).getCommonLabels();
143 : : const int nlabels = qname_->compare(
144 : 8 : dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
145 [ + - ]: 8 : getCurrent()).
146 [ + + ][ + - ]: 8 : getNextName()).getCommonLabels();
[ + - ]
147 : 7 : const int common_labels = std::max(olabels, nlabels);
148 : 7 : const Name wildname(Name("*").concatenate(qname_->split(qlabels -
149 [ + - ][ + - ]: 21 : common_labels)));
[ + - ]
150 : :
151 : : // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
152 : : // otherwise we shouldn't have got NXDOMAIN for the original query in
153 : : // the first place).
154 : : ConstZoneFinderContextPtr fcontext =
155 [ + - ]: 7 : finder.find(wildname, RRType::NSEC(), dnssec_opt_);
156 [ + + ][ + + ]: 12 : if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
[ + + ][ + + ]
157 [ + - ]: 5 : fcontext->rrset->getRdataCount() == 0) {
158 [ + - ][ + - ]: 6 : isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
159 : : }
160 : :
161 : : // Add the (no-) wildcard proof. This can be the same NSEC we already
162 : : // added, but we'd add it here anyway; duplicate checks will take place
163 : : // later in a unified manner.
164 [ + - ]: 4 : authorities_.push_back(fcontext->rrset);
165 : 4 : }
166 : :
167 : : uint8_t
168 : 15 : Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
169 : : bool exact_ok, bool add_closest)
170 : : {
171 : 28 : const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, true);
172 : :
173 : : // Validity check (see the method description). Note that a completely
174 : : // broken findNSEC3 implementation could even return NULL RRset in
175 : : // closest_proof. We don't explicitly check such case; addRRset() will
176 : : // throw an exception, and it will be converted to SERVFAIL at the caller.
177 [ + + ][ + + ]: 13 : if (!exact_ok && !result.next_proof) {
[ + + ]
178 [ + - ][ + - ]: 9 : isc_throw(BadNSEC3, "Matching NSEC3 found for a non existent name: "
179 : : << qname_);
180 : : }
181 : :
182 [ + + ]: 10 : if (add_closest) {
183 [ + - ]: 8 : authorities_.push_back(result.closest_proof);
184 : : }
185 [ + + ]: 10 : if (result.next_proof) {
186 [ + - ]: 8 : authorities_.push_back(result.next_proof);
187 : : }
188 : 10 : return (result.closest_labels);
189 : : }
190 : :
191 : : void
192 : 6 : Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
193 : 12 : const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, false);
194 : :
195 : : // See the comment for addClosestEncloserProof(). We don't check a
196 : : // totally bogus case where closest_proof is NULL here.
197 [ + + ]: 6 : if (match != result.matched) {
198 [ + - ][ + + ]: 6 : isc_throw(BadNSEC3, "Unexpected "
[ + - ][ + - ]
[ + - ][ + - ]
199 : : << (result.matched ? "matching" : "covering")
200 : : << " NSEC3 found for " << name);
201 : : }
202 [ + - ]: 3 : authorities_.push_back(result.closest_proof);
203 : 3 : }
204 : :
205 : : void
206 : 5 : Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
207 : : // Firstly get the NSEC3 proves for Closest Encloser Proof
208 : : // See Section 7.2.1 of RFC 5155.
209 : : const uint8_t closest_labels =
210 : 5 : addClosestEncloserProof(finder, *qname_, false);
211 : :
212 : : // Next, construct the wildcard name at the closest encloser, i.e.,
213 : : // '*' followed by the closest encloser, and add NSEC3 for it.
214 : 2 : const Name wildname(Name("*").concatenate(
215 [ + - ][ + - ]: 6 : qname_->split(qname_->getLabelCount() - closest_labels)));
[ + - ]
216 [ + + ]: 2 : addNSEC3ForName(finder, wildname, false);
217 : 1 : }
218 : :
219 : : void
220 : 8 : Query::addWildcardProof(ZoneFinder& finder,
221 : : const ZoneFinder::Context& db_context)
222 : : {
223 [ + + ]: 8 : if (db_context.isNSECSigned()) {
224 : : // Case for RFC4035 Section 3.1.3.3.
225 : : //
226 : : // The query name shouldn't exist in the zone if there were no wildcard
227 : : // substitution. Confirm that by specifying NO_WILDCARD. It should
228 : : // result in NXDOMAIN and an NSEC RR that proves it should be returned.
229 : : ConstZoneFinderContextPtr fcontext =
230 : 5 : finder.find(*qname_, RRType::NSEC(),
231 : 5 : dnssec_opt_ | ZoneFinder::NO_WILDCARD);
232 [ + + ][ + + ]: 8 : if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
[ + + ][ + + ]
233 [ + - ]: 3 : fcontext->rrset->getRdataCount() == 0) {
234 [ + - ][ + - ]: 6 : isc_throw(BadNSEC,
235 : : "Unexpected NSEC result for wildcard proof");
236 : : }
237 [ + - ]: 2 : authorities_.push_back(fcontext->rrset);
238 : : } else if (db_context.isNSEC3Signed()) {
239 : : // Case for RFC 5155 Section 7.2.6.
240 : : //
241 : : // Note that the closest encloser must be the immediate ancestor
242 : : // of the matching wildcard, so NSEC3 for its next closer (and only
243 : : // that NSEC3) is what we are expected to provided per the RFC (if
244 : : // this assumption isn't met the zone is broken anyway).
245 : 3 : addClosestEncloserProof(finder, *qname_, false, false);
246 : : }
247 : 4 : }
248 : :
249 : : void
250 : 3 : Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
251 : : // There should be one NSEC RR which was found in the zone to prove
252 : : // that there is not matched <QNAME,QTYPE> via wildcard expansion.
253 [ - + ]: 3 : if (nsec->getRdataCount() == 0) {
254 [ # # ]: 0 : isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
255 : : }
256 : :
257 : : ConstZoneFinderContextPtr fcontext =
258 : 3 : finder.find(*qname_, RRType::NSEC(),
259 : 3 : dnssec_opt_ | ZoneFinder::NO_WILDCARD);
260 [ + - ][ + - ]: 6 : if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
[ - + ][ - + ]
261 [ + - ]: 3 : fcontext->rrset->getRdataCount() == 0) {
262 [ # # ][ # # ]: 0 : isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
263 : : }
264 : :
265 [ + - ]: 3 : authorities_.push_back(fcontext->rrset);
266 : 3 : }
267 : :
268 : : void
269 : 7 : Query::addDS(ZoneFinder& finder, const Name& dname) {
270 : : ConstZoneFinderContextPtr ds_context =
271 : 7 : finder.find(dname, RRType::DS(), dnssec_opt_);
272 [ + + ]: 7 : if (ds_context->code == ZoneFinder::SUCCESS) {
273 [ + - ]: 2 : authorities_.push_back(ds_context->rrset);
274 [ + + + + ]: 9 : } else if (ds_context->code == ZoneFinder::NXRRSET &&
[ + + ]
275 : 4 : ds_context->isNSECSigned()) {
276 [ + - ]: 1 : addNXRRsetProof(finder, *ds_context);
277 [ + + + + ]: 7 : } else if (ds_context->code == ZoneFinder::NXRRSET &&
[ + + ]
278 : 3 : ds_context->isNSEC3Signed()) {
279 : : // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
280 [ + - ]: 2 : addClosestEncloserProof(finder, dname, true);
281 [ + + ]: 2 : } else if (ds_context->code != ZoneFinder::NXRRSET) {
282 : : // We know this domain should exist, so the result must be NXRRSET.
283 : : // If not, the zone is broken, so we'll return SERVFAIL by triggering
284 : : // an exception.
285 [ + - ][ + - ]: 2 : isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
286 : : }
287 : 6 : }
288 : :
289 : : void
290 : 18 : Query::addNXRRsetProof(ZoneFinder& finder,
291 : : const ZoneFinder::Context& db_context)
292 : : {
293 [ + + ][ - + ]: 28 : if (db_context.isNSECSigned() && db_context.rrset) {
[ + + ]
294 : 10 : authorities_.push_back(db_context.rrset);
295 [ + + ]: 10 : if (db_context.isWildcard()) {
296 [ + - ]: 3 : addWildcardNXRRSETProof(finder, db_context.rrset);
297 : : }
298 [ + + ][ + + ]: 8 : } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
[ + + ]
299 [ + + ]: 4 : if (*qtype_ == RRType::DS()) {
300 : : // RFC 5155, Section 7.2.4. Add either NSEC3 for the qname or
301 : : // closest (provable) encloser proof in case of optout.
302 : 2 : addClosestEncloserProof(finder, *qname_, true);
303 : : } else {
304 : : // RFC 5155, Section 7.2.3. Just add NSEC3 for the qname.
305 : 2 : addNSEC3ForName(finder, *qname_, true);
306 : : }
307 [ + + ][ - + ]: 4 : } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
[ + + ]
308 : : // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
309 : : // qname, construct the matched wildcard name and add NSEC3 for it.
310 : : const uint8_t closest_labels =
311 : 3 : addClosestEncloserProof(finder, *qname_, false);
312 : 2 : const Name wname = Name("*").concatenate(
313 [ + - ][ + - ]: 6 : qname_->split(qname_->getLabelCount() - closest_labels));
[ + - ]
314 [ + + ]: 2 : addNSEC3ForName(finder, wname, true);
315 : : }
316 : 15 : }
317 : :
318 : : void
319 : 31 : Query::addAuthAdditional(ZoneFinder& finder,
320 : : vector<ConstRRsetPtr>& additionals)
321 : : {
322 : 62 : const Name& origin = finder.getOrigin();
323 : :
324 : : // Fill in authority and addtional sections.
325 : 31 : ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
326 [ + - ]: 31 : dnssec_opt_);
327 : :
328 : : // zone origin name should have NS records
329 [ + + ]: 31 : if (ns_context->code != ZoneFinder::SUCCESS) {
330 [ + - ][ + - ]: 2 : isc_throw(NoApexNS, "There's no apex NS records in zone " <<
[ + - ][ + - ]
[ + - ]
331 : : finder.getOrigin().toText());
332 : : }
333 [ + - ]: 30 : authorities_.push_back(ns_context->rrset);
334 [ + - ]: 30 : ns_context->getAdditional(A_AND_AAAA(), additionals);
335 : 30 : }
336 : :
337 : : namespace {
338 : : // A simple wrapper for DataSourceClient::findZone(). Normally we can simply
339 : : // check the closest zone to the qname, but for type DS query we need to
340 : : // look into the parent zone. Nevertheless, if there is no "parent" (i.e.,
341 : : // the qname consists of a single label, which also means it's the root name),
342 : : // we should search the deepest zone we have (which should be the root zone;
343 : : // otherwise it's a query error).
344 : : DataSourceClient::FindResult
345 : 101 : findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
346 [ + + ][ + + ]: 101 : if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
[ + + ]
347 : 92 : return (client.findZone(qname));
348 : : }
349 [ + - ]: 99 : return (client.findZone(qname.split(1)));
350 : : }
351 : : }
352 : :
353 : : void
354 : 101 : Query::process(datasrc::DataSourceClient& datasrc_client,
355 : : const isc::dns::Name& qname, const isc::dns::RRType& qtype,
356 : : isc::dns::Message& response, bool dnssec)
357 : : {
358 : : // Set up the cleaner object so internal pointers and vectors are
359 : : // always reset after scope leaves this method
360 : : QueryCleaner cleaner(*this);
361 : :
362 : : // Set up query parameters for the rest of the (internal) methods
363 [ + - ]: 101 : initialize(datasrc_client, qname, qtype, response, dnssec);
364 : :
365 : : // Found a zone which is the nearest ancestor to QNAME
366 : : const DataSourceClient::FindResult result = findZone(*datasrc_client_,
367 [ + + ]: 101 : *qname_, *qtype_);
368 : :
369 : : // If we have no matching authoritative zone for the query name, return
370 : : // REFUSED. In short, this is to be compatible with BIND 9, but the
371 : : // background discussion is not that simple. See the relevant topic
372 : : // at the BIND 10 developers's ML:
373 : : // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
374 [ + + ]: 99 : if (result.code != result::SUCCESS &&
375 : : result.code != result::PARTIALMATCH) {
376 : : // If we tried to find a "parent zone" for a DS query and failed,
377 : : // we may still have authority at the child side. If we do, the query
378 : : // has to be handled there.
379 [ + + ][ + - ]: 9 : if (*qtype_ == RRType::DS() && qname_->getLabelCount() > 1 &&
[ + + ][ + + ]
380 [ + - ]: 3 : processDSAtChild()) {
381 : : return;
382 : : }
383 [ + - ]: 4 : response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
384 [ + - ]: 4 : response_->setRcode(Rcode::REFUSED());
385 : : return;
386 : : }
387 : 93 : ZoneFinder& zfinder = *result.zone_finder;
388 : :
389 : : // We have authority for a zone that contain the query name (possibly
390 : : // indirectly via delegation). Look into the zone.
391 [ + - ]: 93 : response_->setHeaderFlag(Message::HEADERFLAG_AA);
392 [ + - ][ + - ]: 93 : response_->setRcode(Rcode::NOERROR());
393 : : boost::function0<ZoneFinderContextPtr> find;
394 : 186 : const bool qtype_is_any = (*qtype_ == RRType::ANY());
395 [ + + ]: 93 : if (qtype_is_any) {
396 : : find = boost::bind(&ZoneFinder::findAll, &zfinder, *qname_,
397 [ + - ][ + - ]: 12 : boost::ref(answers_), dnssec_opt_);
398 : : } else {
399 : : find = boost::bind(&ZoneFinder::find, &zfinder, *qname_, *qtype_,
400 [ + - ][ + - ]: 174 : dnssec_opt_);
401 : : }
402 : : ZoneFinderContextPtr db_context(find());
403 [ + + + + : 91 : switch (db_context->code) {
+ + - ]
404 : : case ZoneFinder::DNAME: {
405 : : // First, put the dname into the answer
406 [ + - ]: 4 : answers_.push_back(db_context->rrset);
407 : : /*
408 : : * Empty DNAME should never get in, as it is impossible to
409 : : * create one in master file.
410 : : *
411 : : * FIXME: Other way to prevent this should be done
412 : : */
413 [ + - ][ - + ]: 4 : assert(db_context->rrset->getRdataCount() > 0);
414 : : // Get the data of DNAME
415 : : const rdata::generic::DNAME& dname(
416 : : dynamic_cast<const rdata::generic::DNAME&>(
417 [ + - ][ + - ]: 4 : db_context->rrset->getRdataIterator()->getCurrent()));
[ + - ]
418 : : // The yet unmatched prefix dname
419 : 4 : const Name prefix(qname_->split(0, qname_->getLabelCount() -
420 [ + - ][ + - ]: 8 : db_context->rrset->getName().getLabelCount()));
421 : : // If we put it together, will it be too long?
422 : : // (The prefix contains trailing ., which will be removed
423 [ + - ][ + + ]: 8 : if (prefix.getLength() - Name::ROOT_NAME().getLength() +
424 [ + - ]: 4 : dname.getDname().getLength() > Name::MAX_WIRE) {
425 : : /*
426 : : * In case the synthesized name is too long, section 4.1
427 : : * of RFC 2672 mandates we return YXDOMAIN.
428 : : */
429 [ + - ]: 1 : response_->setRcode(Rcode::YXDOMAIN());
430 : : break;
431 : : }
432 : : // The new CNAME we are creating (it will be unsigned even
433 : : // with DNSSEC, the DNAME is signed and it can be validated
434 : : // by that)
435 : 3 : RRsetPtr cname(new RRset(*qname_, db_context->rrset->getClass(),
436 [ + - ][ + - ]: 6 : RRType::CNAME(), db_context->rrset->getTTL()));
[ + - ][ + - ]
[ + - ]
437 : : // Construct the new target by replacing the end
438 : 3 : cname->addRdata(rdata::generic::CNAME(qname_->split(0,
439 : 3 : qname_->getLabelCount() -
440 [ + - ]: 6 : db_context->rrset->getName().getLabelCount()).
441 [ + - ][ + - ]: 9 : concatenate(dname.getDname())));
[ + - ][ + - ]
[ + - ]
442 [ + - ]: 3 : answers_.push_back(cname);
443 : : break;
444 : : }
445 : : case ZoneFinder::CNAME:
446 : : /*
447 : : * We don't do chaining yet. Therefore handling a CNAME is
448 : : * mostly the same as handling SUCCESS, but we didn't get
449 : : * what we expected. It means no exceptions in ANY or NS
450 : : * on the origin (though CNAME in origin is probably
451 : : * forbidden anyway).
452 : : *
453 : : * So, just put it there.
454 : : */
455 [ + - ]: 6 : answers_.push_back(db_context->rrset);
456 : :
457 : : // If the answer is a result of wildcard substitution,
458 : : // add a proof that there's no closer name.
459 [ + + ][ - + ]: 6 : if (dnssec_ && db_context->isWildcard()) {
[ + + ]
460 [ + - ]: 2 : addWildcardProof(*result.zone_finder, *db_context);
461 : : }
462 : : break;
463 : : case ZoneFinder::SUCCESS:
464 : : // If query type is ANY, the rrs have already been added
465 [ + + ]: 33 : if (!qtype_is_any) {
466 [ + - ]: 30 : answers_.push_back(db_context->rrset);
467 : : }
468 : :
469 : : // Retrieve additional records for the answer
470 [ + - ]: 33 : db_context->getAdditional(A_AND_AAAA(), additionals_);
471 : :
472 : : // If apex NS records haven't been provided in the answer
473 : : // section, insert apex NS records into the authority section
474 : : // and AAAA/A RRS of each of the NS RDATA into the additional
475 : : // section.
476 : : // Checking the findZone() is a lightweight check to see if
477 : : // qname is the zone origin.
478 [ + + + - : 41 : if (result.code != result::SUCCESS ||
+ + ][ + + ]
[ + + ]
479 : 4 : db_context->code != ZoneFinder::SUCCESS ||
480 : 4 : (*qtype_ != RRType::NS() && !qtype_is_any))
481 : : {
482 [ + + ]: 31 : addAuthAdditional(*result.zone_finder, additionals_);
483 : : }
484 : :
485 : : // If the answer is a result of wildcard substitution,
486 : : // add a proof that there's no closer name.
487 [ + + ][ + + ]: 32 : if (dnssec_ && db_context->isWildcard()) {
[ + + ]
488 [ + + ]: 6 : addWildcardProof(*result.zone_finder, *db_context);
489 : : }
490 : : break;
491 : : case ZoneFinder::DELEGATION:
492 : : // If a DS query resulted in delegation, we also need to check
493 : : // if we are an authority of the child, too. If so, we need to
494 : : // complete the process in the child as specified in Section
495 : : // 2.2.1.2. of RFC3658.
496 [ + + ][ + - ]: 11 : if (*qtype_ == RRType::DS() && processDSAtChild()) {
[ + + ][ + + ]
497 : : return;
498 : : }
499 : :
500 [ + - ]: 10 : response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
501 [ + - ]: 10 : authorities_.push_back(db_context->rrset);
502 : : // Retrieve additional records for the name servers
503 [ + - ]: 10 : db_context->getAdditional(A_AND_AAAA(), additionals_);
504 : :
505 : : // If DNSSEC is requested, see whether there is a DS
506 : : // record for this delegation.
507 [ + + ]: 10 : if (dnssec_) {
508 [ + - ][ + + ]: 7 : addDS(*result.zone_finder, db_context->rrset->getName());
509 : : }
510 : : break;
511 : : case ZoneFinder::NXDOMAIN:
512 [ + - ]: 20 : response_->setRcode(Rcode::NXDOMAIN());
513 [ + + ]: 20 : addSOA(*result.zone_finder);
514 [ + + ]: 16 : if (dnssec_) {
515 [ + + ][ - + ]: 23 : if (db_context->isNSECSigned() && db_context->rrset) {
[ + + ]
516 [ + + ]: 18 : addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
517 [ + - ]: 5 : } else if (db_context->isNSEC3Signed()) {
518 [ + + ]: 5 : addNXDOMAINProofByNSEC3(zfinder);
519 : : }
520 : : }
521 : : break;
522 : : case ZoneFinder::NXRRSET:
523 [ + - ]: 17 : addSOA(*result.zone_finder);
524 [ + + ]: 17 : if (dnssec_) {
525 [ + + ]: 15 : addNXRRsetProof(zfinder, *db_context);
526 : : }
527 : : break;
528 : : default:
529 : : // This is basically a bug of the data source implementation,
530 : : // but could also happen in the middle of development where
531 : : // we try to add a new result code.
532 [ # # ][ # # ]: 0 : isc_throw(isc::NotImplemented, "Unknown result code");
[ # # ]
533 : : break;
534 : : }
535 : :
536 : : response_creator_.create(*response_, answers_, authorities_, additionals_,
537 [ + - ]: 68 : dnssec_);
538 : : }
539 : :
540 : : void
541 : 101 : Query::initialize(datasrc::DataSourceClient& datasrc_client,
542 : : const isc::dns::Name& qname, const isc::dns::RRType& qtype,
543 : : isc::dns::Message& response, bool dnssec)
544 : : {
545 : 101 : datasrc_client_ = &datasrc_client;
546 : 101 : qname_ = &qname;
547 : 101 : qtype_ = &qtype;
548 : 101 : response_ = &response;
549 : 101 : dnssec_ = dnssec;
550 : : dnssec_opt_ = (dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
551 [ + + ]: 101 : isc::datasrc::ZoneFinder::FIND_DEFAULT);
552 : 101 : }
553 : :
554 : : void
555 : 101 : Query::reset() {
556 : 101 : datasrc_client_ = NULL;
557 : 101 : qname_ = NULL;
558 : 101 : qtype_ = NULL;
559 : 101 : response_ = NULL;
560 : 101 : answers_.clear();
561 : 101 : authorities_.clear();
562 : 101 : additionals_.clear();
563 : 101 : response_creator_.clear();
564 : 101 : }
565 : :
566 : : bool
567 : 5 : Query::processDSAtChild() {
568 : : const DataSourceClient::FindResult zresult =
569 : 5 : datasrc_client_->findZone(*qname_);
570 : :
571 [ + + ]: 5 : if (zresult.code != result::SUCCESS) {
572 : : return (false);
573 : : }
574 : :
575 : : // We are receiving a DS query at the child side of the owner name,
576 : : // where the DS isn't supposed to belong. We should return a "no data"
577 : : // response as described in Section 3.1.4.1 of RFC4035 and Section
578 : : // 2.2.1.1 of RFC 3658. find(DS) should result in NXRRSET, in which
579 : : // case (and if DNSSEC is required) we also add the proof for that,
580 : : // but even if find() returns an unexpected result, we don't bother.
581 : : // The important point in this case is to return SOA so that the resolver
582 : : // that happens to contact us can hunt for the appropriate parent zone
583 : : // by seeing the SOA.
584 [ + - ]: 3 : response_->setHeaderFlag(Message::HEADERFLAG_AA);
585 [ + - ][ + - ]: 3 : response_->setRcode(Rcode::NOERROR());
586 [ + - ]: 3 : addSOA(*zresult.zone_finder);
587 : : ConstZoneFinderContextPtr ds_context =
588 [ + - ]: 3 : zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
589 [ + + ]: 3 : if (ds_context->code == ZoneFinder::NXRRSET) {
590 [ + - ]: 2 : if (dnssec_) {
591 [ + - ]: 2 : addNXRRsetProof(*zresult.zone_finder, *ds_context);
592 : : }
593 : : }
594 : :
595 : : response_creator_.create(*response_, answers_, authorities_, additionals_,
596 [ + - ]: 3 : dnssec_);
597 : 3 : return (true);
598 : : }
599 : :
600 : : }
601 : 1 : }
|