query.cc 11 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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.

Michal Vaner's avatar
Michal Vaner committed
15
16
17
#include <vector>
#include <boost/foreach.hpp>

18
19
#include <dns/message.h>
#include <dns/rcode.h>
20
#include <dns/rdataclass.h>
21

22
#include <datasrc/client.h>
23
24
25
26
27

#include <auth/query.h>

using namespace isc::dns;
using namespace isc::datasrc;
28
using namespace isc::dns::rdata;
29
30
31

namespace isc {
namespace auth {
32

33
void
34
Query::getAdditional(ZoneFinder& zone, const RRset& rrset) const {
chenzhengzhang's avatar
chenzhengzhang committed
35
36
37
38
39
40
    RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
        const Rdata& rdata(rdata_iterator->getCurrent());
        if (rrset.getType() == RRType::NS()) {
            // Need to perform the search in the "GLUE OK" mode.
            const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
41
            findAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
chenzhengzhang's avatar
chenzhengzhang committed
42
43
44
        } else if (rrset.getType() == RRType::MX()) {
            const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
            findAddrs(zone, mx.getMXName());
45
        }
46
47
48
49
    }
}

void
50
Query::findAddrs(ZoneFinder& zone, const Name& qname,
51
                 const ZoneFinder::FindOptions options) const
52
{
53
54
55
56
57
58
    // Out of zone name
    NameComparisonResult result = zone.getOrigin().compare(qname);
    if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
        (result.getRelation() != NameComparisonResult::EQUAL))
        return;

59
60
61
62
63
64
65
66
    // Omit additional data which has already been provided in the answer
    // section from the additional.
    //
    // All the address rrset with the owner name of qname have been inserted
    // into ANSWER section.
    if (qname_ == qname && qtype_ == RRType::ANY())
        return;

67
    // Find A rrset
68
    if (qname_ != qname || qtype_ != RRType::A()) {
69
        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL,
70
            static_cast<ZoneFinder::FindOptions>(options | dnssec_opt_));
71
        if (a_result.code == ZoneFinder::SUCCESS) {
72
            response_.addRRset(Message::SECTION_ADDITIONAL,
73
                    boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
74
        }
75
    }
76

77
    // Find AAAA rrset
78
    if (qname_ != qname || qtype_ != RRType::AAAA()) {
79
        ZoneFinder::FindResult aaaa_result =
80
81
82
            zone.find(qname, RRType::AAAA(), NULL,
                      static_cast<ZoneFinder::FindOptions>(options |
                                                           dnssec_opt_));
83
        if (aaaa_result.code == ZoneFinder::SUCCESS) {
84
            response_.addRRset(Message::SECTION_ADDITIONAL,
85
86
                    boost::const_pointer_cast<RRset>(aaaa_result.rrset),
                    dnssec_);
87
        }
88
89
90
    }
}

Michal Vaner's avatar
Michal Vaner committed
91
void
92
Query::putSOA(ZoneFinder& zone) const {
93
    ZoneFinder::FindResult soa_result(zone.find(zone.getOrigin(),
94
        RRType::SOA(), NULL, dnssec_opt_));
95
    if (soa_result.code != ZoneFinder::SUCCESS) {
Michal Vaner's avatar
Michal Vaner committed
96
97
98
99
100
101
102
103
104
        isc_throw(NoSOA, "There's no SOA record in zone " <<
            zone.getOrigin().toText());
    } else {
        /*
         * FIXME:
         * The const-cast is wrong, but the Message interface seems
         * to insist.
         */
        response_.addRRset(Message::SECTION_AUTHORITY,
105
            boost::const_pointer_cast<RRset>(soa_result.rrset), dnssec_);
Michal Vaner's avatar
Michal Vaner committed
106
107
108
    }
}

109
void
110
Query::getAuthAdditional(ZoneFinder& zone) const {
111
    // Fill in authority and addtional sections.
112
    ZoneFinder::FindResult ns_result = zone.find(zone.getOrigin(),
113
114
                                                 RRType::NS(), NULL,
                                                 dnssec_opt_);
115
    // zone origin name should have NS records
116
    if (ns_result.code != ZoneFinder::SUCCESS) {
Jerry's avatar
Jerry committed
117
118
119
120
        isc_throw(NoApexNS, "There's no apex NS records in zone " <<
                zone.getOrigin().toText());
    } else {
        response_.addRRset(Message::SECTION_AUTHORITY,
121
            boost::const_pointer_cast<RRset>(ns_result.rrset), dnssec_);
122
        // Handle additional for authority section
Jerry's avatar
Jerry committed
123
124
        getAdditional(zone, *ns_result.rrset);
    }
125
126
}

127
128
void
Query::process() const {
Jerry's avatar
Jerry committed
129
    bool keep_doing = true;
130
    const bool qtype_is_any = (qtype_ == RRType::ANY());
chenzhengzhang's avatar
chenzhengzhang committed
131

Jerry's avatar
Jerry committed
132
    response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
133
134
    const DataSourceClient::FindResult result =
        datasrc_client_.findZone(qname_);
135

136
137
138
139
140
    // If we have no matching authoritative zone for the query name, return
    // REFUSED.  In short, this is to be compatible with BIND 9, but the
    // background discussion is not that simple.  See the relevant topic
    // at the BIND 10 developers's ML:
    // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
141
142
    if (result.code != result::SUCCESS &&
        result.code != result::PARTIALMATCH) {
143
        response_.setRcode(Rcode::REFUSED());
144
145
146
        return;
    }

Jerry's avatar
Jerry committed
147
148
    // Found a zone which is the nearest ancestor to QNAME, set the AA bit
    response_.setHeaderFlag(Message::HEADERFLAG_AA);
149
    response_.setRcode(Rcode::NOERROR());
150
151
    while (keep_doing) {
        keep_doing = false;
152
        std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
153
        const ZoneFinder::FindResult db_result(
154
155
            result.zone_finder->find(qname_, qtype_, target.get(),
                                     dnssec_opt_));
156
        switch (db_result.code) {
157
            case ZoneFinder::DNAME: {
158
159
                // First, put the dname into the answer
                response_.addRRset(Message::SECTION_ANSWER,
160
161
                    boost::const_pointer_cast<RRset>(db_result.rrset),
                    dnssec_);
162
163
164
                /*
                 * Empty DNAME should never get in, as it is impossible to
                 * create one in master file.
165
166
                 *
                 * FIXME: Other way to prevent this should be done
167
                 */
168
                assert(db_result.rrset->getRdataCount() > 0);
169
170
171
                // Get the data of DNAME
                const rdata::generic::DNAME& dname(
                    dynamic_cast<const rdata::generic::DNAME&>(
172
                    db_result.rrset->getRdataIterator()->getCurrent()));
173
                // The yet unmatched prefix dname
174
                const Name prefix(qname_.split(0, qname_.getLabelCount() -
175
176
177
                    db_result.rrset->getName().getLabelCount()));
                // If we put it together, will it be too long?
                // (The prefix contains trailing ., which will be removed
178
179
                if (prefix.getLength() - Name::ROOT_NAME().getLength() +
                    dname.getDname().getLength() > Name::MAX_WIRE) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
180
181
182
183
                    /*
                     * In case the synthesized name is too long, section 4.1
                     * of RFC 2672 mandates we return YXDOMAIN.
                     */
184
185
186
                    response_.setRcode(Rcode::YXDOMAIN());
                    return;
                }
187
188
189
190
                // The new CNAME we are creating (it will be unsigned even
                // with DNSSEC, the DNAME is signed and it can be validated
                // by that)
                RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
JINMEI Tatuya's avatar
JINMEI Tatuya committed
191
                    RRType::CNAME(), db_result.rrset->getTTL()));
192
193
194
195
196
                // Construct the new target by replacing the end
                cname->addRdata(rdata::generic::CNAME(qname_.split(0,
                    qname_.getLabelCount() -
                    db_result.rrset->getName().getLabelCount()).
                    concatenate(dname.getDname())));
197
                response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
198
                break;
199
            }
200
            case ZoneFinder::CNAME:
201
202
203
204
205
206
                /*
                 * We don't do chaining yet. Therefore handling a CNAME is
                 * mostly the same as handling SUCCESS, but we didn't get
                 * what we expected. It means no exceptions in ANY or NS
                 * on the origin (though CNAME in origin is probably
                 * forbidden anyway).
207
208
                 *
                 * So, just put it there.
209
                 */
210
                response_.addRRset(Message::SECTION_ANSWER,
211
212
                    boost::const_pointer_cast<RRset>(db_result.rrset),
                    dnssec_);
213
                break;
214
            case ZoneFinder::SUCCESS:
chenzhengzhang's avatar
chenzhengzhang committed
215
216
217
218
                if (qtype_is_any) {
                    // If quety type is ANY, insert all RRs under the domain
                    // into answer section.
                    BOOST_FOREACH(RRsetPtr rrset, *target) {
219
220
                        response_.addRRset(Message::SECTION_ANSWER, rrset,
                                           dnssec_);
221
                        // Handle additional for answer section
222
                        getAdditional(*result.zone_finder, *rrset.get());
chenzhengzhang's avatar
chenzhengzhang committed
223
224
225
                    }
                } else {
                    response_.addRRset(Message::SECTION_ANSWER,
226
227
                        boost::const_pointer_cast<RRset>(db_result.rrset),
                        dnssec_);
chenzhengzhang's avatar
chenzhengzhang committed
228
                    // Handle additional for answer section
229
                    getAdditional(*result.zone_finder, *db_result.rrset);
chenzhengzhang's avatar
chenzhengzhang committed
230
                }
231
232
233
234
                // If apex NS records haven't been provided in the answer
                // section, insert apex NS records into the authority section
                // and AAAA/A RRS of each of the NS RDATA into the additional
                // section.
235
236
                if (qname_ != result.zone_finder->getOrigin() ||
                    db_result.code != ZoneFinder::SUCCESS ||
chenzhengzhang's avatar
chenzhengzhang committed
237
                    (qtype_ != RRType::NS() && !qtype_is_any))
238
                {
239
                    getAuthAdditional(*result.zone_finder);
240
                }
241
                break;
242
            case ZoneFinder::DELEGATION:
Jerry's avatar
Jerry committed
243
                response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
244
                response_.addRRset(Message::SECTION_AUTHORITY,
245
246
                    boost::const_pointer_cast<RRset>(db_result.rrset),
                    dnssec_);
247
                getAdditional(*result.zone_finder, *db_result.rrset);
248
                break;
249
            case ZoneFinder::NXDOMAIN:
Michal Vaner's avatar
Michal Vaner committed
250
                // Just empty answer with SOA in authority section
251
                response_.setRcode(Rcode::NXDOMAIN());
252
                putSOA(*result.zone_finder);
253
                break;
254
            case ZoneFinder::NXRRSET:
Michal Vaner's avatar
Michal Vaner committed
255
                // Just empty answer with SOA in authority section
256
                putSOA(*result.zone_finder);
257
258
259
                break;
        }
    }
260
}
Michal Vaner's avatar
Michal Vaner committed
261

262
263
}
}