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

// $Id$

17
18
#include "config.h"

19
#include <cassert>
Evan Hunt's avatar
Evan Hunt committed
20
#include <iomanip>
21
22
23
#include <iostream>
#include <vector>

JINMEI Tatuya's avatar
JINMEI Tatuya committed
24
#include <boost/shared_ptr.hpp>
25
26
#include <boost/foreach.hpp>

Evan Hunt's avatar
Evan Hunt committed
27
28
#include <dns/base32.h>
#include <dns/buffer.h>
29
#include <dns/message.h>
30
#include <dns/name.h>
31
#include <dns/rdataclass.h>
32
#include <dns/rrset.h>
33
#include <dns/rrsetlist.h>
Evan Hunt's avatar
Evan Hunt committed
34
#include <dns/sha1.h>
35

36
#include <cc/data.h>
37
38

#include "data_source.h"
39
#include "query.h"
40

41
42
43
44
45
46
#define RETERR(x) do { \
                      DataSrc::Result r = (x); \
                      if (r != DataSrc::SUCCESS) \
                          return (r); \
                      } while (0)

47
48
49
50
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;

51
namespace isc {
52
53
namespace auth {

JINMEI Tatuya's avatar
JINMEI Tatuya committed
54
55
typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
56
namespace {
57
58
// Add a task to the query task queue to look up additional data
// (i.e., address records for the names included in NS or MX records)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
59
void
60
61
62
63
64
65
66
67
getAdditional(Query& q, RRsetPtr rrset) {
    if (!q.wantAdditional()) {
        return;
    }

    RdataIteratorPtr it = rrset->getRdataIterator();
    for (it->first(); !it->isLast(); it->next()) {
        const Rdata& rd(it->getCurrent());
68
69
        QueryTaskPtr newtask = QueryTaskPtr();

70
71
72
        if (rrset->getType() == RRType::NS()) {
            const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);

73
74
75
76
            newtask = QueryTaskPtr(new QueryTask(ns.getNSName(), q.qclass(),
                                                 Section::ADDITIONAL(),
                                                 QueryTask::GLUE_QUERY,
                                                 QueryTask::GETADDITIONAL)); 
77
78
        } else if (rrset->getType() == RRType::MX()) {
            const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
79
80
81
82
            newtask = QueryTaskPtr(new QueryTask(mx.getMXName(), q.qclass(),
                                                 Section::ADDITIONAL(),
                                                 QueryTask::NOGLUE_QUERY,
                                                 QueryTask::GETADDITIONAL)); 
83
        }
84
85
        if (newtask) {
            q.tasks().push(newtask);
86
87
88
89
90
91
        }
    }
}

// Synthesize a CNAME answer, for the benefit of clients that don't
// understand DNAME
JINMEI Tatuya's avatar
JINMEI Tatuya committed
92
void
93
synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
94
    RdataIteratorPtr it = rrset->getRdataIterator();
95
96
97
98
99
100
101
102
103
104
105
106

    // More than one DNAME RR in the RRset is illegal, so we only have
    // to process the first one.
    it->first();
    if (it->isLast()) {
        return;
    }

    const Rdata& rd(it->getCurrent());
    const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
    const Name& dname_target(dname.getDname());

107
108
109
110
111
112
113
114
115
116
    RRsetPtr cname(new RRset(task->qname, task->qclass, RRType::CNAME(),
                             rrset->getTTL()));

    const int qnlen = task->qname.getLabelCount();
    const int dnlen = rrset->getName().getLabelCount();
    assert(qnlen > dnlen);
    const Name& prefix(task->qname.split(0, qnlen - dnlen));
    cname->addRdata(generic::CNAME(prefix.concatenate(dname_target)));

    target.addRRset(cname);
117
118
119
120
}

// Add a task to the query task queue to look up the data pointed
// to by a CNAME record
JINMEI Tatuya's avatar
JINMEI Tatuya committed
121
122
void
chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
123
    RdataIteratorPtr it = rrset->getRdataIterator();
124
125
126
127
128
129
130
131

    // More than one CNAME RR in the RRset is illegal, so we only have
    // to process the first one.
    it->first();
    if (it->isLast()) {
        return;
    }

132
133
134
135
    if (q.tooMany()) {
        return;
    }

136
137
138
139
140
141
142
    q.tasks().push(QueryTaskPtr(
                       new QueryTask(dynamic_cast<const generic::CNAME&>
                                     (it->getCurrent()).getCname(),
                                     task->qclass,
                                     task->qtype,
                                     Section::ANSWER(),
                                     QueryTask::FOLLOWCNAME)));
143
144
145
}

// Perform the query specified in a QueryTask object
JINMEI Tatuya's avatar
JINMEI Tatuya committed
146
DataSrc::Result
147
doQueryTask(const DataSrc* ds, const Name* zonename, QueryTask& task,
148
            RRsetList& target)
149
{
150
151
    switch (task.op) {
    case QueryTask::AUTH_QUERY:
152
        return (ds->findRRset(task.qname, task.qclass, task.qtype,
153
                              target, task.flags, zonename));
154
155

    case QueryTask::SIMPLE_QUERY:
156
        return (ds->findExactRRset(task.qname, task.qclass, task.qtype,
157
                                   target, task.flags, zonename));
158
159
160

    case QueryTask::GLUE_QUERY:
    case QueryTask::NOGLUE_QUERY:
161
        return (ds->findAddrs(task.qname, task.qclass, target,
162
                              task.flags, zonename));
163
164

    case QueryTask::REF_QUERY:
165
        return (ds->findReferral(task.qname, task.qclass, target,
166
                                 task.flags, zonename));
167
    }
168

169
170
171
172
173
    // Not reached
    return (DataSrc::ERROR);
}

// Copy referral information into the authority section of a message
JINMEI Tatuya's avatar
JINMEI Tatuya committed
174
175
inline void
copyAuth(Query& q, RRsetList& auth) {
176
177
178
179
    BOOST_FOREACH(RRsetPtr rrset, auth) {
        if (rrset->getType() == RRType::DNAME()) {
            continue;
        }
180
181
182
        if (rrset->getType() == RRType::DS() && !q.wantDnssec()) {
            continue;
        }
183
        q.message().addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
184
185
186
        getAdditional(q, rrset);
    }
}
187

188
// Query for referrals (i.e., NS/DS or DNAME) at a given name
JINMEI Tatuya's avatar
JINMEI Tatuya committed
189
inline bool
190
191
refQuery(const Name& name, Query& q, const DataSrc* ds, const Name* zonename,
         RRsetList& target)
192
{
193
    QueryTask newtask(name, q.qclass(), QueryTask::REF_QUERY);
194

195
    if (doQueryTask(ds, zonename, newtask, target) != DataSrc::SUCCESS) {
196
        // Lookup failed
197
198
199
200
        return (false);
    }
    
    // Referral bit is expected, so clear it when checking flags
201
    if ((newtask.flags & ~DataSrc::REFERRAL) != 0) {
202
203
204
205
206
207
208
209
        return (false);
    }

    return (true);
}

// Match downward, from the zone apex to the query name, looking for
// referrals.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
210
inline bool
211
212
hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
              QueryTaskPtr task)
213
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
214
215
    const int nlen = task->qname.getLabelCount();
    const int diff = nlen - zonename->getLabelCount();
216
    if (diff > 1) {
217
        bool found = false;
218
        RRsetList ref;
219
220
        for (int i = diff; i > 1; --i) {
            const Name sub(task->qname.split(i - 1, nlen - i));
221
            if (refQuery(sub, q, ds, zonename, ref)) {
222
223
224
225
226
227
228
                found = true;
                break;
            }
        }

        // Found a referral while getting additional data
        // for something other than NS; we skip it.
229
        if (found && task->op == QueryTask::NOGLUE_QUERY) {
230
231
232
233
234
235
            return (true);
        }

        // Found a referral while getting answer data;
        // send a delegation.
        if (found) {
236
237
            RRsetPtr r = ref.findRRset(RRType::DNAME(), q.qclass());
            if (r != NULL) {
238
                RRsetList syn;
239
240
                q.message().addRRset(Section::ANSWER(), r, q.wantDnssec());
                q.message().setHeaderFlag(MessageFlag::AA());
241
                synthesizeCname(task, r, syn);
242
                if (syn.size() == 1) {
243
                    q.message().addRRset(Section::ANSWER(),
244
245
                                         syn.findRRset(RRType::CNAME(),
                                                       q.qclass()),
246
                                         q.wantDnssec());
247
248
                    chaseCname(q, task, syn.findRRset(RRType::CNAME(),
                                                      q.qclass()));
249
250
251
252
253
254
255
256
257
258
259
260
                    return (true);
                }
            }

            copyAuth(q, ref);
            return (true);
        }
    }

    // We appear to have authoritative data; set the header
    // flag.  (We may clear it later if we find a referral
    // at the actual qname node.)
261
262
    if (task->op == QueryTask::AUTH_QUERY &&
        task->state == QueryTask::GETANSWER) {
263
        q.message().setHeaderFlag(MessageFlag::AA());
264
265
266
267
268
    }

    return (false);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
269
inline DataSrc::Result
Evan Hunt's avatar
Evan Hunt committed
270
271
272
273
274
275
addSOA(Query& q, const Name* zonename, const DataSrc* ds) {
    Message& m = q.message();
    RRsetList soa;

    QueryTask newtask(*zonename, q.qclass(), RRType::SOA(),
                      QueryTask::SIMPLE_QUERY);
276
    RETERR(doQueryTask(ds, zonename, newtask, soa));
277
    if (newtask.flags != 0) {
Evan Hunt's avatar
Evan Hunt committed
278
279
280
        return (DataSrc::ERROR);
    }

281
282
    m.addRRset(Section::AUTHORITY(), soa.findRRset(RRType::SOA(), q.qclass()),
               q.wantDnssec());
Evan Hunt's avatar
Evan Hunt committed
283
284
285
    return (DataSrc::SUCCESS);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
286
inline DataSrc::Result
Evan Hunt's avatar
Evan Hunt committed
287
288
289
290
291
292
293
294
addNSEC(Query& q, const QueryTaskPtr task, const Name& name,
        const Name& zonename, const DataSrc* ds)
{
    RRsetList nsec;
    Message& m = q.message();

    QueryTask newtask(name, task->qclass, RRType::NSEC(),
                      QueryTask::SIMPLE_QUERY); 
295
    RETERR(doQueryTask(ds, &zonename, newtask, nsec));
Evan Hunt's avatar
Evan Hunt committed
296
    if (newtask.flags == 0) {
297
298
        m.addRRset(Section::AUTHORITY(), nsec.findRRset(RRType::NSEC(),
                                                        q.qclass()), true);
Evan Hunt's avatar
Evan Hunt committed
299
300
301
302
303
    }

    return (DataSrc::SUCCESS);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
304
inline DataSrc::Result
305
306
getNsec3(const DataSrc* ds, const Name& zonename, const RRClass& qclass,
         string& hash, RRsetPtr& target)
Evan Hunt's avatar
Evan Hunt committed
307
{
308
    RRsetList rl;
309
    RETERR(ds->findCoveringNSEC3(zonename, hash, rl));
310
    target = rl.findRRset(RRType::NSEC3(), qclass);
Evan Hunt's avatar
Evan Hunt committed
311
312
313
    return (DataSrc::SUCCESS);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
314
ConstNsec3ParamPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
315
getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) {
Evan Hunt's avatar
Evan Hunt committed
316
317
318
319
320
    DataSrc::Result result;
    RRsetList nsec3param;

    QueryTask newtask(zonename, q.qclass(), RRType::NSEC3PARAM(),
                      QueryTask::SIMPLE_QUERY); 
321
    result = doQueryTask(ds, &zonename, newtask, nsec3param);
Evan Hunt's avatar
Evan Hunt committed
322
323
    newtask.flags &= ~DataSrc::REFERRAL;
    if (result != DataSrc::SUCCESS || newtask.flags != 0) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
324
        return (ConstNsec3ParamPtr());
Evan Hunt's avatar
Evan Hunt committed
325
326
    }

327
    RRsetPtr rrset = nsec3param.findRRset(RRType::NSEC3PARAM(), q.qclass());
Evan Hunt's avatar
Evan Hunt committed
328
    if (!rrset) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
329
        return (ConstNsec3ParamPtr());
Evan Hunt's avatar
Evan Hunt committed
330
331
332
333
334
335
336
    }

    // XXX: currently only one NSEC3 chain per zone is supported;
    // we will need to revisit this.
    RdataIteratorPtr it = rrset->getRdataIterator();
    it->first();
    if (it->isLast()) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
337
        return (ConstNsec3ParamPtr());
Evan Hunt's avatar
Evan Hunt committed
338
339
340
341
    }

    const generic::NSEC3PARAM& np =
            dynamic_cast<const generic::NSEC3PARAM&>(it->getCurrent());
JINMEI Tatuya's avatar
JINMEI Tatuya committed
342
343
344
    return (ConstNsec3ParamPtr(new Nsec3Param(np.getHashalg(), np.getFlags(),
                                              np.getIterations(),
                                              np.getSalt())));
Evan Hunt's avatar
Evan Hunt committed
345
346
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
347
inline DataSrc::Result
348
349
proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
        const Name& zonename, const bool wildcard)
Evan Hunt's avatar
Evan Hunt committed
350
{
351
    Message& m = q.message();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
352
353
    ConstNsec3ParamPtr nsec3 = getNsec3Param(q, ds, zonename);
    if (nsec3 != NULL) {
354
355
        // Attach the NSEC3 record covering the QNAME
        RRsetPtr rrset;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
356
        string hash1(nsec3->getHash(task->qname));
357
        RETERR(getNsec3(ds, zonename, q.qclass(), hash1, rrset));
358
359
360
361
362
363
364
365
366
        m.addRRset(Section::AUTHORITY(), rrset, true);

        // If this is an NXRRSET or NOERROR/NODATA, we're done
        if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0) {
            return (DataSrc::SUCCESS);
        }

        // Find the closest provable enclosing name for QNAME
        Name enclosure(zonename);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
367
368
369
        const int nlen = task->qname.getLabelCount();
        const int diff = nlen - enclosure.getLabelCount();
        string hash2;
370
371
372
373
374
375
376
377
        for (int i = 1; i <= diff; ++i) {
            enclosure = task->qname.split(i, nlen - i);
            string nodehash(nsec3->getHash(enclosure));
            if (nodehash == hash1) {
                break;
            }
            hash2 = nodehash;
            RRsetList rl;
Evan Hunt's avatar
Evan Hunt committed
378

379
380
            // hash2 will be overwritten with the actual hash found;
            // we don't want to use one until we find an exact match
381
            RETERR(getNsec3(ds, zonename, q.qclass(), hash2, rrset));
382
383
384
            if (hash2 == nodehash) {
                m.addRRset(Section::AUTHORITY(), rrset, true);
                break;
Evan Hunt's avatar
Evan Hunt committed
385
386
387
            }
        }

388
389
390
391
392
393
394
        // If we are processing a wildcard answer, we're done.
        if (wildcard) {
            return (DataSrc::SUCCESS);
        }

        // Otherwise, there is no wildcard record, so we must add a
        // covering NSEC3 to prove that it doesn't exist.
395
        string hash3(nsec3->getHash(Name("*").concatenate(enclosure)));
396
        RETERR(getNsec3(ds, zonename, q.qclass(), hash3, rrset));
397
        if (hash3 != hash1 && hash3 != hash2) {
398
            m.addRRset(Section::AUTHORITY(), rrset, true);
Evan Hunt's avatar
Evan Hunt committed
399
400
401
        }
    } else {
        Name nsecname(task->qname);
402
        if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) {
403
            ds->findPreviousName(task->qname, nsecname, &zonename);
Evan Hunt's avatar
Evan Hunt committed
404
405
        }

406
        RETERR(addNSEC(q, task, nsecname, zonename, ds));
Evan Hunt's avatar
Evan Hunt committed
407
408
409
410
411
412
        if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
            nsecname == zonename)
        {
            return (DataSrc::SUCCESS);
        }

413
414
415
416
417
418
419
        // If we are processing a wildcard answer, we're done.
        if (wildcard) {
            return (DataSrc::SUCCESS);
        }

        // Otherwise, there is no wildcard record, so we must add an
        // NSEC for the zone to prove the wildcard doesn't exist.
420
        RETERR(addNSEC(q, task, zonename, zonename, ds));
Evan Hunt's avatar
Evan Hunt committed
421
422
423
424
425
    }

    return (DataSrc::SUCCESS);
}

426
// Attempt a wildcard lookup
JINMEI Tatuya's avatar
JINMEI Tatuya committed
427
inline DataSrc::Result
428
429
tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
            const Name* zonename, bool& found)
430
{
431
432
433
434
    Message& m = q.message();
    DataSrc::Result result;
    found = false;

435
436
437
    if ((task->flags & DataSrc::NAME_NOT_FOUND) == 0 || 
        (task->state != QueryTask::GETANSWER &&
         task->state != QueryTask::FOLLOWCNAME)) {
438
439
440
        return (DataSrc::SUCCESS);
    }

441
442
    const int nlen = task->qname.getLabelCount();
    const int diff = nlen - zonename->getLabelCount();
443
444
445
446
447
    if (diff < 1) {
        return (DataSrc::SUCCESS);
    }

    RRsetList wild;
448
    const Name star("*");
449
    bool cname = false;
450

451
    for (int i = 1; i <= diff; ++i) {
452
        const Name& wname(star.concatenate(task->qname.split(i, nlen - i)));
453
454
        QueryTask newtask(wname, task->qclass, task->qtype, Section::ANSWER(),
                          QueryTask::AUTH_QUERY); 
455
        result = doQueryTask(ds, zonename, newtask, wild);
456
        if (result == DataSrc::SUCCESS) {
457
            if (newtask.flags == 0) {
458
459
                task->flags &= ~DataSrc::NAME_NOT_FOUND;
                task->flags &= ~DataSrc::TYPE_NOT_FOUND;
460
461
                found = true;
                break;
462
            } else if ((newtask.flags & DataSrc::CNAME_FOUND) != 0) {
463
464
                task->flags &= ~DataSrc::NAME_NOT_FOUND;
                task->flags &= ~DataSrc::TYPE_NOT_FOUND;
465
466
467
468
                task->flags |= DataSrc::CNAME_FOUND;
                found = true;
                cname = true;
                break;
469
            } else if ((newtask.flags & DataSrc::TYPE_NOT_FOUND) != 0) {
470
                task->flags &= ~DataSrc::NAME_NOT_FOUND;
471
472
473
                task->flags |= DataSrc::TYPE_NOT_FOUND;
                break;
            }
474
475
476
        }
    }

477
    // A wildcard was found.
478
    if (found) {
479
        // Prove the nonexistence of the name we were looking for
480
481
482
483
484
485
        if (q.wantDnssec()) {
            result = proveNX(q, task, ds, *zonename, true);
            if (result != DataSrc::SUCCESS) {
                m.setRcode(Rcode::SERVFAIL());
                return (DataSrc::ERROR);
            }
486
487
        }

488
489
490
        // Add the data to the answer section (but with the name changed to
        // match the qname), and then continue as if this were a normal
        // answer: if a CNAME, chase the target, otherwise add authority.
491
        if (cname) {
492
493
            RRsetPtr rrset = wild.findRRset(RRType::CNAME(), q.qclass());
            if (rrset != NULL) {
494
                rrset->setName(task->qname);
495
496
497
498
499
                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
                chaseCname(q, task, rrset);
            }
        } else {
            BOOST_FOREACH (RRsetPtr rrset, wild) {
500
                rrset->setName(task->qname);
501
502
503
504
                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
            }

            RRsetList auth;
505
            if (! refQuery(*zonename, q, ds, zonename, auth)) {
506
507
508
509
510
511
512
513
514
                return (DataSrc::ERROR);
            }

            copyAuth(q, auth);
        }
    }

    return (DataSrc::SUCCESS);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
515
} // end of anonymous namespace
516
517
518
519
520

//
// doQuery: Processes a query.
// 
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
521
DataSrc::doQuery(Query& q) {
522
523
524
525
526
    Message& m = q.message();
    vector<RRsetPtr> additional;

    m.clearHeaderFlag(MessageFlag::AA());
    while (!q.tasks().empty()) {
527
        QueryTaskPtr task = q.tasks().front();
528
529
        q.tasks().pop();

Evan Hunt's avatar
Evan Hunt committed
530
531
532
533
534
535
        // Can't query directly for RRSIG.
        if (task->qtype == RRType::RRSIG()) {
            m.setRcode(Rcode::REFUSED());
            return;
        }

536
        // These task types should never be on the task queue.
537
538
        if (task->op == QueryTask::SIMPLE_QUERY ||
            task->op == QueryTask::REF_QUERY) {
539
540
541
542
543
544
545
            m.setRcode(Rcode::SERVFAIL());
            return;
        }

        // Find the closest enclosing zone for which we are authoritative,
        // and the concrete data source which is authoritative for it.
        // (Note that RRtype DS queries need to go to the parent.)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
546
        const int nlabels = task->qname.getLabelCount() - 1;
547
        NameMatch match(nlabels != 0 && task->qtype == RRType::DS() ?
548
549
                        task->qname.split(1, task->qname.getLabelCount() - 1) :
                        task->qname);
550
        findClosestEnclosure(match, task->qclass);
551
        const DataSrc* datasource = match.bestDataSrc();
552
553
554
555
        const Name* zonename = match.closestName();

        assert((datasource == NULL && zonename == NULL) ||
               (datasource != NULL && zonename != NULL));
556

557
558
559
560
        RRsetList data;
        Result result = SUCCESS;

        if (datasource) {
561
562
563
            // For these query task types, if there is more than
            // one level between the zone name and qname, we need to
            // check the intermediate nodes for referrals.
564
565
            if ((task->op == QueryTask::AUTH_QUERY ||
                 task->op == QueryTask::NOGLUE_QUERY) &&
566
                hasDelegation(datasource, zonename, q, task)) {
567
568
569
                continue;
            }

570
            result = doQueryTask(datasource, zonename, *task, data);
571
572
573
574
575
576
577
            if (result != SUCCESS) {
                m.setRcode(Rcode::SERVFAIL());
                return;
            }

            // Query found a referral; let's find out if that was expected--
            // i.e., if an NS was at the zone apex, or if we were querying
578
            // specifically for, and found, a DS, NSEC, or DNAME record.
579
            if ((task->flags & REFERRAL) != 0 &&
580
                (zonename->getLabelCount() == task->qname.getLabelCount() ||
581
582
583
584
                 ((task->qtype == RRType::NSEC() ||
                   task->qtype == RRType::DS() ||
                   task->qtype == RRType::DNAME()) &&
                  data.findRRset(task->qtype, task->qclass)))) {
585
                task->flags &= ~REFERRAL;
586
            }
587
        } else {
588
            task->flags = NO_SUCH_ZONE;
589
590
591
592
593
594
595
596
597

            // No such zone.  If we're chasing cnames or adding additional
            // data, that's okay, but if doing an original query, return
            // REFUSED.
            if (task->state == QueryTask::GETANSWER) {
                m.setRcode(Rcode::REFUSED());
                return;
            }
            continue;
598
599
        }

600
        if (result == SUCCESS && task->flags == 0) {
601
            bool have_ns = false, need_auth = false;
602
            switch (task->state) {
603
604
605
            case QueryTask::GETANSWER:
            case QueryTask::FOLLOWCNAME:
                BOOST_FOREACH(RRsetPtr rrset, data) {
606
                    m.addRRset(task->section, rrset, q.wantDnssec());
607
608
609
610
611
612
613
                    if (q.tasks().empty()) {
                        need_auth = true;
                    }
                    getAdditional(q, rrset);
                    if (rrset->getType() == RRType::NS()) {
                        have_ns = true;
                    }
614
                }
615
616
617
618
619
620
                q.setStatus(Query::ANSWERED);
                if (need_auth && !have_ns) {
                    // Data found, no additional processing needed.
                    // Add the NS records for the enclosing zone to
                    // the authority section.
                    RRsetList auth;
621
                    if (!refQuery(*zonename, q, datasource, zonename, auth)) {
622
623
624
                        m.setRcode(Rcode::SERVFAIL());
                        return;
                    }
625

626
                    copyAuth(q, auth);
627
                }
628
                continue;
629

630
631
632
633
634
635
636
637
638
639
640
            case QueryTask::GETADDITIONAL:
                // Got additional data.  Do not add it to the message
                // yet; instead store it and copy it in at the end
                // (this allow RRSIGs to be omitted if necessary).
                BOOST_FOREACH(RRsetPtr rrset, data) {
                    if (q.status() == Query::ANSWERED &&
                        rrset->getName() == q.qname() &&
                        rrset->getType() == q.qtype()) {
                        continue;
                    }
                    additional.push_back(rrset);
641
642
643
                }
                continue;

644
            default:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
645
                isc_throw (Unexpected, "unexpected query state");
646
647
648
649
            }
        } else if (result == ERROR || result == NOT_IMPLEMENTED) {
            m.setRcode(Rcode::SERVFAIL());
            return;
650
        } else if ((task->flags & CNAME_FOUND) != 0) {
651
652
            // The qname node contains a CNAME.  Add a new task to the
            // queue to look up its target.
653
654
            RRsetPtr rrset = data.findRRset(RRType::CNAME(), q.qclass());
            if (rrset != NULL) {
655
                m.addRRset(task->section, rrset, q.wantDnssec());
656
657
658
                chaseCname(q, task, rrset);
            }
            continue;
659
        } else if ((task->flags & REFERRAL) != 0) {
660
            // The qname node contains an out-of-zone referral.
661
            if (task->state == QueryTask::GETANSWER) {
662
663
                RRsetList auth;
                m.clearHeaderFlag(MessageFlag::AA());
664
                if (!refQuery(task->qname, q, datasource, zonename, auth)) {
665
666
                    m.setRcode(Rcode::SERVFAIL());
                    return;
667
                }
668
                BOOST_FOREACH (RRsetPtr rrset, auth) {
669
670
671
                    if (rrset->getType() == RRType::NS()) {
                        m.addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
                    } else if (rrset->getType() == task->qtype) {
672
                        m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
673
674
675
                    } else if (rrset->getType() == RRType::DS() &&
                               q.wantDnssec()) {
                        m.addRRset(Section::AUTHORITY(), rrset, true);
676
677
678
679
680
                    }
                    getAdditional(q, rrset);
                }
            } 
            continue;
681
        } else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
682
683
684
685
686
            // No data found at this qname/qtype.
            // If we were looking for answer data, not additional,
            // and the name was not found, we need to find out whether
            // there are any relevant wildcards.
            bool wildcard_found = false;
687
            result = tryWildcard(q, task, datasource, zonename, wildcard_found);
688
689
690
691
            if (result != SUCCESS) {
                m.setRcode(Rcode::SERVFAIL());
                return;
            }
692

693
            if (wildcard_found) {
694
                continue;
695
696
697
698
699
700
701
702
703
            }

            // If we've reached this point, there is definitely no answer.
            // If we were chasing cnames or adding additional data, that's
            // okay, but if we were doing an original query, reply with the
            // SOA in the authority section.  For NAME_NOT_FOUND, set
            // NXDOMAIN, and also add the previous NSEC to the authority
            // section.  For TYPE_NOT_FOUND, do not set an error rcode,
            // and send the current NSEC in the authority section.
704
            if (task->state == QueryTask::GETANSWER) {
705
                if ((task->flags & NAME_NOT_FOUND) != 0) {
706
                    m.setRcode(Rcode::NXDOMAIN());
707
708
                }

Evan Hunt's avatar
Evan Hunt committed
709
710
                result = addSOA(q, zonename, datasource);
                if (result != SUCCESS) {
711
                    m.setRcode(Rcode::SERVFAIL());
712
                    return;
713
                }
Evan Hunt's avatar
Evan Hunt committed
714
            }
715

Evan Hunt's avatar
Evan Hunt committed
716
717
            Name nsecname(task->qname);
            if ((task->flags & NAME_NOT_FOUND) != 0) {
718
                datasource->findPreviousName(task->qname, nsecname, zonename);
719
            }
720

721
            if (q.wantDnssec()) {
722
                result = proveNX(q, task, datasource, *zonename, false);
Evan Hunt's avatar
Evan Hunt committed
723
                if (result != DataSrc::SUCCESS) {
724
725
726
727
                    m.setRcode(Rcode::SERVFAIL());
                    return;
                }
            }
728

729
730
731
732
733
            return;
        } else {
            // Should never be reached!
            m.setRcode(Rcode::SERVFAIL());
            return;
734
735
736
        }
    }

737
738
739
740
741
742
743
    // We're done, so now copy in the additional data:
    // data first, then signatures.  (If we run out of
    // space, signatures in additional section are
    // optional.)
    BOOST_FOREACH(RRsetPtr rrset, additional) {
        m.addRRset(Section::ADDITIONAL(), rrset, false);
    }
744

745
746
747
748
749
750
751
752
    if (q.wantDnssec()) {
        BOOST_FOREACH(RRsetPtr rrset, additional) {
            if (rrset->getRRsig()) {
                m.addRRset(Section::ADDITIONAL(), rrset->getRRsig(), false);
            }
        }
    }
}
753

754
DataSrc::Result
755
DataSrc::findAddrs(const Name& qname, const RRClass& qclass,
756
757
                   RRsetList& target, uint32_t& flags,
                   const Name* zonename) const
758
759
760
761
762
{
    Result r;
    bool a = false, aaaa = false;

    flags = 0;
763
    r = findExactRRset(qname, qclass, RRType::A(), target, flags, zonename);
764
765
766
767
768
    if (r == SUCCESS && flags == 0) {
        a = true;
    }

    flags = 0;
769
    r = findExactRRset(qname, qclass, RRType::AAAA(), target, flags,
770
                       zonename);
771
772
773
774
775
776
777
778
779
780
781
782
783
784
    if (r == SUCCESS && flags == 0) {
        aaaa = true;
    }

    if (!a && !aaaa) {
        flags = TYPE_NOT_FOUND;
    } else {
        flags = 0;
    }

    return (SUCCESS);
}

DataSrc::Result
785
DataSrc::findReferral(const Name& qname, const RRClass& qclass,
786
787
                      RRsetList& target, uint32_t& flags,
                      const Name* zonename) const
788
789
790
791
792
{
    Result r;
    bool ns = false, ds = false, dname = false;

    flags = 0;
793
    r = findExactRRset(qname, qclass, RRType::NS(), target, flags, zonename);
794
795
796
797
798
799
800
    if (r == SUCCESS && flags == 0) {
        ns = true;
    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
        return (SUCCESS);
    }

    flags = 0;
801
    r = findExactRRset(qname, qclass, RRType::DS(), target, flags, zonename);
802
803
804
805
806
807
808
    if (r == SUCCESS && flags == 0) {
        ds = true;
    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
        return (SUCCESS);
    }

    flags = 0;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
809
    r = findExactRRset(qname, qclass, RRType::DNAME(), target, flags, zonename);
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
    if (r == SUCCESS && flags == 0) {
        dname = true;
    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
        return (SUCCESS);
    }

    if (!ns && !dname && !ds) {
        flags = TYPE_NOT_FOUND;
    } else {
        flags = 0;
    }

    return (SUCCESS);
}

void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
826
MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
827
    if (getClass() != RRClass::ANY() && data_src->getClass() != getClass()) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
828
        isc_throw(Unexpected, "class mismatch");
829
830
    }

831
    data_sources.push_back(data_src);
832
833
}

834
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
835
MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
836
837
838
839
840
841
842
843
844
845
    std::vector<ConstDataSrcPtr>::iterator it, itr;
    for (it = data_sources.begin(); it != data_sources.end(); it++) {
        if (*it == data_src) {
            itr = it;
        }
    }

    data_sources.erase(itr);
}

846
void
847
MetaDataSrc::findClosestEnclosure(NameMatch& match, const RRClass& qclass) const
848
{
849
850
    if (getClass() != qclass &&
        getClass() != RRClass::ANY() && qclass != RRClass::ANY()) {
851
852
853
        return;
    }

854
    BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
855
        data_src->findClosestEnclosure(match, qclass);
856
857
858
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
859
NameMatch::~NameMatch() {
860
861
862
863
    delete closest_name_;
}

void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
864
NameMatch::update(const DataSrc& new_source, const Name& container) {
865
866
867
868
869
870
871
872
    if (closest_name_ == NULL) {
        closest_name_ = new Name(container);
        best_source_ = &new_source;
        return;
    }

    if (container.compare(*closest_name_).getRelation() ==
        NameComparisonResult::SUBDOMAIN) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
873
        const Name* newname = new Name(container);
874
875
876
877
878
879
        delete closest_name_;
        closest_name_ = newname;
        best_source_ = &new_source;
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
880
Nsec3Param::Nsec3Param(const uint8_t a, const uint8_t f, const uint16_t i,
Evan Hunt's avatar
Evan Hunt committed
881
                       const std::vector<uint8_t>& s) :
Evan Hunt's avatar
Evan Hunt committed
882
    algorithm_(a), flags_(f), iterations_(i), salt_(s)
Evan Hunt's avatar
Evan Hunt committed
883
884
885
886
887
888
{}

string
Nsec3Param::getHash(const Name& name) const {
    OutputBuffer buf(0);
    name.toWire(buf);
889

Evan Hunt's avatar
Evan Hunt committed
890
    uint8_t digest[SHA1_HASHSIZE];
JINMEI Tatuya's avatar
JINMEI Tatuya committed
891
    const uint8_t* input = static_cast<const uint8_t*>(buf.getData());
892
    size_t inlength = buf.getLength();
893
    const uint8_t saltlen = salt_.size();
Evan Hunt's avatar
Evan Hunt committed
894

895
    int n = 0;
Evan Hunt's avatar
Evan Hunt committed
896
897
898
    SHA1Context sha;
    do {
        SHA1Reset(&sha);
899
        SHA1Input(&sha, input, inlength);
900
        SHA1Input(&sha, &salt_[0], saltlen);
Evan Hunt's avatar
Evan Hunt committed
901
        SHA1Result(&sha, digest);
902
        input = digest;
Evan Hunt's avatar
Evan Hunt committed
903
        inlength = SHA1_HASHSIZE;
Evan Hunt's avatar
Evan Hunt committed
904
    } while (n++ < iterations_);
Evan Hunt's avatar
Evan Hunt committed
905

906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
    return (encodeBase32(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
}

//
// The following methods are effectively empty, and their parameters are
// unused.  To silence compilers that warn unused function parameters,
// we specify a (compiler dependent) special keyword when available.
// It's defined in config.h, and to avoid including this header file from
// installed files we define the methods here.
//
DataSrc::Result
DataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM) {
    return NOT_IMPLEMENTED;
}

DataSrc::Result
MetaDataSrc::findRRset(const isc::dns::Name& qname UNUSED_PARAM,
                       const isc::dns::RRClass& qclass UNUSED_PARAM,
                       const isc::dns::RRType& qtype UNUSED_PARAM,
                       isc::dns::RRsetList& target UNUSED_PARAM,
                       uint32_t& flags UNUSED_PARAM,
                       const isc::dns::Name* zonename UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
}
Evan Hunt's avatar
Evan Hunt committed
931

932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
DataSrc::Result
MetaDataSrc::findExactRRset(const isc::dns::Name& qname UNUSED_PARAM,
                            const isc::dns::RRClass& qclass UNUSED_PARAM,
                            const isc::dns::RRType& qtype UNUSED_PARAM,
                            isc::dns::RRsetList& target UNUSED_PARAM,
                            uint32_t& flags UNUSED_PARAM,
                            const isc::dns::Name* zonename UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
}

DataSrc::Result
MetaDataSrc::findAddrs(const isc::dns::Name& qname UNUSED_PARAM,
                       const isc::dns::RRClass& qclass UNUSED_PARAM,
                       isc::dns::RRsetList& target UNUSED_PARAM,
                       uint32_t& flags UNUSED_PARAM,
                       const isc::dns::Name* zonename UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
}

DataSrc::Result
MetaDataSrc::findReferral(const isc::dns::Name& qname UNUSED_PARAM,
                          const isc::dns::RRClass& qclass UNUSED_PARAM,
                          isc::dns::RRsetList& target UNUSED_PARAM,
                          uint32_t& flags UNUSED_PARAM,
                          const isc::dns::Name* zonename UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
}

DataSrc::Result
MetaDataSrc::findPreviousName(const isc::dns::Name& qname UNUSED_PARAM,
                              isc::dns::Name& target UNUSED_PARAM,
                              const isc::dns::Name* zonename UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
}

DataSrc::Result
MetaDataSrc::findCoveringNSEC3(const isc::dns::Name& zonename UNUSED_PARAM,
                               std::string& hash UNUSED_PARAM,
                               isc::dns::RRsetList& target UNUSED_PARAM) const
{
    return (NOT_IMPLEMENTED);
Evan Hunt's avatar
Evan Hunt committed
977
978
}

979
980
}
}