query_unittest.cc 97.9 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
// 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).
55
56
57
58
59
60
61
62
63
64
//
// For readability consistency, all strings are placed in a separate line,
// even if they are very short and can reasonably fit in a single line with
// the corresponding variable.  For example, we write
// const char* const foo_txt =
//  "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
// instead of
// const char* const foo_txt = "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
const char* const soa_txt =
    "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
65
66
67
68
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";
69
70
71
const char* const zone_ds_txt =
    "example.com. 3600 IN DS 57855 5 1 "
        "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
72
73
74
75
76
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
77
78
79
80
    "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";
81
82
83
84
// Borrowed from the RFC4035
const char* const delegation_ds_txt =
    "delegation.example.com. 3600 IN DS 57855 5 1 "
        "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
85
86
87
88
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";
89
90
const char* const www_a_txt =
    "www.example.com. 3600 IN A 192.0.2.80\n";
91
92
const char* const cname_txt =
    "cname.example.com. 3600 IN CNAME www.example.com.\n";
93
94
95
96
97
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";
98
// The DNAME to do tests against
99
const char* const dname_txt =
100
101
    "dname.example.com. 3600 IN DNAME "
    "somethinglong.dnametarget.example.com.\n";
102
// Some data at the dname node (allowed by RFC 2672)
103
104
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
105
106
// This is not inside the zone, this is created at runtime
const char* const synthetized_cname_txt =
107
108
    "www.dname.example.com. 3600 IN CNAME "
    "www.somethinglong.dnametarget.example.com.\n";
109
110
111
112
113
// 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";
114
// Wildcards
115
116
const char* const wild_txt =
    "*.wild.example.com. 3600 IN A 192.0.2.7\n";
117
118
119
120
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";
121
122
123
const char* const nsec_cnamewild_txt =
    "*.cnamewild.example.com. 3600 IN NSEC "
    "delegation.example.com. CNAME NSEC RRSIG\n";
124
125
// Wildcard_nxrrset
const char* const wild_txt_nxrrset =
126
    "*.uwild.example.com. 3600 IN A 192.0.2.9\n";
127
const char* const nsec_wild_txt_nxrrset =
128
    "*.uwild.example.com. 3600 IN NSEC www.uwild.example.com. A NSEC RRSIG\n";
129
const char* const wild_txt_next =
130
    "www.uwild.example.com. 3600 IN A 192.0.2.11\n";
131
const char* const nsec_wild_txt_next =
132
    "www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG\n";
133
// Wildcard empty
134
135
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
136
const char* const nsec_empty_txt =
137
    "b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG\n";
138
139
const char* const empty_prev_txt =
    "t.example.com. 3600 IN A 192.0.2.15\n";
140
141
const char* const nsec_empty_prev_txt =
    "t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG\n";
142
143
144
145
146
147
148
149
150
// 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";
151
152
153
// NSEC records.
const char* const nsec_apex_txt =
    "example.com. 3600 IN NSEC cname.example.com. NS SOA NSEC RRSIG\n";
154
155
156
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 =
157
158
159
160
161
162
163
164
165
166
167
168
    ").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";
169
const char* const nsec_nxdomain_txt =
170
    "noglue.example.com. 3600 IN NSEC nonsec.example.com. A\n";
171

172
173
174
175
// NSEC for the normal NXRRSET case
const char* const nsec_www_txt =
    "www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG\n";

176
// Authoritative data without NSEC
177
178
const char* const nonsec_a_txt =
    "nonsec.example.com. 3600 IN A 192.0.2.0\n";
179

180
181
182
183
184
185
186
187
// 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";

188
189
190
191
192
193
// NSEC3 for wild.example.com (used in wildcard tests, will be added on
// demand not to confuse other tests)
const char* const nsec3_atwild_txt =
    "ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en\n";

194
195
196
197
198
199
// NSEC3 for cnamewild.example.com (used in wildcard tests, will be added on
// demand not to confuse other tests)
const char* const nsec3_atcnamewild_txt =
    "k8udemvp1j2f7eg6jebps17vp3n8i58h.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en\n";

200
201
202
203
204
205
206
207
208
209
// NSEC3 for *.uwild.example.com (will be added on demand not to confuse
// other tests)
const char* const nsec3_wild_txt =
    "b4um86eghhds6nea196smvmlo4ors995.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
// NSEC3 for uwild.example.com. (will be added on demand)
const char* const nsec3_uwild_txt =
    "t644ebqk9bibcna874givr6joj62mlhv.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";

210
// (Secure) delegation data; Delegation with DS record
211
212
213
214
215
216
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";

Jelte Jansen's avatar
Jelte Jansen committed
217
218
// (Secure) delegation data; Delegation without DS record (and both NSEC
// and NSEC3 denying its existence)
219
220
221
222
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 "
223
    "unsigned-delegation-optout.example.com. NS RRSIG NSEC\n";
224
// This one will be added on demand
225
226
227
228
229
230
231
232
233
const char* const unsigned_delegation_nsec3_txt =
    "q81r598950igr1eqvc60aedlq66425b5.example.com. 3600 IN NSEC3 1 1 12 "
    "aabbccdd 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom NS RRSIG\n";

// Delegation without DS record, and no direct matching NSEC3 record
const char* const unsigned_delegation_optout_txt =
    "unsigned-delegation-optout.example.com. 3600 IN NS ns.example.net.\n";
const char* const unsigned_delegation_optout_nsec_txt =
    "unsigned-delegation-optout.example.com. 3600 IN NSEC "
234
235
    "*.uwild.example.com. NS RRSIG NSEC\n";

236
237
// (Secure) delegation data; Delegation where the DS lookup will raise an
// exception.
238
239
240
241
const char* const bad_delegation_txt =
    "bad-delegation.example.com. 3600 IN NS ns.example.net.\n";


242
243
244
245
// 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.
246
247
248
249
250
251
string
getCommonRRSIGText(const string& type) {
    return (type +
            string(" 5 3 3600 20000101000000 20000201000000 12345 "
                   "example.com. FAKEFAKEFAKE"));
}
Michal Vaner's avatar
Michal Vaner committed
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// A helper callback of masterLoad() used in InMemoryZoneFinderTest.
void
setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
    *(*it) = rrset;
    ++it;
}

// A helper function that converts a textual form of a single RR into a
// RRsetPtr object.  If it's SOA, origin must be set to its owner name;
// otherwise masterLoad() will reject it.
RRsetPtr
textToRRset(const string& text_rrset, const Name& origin = Name::ROOT_NAME()) {
    stringstream ss(text_rrset);
    RRsetPtr rrset;
    vector<RRsetPtr*> rrsets;
    rrsets.push_back(&rrset);
    masterLoad(ss, origin, RRClass::IN(),
               boost::bind(setRRset, _1, rrsets.begin()));
    return (rrset);
}

274
// This is a mock Zone Finder class for testing.
275
// It is a derived class of ZoneFinder for the convenient of tests.
276
// Its find() method emulates the common behavior of protocol compliant
277
// ZoneFinder classes, but simplifies some minor cases and also supports broken
278
// behavior.
279
280
// For simplicity, most names are assumed to be "in zone"; delegations
// to child zones are identified by the existence of non origin NS records.
281
282
283
284
// 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).
285
class MockZoneFinder : public ZoneFinder {
286
public:
287
    MockZoneFinder() :
Michal Vaner's avatar
Michal Vaner committed
288
        origin_(Name("example.com")),
289
        bad_signed_delegation_name_("bad-delegation.example.com"),
290
        dname_name_("dname.example.com"),
291
292
        has_SOA_(true),
        has_apex_NS_(true),
293
        rrclass_(RRClass::IN()),
294
        include_rrsig_anyway_(false),
295
        use_nsec3_(false),
296
        nsec_name_(origin_),
297
298
        nsec3_fake_(NULL),
        nsec3_name_(NULL)
299
    {
300
301
        stringstream zone_stream;
        zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
302
303
            delegation_txt << delegation_ds_txt << mx_txt << www_a_txt <<
            cname_txt << cname_nxdom_txt << cname_out_txt << dname_txt <<
304
            dname_a_txt << other_zone_rrs << no_txt << nz_txt <<
305
            nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
306
            nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
307
            wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt <<
308
            wild_txt_nxrrset << nsec_wild_txt_nxrrset << wild_txt_next <<
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
309
            nsec_wild_txt_next << empty_txt << nsec_empty_txt <<
310
            empty_prev_txt << nsec_empty_prev_txt <<
311
312
313
            nsec3_apex_txt << nsec3_www_txt <<
            signed_delegation_txt << signed_delegation_ds_txt <<
            unsigned_delegation_txt << unsigned_delegation_nsec_txt <<
314
            unsigned_delegation_optout_txt <<
315
            unsigned_delegation_optout_nsec_txt <<
316
            bad_delegation_txt;
317

318
        masterLoad(zone_stream, origin_, rrclass_,
319
                   boost::bind(&MockZoneFinder::loadRRset, this, _1));
320
321
322
323
324

        empty_nsec_rrset_ = ConstRRsetPtr(new RRset(Name::ROOT_NAME(),
                                                    RRClass::IN(),
                                                    RRType::NSEC(),
                                                    RRTTL(3600)));
325
326
327

        // (Faked) NSEC3 hash map.  For convenience we use hardcoded built-in
        // map instead of calculating and using actual hash.
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
328
329
330
        // The used hash values are borrowed from RFC5155 examples (they are
        // based on the query name, not that they would correspond directly
        // to the name).
331
        hash_map_[Name("example.com")] = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
332
333
        hash_map_[Name("www.example.com")] =
            "q04jkcevqvmu85r014c7dkba38o0ji5r";
334
335
        hash_map_[Name("nxdomain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
336
337
338
339
        hash_map_[Name("nx.domain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
        hash_map_[Name("domain.example.com")] =
            "v644ebqk9bibcna874givr6joj62mlhv";
340
341
342
343
        hash_map_[Name("nxdomain2.example.com")] =
            "q00jkcevqvmu85r014c7dkba38o0ji5r";
        hash_map_[Name("nxdomain3.example.com")] =
            "009mhaveqvm6t7vbl5lop2u3t2rp3tom";
344
        hash_map_[Name("unsigned-delegation.example.com")] =
345
            "q81r598950igr1eqvc60aedlq66425b5"; // a bit larger than H(www)
346
347
        hash_map_[Name("*.uwild.example.com")] =
            "b4um86eghhds6nea196smvmlo4ors995";
348
349
        hash_map_[Name("unsigned-delegation-optout.example.com")] =
            "vld46lphhasfapj8og1pglgiasa5o5gt";
350

351
352
353
354
355
356
357
        // For wildcard proofs
        hash_map_[Name("wild.example.com")] =
            "ji6neoaepv8b5o6k4ev33abha8ht9fgc";
        hash_map_[Name("y.wild.example.com")] =
            "0p9mhaveqvm6t7vbl5lop2u3t2rp3ton"; // a bit larger than H(<apex>)
        hash_map_[Name("x.y.wild.example.com")] =
            "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
358
359
360
361
        hash_map_[Name("cnamewild.example.com")] =
            "k8udemvp1j2f7eg6jebps17vp3n8i58h";
        hash_map_[Name("www.cnamewild.example.com")] =
            "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
362

363
364
365
366
367
        // For closest encloser proof for www1.uwild.example.com:
        hash_map_[Name("uwild.example.com")] =
            "t644ebqk9bibcna874givr6joj62mlhv";
        hash_map_[Name("www1.uwild.example.com")] =
            "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
368
    }
369
370
    virtual isc::dns::Name getOrigin() const { return (origin_); }
    virtual isc::dns::RRClass getClass() const { return (rrclass_); }
371
372
    virtual FindResult find(const isc::dns::Name& name,
                            const isc::dns::RRType& type,
373
                            const FindOptions options = FIND_DEFAULT);
374
375
376
    virtual FindResult findAll(const isc::dns::Name& name,
                               std::vector<ConstRRsetPtr>& target,
                               const FindOptions options = FIND_DEFAULT);
377

378
    virtual ZoneFinder::FindNSEC3Result
379
    findNSEC3(const Name& name, bool recursive);
380

381
382
    // If false is passed, it makes the zone broken as if it didn't have the
    // SOA.
383
    void setSOAFlag(bool on) { has_SOA_ = on; }
384

385
386
387
    // 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; }
388

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

392
393
394
395
396
397
398
399
400
    // 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));
    }

401
402
403
404
    // Once called, the findNSEC3 will return the provided result for the next
    // query. After that, it'll return to operate normally.
    // NULL disables. Does not take ownership of the pointer (it is generally
    // expected to be a local variable in the test function).
405
406
407
    void setNSEC3Result(const FindNSEC3Result* result,
                        const Name* name = NULL)
    {
408
        nsec3_fake_ = result;
409
        nsec3_name_ = name;
410
411
    }

412
413
414
415
    // If true is passed return an empty NSEC3 RRset for some negative
    // answers when DNSSEC is required.
    void setNSEC3Flag(bool on) { use_nsec3_ = on; }

416
    virtual Name findPreviousName(const Name&) const {
417
418
419
        isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
    }

420
421
422
423
424
425
426
427
428
429
    // This method allows tests to insert new record in the middle of the test.
    //
    // \param record_txt textual RR representation of RR (such as soa_txt, etc)
    void addRecord(const string& record_txt) {
        stringstream record_stream;
        record_stream << record_txt;
        masterLoad(record_stream, origin_, rrclass_,
                   boost::bind(&MockZoneFinder::loadRRset, this, _1));
    }

430
431
public:
    // We allow the tests to use these for convenience
432
    ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
433
434
    ConstRRsetPtr empty_nsec_rrset_;

Jerry's avatar
Jerry committed
435
private:
436
437
438
    typedef map<RRType, ConstRRsetPtr> RRsetStore;
    typedef map<Name, RRsetStore> Domains;
    Domains domains_;
439
    Domains delegations_;
440
    Domains nsec3_domains_;
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

    // 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_;
    };

457
    void loadRRset(RRsetPtr rrset) {
458
459
460
461
462
463
464
465
466
467
468
        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;
        }
469
        domains_[rrset->getName()][rrset->getType()] = rrset;
470
471

        // Remember delegation (NS/DNAME) related RRsets separately.
472
        if (rrset->getType() == RRType::NS() && rrset->getName() != origin_) {
473
            delegations_[rrset->getName()][rrset->getType()] = rrset;
474
        } else if (rrset->getName() == dname_name_ &&
475
                   rrset->getType() == RRType::DNAME()) {
476
            dname_rrset_ = rrset;
477
478
479
480
481
482
483
484
485
486
487
        }

        // 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_) {
488
489
490
            rrset->addRRsig(RdataPtr(new generic::RRSIG(
                                         getCommonRRSIGText(rrset->getType().
                                                            toText()))));
491
492
493
494
        }
    }

    const Name origin_;
495
    // Names where we delegate somewhere else
496
    const Name bad_signed_delegation_name_;
497
    const Name dname_name_;
Michal Vaner's avatar
Michal Vaner committed
498
    bool has_SOA_;
Jerry's avatar
Jerry committed
499
    bool has_apex_NS_;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
500
    const RRClass rrclass_;
501
    bool include_rrsig_anyway_;
502
    bool use_nsec3_;
503
504
505
    // The following two will be used for faked NSEC cases
    Name nsec_name_;
    boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
506
507
508
    // The following two are for faking bad NSEC3 responses
    // Enabled when not NULL
    const FindNSEC3Result* nsec3_fake_;
509
    const Name* nsec3_name_;
510
511
public:
    // Public, to allow tests looking up the right names for something
512
    map<Name, string> hash_map_;
513
514
};

515
516
517
// A helper function that generates a new RRset based on "wild_rrset",
// replacing its owner name with 'real_name'.
ConstRRsetPtr
518
substituteWild(const AbstractRRset& wild_rrset, const Name& real_name) {
519
520
521
522
523
524
525
526
527
528
529
530
531
532
    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);
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
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);
}

554
ZoneFinder::FindNSEC3Result
555
MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
556
    // Do we have a fake result set? If so, use it.
557
558
    if (nsec3_fake_ != NULL &&
        (nsec3_name_ == NULL || *nsec3_name_ == name)) {
559
560
561
562
        const FindNSEC3Result* result(nsec3_fake_);
        return (*result);
    }

563
564
565
566
567
568
569
    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)];
570
571
572
573
        if (hlabel.empty()) {
            isc_throw(isc::Unexpected, "findNSEC3() hash failure for " <<
                      name.split(i, labels - i));
        }
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
        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.
595
596
597
                return (ZoneFinder::FindNSEC3Result(false,
                                                    name.getLabelCount(),
                                                    covering_proof,
598
599
600
601
                                                    ConstRRsetPtr()));
            }
        } else {                // exact match
            return (ZoneFinder::FindNSEC3Result(
602
                        true, name.getLabelCount() - i,
603
604
605
606
607
608
609
                        found_domain->second[RRType::NSEC3()],
                        covering_proof));
        }
    }
    isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
}

610
611
ZoneFinder::FindResult
MockZoneFinder::find(const Name& name, const RRType& type,
612
                     const FindOptions options)
613
{
614
615
616
    // 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_) {
617
        return (FindResult(NXDOMAIN, RRsetPtr()));
618
619
620
621
    } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
        return (FindResult(NXDOMAIN, RRsetPtr()));
    }

622
    // Special case for names on or under a zone cut and under DNAME
623
    Domains::iterator it;
624
    if ((options & FIND_GLUE_OK) == 0 &&
625
626
627
628
        (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
629
630
631
632
633

        // DS query for the delegated domain (i.e. an exact match) will be
        // handled just like an in-zone case below.  Others result in
        // DELEGATION.
        if (type != RRType::DS() || it->first != name) {
634
            return (FindResult(DELEGATION, delegation_ns));
635
        }
636
    } else if (name.compare(dname_name_).getRelation() ==
637
               NameComparisonResult::SUBDOMAIN) {
638
        return (FindResult(DNAME, dname_rrset_));
639
640
641
642
643
644
645
646
647
648
    }

    // 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()) {
649
650
651
            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
652
            if ((options & ZoneFinder::FIND_DNSSEC) != 0 ||
653
                include_rrsig_anyway_ ||
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
                !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));
669
670
671
672
673
674
675
676
        }

        // 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));
        }

677
678
679
680
681
682
683
        // 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.
684
        if ((options & FIND_DNSSEC) != 0) {
685
            if (use_nsec3_) {
686
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
687
            }
688
689
            found_rrset = found_domain->second.find(RRType::NSEC());
            if (found_rrset != found_domain->second.end()) {
690
691
                return (FindResult(NXRRSET, found_rrset->second,
                                   RESULT_NSEC_SIGNED));
692
693
            }
        }
694
        return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
695
696
    }

697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    // 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) {
713
            if (use_nsec3_) {
714
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
715
            }
716
717
718
            RRsetStore::const_iterator found_rrset =
                (*domain).second.find(RRType::NSEC());
            if (found_rrset != (*domain).second.end()) {
719
720
                return (FindResult(NXRRSET, found_rrset->second,
                                   RESULT_NSEC_SIGNED));
721
722
723
724
725
            }
        }
        return (FindResult(NXRRSET, RRsetPtr()));
    }

726
727
728
729
    // 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) {
730
731
        const Name wild_suffix(name == Name("x.y.wild.example.com") ?
                               Name("wild.example.com") : name.split(1));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
732
        // Unit Tests use those domains for Wildcard test.
733
734
735
736
        if (name.equals(Name("www.wild.example.com")) ||
            name.equals(Name("x.y.wild.example.com")) ||
            name.equals(Name("www1.uwild.example.com")) ||
            name.equals(Name("a.t.example.com"))) {
737
738
739
            if (name.compare(wild_suffix).getRelation() ==
                NameComparisonResult::SUBDOMAIN) {
                domain = domains_.find(Name("*").concatenate(wild_suffix));
740
741
                // Matched the QNAME
                if (domain != domains_.end()) {
742
743
744
745
                    RRsetStore::const_iterator found_rrset =
                        domain->second.find(type);
                    // Matched the QTYPE
                    if(found_rrset != domain->second.end()) {
746
                        return (FindResult(SUCCESS,
747
                                           substituteWild(
748
749
750
751
752
                                               *found_rrset->second, name),
                                           RESULT_WILDCARD |
                                           (use_nsec3_ ?
                                            RESULT_NSEC3_SIGNED :
                                            RESULT_NSEC_SIGNED)));
753
                    } else {
754
755
                        // No matched QTYPE, this case is for NXRRSET with
                        // WILDCARD
756
                        if (use_nsec3_) {
757
758
759
                            return (FindResult(NXRRSET, RRsetPtr(),
                                               RESULT_WILDCARD |
                                               RESULT_NSEC3_SIGNED));
760
                        }
761
762
                        const Name new_name =
                            Name("*").concatenate(wild_suffix);
763
764
                        found_rrset = domain->second.find(RRType::NSEC());
                        assert(found_rrset != domain->second.end());
765
                        return (FindResult(NXRRSET,
766
                                           substituteWild(
767
                                               *found_rrset->second,
768
769
770
                                               new_name),
                                           RESULT_WILDCARD |
                                           RESULT_NSEC_SIGNED));
771
772
                    }
                } else {
773
                    // This is empty non terminal name case on wildcard.
774
775
                    const Name empty_name = Name("*").concatenate(wild_suffix);
                    if (use_nsec3_) {
776
777
778
                        return (FindResult(NXRRSET, RRsetPtr(),
                                           RESULT_WILDCARD |
                                           RESULT_NSEC3_SIGNED));
779
                    }
780
                    for (Domains::reverse_iterator it = domains_.rbegin();
781
782
783
                         it != domains_.rend();
                         ++it) {
                        RRsetStore::const_iterator nsec_it;
784
                        if ((*it).first < empty_name &&
785
786
                            (nsec_it = (*it).second.find(RRType::NSEC()))
                            != (*it).second.end()) {
787
788
789
                            return (FindResult(NXRRSET, (*nsec_it).second,
                                               RESULT_WILDCARD |
                                               RESULT_NSEC_SIGNED));
790
                        }
791
                    }
792
                }
793
                return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
794
795
             }
        }
796
797
798
799
800
801
802
803
        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());
804
805
806
807
808
            return (FindResult(CNAME,
                               substituteWild(*found_rrset->second, name),
                               RESULT_WILDCARD |
                               (use_nsec3_ ? RESULT_NSEC3_SIGNED :
                                RESULT_NSEC_SIGNED)));
809
        }
810
811
    }

812
    // This is an NXDOMAIN case.
813
814
815
816
817
818
819
    // 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) {
820
        if (use_nsec3_) {
821
            return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
822
823
        }

824
825
826
827
828
829
        // Emulate a broken DataSourceClient for some special names.
        if (nsec_result_ && nsec_name_ == name) {
            return (*nsec_result_);
        }

        // Normal case
830
831
832
        // XXX: some older g++ complains about operator!= if we use
        // const_reverse_iterator
        for (Domains::reverse_iterator it = domains_.rbegin();
833
834
835
836
837
838
             it != domains_.rend();
             ++it) {
            RRsetStore::const_iterator nsec_it;
            if ((*it).first < name &&
                (nsec_it = (*it).second.find(RRType::NSEC()))
                != (*it).second.end()) {
839
840
                return (FindResult(NXDOMAIN, (*nsec_it).second,
                                   RESULT_NSEC_SIGNED));
841
842
843
            }
        }
    }
844
    return (FindResult(NXDOMAIN, RRsetPtr()));
845
846
}

847
848
849
850
851
class QueryTest : public ::testing::Test {
protected:
    QueryTest() :
        qname(Name("www.example.com")), qclass(RRClass::IN()),
        qtype(RRType::A()), response(Message::RENDER),
852
853
854
855
856
857
858
859
        qid(response.getQid()), query_code(Opcode::QUERY().getCode()),
        ns_addrs_and_sig_txt(string(ns_addrs_txt) +
                             "glue.delegation.example.com. 3600 IN RRSIG " +
                             getCommonRRSIGText("A") + "\n" +
                             "glue.delegation.example.com. 3600 IN RRSIG " +
                             getCommonRRSIGText("AAAA") + "\n" +
                             "noglue.example.com. 3600 IN RRSIG " +
                             getCommonRRSIGText("A"))
860
861
    {
        response.setRcode(Rcode::NOERROR());
862
863
        response.setOpcode(Opcode::QUERY());
        // create and add a matching zone.
864
        mock_finder = new MockZoneFinder();
865
        memory_client.addZone(ZoneFinderPtr(mock_finder));
866
    }
867
    MockZoneFinder* mock_finder;
868
869
870
871
    // 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.
872
    InMemoryClient memory_client;
873
874
875
876
    const Name qname;
    const RRClass qclass;
    const RRType qtype;
    Message response;
877
878
    const qid_t qid;
    const uint16_t query_code;
879
    const string ns_addrs_and_sig_txt; // convenient shortcut
880
881
};

882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
// 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
903
904
                    response.endSection(Message::SECTION_ANSWER),
                    check_origin);
905
906
907
908
909
910
911
912
913
914
915
916
917
918
    }
    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));
    }
}

919
TEST_F(QueryTest, noZone) {
920
    // There's no zone in the memory datasource.  So the response should have
921
    // REFUSED.
922
923
    InMemoryClient empty_memory_client;
    Query nozone_query(empty_memory_client, qname, qtype, response);
924
    EXPECT_NO_THROW(nozone_query.process());
925
    EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
926
927
}

928
TEST_F(QueryTest, exactMatch) {
929
    Query query(memory_client, qname, qtype, response);
Jerry's avatar
Jerry committed
930
    EXPECT_NO_THROW(query.process());
931
    // find match rrset
932
933
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                  www_a_txt, zone_ns_txt, ns_addrs_txt);
934
}
935

936
937
938
939
940
941
942
943
944
945
946
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);
}

947
948
949
950
951
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
952
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
953
                  (www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
954
                                           "A 5 3 3600 20000101000000 "
955
956
957
958
959
960
                                           "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")).
961
962
                  c_str(),
                  ns_addrs_and_sig_txt.c_str());
963
964
}

965
966
967
TEST_F(QueryTest, exactAddrMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
968
    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
969
                          response).process());
970

971
972
973
974
    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");
975
976
}

977
978
979
TEST_F(QueryTest, apexNSMatch) {
    // find match rrset, omit authority data which has already been provided
    // in the answer section from the authority section.
980
    EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::NS(),
981
                          response).process());
982

983
984
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
                  zone_ns_txt, NULL, ns_addrs_txt);
985
986
}

chenzhengzhang's avatar
chenzhengzhang committed
987
// test type any query logic
988
989
990
TEST_F(QueryTest, exactAnyMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
991
    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"),
992
                          RRType::ANY(), response).process());
993

994
995
996
    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(),
997
998
999
                  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");
1000
1001
}

chenzhengzhang's avatar
chenzhengzhang committed
1002
1003
1004
TEST_F(QueryTest, apexAnyMatch) {
    // find match rrset, omit additional data which has already been provided
    // in the answer section from the additional.
1005
    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
1006
                          RRType::ANY(), response).process());
1007
1008
1009
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                  (string(soa_txt) + string(zone_ns_txt) +
                   string(nsec_apex_txt)).c_str(),
1010
                  NULL, ns_addrs_txt, mock_finder->getOrigin());
1011
1012
1013
}

TEST_F(QueryTest, mxANYMatch) {
1014
    EXPECT_NO_THROW(Query(memory_client, Name("mx.example.com"),
1015
                          RRType::ANY(), response).process());
1016
1017
    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 3, 4,
                  (string(mx_txt) + string(nsec_mx_txt)).c_str(), zone_ns_txt,
1018
                  (string(ns_addrs_txt) + string(www_a_txt)).c_str());
chenzhengzhang's avatar
chenzhengzhang committed
1019
1020
1021
}

TEST_F(QueryTest, glueANYMatch) {
1022
    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
1023
1024
1025
1026
1027
1028
                          RRType::ANY(), response).process());
    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                  NULL, delegation_txt, ns_addrs_txt);
}

TEST_F(QueryTest, nodomainANY) {
1029
    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"),
chenzhengzhang's avatar
chenzhengzhang committed
1030
1031
                          RRType::ANY(), response).process());
    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
1032
                  NULL, soa_txt, NULL, mock_finder->getOrigin());
chenzhengzhang's avatar
chenzhengzhang committed
1033
1034
}

1035
// This tests that when we need to look up Zone's apex NS records for
Jerry's avatar
Jerry committed
1036
1037
1038
// authoritative answer, and there is no apex NS records. It should
// throw in that case.
TEST_F(QueryTest, noApexNS) {
1039
    // Disable apex NS record
1040
    mock_finder->setApexNSFlag(false);
1041

1042
    EXPECT_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
1043
1044
                       response).process(), Query::NoApexNS);
    // We don't look into the response, as it threw
Jerry's avatar
Jerry committed
1045
1046
}

1047
TEST_F(QueryTest, delegation) {
1048
    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
1049
                          qtype, response).process());
1050

1051
1052
    responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                  NULL, delegation_txt, ns_addrs_txt);
1053
}
1054

1055
TEST_F(QueryTest, secureDelegation) {
Jelte Jansen's avatar
Jelte Jansen committed
1056
1057
    EXPECT_NO_THROW(Query(memory_client,
                          Name("foo.signed-delegation.example.com"),
1058
1059
1060
                          qtype, response, true).process());

    // Should now contain RRSIG and DS record as well.
1061
    responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
1062
                  NULL,
1063
1064
1065
                  (string(signed_delegation_txt) +
                   string(signed_delegation_ds_txt) +
                   string("signed-delegation.example.com. 3600 IN RRSIG ") +
1066
                   getCommonRRSIGText("DS")).c_str(),