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 <exceptions/exceptions.h>
16 : :
17 : : #include <dns/name.h>
18 : : #include <dns/nsec3hash.h>
19 : : #include <dns/rdataclass.h>
20 : : #include <dns/rrclass.h>
21 : : #include <dns/rrsetlist.h>
22 : : #include <dns/masterload.h>
23 : :
24 : : #include <datasrc/memory_datasrc.h>
25 : : #include <datasrc/rbtree.h>
26 : : #include <datasrc/rbnode_rrset.h>
27 : : #include <datasrc/logger.h>
28 : : #include <datasrc/iterator.h>
29 : : #include <datasrc/data_source.h>
30 : : #include <datasrc/factory.h>
31 : :
32 : : #include <boost/function.hpp>
33 : : #include <boost/shared_ptr.hpp>
34 : : #include <boost/scoped_ptr.hpp>
35 : : #include <boost/bind.hpp>
36 : : #include <boost/foreach.hpp>
37 : :
38 : : #include <algorithm>
39 : : #include <map>
40 : : #include <utility>
41 : : #include <cctype>
42 : : #include <cassert>
43 : :
44 : : using namespace std;
45 : : using namespace isc::dns;
46 : : using namespace isc::dns::rdata;
47 : : using boost::scoped_ptr;
48 : :
49 : : namespace isc {
50 : : namespace datasrc {
51 : :
52 : : using namespace internal;
53 : :
54 : : namespace {
55 : : // Some type aliases
56 : :
57 : : // A functor type used for loading.
58 : : typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
59 : :
60 : : // RRset specified for this implementation
61 : : typedef boost::shared_ptr<internal::RBNodeRRset> RBNodeRRsetPtr;
62 : : typedef boost::shared_ptr<const internal::RBNodeRRset> ConstRBNodeRRsetPtr;
63 : :
64 : : /*
65 : : * Each domain consists of some RRsets. They will be looked up by the
66 : : * RRType.
67 : : *
68 : : * The use of map is questionable with regard to performance - there'll
69 : : * be usually only few RRsets in the domain, so the log n benefit isn't
70 : : * much and a vector/array might be faster due to its simplicity and
71 : : * continuous memory location. But this is unlikely to be a performance
72 : : * critical place and map has better interface for the lookups, so we use
73 : : * that.
74 : : */
75 : : typedef map<RRType, ConstRBNodeRRsetPtr> Domain;
76 : : typedef Domain::value_type DomainPair;
77 : : typedef boost::shared_ptr<Domain> DomainPtr;
78 : : // The tree stores domains
79 : : typedef RBTree<Domain> DomainTree;
80 : : typedef RBNode<Domain> DomainNode;
81 : :
82 : : // In the following dedicated namespace we define a few application-specific
83 : : // RBNode flags. We use a separate namespace so we can consolidate the
84 : : // definition in a single place, which would hopefully reduce the risk of
85 : : // collisions.
86 : : // (Note: it's within an unnamed namespace, so effectively private.)
87 : : namespace domain_flag {
88 : : // This flag indicates the node is at a "wildcard level" (in short, it means
89 : : // one of the node's immediate child is a wildcard). See addWildcards()
90 : : // for more details.
91 : : const DomainNode::Flags WILD = DomainNode::FLAG_USER1;
92 : :
93 : : // This flag is used for additional record shortcut. If a node has this
94 : : // flag, it's under a zone cut for a delegation to a child zone.
95 : : // Note: for a statically built zone this information is stable, but if we
96 : : // change the implementation to be dynamically modifiable, it may not be
97 : : // realistic to keep this flag update for all affected nodes, and we may
98 : : // have to reconsider the mechanism.
99 : : const DomainNode::Flags GLUE = DomainNode::FLAG_USER2;
100 : :
101 : : // This flag indicates the node is generated as a result of wildcard
102 : : // expansion. In this implementation, this flag can be set only in
103 : : // the separate auxiliary tree of ZoneData (see the structure description).
104 : : const DomainNode::Flags WILD_EXPANDED = DomainNode::FLAG_USER3;
105 : : };
106 : :
107 : : // Separate storage for NSEC3 RRs (and their RRSIGs). It's an STL map
108 : : // from string to the NSEC3 RRset. The map key is the first label
109 : : // (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
110 : : // value). We can use the standard string comparison (if the comparison
111 : : // target is also upper cased) due to the nature of NSEC3 owner names.
112 : : //
113 : : // Note: We maintain the RRsets in the form of RBNodeRRset even if they are
114 : : // not stored in the RB tree. The reason is because comparison can be
115 : : // more efficient if we make sure all RRsets returned from this module are
116 : : // of the same type.
117 : : typedef map<string, ConstRBNodeRRsetPtr> NSEC3Map;
118 : : typedef NSEC3Map::value_type NSEC3Pair;
119 : :
120 : : // Actual zone data: Essentially a set of zone's RRs. This is defined as
121 : : // a separate structure so that it'll be replaceable on reload.
122 [ + - ][ + - ]: 214 : struct ZoneData {
123 : 214 : ZoneData(const Name& origin) :
124 : : domains_(true),
125 : : origin_data_(NULL),
126 : 0 : nsec_signed_(false)
127 : : {
128 : : // We create the node for origin (it needs to exist anyway in future)
129 [ + - ]: 214 : domains_.insert(origin, &origin_data_);
130 [ + - ][ + - ]: 214 : DomainPtr origin_domain(new Domain);
131 : 214 : origin_data_->setData(origin_domain);
132 : 214 : }
133 : :
134 : : // The main data (name + RRsets)
135 : : DomainTree domains_;
136 : :
137 : : // An auxiliary tree for wildcard expanded data used in additional data
138 : : // processing. It contains names like "ns.wild.example" in the following
139 : : // example:
140 : : // child.wild.example. NS ns.wild.example.
141 : : // *.wild.example IN AAAA 2001:db8::1234
142 : : // (and there's no exact ns.wild.example. in the zone). This tree contains
143 : : // such names with a copy of the RRsets of the matching wildcard name
144 : : // with its owner name expanded, e.g.:
145 : : // ns.wild.example. IN AAAA 2001:db8::1234
146 : : // In theory, this tree could have many such wildcard-expandable names,
147 : : // each of which has a copy of the original list of RRsets. In practice,
148 : : // however, it should be very rare that names for additional section
149 : : // processing are subject to wildcard expansion, so in most cases this tree
150 : : // should be even empty, and even if it has content it should be very
151 : : // small.
152 : : private:
153 : : scoped_ptr<DomainTree> aux_wild_domains_;
154 : : public:
155 : 65 : DomainTree& getAuxWildDomains() {
156 [ + + ]: 65 : if (!aux_wild_domains_) {
157 : 13 : aux_wild_domains_.reset(new DomainTree);
158 : : }
159 : 65 : return (*aux_wild_domains_);
160 : : }
161 : :
162 : : // Shortcut to the origin node, which should always exist
163 : : DomainNode* origin_data_;
164 : :
165 : : // The optional NSEC3 related data
166 : 35 : struct NSEC3Data {
167 : : NSEC3Data(const generic::NSEC3PARAM& nsec3param) :
168 [ + - ]: 20 : hash_(NSEC3Hash::create(nsec3param))
169 : : {}
170 : : NSEC3Data(const generic::NSEC3& nsec3) :
171 [ + - ][ # # ]: 15 : hash_(NSEC3Hash::create(nsec3))
172 : : {}
173 : : NSEC3Map map_; // Actual NSEC3 RRs
174 : : const scoped_ptr<NSEC3Hash> hash_; // hash parameter/calculator
175 : : };
176 : : scoped_ptr<NSEC3Data> nsec3_data_; // non NULL only when it's NSEC3 signed
177 : : bool nsec_signed_; // True if there's at least one NSEC record
178 : :
179 : : // This templated structure encapsulates the find result of findNode()
180 : : // method (also templated) below.
181 : : // The template parameter is expected to be either 'const DomainNode' or
182 : : // 'DomainNode' (to avoid misuse the template definition itself is kept
183 : : // private - we only expose expected typedefs). The former is expected
184 : : // to be used for lookups, and the latter is expected to be used for
185 : : // constructing the zone.
186 : : private:
187 : : template <typename NodeType>
188 : 734 : struct FindNodeResultBase {
189 : : // Bitwise flags to represent supplemental information of the
190 : : // search result:
191 : : // Search resulted in a wildcard match.
192 : : static const unsigned int FIND_WILDCARD = 1;
193 : : // Search encountered a zone cut due to NS but continued to look for
194 : : // a glue.
195 : : static const unsigned int FIND_ZONECUT = 2;
196 : :
197 : : FindNodeResultBase(ZoneFinder::Result code_param,
198 : : NodeType* node_param,
199 : : ConstRBNodeRRsetPtr rrset_param,
200 : : unsigned int flags_param = 0) :
201 : : code(code_param), node(node_param), rrset(rrset_param),
202 : 1468 : flags(flags_param)
203 : : {}
204 : : const ZoneFinder::Result code;
205 : : NodeType* const node;
206 : : ConstRBNodeRRsetPtr const rrset;
207 : : const unsigned int flags;
208 : : };
209 : : public:
210 : : typedef FindNodeResultBase<const DomainNode> FindNodeResult;
211 : : typedef FindNodeResultBase<DomainNode> FindMutableNodeResult;
212 : :
213 : : // Identify the RBTree node that best matches the given name.
214 : : // See implementation notes below.
215 : : //
216 : : // The caller should pass an empty node_path, and it will contain the
217 : : // search context (all ancestor nodes that the underlying RBTree search
218 : : // traverses, and how the search stops) for possible later use at the
219 : : // caller side.
220 : : template <typename ResultType>
221 : : ResultType findNode(const Name& name,
222 : : RBTreeNodeChain<Domain>& node_path,
223 : : ZoneFinder::FindOptions options) const;
224 : :
225 : : // A helper method for NSEC-signed zones. It searches the zone for
226 : : // the "closest" NSEC corresponding to the search context stored in
227 : : // node_path (it should contain sufficient information to identify the
228 : : // previous name of the query name in the zone). In some cases the
229 : : // immediate closest name may not have NSEC (when it's under a zone cut
230 : : // for glue records, or even when the zone is partly broken), so this
231 : : // method continues the search until it finds a name that has NSEC,
232 : : // and returns the one found first. Due to the prerequisite (see below),
233 : : // it should always succeed.
234 : : //
235 : : // node_path must store valid search context (in practice, it's expected
236 : : // to be set by findNode()); otherwise the underlying RBTree implementation
237 : : // throws.
238 : : //
239 : : // If the zone is not considered NSEC-signed or DNSSEC records were not
240 : : // required in the original search context (specified in options), this
241 : : // method doesn't bother to find NSEC, and simply returns NULL. So, by
242 : : // definition of "NSEC-signed", when it really tries to find an NSEC it
243 : : // should succeed; there should be one at least at the zone origin.
244 : : ConstRBNodeRRsetPtr
245 : : getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
246 : : ZoneFinder::FindOptions options) const;
247 : : };
248 : :
249 : : ConstRBNodeRRsetPtr
250 : 108 : ZoneData::getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
251 : : ZoneFinder::FindOptions options) const
252 : : {
253 [ + + ][ + + ]: 108 : if (!nsec_signed_ || (options & ZoneFinder::FIND_DNSSEC) == 0) {
254 : : return (ConstRBNodeRRsetPtr());
255 : : }
256 : :
257 : : const DomainNode* prev_node;
258 [ + - ]: 54 : while ((prev_node = domains_.previousNode(node_path)) != NULL) {
259 [ + + ]: 54 : if (!prev_node->isEmpty()) {
260 : : const Domain::const_iterator found =
261 : 36 : prev_node->getData()->find(RRType::NSEC());
262 [ + + ]: 36 : if (found != prev_node->getData()->end()) {
263 : 17 : return (found->second);
264 : : }
265 : : }
266 : : }
267 : : // This must be impossible and should be an internal bug.
268 : : // See the description at the method declaration.
269 : 108 : assert(false);
270 : : // Even though there is an assert here, strict compilers
271 : : // will still need some return value.
272 : : return (ConstRBNodeRRsetPtr());
273 : : }
274 : :
275 : : /// Maintain intermediate data specific to the search context used in
276 : : /// \c find().
277 : : ///
278 : : /// It will be passed to \c cutCallback() (see below) and record a possible
279 : : /// zone cut node and related RRset (normally NS or DNAME).
280 : 745 : struct FindState {
281 : : FindState(bool glue_ok) :
282 : : zonecut_node_(NULL),
283 : : dname_node_(NULL),
284 : 1490 : glue_ok_(glue_ok)
285 : : {}
286 : :
287 : : // These will be set to a domain node of the highest delegation point,
288 : : // if any. In fact, we could use a single variable instead of both.
289 : : // But then we would need to distinquish these two cases by something
290 : : // else and it seemed little more confusing when this was written.
291 : : const DomainNode* zonecut_node_;
292 : : const DomainNode* dname_node_;
293 : :
294 : : // Delegation RRset (NS or DNAME), if found.
295 : : ConstRBNodeRRsetPtr rrset_;
296 : :
297 : : // Whether to continue search below a delegation point.
298 : : // Set at construction time.
299 : : const bool glue_ok_;
300 : : };
301 : :
302 : : // A callback called from possible zone cut nodes and nodes with DNAME.
303 : : // This will be passed from findNode() to \c RBTree::find().
304 : 148 : bool cutCallback(const DomainNode& node, FindState* state) {
305 : : // We need to look for DNAME first, there's allowed case where
306 : : // DNAME and NS coexist in the apex. DNAME is the one to notice,
307 : : // the NS is authoritative, not delegation (corner case explicitly
308 : : // allowed by section 3 of 2672)
309 : : const Domain::const_iterator found_dname(node.getData()->find(
310 : 148 : RRType::DNAME()));
311 [ + + ]: 148 : if (found_dname != node.getData()->end()) {
312 [ + - ]: 17 : LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
313 : 17 : state->dname_node_ = &node;
314 : 17 : state->rrset_ = found_dname->second;
315 : : // No more processing below the DNAME (RFC 2672, section 3
316 : : // forbids anything to exist below it, so there's no need
317 : : // to actually search for it). This is strictly speaking
318 : : // a different way than described in 4.1 of that RFC,
319 : : // but because of the assumption in section 3, it has the
320 : : // same behaviour.
321 : 148 : return (true);
322 : : }
323 : :
324 : : // Look for NS
325 : 131 : const Domain::const_iterator found_ns(node.getData()->find(RRType::NS()));
326 [ + - ]: 131 : if (found_ns != node.getData()->end()) {
327 : : // We perform callback check only for the highest zone cut in the
328 : : // rare case of nested zone cuts.
329 [ + + ]: 131 : if (state->zonecut_node_ != NULL) {
330 : : return (false);
331 : : }
332 : :
333 [ + - ]: 129 : LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
334 : :
335 : : // BIND 9 checks if this node is not the origin. That's probably
336 : : // because it can support multiple versions for dynamic updates
337 : : // and IXFR, and it's possible that the callback is called at
338 : : // the apex and the DNAME doesn't exist for a particular version.
339 : : // It cannot happen for us (at least for now), so we don't do
340 : : // that check.
341 : 129 : state->zonecut_node_ = &node;
342 : 129 : state->rrset_ = found_ns->second;
343 : :
344 : : // Unless glue is allowed the search stops here, so we return
345 : : // false; otherwise return true to continue the search.
346 : 129 : return (!state->glue_ok_);
347 : : }
348 : :
349 : : // This case should not happen because we enable callback only
350 : : // when we add an RR searched for above.
351 : 0 : assert(0);
352 : : // This is here to avoid warning (therefore compilation error)
353 : : // in case assert is turned off. Otherwise we could get "Control
354 : : // reached end of non-void function".
355 : : return (false);
356 : : }
357 : :
358 : : // Implementation notes: this method identifies an RBT node that best matches
359 : : // the give name in terms of DNS query handling. In many cases,
360 : : // DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
361 : : // the given name is generally expected to be contained in the zone, so
362 : : // even if it doesn't exist, it should at least match the zone origin).
363 : : // If it finds an exact match, that's obviously the best one. The partial
364 : : // match case is more complicated.
365 : : //
366 : : // We first need to consider the case where search hits a delegation point,
367 : : // either due to NS or DNAME. They are indicated as either dname_node_ or
368 : : // zonecut_node_ being non NULL. Usually at most one of them will be
369 : : // something else than NULL (it might happen both are NULL, in which case we
370 : : // consider it NOT FOUND). There's one corner case when both might be
371 : : // something else than NULL and it is in case there's a DNAME under a zone
372 : : // cut and we search in glue OK mode ‒ in that case we don't stop on the
373 : : // domain with NS and ignore it for the answer, but it gets set anyway. Then
374 : : // we find the DNAME and we need to act by it, therefore we first check for
375 : : // DNAME and then for NS. In all other cases it doesn't matter, as at least
376 : : // one of them is NULL.
377 : : //
378 : : // Next, we need to check if the RBTree search stopped at a node for a
379 : : // subdomain of the search name (so the comparison result that stopped the
380 : : // search is "SUPERDOMAIN"), it means the stopping node is an empty
381 : : // non-terminal node. In this case the search name is considered to exist
382 : : // but no data should be found there.
383 : : //
384 : : // If none of above is the case, we then consider whether there's a matching
385 : : // wildcard. DomainTree::find() records the node if it encounters a
386 : : // "wildcarding" node, i.e., the immediate ancestor of a wildcard name
387 : : // (e.g., wild.example.com for *.wild.example.com), and returns it if it
388 : : // doesn't find any node that better matches the query name. In this case
389 : : // we'll check if there's indeed a wildcard below the wildcarding node.
390 : : //
391 : : // Note, first, that the wildcard is checked after the empty
392 : : // non-terminal domain case above, because if that one triggers, it
393 : : // means we should not match according to 4.3.3 of RFC 1034 (the query
394 : : // name is known to exist).
395 : : //
396 : : // Before we try to find a wildcard, we should check whether there's
397 : : // an existing node that would cancel the wildcard match. If
398 : : // DomainTree::find() stopped at a node which has a common ancestor
399 : : // with the query name, it might mean we are comparing with a
400 : : // non-wildcard node. In that case, we check which part is common. If
401 : : // we have something in common that lives below the node we got (the
402 : : // one above *), then we should cancel the match according to section
403 : : // 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
404 : : // query name is known to exist).
405 : : //
406 : : // If there's no node below the wildcarding node that shares a common ancestor
407 : : // of the query name, we can conclude the wildcard is the best match.
408 : : // We'll then identify the wildcard node via an incremental search. Note that
409 : : // there's no possibility that the query name is at an empty non terminal
410 : : // node below the wildcarding node at this stage; that case should have been
411 : : // caught above.
412 : : //
413 : : // If none of the above succeeds, we conclude the name doesn't exist in
414 : : // the zone.
415 : : template <typename ResultType>
416 : : ResultType
417 : 745 : ZoneData::findNode(const Name& name, RBTreeNodeChain<Domain>& node_path,
418 : : ZoneFinder::FindOptions options) const
419 : : {
420 : 745 : DomainNode* node = NULL;
421 : 745 : FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
422 : :
423 : : const DomainTree::Result result =
424 [ + - ][ + - ]: 745 : domains_.find(name, &node, node_path, cutCallback, &state);
425 : : const unsigned int zonecut_flag =
426 [ + + ][ + + ]: 745 : (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
427 [ + + ][ + + ]: 745 : if (result == DomainTree::EXACTMATCH) {
428 : : return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
429 : 559 : zonecut_flag));
430 [ + - ][ + + ]: 186 : } else if (result == DomainTree::PARTIALMATCH) {
431 [ - + ][ - + ]: 175 : assert(node != NULL);
432 [ + + ][ + + ]: 175 : if (state.dname_node_ != NULL) { // DNAME
433 [ + - ][ + - ]: 17 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
434 : : arg(state.rrset_->getName());
435 : : return (ResultType(ZoneFinder::DNAME, NULL, state.rrset_));
436 : : }
437 [ - + ][ + + ]: 158 : if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
438 [ # # ][ # # ]: 16 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
[ # # ][ # # ]
[ # # ][ # # ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
439 : : arg(state.rrset_->getName());
440 : : return (ResultType(ZoneFinder::DELEGATION, NULL, state.rrset_));
441 : : }
442 [ - + ][ + + ]: 142 : if (node_path.getLastComparisonResult().getRelation() ==
443 : : NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
444 [ # # ][ # # ]: 12 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name);
[ # # ][ # # ]
[ # # ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
445 : : return (ResultType(ZoneFinder::NXRRSET, node,
446 [ # # ][ + - ]: 12 : getClosestNSEC(node_path, options)));
447 : : }
448 [ + - ][ - + ]: 130 : if (node->getFlag(domain_flag::WILD) && // maybe a wildcard, check only
[ + - ][ + + ]
[ + + ][ + + ]
449 : : (options & ZoneFinder::NO_WILDCARD) == 0) { // if not disabled.
450 [ + - ][ + - ]: 91 : if (node_path.getLastComparisonResult().getRelation() ==
[ - + ][ + - ]
[ + + ][ + + ]
451 : : NameComparisonResult::COMMONANCESTOR &&
452 : : node_path.getLastComparisonResult().getCommonLabels() > 1) {
453 : : // Wildcard canceled. Treat it as NXDOMAIN.
454 : : // Note: Because the way the tree stores relative names, we
455 : : // will have exactly one common label (the ".") in case we have
456 : : // nothing common under the node we got, and we will get
457 : : // more common labels otherwise (yes, this relies on the
458 : : // internal RBTree structure, which leaks out through this
459 : : // little bit).
460 [ # # ][ # # ]: 8 : LOG_DEBUG(logger, DBG_TRACE_DATA,
[ # # ][ # # ]
[ # # ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
461 : : DATASRC_MEM_WILDCARD_CANCEL).arg(name);
462 : : return (ResultType(ZoneFinder::NXDOMAIN, NULL,
463 [ # # ][ + - ]: 8 : getClosestNSEC(node_path, options)));
464 : : }
465 : : // Now the wildcard should be the best match.
466 : : const Name wildcard(Name("*").concatenate(
467 [ + - ][ + - ]: 166 : node_path.getAbsoluteName()));
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
468 : :
469 : : // Clear the node_path so that we don't keep incorrect (NSEC)
470 : : // context
471 : : node_path.clear();
472 : : DomainTree::Result result(domains_.find(wildcard, &node,
473 : 166 : node_path));
474 : : // Otherwise, why would the domain_flag::WILD be there if
475 : : // there was no wildcard under it?
476 [ - + ][ - + ]: 83 : assert(result == DomainTree::EXACTMATCH);
477 : : return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
478 : : FindNodeResult::FIND_WILDCARD |
479 : 166 : zonecut_flag));
480 : : }
481 : : // Nothing really matched.
482 [ # # ][ # # ]: 39 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
[ # # ][ # # ]
[ # # ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
483 : : return (ResultType(ZoneFinder::NXDOMAIN, node,
484 [ # # ][ + - ]: 39 : getClosestNSEC(node_path, options)));
485 : : } else {
486 : : // If the name is neither an exact or partial match, it is
487 : : // out of bailiwick, which is considered an error.
488 [ # # ][ # # ]: 22 : isc_throw(OutOfZone, name.toText() << " not in " <<
[ # # ][ # # ]
[ # # ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
489 : : origin_data_->getName());
490 : : }
491 : : }
492 : : } // unnamed namespace
493 : :
494 : : namespace internal {
495 : :
496 : : /// \brief An encapsulation type for a pointer of an additional node
497 : : /// associated with an \c RBNodeRRset object.
498 : : ///
499 : : /// Currently this is defined as a structure only so that it can declared
500 : : /// in rbnode_rrset.h; this is essentially a pointer to \c DomainNode.
501 : : /// In future, however, this structure may have other attributes.
502 : 0 : struct AdditionalNodeInfo {
503 : 423 : explicit AdditionalNodeInfo(DomainNode* node) : node_(node) {}
504 : : DomainNode* node_;
505 : : };
506 : :
507 : : //
508 : : // RBNodeRRset details
509 : : //
510 : 2058 : struct RBNodeRRsetImpl {
511 : : public:
512 : 4116 : RBNodeRRsetImpl(const ConstRRsetPtr& rrset) : rrset_(rrset)
513 : : {}
514 : :
515 : : ConstRRsetPtr rrset_; ///< Underlying RRset
516 : : scoped_ptr<vector<AdditionalNodeInfo> > additionals_;
517 : : };
518 : :
519 : 2058 : RBNodeRRset::RBNodeRRset(const ConstRRsetPtr& rrset) :
520 [ + - ]: 2058 : impl_(new RBNodeRRsetImpl(rrset))
521 : : {
522 : 2058 : }
523 : :
524 : 4030 : RBNodeRRset::~RBNodeRRset() {
525 [ + - ]: 4116 : delete impl_;
526 : 4030 : }
527 : :
528 : : unsigned int
529 : 20 : RBNodeRRset::getRdataCount() const {
530 : 20 : return (impl_->rrset_->getRdataCount());
531 : : }
532 : :
533 : : const Name&
534 : 4447 : RBNodeRRset::getName() const {
535 : 4447 : return (impl_->rrset_->getName());
536 : : }
537 : :
538 : : const RRClass&
539 : 375 : RBNodeRRset::getClass() const {
540 : 375 : return (impl_->rrset_->getClass());
541 : : }
542 : :
543 : : const RRType&
544 : 15013 : RBNodeRRset::getType() const {
545 : 15013 : return (impl_->rrset_->getType());
546 : : }
547 : :
548 : : const RRTTL&
549 : 251 : RBNodeRRset::getTTL() const {
550 : 251 : return (impl_->rrset_->getTTL());
551 : : }
552 : :
553 : : void
554 : 1 : RBNodeRRset::setName(const Name&) {
555 [ + - ][ + - ]: 2 : isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
556 : : }
557 : :
558 : : void
559 : 1 : RBNodeRRset::setTTL(const RRTTL&) {
560 [ + - ][ + - ]: 2 : isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
561 : : }
562 : :
563 : : string
564 : 335 : RBNodeRRset::toText() const {
565 : 335 : return (impl_->rrset_->toText());
566 : : }
567 : :
568 : : unsigned int
569 : 14 : RBNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
570 : 14 : return (impl_->rrset_->toWire(renderer));
571 : : }
572 : :
573 : : unsigned int
574 : 2 : RBNodeRRset::toWire(isc::util::OutputBuffer& buffer) const {
575 : 2 : return (impl_->rrset_->toWire(buffer));
576 : : }
577 : :
578 : : void
579 : 1 : RBNodeRRset::addRdata(ConstRdataPtr) {
580 [ + - ][ + - ]: 2 : isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
581 : : }
582 : :
583 : : void
584 : 2 : RBNodeRRset::addRdata(const Rdata&) {
585 [ + - ][ + - ]: 4 : isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
586 : : }
587 : :
588 : : RdataIteratorPtr
589 : 1665 : RBNodeRRset::getRdataIterator() const {
590 : 1665 : return (impl_->rrset_->getRdataIterator());
591 : : }
592 : :
593 : : RRsetPtr
594 : 729 : RBNodeRRset::getRRsig() const {
595 : 729 : return (impl_->rrset_->getRRsig());
596 : : }
597 : :
598 : : void
599 : 1 : RBNodeRRset::addRRsig(const ConstRdataPtr& rdata) {
600 : 2 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
601 : 1 : p->addRRsig(rdata);
602 : 1 : }
603 : :
604 : : void
605 : 1 : RBNodeRRset::addRRsig(const RdataPtr& rdata) {
606 : 2 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
607 : 1 : p->addRRsig(rdata);
608 : 1 : }
609 : :
610 : : void
611 : 2 : RBNodeRRset::addRRsig(const AbstractRRset& sigs) {
612 : 4 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
613 : 2 : p->addRRsig(sigs);
614 : 2 : }
615 : :
616 : : void
617 : 642 : RBNodeRRset::addRRsig(const ConstRRsetPtr& sigs) {
618 : 1284 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
619 : 642 : p->addRRsig(sigs);
620 : 642 : }
621 : :
622 : : void
623 : 1 : RBNodeRRset::addRRsig(const RRsetPtr& sigs) {
624 : 2 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
625 : 1 : p->addRRsig(sigs);
626 : 1 : }
627 : :
628 : : void
629 : 1 : RBNodeRRset::removeRRsig() {
630 : 2 : AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
631 : 1 : p->removeRRsig();
632 : 1 : }
633 : :
634 : : ConstRRsetPtr
635 : 22 : RBNodeRRset::getUnderlyingRRset() const {
636 : 22 : return (impl_->rrset_);
637 : : }
638 : :
639 : : void
640 : 423 : RBNodeRRset::addAdditionalNode(const AdditionalNodeInfo& additional) {
641 : : // Lazy initialization
642 [ + + ]: 423 : if (!impl_->additionals_) {
643 : 240 : impl_->additionals_.reset(new vector<AdditionalNodeInfo>);
644 : : }
645 : 423 : impl_->additionals_->push_back(additional);
646 : 423 : }
647 : :
648 : : const vector<AdditionalNodeInfo>*
649 : 33 : RBNodeRRset::getAdditionalNodes() const {
650 : 33 : return (impl_->additionals_.get());
651 : : }
652 : :
653 : : void
654 : 51 : RBNodeRRset::copyAdditionalNodes(RBNodeRRset& dst) const {
655 [ + + ]: 51 : if (impl_->additionals_) {
656 : : dst.impl_->additionals_.reset(
657 : 1 : new vector<AdditionalNodeInfo>(impl_->additionals_->begin(),
658 : 2 : impl_->additionals_->end()));
659 : : }
660 : 51 : }
661 : :
662 : : } // end of internal
663 : :
664 : : namespace {
665 : : /*
666 : : * Prepares a rrset to be return as a result.
667 : : *
668 : : * If rename is false, it returns the one provided. If it is true, it
669 : : * creates a new rrset with the same data but with provided name.
670 : : * In addition, if DNSSEC records are required by the original caller of
671 : : * find(), it also creates expanded RRSIG based on the RRSIG of the
672 : : * wildcard RRset.
673 : : * It is designed for wildcard case, where we create the rrsets
674 : : * dynamically.
675 : : */
676 : : ConstRBNodeRRsetPtr
677 : 154 : prepareRRset(const Name& name, const ConstRBNodeRRsetPtr& rrset, bool rename,
678 : : ZoneFinder::FindOptions options)
679 : : {
680 [ + + ]: 154 : if (rename) {
681 [ + - ]: 102 : LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
682 [ + - ][ + - ]: 51 : arg(rrset->getName()).arg(name);
683 : 51 : RRsetPtr result_base(new RRset(name, rrset->getClass(),
684 [ + - ]: 51 : rrset->getType(), rrset->getTTL()));
685 [ + - ][ + - ]: 102 : for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
[ + + ]
686 [ + - ]: 51 : i->next()) {
687 [ + - ][ + - ]: 51 : result_base->addRdata(i->getCurrent());
688 : : }
689 [ + + ]: 51 : if ((options & ZoneFinder::FIND_DNSSEC) != 0) {
690 [ + - ]: 19 : ConstRRsetPtr sig_rrset = rrset->getRRsig();
691 [ + + ]: 19 : if (sig_rrset) {
692 : 6 : RRsetPtr result_sig(new RRset(name, sig_rrset->getClass(),
693 : : RRType::RRSIG(),
694 [ + - ][ + - ]: 6 : sig_rrset->getTTL()));
[ + - ][ + - ]
[ + - ]
695 [ + - ][ + + ]: 18 : for (RdataIteratorPtr i(sig_rrset->getRdataIterator());
696 [ + - ]: 12 : !i->isLast();
697 [ + - ]: 6 : i->next())
698 : : {
699 [ + - ][ + - ]: 6 : result_sig->addRdata(i->getCurrent());
700 : : }
701 [ + - ]: 6 : result_base->addRRsig(result_sig);
702 : : }
703 : : }
704 [ + - ][ + - ]: 51 : RBNodeRRsetPtr result(new RBNodeRRset(result_base));
705 [ + - ]: 51 : rrset->copyAdditionalNodes(*result);
706 : : return (result);
707 : : } else {
708 : : return (rrset);
709 : : }
710 : : }
711 : :
712 : : // Specialized version of ZoneFinder::ResultContext, which specifically
713 : : // holds rrset in the form of RBNodeRRset.
714 : 298 : struct RBNodeResultContext {
715 : : /// \brief Constructor
716 : : ///
717 : : /// The first three parameters correspond to those of
718 : : /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
719 : : /// found RBNode in the search.
720 : : RBNodeResultContext(ZoneFinder::Result code_param,
721 : : ConstRBNodeRRsetPtr rrset_param,
722 : : ZoneFinder::FindResultFlags flags_param,
723 : : const DomainNode* node) :
724 : : code(code_param), rrset(rrset_param), flags(flags_param),
725 : 596 : found_node(node)
726 : : {}
727 : :
728 : : const ZoneFinder::Result code;
729 : : const ConstRBNodeRRsetPtr rrset;
730 : : const ZoneFinder::FindResultFlags flags;
731 : : const DomainNode* const found_node;
732 : : };
733 : : }
734 : :
735 : 596 : class InMemoryZoneFinder::Context : public ZoneFinder::Context {
736 : : public:
737 : : /// \brief Constructor.
738 : : ///
739 : : /// Note that we don't have a specific constructor for the findAll() case.
740 : : /// For (successful) type ANY query, found_node points to the
741 : : /// corresponding RB node, which is recorded within this specialized
742 : : /// context.
743 : 298 : Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
744 : : const RBNodeResultContext& result) :
745 : : ZoneFinder::Context(finder, options,
746 : : ResultContext(result.code, result.rrset,
747 : : result.flags)),
748 : 1490 : rrset_(result.rrset), found_node_(result.found_node)
749 : 298 : {}
750 : :
751 : : protected:
752 : 31 : virtual void getAdditionalImpl(const vector<RRType>& requested_types,
753 : : vector<ConstRRsetPtr>& result)
754 : : {
755 [ + + ]: 31 : if (!rrset_) {
756 : : // In this case this context should encapsulate the result of
757 : : // findAll() and found_node_ should point to a valid answer node.
758 [ + - ][ + - ]: 1 : if (found_node_ == NULL || found_node_->isEmpty()) {
[ - + ]
759 [ # # ]: 0 : isc_throw(isc::Unexpected,
760 : : "Invalid call to in-memory getAdditional: caller's "
761 : : "bug or broken zone");
762 : : }
763 [ + + ][ + - ]: 7 : BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
[ + - ][ + + ]
[ + + ]
764 : 3 : getAdditionalForRRset(*dom_it.second, requested_types,
765 : 3 : result);
766 : : }
767 : : } else {
768 : 30 : getAdditionalForRRset(*rrset_, requested_types, result);
769 : : }
770 : 31 : }
771 : :
772 : : private:
773 : : // Retrieve additional RRsets for a given RRset associated in the context.
774 : : // The process is straightforward: it examines the link to
775 : : // AdditionalNodeInfo vector (if set), and find RRsets of the requested
776 : : // type for each node.
777 : 33 : static void getAdditionalForRRset(const RBNodeRRset& rrset,
778 : : const vector<RRType>& requested_types,
779 : : vector<ConstRRsetPtr>& result)
780 : : {
781 : : const vector<AdditionalNodeInfo>* additionals_ =
782 : 33 : rrset.getAdditionalNodes();
783 [ + + ]: 33 : if (additionals_ == NULL) {
784 : 33 : return;
785 : : }
786 : 28 : const bool glue_ok = (rrset.getType() == RRType::NS());
787 [ + + ][ + - ]: 146 : BOOST_FOREACH(const AdditionalNodeInfo& additional, *additionals_) {
[ + - ][ + + ]
[ + + ]
788 [ - + ]: 59 : assert(additional.node_ != NULL);
789 [ + + ]: 59 : if (additional.node_->isEmpty()) {
790 : 1 : continue;
791 : : }
792 [ + + ][ + + ]: 58 : if (!glue_ok && additional.node_->getFlag(domain_flag::GLUE)) {
[ + + ]
793 : 5 : continue;
794 : : }
795 : : const bool wild_expanded =
796 : 106 : additional.node_->getFlag(domain_flag::WILD_EXPANDED);
797 [ + + ][ + - ]: 317 : BOOST_FOREACH(const RRType& rrtype, requested_types) {
[ + - ][ + + ]
[ + + ]
798 : : Domain::const_iterator found =
799 : 86 : additional.node_->getData()->find(rrtype);
800 [ + + ]: 86 : if (found != additional.node_->getData()->end()) {
801 : : // If the additional node was generated as a result of
802 : : // wildcard expansion, we return the underlying RRset,
803 : : // in case the caller has the same RRset but as a result
804 : : // of normal find() and needs to know they are of the same
805 : : // kind; otherwise we simply use the stored RBNodeRRset.
806 [ + + ]: 51 : if (wild_expanded) {
807 [ + - ]: 1 : result.push_back(found->second->getUnderlyingRRset());
808 : : } else {
809 [ + - ]: 50 : result.push_back(found->second);
810 : : }
811 : : }
812 : : }
813 : : }
814 : : }
815 : :
816 : : const ConstRBNodeRRsetPtr rrset_;
817 : : const DomainNode* const found_node_;
818 : : };
819 : :
820 : : // Private data and hidden methods of InMemoryZoneFinder
821 [ + - ]: 151 : struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
822 : : // Constructor
823 : : InMemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) :
824 : : zone_class_(zone_class), origin_(origin),
825 [ + - ][ + - ]: 302 : zone_data_(new ZoneData(origin_))
[ + - ]
826 : : {}
827 : :
828 : : // Information about the zone
829 : : RRClass zone_class_;
830 : : Name origin_;
831 : : string file_name_;
832 : :
833 : : // The actual zone data
834 : : scoped_ptr<ZoneData> zone_data_;
835 : :
836 : : // Common process for zone load.
837 : : // rrset_installer is a functor that takes another functor as an argument,
838 : : // and expected to call the latter for each RRset of the zone. How the
839 : : // sequence of the RRsets is generated depends on the internal
840 : : // details of the loader: either from a textual master file or from
841 : : // another data source.
842 : : // filename is the file name of the master file or empty if the zone is
843 : : // loaded from another data source.
844 : : void load(const string& filename,
845 : : boost::function<void(LoadCallback)> rrset_installer);
846 : :
847 : : // Add the necessary magic for any wildcard contained in 'name'
848 : : // (including itself) to be found in the zone.
849 : : //
850 : : // In order for wildcard matching to work correctly in find(),
851 : : // we must ensure that a node for the wildcarding level exists in the
852 : : // backend RBTree.
853 : : // E.g. if the wildcard name is "*.sub.example." then we must ensure
854 : : // that "sub.example." exists and is marked as a wildcard level.
855 : : // Note: the "wildcarding level" is for the parent name of the wildcard
856 : : // name (such as "sub.example.").
857 : : //
858 : : // We also perform the same trick for empty wild card names possibly
859 : : // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
860 : 1051 : void addWildcards(DomainTree& domains, const Name& name) {
861 : 2102 : Name wname(name);
862 : 1051 : const unsigned int labels(wname.getLabelCount());
863 : 1051 : const unsigned int origin_labels(origin_.getLabelCount());
864 [ + - ][ + + ]: 3687 : for (unsigned int l = labels;
865 : : l > origin_labels;
866 [ + - ]: 2636 : --l, wname = wname.split(1)) {
867 [ + - ][ + + ]: 1318 : if (wname.isWildcard()) {
868 [ + - ][ + - ]: 168 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD).
[ + - ]
869 [ + - ][ + - ]: 84 : arg(name);
870 : : // Ensure a separate level exists for the "wildcarding" name,
871 : : // and mark the node as "wild".
872 : : DomainNode* node;
873 : 84 : DomainTree::Result result(domains.insert(wname.split(1),
874 [ + - ][ + - ]: 84 : &node));
875 : 0 : assert(result == DomainTree::SUCCESS ||
876 [ - + ]: 84 : result == DomainTree::ALREADYEXISTS);
877 [ + - ]: 84 : node->setFlag(domain_flag::WILD);
878 : :
879 : : // Ensure a separate level exists for the wildcard name.
880 : : // Note: for 'name' itself we do this later anyway, but the
881 : : // overhead should be marginal because wildcard names should
882 : : // be rare.
883 [ + - ]: 84 : result = domains.insert(wname, &node);
884 : 0 : assert(result == DomainTree::SUCCESS ||
885 [ - + ]: 84 : result == DomainTree::ALREADYEXISTS);
886 : : }
887 : : }
888 : 1051 : }
889 : :
890 : : // A helper predicate used in contextCheck() to check if a given domain
891 : : // name has a RRset of type different than NSEC.
892 : 3 : static bool isNotNSEC(const DomainPair& element) {
893 : 3 : return (element.second->getType() != RRType::NSEC());
894 : : }
895 : :
896 : : /*
897 : : * Does some checks in context of the data that are already in the zone.
898 : : * Currently checks for forbidden combinations of RRsets in the same
899 : : * domain (CNAME+anything, DNAME+NS).
900 : : *
901 : : * If such condition is found, it throws AddError.
902 : : */
903 : 1051 : void contextCheck(const AbstractRRset& rrset, const Domain& domain) const {
904 : : // Ensure CNAME and other type of RR don't coexist for the same
905 : : // owner name except with NSEC, which is the only RR that can coexist
906 : : // with CNAME (and also RRSIG, which is handled separately)
907 [ + + ]: 1051 : if (rrset.getType() == RRType::CNAME()) {
908 [ + + ]: 25 : if (find_if(domain.begin(), domain.end(), isNotNSEC)
909 : 50 : != domain.end()) {
910 [ + - ]: 4 : LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY).
911 [ + - ]: 2 : arg(rrset.getName());
912 [ + - ][ + - ]: 4 : isc_throw(AddError, "CNAME can't be added with other data for "
[ + - ][ + - ]
913 : : << rrset.getName());
914 : : }
915 [ + + + + ]: 2025 : } else if (rrset.getType() != RRType::NSEC() &&
[ + + ]
916 : 999 : domain.find(RRType::CNAME()) != domain.end()) {
917 [ + - ][ + - ]: 1 : LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset.getName());
918 [ + - ][ + - ]: 2 : isc_throw(AddError, "CNAME and " << rrset.getType() <<
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
919 : : " can't coexist for " << rrset.getName());
920 : : }
921 : :
922 : : /*
923 : : * Similar with DNAME, but it must not coexist only with NS and only in
924 : : * non-apex domains.
925 : : * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
926 : : */
927 [ + + + + : 3884 : if (rrset.getName() != origin_ &&
+ + + + +
+ ][ + + ]
928 : : // Adding DNAME, NS already there
929 : 827 : ((rrset.getType() == RRType::DNAME() &&
930 : 31 : domain.find(RRType::NS()) != domain.end()) ||
931 : : // Adding NS, DNAME already there
932 : 826 : (rrset.getType() == RRType::NS() &&
933 : 104 : domain.find(RRType::DNAME()) != domain.end())))
934 : : {
935 [ + - ][ + - ]: 2 : LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset.getName());
936 [ + - ][ + - ]: 4 : isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
[ + - ][ + - ]
937 : : "domain " << rrset.getName());
938 : : }
939 : 1046 : }
940 : :
941 : : // Validate rrset before adding it to the zone. If something is wrong
942 : : // it throws an exception. It doesn't modify the zone, and provides
943 : : // the strong exception guarantee.
944 : 1737 : void addValidation(const ConstRRsetPtr rrset) {
945 [ + + ]: 1737 : if (!rrset) {
946 [ + - ]: 2 : isc_throw(NullRRset, "The rrset provided is NULL");
947 : : }
948 [ + + ]: 1736 : if (rrset->getRdataCount() == 0) {
949 [ + - ][ + - ]: 2 : isc_throw(AddError, "The rrset provided is empty: " <<
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
950 : : rrset->getName() << "/" << rrset->getType());
951 : : }
952 : : // Check for singleton RRs. It should probably handled at a different
953 : : // layer in future.
954 [ + + + + : 3505 : if ((rrset->getType() == RRType::CNAME() ||
+ + ][ + + ]
955 : 1709 : rrset->getType() == RRType::DNAME()) &&
956 : 61 : rrset->getRdataCount() > 1)
957 : : {
958 : : // XXX: this is not only for CNAME or DNAME. We should generalize
959 : : // this code for all other "singleton RR types" (such as SOA) in a
960 : : // separate task.
961 [ + - + - ]: 6 : LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()).
962 [ + - ]: 4 : arg(rrset->getType());
963 [ + - ][ + - ]: 4 : isc_throw(AddError, "multiple RRs of singleton type for "
[ + - ][ + - ]
964 : : << rrset->getName());
965 : : }
966 : : // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
967 : : // implementation requests it be so at the moment.
968 [ + + + + : 3489 : if ((rrset->getType() == RRType::NSEC3() ||
+ + ][ + + ]
969 : 1532 : rrset->getType() == RRType::NSEC3PARAM()) &&
970 : 224 : rrset->getRdataCount() > 1) {
971 [ + - ][ + - ]: 4 : isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
[ + - ][ + - ]
[ + - ]
972 : : << rrset->getName() << " which isn't supported");
973 : : }
974 : :
975 : 1731 : NameComparisonResult compare(origin_.compare(rrset->getName()));
976 [ + + ][ + + ]: 1731 : if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
[ + + ]
977 : : compare.getRelation() != NameComparisonResult::EQUAL)
978 : : {
979 [ + - ][ + - ]: 2 : LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()).
980 [ + - ]: 1 : arg(origin_);
981 [ + - ][ + - ]: 2 : isc_throw(OutOfZone, "The name " << rrset->getName() <<
[ + - ][ + - ]
[ + - ]
982 : : " is not contained in zone " << origin_);
983 : : }
984 : :
985 : : // Some RR types do not really work well with a wildcard.
986 : : // Even though the protocol specifically doesn't completely ban such
987 : : // usage, we refuse to load a zone containing such RR in order to
988 : : // keep the lookup logic simpler and more predictable.
989 : : // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
990 : : // for more technical background. Note also that BIND 9 refuses
991 : : // NS at a wildcard, so in that sense we simply provide compatible
992 : : // behavior.
993 [ + + ]: 1730 : if (rrset->getName().isWildcard()) {
994 [ + + ]: 76 : if (rrset->getType() == RRType::NS()) {
995 [ + - ]: 2 : LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS).
996 [ + - ]: 1 : arg(rrset->getName());
997 [ + - ][ + - ]: 2 : isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
[ + - ][ + - ]
998 : : rrset->getName());
999 : : }
1000 [ + + ]: 75 : if (rrset->getType() == RRType::DNAME()) {
1001 [ + - ]: 2 : LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME).
1002 [ + - ]: 1 : arg(rrset->getName());
1003 [ + - ][ + - ]: 2 : isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
[ + - ][ + - ]
1004 : : rrset->getName());
1005 : : }
1006 : : }
1007 : :
1008 : : // Owner names of NSEC3 have special format as defined in RFC5155,
1009 : : // and cannot be a wildcard name or must be one label longer than
1010 : : // the zone origin. While the RFC doesn't prohibit other forms of
1011 : : // names, no sane zone would have such names for NSEC3.
1012 : : // BIND 9 also refuses NSEC3 at wildcard.
1013 [ + + + + : 2127 : if (rrset->getType() == RRType::NSEC3() &&
+ + ][ + + ]
1014 : 200 : (rrset->getName().isWildcard() ||
1015 : 199 : rrset->getName().getLabelCount() !=
1016 : : origin_.getLabelCount() + 1)) {
1017 [ + - ]: 4 : LOG_ERROR(logger, DATASRC_BAD_NSEC3_NAME).
1018 [ + - ]: 2 : arg(rrset->getName());
1019 [ + - ][ + - ]: 4 : isc_throw(AddError, "Invalid NSEC3 owner name: " <<
[ + - ][ + - ]
1020 : : rrset->getName());
1021 : : }
1022 : 1726 : }
1023 : :
1024 : 477 : result::Result addRRsig(const ConstRRsetPtr sig_rrset, ZoneData& zone_data)
1025 : : {
1026 : : // Check consistency of the type covered.
1027 : : // We know the RRset isn't empty, so the following check is safe.
1028 : 477 : RdataIteratorPtr rit = sig_rrset->getRdataIterator();
1029 : : const RRType covered = dynamic_cast<const generic::RRSIG&>(
1030 [ + - ][ + - ]: 477 : rit->getCurrent()).typeCovered();
[ + - ][ # # ]
[ # # ][ # # ]
1031 [ + - ][ + - ]: 478 : for (rit->next(); !rit->isLast(); rit->next()) {
[ + - ][ + + ]
[ # # ][ # # ]
[ # # ][ # # ]
1032 [ + + ][ # # ]: 2 : if (dynamic_cast<const generic::RRSIG&>(
1033 [ + - ][ + - ]: 2 : rit->getCurrent()).typeCovered() != covered) {
[ + - ][ # # ]
[ # # ][ # # ]
1034 [ + - ][ + - ]: 2 : isc_throw(AddError, "RRSIG contains mixed covered types: "
[ + - ][ + - ]
[ + - ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1035 : : << sig_rrset->toText());
1036 : : }
1037 : : }
1038 : :
1039 : : // Find the RRset to be covered; if not found, treat it as an error
1040 : : // for now.
1041 : : ConstRRsetPtr covered_rrset;
1042 [ + + ][ # # ]: 476 : if (covered != RRType::NSEC3()) {
1043 : 299 : DomainNode* node = NULL;
1044 [ + - ][ + - ]: 299 : if (zone_data.domains_.find(sig_rrset->getName(), &node) !=
[ + + ][ + - ]
[ + + ][ + + ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1045 : 298 : DomainTree::EXACTMATCH || node == NULL || !node->getData()) {
1046 [ + - ][ + - ]: 4 : isc_throw(AddError,
[ + - ][ + - ]
[ + - ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1047 : : "RRSIG is being added, but no RR to be covered: "
1048 : : << sig_rrset->getName());
1049 : : }
1050 : 297 : const Domain::const_iterator it = node->getData()->find(covered);
1051 [ + + # # ]: 297 : if (it != node->getData()->end()) {
1052 [ + - ][ # # ]: 296 : covered_rrset = it->second;
1053 : : }
1054 : : } else {
1055 : : // In case of NSEC3 if something is found it must be NSEC3 RRset
1056 : : // under the assumption of our current implementation.
1057 [ + + ][ # # ]: 177 : if (zone_data.nsec3_data_) {
1058 : : // Convert the first label to upper-cased text. Note that
1059 : : // for a valid NSEC3 RR the label should only consist of
1060 : : // positive 8-bit char values, so using toupper(int) should be
1061 : : // safe (if it's a bogus label for NSEC3 the zone won't work
1062 : : // anyway). Also note the '::' below: g++'s STL implementation
1063 : : // seems to require it to toupper to make this compile.
1064 : : string fst_label =
1065 [ + - ][ + - ]: 352 : sig_rrset->getName().split(0, 1).toText(true);
[ + - ][ # # ]
[ # # ][ # # ]
1066 : : transform(fst_label.begin(), fst_label.end(),
1067 [ + - ][ # # ]: 176 : fst_label.begin(), ::toupper);
1068 : :
1069 : : NSEC3Map::const_iterator found =
1070 : 176 : zone_data.nsec3_data_->map_.find(fst_label);
1071 [ + + # # ]: 176 : if (found != zone_data.nsec3_data_->map_.end()) {
1072 [ + - ][ # # ]: 175 : covered_rrset = found->second;
1073 [ + - ][ - + ]: 175 : assert(covered_rrset->getType() == covered);
[ # # ][ # # ]
1074 : : }
1075 : : }
1076 : : }
1077 [ + + ][ # # ]: 474 : if (!covered_rrset) {
1078 [ + - ][ + - ]: 6 : isc_throw(AddError, "RRSIG is being added, but no RR of "
[ + - ][ + - ]
[ + - ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1079 : : "covered type found: " << sig_rrset->toText());
1080 : : }
1081 : :
1082 : : // The current implementation doesn't allow an existing RRSIG to be
1083 : : // overridden (or updated with additional ones).
1084 [ + - ][ + + ]: 942 : if (covered_rrset->getRRsig()) {
[ # # ][ # # ]
1085 [ + - ][ + - ]: 6 : isc_throw(AddError,
[ + - ][ + - ]
[ + - ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1086 : : "RRSIG is being added to override an existing one: "
1087 : : << sig_rrset->toText());
1088 : : }
1089 : :
1090 : : // All okay, setting the RRSIG.
1091 : : // XXX: we break const-ness of the covered RRsets. In practice the
1092 : : // ownership of these RRsets would have been given to us so it should
1093 : : // be safe, but it's still a very bad practice.
1094 : : // We'll fix this problem anyway when we update the underlying
1095 : : // representation so that it's more space efficient.
1096 : : // Note: there's a slight chance of getting an exception.
1097 : : // As noted in add(), we give up strong exception guarantee in such
1098 : : // cases.
1099 [ + - ][ # # ]: 468 : boost::const_pointer_cast<AbstractRRset>(covered_rrset)->addRRsig(sig_rrset);
1100 : :
1101 : 468 : return (result::SUCCESS);
1102 : : }
1103 : :
1104 : 0 : result::Result addNSEC3(const ConstRRsetPtr rrset, ZoneData& zone_data) {
1105 : : // We know rrset has exactly one RDATA
1106 : : const generic::NSEC3& nsec3_rdata =
1107 : : dynamic_cast<const generic::NSEC3&>(
1108 [ + - ][ + - ]: 198 : rrset->getRdataIterator()->getCurrent());
[ + - ][ # # ]
[ # # ]
1109 : :
1110 : : // If we've not done any NSEC3 setup for the zone, do it now;
1111 : : // otherwise check parameter consistency.
1112 [ + + # # ]: 198 : if (!zone_data.nsec3_data_) {
1113 [ + - ][ + - ]: 30 : zone_data.nsec3_data_.reset(new ZoneData::NSEC3Data(nsec3_rdata));
1114 [ + - ][ + + ]: 366 : } else if (!zone_data.nsec3_data_->hash_->match(nsec3_rdata)) {
[ # # ]
1115 [ + - ][ + - ]: 4 : isc_throw(AddError, "NSEC3 with inconsistent parameters: " <<
[ + - ][ + - ]
[ + - ][ # # ]
[ # # ][ # # ]
[ # # ]
1116 : : rrset->toText());
1117 : : }
1118 : :
1119 [ + - ][ + - ]: 392 : string fst_label = rrset->getName().split(0, 1).toText(true);
[ + - ][ # # ]
1120 : : transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
1121 [ + - ][ # # ]: 196 : ::toupper);
1122 : :
1123 : : // Our current implementation doesn't allow an existing NSEC3 to be
1124 : : // updated/overridden.
1125 [ + + # # ]: 392 : if (zone_data.nsec3_data_->map_.find(fst_label) !=
1126 : 392 : zone_data.nsec3_data_->map_.end()) {
1127 : : return (result::EXIST);
1128 : : }
1129 : :
1130 : 195 : zone_data.nsec3_data_->map_.insert(
1131 [ + - ][ + - ]: 390 : NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
[ + - ][ # # ]
[ # # ][ # # ]
1132 : : return (result::SUCCESS);
1133 : : }
1134 : :
1135 : : /*
1136 : : * Implementation of longer methods. We put them here, because the
1137 : : * access is without the impl_-> and it will get inlined anyway.
1138 : : */
1139 : : // Implementation of InMemoryZoneFinder::add
1140 : 1737 : result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data,
1141 : : vector<RBNodeRRset*>* need_additionals)
1142 : : {
1143 : : // Sanitize input. This will cause an exception to be thrown
1144 : : // if the input RRset is empty.
1145 [ + + ]: 1737 : addValidation(rawrrset);
1146 : :
1147 : : // OK, can add the RRset.
1148 [ + - ]: 3452 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
1149 [ + - ][ + - ]: 1726 : arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
[ + - ]
1150 : :
1151 : : // ... although instead of loading the RRset directly, we encapsulate
1152 : : // it within an RBNodeRRset. This contains additional information that
1153 : : // speeds up queries.
1154 [ + - ]: 1726 : RBNodeRRsetPtr rrset(new RBNodeRRset(rawrrset));
1155 : :
1156 [ + - ][ + + ]: 1726 : if (rrset->getType() == RRType::NSEC3()) {
1157 : 196 : return (addNSEC3(rrset, zone_data));
1158 : : }
1159 : :
1160 : : // RRSIGs are special in various points, so we handle it in a
1161 : : // separate dedicated method.
1162 [ + - ][ + + ]: 1528 : if (rrset->getType() == RRType::RRSIG()) {
1163 [ + + ]: 477 : return (addRRsig(rrset, zone_data));
1164 : : }
1165 : :
1166 : : // Add wildcards possibly contained in the owner name to the domain
1167 : : // tree.
1168 : : // Note: this can throw an exception, breaking strong exception
1169 : : // guarantee. (see also the note for contextCheck() below).
1170 [ + - ][ + - ]: 1051 : addWildcards(zone_data.domains_, rrset->getName());
1171 : :
1172 : : // Get the node
1173 : : DomainNode* node;
1174 [ + - ]: 1051 : DomainTree::Result result = zone_data.domains_.insert(rrset->getName(),
1175 [ + - ]: 1051 : &node);
1176 : : // Just check it returns reasonable results
1177 : 0 : assert((result == DomainTree::SUCCESS ||
1178 [ + - ][ - + ]: 1051 : result == DomainTree::ALREADYEXISTS) && node!= NULL);
1179 : :
1180 : : // Now get the domain
1181 : : DomainPtr domain;
1182 : : // It didn't exist yet, create it
1183 [ + + ]: 1051 : if (node->isEmpty()) {
1184 [ + - ]: 652 : domain.reset(new Domain);
1185 : 652 : node->setData(domain);
1186 : : } else { // Get existing one
1187 [ + - ]: 399 : domain = node->getData();
1188 : : }
1189 : :
1190 : : // Checks related to the surrounding data.
1191 : : // Note: when the check fails and the exception is thrown, it may
1192 : : // break strong exception guarantee. At the moment we prefer
1193 : : // code simplicity and don't bother to introduce complicated
1194 : : // recovery code.
1195 [ + + ]: 1051 : contextCheck(*rrset, *domain);
1196 : :
1197 : : // Try inserting the rrset there
1198 [ + - ][ + + ]: 3138 : if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
1199 : : // Ok, we just put it in
1200 : :
1201 : : // If this RRset creates a zone cut at this node, mark the node
1202 : : // indicating the need for callback in find().
1203 [ + - ][ + + ]: 1213 : if (rrset->getType() == RRType::NS() &&
[ + + ][ + + ]
1204 [ + - ]: 171 : rrset->getName() != origin_) {
1205 [ + - ]: 103 : node->setFlag(DomainNode::FLAG_CALLBACK);
1206 : : // If it is DNAME, we have a callback as well here
1207 [ + - ][ + + ]: 939 : } else if (rrset->getType() == RRType::DNAME()) {
1208 [ + - ]: 32 : node->setFlag(DomainNode::FLAG_CALLBACK);
1209 : : }
1210 : :
1211 [ + + ][ + + ]: 2655 : if (need_additionals != NULL &&
[ + + ][ + + ]
1212 [ + - ]: 880 : (rrset->getType() == RRType::NS() ||
1213 [ + - ]: 733 : rrset->getType() == RRType::MX())) {
1214 [ + - ]: 243 : need_additionals->push_back(rrset.get());
1215 : : }
1216 : :
1217 : : // If we've added NSEC3PARAM at zone origin, set up NSEC3 specific
1218 : : // data or check consistency with already set up parameters.
1219 [ + - ][ + + ]: 1064 : if (rrset->getType() == RRType::NSEC3PARAM() &&
[ + + ][ + + ]
1220 [ + - ]: 22 : rrset->getName() == origin_) {
1221 : : // We know rrset has exactly one RDATA
1222 : : const generic::NSEC3PARAM& param =
1223 : : dynamic_cast<const generic::NSEC3PARAM&>(
1224 [ + - ][ + - ]: 21 : rrset->getRdataIterator()->getCurrent());
[ + - ]
1225 : :
1226 [ + + ]: 21 : if (!zone_data.nsec3_data_) {
1227 : : zone_data.nsec3_data_.reset(
1228 [ + - ][ + - ]: 40 : new ZoneData::NSEC3Data(param));
1229 [ + - ][ + - ]: 2 : } else if (!zone_data.nsec3_data_->hash_->match(param)) {
1230 [ + - ][ + - ]: 2 : isc_throw(AddError, "NSEC3PARAM with inconsistent "
[ + - ][ + - ]
[ + - ]
1231 : : "parameters: " << rrset->toText());
1232 : : }
1233 [ + - ][ + + ]: 1021 : } else if (rrset->getType() == RRType::NSEC()) {
1234 : : // If it is NSEC signed zone, so we put a flag there
1235 : : // (flag is enough)
1236 : 27 : zone_data.nsec_signed_ = true;
1237 : : }
1238 : : return (result::SUCCESS);
1239 : : } else {
1240 : : // The RRSet of given type was already there
1241 : : return (result::EXIST);
1242 : : }
1243 : : }
1244 : :
1245 : : /*
1246 : : * Same as above, but it checks the return value and if it already exists,
1247 : : * it throws.
1248 : : */
1249 : 1516 : void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data,
1250 : : vector<RBNodeRRset*>* need_additionals)
1251 : : {
1252 [ + - + ]: 1516 : switch (add(set, *zone_data, need_additionals)) {
1253 : : case result::EXIST:
1254 [ + - ]: 4 : LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
1255 [ + - ][ + - ]: 2 : arg(set->getName()).arg(set->getType());
1256 [ + - ][ + - ]: 4 : isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
[ + - ]
1257 : : set->toText());
1258 : : case result::SUCCESS:
1259 : 1514 : return;
1260 : : default:
1261 : 0 : assert(0);
1262 : : }
1263 : : }
1264 : :
1265 : : // A helper function for the NXRRSET case in find(). If the zone is
1266 : : // NSEC-signed and DNSSEC records are requested, try to find NSEC
1267 : : // on the given node, and return it if found; return NULL for all other
1268 : : // cases.
1269 : : ConstRBNodeRRsetPtr getNSECForNXRRSET(FindOptions options,
1270 : : const DomainNode& node) const
1271 : : {
1272 [ + + ][ + + ]: 32 : if (zone_data_->nsec_signed_ &&
[ + + ]
1273 : : (options & ZoneFinder::FIND_DNSSEC) != 0) {
1274 : : const Domain::const_iterator found =
1275 : 3 : node.getData()->find(RRType::NSEC());
1276 [ + - ]: 3 : if (found != node.getData()->end()) {
1277 : 3 : return (found->second);
1278 : : }
1279 : : }
1280 : : return (ConstRBNodeRRsetPtr());
1281 : : }
1282 : :
1283 : : // Set up FindContext object as a return value of find(), taking into
1284 : : // account wildcard matches and DNSSEC information. We set the NSEC/NSEC3
1285 : : // flag when applicable regardless of the find option; the caller would
1286 : : // simply ignore these when they didn't request DNSSEC related results.
1287 : : // When the optional parameter 'node' is given (in which case it should be
1288 : : // non NULL), it means it's a result of ANY query and the context should
1289 : : // remember the matched node.
1290 : 298 : RBNodeResultContext createFindResult(Result code,
1291 : : ConstRBNodeRRsetPtr rrset,
1292 : : bool wild = false,
1293 : : const DomainNode* node = NULL) const
1294 : : {
1295 : 298 : FindResultFlags flags = RESULT_DEFAULT;
1296 [ + + ]: 298 : if (wild) {
1297 : 57 : flags = flags | RESULT_WILDCARD;
1298 : : }
1299 [ + + ][ + + ]: 298 : if (code == NXRRSET || code == NXDOMAIN || wild) {
1300 [ + + ]: 356 : if (zone_data_->nsec3_data_) {
1301 : 43 : flags = flags | RESULT_NSEC3_SIGNED;
1302 : : }
1303 [ + + ]: 178 : if (zone_data_->nsec_signed_) {
1304 : 59 : flags = flags | RESULT_NSEC_SIGNED;
1305 : : }
1306 : : }
1307 : 298 : return (RBNodeResultContext(code, rrset, flags, node));
1308 : : }
1309 : :
1310 : : // Implementation of InMemoryZoneFinder::find
1311 : 309 : RBNodeResultContext find(const Name& name, RRType type,
1312 : : std::vector<ConstRRsetPtr>* target,
1313 : : const FindOptions options) const
1314 : : {
1315 [ + - ][ + - ]: 618 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
1316 [ + - ]: 309 : arg(type);
1317 : :
1318 : : // Get the node. All other cases than an exact match are handled
1319 : : // in findNode(). We simply construct a result structure and return.
1320 : : RBTreeNodeChain<Domain> node_path; // findNode will fill in this
1321 : : const ZoneData::FindNodeResult node_result =
1322 : : zone_data_->findNode<ZoneData::FindNodeResult>(name, node_path,
1323 : 309 : options);
1324 [ + + ]: 298 : if (node_result.code != SUCCESS) {
1325 [ + - ]: 79 : return (createFindResult(node_result.code, node_result.rrset));
1326 : : }
1327 : :
1328 : : // We've found an exact match, may or may not be a result of wildcard.
1329 : 219 : const DomainNode* node = node_result.node;
1330 [ - + ]: 219 : assert(node != NULL);
1331 : : const bool rename = ((node_result.flags &
1332 : 219 : ZoneData::FindNodeResult::FIND_WILDCARD) != 0);
1333 : :
1334 : : // If there is an exact match but the node is empty, it's equivalent
1335 : : // to NXRRSET.
1336 [ + + ]: 219 : if (node->isEmpty()) {
1337 [ + - ][ + - ]: 98 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
[ + - ]
1338 [ + - ][ + - ]: 49 : arg(name);
1339 : : return (createFindResult(NXRRSET,
1340 : : zone_data_->getClosestNSEC(node_path,
1341 : : options),
1342 [ + - ][ + - ]: 49 : rename));
1343 : : }
1344 : :
1345 : : Domain::const_iterator found;
1346 : :
1347 : : // If the node callback is enabled, this may be a zone cut. If it
1348 : : // has a NS RR, we should return a delegation, but not in the apex.
1349 : : // There is one exception: the case for DS query, which should always
1350 : : // be considered in-zone lookup.
1351 [ + + + + ]: 187 : if (node->getFlag(DomainNode::FLAG_CALLBACK) &&
[ + + ][ + + ]
1352 : 9 : node != zone_data_->origin_data_ && type != RRType::DS()) {
1353 : 7 : found = node->getData()->find(RRType::NS());
1354 [ + + ]: 7 : if (found != node->getData()->end()) {
1355 [ + - ][ + - ]: 8 : LOG_DEBUG(logger, DBG_TRACE_DATA,
[ + - ]
1356 [ + - ][ + - ]: 4 : DATASRC_MEM_EXACT_DELEGATION).arg(name);
1357 : : return (createFindResult(DELEGATION,
1358 : : prepareRRset(name, found->second,
1359 [ + - ][ + - ]: 4 : rename, options)));
1360 : : }
1361 : : }
1362 : :
1363 : : // handle type any query
1364 [ + + ][ - + ]: 166 : if (target != NULL && !node->getData()->empty()) {
[ + + ]
1365 : : // Empty domain will be handled as NXRRSET by normal processing
1366 [ + + ]: 30 : for (found = node->getData()->begin();
1367 : 21 : found != node->getData()->end(); ++found)
1368 : : {
1369 : : target->push_back(prepareRRset(name, found->second, rename,
1370 [ + - ][ + - ]: 24 : options));
1371 : : }
1372 [ + - ][ + - ]: 18 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
[ + - ]
1373 [ + - ][ + - ]: 9 : arg(name);
1374 : : return (createFindResult(SUCCESS, ConstRBNodeRRsetPtr(), rename,
1375 [ + - ]: 9 : node));
1376 : : }
1377 : :
1378 : 157 : found = node->getData()->find(type);
1379 [ + + ]: 157 : if (found != node->getData()->end()) {
1380 : : // Good, it is here
1381 [ + - ][ + - ]: 238 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
[ + - ][ + - ]
[ + - ]
1382 [ + - ]: 119 : arg(type);
1383 : : return (createFindResult(SUCCESS, prepareRRset(name,
1384 : : found->second,
1385 : : rename, options),
1386 [ + - ][ + - ]: 119 : rename));
1387 : : } else {
1388 : : // Next, try CNAME.
1389 : 38 : found = node->getData()->find(RRType::CNAME());
1390 [ + + ]: 38 : if (found != node->getData()->end()) {
1391 [ + - ][ + - ]: 6 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
[ + - ][ + - ]
[ + - ]
1392 : : return (createFindResult(CNAME,
1393 : : prepareRRset(name, found->second,
1394 : : rename, options),
1395 [ + - ][ + - ]: 6 : rename));
1396 : : }
1397 : : }
1398 : : // No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
1399 : : return (createFindResult(NXRRSET, getNSECForNXRRSET(options, *node),
1400 [ + - ]: 32 : rename));
1401 : : }
1402 : : };
1403 : :
1404 : 151 : InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class,
1405 : : const Name& origin) :
1406 [ + - ]: 151 : impl_(new InMemoryZoneFinderImpl(zone_class, origin))
1407 : : {
1408 [ + - ][ + - ]: 302 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
[ + - ][ + - ]
[ + - ]
1409 [ + - ]: 151 : arg(zone_class);
1410 : 151 : }
1411 : :
1412 : 224 : InMemoryZoneFinder::~InMemoryZoneFinder() {
1413 [ + - ][ + - ]: 453 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()).
[ + - ][ + - ]
[ + - ][ + - ]
1414 [ + - ][ + - ]: 302 : arg(getClass());
1415 [ + - ]: 302 : delete impl_;
1416 : 224 : }
1417 : :
1418 : : Name
1419 : 438 : InMemoryZoneFinder::getOrigin() const {
1420 : 438 : return (impl_->origin_);
1421 : : }
1422 : :
1423 : : RRClass
1424 : 290 : InMemoryZoneFinder::getClass() const {
1425 : 290 : return (impl_->zone_class_);
1426 : : }
1427 : :
1428 : : ZoneFinderContextPtr
1429 : 286 : InMemoryZoneFinder::find(const Name& name, const RRType& type,
1430 : : const FindOptions options)
1431 : : {
1432 : : return (ZoneFinderContextPtr(
1433 : : new Context(*this, options, impl_->find(name, type, NULL,
1434 [ + - ][ + - ]: 562 : options))));
[ + - ]
1435 : : }
1436 : :
1437 : : ZoneFinderContextPtr
1438 : 23 : InMemoryZoneFinder::findAll(const Name& name,
1439 : : std::vector<ConstRRsetPtr>& target,
1440 : : const FindOptions options)
1441 : : {
1442 : : return (ZoneFinderContextPtr(
1443 : : new Context(*this, options, impl_->find(name, RRType::ANY(),
1444 [ + - ][ + - ]: 45 : &target, options))));
[ + - ]
1445 : : }
1446 : :
1447 : : ZoneFinder::FindNSEC3Result
1448 : 22 : InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
1449 [ + - ][ + - ]: 66 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
1450 [ + + ][ + - ]: 44 : arg(recursive ? "recursive" : "non-recursive");
1451 : :
1452 [ + + ]: 22 : if (!impl_->zone_data_->nsec3_data_) {
1453 [ + - ][ + - ]: 2 : isc_throw(DataSourceError,
[ + - ][ + - ]
1454 : : "findNSEC3 attempt for non NSEC3 signed zone: " <<
1455 : : impl_->origin_ << "/" << impl_->zone_class_);
1456 : : }
1457 : 21 : const NSEC3Map& map = impl_->zone_data_->nsec3_data_->map_;
1458 [ + + ]: 21 : if (map.empty()) {
1459 [ + - ][ + - ]: 2 : isc_throw(DataSourceError,
[ + - ][ + - ]
1460 : : "findNSEC3 attempt but zone has no NSEC3 RR: " <<
1461 : : impl_->origin_ << "/" << impl_->zone_class_);
1462 : : }
1463 : 20 : const NameComparisonResult cmp_result = name.compare(impl_->origin_);
1464 [ + + ][ + + ]: 20 : if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
[ + + ]
1465 : : cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
1466 [ + - ][ + - ]: 4 : isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
[ + - ][ + - ]
[ + - ][ + - ]
1467 : : << name << ", zone: " << impl_->origin_ << "/"
1468 : : << impl_->zone_class_);
1469 : : }
1470 : :
1471 : : // Convenient shortcuts
1472 : 18 : const NSEC3Hash& nsec3hash = *impl_->zone_data_->nsec3_data_->hash_;
1473 : 36 : const unsigned int olabels = impl_->origin_.getLabelCount();
1474 : 18 : const unsigned int qlabels = name.getLabelCount();
1475 : :
1476 : : ConstRBNodeRRsetPtr covering_proof; // placeholder of the next closer proof
1477 : : // Examine all names from the query name to the origin name, stripping
1478 : : // the deepest label one by one, until we find a name that has a matching
1479 : : // NSEC3 hash.
1480 [ + + ]: 26 : for (unsigned int labels = qlabels; labels >= olabels; --labels) {
1481 : : const string hlabel = nsec3hash.calculate(
1482 [ + + ][ + - ]: 50 : labels == qlabels ? name : name.split(qlabels - labels, labels));
[ + - ][ + - ]
1483 : 25 : NSEC3Map::const_iterator found = map.lower_bound(hlabel);
1484 [ + - ][ + - ]: 50 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
[ + - ]
1485 [ + - ][ + - ]: 25 : arg(name).arg(labels).arg(hlabel);
[ + - ][ + - ]
1486 : :
1487 : : // If the given hash is larger than the largest stored hash or
1488 : : // the first label doesn't match the target, identify the "previous"
1489 : : // hash value and remember it as the candidate next closer proof.
1490 [ + + ][ + + ]: 48 : if (found == map.end() || found->first != hlabel) {
[ + + ]
1491 : : // If the given hash is larger or smaller than everything,
1492 : : // the covering proof is the NSEC3 that has the largest hash.
1493 : : // Note that we know the map isn't empty, so rbegin() is
1494 : : // safe.
1495 [ + + ][ + + ]: 14 : if (found == map.end() || found == map.begin()) {
[ + + ]
1496 [ + - ]: 6 : covering_proof = map.rbegin()->second;
1497 : : } else {
1498 : : // Otherwise, H(found_entry-1) < given_hash < H(found_entry).
1499 : : // The covering proof is the first one (and it's valid
1500 : : // because found is neither begin nor end)
1501 [ + - ]: 8 : covering_proof = (--found)->second;
1502 : : }
1503 [ + + ]: 14 : if (!recursive) { // in non recursive mode, we are done.
1504 [ + - ][ + - ]: 12 : LOG_DEBUG(logger, DBG_TRACE_BASIC,
[ + - ]
1505 : : DATASRC_MEM_FINDNSEC3_COVER).
1506 [ + - ][ + - ]: 6 : arg(name).arg(*covering_proof);
[ + - ]
1507 : : return (FindNSEC3Result(false, labels, covering_proof,
1508 : 6 : ConstRRsetPtr()));
1509 : : }
1510 : : } else { // found an exact match.
1511 [ + - ][ + - ]: 22 : LOG_DEBUG(logger, DBG_TRACE_BASIC,
[ + - ]
1512 [ + - ][ + - ]: 11 : DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
[ + - ]
1513 [ + - ]: 22 : arg(*found->second);
1514 : : return (FindNSEC3Result(true, labels, found->second,
1515 : 22 : covering_proof));
1516 : : }
1517 : : }
1518 : :
1519 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
[ + - ][ + - ]
[ + - ]
1520 : : "a broken NSEC3 zone: " << impl_->origin_ << "/"
1521 : : << impl_->zone_class_);
1522 : : }
1523 : :
1524 : : result::Result
1525 : 221 : InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
1526 : 221 : return (impl_->add(rrset, *impl_->zone_data_, NULL));
1527 : : }
1528 : :
1529 : : namespace {
1530 : : // This should eventually be more generalized.
1531 : : const Name
1532 : 488 : getAdditionalName(RRType rrtype, const rdata::Rdata& rdata) {
1533 [ + + ]: 488 : if (rrtype == RRType::NS()) {
1534 [ + - ]: 367 : const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
1535 : 367 : return (ns.getNSName());
1536 : : } else {
1537 : : // In our usage the only other possible case is MX.
1538 [ - + ]: 121 : assert(rrtype == RRType::MX());
1539 [ + - ]: 609 : const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
1540 : 121 : return (mx.getMXName());
1541 : : }
1542 : : }
1543 : :
1544 : : void
1545 : 13 : convertAndInsert(const DomainPair& rrset_item, DomainPtr dst_domain,
1546 : : const Name* dstname)
1547 : : {
1548 : : // We copy RRSIGs, too, if they are attached in case we need it in
1549 : : // getAdditional().
1550 : : dst_domain->insert(DomainPair(rrset_item.first,
1551 : : prepareRRset(*dstname, rrset_item.second,
1552 : : true,
1553 : 26 : ZoneFinder::FIND_DNSSEC)));
1554 : 13 : }
1555 : :
1556 : : void
1557 : 240 : addAdditional(RBNodeRRset* rrset, ZoneData* zone_data,
1558 : : vector<RBNodeRRset*>* wild_rrsets)
1559 : : {
1560 : 240 : RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
1561 : 240 : bool match_wild = false; // will be true if wildcard match is found
1562 : : RBTreeNodeChain<Domain> node_path; // placeholder for findNode()
1563 [ + - ][ + - ]: 689 : for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
[ + + ]
1564 : : // For each domain name that requires additional section processing
1565 : : // in each RDATA, search the tree for the name and remember it if
1566 : : // found. If the name is under a zone cut (for a delegation to a
1567 : : // child zone), mark the node as "GLUE", so we can selectively
1568 : : // include/exclude them when we use it.
1569 : :
1570 [ + - ]: 449 : const Name& name = getAdditionalName(rrset->getType(),
1571 [ + - ][ + - ]: 1347 : rdata_iterator->getCurrent());
1572 : : // if the name is not in or below this zone, skip it
1573 : : const NameComparisonResult::NameRelation reln =
1574 [ + - ]: 449 : name.compare(zone_data->origin_data_->getName()).getRelation();
1575 [ + + ]: 449 : if (reln != NameComparisonResult::SUBDOMAIN &&
1576 : : reln != NameComparisonResult::EQUAL) {
1577 : 13 : continue;
1578 : : }
1579 : : node_path.clear();
1580 : : const ZoneData::FindMutableNodeResult result =
1581 : : zone_data->findNode<ZoneData::FindMutableNodeResult>(
1582 [ + - ]: 436 : name, node_path, ZoneFinder::FIND_GLUE_OK);
1583 [ + + ]: 436 : if (result.code != ZoneFinder::SUCCESS) {
1584 : : // We are not interested in anything but a successful match.
1585 : 13 : continue;
1586 : : }
1587 : 423 : DomainNode* node = result.node;
1588 [ - + ]: 423 : assert(node != NULL);
1589 [ + + ]: 462 : if ((result.flags & ZoneData::FindNodeResult::FIND_ZONECUT) != 0 ||
[ + + + + ]
[ + + ]
1590 : : (node->getFlag(DomainNode::FLAG_CALLBACK) &&
1591 : 39 : node->getData()->find(RRType::NS()) != node->getData()->end())) {
1592 : : // The node is under or at a zone cut; mark it as a glue.
1593 [ + - ]: 134 : node->setFlag(domain_flag::GLUE);
1594 : : }
1595 : :
1596 : : // A rare case: the additional name may have to be expanded with a
1597 : : // wildcard. We'll store the name in a separate auxiliary tree,
1598 : : // copying all RRsets of the original wildcard node with expanding
1599 : : // the owner name. This is costly in terms of memory, but this case
1600 : : // should be pretty rare. On the other hand we won't have to worry
1601 : : // about wildcard expansion in getAdditional, which is quite
1602 : : // performance sensitive.
1603 : 423 : DomainNode* wildnode = NULL;
1604 [ + + ]: 423 : if ((result.flags & ZoneData::FindNodeResult::FIND_WILDCARD) != 0) {
1605 : : // Wildcard and glue shouldn't coexist. Make it sure here.
1606 [ - + ]: 26 : assert(!node->getFlag(domain_flag::GLUE));
1607 : :
1608 [ + - ][ + - ]: 26 : if (zone_data->getAuxWildDomains().insert(name, &wildnode)
[ + - ]
1609 : : == DomainTree::SUCCESS) {
1610 : : // If we first insert the node, copy the RRsets. If the
1611 : : // original node was empty, we add empty data so
1612 : : // addWildAdditional() can get an exactmatch for this name.
1613 [ + - ][ + - ]: 26 : DomainPtr dst_domain(new Domain);
1614 [ + + ]: 26 : if (!node->isEmpty()) {
1615 : 13 : for_each(node->getData()->begin(), node->getData()->end(),
1616 : : boost::bind(convertAndInsert, _1, dst_domain,
1617 [ + - + - ]: 26 : &name));
1618 : : }
1619 : 26 : wildnode->setData(dst_domain);
1620 : : // Mark the node as "wildcard expanded" so it can be
1621 : : // distinguished at lookup time.
1622 [ + - ]: 26 : wildnode->setFlag(domain_flag::WILD_EXPANDED);
1623 : : }
1624 : 26 : match_wild = true;
1625 : 26 : node = wildnode;
1626 : : }
1627 : :
1628 : : // If this name wasn't subject to wildcard substitution, we can add
1629 : : // the additional information to the RRset now; otherwise I'll defer
1630 : : // it until the entire auxiliary tree is built (pointers may be
1631 : : // invalidated as we build it).
1632 [ + + ]: 423 : if (wildnode == NULL) {
1633 : : // Note that node may be empty. We should keep it in the list
1634 : : // in case we dynamically update the tree and it becomes non empty
1635 : : // (which is not supported yet)
1636 [ + - ]: 397 : rrset->addAdditionalNode(AdditionalNodeInfo(node));
1637 : : }
1638 : : }
1639 : :
1640 [ + + ]: 240 : if (match_wild) {
1641 [ + - ]: 13 : wild_rrsets->push_back(rrset);
1642 : : }
1643 : 240 : }
1644 : :
1645 : : void
1646 : 13 : addWildAdditional(RBNodeRRset* rrset, ZoneData* zone_data) {
1647 : : // Similar to addAdditional(), but due to the first stage we know that
1648 : : // the rrset should contain a name stored in the auxiliary trees, and
1649 : : // that it should be found as an exact match. The RRset may have other
1650 : : // names that didn't require wildcard expansion, but we can simply ignore
1651 : : // them in this context. (Note that if we find an exact match in the
1652 : : // auxiliary tree, it shouldn't be in the original zone; otherwise it
1653 : : // shouldn't have resulted in wildcard in the first place).
1654 : :
1655 : 13 : RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
1656 [ + - ][ + - ]: 52 : for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
[ + + ]
1657 [ + - ]: 39 : const Name& name = getAdditionalName(rrset->getType(),
1658 [ + - ][ + - ]: 117 : rdata_iterator->getCurrent());
1659 : 39 : DomainNode* wildnode = NULL;
1660 [ + - ][ + - ]: 39 : if (zone_data->getAuxWildDomains().find(name, &wildnode) ==
[ + + ]
1661 : : DomainTree::EXACTMATCH) {
1662 [ + - ]: 26 : rrset->addAdditionalNode(AdditionalNodeInfo(wildnode));
1663 : : }
1664 : : }
1665 : 13 : }
1666 : : }
1667 : :
1668 : : void
1669 : 63 : InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
1670 : : const string& filename,
1671 : : boost::function<void(LoadCallback)> rrset_installer)
1672 : : {
1673 : 63 : vector<RBNodeRRset*> need_additionals;
1674 [ + - ]: 126 : scoped_ptr<ZoneData> tmp(new ZoneData(origin_));
[ + - + - ]
1675 : :
1676 : : rrset_installer(boost::bind(&InMemoryZoneFinderImpl::addFromLoad, this,
1677 [ + - ]: 126 : _1, tmp.get(), &need_additionals));
1678 : :
1679 : 55 : vector<RBNodeRRset*> wild_additionals;
1680 : : for_each(need_additionals.begin(), need_additionals.end(),
1681 : 55 : boost::bind(addAdditional, _1, tmp.get(), &wild_additionals));
1682 : : for_each(wild_additionals.begin(), wild_additionals.end(),
1683 : 55 : boost::bind(addWildAdditional, _1, tmp.get()));
1684 : :
1685 : : // If the zone is NSEC3-signed, check if it has NSEC3PARAM
1686 [ + + ]: 110 : if (tmp->nsec3_data_) {
1687 : : // Note: origin_data_ is set on creation of ZoneData, and the load
1688 : : // process only adds new nodes (and their data), so this assertion
1689 : : // should hold.
1690 [ + - ][ - + ]: 16 : assert(tmp->origin_data_ != NULL && !tmp->origin_data_->isEmpty());
1691 [ + + ]: 32 : if (tmp->origin_data_->getData()->find(RRType::NSEC3PARAM()) ==
1692 : 32 : tmp->origin_data_->getData()->end()) {
1693 [ + - ][ + - ]: 2 : LOG_WARN(logger, DATASRC_MEM_NO_NSEC3PARAM).
[ + - ]
1694 [ + - ][ + - ]: 1 : arg(origin_).arg(zone_class_);
[ + - ]
1695 : : }
1696 : : }
1697 : :
1698 : : // If it went well, put it inside
1699 : 55 : file_name_ = filename;
1700 : 55 : tmp.swap(zone_data_);
1701 : : // And let the old data die with tmp
1702 : 55 : }
1703 : :
1704 : : namespace {
1705 : : // A wrapper for dns::masterLoad used by load() below. Essentially it
1706 : : // converts the two callback types. Note the mostly redundant wrapper of
1707 : : // boost::bind. It converts function<void(ConstRRsetPtr)> to
1708 : : // function<void(RRsetPtr)> (masterLoad() expects the latter). SunStudio
1709 : : // doesn't seem to do this conversion if we just pass 'callback'.
1710 : : void
1711 : 58 : masterLoadWrapper(const char* const filename, const Name& origin,
1712 : : const RRClass& zone_class, LoadCallback callback)
1713 : : {
1714 [ + + ]: 174 : masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
1715 : 51 : }
1716 : :
1717 : : // The installer called from Impl::load() for the iterator version of load().
1718 : : void
1719 : 5 : generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
1720 : : ConstRRsetPtr rrset;
1721 [ + - ]: 5 : vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
1722 : :
1723 : : // The current internal implementation assumes an RRSIG is always added
1724 : : // after the RRset they cover. So we store any RRSIGs in 'rrsigs' until
1725 : : // it's safe to add them; based on our assumption if the owner name
1726 : : // changes, all covered RRsets of the previous name should have been
1727 : : // installed and any pending RRSIGs can be added at that point. RRSIGs
1728 : : // of the last name from the iterator must be added separately.
1729 [ + - ][ + - ]: 96 : while ((rrset = iterator->getNextRRset()) != NULL) {
[ + + ]
1730 [ + + ][ + - ]: 50 : if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
[ + - ][ + + ]
[ + + ]
1731 [ + + ][ + - ]: 19 : BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
[ + - ][ + + ]
[ + + ]
1732 [ + - ]: 4 : callback(sig_rrset);
1733 : : }
1734 : : rrsigs.clear();
1735 : : }
1736 [ + - ][ + + ]: 44 : if (rrset->getType() == RRType::RRSIG()) {
1737 [ + - ]: 5 : rrsigs.push_back(rrset);
1738 : : } else {
1739 [ + + ]: 39 : callback(rrset);
1740 : : }
1741 : : }
1742 : :
1743 [ + + ][ + - ]: 8 : BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
[ + - ][ + + ]
[ + + ]
1744 [ + - ]: 1 : callback(sig_rrset);
1745 : : }
1746 : 4 : }
1747 : : }
1748 : :
1749 : : void
1750 : 58 : InMemoryZoneFinder::load(const string& filename) {
1751 [ + - ][ + - ]: 116 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
[ + - ][ + - ]
1752 [ + - ]: 58 : arg(filename);
1753 : :
1754 : : impl_->load(filename,
1755 : 58 : boost::bind(masterLoadWrapper, filename.c_str(), getOrigin(),
1756 [ + - + + ]: 116 : getClass(), _1));
[ + - ]
1757 : 51 : }
1758 : :
1759 : : void
1760 : 5 : InMemoryZoneFinder::load(ZoneIterator& iterator) {
1761 : 5 : impl_->load(string(),
1762 [ + + ]: 10 : boost::bind(generateRRsetFromIterator, &iterator, _1));
1763 : 4 : }
1764 : :
1765 : : void
1766 : 4 : InMemoryZoneFinder::swap(InMemoryZoneFinder& zone_finder) {
1767 [ + - + - ]: 12 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()).
[ + - ][ + - ]
[ + - ]
1768 [ + - ]: 12 : arg(zone_finder.getOrigin());
1769 : 4 : std::swap(impl_, zone_finder.impl_);
1770 : 4 : }
1771 : :
1772 : : const string
1773 : 10 : InMemoryZoneFinder::getFileName() const {
1774 : 10 : return (impl_->file_name_);
1775 : : }
1776 : :
1777 : : isc::dns::Name
1778 : 1 : InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
1779 [ + - ][ + - ]: 2 : isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
1780 : : "yet, can't find previous name");
1781 : : }
1782 : :
1783 : : /// Implementation details for \c InMemoryClient hidden from the public
1784 : : /// interface.
1785 : : ///
1786 : : /// For now, \c InMemoryClient only contains a \c ZoneTable object, which
1787 : : /// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
1788 : : /// member variables later for new features.
1789 [ + - ]: 158 : class InMemoryClient::InMemoryClientImpl {
1790 : : public:
1791 [ + - ]: 158 : InMemoryClientImpl() : zone_count(0) {}
1792 : : unsigned int zone_count;
1793 : : ZoneTable zone_table;
1794 : : };
1795 : :
1796 [ + - ]: 158 : InMemoryClient::InMemoryClient() : impl_(new InMemoryClientImpl)
1797 : 158 : {}
1798 : :
1799 : 213 : InMemoryClient::~InMemoryClient() {
1800 [ + - ]: 316 : delete impl_;
1801 : 213 : }
1802 : :
1803 : : unsigned int
1804 : 19 : InMemoryClient::getZoneCount() const {
1805 : 19 : return (impl_->zone_count);
1806 : : }
1807 : :
1808 : : result::Result
1809 : 161 : InMemoryClient::addZone(ZoneFinderPtr zone_finder) {
1810 [ + + ]: 161 : if (!zone_finder) {
1811 [ + - ]: 2 : isc_throw(InvalidParameter,
1812 : : "Null pointer is passed to InMemoryClient::addZone()");
1813 : : }
1814 : :
1815 [ + - ][ + - ]: 320 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
1816 [ + - ][ + - ]: 480 : arg(zone_finder->getOrigin()).arg(zone_finder->getClass().toText());
[ + - ][ + - ]
1817 : :
1818 [ + - ]: 160 : const result::Result result = impl_->zone_table.addZone(zone_finder);
1819 [ + + ]: 160 : if (result == result::SUCCESS) {
1820 : 156 : ++impl_->zone_count;
1821 : : }
1822 : 160 : return (result);
1823 : : }
1824 : :
1825 : : InMemoryClient::FindResult
1826 : 176 : InMemoryClient::findZone(const isc::dns::Name& name) const {
1827 [ + - ][ + - ]: 176 : LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name);
1828 : 176 : ZoneTable::FindResult result(impl_->zone_table.findZone(name));
1829 : 176 : return (FindResult(result.code, result.zone));
1830 : : }
1831 : :
1832 : : namespace {
1833 : :
1834 : 8 : class MemoryIterator : public ZoneIterator {
1835 : : private:
1836 : : RBTreeNodeChain<Domain> chain_;
1837 : : Domain::const_iterator dom_iterator_;
1838 : : const DomainTree& tree_;
1839 : : const DomainNode* node_;
1840 : : // Only used when separate_rrs_ is true
1841 : : RdataIteratorPtr rdata_iterator_;
1842 : : bool separate_rrs_;
1843 : : bool ready_;
1844 : : public:
1845 : : MemoryIterator(const DomainTree& tree, const Name& origin, bool separate_rrs) :
1846 : : tree_(tree),
1847 : : separate_rrs_(separate_rrs),
1848 : 20 : ready_(true)
1849 : : {
1850 : : // Find the first node (origin) and preserve the node chain for future
1851 : : // searches
1852 : 8 : DomainTree::Result result(tree_.find(origin, &node_, chain_));
1853 : : // It can't happen that the origin is not in there
1854 [ - + ]: 4 : if (result != DomainTree::EXACTMATCH) {
1855 [ # # ][ # # ]: 0 : isc_throw(Unexpected,
1856 : : "In-memory zone corrupted, missing origin node");
1857 : : }
1858 : : // Initialize the iterator if there's somewhere to point to
1859 [ + - ][ - + ]: 8 : if (node_ != NULL && node_->getData() != DomainPtr()) {
[ + - ][ + - ]
1860 : 4 : dom_iterator_ = node_->getData()->begin();
1861 [ + + ][ + + ]: 4 : if (separate_rrs_ && dom_iterator_ != node_->getData()->end()) {
[ + + ]
1862 [ + - ][ + - ]: 1 : rdata_iterator_ = dom_iterator_->second->getRdataIterator();
1863 : : }
1864 : : }
1865 : : }
1866 : :
1867 : 13 : virtual ConstRRsetPtr getNextRRset() {
1868 [ + + ]: 13 : if (!ready_) {
1869 [ + - ]: 4 : isc_throw(Unexpected, "Iterating past the zone end");
1870 : : }
1871 : : /*
1872 : : * This cycle finds the first nonempty node with yet unused RRset.
1873 : : * If it is NULL, we run out of nodes. If it is empty, it doesn't
1874 : : * contain any RRsets. If we are at the end, just get to next one.
1875 : : */
1876 [ + + ]: 43 : while (node_ != NULL && (node_->getData() == DomainPtr() ||
[ + - + + ]
[ + + ][ + + ]
1877 : 13 : dom_iterator_ == node_->getData()->end())) {
1878 : 6 : node_ = tree_.nextNode(chain_);
1879 : : // If there's a node, initialize the iterator and check next time
1880 : : // if the map is empty or not
1881 [ + + ][ - + ]: 8 : if (node_ != NULL && node_->getData() != NULL) {
[ + + ]
1882 : 2 : dom_iterator_ = node_->getData()->begin();
1883 : : // New RRset, so get a new rdata iterator
1884 [ + + ]: 2 : if (separate_rrs_) {
1885 [ + - ]: 1 : rdata_iterator_ = dom_iterator_->second->getRdataIterator();
1886 : : }
1887 : : }
1888 : : }
1889 [ + + ]: 11 : if (node_ == NULL) {
1890 : : // That's all, folks
1891 : 4 : ready_ = false;
1892 : : return (ConstRRsetPtr());
1893 : : }
1894 : :
1895 [ + + ]: 7 : if (separate_rrs_) {
1896 : : // For separate rrs, reconstruct a new RRset with just the
1897 : : // 'current' rdata
1898 : 4 : RRsetPtr result(new RRset(dom_iterator_->second->getName(),
1899 : 4 : dom_iterator_->second->getClass(),
1900 : 4 : dom_iterator_->second->getType(),
1901 [ + - ]: 12 : dom_iterator_->second->getTTL()));
1902 [ + - ][ + - ]: 4 : result->addRdata(rdata_iterator_->getCurrent());
1903 [ + - ]: 4 : rdata_iterator_->next();
1904 [ + - ][ + + ]: 4 : if (rdata_iterator_->isLast()) {
1905 : : // all used up, next.
1906 : 3 : ++dom_iterator_;
1907 : : // New RRset, so get a new rdata iterator, but only if this
1908 : : // was not the final RRset in the chain
1909 [ + + ]: 3 : if (dom_iterator_ != node_->getData()->end()) {
1910 [ + - ][ + - ]: 1 : rdata_iterator_ = dom_iterator_->second->getRdataIterator();
1911 : : }
1912 : : }
1913 : : return (result);
1914 : : } else {
1915 : : // The iterator points to the next yet unused RRset now
1916 : 6 : ConstRRsetPtr result(dom_iterator_->second);
1917 : :
1918 : : // This one is used, move it to the next time for next call
1919 : 3 : ++dom_iterator_;
1920 : :
1921 : : return (result);
1922 : : }
1923 : : }
1924 : :
1925 : 0 : virtual ConstRRsetPtr getSOA() const {
1926 [ # # ][ # # ]: 0 : isc_throw(NotImplemented, "Not imelemented");
1927 : : }
1928 : : };
1929 : :
1930 : : } // End of anonymous namespace
1931 : :
1932 : : ZoneIteratorPtr
1933 : 9 : InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
1934 : 9 : ZoneTable::FindResult result(impl_->zone_table.findZone(name));
1935 [ + + ]: 9 : if (result.code != result::SUCCESS) {
1936 [ + - ][ + - ]: 10 : isc_throw(DataSourceError, "No such zone: " + name.toText());
[ + - ][ + - ]
1937 : : }
1938 : :
1939 : : const InMemoryZoneFinder*
1940 [ + - ]: 4 : zone(dynamic_cast<const InMemoryZoneFinder*>(result.zone.get()));
1941 [ - + ]: 4 : if (zone == NULL) {
1942 : : /*
1943 : : * TODO: This can happen only during some of the tests and only as
1944 : : * a temporary solution. This should be fixed by #1159 and then
1945 : : * this cast and check shouldn't be necessary. We don't have
1946 : : * test for handling a "can not happen" condition.
1947 : : */
1948 [ # # ][ # # ]: 0 : isc_throw(Unexpected, "The zone at " + name.toText() +
[ # # ][ # # ]
1949 : : " is not InMemoryZoneFinder");
1950 : : }
1951 : : return (ZoneIteratorPtr(new MemoryIterator(
1952 : 4 : zone->impl_->zone_data_->domains_, name,
1953 [ + - ]: 8 : separate_rrs)));
1954 : : }
1955 : :
1956 : : ZoneUpdaterPtr
1957 : 2 : InMemoryClient::getUpdater(const isc::dns::Name&, bool, bool) const {
1958 [ + - ][ + - ]: 4 : isc_throw(isc::NotImplemented, "Update attempt on in memory data source");
1959 : : }
1960 : :
1961 : : pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
1962 : 0 : InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
1963 : : uint32_t) const
1964 : : {
1965 [ # # ][ # # ]: 0 : isc_throw(isc::NotImplemented, "Journaling isn't supported for "
1966 : : "in memory data source");
1967 : : }
1968 : :
1969 : : } // end of namespace datasrc
1970 : 1391 : } // end of namespace isc
|