Branch data Line data Source code
1 : : // Copyright (C) 2011 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 <stdlib.h>
18 : : #include <netinet/in.h>
19 : : #include <sys/socket.h>
20 : : #include <unistd.h> // for some IPC/network system calls
21 : : #include <string>
22 : :
23 : : #include <boost/lexical_cast.hpp>
24 : : #include <boost/bind.hpp>
25 : :
26 : : #include <dns/question.h>
27 : : #include <dns/message.h>
28 : : #include <dns/opcode.h>
29 : : #include <dns/exceptions.h>
30 : : #include <dns/rdataclass.h>
31 : : #include <resolve/resolve.h>
32 : : #include <resolve/resolve_log.h>
33 : : #include <resolve/resolve_messages.h>
34 : : #include <cache/resolver_cache.h>
35 : : #include <nsas/address_request_callback.h>
36 : : #include <nsas/nameserver_address.h>
37 : :
38 : : #include <asio.hpp>
39 : : #include <asiodns/dns_service.h>
40 : : #include <asiodns/io_fetch.h>
41 : : #include <asiolink/io_service.h>
42 : : #include <resolve/response_classifier.h>
43 : : #include <resolve/recursive_query.h>
44 : :
45 : : using namespace isc::dns;
46 : : using namespace isc::util;
47 : : using namespace isc::asiolink;
48 : : using namespace isc::resolve;
49 : :
50 : : namespace isc {
51 : : namespace asiodns {
52 : :
53 : : namespace {
54 : : // Function to check if the given name/class has any address in the cache
55 : : bool
56 : 8 : hasAddress(const Name& name, const RRClass& rrClass,
57 : : const isc::cache::ResolverCache& cache)
58 : : {
59 : : // FIXME: If we are single-stack and we get only the other type of
60 : : // address, what should we do? In that case, it will be considered
61 : : // unreachable, which is most probably true, because A and AAAA will
62 : : // usually have the same RTT, so we should have both or none from the
63 : : // glue.
64 [ + - ][ # # ]: 8 : return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() ||
65 [ + + ][ + - ]: 28 : cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
[ - + ][ + + ]
[ + + ][ # # ]
66 : : }
67 : :
68 : : // Convenience function for debug messages. Question::toText() includes
69 : : // a trailing newline in its output, which makes it awkward to embed in a
70 : : // message. This just strips that newline from it.
71 : : std::string
72 : 30 : questionText(const isc::dns::Question& question) {
73 : 30 : std::string text = question.toText();
74 [ + - ]: 30 : if (!text.empty()) {
75 [ + - ]: 30 : text.erase(text.size() - 1);
76 : : }
77 : 30 : return (text);
78 : : }
79 : :
80 : : } // anonymous namespace
81 : :
82 : : /// \brief Find deepest usable delegation in the cache
83 : : ///
84 : : /// This finds the deepest delegation we have in cache and is safe to use.
85 : : /// It is not public function, therefore it's not in header. But it's not
86 : : /// in anonymous namespace, so we can call it from unittests.
87 : : /// \param name The name we want to delegate to.
88 : : /// \param rrclass The class.
89 : : /// \param cache The place too look for known delegations.
90 : : std::string
91 : 5 : deepestDelegation(Name name, RRClass rrclass,
92 : : isc::cache::ResolverCache& cache)
93 : : {
94 : : RRsetPtr cachedNS;
95 : : // Look for delegation point from bottom, until we find one with
96 : : // IP address or get to root.
97 : : //
98 : : // We need delegation with IP address so we can ask it right away.
99 : : // If we don't have the IP address, we would need to ask above it
100 : : // anyway in the best case, and the NS could be inside the zone,
101 : : // and we could get all loopy with the NSAS in the worst case.
102 [ + - ][ + + ]: 27 : while (name.getLabelCount() > 1 &&
[ + + ]
103 [ + - ][ + - ]: 27 : (cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) {
[ + - ][ + - ]
[ # # ][ # # ]
104 : : // Look if we have an IP address for the NS
105 [ + - ][ + + ]: 18 : for (RdataIteratorPtr ns(cachedNS->getRdataIterator());
106 [ + - ][ + - ]: 12 : !ns->isLast(); ns->next()) {
107 : : // Do we have IP for this specific NS?
108 [ + - ][ + + ]: 8 : if (hasAddress(dynamic_cast<const rdata::generic::NS&>(
109 [ + - ][ + - ]: 8 : ns->getCurrent()).getNSName(), rrclass,
[ + - ]
110 : : cache)) {
111 : : // Found one, stop checking and use this zone
112 : : // (there may be more addresses, that's only better)
113 [ + - ][ + - ]: 2 : return (cachedNS->getName().toText());
114 : : }
115 : : }
116 : : // We don't have anything for this one, so try something higher
117 [ + - ]: 4 : if (name.getLabelCount() > 1) {
118 [ + - ][ + - ]: 9 : name = name.split(1);
119 : : }
120 : : }
121 : : // Fallback, nothing found, start at root
122 [ + - ]: 3 : return (".");
123 : : }
124 : :
125 : : typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
126 : :
127 : : // Here we do not use the typedef above, as the SunStudio compiler
128 : : // mishandles this in its name mangling, and wouldn't compile.
129 : : // We can probably use a typedef, but need to move it to a central
130 : : // location and use it consistently.
131 : 17 : RecursiveQuery::RecursiveQuery(DNSServiceBase& dns_service,
132 : : isc::nsas::NameserverAddressStore& nsas,
133 : : isc::cache::ResolverCache& cache,
134 : : const std::vector<std::pair<std::string, uint16_t> >& upstream,
135 : : const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
136 : : int query_timeout, int client_timeout, int lookup_timeout,
137 : : unsigned retries)
138 : : :
139 : : dns_service_(dns_service),
140 : : nsas_(nsas), cache_(cache),
141 [ + - ]: 17 : upstream_(new AddressVector(upstream)),
142 [ + - ]: 17 : upstream_root_(new AddressVector(upstream_root)),
143 : : test_server_("", 0),
144 : : query_timeout_(query_timeout), client_timeout_(client_timeout),
145 [ + - ][ + - ]: 68 : lookup_timeout_(lookup_timeout), retries_(retries), rtt_recorder_()
[ + - ]
146 : : {
147 : 17 : }
148 : :
149 : : // Set the test server - only used for unit testing.
150 : : void
151 : 2 : RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
152 [ + - ][ + - ]: 2 : LOG_WARN(isc::resolve::logger, RESLIB_TEST_SERVER).arg(address).arg(port);
[ + - ]
153 : 2 : test_server_.first = address;
154 : 2 : test_server_.second = port;
155 : 2 : }
156 : :
157 : : // Set the RTT recorder - only used for testing
158 : : void
159 : 2 : RecursiveQuery::setRttRecorder(boost::shared_ptr<RttRecorder>& recorder) {
160 : 2 : rtt_recorder_ = recorder;
161 : 2 : }
162 : :
163 : : namespace {
164 : :
165 : : typedef std::pair<std::string, uint16_t> addr_t;
166 : :
167 : : /*
168 : : * This is a query in progress. When a new query is made, this one holds
169 : : * the context information about it, like how many times we are allowed
170 : : * to retry on failure, what to do when we succeed, etc.
171 : : *
172 : : * Used by RecursiveQuery::sendQuery.
173 : : */
174 [ # # ][ # # ]: 0 : class RunningQuery : public IOFetch::Callback {
175 : :
176 : 0 : class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
177 : : public:
178 : 6 : ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
179 : :
180 : 2 : void success(const isc::nsas::NameserverAddress& address) {
181 : : // Success callback, send query to found namesever
182 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
183 [ + - ][ + - ]: 4 : .arg(address.getAddress().toText());
184 : 2 : rq_->nsasCallbackCalled();
185 : 2 : rq_->sendTo(address);
186 : 2 : }
187 : :
188 : 1 : void unreachable() {
189 : : // Nameservers unreachable: drop query or send servfail?
190 [ + - ]: 1 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
191 : 1 : rq_->nsasCallbackCalled();
192 : 1 : rq_->makeSERVFAIL();
193 : 1 : rq_->callCallback(true);
194 : 1 : rq_->stop();
195 : 1 : }
196 : :
197 : : private:
198 : : RunningQuery* rq_;
199 : : };
200 : :
201 : :
202 : : private:
203 : : // The io service to handle async calls
204 : : IOService& io_;
205 : :
206 : : // Info for (re)sending the query (the question and destination)
207 : : Question question_;
208 : :
209 : : // This is the query message got from client
210 : : ConstMessagePtr query_message_;
211 : :
212 : : // This is where we build and store our final answer
213 : : MessagePtr answer_message_;
214 : :
215 : : // Test server - only used for testing. This takes precedence over all
216 : : // other servers if the port is non-zero.
217 : : std::pair<std::string, uint16_t> test_server_;
218 : :
219 : : // Buffer to store the intermediate results.
220 : : OutputBufferPtr buffer_;
221 : :
222 : : // The callback will be called when we have either decided we
223 : : // are done, or when we give up
224 : : isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
225 : :
226 : : // Protocol used for the last query. This is set to IOFetch::UDP when a
227 : : // new upstream query is initiated, and changed to IOFetch::TCP if a
228 : : // packet is returned with the TC bit set. It is stored here to detect the
229 : : // case of a TCP packet being returned with the TC bit set.
230 : : IOFetch::Protocol protocol_;
231 : :
232 : : // EDNS flag
233 : : bool edns_;
234 : :
235 : : // To prevent both unreasonably long cname chains and cname loops,
236 : : // we simply keep a counter of the number of CNAMEs we have
237 : : // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
238 : : // from lib/resolve/response_classifier.h)
239 : : unsigned cname_count_;
240 : :
241 : : /*
242 : : * TODO Do something more clever with timeouts. In the long term, some
243 : : * computation of average RTT, increase with each retry, etc.
244 : : */
245 : : // Timeout information for outgoing queries
246 : : int query_timeout_;
247 : : unsigned retries_;
248 : :
249 : : // normal query state
250 : :
251 : : // Update the question that will be sent to the server
252 : : void setQuestion(const Question& new_question) {
253 : : question_ = new_question;
254 : : }
255 : :
256 : : // TODO: replace by our wrapper
257 : : asio::deadline_timer client_timer;
258 : : asio::deadline_timer lookup_timer;
259 : :
260 : : // If we timed out ourselves (lookup timeout), stop issuing queries
261 : : bool done_;
262 : :
263 : : // If we have a client timeout, we call back with a failure message,
264 : : // but we do not stop yet. We use this variable to make sure we
265 : : // don't call back a second time later
266 : : bool callback_called_;
267 : :
268 : : // Reference to our NSAS
269 : : isc::nsas::NameserverAddressStore& nsas_;
270 : :
271 : : // Reference to our cache
272 : : isc::cache::ResolverCache& cache_;
273 : :
274 : : // the 'current' zone we are in (i.e.) we start out at the root,
275 : : // and for each delegation this gets updated with the zone the
276 : : // delegation points to.
277 : : // TODO: make this a Name (it is a string right now because most
278 : : // of the call we use it in take a string, we need update those
279 : : // too).
280 : : std::string cur_zone_;
281 : :
282 : : // This is the handler we pass on to the NSAS; it is called when
283 : : // the NSAS has an address for us to query
284 : : boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
285 : :
286 : : // this is set to true if we have asked the nsas to give us
287 : : // an address and we are waiting for it to call us back.
288 : : // We use is to cancel the outstanding callback in case we
289 : : // have a lookup timeout and decide to give up
290 : : bool nsas_callback_out_;
291 : :
292 : : // This is the nameserver we have an outstanding query to.
293 : : // It is used to update the RTT once the query returns
294 : : isc::nsas::NameserverAddress current_ns_address;
295 : :
296 : : // The moment in time we sent a query to the nameserver above.
297 : : struct timeval current_ns_qsent_time;
298 : :
299 : : // RunningQuery deletes itself when it is done. In order for us
300 : : // to do this safely, we must make sure that there are no events
301 : : // that might call back to it. There are two types of events in
302 : : // this sense; the timers we set ourselves (lookup and client),
303 : : // and outstanding queries to nameservers. When each of these is
304 : : // started, we increase this value. When they fire, it is decreased
305 : : // again. We cannot delete ourselves until this value is back to 0.
306 : : //
307 : : // Note that the NSAS callback is *not* seen as an outstanding
308 : : // event; we can cancel the NSAS callback safely.
309 : : size_t outstanding_events_;
310 : :
311 : : // RTT Recorder. Used for testing, the RTTs of queries are
312 : : // sent to this object as well as being used to update the NSAS.
313 : : boost::shared_ptr<RttRecorder> rtt_recorder_;
314 : :
315 : : // perform a single lookup; first we check the cache to see
316 : : // if we have a response for our query stored already. if
317 : : // so, call handlerecursiveresponse(), if not, we call send()
318 : 3 : void doLookup() {
319 [ + - ][ + - ]: 6 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_LOOKUP)
320 [ + - ][ + - ]: 6 : .arg(questionText(question_));
321 : :
322 : 6 : Message cached_message(Message::RENDER);
323 [ + - ]: 3 : isc::resolve::initResponseMessage(question_, cached_message);
324 [ + - ][ - + ]: 3 : if (cache_.lookup(question_.getName(), question_.getType(),
325 : 6 : question_.getClass(), cached_message)) {
326 : :
327 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_FIND)
[ # # ]
328 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
329 : : // Should these be set by the cache too?
330 [ # # ][ # # ]: 0 : cached_message.setOpcode(Opcode::QUERY());
331 [ # # ][ # # ]: 0 : cached_message.setRcode(Rcode::NOERROR());
332 [ # # ]: 0 : cached_message.setHeaderFlag(Message::HEADERFLAG_QR);
333 [ # # ][ # # ]: 0 : if (handleRecursiveAnswer(cached_message)) {
334 [ # # ]: 0 : callCallback(true);
335 [ # # ]: 0 : stop();
336 : : }
337 : : } else {
338 : 3 : cur_zone_ = deepestDelegation(question_.getName(),
339 [ + - ][ + - ]: 6 : question_.getClass(), cache_);
340 [ + - ][ + - ]: 6 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
[ + - ]
341 [ + - ][ + - ]: 6 : .arg(questionText(question_)).arg(cur_zone_);
[ + - ][ + - ]
342 [ + - ]: 3 : send();
343 : : }
344 : :
345 : 3 : }
346 : :
347 : : // Send the current question to the given nameserver address
348 : : void sendTo(const isc::nsas::NameserverAddress& address) {
349 : : // We need to keep track of the Address, so that we can update
350 : : // the RTT
351 : 2 : current_ns_address = address;
352 : 2 : gettimeofday(¤t_ns_qsent_time, NULL);
353 : 2 : ++outstanding_events_;
354 [ + - ]: 2 : if (test_server_.second != 0) {
355 : : IOFetch query(protocol_, io_, question_,
356 : : test_server_.first,
357 : : test_server_.second, buffer_, this,
358 : 4 : query_timeout_, edns_);
359 [ + - ][ + - ]: 2 : io_.get_io_service().post(query);
[ + - ]
360 : : } else {
361 : : IOFetch query(protocol_, io_, question_,
362 : : current_ns_address.getAddress(),
363 : : 53, buffer_, this,
364 : 0 : query_timeout_, edns_);
365 [ # # ][ # # ]: 0 : io_.get_io_service().post(query);
[ # # ]
366 : : }
367 : : }
368 : :
369 : : // 'general' send, ask the NSAS to give us an address.
370 : 7 : void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
371 : 7 : protocol_ = protocol; // Store protocol being used for this
372 : 7 : edns_ = edns;
373 [ + + ]: 7 : if (test_server_.second != 0) {
374 : : // Send query to test server
375 [ + - ][ + - ]: 12 : LOG_DEBUG(isc::resolve::logger,
376 : : RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
377 [ + - ][ + - ]: 12 : .arg(questionText(question_)).arg(test_server_.first);
[ + - ]
378 : 6 : gettimeofday(¤t_ns_qsent_time, NULL);
379 : 6 : ++outstanding_events_;
380 : : IOFetch query(protocol, io_, question_,
381 : : test_server_.first,
382 : : test_server_.second, buffer_, this,
383 : 12 : query_timeout_, edns_);
384 [ + - ][ + - ]: 6 : io_.get_io_service().post(query);
[ + - ]
385 : :
386 : : } else {
387 : : // Ask the NSAS for an address for the current zone,
388 : : // the callback will call the actual sendTo()
389 [ + - ]: 2 : LOG_DEBUG(isc::resolve::logger,
390 : : RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
391 [ + - ]: 1 : .arg(cur_zone_);
392 : :
393 : : // Can we have multiple calls to nsas_out? Let's assume not
394 : : // for now
395 [ - + ]: 1 : assert(!nsas_callback_out_);
396 : 1 : nsas_callback_out_ = true;
397 [ + - ]: 2 : nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
398 : : }
399 : 7 : }
400 : :
401 : : // Called by our NSAS callback handler so we know we do not have
402 : : // an outstanding NSAS call anymore.
403 : 0 : void nsasCallbackCalled() {
404 : 3 : nsas_callback_out_ = false;
405 : 0 : }
406 : :
407 : : // This function is called by operator() and lookup();
408 : : // We have an answer either from a nameserver or the cache, and
409 : : // we do not know yet if this is a final answer we can send back or
410 : : // that more recursive processing needs to be done.
411 : : // Depending on the content, we go on recursing or return
412 : : //
413 : : // This method also updates the cache, depending on the content
414 : : // of the message
415 : : //
416 : : // returns true if we are done (either we have an answer or an
417 : : // error message)
418 : : // returns false if we are not done
419 : 6 : bool handleRecursiveAnswer(const Message& incoming) {
420 : :
421 : : // In case we get a CNAME, we store the target
422 : : // here (classify() will set it when it walks through
423 : : // the cname chain to verify it).
424 : 12 : Name cname_target(question_.getName());
425 : :
426 : : isc::resolve::ResponseClassifier::Category category =
427 : : isc::resolve::ResponseClassifier::classify(
428 [ + - ]: 6 : question_, incoming, cname_target, cname_count_);
429 : :
430 : 6 : bool found_ns = false;
431 : :
432 [ + - - + : 6 : switch (category) {
+ + - ]
433 : : case isc::resolve::ResponseClassifier::ANSWER:
434 : : case isc::resolve::ResponseClassifier::ANSWERCNAME:
435 : : // Answer received - copy and return.
436 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
[ + - ]
437 [ + - ][ + - ]: 4 : .arg(questionText(question_));
[ + - ]
438 [ + - ]: 2 : isc::resolve::copyResponseMessage(incoming, answer_message_);
439 [ + - ]: 2 : cache_.update(*answer_message_);
440 : : return (true);
441 : : break;
442 : :
443 : : case isc::resolve::ResponseClassifier::CNAME:
444 : : // CNAME received.
445 : :
446 : : // (unfinished) CNAME. We set our question_ to the CNAME
447 : : // target, then start over at the beginning (for now, that
448 : : // is, we reset our 'current servers' to the root servers).
449 [ # # ]: 0 : if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
450 : : // CNAME chain too long - just give up
451 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONG_CHAIN)
[ # # ]
452 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
453 [ # # ]: 0 : makeSERVFAIL();
454 : : return (true);
455 : : }
456 : :
457 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
[ # # ]
458 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
459 : :
460 : : answer_message_->appendSection(Message::SECTION_ANSWER,
461 [ # # ]: 0 : incoming);
462 : :
463 : 0 : question_ = Question(cname_target, question_.getClass(),
464 : 0 : question_.getType());
465 : :
466 : : // Follow CNAME chain.
467 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOW_CNAME)
[ # # ]
468 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
469 [ # # ]: 0 : doLookup();
470 : : return (false);
471 : : break;
472 : :
473 : : case isc::resolve::ResponseClassifier::NXDOMAIN:
474 : : case isc::resolve::ResponseClassifier::NXRRSET:
475 : : // Received NXDOMAIN or NXRRSET, just copy and return
476 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOM_NXRR)
[ # # ]
477 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
478 [ # # ]: 0 : isc::resolve::copyResponseMessage(incoming, answer_message_);
479 : : // no negcache yet
480 : : //cache_.update(*answer_message_);
481 : : return (true);
482 : : break;
483 : :
484 : : case isc::resolve::ResponseClassifier::REFERRAL:
485 : : // Response is a referral
486 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
[ + - ]
487 [ + - ][ + - ]: 4 : .arg(questionText(question_));
[ + - ]
488 : :
489 [ + - ]: 2 : cache_.update(incoming);
490 : : // Referral. For now we just take the first glue address
491 : : // we find and continue with that
492 : :
493 : : // auth section should have at least one RRset
494 : : // and one of them should be an NS (otherwise
495 : : // classifier should have error'd) to a subdomain
496 [ + - ][ # # ]: 8 : for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
[ - + ][ + - ]
[ + - ]
497 [ + - ][ + - ]: 4 : rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
[ + - ][ # # ]
498 : : ++rrsi) {
499 [ + - ]: 2 : ConstRRsetPtr rrs = *rrsi;
500 [ + - ][ + - ]: 2 : if (rrs->getType() == RRType::NS()) {
501 [ + - ][ + - ]: 2 : NameComparisonResult compare(Name(cur_zone_).compare(rrs->getName()));
[ + - ]
502 [ + - ]: 2 : if (compare.getRelation() == NameComparisonResult::SUPERDOMAIN) {
503 : : // TODO: make cur_zone_ a Name instead of a string
504 : : // (this requires a few API changes in related
505 : : // libraries, so as not to need many conversions)
506 [ + - ][ + - ]: 4 : cur_zone_ = rrs->getName().toText();
507 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFER_ZONE)
[ + - ]
508 [ + - ][ + - ]: 2 : .arg(cur_zone_);
509 : 2 : found_ns = true;
510 : : break;
511 : : }
512 : : }
513 : : }
514 : :
515 [ + - ]: 2 : if (found_ns) {
516 : : // next resolver round
517 : : // we do NOT use doLookup() here, but send() (i.e. we
518 : : // skip the cache), since if we had the final answer
519 : : // instead of a delegation cached, we would have been
520 : : // there by now.
521 [ + - ][ + - ]: 4 : GlueHints glue_hints(cur_zone_, incoming);
522 : :
523 : : // Ask the NSAS for an address, or glue.
524 : : // This will eventually result in either sendTo()
525 : : // or stop() being called by nsas_callback_
526 [ - + ]: 2 : assert(!nsas_callback_out_);
527 : 2 : nsas_callback_out_ = true;
528 : 2 : nsas_.lookup(cur_zone_, question_.getClass(),
529 [ + - ]: 2 : nsas_callback_, ANY_OK, glue_hints);
530 : : return (false);
531 : : } else {
532 : : // Referral was received but did not contain an NS RRset.
533 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NO_NS_RRSET)
[ # # ]
534 [ # # ][ # # ]: 0 : .arg(questionText(question_));
[ # # ]
535 : :
536 : : // TODO this will result in answering with the delegation. oh well
537 [ # # ]: 0 : isc::resolve::copyResponseMessage(incoming, answer_message_);
538 : : return (true);
539 : : }
540 : : break;
541 : :
542 : : case isc::resolve::ResponseClassifier::TRUNCATED:
543 : : // Truncated packet. If the protocol we used for the last one is
544 : : // UDP, re-query using TCP. Otherwise regard it as an error.
545 [ + - ]: 1 : if (protocol_ == IOFetch::UDP) {
546 [ + - ][ + - ]: 2 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
[ + - ]
547 [ + - ][ + - ]: 2 : RESLIB_TRUNCATED).arg(questionText(question_));
[ + - ]
548 [ + - ]: 1 : send(IOFetch::TCP);
549 : : return (false);
550 : : }
551 : :
552 : : // Was a TCP query so we have received a packet over TCP with the
553 : : // TC bit set: report an error by going to the common
554 : : // error code.
555 : : goto SERVFAIL;
556 : :
557 : : case isc::resolve::ResponseClassifier::RCODE:
558 : : // see if it's a FORMERR and a potential EDNS problem
559 [ + - ][ + - ]: 1 : if (incoming.getRcode() == Rcode::FORMERR()) {
560 [ + - ][ + - ]: 1 : if (protocol_ == IOFetch::UDP && edns_) {
561 : : // TODO: in case we absolutely need EDNS (i.e. for DNSSEC
562 : : // aware queries), we might want to try TCP before we give
563 : : // up. For now, just try UDP, no EDNS
564 [ + - ]: 1 : send(IOFetch::UDP, false);
565 : : return (false);
566 : : }
567 : :
568 : : // TC should take care of non-EDNS over UDP, fall through to
569 : : // SERVFAIL if we get FORMERR instead
570 : : }
571 : : goto SERVFAIL;
572 : :
573 : : default:
574 : : SERVFAIL:
575 : : // Some error in received packet it. Report it and return SERVFAIL
576 : : // to the caller.
577 [ # # ][ # # ]: 0 : if (logger.isDebugEnabled()) {
578 [ # # ]: 0 : reportResponseClassifierError(category, incoming.getRcode());
579 : : }
580 [ # # ]: 0 : makeSERVFAIL();
581 : : return (true);
582 : : }
583 : :
584 : : // If we get here, there is some serious logic error (or a missing
585 : : // "return").
586 : : assert(false);
587 : : return (true); // To keep the compiler happy
588 : : }
589 : :
590 : : /// \brief Report classification-detected error
591 : : ///
592 : : /// When the response classifier has detected an error in the response from
593 : : /// an upstream query, this method is called to log a debug message giving
594 : : /// information about the problem.
595 : : ///
596 : : /// \param category Classification code for the packet
597 : : /// \param rcode RCODE value in the packet
598 : : void reportResponseClassifierError(ResponseClassifier::Category category,
599 : : const Rcode& rcode)
600 : : {
601 : : // We could set up a table of response classifications to message
602 : : // IDs here and index into that table. But given that (a) C++ does
603 : : // not have C's named initializers, (b) the codes for the
604 : : // response classifier are in another module and (c) not all messages
605 : : // have the same number of arguments, the setup of the table would be
606 : : // almost as long as the code here: it would need to include a number
607 : : // of assertions to ensure that any change to the the response
608 : : // classifier codes was detected, and the checking logic would need to
609 : : // check that the numeric value of the code lay within the defined
610 : : // limits of the table.
611 : :
612 [ # # ]: 0 : if (category == ResponseClassifier::RCODE) {
613 : :
614 : : // Special case as this message takes two arguments.
615 [ # # ][ # # ]: 0 : LOG_DEBUG(logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_ERROR).
[ # # ]
616 [ # # ][ # # ]: 0 : arg(questionText(question_)).arg(rcode);
[ # # ][ # # ]
617 : :
618 : : } else {
619 : :
620 : : isc::log::MessageID message_id;
621 [ # # # # : 0 : switch (category) {
# # # # #
# # # ]
622 : : case ResponseClassifier::TRUNCATED:
623 : 0 : message_id = RESLIB_TCP_TRUNCATED;
624 : : break;
625 : :
626 : : case ResponseClassifier::EMPTY:
627 : 0 : message_id = RESLIB_EMPTY_RESPONSE;
628 : : break;
629 : :
630 : : case ResponseClassifier::EXTRADATA:
631 : 0 : message_id = RESLIB_EXTRADATA_RESPONSE;
632 : : break;
633 : :
634 : : case ResponseClassifier::INVNAMCLASS:
635 : 0 : message_id = RESLIB_INVALID_NAMECLASS_RESPONSE;
636 : : break;
637 : :
638 : : case ResponseClassifier::INVTYPE:
639 : 0 : message_id = RESLIB_INVALID_TYPE_RESPONSE;
640 : : break;
641 : :
642 : : case ResponseClassifier::MISMATQUEST:
643 : 0 : message_id = RESLIB_INVALID_QNAME_RESPONSE;
644 : : break;
645 : :
646 : : case ResponseClassifier::MULTICLASS:
647 : 0 : message_id = RESLIB_MULTIPLE_CLASS_RESPONSE;
648 : : break;
649 : :
650 : : case ResponseClassifier::NOTONEQUEST:
651 : 0 : message_id = RESLIB_NOT_ONE_QNAME_RESPONSE;
652 : : break;
653 : :
654 : : case ResponseClassifier::NOTRESPONSE:
655 : 0 : message_id = RESLIB_NOT_RESPONSE;
656 : : break;
657 : :
658 : : case ResponseClassifier::NOTSINGLE:
659 : 0 : message_id = RESLIB_NOTSINGLE_RESPONSE;
660 : : break;
661 : :
662 : : case ResponseClassifier::OPCODE:
663 : 0 : message_id = RESLIB_OPCODE_RESPONSE;
664 : : break;
665 : :
666 : : default:
667 : 0 : message_id = RESLIB_ERROR_RESPONSE;
668 : : break;
669 : : }
670 [ # # ][ # # ]: 0 : LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
[ # # ]
671 [ # # ][ # # ]: 0 : arg(questionText(question_));
[ # # ]
672 : : }
673 : : }
674 : :
675 : : public:
676 : 3 : RunningQuery(IOService& io,
677 : : const Question& question,
678 : : MessagePtr answer_message,
679 : : std::pair<std::string, uint16_t>& test_server,
680 : : OutputBufferPtr buffer,
681 : : isc::resolve::ResolverInterface::CallbackPtr cb,
682 : : int query_timeout, int client_timeout, int lookup_timeout,
683 : : unsigned retries,
684 : : isc::nsas::NameserverAddressStore& nsas,
685 : : isc::cache::ResolverCache& cache,
686 : : boost::shared_ptr<RttRecorder>& recorder)
687 : : :
688 : : io_(io),
689 : : question_(question),
690 : : query_message_(),
691 : : answer_message_(answer_message),
692 : : test_server_(test_server),
693 : : buffer_(buffer),
694 : : resolvercallback_(cb),
695 : : protocol_(IOFetch::UDP),
696 : : cname_count_(0),
697 : : query_timeout_(query_timeout),
698 : : retries_(retries),
699 [ + - ]: 3 : client_timer(io.get_io_service()),
700 [ + - ]: 3 : lookup_timer(io.get_io_service()),
701 : : done_(false),
702 : : callback_called_(false),
703 : : nsas_(nsas),
704 : : cache_(cache),
705 : : cur_zone_("."),
706 : : nsas_callback_(),
707 : : nsas_callback_out_(false),
708 : : outstanding_events_(0),
709 [ + - ][ + - ]: 6 : rtt_recorder_(recorder)
[ + - ]
710 : : {
711 : : // Set here to avoid using "this" in initializer list.
712 [ + - ]: 3 : nsas_callback_.reset(new ResolverNSASCallback(this));
713 : :
714 : : // Setup the timer to stop trying (lookup_timeout)
715 [ + - ]: 3 : if (lookup_timeout >= 0) {
716 : : lookup_timer.expires_from_now(
717 [ + - ]: 3 : boost::posix_time::milliseconds(lookup_timeout));
718 : 3 : ++outstanding_events_;
719 [ + - ]: 3 : lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
720 : : }
721 : :
722 : : // Setup the timer to send an answer (client_timeout)
723 [ + - ]: 3 : if (client_timeout >= 0) {
724 : : client_timer.expires_from_now(
725 [ + - ]: 3 : boost::posix_time::milliseconds(client_timeout));
726 : 3 : ++outstanding_events_;
727 [ + - ]: 3 : client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
728 : : }
729 : :
730 [ + - ]: 3 : doLookup();
731 : 3 : }
732 : :
733 : : // called if we have a lookup timeout; if our callback has
734 : : // not been called, call it now. Then stop.
735 : 0 : void lookupTimeout() {
736 [ # # ]: 0 : if (!callback_called_) {
737 : 0 : makeSERVFAIL();
738 : 0 : callCallback(true);
739 : : }
740 [ # # ]: 0 : assert(outstanding_events_ > 0);
741 : 0 : --outstanding_events_;
742 : 0 : stop();
743 : 0 : }
744 : :
745 : : // called if we have a client timeout; if our callback has
746 : : // not been called, call it now. But do not stop.
747 : 0 : void clientTimeout() {
748 [ # # ]: 0 : if (!callback_called_) {
749 : 0 : makeSERVFAIL();
750 : 0 : callCallback(true);
751 : : }
752 [ # # ]: 0 : assert(outstanding_events_ > 0);
753 : 0 : --outstanding_events_;
754 [ # # ]: 0 : if (outstanding_events_ == 0) {
755 : 0 : stop();
756 : : }
757 : 0 : }
758 : :
759 : : // If the callback has not been called yet, call it now
760 : : // If success is true, we call 'success' with our answer_message
761 : : // If it is false, we call failure()
762 : 3 : void callCallback(bool success) {
763 [ + - ]: 3 : if (!callback_called_) {
764 : 3 : callback_called_ = true;
765 : :
766 : : // There are two types of messages we could store in the
767 : : // cache;
768 : : // 1. answers to our fetches from authoritative servers,
769 : : // exactly as we receive them, and
770 : : // 2. answers to queries we received from clients, which
771 : : // have received additional processing (following CNAME
772 : : // chains, for instance)
773 : : //
774 : : // Doing only the first would mean we would have to re-do
775 : : // processing when we get data from our cache, and doing
776 : : // only the second would miss out on the side-effect of
777 : : // having nameserver data in our cache.
778 : : //
779 : : // So right now we do both. Since the cache (currently)
780 : : // stores Messages on their question section only, this
781 : : // does mean that we overwrite the messages we stored in
782 : : // the previous iteration if we are following a delegation.
783 [ + - ]: 3 : if (success) {
784 [ + - ]: 6 : resolvercallback_->success(answer_message_);
785 : : } else {
786 : 0 : resolvercallback_->failure();
787 : : }
788 : : }
789 : 3 : }
790 : :
791 : : // We are done. If there are no more outstanding events, we delete
792 : : // ourselves. If there are any, we do not.
793 : 3 : void stop() {
794 : 3 : done_ = true;
795 [ - + ]: 3 : if (nsas_callback_out_) {
796 [ # # ]: 0 : nsas_.cancel(cur_zone_, question_.getClass(), nsas_callback_);
797 : 0 : nsas_callback_out_ = false;
798 : : }
799 : 3 : client_timer.cancel();
800 : 3 : lookup_timer.cancel();
801 [ - + ]: 3 : if (outstanding_events_ > 0) {
802 : 3 : return;
803 : : } else {
804 [ # # ]: 0 : delete this;
805 : : }
806 : : }
807 : :
808 : : // This function is used as callback from DNSQuery.
809 : 8 : virtual void operator()(IOFetch::Result result) {
810 : : // XXX is this the place for TCP retry?
811 [ - + ]: 8 : assert(outstanding_events_ > 0);
812 : 8 : --outstanding_events_;
813 : :
814 [ + - ][ + + ]: 8 : if (!done_ && result != IOFetch::TIME_OUT) {
815 : : // we got an answer
816 : :
817 : : // Update the NSAS with the time it took
818 : : struct timeval cur_time;
819 : 7 : gettimeofday(&cur_time, NULL);
820 : 7 : uint32_t rtt = 0;
821 : :
822 : : // Only calculate RTT if it is positive
823 [ + - ][ + - ]: 7 : if (cur_time.tv_sec > current_ns_qsent_time.tv_sec ||
[ + - ]
824 : : (cur_time.tv_sec == current_ns_qsent_time.tv_sec &&
825 : : cur_time.tv_usec > current_ns_qsent_time.tv_usec)) {
826 : 7 : rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
827 : 7 : rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
828 : : }
829 [ + - ][ + - ]: 7 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
830 : 7 : current_ns_address.updateRTT(rtt);
831 [ + - ]: 7 : if (rtt_recorder_) {
832 : 14 : rtt_recorder_->addRtt(rtt);
833 : : }
834 : :
835 : : try {
836 [ + - ][ + - ]: 14 : Message incoming(Message::PARSE);
837 : 7 : InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
838 : :
839 [ + + ]: 7 : incoming.fromWire(ibuf);
840 : :
841 : 6 : buffer_->clear();
842 [ + - ]: 6 : done_ = handleRecursiveAnswer(incoming);
843 [ + + ]: 6 : if (done_) {
844 [ + - ]: 2 : callCallback(true);
845 [ + - ]: 2 : stop();
846 : : }
847 [ - + ]: 2 : } catch (const isc::dns::DNSProtocolError& dpe) {
848 : : // Right now, we treat this similar to timeouts
849 : : // (except we don't store RTT)
850 : : // We probably want to make this an integral part
851 : : // of the fetch data process. (TODO)
852 [ + - ]: 1 : if (retries_--) {
853 : : // Retry
854 [ - + ][ + - ]: 2 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
[ - + ]
855 : : RESLIB_PROTOCOL_RETRY)
856 [ - + ][ - + ]: 2 : .arg(questionText(question_)).arg(dpe.what())
[ - + ][ - + ]
857 [ - + ]: 1 : .arg(retries_);
858 [ - + ]: 1 : send();
859 : : } else {
860 : : // Give up
861 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
[ # # ]
862 : : RESLIB_PROTOCOL)
863 [ # # ][ # # ]: 0 : .arg(questionText(question_)).arg(dpe.what());
[ # # ][ # # ]
864 [ # # ]: 0 : if (!callback_called_) {
865 [ # # ]: 0 : makeSERVFAIL();
866 [ # # ]: 0 : callCallback(true);
867 : : }
868 [ # # ]: 0 : stop();
869 : : }
870 : : }
871 [ + - ][ - + ]: 1 : } else if (!done_ && retries_--) {
[ + - ]
872 : : // Query timed out, but we have some retries, so send again
873 [ + - ][ + - ]: 2 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT_RETRY)
874 [ + - ][ + - ]: 2 : .arg(questionText(question_))
[ + - ]
875 [ + - ][ + - ]: 3 : .arg(current_ns_address.getAddress().toText()).arg(retries_);
876 : 1 : current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
877 : 1 : send();
878 : : } else {
879 : : // We are either already done, or out of retries
880 [ # # ]: 0 : if (result == IOFetch::TIME_OUT) {
881 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
882 [ # # ][ # # ]: 0 : .arg(questionText(question_))
[ # # ]
883 [ # # ]: 0 : .arg(current_ns_address.getAddress().toText());
884 : 0 : current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
885 : : }
886 [ # # ]: 0 : if (!callback_called_) {
887 : 0 : makeSERVFAIL();
888 : 0 : callCallback(true);
889 : : }
890 : 0 : stop();
891 : : }
892 : 8 : }
893 : :
894 : : // Clear the answer parts of answer_message, and set the rcode
895 : : // to servfail
896 : 1 : void makeSERVFAIL() {
897 [ + - ]: 2 : isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
898 : 1 : }
899 : : };
900 : :
901 [ # # ][ # # ]: 0 : class ForwardQuery : public IOFetch::Callback {
902 : : private:
903 : : // The io service to handle async calls
904 : : IOService& io_;
905 : :
906 : : // This is the query message got from client
907 : : ConstMessagePtr query_message_;
908 : :
909 : : // This is where we build and store our final answer
910 : : MessagePtr answer_message_;
911 : :
912 : : // List of nameservers to forward to
913 : : boost::shared_ptr<AddressVector> upstream_;
914 : :
915 : : // Buffer to store the result.
916 : : OutputBufferPtr buffer_;
917 : :
918 : : // This will be notified when we succeed or fail
919 : : isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
920 : :
921 : : /*
922 : : * TODO Do something more clever with timeouts. In the long term, some
923 : : * computation of average RTT, increase with each retry, etc.
924 : : */
925 : : // Timeout information
926 : : int query_timeout_;
927 : :
928 : : // TODO: replace by our wrapper
929 : : asio::deadline_timer client_timer;
930 : : asio::deadline_timer lookup_timer;
931 : :
932 : : // Make FowardQuery deletes itself safely. for more information see
933 : : // the comments of outstanding_events in RunningQuery.
934 : : size_t outstanding_events_;
935 : :
936 : : // If we have a client timeout, we call back with a failure message,
937 : : // but we do not stop yet. We use this variable to make sure we
938 : : // don't call back a second time later
939 : : bool callback_called_;
940 : :
941 : : // send the query to the server.
942 : : void send(IOFetch::Protocol protocol = IOFetch::UDP) {
943 : 5 : const int uc = upstream_->size();
944 : 5 : buffer_->clear();
945 : 5 : int serverIndex = rand() % uc;
946 [ + - ][ + - ]: 10 : ConstQuestionPtr question = *(query_message_->beginQuestion());
[ + - ]
947 [ + - ][ + - ]: 10 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
[ + - ]
948 [ + - ][ + - ]: 10 : .arg(questionText(*question))
[ + - ]
949 [ + - ][ + - ]: 10 : .arg(upstream_->at(serverIndex).first);
950 : :
951 : 5 : ++outstanding_events_;
952 : : // Forward the query, create the IOFetch with
953 : : // query message, so that query flags can be forwarded
954 : : // together.
955 : : IOFetch query(protocol, io_, query_message_,
956 [ + - ]: 5 : upstream_->at(serverIndex).first,
957 [ + - ]: 5 : upstream_->at(serverIndex).second,
958 [ + - ][ + - ]: 15 : buffer_, this, query_timeout_);
[ + - ]
959 : :
960 [ + - ][ + - ]: 5 : io_.get_io_service().post(query);
[ + - ]
961 : : }
962 : :
963 : : public:
964 : : ForwardQuery(IOService& io,
965 : : ConstMessagePtr query_message,
966 : : MessagePtr answer_message,
967 : : boost::shared_ptr<AddressVector> upstream,
968 : : OutputBufferPtr buffer,
969 : : isc::resolve::ResolverInterface::CallbackPtr cb,
970 : : int query_timeout, int client_timeout, int lookup_timeout) :
971 : : io_(io),
972 : : query_message_(query_message),
973 : : answer_message_(answer_message),
974 : : upstream_(upstream),
975 : : buffer_(buffer),
976 : : resolvercallback_(cb),
977 : : query_timeout_(query_timeout),
978 [ + - ]: 5 : client_timer(io.get_io_service()),
979 [ + - ]: 5 : lookup_timer(io.get_io_service()),
980 : : outstanding_events_(0),
981 [ + - ][ + - ]: 10 : callback_called_(false)
982 : : {
983 : : // Setup the timer to stop trying (lookup_timeout)
984 [ + - ]: 5 : if (lookup_timeout >= 0) {
985 : : lookup_timer.expires_from_now(
986 [ + - ]: 5 : boost::posix_time::milliseconds(lookup_timeout));
987 : 5 : ++outstanding_events_;
988 [ + - ]: 5 : lookup_timer.async_wait(boost::bind(&ForwardQuery::lookupTimeout, this));
989 : : }
990 : :
991 : : // Setup the timer to send an answer (client_timeout)
992 [ + - ]: 5 : if (client_timeout >= 0) {
993 : : client_timer.expires_from_now(
994 [ + - ]: 5 : boost::posix_time::milliseconds(client_timeout));
995 : 5 : ++outstanding_events_;
996 [ + - ]: 5 : client_timer.async_wait(boost::bind(&ForwardQuery::clientTimeout, this));
997 : : }
998 : :
999 : : send();
1000 : : }
1001 : :
1002 : 3 : virtual void lookupTimeout() {
1003 [ + - ]: 3 : if (!callback_called_) {
1004 : 3 : makeSERVFAIL();
1005 : 3 : callCallback(false);
1006 : : }
1007 [ - + ]: 3 : assert(outstanding_events_ > 0);
1008 : 3 : --outstanding_events_;
1009 : 3 : stop();
1010 : 3 : }
1011 : :
1012 : 1 : virtual void clientTimeout() {
1013 [ + - ]: 1 : if (!callback_called_) {
1014 : 1 : makeSERVFAIL();
1015 : 1 : callCallback(false);
1016 : : }
1017 [ - + ]: 1 : assert(outstanding_events_ > 0);
1018 : 1 : --outstanding_events_;
1019 : 1 : stop();
1020 : 1 : }
1021 : :
1022 : : // If the callback has not been called yet, call it now
1023 : : // If success is true, we call 'success' with our answer_message
1024 : : // If it is false, we call failure()
1025 : 4 : void callCallback(bool success) {
1026 [ + - ]: 4 : if (!callback_called_) {
1027 : 4 : callback_called_ = true;
1028 [ - + ]: 4 : if (success) {
1029 [ # # ]: 0 : resolvercallback_->success(answer_message_);
1030 : : } else {
1031 : 4 : resolvercallback_->failure();
1032 : : }
1033 : : }
1034 : 4 : }
1035 : :
1036 : 5 : virtual void stop() {
1037 : : // if we cancel our timers, we will still get an event for
1038 : : // that, so we cannot delete ourselves just yet (those events
1039 : : // would be bound to a deleted object)
1040 : : // cancel them one by one, both cancels should get us back
1041 : : // here again.
1042 : : // same goes if we have an outstanding query (can't delete
1043 : : // until that one comes back to us)
1044 : 5 : lookup_timer.cancel();
1045 : 5 : client_timer.cancel();
1046 [ - + ]: 5 : if (outstanding_events_ > 0) {
1047 : 5 : return;
1048 : : } else {
1049 [ # # ]: 0 : delete this;
1050 : : }
1051 : : }
1052 : :
1053 : : // This function is used as callback from DNSQuery.
1054 : 1 : virtual void operator()(IOFetch::Result result) {
1055 : : // XXX is this the place for TCP retry?
1056 [ - + ]: 1 : assert(outstanding_events_ > 0);
1057 : 1 : --outstanding_events_;
1058 [ - + ]: 1 : if (result != IOFetch::TIME_OUT) {
1059 : : // we got an answer
1060 : 0 : Message incoming(Message::PARSE);
1061 : 0 : InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
1062 [ # # ]: 0 : incoming.fromWire(ibuf);
1063 [ # # ]: 0 : isc::resolve::copyResponseMessage(incoming, answer_message_);
1064 [ # # ]: 0 : callCallback(true);
1065 : : }
1066 : :
1067 : 1 : stop();
1068 : 1 : }
1069 : :
1070 : : // Clear the answer parts of answer_message, and set the rcode
1071 : : // to servfail
1072 : 4 : void makeSERVFAIL() {
1073 [ + - ]: 8 : isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
1074 : 4 : }
1075 : : };
1076 : :
1077 : : }
1078 : :
1079 : : void
1080 : 2 : RecursiveQuery::resolve(const QuestionPtr& question,
1081 : : const isc::resolve::ResolverInterface::CallbackPtr callback)
1082 : : {
1083 : 2 : IOService& io = dns_service_.getIOService();
1084 : :
1085 [ + - ]: 2 : MessagePtr answer_message(new Message(Message::RENDER));
1086 [ + - ]: 2 : isc::resolve::initResponseMessage(*question, *answer_message);
1087 : :
1088 [ + - ]: 2 : OutputBufferPtr buffer(new OutputBuffer(0));
1089 : :
1090 : : // First try to see if we have something cached in the messagecache
1091 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
[ + - ]
1092 [ + - ][ + - ]: 4 : .arg(questionText(*question)).arg(1);
[ + - ][ + - ]
1093 [ - + ][ # # ]: 4 : if (cache_.lookup(question->getName(), question->getType(),
[ - + ]
1094 [ + - ]: 4 : question->getClass(), *answer_message) &&
1095 [ # # ]: 0 : answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
1096 : : // Message found, return that
1097 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
[ # # ]
1098 [ # # ][ # # ]: 0 : .arg(questionText(*question)).arg(1);
[ # # ][ # # ]
1099 : :
1100 : : // TODO: err, should cache set rcode as well?
1101 [ # # ][ # # ]: 0 : answer_message->setRcode(Rcode::NOERROR());
1102 [ # # ]: 0 : callback->success(answer_message);
1103 : : } else {
1104 : : // Perhaps we only have the one RRset?
1105 : : // TODO: can we do this? should we check for specific types only?
1106 : 2 : RRsetPtr cached_rrset = cache_.lookup(question->getName(),
1107 : 2 : question->getType(),
1108 [ + - ]: 4 : question->getClass());
1109 [ - + ]: 2 : if (cached_rrset) {
1110 : : // Found single RRset in cache
1111 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
[ # # ]
1112 [ # # ][ # # ]: 0 : .arg(questionText(*question)).arg(1);
[ # # ][ # # ]
1113 : : answer_message->addRRset(Message::SECTION_ANSWER,
1114 [ # # ]: 0 : cached_rrset);
1115 [ # # ][ # # ]: 0 : answer_message->setRcode(Rcode::NOERROR());
1116 [ # # ]: 0 : callback->success(answer_message);
1117 : : } else {
1118 : : // Message not found in cache, start recursive query. It will
1119 : : // delete itself when it is done
1120 [ + - ][ + - ]: 4 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
[ + - ]
1121 [ + - ][ + - ]: 4 : .arg(questionText(*question)).arg(1);
[ + - ][ + - ]
1122 : : new RunningQuery(io, *question, answer_message,
1123 : : test_server_, buffer, callback,
1124 : : query_timeout_, client_timeout_,
1125 : : lookup_timeout_, retries_, nsas_,
1126 [ + - ][ + - ]: 4 : cache_, rtt_recorder_);
1127 : : }
1128 : : }
1129 : 2 : }
1130 : :
1131 : : void
1132 : 1 : RecursiveQuery::resolve(const Question& question,
1133 : : MessagePtr answer_message,
1134 : : OutputBufferPtr buffer,
1135 : : DNSServer* server)
1136 : : {
1137 : : // XXX: eventually we will need to be able to determine whether
1138 : : // the message should be sent via TCP or UDP, or sent initially via
1139 : : // UDP and then fall back to TCP on failure, but for the moment
1140 : : // we're only going to handle UDP.
1141 : 1 : IOService& io = dns_service_.getIOService();
1142 : :
1143 : : isc::resolve::ResolverInterface::CallbackPtr crs(
1144 : 2 : new isc::resolve::ResolverCallbackServer(server));
1145 : :
1146 : : // TODO: general 'prepareinitialanswer'
1147 [ + - ][ + - ]: 1 : answer_message->setOpcode(isc::dns::Opcode::QUERY());
1148 [ + - ]: 1 : answer_message->addQuestion(question);
1149 : :
1150 : : // First try to see if we have something cached in the messagecache
1151 [ + - ][ + - ]: 2 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
[ + - ]
1152 [ + - ][ + - ]: 2 : .arg(questionText(question)).arg(2);
[ + - ][ + - ]
1153 : :
1154 [ - + ][ # # ]: 1 : if (cache_.lookup(question.getName(), question.getType(),
[ - + ]
1155 [ + - ]: 1 : question.getClass(), *answer_message) &&
1156 [ # # ]: 0 : answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
1157 : :
1158 : : // Message found, return that
1159 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
[ # # ]
1160 [ # # ][ # # ]: 0 : .arg(questionText(question)).arg(2);
[ # # ][ # # ]
1161 : : // TODO: err, should cache set rcode as well?
1162 [ # # ][ # # ]: 0 : answer_message->setRcode(Rcode::NOERROR());
1163 [ # # ]: 0 : crs->success(answer_message);
1164 : : } else {
1165 : : // Perhaps we only have the one RRset?
1166 : : // TODO: can we do this? should we check for specific types only?
1167 : 1 : RRsetPtr cached_rrset = cache_.lookup(question.getName(),
1168 : 1 : question.getType(),
1169 [ + - ]: 1 : question.getClass());
1170 [ - + ]: 1 : if (cached_rrset) {
1171 : : // Found single RRset in cache
1172 [ # # ][ # # ]: 0 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
[ # # ]
1173 [ # # ][ # # ]: 0 : .arg(questionText(question)).arg(2);
[ # # ][ # # ]
1174 : : answer_message->addRRset(Message::SECTION_ANSWER,
1175 [ # # ]: 0 : cached_rrset);
1176 [ # # ][ # # ]: 0 : answer_message->setRcode(Rcode::NOERROR());
1177 [ # # ]: 0 : crs->success(answer_message);
1178 : :
1179 : : } else {
1180 : : // Message not found in cache, start recursive query. It will
1181 : : // delete itself when it is done
1182 [ + - ][ + - ]: 2 : LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
[ + - ]
1183 [ + - ][ + - ]: 2 : .arg(questionText(question)).arg(2);
[ + - ][ + - ]
1184 : : new RunningQuery(io, question, answer_message,
1185 : : test_server_, buffer, crs, query_timeout_,
1186 : : client_timeout_, lookup_timeout_, retries_,
1187 [ + - ][ + - ]: 1 : nsas_, cache_, rtt_recorder_);
1188 : : }
1189 : : }
1190 : 1 : }
1191 : :
1192 : : void
1193 : 5 : RecursiveQuery::forward(ConstMessagePtr query_message,
1194 : : MessagePtr answer_message,
1195 : : OutputBufferPtr buffer,
1196 : : DNSServer* server,
1197 : : isc::resolve::ResolverInterface::CallbackPtr callback)
1198 : : {
1199 : : // XXX: eventually we will need to be able to determine whether
1200 : : // the message should be sent via TCP or UDP, or sent initially via
1201 : : // UDP and then fall back to TCP on failure, but for the moment
1202 : : // we're only going to handle UDP.
1203 : 5 : IOService& io = dns_service_.getIOService();
1204 : :
1205 [ + + ]: 5 : if (!callback) {
1206 : 1 : callback.reset(new isc::resolve::ResolverCallbackServer(server));
1207 : : }
1208 : :
1209 : : // TODO: general 'prepareinitialanswer'
1210 : 5 : answer_message->setOpcode(isc::dns::Opcode::QUERY());
1211 [ + - ][ + - ]: 10 : ConstQuestionPtr question = *query_message->beginQuestion();
1212 [ + - ]: 5 : answer_message->addQuestion(*question);
1213 : :
1214 : : // implement the simplest forwarder, which will pass
1215 : : // everything throught without interpretation, except
1216 : : // QID, port number. The response will not be cached.
1217 : : // It will delete itself when it is done
1218 : : new ForwardQuery(io, query_message, answer_message,
1219 : : upstream_, buffer, callback, query_timeout_,
1220 [ + - ]: 5 : client_timeout_, lookup_timeout_);
1221 : 5 : }
1222 : :
1223 : : } // namespace asiodns
1224 [ + - ][ + - ]: 6 : } // namespace isc
|