Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <limits>
18 : : #include <dns/message.h>
19 : : #include <nsas/nsas_entry.h>
20 : : #include "message_entry.h"
21 : : #include "message_utility.h"
22 : : #include "rrset_cache.h"
23 : : #include "logger.h"
24 : :
25 : : using namespace isc::dns;
26 : : using namespace std;
27 : :
28 : : // Put file scope functions in unnamed namespace.
29 : : namespace {
30 : :
31 : : // Get the shortest existing ancestor which is the owner name of
32 : : // one DNAME record for the given query name.
33 : : // Note: there may be multiple DNAME records(DNAME chain) in answer
34 : : // section. In most cases they are in order, but the code can't depend
35 : : // on that, it has to find the starter by iterating the DNAME chain.
36 : : Name
37 : 9 : getDNAMEChainStarter(const Message& message, const Name& query_name) {
38 : 9 : Name dname = query_name;
39 [ + - ][ + - ]: 18 : RRsetIterator rrset_iter = message.beginSection(Message::SECTION_ANSWER);
40 [ + - ][ + - ]: 47 : while(rrset_iter != message.endSection(Message::SECTION_ANSWER)) {
[ + - ][ + + ]
41 [ + - ][ + - ]: 38 : if ((*rrset_iter)->getType() == RRType::DNAME()) {
[ + + ]
42 [ + - ][ + - ]: 16 : const Name& rrname = (*rrset_iter)->getName();
43 [ + + ]: 16 : if (NameComparisonResult::SUBDOMAIN ==
44 [ + - ]: 16 : dname.compare(rrname).getRelation()) {
45 [ + - ]: 5 : dname = rrname;
46 : : }
47 : : }
48 [ + - ]: 38 : ++rrset_iter;
49 : : }
50 : :
51 : 9 : return (dname);
52 : : }
53 : :
54 : : } // End of unnamed namespace
55 : :
56 : : namespace isc {
57 : : namespace cache {
58 : :
59 : 3 : static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
60 : :
61 : : // As with caching positive responses it is sensible for a resolver to
62 : : // limit for how long it will cache a negative response as the protocol
63 : : // supports caching for up to 68 years. Such a limit should not be
64 : : // greater than that applied to positive answers and preferably be
65 : : // tunable. Values of one to three hours have been found to work well
66 : : // and would make sensible a default. Values exceeding one day have
67 : : // been found to be problematic. (sec 5, RFC2308)
68 : : // The default value is 3 hours (10800 seconds)
69 : : // TODO:Give an option to let user configure
70 : : static uint32_t MAX_NEGATIVE_CACHE_TTL = 10800;
71 : :
72 : : // Sets the maximum time for which the server will cache ordinary (positive) answers. The
73 : : // default is one week (7 days = 604800 seconds)
74 : : // TODO:Give an option to let user configure
75 : : static uint32_t MAX_NORMAL_CACHE_TTL = 604800;
76 : :
77 : 36 : MessageEntry::MessageEntry(const isc::dns::Message& msg,
78 : : const RRsetCachePtr& rrset_cache,
79 : : const RRsetCachePtr& negative_soa_cache):
80 : : rrset_cache_(rrset_cache),
81 : : negative_soa_cache_(negative_soa_cache),
82 : : headerflag_aa_(false),
83 : 0 : headerflag_tc_(false)
84 : : {
85 [ + - ]: 36 : initMessageEntry(msg);
86 [ + - ]: 72 : entry_name_ = genCacheEntryName(query_name_, query_type_);
87 [ + - ]: 36 : hash_key_ptr_ = new HashKey(entry_name_, RRClass(query_class_));
88 : 36 : }
89 : :
90 : : bool
91 : 20 : MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
92 : : const time_t time_now)
93 : : {
94 : 20 : uint16_t entry_count = answer_count_ + authority_count_ + additional_count_;
95 : 20 : rrset_entry_vec.reserve(rrset_entry_vec.size() + entry_count);
96 [ + + ]: 85 : for (int index = 0; index < entry_count; ++index) {
97 : 130 : RRsetCache* rrset_cache = rrsets_[index].cache_;
98 : 65 : RRsetEntryPtr rrset_entry = rrset_cache->lookup(rrsets_[index].name_,
99 : 65 : rrsets_[index].type_);
100 [ + + ][ + - ]: 65 : if (rrset_entry && time_now < rrset_entry->getExpireTime()) {
[ + + ][ + + ]
101 : : rrset_entry_vec.push_back(rrset_entry);
102 : : } else {
103 : 2 : return (false);
104 : : }
105 : : }
106 : :
107 : : return (true);
108 : : }
109 : :
110 : : void
111 : 54 : MessageEntry::addRRset(isc::dns::Message& message,
112 : : const vector<RRsetEntryPtr>& rrset_entry_vec,
113 : : const isc::dns::Message::Section& section,
114 : : bool dnssec_need)
115 : : {
116 : 54 : uint16_t start_index = 0;
117 : 54 : uint16_t end_index = answer_count_;
118 [ - + ]: 54 : assert(section != Message::SECTION_QUESTION);
119 : :
120 [ + + ]: 54 : if (section == Message::SECTION_AUTHORITY) {
121 : 18 : start_index = answer_count_;
122 : 18 : end_index = answer_count_ + authority_count_;
123 [ + + ]: 36 : } else if (section == Message::SECTION_ADDITIONAL) {
124 : 18 : start_index = answer_count_ + authority_count_;
125 : 18 : end_index = start_index + additional_count_;
126 : : }
127 : :
128 [ + + ]: 114 : for (uint16_t index = start_index; index < end_index; ++index) {
129 : 60 : message.addRRset(section, rrset_entry_vec[index]->getRRset(),
130 [ + - ]: 60 : dnssec_need);
131 : : }
132 : 54 : }
133 : :
134 : : bool
135 : 20 : MessageEntry::genMessage(const time_t& time_now,
136 : : isc::dns::Message& msg)
137 : : {
138 [ + + ]: 20 : if (time_now >= expire_time_) {
139 : : // The message entry has expired.
140 : : return (false);
141 : : } else {
142 : : // Before do any generation, we should check if some rrset
143 : : // has expired, if it is, return false.
144 : 20 : vector<RRsetEntryPtr> rrset_entry_vec;
145 [ + - ][ + + ]: 19 : if (false == getRRsetEntries(rrset_entry_vec, time_now)) {
146 [ + - ][ + - ]: 2 : LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_ENTRY_MISSING_RRSET).
[ + - ]
147 [ + - ]: 1 : arg(entry_name_);
148 : : return (false);
149 : : }
150 : :
151 : : // Begin message generation. We don't need to add question
152 : : // section, since it has been included in the message.
153 : : // Set cached header flags.
154 : : // The AA flag bit should be cleared because this is a response from
155 : : // resolver cache
156 [ + - ]: 18 : msg.setHeaderFlag(Message::HEADERFLAG_AA, false);
157 [ + - ]: 18 : msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
158 : :
159 [ + - ]: 18 : bool dnssec_need = msg.getEDNS().get();
160 [ + - ]: 18 : addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER, dnssec_need);
161 [ + - ]: 18 : addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY, dnssec_need);
162 [ + - ]: 18 : addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL, dnssec_need);
163 : :
164 : : return (true);
165 : : }
166 : : }
167 : :
168 : : RRsetTrustLevel
169 : 176 : MessageEntry::getRRsetTrustLevel(const Message& message,
170 : : const isc::dns::RRsetPtr& rrset,
171 : : const isc::dns::Message::Section& section)
172 : : {
173 : 176 : bool aa = message.getHeaderFlag(Message::HEADERFLAG_AA);
174 [ + + + - ]: 176 : switch(section) {
175 : : case Message::SECTION_ANSWER: {
176 [ + + ]: 59 : if (aa) {
177 : : // According RFC2181 section 5.4.1, only the record
178 : : // describing that ailas is necessarily authoritative.
179 : : // If there are CNAME(Not synchronized from DNAME)
180 : : // records in answer section, only the CNAME record
181 : : // whose owner name is same with qname is assumed as
182 : : // authoritative, all the left records are not authoritative.
183 : : //
184 : : // If there are DNAME records in answer section,
185 : : // Only the start DNAME and the synchronized CNAME record
186 : : // from it are authoritative, any other records in answer
187 : : // section are non-authoritative.
188 : 104 : QuestionIterator quest_iter = message.beginQuestion();
189 : : // Make sure question section is not empty.
190 [ + - ][ + - ]: 52 : assert( quest_iter != message.endQuestion());
[ - + ][ + - ]
191 : :
192 [ + - ]: 52 : const Name& query_name = (*quest_iter)->getName();
193 [ + - ]: 52 : const RRType& type = rrset->getType();
194 [ + - ]: 52 : const Name& name = rrset->getName();
195 [ + + ]: 172 : if ((type == RRType::CNAME() && name == query_name) ||
[ + + + + ]
[ + + ][ + + ]
196 : 41 : (type == RRType::DNAME() &&
197 [ + - ][ + + ]: 61 : name == getDNAMEChainStarter(message, query_name))) {
[ # # ]
198 : : return (RRSET_TRUST_ANSWER_AA);
199 : : } else {
200 : : // If there is a CNAME record whose ower name is the same as
201 : : // the query name in answer section, the other records in answer
202 : : // section are non-authoritative, except the starter of DNAME
203 : : // chain (only checking CNAME is enough, because if the CNAME
204 : : // record is synthesized from a DNAME record, that DNAME
205 : : // record must be the starter of the DNAME chain).
206 [ + - ][ + - ]: 74 : RRsetIterator iter = message.beginSection(Message::SECTION_ANSWER);
[ + - ]
207 [ + - ][ + - ]: 62 : while(iter != message.endSection(Message::SECTION_ANSWER)) {
[ + - ][ + + ]
208 [ + - ][ + - ]: 59 : if ((*iter)->getType() == RRType::CNAME() &&
[ + + ][ - + ]
[ + + ]
209 [ + - ][ + - ]: 17 : (*iter)->getName() == query_name) {
210 : : return (RRSET_TRUST_ANSWER_NONAA);
211 : : }
212 [ + - ]: 25 : ++iter;
213 : : }
214 : : }
215 : : return (RRSET_TRUST_ANSWER_AA);
216 : : } else {
217 : : return (RRSET_TRUST_ANSWER_NONAA);
218 : : }
219 : : break;
220 : : }
221 : :
222 : : case Message::SECTION_AUTHORITY: {
223 [ + + ]: 31 : if (aa) {
224 : : return (RRSET_TRUST_AUTHORITY_AA);
225 : : } else {
226 : 6 : return (RRSET_TRUST_AUTHORITY_NONAA);
227 : : }
228 : : break;
229 : : }
230 : :
231 : : case Message::SECTION_ADDITIONAL: {
232 [ + + ]: 86 : if (aa) {
233 : : return (RRSET_TRUST_ADDITIONAL_AA);
234 : : } else {
235 : 176 : return (RRSET_TRUST_ADDITIONAL_NONAA);
236 : : }
237 : : break;
238 : : }
239 : :
240 : : default:
241 : : return (RRSET_TRUST_DEFAULT);
242 : : }
243 : : }
244 : :
245 : : void
246 : 107 : MessageEntry::parseSection(const isc::dns::Message& msg,
247 : : const Message::Section& section,
248 : : uint32_t& smaller_ttl,
249 : : uint16_t& rrset_count)
250 : : {
251 : 107 : RRsetIterator iter;
252 : 107 : int count = 0;
253 [ + - ][ + - ]: 780 : for (iter = msg.beginSection(section);
[ + - ][ + - ]
[ + - ][ + + ]
254 [ + - ][ + - ]: 520 : iter != msg.endSection(section);
255 : : ++iter) {
256 : : // Add the rrset entry to rrset_cache or update the existed
257 : : // rrset entry if the new one is more authoritative.
258 : : //TODO set proper rrset trust level.
259 [ + - ]: 153 : RRsetPtr rrset_ptr = *iter;
260 [ + - ]: 153 : RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr, section);
261 [ + - ]: 306 : RRsetEntryPtr rrset_entry = rrset_cache_->update(*rrset_ptr, level);
262 [ + - ][ + - ]: 153 : rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType(),
263 [ + - ]: 153 : rrset_cache_.get()));
264 : :
265 [ + - ]: 153 : uint32_t rrset_ttl = rrset_entry->getTTL();
266 [ + + ]: 153 : if (smaller_ttl > rrset_ttl) {
267 : 52 : smaller_ttl = rrset_ttl;
268 : : }
269 : :
270 : 153 : count++;
271 : : }
272 : :
273 : 107 : rrset_count = count;
274 : 107 : }
275 : :
276 : : void
277 : 4 : MessageEntry::parseNegativeResponseAuthoritySection(const isc::dns::Message& msg,
278 : : uint32_t& min_ttl,
279 : : uint16_t& rrset_count)
280 : : {
281 : 4 : uint16_t count = 0;
282 [ + - ][ + - ]: 24 : for (RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
[ + + ]
283 [ + - ][ + - ]: 16 : iter != msg.endSection(Message::SECTION_AUTHORITY);
284 : : ++iter) {
285 [ + - ]: 4 : RRsetPtr rrset_ptr = *iter;
286 : : RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr,
287 [ + - ]: 4 : Message::SECTION_AUTHORITY);
288 : 4 : boost::shared_ptr<RRsetCache> rrset_cache_ptr = rrset_cache_;
289 [ + - ][ + - ]: 4 : if (rrset_ptr->getType() == RRType::SOA()) {
290 : 4 : rrset_cache_ptr = negative_soa_cache_;
291 : : }
292 : :
293 [ + - ]: 8 : RRsetEntryPtr rrset_entry = rrset_cache_ptr->update(*rrset_ptr, level);
294 [ + - ]: 4 : rrsets_.push_back(RRsetRef(rrset_ptr->getName(),
295 [ + - ]: 4 : rrset_ptr->getType(),
296 [ + - ]: 4 : rrset_cache_ptr.get()));
297 [ + - ]: 4 : uint32_t rrset_ttl = rrset_entry->getTTL();
298 [ + - ]: 4 : if (min_ttl > rrset_ttl) {
299 : 4 : min_ttl = rrset_ttl;
300 : : }
301 : 4 : ++count;
302 : : }
303 : :
304 : 4 : rrset_count = count;
305 : 4 : }
306 : :
307 : : void
308 : 36 : MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
309 : : //TODO better way to cache the header flags?
310 : 36 : headerflag_aa_ = msg.getHeaderFlag(Message::HEADERFLAG_AA);
311 : 36 : headerflag_tc_ = msg.getHeaderFlag(Message::HEADERFLAG_TC);
312 : :
313 : : // We only cache the first question in question section.
314 : : // TODO, do we need to support muptiple questions?
315 : 36 : query_count_ = 1;
316 : 72 : QuestionIterator iter = msg.beginQuestion();
317 [ + - ][ + - ]: 72 : query_name_ = (*iter)->getName().toText();
318 [ + - ]: 36 : query_type_ = (*iter)->getType().getCode();
319 [ + - ]: 36 : query_class_ = (*iter)->getClass().getCode();
320 : :
321 : 36 : uint32_t min_ttl = MAX_UINT32;
322 : :
323 [ + - ]: 36 : bool isNegativeResponse = MessageUtility::isNegativeResponse(msg);
324 : :
325 [ + - ]: 36 : parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
326 [ + + ]: 36 : if (!isNegativeResponse) {
327 [ + - ]: 32 : parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
328 : : } else {
329 [ + - ]: 4 : parseNegativeResponseAuthoritySection(msg, min_ttl, authority_count_);
330 : : }
331 [ + - ]: 36 : parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
332 : :
333 : : // Limit the ttl to a prset max-value
334 [ + + ]: 36 : if (!isNegativeResponse) {
335 [ + + ]: 32 : if (min_ttl > MAX_NORMAL_CACHE_TTL) {
336 : 1 : min_ttl = MAX_NORMAL_CACHE_TTL;
337 : : }
338 : : } else {
339 [ + + ]: 4 : if (min_ttl > MAX_NEGATIVE_CACHE_TTL) {
340 : 3 : min_ttl = MAX_NEGATIVE_CACHE_TTL;
341 : : }
342 : : }
343 : :
344 : 36 : expire_time_ = time(NULL) + min_ttl;
345 : 36 : }
346 : :
347 : : } // namespace cache
348 : 13 : } // namespace isc
|