Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
3 : : *
4 : : * Permission to use, copy, modify, and/or distribute this software for any
5 : : * purpose with or without fee is hereby granted, provided that the above
6 : : * copyright notice and this permission notice appear in all copies.
7 : : *
8 : : * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 : : * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 : : * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 : : * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 : : * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 : : * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 : : * PERFORMANCE OF THIS SOFTWARE.
15 : : */
16 : :
17 : : #include <exceptions/exceptions.h>
18 : : #include <dns/rrset.h>
19 : : #include <datasrc/zone.h>
20 : :
21 : : #include <boost/noncopyable.hpp>
22 : :
23 : : #include <functional>
24 : : #include <vector>
25 : :
26 : : namespace isc {
27 : : namespace dns {
28 : : class Message;
29 : : class Name;
30 : : class RRType;
31 : : class RRset;
32 : : }
33 : :
34 : : namespace datasrc {
35 : : class DataSourceClient;
36 : : }
37 : :
38 : : namespace auth {
39 : :
40 : : /// The \c Query class represents a standard DNS query that encapsulates
41 : : /// processing logic to answer the query.
42 : : ///
43 : : /// Many of the design details for this class are still in flux.
44 : : /// We'll revisit and update them as we add more functionality, for example:
45 : : /// - as a related point, we may have to pass the RR class of the query.
46 : : /// in the initial implementation the RR class is an attribute of
47 : : /// datasource and omitted. It's not clear if this assumption holds with
48 : : /// generic data sources. On the other hand, it will help keep
49 : : /// implementation simpler, and we might rather want to modify the design
50 : : /// of the data source on this point.
51 : : /// - return value of process(). rather than or in addition to setting the
52 : : /// Rcode, we might use it as a return value of \c process().
53 : : /// - we'll have to be able to specify whether DNSSEC is requested.
54 : : /// It's an open question whether it should be in the constructor or via a
55 : : /// separate attribute setter.
56 : : /// - likewise, we'll eventually need to do per zone access control, for which
57 : : /// we need querier's information such as its IP address.
58 : : /// - datasrc_client and response may better be parameters to process() instead
59 : : /// of the constructor.
60 : : ///
61 : : /// <b>Note:</b> The class name is intentionally the same as the one used in
62 : : /// the datasrc library. This is because the plan is to eventually merge
63 : : /// the two classes. We could give it a different name such as "AuthQuery"
64 : : /// to avoid possible ambiguity, but it may sound redundant in that it's
65 : : /// obvious that this class is for authoritative queries.
66 : : /// Since the interfaces are very different for now and it's less
67 : : /// likely to misuse one of the classes instead of the other
68 : : /// accidentally, and since it's considered a temporary development state,
69 : : /// we keep this name at the moment.
70 [ + - ][ + - ]: 274 : class Query : boost::noncopyable {
[ + - ]
71 : : private:
72 : : /// \brief Initial reserved size for the vectors in Query
73 : : ///
74 : : /// The value is larger than we expect the vectors to even become, and
75 : : /// has been chosen arbitrarily. The reason to set them quite high is
76 : : /// to prevent reallocation on addition.
77 : : static const size_t RESERVE_RRSETS = 64;
78 : :
79 : : /// \brief Adds a SOA.
80 : : ///
81 : : /// Adds a SOA of the zone into the authority zone of response_.
82 : : /// Can throw NoSOA.
83 : : ///
84 : : void addSOA(isc::datasrc::ZoneFinder& finder);
85 : :
86 : : /// \brief Adds the DS rrset for the given name, if available
87 : : ///
88 : : /// This is intended to be called when returning a delegation, and
89 : : /// if DNSSEC data is requested. If the DS record is not found
90 : : /// (signaled by find() returning NXRRSET), and the zone is signed
91 : : /// with NSEC, an NSEC denial of existence proof is added.
92 : : ///
93 : : /// \exception BadDS raised if find() returns anything other than
94 : : /// SUCCESS or NXRRSET when searching for the DS
95 : : /// record.
96 : : /// \param finder The ZoneFinder where the delegation was found
97 : : /// \param ds_name The name of the delegation RRset
98 : : void addDS(isc::datasrc::ZoneFinder& finder,
99 : : const isc::dns::Name& ds_name);
100 : :
101 : : /// \brief Adds NSEC(3) denial proof for the given NXRRset result
102 : : ///
103 : : /// If available, NSEC or NSEC3 records are added to the authority
104 : : /// section (depending on whether isNSECSigned() or isNSEC3Signed()
105 : : /// returns true).
106 : : ///
107 : : /// \param finder The ZoneFinder that was used to search for the missing
108 : : /// data
109 : : /// \param db_result The ZoneFinder::FindResult returned by find()
110 : : void addNXRRsetProof(isc::datasrc::ZoneFinder& finder,
111 : : const isc::datasrc::ZoneFinder::Context& db_context);
112 : :
113 : : /// Add NSEC RRs that prove an NXDOMAIN result.
114 : : ///
115 : : /// This corresponds to Section 3.1.3.2 of RFC 4035.
116 : : void addNXDOMAINProofByNSEC(isc::datasrc::ZoneFinder& finder,
117 : : isc::dns::ConstRRsetPtr nsec);
118 : :
119 : : /// Add NSEC3 RRs that prove an NXDOMAIN result.
120 : : ///
121 : : /// This corresponds to Section 7.2.2 of RFC 5155.
122 : : void addNXDOMAINProofByNSEC3(isc::datasrc::ZoneFinder& finder);
123 : :
124 : : /// Add NSEC or NSEC3 RRs that prove a wildcard answer is the best one.
125 : : ///
126 : : /// This corresponds to Section 3.1.3.3 of RFC 4035 and Section 7.2.6
127 : : /// of RFC5155.
128 : : void addWildcardProof(
129 : : isc::datasrc::ZoneFinder& finder,
130 : : const isc::datasrc::ZoneFinder::Context& db_context);
131 : :
132 : : /// \brief Adds one NSEC RR proved no matched QNAME,one NSEC RR proved no
133 : : /// matched <QNAME,QTYPE> through wildcard extension.
134 : : ///
135 : : /// Add NSEC RRs that prove an WILDCARD_NXRRSET result.
136 : : /// This corresponds to Section 3.1.3.4 of RFC 4035.
137 : : /// \param finder The ZoneFinder through which the authority data for the
138 : : /// query is to be found.
139 : : /// \param nsec The RRset (NSEC RR) which proved that there is no matched
140 : : /// <QNAME,QTTYPE>.
141 : : void addWildcardNXRRSETProof(isc::datasrc::ZoneFinder& finder,
142 : : isc::dns::ConstRRsetPtr nsec);
143 : :
144 : : /// \brief Look up a zone's NS RRset and their address records for an
145 : : /// authoritative answer, and add them to the additional section.
146 : : ///
147 : : /// On returning an authoritative answer, insert a zone's NS into the
148 : : /// authority section and AAAA/A RRs of each of the NS RDATA into the
149 : : /// additional section.
150 : : ///
151 : : /// <b>Notes to developer:</b>
152 : : ///
153 : : /// We should omit address records which has already been provided in the
154 : : /// answer section from the additional.
155 : : ///
156 : : /// For now, in order to optimize the additional section processing, we
157 : : /// include AAAA/A RRs under a zone cut in additional section. (BIND 9
158 : : /// excludes under-cut RRs; NSD include them.)
159 : : ///
160 : : /// \param finder The \c ZoneFinder through which the NS and additional
161 : : /// data for the query are to be found.
162 : : void addAuthAdditional(isc::datasrc::ZoneFinder& finder,
163 : : std::vector<isc::dns::ConstRRsetPtr>& additionals);
164 : :
165 : : /// \brief Process a DS query possible at the child side of zone cut.
166 : : ///
167 : : /// This private method is a subroutine of process(), and is called if
168 : : /// there's a possibility that this server has authority for the child
169 : : /// side of the DS's owner name (and it's detected that the server at
170 : : /// least doesn't have authority at the parent side). This method
171 : : /// first checks if it has authority for the child, and if does,
172 : : /// just build a "no data" response with SOA for the zone origin
173 : : /// (possibly with a proof for the no data) as specified in Section
174 : : /// 2.2.1.1 of RFC3658.
175 : : ///
176 : : /// It returns true if this server has authority of the child; otherwise
177 : : /// it returns false. In the former case, the caller is expected to
178 : : /// terminate the query processing, because it should have been completed
179 : : /// within this method.
180 : : bool processDSAtChild();
181 : :
182 : : /// \brief Add NSEC3 to the response for a closest encloser proof for a
183 : : /// given name.
184 : : ///
185 : : /// This method calls \c findNSEC3() of the given zone finder for the
186 : : /// given name in the recursive mode, and adds the returned NSEC3(s) to
187 : : /// the authority section of the response message associated with the
188 : : /// \c Query object.
189 : : ///
190 : : /// It returns the number of labels of the closest encloser (returned via
191 : : /// the \c findNSEC3() call) in case the caller needs to use that value
192 : : /// for subsequent processing, i.e, constructing the best possible wildcard
193 : : /// name that (would) match the query name.
194 : : ///
195 : : /// Unless \c exact_ok is true, \c name is expected to be non existent,
196 : : /// in which case findNSEC3() in the recursive mode must return both
197 : : /// closest and next proofs. If the latter is NULL, it means a run time
198 : : /// collision (or the zone is broken in other way), and this method throws
199 : : /// a BadNSEC3 exception.
200 : : ///
201 : : /// If \c exact_ok is true, this method takes into account the case
202 : : /// where the name exists and may or may not be at a zone cut to an
203 : : /// optout zone. In this case, depending on whether the zone is optout
204 : : /// or not, findNSEC3() may return non-NULL or NULL next_proof
205 : : /// (respectively). This method adds the next proof if and only if
206 : : /// findNSEC3() returns non NULL value for it. The Opt-Out flag
207 : : /// must be set or cleared accordingly, but this method doesn't check that
208 : : /// in this level (as long as the zone is signed validly and findNSEC3()
209 : : /// for the data source is implemented as documented, the condition
210 : : /// should be met; otherwise we'd let the validator detect the error).
211 : : ///
212 : : /// By default this method always adds the closest proof.
213 : : /// If \c add_closest is false, it only adds the next proof to the message.
214 : : /// This correspond to the case of "wildcard answer responses" as described
215 : : /// in Section 7.2.6 of RFC5155.
216 : : uint8_t addClosestEncloserProof(isc::datasrc::ZoneFinder& finder,
217 : : const isc::dns::Name& name, bool exact_ok,
218 : : bool add_closest = true);
219 : :
220 : : /// \brief Add matching or covering NSEC3 to the response for a give name.
221 : : ///
222 : : /// This method calls \c findNSEC3() of the given zone finder for the
223 : : /// given name in the non recursive mode, and adds the returned NSEC3 to
224 : : /// the authority section of the response message associated with the
225 : : /// \c Query object.
226 : : ///
227 : : /// Depending on the caller's context, the returned NSEC3 is one and
228 : : /// only one of matching or covering NSEC3. If \c match is true the
229 : : /// returned NSEC3 must be a matching one; otherwise it must be a covering
230 : : /// one. If this assumption isn't met this method throws a BadNSEC3
231 : : /// exception (if it must be a matching NSEC3 but is not, it means a broken
232 : : /// zone, maybe with incorrect optout NSEC3s; if it must be a covering
233 : : /// NSEC3 but is not, it means a run time collision; or the \c findNSEC3()
234 : : /// implementation is broken for both cases.)
235 : : void addNSEC3ForName(isc::datasrc::ZoneFinder& finder,
236 : : const isc::dns::Name& name, bool match);
237 : :
238 : : /// Set up the Query object for a new query lookup
239 : : ///
240 : : /// This is the first step of the process() method, and initializes
241 : : /// the member data
242 : : ///
243 : : /// \param datasrc_client The datasource wherein the answer to the query is
244 : : /// to be found.
245 : : /// \param qname The query name
246 : : /// \param qtype The RR type of the query
247 : : /// \param response The response message to store the answer to the query.
248 : : /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
249 : : /// possible.
250 : : void initialize(datasrc::DataSourceClient& datasrc_client,
251 : : const isc::dns::Name& qname, const isc::dns::RRType& qtype,
252 : : isc::dns::Message& response, bool dnssec = false);
253 : :
254 : : /// \brief Resets any partly built response data, and internal pointers
255 : : ///
256 : : /// Called by the QueryCleaner object upon its destruction
257 : : void reset();
258 : :
259 : : /// \brief Internal class used for cleanup of Query members
260 : : ///
261 : : /// The process() call creates an object of this class, which
262 : : /// upon its destruction, calls Query::reset(), so that outside
263 : : /// of single calls to process(), the query state is always clean.
264 : : class QueryCleaner {
265 : : public:
266 : 101 : QueryCleaner(isc::auth::Query& query) : query_(query) {}
267 : 101 : ~QueryCleaner() { query_.reset(); }
268 : : private:
269 : : isc::auth::Query& query_;
270 : : };
271 : :
272 : : protected:
273 : : // Following methods declared protected so they can be accessed
274 : : // by unit tests.
275 : :
276 : : void createResponse();
277 : :
278 : : public:
279 : : /// Default constructor.
280 : : ///
281 : : /// Query parameters will be set by the call to process()
282 : : ///
283 : : Query() :
284 : : datasrc_client_(NULL), qname_(NULL), qtype_(NULL),
285 : : dnssec_(false), dnssec_opt_(isc::datasrc::ZoneFinder::FIND_DEFAULT),
286 [ + - ]: 86 : response_(NULL)
287 : : {
288 [ + - ]: 180 : answers_.reserve(RESERVE_RRSETS);
289 [ + - ]: 180 : authorities_.reserve(RESERVE_RRSETS);
290 [ - + ]: 180 : additionals_.reserve(RESERVE_RRSETS);
291 : : }
292 : :
293 : :
294 : : /// Process the query.
295 : : ///
296 : : /// This method first identifies the zone that best matches the query
297 : : /// name (and in some cases RR type when the search is dependent on the
298 : : /// type) and then searches the zone for an entry that best matches the
299 : : /// query name.
300 : : /// It then updates the response message accordingly; for example, a
301 : : /// successful search would result in adding a corresponding RRset to
302 : : /// the answer section of the response.
303 : : ///
304 : : /// If no matching zone is found in the datasource, the RCODE of
305 : : /// SERVFAIL will be set in the response.
306 : : /// <b>Note:</b> this is different from the error code that BIND 9 returns
307 : : /// by default when it's configured as an authoritative-only server (and
308 : : /// from the behavior of the BIND 10 datasrc library, which was implemented
309 : : /// to be compatible with BIND 9).
310 : : /// The difference comes from the fact that BIND 9 returns REFUSED as a
311 : : /// result of access control check on the use of its cache.
312 : : /// Since BIND 10's authoritative server doesn't have the notion of cache
313 : : /// by design, it doesn't make sense to return REFUSED. On the other hand,
314 : : /// providing compatible behavior may have its own benefit, so this point
315 : : /// should be revisited later.
316 : : ///
317 : : /// This might throw BadZone or any of its specific subclasses, but that
318 : : /// shouldn't happen in real-life (as BadZone means wrong data, it should
319 : : /// have been rejected upon loading).
320 : : ///
321 : : /// \param datasrc_client The datasource wherein the answer to the query is
322 : : /// to be found.
323 : : /// \param qname The query name
324 : : /// \param qtype The RR type of the query
325 : : /// \param response The response message to store the answer to the query.
326 : : /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
327 : : /// possible.
328 : : void process(datasrc::DataSourceClient& datasrc_client,
329 : : const isc::dns::Name& qname, const isc::dns::RRType& qtype,
330 : : isc::dns::Message& response, bool dnssec = false);
331 : :
332 : : /// \short Bad zone data encountered.
333 : : ///
334 : : /// This is thrown when process encounteres misconfigured zone in a way
335 : : /// it can't continue. This throws, not sets the Rcode, because such
336 : : /// misconfigured zone should not be present in the data source and
337 : : /// should have been rejected sooner.
338 : 17 : struct BadZone : public isc::Exception {
339 : 17 : BadZone(const char* file, size_t line, const char* what) :
340 : 34 : Exception(file, line, what)
341 : 17 : {}
342 : : };
343 : :
344 : : /// \short Zone is missing its SOA record.
345 : : ///
346 : : /// We tried to add a SOA into the authoritative section, but the zone
347 : : /// does not contain one.
348 : 2 : struct NoSOA : public BadZone {
349 : : NoSOA(const char* file, size_t line, const char* what) :
350 [ + - ]: 2 : BadZone(file, line, what)
351 : : {}
352 : : };
353 : :
354 : : /// \short Zone is missing its apex NS records.
355 : : ///
356 : : /// We tried to add apex NS records into the authority section, but the
357 : : /// zone does not contain any.
358 : 1 : struct NoApexNS: public BadZone {
359 : : NoApexNS(const char* file, size_t line, const char* what) :
360 [ + - ]: 1 : BadZone(file, line, what)
361 : : {}
362 : : };
363 : :
364 : : /// An invalid result is given when a valid NSEC is expected
365 : : ///
366 : : /// This can only happen when the underlying data source implementation or
367 : : /// the zone is broken. By throwing an exception we treat such cases
368 : : /// as SERVFAIL.
369 : 7 : struct BadNSEC : public BadZone {
370 : : BadNSEC(const char* file, size_t line, const char* what) :
371 [ # # ][ # # ]: 7 : BadZone(file, line, what)
[ + - ][ + - ]
[ + - ]
372 : : {}
373 : : };
374 : :
375 : : /// An invalid result is given when a valid NSEC3 is expected
376 : : ///
377 : : /// This can only happen when the underlying data source implementation or
378 : : /// the zone is broken. By throwing an exception we treat such cases
379 : : /// as SERVFAIL.
380 : 6 : struct BadNSEC3 : public BadZone {
381 : : BadNSEC3(const char* file, size_t line, const char* what) :
382 [ + - ][ + - ]: 6 : BadZone(file, line, what)
383 : : {}
384 : : };
385 : :
386 : : /// An invalid result is given when a valid DS records (or NXRRSET) is
387 : : /// expected
388 : : ///
389 : : /// This can only happen when the underlying data source implementation
390 : : /// or the zone is broken. A DS query for a known delegation point should
391 : : /// either result in SUCCESS (if available) or NXRRSET
392 : 1 : struct BadDS : public BadZone {
393 : : BadDS(const char* file, size_t line, const char* what) :
394 [ + - ]: 1 : BadZone(file, line, what)
395 : : {}
396 : : };
397 : :
398 : : /// \brief Response Creator Class
399 : : ///
400 : : /// This is a helper class of Query, and is expected to be used during the
401 : : /// construction of the response message. This class performs the
402 : : /// duplicate RRset detection check. It keeps a list of RRsets added
403 : : /// to the message and does not add an RRset if it is the same as one
404 : : /// already added.
405 : : ///
406 : : /// This class is essentially private to Query, but is visible to public
407 : : /// for testing purposes. It's not expected to be used from a normal
408 : : /// application.
409 : 181 : class ResponseCreator {
410 : : public:
411 : : /// \brief Constructor
412 : : ///
413 : : /// Reserves space for the list of RRsets. Although the
414 : : /// ResponseCreator will be used to create a message from the
415 : : /// contents of the Query object's answers_, authorities_ and
416 : : /// additionals_ elements, and each of these are sized to
417 : : /// RESERVE_RRSETS, it is _extremely_ unlikely that all three will be
418 : : /// filled to capacity. So we reserve more elements than in each of
419 : : /// these components, but not three times the amount.
420 : : ///
421 : : /// As with the answers_, authorities_ and additionals_ elements, the
422 : : /// reservation is made in the constructor to avoid dynamic allocation
423 : : /// of memory. The ResponseCreator is a member variable of the Query
424 : : /// object so is constructed once and lasts as long as that object.
425 : : /// Internal state is cleared through the clear() method.
426 : 181 : ResponseCreator() {
427 [ + + ]: 181 : added_.reserve(2 * RESERVE_RRSETS);
428 : 87 : }
429 : :
430 : : /// \brief Reset internal state
431 : : void clear() {
432 : 101 : added_.clear();
433 : : }
434 : :
435 : : /// \brief Complete the response message with filling in the
436 : : /// response sections.
437 : : ///
438 : : /// This is the final step of the Query::process() method, and within
439 : : /// that method, it should be called before it returns (if any
440 : : /// response data is to be added)
441 : : ///
442 : : /// This will take a message to build and each RRsets for the answer,
443 : : /// authority, and additional sections, and add them to their
444 : : /// corresponding sections in the given message. The RRsets are
445 : : /// filtered such that a particular RRset appears only once in the
446 : : /// message.
447 : : ///
448 : : /// If \c dnssec is true, it tells the message to include any RRSIGs
449 : : /// attached to the RRsets.
450 : : void create(
451 : : isc::dns::Message& message,
452 : : const std::vector<isc::dns::ConstRRsetPtr>& answers_,
453 : : const std::vector<isc::dns::ConstRRsetPtr>& authorities_,
454 : : const std::vector<isc::dns::ConstRRsetPtr>& additionals_,
455 : : const bool dnssec);
456 : :
457 : : private:
458 : : // \brief RRset comparison functor.
459 : : struct IsSameKind : public std::binary_function<
460 : : const isc::dns::AbstractRRset*,
461 : : const isc::dns::AbstractRRset*,
462 : : bool> {
463 : 0 : bool operator()(const isc::dns::AbstractRRset* r1,
464 : : const isc::dns::AbstractRRset* r2) const {
465 : 399 : return (r1->isSameKind(*r2));
466 : : }
467 : : };
468 : :
469 : : /// Insertion operation
470 : : ///
471 : : /// \param message Message to which the RRset is to be added
472 : : /// \param section Section of the message in which the RRset is put
473 : : /// \param rrset Pointer to RRset to be added to the message
474 : : /// \param dnssec Whether RRSIG records should be added as well
475 : : void addRRset(isc::dns::Message& message,
476 : : const isc::dns::Message::Section section,
477 : : const isc::dns::ConstRRsetPtr& rrset, const bool dnssec);
478 : :
479 : :
480 : : private:
481 : : /// List of RRsets already added to the message
482 : : std::vector<const isc::dns::AbstractRRset*> added_;
483 : : };
484 : :
485 : : private:
486 : : const isc::datasrc::DataSourceClient* datasrc_client_;
487 : : const isc::dns::Name* qname_;
488 : : const isc::dns::RRType* qtype_;
489 : : bool dnssec_;
490 : : isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
491 : : ResponseCreator response_creator_;
492 : :
493 : : isc::dns::Message* response_;
494 : : std::vector<isc::dns::ConstRRsetPtr> answers_;
495 : : std::vector<isc::dns::ConstRRsetPtr> authorities_;
496 : : std::vector<isc::dns::ConstRRsetPtr> additionals_;
497 : : };
498 : :
499 : : }
500 : : }
501 : :
502 : : // Local Variables:
503 : : // mode: c++
504 : : // End:
|