zone_data_updater.cc 16.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
#include <exceptions/exceptions.h>

17
#include <datasrc/memory/zone_data_updater.h>
18
#include <datasrc/memory/logger.h>
19
#include <datasrc/memory/util_internal.h>
20
21
22
#include <datasrc/zone.h>

#include <dns/rdataclass.h>
23

24
#include <cassert>
25
#include <string>
26

27
28
29
30
31
32
33
using namespace isc::dns;
using namespace isc::dns::rdata;

namespace isc {
namespace datasrc {
namespace memory {

34
35
using detail::getCoveredType;

36
37
38
39
40
41
42
43
44
45
void
ZoneDataUpdater::addWildcards(const Name& name) {
    Name wname(name);
    const unsigned int labels(wname.getLabelCount());
    const unsigned int origin_labels(zone_name_.getLabelCount());
    for (unsigned int l = labels;
         l > origin_labels;
         --l, wname = wname.split(1))
    {
        if (wname.isWildcard()) {
46
47
48
            LOG_DEBUG(logger, DBG_TRACE_DATA,
                      DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);

49
50
51
            // Ensure a separate level exists for the "wildcarding"
            // name, and mark the node as "wild".
            ZoneNode* node;
52
            zone_data_->insertName(mem_sgmt_, wname.split(1), &node);
53
54
55
56
57
58
            node->setFlag(ZoneData::WILDCARD_NODE);

            // Ensure a separate level exists for the wildcard name.
            // Note: for 'name' itself we do this later anyway, but the
            // overhead should be marginal because wildcard names should
            // be rare.
59
            zone_data_->insertName(mem_sgmt_, wname, &node);
60
61
62
63
64
65
        }
    }
}

void
ZoneDataUpdater::contextCheck(const AbstractRRset& rrset,
66
                              const RdataSet* rdataset) const
67
{
68
69
70
71
    // Ensure CNAME and other type of RR don't coexist for the same
    // owner name except with NSEC, which is the only RR that can
    // coexist with CNAME (and also RRSIG, which is handled separately)
    if (rrset.getType() == RRType::CNAME()) {
72
        for (const RdataSet* sp = rdataset; sp != NULL; sp = sp->getNext()) {
73
            if (sp->type != RRType::NSEC()) {
74
75
                LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
                    arg(rrset.getName());
76
77
78
79
80
81
                isc_throw(AddError,
                          "CNAME can't be added with " << sp->type
                          << " RRType for " << rrset.getName());
            }
        }
    } else if ((rrset.getType() != RRType::NSEC()) &&
82
               (RdataSet::find(rdataset, RRType::CNAME()) != NULL))
83
    {
84
85
        LOG_ERROR(logger,
                  DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
86
87
88
89
90
91
92
93
94
95
96
        isc_throw(AddError,
                  "CNAME and " << rrset.getType() <<
                  " can't coexist for " << rrset.getName());
    }

    // Similar with DNAME, but it must not coexist only with NS and only
    // in non-apex domains.  RFC 2672 section 3 mentions that it is
    // implied from it and RFC 2181.
    if (rrset.getName() != zone_name_ &&
        // Adding DNAME, NS already there
        ((rrset.getType() == RRType::DNAME() &&
97
          RdataSet::find(rdataset, RRType::NS()) != NULL) ||
98
99
         // Adding NS, DNAME already there
         (rrset.getType() == RRType::NS() &&
100
          RdataSet::find(rdataset, RRType::DNAME()) != NULL)))
101
    {
102
        LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
103
104
105
106
107
108
109
        isc_throw(AddError, "DNAME can't coexist with NS in non-apex domain: "
                  << rrset.getName());
    }
}

void
ZoneDataUpdater::validate(const isc::dns::ConstRRsetPtr rrset) const {
110
    assert(rrset);
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

    if (rrset->getRdataCount() == 0) {
        isc_throw(AddError,
                  "The rrset provided is empty: "
                  << rrset->getName() << "/" << rrset->getType());
    }

    // Check for singleton RRs. It should probably handled at a different
    // layer in future.
    if ((rrset->getType() == RRType::CNAME() ||
         rrset->getType() == RRType::DNAME()) &&
        rrset->getRdataCount() > 1)
    {
        // XXX: this is not only for CNAME or DNAME. We should
        // generalize this code for all other "singleton RR types" (such
        // as SOA) in a separate task.
127
128
129
        LOG_ERROR(logger,
                  DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
            arg(rrset->getType());
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
        isc_throw(AddError, "multiple RRs of singleton type for "
                  << rrset->getName());
    }

    // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
    // implementation requests it be so at the moment.
    if ((rrset->getType() == RRType::NSEC3() ||
         rrset->getType() == RRType::NSEC3PARAM()) &&
        (rrset->getRdataCount() > 1))
    {
        isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
                  << rrset->getName() << " which isn't supported");
    }

    // For RRSIGs, check consistency of the type covered.  We know the
    // RRset isn't empty, so the following check is safe.
    if (rrset->getType() == RRType::RRSIG()) {
        RdataIteratorPtr rit = rrset->getRdataIterator();
        const RRType covered = dynamic_cast<const generic::RRSIG&>(
            rit->getCurrent()).typeCovered();
        for (rit->next(); !rit->isLast(); rit->next()) {
            if (dynamic_cast<const generic::RRSIG&>(
                     rit->getCurrent()).typeCovered() != covered)
            {
                isc_throw(AddError, "RRSIG contains mixed covered types: "
                          << rrset->toText());
            }
        }
    }

    const NameComparisonResult compare = zone_name_.compare(rrset->getName());
    if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
        compare.getRelation() != NameComparisonResult::EQUAL)
    {
164
165
166
        LOG_ERROR(logger,
                  DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
            arg(zone_name_);
167
        isc_throw(AddError,
168
169
170
171
172
173
174
175
176
177
178
179
180
                  "The name " << rrset->getName() <<
                  " is not contained in zone " << zone_name_);
    }

    // Some RR types do not really work well with a wildcard.  Even
    // though the protocol specifically doesn't completely ban such
    // usage, we refuse to load a zone containing such RR in order to
    // keep the lookup logic simpler and more predictable.  See RFC4592
    // and (for DNAME) RFC6672 for more technical background.  Note also
    // that BIND 9 refuses NS at a wildcard, so in that sense we simply
    // provide compatible behavior.
    if (rrset->getName().isWildcard()) {
        if (rrset->getType() == RRType::NS()) {
181
182
            LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
                arg(rrset->getName());
183
184
185
186
187
            isc_throw(AddError, "Invalid NS owner name (wildcard): "
                      << rrset->getName());
        }

        if (rrset->getType() == RRType::DNAME()) {
188
189
            LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
                arg(rrset->getName());
190
191
192
193
194
195
196
197
198
199
200
201
            isc_throw(AddError, "Invalid DNAME owner name (wildcard): "
                      << rrset->getName());
        }
    }

    // Owner names of NSEC3 have special format as defined in RFC5155,
    // and cannot be a wildcard name or must be one label longer than
    // the zone origin.  While the RFC doesn't prohibit other forms of
    // names, no sane zone would have such names for NSEC3.  BIND 9 also
    // refuses NSEC3 at wildcard.
    if (rrset->getType() == RRType::NSEC3() &&
        (rrset->getName().isWildcard() ||
202
         rrset->getName().getLabelCount() != zone_name_.getLabelCount() + 1))
203
    {
204
        LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).arg(rrset->getName());
205
206
207
208
209
        isc_throw(AddError, "Invalid NSEC3 owner name: " <<
                  rrset->getName() << "; zone: " << zone_name_);
    }
}

210
211
212
const NSEC3Hash*
ZoneDataUpdater::getNSEC3Hash() {
    if (hash_ == NULL) {
213
        NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
214
215
216
217
218
219
220
221
222
223
224
225
        // This should never be NULL in this codepath.
        assert(nsec3_data != NULL);

        hash_ = NSEC3Hash::create(nsec3_data->hashalg,
                                  nsec3_data->iterations,
                                  nsec3_data->getSaltData(),
                                  nsec3_data->getSaltLen());
    }

    return (hash_);
}

226
template <typename T>
227
void
228
ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
229
    // We know rrset has exactly one RDATA
230
231
    const T& nsec3_rdata =
        dynamic_cast<const T&>(
232
233
            rrset->getRdataIterator()->getCurrent());

234
    NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
235
    if (nsec3_data == NULL) {
236
        nsec3_data = NSEC3Data::create(mem_sgmt_, zone_name_, nsec3_rdata);
237
238
        zone_data_->setNSEC3Data(nsec3_data);
        zone_data_->setSigned(true);
239
    } else {
240
        const NSEC3Hash* hash = getNSEC3Hash();
241
        if (!hash->match(nsec3_rdata)) {
242
            isc_throw(AddError,
243
244
                      rrset->getType() << " with inconsistent parameters: "
                      << rrset->toText());
245
246
        }
    }
247
248
249
}

void
250
251
ZoneDataUpdater::addNSEC3(const Name& name, const ConstRRsetPtr& rrset,
                          const ConstRRsetPtr& rrsig)
252
{
253
254
255
    if (rrset) {
        setupNSEC3<generic::NSEC3>(rrset);
    }
256

257
    NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
258
259
260
261
262
263
264
265
266
267
268
    if (nsec3_data == NULL) {
        // This is some tricky case: an RRSIG for NSEC3 is given without the
        // covered NSEC3, and we don't even know any NSEC3 related data.
        // This situation is not necessarily broken, but in our current
        // implementation it's very difficult to deal with.  So we reject it;
        // hopefully this case shouldn't happen in practice, at least unless
        // zone is really broken.
        assert(!rrset);
        isc_throw(NotImplemented,
                  "RRSIG for NSEC3 cannot be added - no known NSEC3 data");
    }
269
270

    ZoneNode* node;
271
    nsec3_data->insertName(mem_sgmt_, name, &node);
272

273
274
    // Create a new RdataSet, merging any existing NSEC3 data for this
    // name.
275
276
277
278
    RdataSet* old_rdataset = node->getData();
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, rrset, rrsig,
                                          old_rdataset);
    old_rdataset = node->setData(rdataset);
279
    if (old_rdataset != NULL) {
280
        RdataSet::destroy(mem_sgmt_, old_rdataset, rrclass_);
281
282
283
284
    }
}

void
285
ZoneDataUpdater::addRdataSet(const Name& name, const RRType& rrtype,
286
287
                             const ConstRRsetPtr& rrset,
                             const ConstRRsetPtr& rrsig)
288
{
289
    if (rrtype == RRType::NSEC3()) {
290
        addNSEC3(name, rrset, rrsig);
291
292
    } else {
        ZoneNode* node;
293
        zone_data_->insertName(mem_sgmt_, name, &node);
294
295
296
297
298
299
300

        RdataSet* rdataset_head = node->getData();

        // Checks related to the surrounding data.  Note: when the check
        // fails and the exception is thrown, it may break strong
        // exception guarantee.  At the moment we prefer code simplicity
        // and don't bother to introduce complicated recovery code.
301
302
303
        if (rrset) { // this check is only for covered RRset, not RRSIG
            contextCheck(*rrset, rdataset_head);
        }
304

305
306
307
        // Create a new RdataSet, merging any existing data for this
        // type.
        RdataSet* old_rdataset = RdataSet::find(rdataset_head, rrtype, true);
308
        RdataSet* rdataset_new = RdataSet::create(mem_sgmt_, encoder_,
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
                                                  rrset, rrsig, old_rdataset);
        if (old_rdataset == NULL) {
            // There is no existing RdataSet. Prepend the new RdataSet
            // to the list.
            rdataset_new->next = rdataset_head;
            node->setData(rdataset_new);
        } else {
            // Replace the old RdataSet in the list with the newly
            // created one, and destroy the old one.
            for (RdataSet* cur = rdataset_head, *prev = NULL;
                 cur != NULL;
                 prev = cur, cur = cur->getNext()) {
                if (cur == old_rdataset) {
                    rdataset_new->next = cur->getNext();
                    if (prev == NULL) {
                        node->setData(rdataset_new);
                    } else {
                        prev->next = rdataset_new;
                    }
                    break;
                }
            }
            RdataSet::destroy(mem_sgmt_, old_rdataset, rrclass_);
        }
333

334
        // Ok, we just put it in.
335

336
        // Convenient (and more efficient) shortcut to check RRsets at origin
337
        const bool is_origin = (node == zone_data_->getOriginNode());
338
339

        // If this RRset creates a zone cut at this node, mark the node
340
341
        // indicating the need for callback in find().  Note that we do this
        // only when non RRSIG RRset of that type is added.
342
        if (rrset && rrtype == RRType::NS() && !is_origin) {
343
344
            node->setFlag(ZoneNode::FLAG_CALLBACK);
            // If it is DNAME, we have a callback as well here
345
        } else if (rrset && rrtype == RRType::DNAME()) {
346
347
348
349
350
351
            node->setFlag(ZoneNode::FLAG_CALLBACK);
        }

        // If we've added NSEC3PARAM at zone origin, set up NSEC3
        // specific data or check consistency with already set up
        // parameters.
352
        if (rrset && rrtype == RRType::NSEC3PARAM() && is_origin) {
353
            setupNSEC3<generic::NSEC3PARAM>(rrset);
354
        } else if (rrset && rrtype == RRType::NSEC() && is_origin) {
355
356
357
358
            // If it is NSEC signed zone, we mark the zone as signed
            // (conceptually "signed" is a broader notion but our
            // current zone finder implementation regards "signed" as
            // "NSEC signed")
359
            zone_data_->setSigned(true);
360
        }
361
362
363
364
365
366
367

        // If we are adding a new SOA at the origin, update zone's min TTL.
        // Note: if the input is broken and contains multiple SOAs, the load
        // or update will be rejected higher level.  We just always (though
        // this should be only once in normal cases) update the TTL.
        if (rrset && rrtype == RRType::SOA() && is_origin) {
            // Our own validation ensures the RRset is not empty.
368
            zone_data_->setMinTTL(
369
370
371
                dynamic_cast<const generic::SOA&>(
                    rrset->getRdataIterator()->getCurrent()).getMinimum());
        }
372
373
374
    }
}

375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
void
ZoneDataUpdater::addInternal(const isc::dns::Name& name,
                     const isc::dns::RRType& rrtype,
                     const isc::dns::ConstRRsetPtr& rrset,
                     const isc::dns::ConstRRsetPtr& rrsig)
{
    // Add wildcards possibly contained in the owner name to the domain
    // tree.  This can only happen for the normal (non-NSEC3) tree.
    // Note: this can throw an exception, breaking strong exception
    // guarantee.  (see also the note for the call to contextCheck()
    // above).
    if (rrtype != RRType::NSEC3()) {
        addWildcards(name);
    }

    addRdataSet(name, rrtype, rrset, rrsig);
}

393
394
void
ZoneDataUpdater::add(const ConstRRsetPtr& rrset,
395
396
                     const ConstRRsetPtr& sig_rrset)
{
397
398
399
400
401
402
403
404
    // Validate input.
    if (!rrset && !sig_rrset) {
        isc_throw(NullRRset,
                  "ZoneDataUpdater::add is given 2 NULL pointers");
    }
    if (rrset) {
        validate(rrset);
    }
405
406
407
408
    if (sig_rrset) {
        validate(sig_rrset);
    }

409
410
411
412
    const Name& name = rrset ? rrset->getName() : sig_rrset->getName();
    const RRType& rrtype = rrset ? rrset->getType() :
        getCoveredType(sig_rrset);

413
    // OK, can add the RRset.
414
415
416
    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).arg(name).
        arg(rrset ? rrtype.toText() : "RRSIG(" + rrtype.toText() + ")").
        arg(zone_name_);
417

418
419
420
421
422
423
424
425
426
427
428
429
    // Store the address, it may change during growth and the address inside
    // would get updated.
    bool added = false;
    do {
        try {
            addInternal(name, rrtype, rrset, sig_rrset);
            added = true;
        } catch (const isc::util::MemorySegmentGrown&) {
            // The segment has grown. So, we update the base pointer (because
            // the data may have been remapped somewhere else in the process).
            zone_data_ =
                static_cast<ZoneData*>(
430
                    mem_sgmt_.getNamedAddress("updater_zone_data").second);
431
432
433
        }
        // Retry if it didn't add due to the growth
    } while (!added);
434
435
436
437
438
}

} // namespace memory
} // namespace datasrc
} // namespace isc