Branch data Line data Source code
1 : : // Copyright (C) 2009 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 <stdint.h>
18 : : #include <sys/types.h>
19 : : #include <netinet/in.h>
20 : :
21 : : #include <algorithm>
22 : : #include <vector>
23 : : #include <cassert>
24 : :
25 : : #include <boost/shared_ptr.hpp>
26 : : #include <boost/foreach.hpp>
27 : :
28 : : #include <exceptions/exceptions.h>
29 : :
30 : : #include <acl/dns.h>
31 : : #include <acl/loader.h>
32 : :
33 : : #include <asiodns/asiodns.h>
34 : : #include <asiolink/asiolink.h>
35 : :
36 : : #include <config/ccsession.h>
37 : :
38 : : #include <exceptions/exceptions.h>
39 : :
40 : : #include <util/buffer.h>
41 : :
42 : : #include <dns/opcode.h>
43 : : #include <dns/rcode.h>
44 : : #include <dns/exceptions.h>
45 : : #include <dns/name.h>
46 : : #include <dns/question.h>
47 : : #include <dns/rrset.h>
48 : : #include <dns/rrttl.h>
49 : : #include <dns/message.h>
50 : : #include <dns/messagerenderer.h>
51 : :
52 : : #include <server_common/client.h>
53 : : #include <server_common/portconfig.h>
54 : :
55 : : #include <resolve/recursive_query.h>
56 : :
57 : : #include "resolver.h"
58 : : #include "resolver_log.h"
59 : :
60 : : using namespace std;
61 : : using namespace isc;
62 : : using namespace isc::util;
63 : : using namespace isc::acl;
64 : : using isc::acl::dns::RequestACL;
65 : : using namespace isc::dns;
66 : : using namespace isc::data;
67 : : using namespace isc::config;
68 : : using namespace isc::asiodns;
69 : : using namespace isc::asiolink;
70 : : using namespace isc::server_common;
71 : : using namespace isc::server_common::portconfig;
72 : :
73 : : class ResolverImpl {
74 : : private:
75 : : // prohibit copy
76 : : ResolverImpl(const ResolverImpl& source);
77 : : ResolverImpl& operator=(const ResolverImpl& source);
78 : : public:
79 : 33 : ResolverImpl() :
80 : : config_session_(NULL),
81 : : query_timeout_(2000),
82 : : client_timeout_(4000),
83 : : lookup_timeout_(30000),
84 : : retries_(3),
85 : : // we apply "reject all" (implicit default of the loader) ACL by
86 : : // default:
87 [ + - ]: 66 : query_acl_(acl::dns::getRequestLoader().load(Element::fromJSON("[]"))),
88 [ + - ][ + - ]: 66 : rec_query_(NULL)
[ + - ]
89 : 33 : {}
90 : :
91 : 33 : ~ResolverImpl() {
92 [ + - ]: 33 : queryShutdown();
93 : : }
94 : :
95 : : void querySetup(DNSServiceBase& dnss,
96 : : isc::nsas::NameserverAddressStore& nsas,
97 : : isc::cache::ResolverCache& cache)
98 : : {
99 [ - + ]: 7 : assert(!rec_query_); // queryShutdown must be called first
100 [ + - ][ + - ]: 7 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUERY_SETUP);
[ + - ][ + - ]
101 : : rec_query_ = new RecursiveQuery(dnss,
102 : : nsas, cache,
103 : : upstream_,
104 : : upstream_root_,
105 : : query_timeout_,
106 : : client_timeout_,
107 : : lookup_timeout_,
108 [ + - ][ + - ]: 7 : retries_);
109 : : }
110 : :
111 : 40 : void queryShutdown() {
112 : : // only shut down if we have actually called querySetup before
113 : : // (this is not a safety check, just to prevent logging of
114 : : // actions that are not performed
115 [ + + ]: 40 : if (rec_query_) {
116 [ + - ]: 14 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT,
117 : 7 : RESOLVER_QUERY_SHUTDOWN);
118 [ + - ]: 14 : delete rec_query_;
119 : 7 : rec_query_ = NULL;
120 : : }
121 : 40 : }
122 : :
123 : 4 : void setForwardAddresses(const AddressList& upstream,
124 : : DNSServiceBase* dnss)
125 : : {
126 : 4 : upstream_ = upstream;
127 [ + - ]: 4 : if (dnss != NULL) {
128 [ + + ]: 4 : if (!upstream_.empty()) {
129 [ + + ][ + - ]: 11 : BOOST_FOREACH(const AddressPair& address, upstream) {
[ + - ][ + + ]
[ + + ]
130 [ + - ]: 9 : LOG_INFO(resolver_logger, RESOLVER_FORWARD_ADDRESS)
131 [ + - ][ + - ]: 3 : .arg(address.first).arg(address.second);
132 : : }
133 : : } else {
134 [ + - ]: 2 : LOG_INFO(resolver_logger, RESOLVER_RECURSIVE);
135 : : }
136 : : }
137 : 4 : }
138 : :
139 : 3 : void setRootAddresses(const AddressList& upstream_root,
140 : : DNSServiceBase* dnss)
141 : : {
142 : 3 : upstream_root_ = upstream_root;
143 [ + - ]: 3 : if (dnss != NULL) {
144 [ + + ]: 3 : if (!upstream_root_.empty()) {
145 [ + + ][ + - ]: 8 : BOOST_FOREACH(const AddressPair& address, upstream_root) {
[ + - ][ + + ]
[ + + ]
146 [ + - ]: 6 : LOG_INFO(resolver_logger, RESOLVER_SET_ROOT_ADDRESS)
147 [ + - ][ + - ]: 2 : .arg(address.first).arg(address.second);
148 : : }
149 : : } else {
150 [ + - ]: 1 : LOG_WARN(resolver_logger, RESOLVER_NO_ROOT_ADDRESS);
151 : : }
152 : : }
153 : 3 : }
154 : :
155 : : void resolve(const isc::dns::QuestionPtr& question,
156 : : const isc::resolve::ResolverInterface::CallbackPtr& callback);
157 : :
158 : : enum NormalQueryResult { RECURSION, DROPPED, ERROR };
159 : : NormalQueryResult processNormalQuery(const IOMessage& io_message,
160 : : MessagePtr query_message,
161 : : MessagePtr answer_message,
162 : : OutputBufferPtr buffer,
163 : : DNSServer* server);
164 : :
165 : : const RequestACL& getQueryACL() const {
166 : : return (*query_acl_);
167 : : }
168 : :
169 : : void setQueryACL(boost::shared_ptr<const RequestACL> new_acl) {
170 : 20 : query_acl_ = new_acl;
171 : : }
172 : :
173 : : /// Currently non-configurable, but will be.
174 : : static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
175 : :
176 : : /// These members are public because Resolver accesses them directly.
177 : : ModuleCCSession* config_session_;
178 : : /// Addresses of the root nameserver(s)
179 : : AddressList upstream_root_;
180 : : /// Addresses of the forward nameserver
181 : : AddressList upstream_;
182 : : /// Addresses we listen on
183 : : AddressList listen_;
184 : :
185 : : /// Timeout for outgoing queries in milliseconds
186 : : int query_timeout_;
187 : : /// Timeout for incoming client queries in milliseconds
188 : : int client_timeout_;
189 : : /// Timeout for lookup processing in milliseconds
190 : : int lookup_timeout_;
191 : :
192 : : /// Number of retries after timeout
193 : : unsigned retries_;
194 : :
195 : : private:
196 : : /// ACL on incoming queries
197 : : boost::shared_ptr<const RequestACL> query_acl_;
198 : :
199 : : /// Object to handle upstream queries
200 : : RecursiveQuery* rec_query_;
201 : : };
202 : :
203 : : /*
204 : : * std::for_each has a broken interface. It makes no sense in a language
205 : : * without lambda functions/closures. These two classes emulate the lambda
206 : : * functions so for_each can be used.
207 : : */
208 : 69 : class QuestionInserter {
209 : : public:
210 : 23 : QuestionInserter(MessagePtr message) : message_(message) {}
211 : : void operator()(const QuestionPtr question) {
212 [ + - ]: 8 : message_->addQuestion(question);
213 : : }
214 : : MessagePtr message_;
215 : : };
216 : :
217 : :
218 : : // TODO: REMOVE, USE isc::resolve::MakeErrorMessage?
219 : : void
220 : 23 : makeErrorMessage(MessagePtr message, MessagePtr answer_message,
221 : : OutputBufferPtr buffer, const Rcode& rcode)
222 : : {
223 : : // extract the parameters that should be kept.
224 : : // XXX: with the current implementation, it's not easy to set EDNS0
225 : : // depending on whether the query had it. So we'll simply omit it.
226 : 23 : const qid_t qid = message->getQid();
227 : 23 : const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
228 : 23 : const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
229 : 23 : const Opcode& opcode = message->getOpcode();
230 : 23 : vector<QuestionPtr> questions;
231 : :
232 : : // answer_message is actually ignored right now,
233 : : // see the comment in #607
234 [ + - ]: 23 : answer_message->setRcode(rcode);
235 [ + - ]: 23 : answer_message->setOpcode(opcode);
236 [ + - ]: 23 : answer_message->setQid(qid);
237 : :
238 : : // If this is an error to a query or notify, we should also copy the
239 : : // question section.
240 [ + - ][ + + ]: 23 : if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
[ + - ][ + + ]
[ + + ]
241 [ + - ][ + - ]: 9 : questions.assign(message->beginQuestion(), message->endQuestion());
[ + - ][ + - ]
[ + - ]
242 : : }
243 : :
244 [ + - ]: 23 : message->clear(Message::RENDER);
245 [ + - ]: 23 : message->setQid(qid);
246 [ + - ]: 23 : message->setOpcode(opcode);
247 [ + - ]: 23 : message->setHeaderFlag(Message::HEADERFLAG_QR);
248 [ - + ]: 23 : if (rd) {
249 [ # # ]: 0 : message->setHeaderFlag(Message::HEADERFLAG_RD);
250 : : }
251 [ - + ]: 23 : if (cd) {
252 [ # # ]: 0 : message->setHeaderFlag(Message::HEADERFLAG_CD);
253 : : }
254 [ + - ]: 23 : for_each(questions.begin(), questions.end(), QuestionInserter(message));
255 [ + - ]: 23 : message->setRcode(rcode);
256 [ + - ][ + - ]: 46 : MessageRenderer renderer;
257 [ + - ]: 23 : renderer.setBuffer(buffer.get());
258 [ + - ]: 23 : message->toWire(renderer);
259 : 23 : }
260 : :
261 : : // This is a derived class of \c DNSLookup, to serve as a
262 : : // callback in the asiolink module. It calls
263 : : // Resolver::processMessage() on a single DNS message.
264 : 33 : class MessageLookup : public DNSLookup {
265 : : public:
266 : 66 : MessageLookup(Resolver* srv) : server_(srv) {}
267 : :
268 : : // \brief Handle the DNS Lookup
269 : 0 : virtual void operator()(const IOMessage& io_message,
270 : : MessagePtr query_message,
271 : : MessagePtr answer_message,
272 : : OutputBufferPtr buffer,
273 : : DNSServer* server) const
274 : : {
275 : : server_->processMessage(io_message, query_message,
276 [ # # ]: 0 : answer_message, buffer, server);
277 : 0 : }
278 : : private:
279 : : Resolver* server_;
280 : : };
281 : :
282 : : // This is a derived class of \c DNSAnswer, to serve as a
283 : : // callback in the asiolink module. It takes a completed
284 : : // set of answer data from the DNS lookup and assembles it
285 : : // into a wire-format response.
286 : 33 : class MessageAnswer : public DNSAnswer {
287 : : public:
288 : 0 : virtual void operator()(const IOMessage& io_message,
289 : : MessagePtr query_message,
290 : : MessagePtr answer_message,
291 : : OutputBufferPtr buffer) const
292 : : {
293 : 0 : const qid_t qid = query_message->getQid();
294 : 0 : const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
295 : 0 : const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
296 : :
297 : : // The opcode and question section should have already been set,
298 : : // fill in the final details of the answer message
299 : 0 : answer_message->setQid(qid);
300 : :
301 : 0 : answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
302 : 0 : answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
303 : 0 : answer_message->setHeaderFlag(Message::HEADERFLAG_RD, rd);
304 : 0 : answer_message->setHeaderFlag(Message::HEADERFLAG_CD, cd);
305 : :
306 : : // Now we can clear the buffer and render the new message into it
307 : : buffer->clear();
308 : 0 : MessageRenderer renderer;
309 [ # # ]: 0 : renderer.setBuffer(buffer.get());
310 : :
311 [ # # ]: 0 : ConstEDNSPtr edns(query_message->getEDNS());
312 [ # # ][ # # ]: 0 : const bool dnssec_ok = edns && edns->getDNSSECAwareness();
313 [ # # ]: 0 : if (edns) {
314 [ # # ][ # # ]: 0 : EDNSPtr edns_response(new EDNS());
315 : 0 : edns_response->setDNSSECAwareness(dnssec_ok);
316 : :
317 : : // TODO: We should make our own edns bufsize length configurable
318 : 0 : edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
319 [ # # ]: 0 : answer_message->setEDNS(edns_response);
320 : : }
321 : :
322 [ # # ][ # # ]: 0 : if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
323 [ # # ]: 0 : if (edns) {
324 [ # # ]: 0 : renderer.setLengthLimit(edns->getUDPSize());
325 : : } else {
326 [ # # ]: 0 : renderer.setLengthLimit(Message::DEFAULT_MAX_UDPSIZE);
327 : : }
328 : : } else {
329 [ # # ]: 0 : renderer.setLengthLimit(65535);
330 : : }
331 : :
332 [ # # ]: 0 : answer_message->toWire(renderer);
333 [ # # ]: 0 : renderer.setBuffer(NULL);
334 : :
335 [ # # ][ # # ]: 0 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
[ # # ]
336 : : RESOLVER_DNS_MESSAGE_SENT)
337 [ # # ][ # # ]: 0 : .arg(renderer.getLength()).arg(*answer_message);
[ # # ]
338 : 0 : }
339 : : };
340 : :
341 : : // This is a derived class of \c SimpleCallback, to serve
342 : : // as a callback in the asiolink module. It checks for queued
343 : : // configuration messages, and executes them if found.
344 : 33 : class ConfigCheck : public SimpleCallback {
345 : : public:
346 : 66 : ConfigCheck(Resolver* srv) : server_(srv) {}
347 : 0 : virtual void operator()(const IOMessage&) const {
348 [ # # ]: 0 : if (server_->getConfigSession()->hasQueuedMsgs()) {
349 : 0 : server_->getConfigSession()->checkCommand();
350 : : }
351 : 0 : }
352 : : private:
353 : : Resolver* server_;
354 : : };
355 : :
356 : 33 : Resolver::Resolver() :
357 : 0 : impl_(new ResolverImpl()),
358 : : dnss_(NULL),
359 : : checkin_(NULL),
360 : : dns_lookup_(NULL),
361 : : dns_answer_(new MessageAnswer),
362 : : nsas_(NULL),
363 [ + - ][ + - ]: 33 : cache_(NULL)
[ + - ]
364 : : {
365 : : // Operations referring to "this" must be done in the constructor body
366 : : // (some compilers will issue warnings if "this" is referred to in the
367 : : // initialization list).
368 [ + - ]: 33 : checkin_ = new ConfigCheck(this);
369 [ + - ]: 33 : dns_lookup_ = new MessageLookup(this);
370 : 33 : }
371 : :
372 : 33 : Resolver::~Resolver() {
373 [ + - ]: 66 : delete impl_;
374 [ + - ][ + - ]: 33 : delete checkin_;
375 [ + - ][ + - ]: 33 : delete dns_lookup_;
376 [ + - ][ + - ]: 33 : delete dns_answer_;
377 : 33 : }
378 : :
379 : : void
380 : 20 : Resolver::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
381 : 20 : dnss_ = &dnss;
382 : 20 : }
383 : :
384 : : void
385 : 0 : Resolver::setNameserverAddressStore(isc::nsas::NameserverAddressStore& nsas)
386 : : {
387 : 0 : nsas_ = &nsas;
388 : 0 : }
389 : :
390 : : void
391 : 0 : Resolver::setCache(isc::cache::ResolverCache& cache)
392 : : {
393 : 0 : cache_ = &cache;
394 : 0 : }
395 : :
396 : :
397 : : void
398 : 0 : Resolver::setConfigSession(ModuleCCSession* config_session) {
399 : 0 : impl_->config_session_ = config_session;
400 : 0 : }
401 : :
402 : : ModuleCCSession*
403 : 0 : Resolver::getConfigSession() const {
404 : 0 : return (impl_->config_session_);
405 : : }
406 : :
407 : : void
408 : 0 : Resolver::resolve(const isc::dns::QuestionPtr& question,
409 : : const isc::resolve::ResolverInterface::CallbackPtr& callback)
410 : : {
411 : 0 : impl_->resolve(question, callback);
412 : 0 : }
413 : :
414 : :
415 : : void
416 : 28 : Resolver::processMessage(const IOMessage& io_message,
417 : : MessagePtr query_message,
418 : : MessagePtr answer_message,
419 : : OutputBufferPtr buffer,
420 : : DNSServer* server)
421 : : {
422 : 28 : InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
423 : : // First, check the header part. If we fail even for the base header,
424 : : // just drop the message.
425 : :
426 : : // In the following code, the debug output is such that there should only be
427 : : // one debug message if packet processing failed. There could be two if
428 : : // it succeeded.
429 : : try {
430 [ + + ]: 28 : query_message->parseHeader(request_buffer);
431 : :
432 : : // Ignore all responses.
433 [ + - ][ + + ]: 27 : if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
434 [ + - ][ + - ]: 3 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_UNEXPECTED_RESPONSE);
[ + - ][ + - ]
435 [ + - ]: 3 : server->resume(false);
436 : : return;
437 : : }
438 [ - + ]: 2 : } catch (const Exception& ex) {
439 [ - + ][ + - ]: 2 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_HEADER_ERROR)
[ - + ]
440 [ - + ][ - + ]: 1 : .arg(ex.what());
441 [ - + ]: 1 : server->resume(false);
442 : : return;
443 : : }
444 : :
445 : : // Parse the message. On failure, return an appropriate error.
446 : : try {
447 [ + + ]: 24 : query_message->fromWire(request_buffer);
448 : 6 : } catch (const DNSProtocolError& error) {
449 [ - + ][ + - ]: 6 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTOCOL_ERROR)
[ - + ]
450 [ - + ][ - + ]: 3 : .arg(error.what()).arg(error.getRcode());
[ - + ][ - + ]
451 : : makeErrorMessage(query_message, answer_message,
452 [ - + ][ - + ]: 6 : buffer, error.getRcode());
453 [ - + ]: 3 : server->resume(true);
454 : : return;
455 [ - + - ]: 3 : } catch (const Exception& ex) {
456 [ # # ][ # # ]: 0 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_MESSAGE_ERROR)
[ # # ]
457 [ # # ][ # # ]: 0 : .arg(ex.what()).arg(Rcode::SERVFAIL());
[ # # ][ # # ]
458 : : makeErrorMessage(query_message, answer_message,
459 [ # # ][ # # ]: 0 : buffer, Rcode::SERVFAIL());
460 [ # # ]: 0 : server->resume(true);
461 : : return;
462 : : } // Other exceptions will be handled at a higher layer.
463 : :
464 : : // Note: there appears to be no LOG_DEBUG for a successfully-received
465 : : // message. This is not an oversight - it is handled below. In the
466 : : // meantime, output the full message for debug purposes (if requested).
467 [ + - ]: 42 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
468 [ + - ]: 21 : RESOLVER_DNS_MESSAGE_RECEIVED).arg(*query_message);
469 : :
470 : : // Perform further protocol-level validation.
471 : 21 : bool send_answer = true;
472 [ + + ]: 21 : if (query_message->getOpcode() == Opcode::NOTIFY()) {
473 : :
474 : : makeErrorMessage(query_message, answer_message,
475 [ + - ]: 1 : buffer, Rcode::NOTAUTH());
476 : : // Notify arrived, but we are not authoritative.
477 [ + - ]: 2 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
478 : 1 : RESOLVER_NOTIFY_RECEIVED);
479 [ + + ]: 20 : } else if (query_message->getOpcode() != Opcode::QUERY()) {
480 : : // Unsupported opcode.
481 [ + - ]: 28 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
482 [ + - ]: 14 : RESOLVER_UNSUPPORTED_OPCODE).arg(query_message->getOpcode());
483 : : makeErrorMessage(query_message, answer_message,
484 [ + - ]: 28 : buffer, Rcode::NOTIMP());
485 [ + + ]: 6 : } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
486 : : // Not one question
487 [ + - ]: 2 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
488 : : RESOLVER_NOT_ONE_QUESTION)
489 [ + - ]: 1 : .arg(query_message->getRRCount(Message::SECTION_QUESTION));
490 : : makeErrorMessage(query_message, answer_message, buffer,
491 [ + - ]: 2 : Rcode::FORMERR());
492 : : } else {
493 : : const ResolverImpl::NormalQueryResult result =
494 : : impl_->processNormalQuery(io_message, query_message,
495 [ + - ]: 5 : answer_message, buffer, server);
496 [ + - ]: 5 : if (result == ResolverImpl::RECURSION) {
497 : : // The RecursiveQuery object will post the "resume" event to the
498 : : // DNSServer when an answer arrives, so we don't have to do it now.
499 : : return;
500 [ + + ]: 5 : } else if (result == ResolverImpl::DROPPED) {
501 : 1 : send_answer = false;
502 : : }
503 : : }
504 : :
505 : 28 : server->resume(send_answer);
506 : : }
507 : :
508 : : void
509 : 0 : ResolverImpl::resolve(const QuestionPtr& question,
510 : : const isc::resolve::ResolverInterface::CallbackPtr& callback)
511 : : {
512 [ # # ]: 0 : rec_query_->resolve(question, callback);
513 : 0 : }
514 : :
515 : : ResolverImpl::NormalQueryResult
516 : 5 : ResolverImpl::processNormalQuery(const IOMessage& io_message,
517 : : MessagePtr query_message,
518 : : MessagePtr answer_message,
519 : : OutputBufferPtr buffer,
520 : : DNSServer* server)
521 : : {
522 [ + - ][ + - ]: 10 : const ConstQuestionPtr question = *query_message->beginQuestion();
523 : 5 : const RRType qtype = question->getType();
524 : 5 : const RRClass qclass = question->getClass();
525 : :
526 : : // Apply query ACL
527 [ + - ][ + - ]: 10 : const Client client(io_message);
528 : : const BasicAction query_action(
529 : 5 : getQueryACL().execute(acl::dns::RequestContext(
530 [ + - ]: 5 : client.getRequestSourceIPAddress(),
531 [ + - ]: 5 : query_message->getTSIGRecord())));
532 [ + + ]: 5 : if (query_action == isc::acl::REJECT) {
533 [ + - ][ + - ]: 2 : LOG_INFO(resolver_logger, RESOLVER_QUERY_REJECTED)
[ + - ]
534 [ + - ][ + - ]: 1 : .arg(question->getName()).arg(qtype).arg(qclass).arg(client);
[ + - ][ + - ]
[ + - ]
535 : : makeErrorMessage(query_message, answer_message, buffer,
536 [ + - ][ + - ]: 2 : Rcode::REFUSED());
537 : : return (ERROR);
538 [ + + ]: 4 : } else if (query_action == isc::acl::DROP) {
539 [ + - ][ + - ]: 2 : LOG_INFO(resolver_logger, RESOLVER_QUERY_DROPPED)
[ + - ]
540 [ + - ][ + - ]: 1 : .arg(question->getName()).arg(qtype).arg(qclass).arg(client);
[ + - ][ + - ]
[ + - ]
541 : : return (DROPPED);
542 : : }
543 [ + - ][ + - ]: 6 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_QUERY_ACCEPTED)
[ + - ]
544 [ + - ][ + - ]: 3 : .arg(question->getName()).arg(qtype).arg(question->getClass())
[ + - ][ + - ]
545 [ + - ]: 3 : .arg(client);
546 : :
547 : : // ACL passed. Reject inappropriate queries for the resolver.
548 [ + + ]: 3 : if (qtype == RRType::AXFR()) {
549 [ + - ][ + + ]: 2 : if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
550 : : // Can't process AXFR request received over UDP
551 [ + - ][ + - ]: 1 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_AXFR_UDP);
[ + - ][ + - ]
552 : : makeErrorMessage(query_message, answer_message, buffer,
553 [ + - ][ + - ]: 2 : Rcode::FORMERR());
554 : : } else {
555 : : // ... or over TCP for that matter
556 [ + - ][ + - ]: 1 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_AXFR_TCP);
[ + - ][ + - ]
557 : : makeErrorMessage(query_message, answer_message, buffer,
558 [ + - ][ + - ]: 2 : Rcode::NOTIMP());
559 : : }
560 : : return (ERROR);
561 [ + - ]: 1 : } else if (qtype == RRType::IXFR()) {
562 : : // Can't process IXFR request
563 [ + - ][ + - ]: 1 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
[ + - ][ + - ]
564 : : makeErrorMessage(query_message, answer_message, buffer,
565 [ + - ][ + - ]: 2 : Rcode::NOTIMP());
566 : : return (ERROR);
567 [ # # ]: 0 : } else if (qclass != RRClass::IN()) {
568 : : // Non-IN message received, refuse it.
569 [ # # ][ # # ]: 0 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NON_IN_PACKET)
[ # # ]
570 [ # # ][ # # ]: 0 : .arg(question->getClass());
571 : : makeErrorMessage(query_message, answer_message, buffer,
572 [ # # ][ # # ]: 0 : Rcode::REFUSED());
573 : : return (ERROR);
574 : : }
575 : :
576 : : // Everything is okay. Start resolver.
577 [ # # ]: 0 : if (upstream_.empty()) {
578 : : // Processing normal query
579 [ # # ][ # # ]: 0 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_NORMAL_QUERY);
[ # # ][ # # ]
580 [ # # ]: 0 : rec_query_->resolve(*question, answer_message, buffer, server);
581 : : } else {
582 : : // Processing forward query
583 [ # # ][ # # ]: 0 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_FORWARD_QUERY);
[ # # ][ # # ]
584 [ # # ]: 0 : rec_query_->forward(query_message, answer_message, buffer, server);
585 : : }
586 : :
587 : : return (RECURSION);
588 : : }
589 : :
590 : : ConstElementPtr
591 : 56 : Resolver::updateConfig(ConstElementPtr config, bool startup) {
592 [ + - ]: 112 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIG_UPDATED)
593 [ + - ]: 56 : .arg(*config);
594 : :
595 : : try {
596 : : // Parse forward_addresses
597 [ + - ][ + - ]: 56 : ConstElementPtr rootAddressesE(config->get("root_addresses"));
598 : : AddressList rootAddresses(parseAddresses(rootAddressesE,
599 [ + - ][ + - ]: 224 : "root_addresses"));
600 [ + - ][ + - ]: 56 : ConstElementPtr forwardAddressesE(config->get("forward_addresses"));
601 : : AddressList forwardAddresses(parseAddresses(forwardAddressesE,
602 [ + - ][ + + ]: 219 : "forward_addresses"));
603 [ + - ][ + - ]: 51 : ConstElementPtr listenAddressesE(config->get("listen_on"));
604 : : AddressList listenAddresses(parseAddresses(listenAddressesE,
605 [ + - ][ + + ]: 198 : "listen_on"));
606 [ + - ][ + - ]: 45 : const ConstElementPtr query_acl_cfg(config->get("query_acl"));
607 : : const boost::shared_ptr<const RequestACL> query_acl =
608 [ + - ]: 26 : query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) :
609 [ + + ][ + + ]: 71 : boost::shared_ptr<RequestACL>();
610 : 39 : bool set_timeouts(false);
611 : 39 : int qtimeout = impl_->query_timeout_;
612 : 39 : int ctimeout = impl_->client_timeout_;
613 : 39 : int ltimeout = impl_->lookup_timeout_;
614 : 39 : unsigned retries = impl_->retries_;
615 [ + - ][ + - ]: 39 : ConstElementPtr qtimeoutE(config->get("timeout_query")),
616 [ + - ][ + - ]: 39 : ctimeoutE(config->get("timeout_client")),
617 [ + - ][ + - ]: 39 : ltimeoutE(config->get("timeout_lookup")),
618 [ + - ][ + - ]: 39 : retriesE(config->get("retries"));
619 [ + + ]: 39 : if (qtimeoutE) {
620 : : // It should be safe to just get it, the config manager should
621 : : // check for us
622 [ + + ]: 3 : qtimeout = qtimeoutE->intValue();
623 [ + + ]: 2 : if (qtimeout < -1) {
624 [ + - ][ + - ]: 2 : LOG_ERROR(resolver_logger, RESOLVER_QUERY_TIME_SMALL)
[ + - ]
625 [ + - ][ + - ]: 1 : .arg(qtimeout);
626 [ + - ][ + - ]: 2 : isc_throw(BadValue, "Query timeout too small");
627 : : }
628 : : set_timeouts = true;
629 : : }
630 [ + + ]: 37 : if (ctimeoutE) {
631 [ + + ]: 3 : ctimeout = ctimeoutE->intValue();
632 [ + + ]: 2 : if (ctimeout < -1) {
633 [ + - ][ + - ]: 2 : LOG_ERROR(resolver_logger, RESOLVER_CLIENT_TIME_SMALL)
[ + - ]
634 [ + - ][ + - ]: 1 : .arg(ctimeout);
635 [ + - ][ + - ]: 2 : isc_throw(BadValue, "Client timeout too small");
636 : : }
637 : : set_timeouts = true;
638 : : }
639 [ + + ]: 35 : if (ltimeoutE) {
640 [ + + ]: 3 : ltimeout = ltimeoutE->intValue();
641 [ + + ]: 2 : if (ltimeout < -1) {
642 [ + - ][ + - ]: 2 : LOG_ERROR(resolver_logger, RESOLVER_LOOKUP_TIME_SMALL)
[ + - ]
643 [ + - ][ + - ]: 1 : .arg(ltimeout);
644 [ + - ][ + - ]: 2 : isc_throw(BadValue, "Lookup timeout too small");
645 : : }
646 : : set_timeouts = true;
647 : : }
648 [ + + ]: 33 : if (retriesE) {
649 : : // Do the assignment from "retriesE->intValue()" to "retries"
650 : : // _after_ the comparison (as opposed to before it for the timeouts)
651 : : // because "retries" is unsigned.
652 [ + + ][ + + ]: 3 : if (retriesE->intValue() < 0) {
653 [ + - ][ + - ]: 2 : LOG_ERROR(resolver_logger, RESOLVER_NEGATIVE_RETRIES)
[ + - ]
654 [ + - ][ + - ]: 1 : .arg(retriesE->intValue());
[ + - ]
655 [ + - ][ + - ]: 2 : isc_throw(BadValue, "Negative number of retries");
656 : : }
657 [ + - ]: 1 : retries = retriesE->intValue();
658 : 1 : set_timeouts = true;
659 : : }
660 : : // Everything OK, so commit the changes
661 : : // listenAddresses can fail to bind, so try them first
662 : 31 : bool need_query_restart = false;
663 : :
664 [ + + ][ + + ]: 61 : if (!startup && listenAddressesE) {
[ + + ]
665 [ + + ]: 5 : setListenAddresses(listenAddresses);
666 : : need_query_restart = true;
667 : : }
668 [ + + ]: 28 : if (forwardAddressesE) {
669 [ + - ]: 2 : setForwardAddresses(forwardAddresses);
670 : : need_query_restart = true;
671 : : }
672 [ + + ]: 28 : if (rootAddressesE) {
673 [ + - ]: 3 : setRootAddresses(rootAddresses);
674 : : need_query_restart = true;
675 : : }
676 [ + + ]: 28 : if (set_timeouts) {
677 [ + - ]: 1 : setTimeouts(qtimeout, ctimeout, ltimeout, retries);
678 : : need_query_restart = true;
679 : : }
680 [ + + ]: 28 : if (query_acl) {
681 [ + - ]: 20 : setQueryACL(query_acl);
682 : : }
683 [ + + ][ - + ]: 29 : if (startup && listenAddressesE) {
[ + + ]
684 [ - + ]: 1 : setListenAddresses(listenAddresses);
685 : : need_query_restart = true;
686 : : }
687 : :
688 [ + + ]: 27 : if (need_query_restart) {
689 [ + - ]: 7 : impl_->queryShutdown();
690 : 7 : impl_->querySetup(*dnss_, *nsas_, *cache_);
691 : : }
692 [ + - ]: 27 : return (isc::config::createAnswer());
693 : :
694 [ - + ]: 58 : } catch (const isc::Exception& error) {
695 : :
696 : : // Configuration error
697 [ - + ][ + - ]: 29 : LOG_ERROR(resolver_logger, RESOLVER_CONFIG_ERROR).arg(error.what());
[ - + ][ - + ]
[ - + ]
698 [ - + ][ - + ]: 29 : return (isc::config::createAnswer(1, error.what()));
699 : : }
700 : : }
701 : :
702 : : void
703 : 4 : Resolver::setForwardAddresses(const AddressList& addresses)
704 : : {
705 : 4 : impl_->setForwardAddresses(addresses, dnss_);
706 : 4 : }
707 : :
708 : : void
709 : 3 : Resolver::setRootAddresses(const AddressList& addresses)
710 : : {
711 : 3 : impl_->setRootAddresses(addresses, dnss_);
712 : 3 : }
713 : :
714 : : bool
715 : 5 : Resolver::isForwarding() const {
716 : 5 : return (!impl_->upstream_.empty());
717 : : }
718 : :
719 : : AddressList
720 : 9 : Resolver::getForwardAddresses() const {
721 : 9 : return (impl_->upstream_);
722 : : }
723 : :
724 : : AddressList
725 : 6 : Resolver::getRootAddresses() const {
726 : 6 : return (impl_->upstream_root_);
727 : : }
728 : :
729 : : void
730 : 8 : Resolver::setListenAddresses(const AddressList& addresses) {
731 : 8 : installListenAddresses(addresses, impl_->listen_, *dnss_);
732 : 4 : }
733 : :
734 : : void
735 : 3 : Resolver::setTimeouts(int query_timeout, int client_timeout,
736 : : int lookup_timeout, unsigned retries) {
737 [ + - ]: 6 : LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_SET_PARAMS)
738 [ + - ][ + - ]: 3 : .arg(query_timeout).arg(client_timeout).arg(lookup_timeout)
[ + - ]
739 [ + - ]: 3 : .arg(retries);
740 : :
741 : 3 : impl_->query_timeout_ = query_timeout;
742 : 3 : impl_->client_timeout_ = client_timeout;
743 : 3 : impl_->lookup_timeout_ = lookup_timeout;
744 : 3 : impl_->retries_ = retries;
745 : 3 : }
746 : :
747 : : int
748 : 3 : Resolver::getQueryTimeout() const {
749 : 3 : return impl_->query_timeout_;
750 : : }
751 : :
752 : : int
753 : 3 : Resolver::getClientTimeout() const {
754 : 3 : return impl_->client_timeout_;
755 : : }
756 : :
757 : : int
758 : 3 : Resolver::getLookupTimeout() const {
759 : 3 : return impl_->lookup_timeout_;
760 : : }
761 : :
762 : : int
763 : 3 : Resolver::getRetries() const {
764 : 3 : return impl_->retries_;
765 : : }
766 : :
767 : : AddressList
768 : 11 : Resolver::getListenAddresses() const {
769 : 11 : return (impl_->listen_);
770 : : }
771 : :
772 : : const RequestACL&
773 : 16 : Resolver::getQueryACL() const {
774 : 16 : return (impl_->getQueryACL());
775 : : }
776 : :
777 : : void
778 : 21 : Resolver::setQueryACL(boost::shared_ptr<const RequestACL> new_acl) {
779 [ + + ]: 21 : if (!new_acl) {
780 [ + - ]: 2 : isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL");
781 : : }
782 : :
783 [ + - ]: 20 : LOG_INFO(resolver_logger, RESOLVER_SET_QUERY_ACL);
784 : 20 : impl_->setQueryACL(new_acl);
785 : 21 : }
|