query_unittest.cc 72.7 KB
Newer Older
Xie Jiagui's avatar
Xie Jiagui committed
1
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// 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
#include <sstream>
16
17
#include <vector>
#include <map>
18

19
#include <boost/bind.hpp>
20
#include <boost/scoped_ptr.hpp>
21

22
23
#include <exceptions/exceptions.h>

24
#include <dns/masterload.h>
25
26
#include <dns/message.h>
#include <dns/name.h>
27
#include <dns/opcode.h>
28
#include <dns/rcode.h>
29
#include <dns/rrttl.h>
30
#include <dns/rrtype.h>
31
#include <dns/rdataclass.h>
32

Michal Vaner's avatar
Michal Vaner committed
33
#include <datasrc/memory_datasrc.h>
34
35
36

#include <auth/query.h>

37
#include <testutils/dnsmessage_test.h>
38

39
40
#include <gtest/gtest.h>

41
using namespace std;
42
using namespace isc::dns;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
43
using namespace isc::dns::rdata;
44
45
using namespace isc::datasrc;
using namespace isc::auth;
46
using namespace isc::testutils;
47

48
49
namespace {

50
51
52
53
54
55
56
57
58
59
// This is the content of the mock zone (see below).
// It's a sequence of textual RRs that is supposed to be parsed by
// dns::masterLoad().  Some of the RRs are also used as the expected
// data in specific tests, in which case they are referenced via specific
// local variables (such as soa_txt).
const char* const soa_txt = "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
const char* const zone_ns_txt =
    "example.com. 3600 IN NS glue.delegation.example.com.\n"
    "example.com. 3600 IN NS noglue.example.com.\n"
    "example.com. 3600 IN NS example.net.\n";
60
// TBD: CHECK IF IT IS REALLY USED
61
62
63
const char* const zone_ds_txt =
    "example.com. 3600 IN DS 57855 5 1 "
        "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
64
65
66
67
68
const char* const ns_addrs_txt =
    "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
    "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n"
    "noglue.example.com. 3600 IN A 192.0.2.53\n";
const char* const delegation_txt =
chenzhengzhang's avatar
chenzhengzhang committed
69
70
71
72
    "delegation.example.com. 3600 IN NS glue.delegation.example.com.\n"
    "delegation.example.com. 3600 IN NS noglue.example.com.\n"
    "delegation.example.com. 3600 IN NS cname.example.com.\n"
    "delegation.example.com. 3600 IN NS example.org.\n";
73
74
75
76
// Borrowed from the RFC4035
const char* const delegation_ds_txt =
    "delegation.example.com. 3600 IN DS 57855 5 1 "
        "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
77
78
79
80
81
const char* const mx_txt =
    "mx.example.com. 3600 IN MX 10 www.example.com.\n"
    "mx.example.com. 3600 IN MX 20 mailer.example.org.\n"
    "mx.example.com. 3600 IN MX 30 mx.delegation.example.com.\n";
const char* const www_a_txt = "www.example.com. 3600 IN A 192.0.2.80\n";
82
83
const char* const cname_txt =
    "cname.example.com. 3600 IN CNAME www.example.com.\n";
84
85
86
87
88
const char* const cname_nxdom_txt =
    "cnamenxdom.example.com. 3600 IN CNAME nxdomain.example.com.\n";
// CNAME Leading out of zone
const char* const cname_out_txt =
    "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
89
// The DNAME to do tests against
90
const char* const dname_txt =
91
92
    "dname.example.com. 3600 IN DNAME "
    "somethinglong.dnametarget.example.com.\n";
93
// Some data at the dname node (allowed by RFC 2672)
94
95
const char* const dname_a_txt =
    "dname.example.com. 3600 IN A 192.0.2.5\n";
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
96
97
// This is not inside the zone, this is created at runtime
const char* const synthetized_cname_txt =
98
99
    "www.dname.example.com. 3600 IN CNAME "
    "www.somethinglong.dnametarget.example.com.\n";
100
101
102
103
104
// The rest of data won't be referenced from the test cases.
const char* const other_zone_rrs =
    "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
    "cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
    "mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
105
106
107
108
109
110
111
// Wildcards
const char* const wild_txt = "*.wild.example.com. 3600 IN A 192.0.2.7\n";
const char* const nsec_wild_txt =
    "*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG\n";
const char* const cnamewild_txt =
    "*.cnamewild.example.com. 3600 IN CNAME www.example.org.\n";
const char* const nsec_cnamewild_txt = "*.cnamewild.example.com. "
112
    "3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG\n";
113
114
// Wildcard_nxrrset
const char* const wild_txt_nxrrset =
115
    "*.uwild.example.com. 3600 IN A 192.0.2.9\n";
116
const char* const nsec_wild_txt_nxrrset =
117
    "*.uwild.example.com. 3600 IN NSEC www.uwild.example.com. A NSEC RRSIG\n";
118
const char* const wild_txt_next =
119
    "www.uwild.example.com. 3600 IN A 192.0.2.11\n";
120
const char* const nsec_wild_txt_next =
121
    "www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG\n";
122
123
// Wildcard empty
const char* const empty_txt = "b.*.t.example.com. 3600 IN A 192.0.2.13\n";
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
124
const char* const nsec_empty_txt =
125
126
127
128
    "b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG\n";
const char* const empty_prev_txt = "t.example.com. 3600 IN A 192.0.2.15\n";
const char* const nsec_empty_prev_txt =
    "t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG\n";
129
130
131
132
133
134
135
136
137
// Used in NXDOMAIN proof test.  We are going to test some unusual case where
// the best possible wildcard is below the "next domain" of the NSEC RR that
// proves the NXDOMAIN, i.e.,
// mx.example.com. (exist)
// (.no.example.com. (qname, NXDOMAIN)
// ).no.example.com. (exist)
// *.no.example.com. (best possible wildcard, not exist)
const char* const no_txt =
    ").no.example.com. 3600 IN AAAA 2001:db8::53\n";
138
139
140
// NSEC records.
const char* const nsec_apex_txt =
    "example.com. 3600 IN NSEC cname.example.com. NS SOA NSEC RRSIG\n";
141
142
143
const char* const nsec_mx_txt =
    "mx.example.com. 3600 IN NSEC ).no.example.com. MX NSEC RRSIG\n";
const char* const nsec_no_txt =
144
145
146
147
148
149
150
151
152
153
154
155
    ").no.example.com. 3600 IN NSEC nz.no.example.com. AAAA NSEC RRSIG\n";
// We'll also test the case where a single NSEC proves both NXDOMAIN and the
// non existence of wildcard.  The following records will be used for that
// test.
// ).no.example.com. (exist, whose NSEC proves everything)
// *.no.example.com. (best possible wildcard, not exist)
// nx.no.example.com. (NXDOMAIN)
// nz.no.example.com. (exist)
const char* const nz_txt =
    "nz.no.example.com. 3600 IN AAAA 2001:db8::5300\n";
const char* const nsec_nz_txt =
    "nz.no.example.com. 3600 IN NSEC noglue.example.com. AAAA NSEC RRSIG\n";
156
const char* const nsec_nxdomain_txt =
157
    "noglue.example.com. 3600 IN NSEC nonsec.example.com. A\n";
158

159
160
161
162
// NSEC for the normal NXRRSET case
const char* const nsec_www_txt =
    "www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG\n";

163
164
165
// Authoritative data without NSEC
const char* const nonsec_a_txt = "nonsec.example.com. 3600 IN A 192.0.2.0\n";

166
167
168
169
170
171
172
173
// NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
const char* const nsec3_apex_txt =
    "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG\n";
const char* const nsec3_www_txt =
    "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";

174
// (Secure) delegation data; Delegation with DS record
175
176
177
178
179
180
const char* const signed_delegation_txt =
    "signed-delegation.example.com. 3600 IN NS ns.example.net.\n";
const char* const signed_delegation_ds_txt =
    "signed-delegation.example.com. 3600 IN DS 12345 8 2 "
    "764501411DE58E8618945054A3F620B36202E115D015A7773F4B78E0F952CECA\n";

181
182
// (Secure) delegation data; Delegation without DS record (and NSEC denying
// its existence.
183
184
185
186
187
188
const char* const unsigned_delegation_txt =
    "unsigned-delegation.example.com. 3600 IN NS ns.example.net.\n";
const char* const unsigned_delegation_nsec_txt =
    "unsigned-delegation.example.com. 3600 IN NSEC "
    "*.uwild.example.com. NS RRSIG NSEC\n";

189
190
// (Secure) delegation data; Delegation where the DS lookup will raise an
// exception.
191
192
193
194
const char* const bad_delegation_txt =
    "bad-delegation.example.com. 3600 IN NS ns.example.net.\n";


195
196
197
198
// A helper function that generates a textual representation of RRSIG RDATA
// for the given covered type.  The resulting RRSIG may not necessarily make
// sense in terms of the DNSSEC protocol, but for our testing purposes it's
// okay.
199
200
201
202
203
204
string
getCommonRRSIGText(const string& type) {
    return (type +
            string(" 5 3 3600 20000101000000 20000201000000 12345 "
                   "example.com. FAKEFAKEFAKE"));
}
Michal Vaner's avatar
Michal Vaner committed
205

206
// This is a mock Zone Finder class for testing.
207
// It is a derived class of ZoneFinder for the convenient of tests.
208
// Its find() method emulates the common behavior of protocol compliant
209
// ZoneFinder classes, but simplifies some minor cases and also supports broken
210
// behavior.
211
212
// For simplicity, most names are assumed to be "in zone"; delegations
// to child zones are identified by the existence of non origin NS records.
213
214
215
216
// Another special name is "dname.example.com".  Query names under this name
// will result in DNAME.
// This mock zone doesn't handle empty non terminal nodes (if we need to test
// such cases find() should have specialized code for it).
217
class MockZoneFinder : public ZoneFinder {
218
public:
219
    MockZoneFinder() :
Michal Vaner's avatar
Michal Vaner committed
220
        origin_(Name("example.com")),
221
        bad_signed_delegation_name_("bad-delegation.example.com"),
222
        dname_name_("dname.example.com"),
223
224
        has_SOA_(true),
        has_apex_NS_(true),
225
        rrclass_(RRClass::IN()),
226
        include_rrsig_anyway_(false),
227
        use_nsec3_(false),
228
        nsec_name_(origin_)
229
    {
230
        stringstream zone_stream;
231
        zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
232
233
            delegation_txt << delegation_ds_txt << mx_txt << www_a_txt <<
            cname_txt << cname_nxdom_txt << cname_out_txt << dname_txt <<
234
            dname_a_txt << other_zone_rrs << no_txt << nz_txt <<
235
            nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
236
            nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
237
            wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt <<
238
            wild_txt_nxrrset << nsec_wild_txt_nxrrset << wild_txt_next <<
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
239
            nsec_wild_txt_next << empty_txt << nsec_empty_txt <<
240
            empty_prev_txt << nsec_empty_prev_txt <<
241
242
243
244
            nsec3_apex_txt << nsec3_www_txt <<
            signed_delegation_txt << signed_delegation_ds_txt <<
            unsigned_delegation_txt << unsigned_delegation_nsec_txt <<
            bad_delegation_txt;
245

246
        masterLoad(zone_stream, origin_, rrclass_,
247
                   boost::bind(&MockZoneFinder::loadRRset, this, _1));
248
249
250
251
252

        empty_nsec_rrset_ = ConstRRsetPtr(new RRset(Name::ROOT_NAME(),
                                                    RRClass::IN(),
                                                    RRType::NSEC(),
                                                    RRTTL(3600)));
253
254
255
256
257
258
259

        // (Faked) NSEC3 hash map.  For convenience we use hardcoded built-in
        // map instead of calculating and using actual hash.
        // The used hash values are borrowed from RFC5155 examples.
        hash_map_[Name("example.com")] = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
        hash_map_[Name("nxdomain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
260
261
262
263
        hash_map_[Name("nx.domain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
        hash_map_[Name("domain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
264
265
266
267
        hash_map_[Name("nxdomain2.example.com")] =
            "q00jkcevqvmu85r014c7dkba38o0ji5r";
        hash_map_[Name("nxdomain3.example.com")] =
            "009mhaveqvm6t7vbl5lop2u3t2rp3tom";
268
    }
269
270
    virtual isc::dns::Name getOrigin() const { return (origin_); }
    virtual isc::dns::RRClass getClass() const { return (rrclass_); }
271
272
    virtual FindResult find(const isc::dns::Name& name,
                            const isc::dns::RRType& type,
273
                            const FindOptions options = FIND_DEFAULT);
274
275
276
    virtual FindResult findAll(const isc::dns::Name& name,
                               std::vector<ConstRRsetPtr>& target,
                               const FindOptions options = FIND_DEFAULT);
277

278
    virtual ZoneFinder::FindNSEC3Result
279
    findNSEC3(const Name& name, bool recursive);
280

281
282
    // If false is passed, it makes the zone broken as if it didn't have the
    // SOA.
283
    void setSOAFlag(bool on) { has_SOA_ = on; }
284

285
286
287
    // If false is passed, it makes the zone broken as if it didn't have
    // the apex NS.
    void setApexNSFlag(bool on) { has_apex_NS_ = on; }
288

289
290
291
    // Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK
    void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; }

292
293
294
295
296
297
298
299
300
    // Once called, this "faked" result will be returned when NSEC is expected
    // for the specified query name.
    void setNSECResult(const Name& nsec_name, Result code,
                       ConstRRsetPtr rrset)
    {
        nsec_name_ = nsec_name;
        nsec_result_.reset(new ZoneFinder::FindResult(code, rrset));
    }

301
302
303
304
    // If true is passed return an empty NSEC3 RRset for some negative
    // answers when DNSSEC is required.
    void setNSEC3Flag(bool on) { use_nsec3_ = on; }

305
306
307
308
    Name findPreviousName(const Name&) const {
        isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
    }

309
310
public:
    // We allow the tests to use these for convenience
311
    ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
312
313
    ConstRRsetPtr empty_nsec_rrset_;

Jerry's avatar
Jerry committed
314
private:
315
316
317
    typedef map<RRType, ConstRRsetPtr> RRsetStore;
    typedef map<Name, RRsetStore> Domains;
    Domains domains_;
318
    Domains delegations_;
319
    Domains nsec3_domains_;
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

    // This is used to identify delegation to a child zone, and used to
    // find a matching entry in delegations_.  Note that first found entry
    // is returned, so it's not a longest match.  Test data must be set up
    // to ensure the first match is always the longest match.
    struct SubdomainMatch {
        SubdomainMatch(const Name& name) : name_(name) {}
        bool operator()(const pair<Name, RRsetStore>& domain_elem) const {
            return (name_ == domain_elem.first ||
                    name_.compare(domain_elem.first).getRelation() ==
                    NameComparisonResult::SUBDOMAIN);
        }
    private:
        const Name& name_;
    };

336
    void loadRRset(RRsetPtr rrset) {
337
338
339
340
341
342
343
344
345
346
347
        if (rrset->getType() == RRType::NSEC3()) {
            // NSEC3 should go to the dedicated table
            nsec3_domains_[rrset->getName()][rrset->getType()] = rrset;

            // By nature it should have RRSIG.  (We may want to selectively
            // omit this to test pathological cases).
            rrset->addRRsig(RdataPtr(new generic::RRSIG(
                                         getCommonRRSIGText(rrset->getType().
                                                            toText()))));
            return;
        }
348
        domains_[rrset->getName()][rrset->getType()] = rrset;
349
350

        // Remember delegation (NS/DNAME) related RRsets separately.
351
        if (rrset->getType() == RRType::NS() && rrset->getName() != origin_) {
352
            delegations_[rrset->getName()][rrset->getType()] = rrset;
353
        } else if (rrset->getName() == dname_name_ &&
354
            rrset->getType() == RRType::DNAME()) {
355
            dname_rrset_ = rrset;
356
357
358
359
360
361
362
363
364
365
366
        }

        // Add some signatures.  For NS, we only have RRSIG for the origin
        // name. For others generate RRSIG unconditionally.  Technically this
        // is wrong because we shouldn't have it for names under a zone
        // cut.  But in our tests that doesn't matter, so we add them
        // just for simplicity.
        // Note that this includes RRSIG for DS with secure delegations.
        // They should have RRSIGs, so that's actually expected data, not just
        // for simplicity.
        if (rrset->getType() != RRType::NS() || rrset->getName() == origin_) {
367
368
369
            rrset->addRRsig(RdataPtr(new generic::RRSIG(
                                         getCommonRRSIGText(rrset->getType().
                                                            toText()))));
370
371
372
373
        }
    }

    const Name origin_;
374
    // Names where we delegate somewhere else
375
    const Name bad_signed_delegation_name_;
376
    const Name dname_name_;
Michal Vaner's avatar
Michal Vaner committed
377
    bool has_SOA_;
Jerry's avatar
Jerry committed
378
    bool has_apex_NS_;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
379
    const RRClass rrclass_;
380
    bool include_rrsig_anyway_;
381
    bool use_nsec3_;
382
383
384
    // The following two will be used for faked NSEC cases
    Name nsec_name_;
    boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
385
    map<Name, string> hash_map_;
386
387
};

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// A helper function that generates a new RRset based on "wild_rrset",
// replacing its owner name with 'real_name'.
ConstRRsetPtr
substituteWild(const RRset& wild_rrset, const Name& real_name) {
    RRsetPtr rrset(new RRset(real_name, wild_rrset.getClass(),
                             wild_rrset.getType(), wild_rrset.getTTL()));
    // For simplicity we only consider the case with one RDATA (for now)
    rrset->addRdata(wild_rrset.getRdataIterator()->getCurrent());
    ConstRRsetPtr wild_sig = wild_rrset.getRRsig();
    if (wild_sig) {
        RRsetPtr sig(new RRset(real_name, wild_sig->getClass(),
                               wild_sig->getType(), wild_sig->getTTL()));
        sig->addRdata(wild_sig->getRdataIterator()->getCurrent());
        rrset->addRRsig(sig);
    }
    return (rrset);
}

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
ZoneFinder::FindResult
MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                        const FindOptions options)
{
    ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
    if (result.code == NXRRSET) {
        const Domains::const_iterator found_domain = domains_.find(name);
        if (!found_domain->second.empty()) {
            for (RRsetStore::const_iterator found_rrset =
                 found_domain->second.begin();
                 found_rrset != found_domain->second.end(); ++found_rrset) {
                // Insert RRs under the domain name into target
                target.push_back(found_rrset->second);
            }
            return (FindResult(SUCCESS, RRsetPtr()));
        }
    }

    return (result);
}

427
ZoneFinder::FindNSEC3Result
428
MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
    ConstRRsetPtr covering_proof;
    const int labels = name.getLabelCount();

    // For brevity, we assume several things below: maps should have an
    // expected entry when operator[] is used; maps are not empty.
    for (int i = 0; i < labels; ++i) {
        const string hlabel = hash_map_[name.split(i, labels - i)];
        const Name hname = Name(hlabel + ".example.com");
        // We don't use const_iterator so that we can use operator[] below
        Domains::iterator found_domain = nsec3_domains_.lower_bound(hname);

        // If the given hash is larger than the largest stored hash or
        // the first label doesn't match the target, identify the "previous"
        // hash value and remember it as the candidate next closer proof.
        if (found_domain == nsec3_domains_.end() ||
            found_domain->first.split(0, 1).toText(true) != hlabel) {
            // If the given hash is larger or smaller than everything,
            // the covering proof is the NSEC3 that has the largest hash.
            if (found_domain == nsec3_domains_.end() ||
                found_domain == nsec3_domains_.begin()) {
                covering_proof =
                    nsec3_domains_.rbegin()->second[RRType::NSEC3()];
            } else {
                // Otherwise, H(found_domain-1) < given_hash < H(found_domain)
                // The covering proof is the first one.
                covering_proof = (--found_domain)->second[RRType::NSEC3()];
            }
            if (!recursive) {   // in non recursive mode, we are done.
457
458
459
                return (ZoneFinder::FindNSEC3Result(false,
                                                    name.getLabelCount(),
                                                    covering_proof,
460
461
462
463
                                                    ConstRRsetPtr()));
            }
        } else {                // exact match
            return (ZoneFinder::FindNSEC3Result(
464
                        true, name.getLabelCount() - i,
465
466
467
468
469
470
471
                        found_domain->second[RRType::NSEC3()],
                        covering_proof));
        }
    }
    isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
}

472
473
ZoneFinder::FindResult
MockZoneFinder::find(const Name& name, const RRType& type,
474
                     const FindOptions options)
475
{
476
477
478
    // Emulating a broken zone: mandatory apex RRs are missing if specifically
    // configured so (which are rare cases).
    if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
479
        return (FindResult(NXDOMAIN, RRsetPtr()));
480
481
482
483
484
    } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
        return (FindResult(NXDOMAIN, RRsetPtr()));
    }

    // Special case for names on or under a zone cut
485
    Domains::iterator it;
486
    if ((options & FIND_GLUE_OK) == 0 &&
487
488
489
490
        (it = find_if(delegations_.begin(), delegations_.end(),
                      SubdomainMatch(name))) != delegations_.end()) {
        ConstRRsetPtr delegation_ns = it->second[RRType::NS()];
        assert(delegation_ns); // should be ensured by how we construct it
491
        if (type != RRType::DS()) {
492
            // DS query will be handled just like an in-zone case below.
493
            return (FindResult(DELEGATION, delegation_ns));
494
        }
495
496
497
498
    } else if (name.compare(dname_name_).getRelation() ==
               NameComparisonResult::SUBDOMAIN) {
        // And under DNAME
        return (FindResult(DNAME, dname_rrset_));
499
500
501
502
503
504
505
506
507
508
    }

    // normal cases.  names are searched for only per exact-match basis
    // for simplicity.
    const Domains::const_iterator found_domain = domains_.find(name);
    if (found_domain != domains_.end()) {
        // First, try exact match.
        RRsetStore::const_iterator found_rrset =
            found_domain->second.find(type);
        if (found_rrset != found_domain->second.end()) {
509
510
511
            ConstRRsetPtr rrset;
            // Strip whatever signature there is in case DNSSEC is not required
            // Just to make sure the Query asks for it when it is needed
512
            if ((options & ZoneFinder::FIND_DNSSEC) != 0 ||
513
                include_rrsig_anyway_ ||
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
                !found_rrset->second->getRRsig()) {
                rrset = found_rrset->second;
            } else {
                RRsetPtr noconst(new RRset(found_rrset->second->getName(),
                                           found_rrset->second->getClass(),
                                           found_rrset->second->getType(),
                                           found_rrset->second->getTTL()));
                for (RdataIteratorPtr
                     i(found_rrset->second->getRdataIterator());
                     !i->isLast(); i->next()) {
                    noconst->addRdata(i->getCurrent());
                }
                rrset = noconst;
            }
            return (FindResult(SUCCESS, rrset));
529
530
531
532
533
534
535
536
        }

        // Otherwise, if this domain name has CNAME, return it.
        found_rrset = found_domain->second.find(RRType::CNAME());
        if (found_rrset != found_domain->second.end()) {
            return (FindResult(CNAME, found_rrset->second));
        }

537
538
539
540
541
542
543
        // Otherwise it's NXRRSET case...
        // ...but a special pathological case first:
        if (found_domain->first == bad_signed_delegation_name_ &&
            type == RRType::DS()) {
            return (FindResult(NXDOMAIN, RRsetPtr()));
        }
        // normal cases follow.
544
        if ((options & FIND_DNSSEC) != 0) {
545
            if (use_nsec3_) {
546
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
547
            }
548
549
            found_rrset = found_domain->second.find(RRType::NSEC());
            if (found_rrset != found_domain->second.end()) {
550
551
                return (FindResult(NXRRSET, found_rrset->second,
                                   RESULT_NSEC_SIGNED));
552
553
            }
        }
554
        return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
555
556
    }

557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
    // query name isn't found in our domains.
    // We first check if the query name is an empty non terminal name
    // of the zone by naive linear search.
    Domains::const_iterator domain;
    for (domain = domains_.begin(); domain != domains_.end(); ++domain) {
        if (name.compare((*domain).first).getRelation() ==
            NameComparisonResult::SUPERDOMAIN) {
            break;
        }
    }
    if (domain != domains_.end()) {
        // The query name is in an empty non terminal node followed by 'domain'
        // (for simplicity we ignore the pathological case of 'domain' is
        // the origin of the zone)
        --domain;               // reset domain to the "previous name"
        if ((options & FIND_DNSSEC) != 0) {
573
            if (use_nsec3_) {
574
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
575
            }
576
577
578
            RRsetStore::const_iterator found_rrset =
                (*domain).second.find(RRType::NSEC());
            if (found_rrset != (*domain).second.end()) {
579
580
                return (FindResult(NXRRSET, found_rrset->second,
                                   RESULT_NSEC_SIGNED));
581
582
583
584
585
            }
        }
        return (FindResult(NXRRSET, RRsetPtr()));
    }

586
587
588
589
    // Another possibility is wildcard.  For simplicity we only check
    // hardcoded specific cases, ignoring other details such as canceling
    // due to the existence of closer name.
    if ((options & NO_WILDCARD) == 0) {
590
        const Name wild_suffix(name.split(1));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
591
        // Unit Tests use those domains for Wildcard test.
592
        if (name.equals(Name("www.wild.example.com"))||
593
594
           name.equals(Name("www1.uwild.example.com"))||
           name.equals(Name("a.t.example.com"))) {
595
596
597
            if (name.compare(wild_suffix).getRelation() ==
                NameComparisonResult::SUBDOMAIN) {
                domain = domains_.find(Name("*").concatenate(wild_suffix));
598
599
                // Matched the QNAME
                if (domain != domains_.end()) {
600
601
602
603
                    RRsetStore::const_iterator found_rrset =
                        domain->second.find(type);
                    // Matched the QTYPE
                    if(found_rrset != domain->second.end()) {
604
                        return (FindResult(SUCCESS,
605
                                           substituteWild(
606
607
608
609
610
                                               *found_rrset->second, name),
                                           RESULT_WILDCARD |
                                           (use_nsec3_ ?
                                            RESULT_NSEC3_SIGNED :
                                            RESULT_NSEC_SIGNED)));
611
612
                    } else {
                        // No matched QTYPE, this case is for WILDCARD_NXRRSET
613
                        if (use_nsec3_) {
614
615
616
                            return (FindResult(NXRRSET, RRsetPtr(),
                                               RESULT_WILDCARD |
                                               RESULT_NSEC3_SIGNED));
617
                        }
618
619
                        const Name new_name =
                            Name("*").concatenate(wild_suffix);
620
621
                        found_rrset = domain->second.find(RRType::NSEC());
                        assert(found_rrset != domain->second.end());
622
                        return (FindResult(NXRRSET,
623
                                           substituteWild(
624
                                               *found_rrset->second,
625
626
627
                                               new_name),
                                           RESULT_WILDCARD |
                                           RESULT_NSEC_SIGNED));
628
629
                    }
                } else {
630
                    // This is empty non terminal name case on wildcard.
631
632
                    const Name empty_name = Name("*").concatenate(wild_suffix);
                    if (use_nsec3_) {
633
634
635
                        return (FindResult(NXRRSET, RRsetPtr(),
                                           RESULT_WILDCARD |
                                           RESULT_NSEC3_SIGNED));
636
                    }
637
                    for (Domains::reverse_iterator it = domains_.rbegin();
638
639
640
                         it != domains_.rend();
                         ++it) {
                        RRsetStore::const_iterator nsec_it;
641
                        if ((*it).first < empty_name &&
642
643
                            (nsec_it = (*it).second.find(RRType::NSEC()))
                            != (*it).second.end()) {
644
645
646
                            return (FindResult(NXRRSET, (*nsec_it).second,
                                               RESULT_WILDCARD |
                                               RESULT_NSEC_SIGNED));
647
                        }
648
                    }
649
                }
650
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
651
652
             }
        }
653
654
655
656
657
658
659
660
        const Name cnamewild_suffix("cnamewild.example.com");
        if (name.compare(cnamewild_suffix).getRelation() ==
            NameComparisonResult::SUBDOMAIN) {
            domain = domains_.find(Name("*").concatenate(cnamewild_suffix));
            assert(domain != domains_.end());
            RRsetStore::const_iterator found_rrset =
                domain->second.find(RRType::CNAME());
            assert(found_rrset != domain->second.end());
661
662
663
664
665
            return (FindResult(CNAME,
                               substituteWild(*found_rrset->second, name),
                               RESULT_WILDCARD |
                               (use_nsec3_ ? RESULT_NSEC3_SIGNED :
                                RESULT_NSEC_SIGNED)));
666
        }
667
668
    }

669
    // This is an NXDOMAIN case.
670
671
672
673
674
675
676
    // If we need DNSSEC proof, find the "previous name" that has an NSEC RR
    // and return NXDOMAIN with the found NSEC.  Otherwise, just return the
    // NXDOMAIN code and NULL.  If DNSSEC proof is requested but no NSEC is
    // found, we return NULL, too.  (For simplicity under the test conditions
    // we don't care about pathological cases such as the name is "smaller"
    // than the origin)
    if ((options & FIND_DNSSEC) != 0) {
677
        if (use_nsec3_) {
678
            return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
679
680
        }

681
682
683
684
685
686
        // Emulate a broken DataSourceClient for some special names.
        if (nsec_result_ && nsec_name_ == name) {
            return (*nsec_result_);
        }

        // Normal case
687
688
689
        // XXX: some older g++ complains about operator!= if we use
        // const_reverse_iterator
        for (Domains::reverse_iterator it = domains_.rbegin();
690
691
692
693
694
695
             it != domains_.rend();
             ++it) {
            RRsetStore::const_iterator nsec_it;
            if ((*it).first < name &&
                (nsec_it = (*it).second.find(RRType::NSEC()))
                != (*it).second.end()) {
696
697
                return (FindResult(NXDOMAIN, (*nsec_it).second,
                                   RESULT_NSEC_SIGNED));
698
699
700
            }
        }
    }
701
    return (FindResult(NXDOMAIN, RRsetPtr()));
702
703
}

704
705
706
707
708
class QueryTest : public ::testing::Test {
protected:
    QueryTest() :
        qname(Name("www.example.com")), qclass(RRClass::IN()),
        qtype(RRType::A()), response(Message::RENDER),
709
        qid(response.getQid()), query_code(Opcode::QUERY().getCode())
710
711
    {
        response.setRcode(Rcode::NOERROR());
712
713
        response.setOpcode(Opcode::QUERY());
        // create and add a matching zone.
714
        mock_finder = new MockZoneFinder();
715
        memory_client.addZone(ZoneFinderPtr(mock_finder));
716
    }
717
    MockZoneFinder* mock_finder;
718
719
720
721
    // We use InMemoryClient here. We could have some kind of mock client
    // here, but historically, the Query supported only InMemoryClient
    // (originally named MemoryDataSrc) and was tested with it, so we keep
    // it like this for now.
722
    InMemoryClient memory_client;
723
724
725
726
    const Name qname;
    const RRClass qclass;
    const RRType qtype;
    Message response;
727
728
    const qid_t qid;
    const uint16_t query_code;
729
730
};

731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
// A wrapper to check resulting response message commonly used in
// tests below.
// check_origin needs to be specified only when the authority section has
// an SOA RR.  The interface is not generic enough but should be okay
// for our test cases in practice.
void
responseCheck(Message& response, const isc::dns::Rcode& rcode,
              unsigned int flags, const unsigned int ancount,
              const unsigned int nscount, const unsigned int arcount,
              const char* const expected_answer,
              const char* const expected_authority,
              const char* const expected_additional,
              const Name& check_origin = Name::ROOT_NAME())
{
    // In our test cases QID, Opcode, and QDCOUNT should be constant, so
    // we don't bother the test cases specifying these values.
    headerCheck(response, response.getQid(), rcode, Opcode::QUERY().getCode(),
                flags, 0, ancount, nscount, arcount);
    if (expected_answer != NULL) {
        rrsetsCheck(expected_answer,
                    response.beginSection(Message::SECTION_ANSWER),
chenzhengzhang's avatar
chenzhengzhang committed
752
753
                    response.endSection(Message::SECTION_ANSWER),
                    check_origin);
754
755
756
757
758
759
760
761
762
763
764
765
766
767
    }
    if (expected_authority != NULL) {
        rrsetsCheck(expected_authority,
                    response.beginSection(Message::SECTION_AUTHORITY),
                    response.endSection(Message::SECTION_AUTHORITY),
                    check_origin);
    }
    if (expected_additional != NULL) {
        rrsetsCheck(expected_additional,
                    response.beginSection(Message::SECTION_ADDITIONAL),
                    response.endSection(Message::SECTION_ADDITIONAL));
    }
}

768
TEST_F(QueryTest, noZone) {
769
    // There's no zone in the memory datasource.  So the response should have
770
    // REFUSED.
771
772
    InMemoryClient empty_memory_client;
    Query nozone_query(empty_memory_client, qname, qtype, response);
773
    EXPECT_NO_THROW(nozone_query.process());
774
    EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
775
776
}

777
TEST_F(QueryTest, exactMatch) {
778
    Query query(memory_client, qname, qtype, response);
Jerry's avatar
Jerry committed
779
    EXPECT_NO_THROW(query.process());
780
    // find match rrset
781
782
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                  www_a_txt, zone_ns_txt, ns_addrs_txt);
783
}
784

785
786
787
788
789
790
791
792
793
794
795
TEST_F(QueryTest, exactMatchIgnoreSIG) {
    // Check that we do not include the RRSIG when not requested even when
    // we receive it from the data source.
    mock_finder->setIncludeRRSIGAnyway(true);
    Query query(memory_client, qname, qtype, response);
    EXPECT_NO_THROW(query.process());
    // find match rrset
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                  www_a_txt, zone_ns_txt, ns_addrs_txt);
}

796
797
798
799
800
TEST_F(QueryTest, dnssecPositive) {
    // Just like exactMatch, but the signatures should be included as well
    Query query(memory_client, qname, qtype, response, true);
    EXPECT_NO_THROW(query.process());
    // find match rrset
801
802
803
804
805
    // We can't let responseCheck to check the additional section as well,
    // it gets confused by the two RRs for glue.delegation.../RRSIG due
    // to it's design and fixing it would be hard. Therefore we simply
    // check manually this one time.
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
806
                  (www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
807
                                           "A 5 3 3600 20000101000000 "
808
809
810
811
812
813
                                           "20000201000000 12345 example.com. "
                                           "FAKEFAKEFAKE\n")).c_str(),
                  (zone_ns_txt + std::string("example.com. 3600 IN RRSIG NS 5 "
                                             "3 3600 20000101000000 "
                                             "20000201000000 12345 "
                                             "example.com. FAKEFAKEFAKE\n")).
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
                  c_str(), NULL);
    RRsetIterator iterator(response.beginSection(Message::SECTION_ADDITIONAL));
    const char* additional[] = {
        "glue.delegation.example.com. 3600 IN A 192.0.2.153\n",
        "glue.delegation.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
            "20000201000000 12345 example.com. FAKEFAKEFAKE\n",
        "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n",
        "glue.delegation.example.com. 3600 IN RRSIG AAAA 5 3 3600 "
            "20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE\n",
        "noglue.example.com. 3600 IN A 192.0.2.53\n",
        "noglue.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
            "20000201000000 12345 example.com. FAKEFAKEFAKE\n",
        NULL
    };
    for (const char** rr(additional); *rr != NULL; ++ rr) {
        ASSERT_FALSE(iterator ==
                     response.endSection(Message::SECTION_ADDITIONAL));
        EXPECT_EQ(*rr, (*iterator)->toText());
        iterator ++;
    }
    EXPECT_TRUE(iterator == response.endSection(Message::SECTION_ADDITIONAL));
835
836
}

837
838
839
TEST_F(QueryTest, exactAddrMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
840
    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
841
                          response).process());
842

843
844
845
846
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
                  "noglue.example.com. 3600 IN A 192.0.2.53\n", zone_ns_txt,
                  "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
                  "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
847
848
}

849
850
851
TEST_F(QueryTest, apexNSMatch) {
    // find match rrset, omit authority data which has already been provided
    // in the answer section from the authority section.
852
    EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::NS(),
853
                          response).process());
854

855
856
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
                  zone_ns_txt, NULL, ns_addrs_txt);
857
858
}

chenzhengzhang's avatar
chenzhengzhang committed
859
// test type any query logic
860
861
862
TEST_F(QueryTest, exactAnyMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
863
    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"),
864
                          RRType::ANY(), response).process());
865

866
867
868
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 3, 2,
                  (string("noglue.example.com. 3600 IN A 192.0.2.53\n") +
                   string(nsec_nxdomain_txt)).c_str(),
869
870
871
                  zone_ns_txt,
                  "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
                  "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
872
873
}

chenzhengzhang's avatar
chenzhengzhang committed
874
875
876
TEST_F(QueryTest, apexAnyMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
877
    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
878
                          RRType::ANY(), response).process());
879
880
881
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                  (string(soa_txt) + string(zone_ns_txt) +
                   string(nsec_apex_txt)).c_str(),
882
                  NULL, ns_addrs_txt, mock_finder->getOrigin());
883
884
885
}

TEST_F(QueryTest, mxANYMatch) {
886
    EXPECT_NO_THROW(Query(memory_client, Name("mx.example.com"),
887
                          RRType::ANY(), response).process());
888
889
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 3, 4,
                  (string(mx_txt) + string(nsec_mx_txt)).c_str(), zone_ns_txt,
890
                  (string(ns_addrs_txt) + string(www_a_txt)).c_str());
chenzhengzhang's avatar
chenzhengzhang committed
891
892
893
}

TEST_F(QueryTest, glueANYMatch) {
894
    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
895
896
897
898
899
900
                          RRType::ANY(), response).process());
    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                  NULL, delegation_txt, ns_addrs_txt);
}

TEST_F(QueryTest, nodomainANY) {
901
    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
902
903
                          RRType::ANY(), response).process());
    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
904
                  NULL, soa_txt, NULL, mock_finder->getOrigin());
chenzhengzhang's avatar
chenzhengzhang committed
905
906
}

907
// This tests that when we need to look up Zone's apex NS records for
Jerry's avatar
Jerry committed
908
909
910
// authoritative answer, and there is no apex NS records. It should
// throw in that case.
TEST_F(QueryTest, noApexNS) {
911
    // Disable apex NS record
912
    mock_finder->setApexNSFlag(false);
913

914
    EXPECT_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
915
916
                       response).process(), Query::NoApexNS);
    // We don't look into the response, as it threw
Jerry's avatar
Jerry committed
917
918
}

919
TEST_F(QueryTest, delegation) {
920
    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
921
                          qtype, response).process());
922

923
924
    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                  NULL, delegation_txt, ns_addrs_txt);
925
}
926

927
TEST_F(QueryTest, secureDelegation) {
Jelte Jansen's avatar
Jelte Jansen committed
928
929
    EXPECT_NO_THROW(Query(memory_client,
                          Name("foo.signed-delegation.example.com"),
930
931
932
                          qtype, response, true).process());

    // Should now contain RRSIG and DS record as well.
933
    responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
934
                  NULL,
935
936
937
                  (string(signed_delegation_txt) +
                   string(signed_delegation_ds_txt) +
                   string("signed-delegation.example.com. 3600 IN RRSIG ") +
938
                   getCommonRRSIGText("DS")).c_str(),
939
940
941
942
943
944
945
946
                  NULL);
}

TEST_F(QueryTest, secureUnsignedDelegation) {
    EXPECT_NO_THROW(Query(memory_client,
                          Name("foo.unsigned-delegation.example.com"),
                          qtype, response, true).process());

Jelte Jansen's avatar
Jelte Jansen committed
947
    // Should now contain RRSIG and NSEC record as well.
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
    responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
                  NULL,
                  (string(unsigned_delegation_txt) +
                   string(unsigned_delegation_nsec_txt) +
                   string("unsigned-delegation.example.com. 3600 IN RRSIG ") +
                   getCommonRRSIGText("NSEC")).c_str(),
                  NULL);
}

TEST_F(QueryTest, badSecureDelegation) {
    // Test whether exception is raised if DS query at delegation results in
    // something different than SUCCESS or NXRRSET
    EXPECT_THROW(Query(memory_client, Name("bad-delegation.example.com"),
                       qtype, response, true).process(), Query::BadDS);

    // But only if DNSSEC is requested (it shouldn't even try to look for
    // the DS otherwise)
    EXPECT_NO_THROW(Query(memory_client, Name("bad-delegation.example.com"),
                          qtype, response).process());
967
968
969
}


970
TEST_F(QueryTest, nxdomain) {
971
    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
972
                          response).process());
973
    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
974
                  NULL, soa_txt, NULL, mock_finder->getOrigin());
975
}
976

977
TEST_F(QueryTest, nxdomainWithNSEC) {
978
979
980
    // NXDOMAIN with DNSSEC proof.  We should have SOA, NSEC that proves
    // NXDOMAIN and NSEC that proves nonexistence of matching wildcard,
    // as well as their RRSIGs.
981
982
    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
                          response, true).process());
983
    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
984
985
986
987
988
                  NULL, (string(soa_txt) +
                         string("example.com. 3600 IN RRSIG ") +
                         getCommonRRSIGText("SOA") + "\n" +
                         string(nsec_nxdomain_txt) + "\n" +
                         string("noglue.example.com. 3600 IN RRSIG ") +
989
990
991
                         getCommonRRSIGText("NSEC") + "\n" +
                         string(nsec_apex_txt) + "\n" +
                         string("example.com. 3600 IN RRSIG ") +
992
                         getCommonRRSIGText("NSEC")).c_str(),
993
994
995
996
997
998
999
1000
                  NULL, mock_finder->getOrigin());
}

TEST_F(QueryTest, nxdomainWithNSEC2) {
    // See comments about no_txt.  In this case the best possible wildcard
    // is derived from the next domain of the NSEC that proves NXDOMAIN, and
    // the NSEC to provide the non existence of wildcard is different from
    // the first NSEC.
For faster browsing, not all history is shown. View entire blame