resolver.cc 20.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config.h>

#include <netinet/in.h>

#include <algorithm>
#include <vector>
Michal Vaner's avatar
Michal Vaner committed
21
#include <cassert>
22 23 24

#include <asiolink/asiolink.h>

25
#include <boost/foreach.hpp>
26
#include <boost/lexical_cast.hpp>
27

28 29 30 31
#include <config/ccsession.h>

#include <exceptions/exceptions.h>

Michal Vaner's avatar
Michal Vaner committed
32 33
#include <dns/opcode.h>
#include <dns/rcode.h>
34 35 36 37 38 39 40
#include <dns/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/question.h>
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
41
#include <dns/messagerenderer.h>
42
#include <server_common/portconfig.h>
43

44 45
#include <resolve/recursive_query.h>

46 47
#include <log/dummylog.h>

48
#include <resolver/resolver.h>
49 50 51 52 53 54 55

using namespace std;

using namespace isc;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
56
using isc::log::dlog;
57
using namespace asiolink;
58
using namespace isc::server_common::portconfig;
59

60
class ResolverImpl {
61 62
private:
    // prohibit copy
63 64
    ResolverImpl(const ResolverImpl& source);
    ResolverImpl& operator=(const ResolverImpl& source);
65
public:
66
    ResolverImpl() :
67
        config_session_(NULL),
68 69 70
        query_timeout_(2000),
        client_timeout_(4000),
        lookup_timeout_(30000),
71
        retries_(3),
Michal Vaner's avatar
Michal Vaner committed
72
        rec_query_(NULL)
73 74
    {}

75
    ~ResolverImpl() {
76 77 78
        queryShutdown();
    }

79 80 81 82
    void querySetup(DNSService& dnss,
                    isc::nsas::NameserverAddressStore& nsas,
                    isc::cache::ResolverCache& cache)
    {
Michal Vaner's avatar
Michal Vaner committed
83
        assert(!rec_query_); // queryShutdown must be called first
84
        dlog("Query setup");
85 86 87
        rec_query_ = new RecursiveQuery(dnss, 
                                        nsas, cache,
                                        upstream_,
88
                                        upstream_root_,
Jelte Jansen's avatar
Jelte Jansen committed
89 90 91 92
                                        query_timeout_,
                                        client_timeout_,
                                        lookup_timeout_,
                                        retries_);
93 94 95
    }

    void queryShutdown() {
96 97 98 99 100 101 102 103
        // only shut down if we have actually called querySetup before
        // (this is not a safety check, just to prevent logging of
        // actions that are not performed
        if (rec_query_) {
            dlog("Query shutdown");
            delete rec_query_;
            rec_query_ = NULL;
        }
104 105
    }

106
    void setForwardAddresses(const AddressList& upstream,
Michal Vaner's avatar
Michal Vaner committed
107
        DNSService *dnss)
108 109
    {
        upstream_ = upstream;
Michal Vaner's avatar
Michal Vaner committed
110
        if (dnss) {
Jelte Jansen's avatar
Jelte Jansen committed
111
            if (!upstream_.empty()) {
112
                dlog("Setting forward addresses:");
113
                BOOST_FOREACH(const AddressPair& address, upstream) {
114 115 116
                    dlog(" " + address.first + ":" +
                        boost::lexical_cast<string>(address.second));
                }
Jelte Jansen's avatar
Jelte Jansen committed
117 118
            } else {
                dlog("No forward addresses, running in recursive mode");
119
            }
120 121 122
        }
    }

123
    void setRootAddresses(const AddressList& upstream_root,
chenzhengzhang's avatar
chenzhengzhang committed
124 125 126 127 128 129
                          DNSService *dnss)
    {
        upstream_root_ = upstream_root;
        if (dnss) {
            if (!upstream_root_.empty()) {
                dlog("Setting root addresses:");
130
                BOOST_FOREACH(const AddressPair& address, upstream_root) {
chenzhengzhang's avatar
chenzhengzhang committed
131 132 133 134 135 136 137 138
                    dlog(" " + address.first + ":" +
                        boost::lexical_cast<string>(address.second));
                }
            } else {
                dlog("No root addresses");
            }
        }
    }
139
    
140
    void resolve(const isc::dns::QuestionPtr& question,
141
        const isc::resolve::ResolverInterface::CallbackPtr& callback);
142

143 144
    void processNormalQuery(const Question& question,
                            MessagePtr answer_message,
145
                            OutputBufferPtr buffer,
146
                            DNSServer* server);
147

Evan Hunt's avatar
Evan Hunt committed
148 149
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
150

151
    /// These members are public because Resolver accesses them directly.
Evan Hunt's avatar
Evan Hunt committed
152
    ModuleCCSession* config_session_;
chenzhengzhang's avatar
chenzhengzhang committed
153
    /// Addresses of the root nameserver(s)
154
    AddressList upstream_root_;
155
    /// Addresses of the forward nameserver
156
    AddressList upstream_;
Michal Vaner's avatar
Michal Vaner committed
157
    /// Addresses we listen on
158
    AddressList listen_;
159

160 161 162 163 164 165 166
    /// Timeout for outgoing queries in milliseconds
    int query_timeout_;
    /// Timeout for incoming client queries in milliseconds
    int client_timeout_;
    /// Timeout for lookup processing in milliseconds
    int lookup_timeout_;
    
Michal Vaner's avatar
Michal Vaner committed
167 168 169
    /// Number of retries after timeout
    unsigned retries_;

Evan Hunt's avatar
Evan Hunt committed
170
private:
171

172
    /// Object to handle upstream queries
173
    RecursiveQuery* rec_query_;
174 175
};

176 177 178 179 180
/*
 * std::for_each has a broken interface. It makes no sense in a language
 * without lambda functions/closures. These two classes emulate the lambda
 * functions so for_each can be used.
 */
181 182 183 184
class QuestionInserter {
public:
    QuestionInserter(MessagePtr message) : message_(message) {}
    void operator()(const QuestionPtr question) {
185 186
        dlog(string("Adding question ") + question->getName().toText() +
            " to message");
187 188 189 190 191
        message_->addQuestion(question);
    }
    MessagePtr message_;
};

192 193

// TODO: REMOVE, USE isc::resolve::MakeErrorMessage?
194
void
195 196
makeErrorMessage(MessagePtr message, MessagePtr answer_message,
                 OutputBufferPtr buffer, const Rcode& rcode)
197 198 199 200 201
{
    // extract the parameters that should be kept.
    // XXX: with the current implementation, it's not easy to set EDNS0
    // depending on whether the query had it.  So we'll simply omit it.
    const qid_t qid = message->getQid();
Michal Vaner's avatar
Michal Vaner committed
202 203
    const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
    const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
204 205 206
    const Opcode& opcode = message->getOpcode();
    vector<QuestionPtr> questions;

207 208 209 210 211 212
    // answer_message is actually ignored right now,
    // see the comment in #607
    answer_message->setRcode(rcode);
    answer_message->setOpcode(opcode);
    answer_message->setQid(qid);

213 214 215 216 217 218 219 220 221
    // If this is an error to a query or notify, we should also copy the
    // question section.
    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
        questions.assign(message->beginQuestion(), message->endQuestion());
    }

    message->clear(Message::RENDER);
    message->setQid(qid);
    message->setOpcode(opcode);
Michal Vaner's avatar
Michal Vaner committed
222
    message->setHeaderFlag(Message::HEADERFLAG_QR);
223
    if (rd) {
Michal Vaner's avatar
Michal Vaner committed
224
        message->setHeaderFlag(Message::HEADERFLAG_RD);
225 226
    }
    if (cd) {
Michal Vaner's avatar
Michal Vaner committed
227
        message->setHeaderFlag(Message::HEADERFLAG_CD);
228 229 230 231 232 233
    }
    for_each(questions.begin(), questions.end(), QuestionInserter(message));
    message->setRcode(rcode);
    MessageRenderer renderer(*buffer);
    message->toWire(renderer);

234 235 236
    dlog(string("Sending an error response (") +
        boost::lexical_cast<string>(renderer.getLength()) + " bytes):\n" +
        message->toText());
237
}
238

239
// This is a derived class of \c DNSLookup, to serve as a
240
// callback in the asiolink module.  It calls
241
// Resolver::processMessage() on a single DNS message.
242
class MessageLookup : public DNSLookup {
243
public:
244
    MessageLookup(Resolver* srv) : server_(srv) {}
245 246

    // \brief Handle the DNS Lookup
247
    virtual void operator()(const IOMessage& io_message,
248
                            MessagePtr query_message,
249 250 251
                            MessagePtr answer_message,
                            OutputBufferPtr buffer,
                            DNSServer* server) const
252
    {
253 254
        server_->processMessage(io_message, query_message,
                                answer_message, buffer, server);
255 256
    }
private:
257
    Resolver* server_;
258 259 260 261 262 263 264 265 266
};

// This is a derived class of \c DNSAnswer, to serve as a
// callback in the asiolink module.  It takes a completed
// set of answer data from the DNS lookup and assembles it
// into a wire-format response.
class MessageAnswer : public DNSAnswer {
public:
    virtual void operator()(const IOMessage& io_message,
267
                            MessagePtr query_message,
268
                            MessagePtr answer_message,
269
                            OutputBufferPtr buffer) const
270
    {
271 272 273
        const qid_t qid = query_message->getQid();
        const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
        const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
274 275 276
        
        // The opcode and question section should have already been set,
        // fill in the final details of the answer message
277
        answer_message->setQid(qid);
278

279 280
        answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
        answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
281 282
        answer_message->setHeaderFlag(Message::HEADERFLAG_RD, rd);
        answer_message->setHeaderFlag(Message::HEADERFLAG_CD, cd);
283 284 285 286 287

        // Now we can clear the buffer and render the new message into it
        buffer->clear();
        MessageRenderer renderer(*buffer);

288 289 290 291 292 293 294 295 296 297 298
        ConstEDNSPtr edns(query_message->getEDNS());
        const bool dnssec_ok = edns && edns->getDNSSECAwareness();
        if (edns) {
            EDNSPtr edns_response(new EDNS());
            edns_response->setDNSSECAwareness(dnssec_ok);

            // TODO: We should make our own edns bufsize length configurable
            edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
            answer_message->setEDNS(edns_response);
        }
        
299
        if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
300 301 302 303 304
            if (edns) {
                renderer.setLengthLimit(edns->getUDPSize());
            } else {
                renderer.setLengthLimit(Message::DEFAULT_MAX_UDPSIZE);
            }
305 306 307
        } else {
            renderer.setLengthLimit(65535);
        }
308

309
        answer_message->toWire(renderer);
310

311 312
        dlog(string("sending a response (") +
            boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
313
            answer_message->toText());
314 315 316
    }
};

317
// This is a derived class of \c SimpleCallback, to serve
318 319
// as a callback in the asiolink module.  It checks for queued
// configuration messages, and executes them if found.
320
class ConfigCheck : public SimpleCallback {
321
public:
322
    ConfigCheck(Resolver* srv) : server_(srv) {}
Michal Vaner's avatar
Michal Vaner committed
323
    virtual void operator()(const IOMessage&) const {
324 325
        if (server_->getConfigSession()->hasQueuedMsgs()) {
            server_->getConfigSession()->checkCommand();
326 327 328
        }
    }
private:
329
    Resolver* server_;
330 331
};

332 333
Resolver::Resolver() :
    impl_(new ResolverImpl()),
334
    checkin_(new ConfigCheck(this)),
335
    dns_lookup_(new MessageLookup(this)),
Michal Vaner's avatar
Michal Vaner committed
336
    dns_answer_(new MessageAnswer)
337 338
{}

339
Resolver::~Resolver() {
340
    delete impl_;
341 342 343
    delete checkin_;
    delete dns_lookup_;
    delete dns_answer_;
344 345 346
}

void
347
Resolver::setDNSService(asiolink::DNSService& dnss) {
348
    dnss_ = &dnss;
349 350
}

351 352 353 354 355 356 357 358 359 360 361 362 363
void
Resolver::setNameserverAddressStore(isc::nsas::NameserverAddressStore& nsas)
{
    nsas_ = &nsas;
}

void
Resolver::setCache(isc::cache::ResolverCache& cache)
{
    cache_ = &cache;
}


364
void
365
Resolver::setConfigSession(ModuleCCSession* config_session) {
366 367 368 369
    impl_->config_session_ = config_session;
}

ModuleCCSession*
370
Resolver::getConfigSession() const {
371 372 373
    return (impl_->config_session_);
}

374 375
void
Resolver::resolve(const isc::dns::QuestionPtr& question,
376
    const isc::resolve::ResolverInterface::CallbackPtr& callback)
377 378 379 380 381
{
    impl_->resolve(question, callback);
}


382
void
383
Resolver::processMessage(const IOMessage& io_message,
384
                         MessagePtr query_message,
385 386 387
                         MessagePtr answer_message,
                         OutputBufferPtr buffer,
                         DNSServer* server)
388
{
389
    dlog("Got a DNS message");
390 391 392 393
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
394
        query_message->parseHeader(request_buffer);
395 396

        // Ignore all responses.
397
        if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
398
            dlog("Received unexpected response, ignoring");
399
            server->resume(false);
400
            return;
401 402
        }
    } catch (const Exception& ex) {
Scott Mann's avatar
Scott Mann committed
403
        dlog(string("DNS packet exception: ") + ex.what(),true);
404
        server->resume(false);
405
        return;
406 407 408 409
    }

    // Parse the message.  On failure, return an appropriate error.
    try {
410
        query_message->fromWire(request_buffer);
411
    } catch (const DNSProtocolError& error) {
412 413
        dlog(string("returning ") + error.getRcode().toText() + ": " + 
            error.what());
414 415
        makeErrorMessage(query_message, answer_message,
                         buffer, error.getRcode());
416
        server->resume(true);
417
        return;
418
    } catch (const Exception& ex) {
419
        dlog(string("returning SERVFAIL: ") + ex.what());
420 421
        makeErrorMessage(query_message, answer_message,
                         buffer, Rcode::SERVFAIL());
422
        server->resume(true);
423
        return;
424 425
    } // other exceptions will be handled at a higher layer.

426
    dlog("received a message:\n" + query_message->toText());
427 428

    // Perform further protocol-level validation.
429
    bool sendAnswer = true;
430
    if (query_message->getOpcode() == Opcode::NOTIFY()) {
431 432
        makeErrorMessage(query_message, answer_message,
                         buffer, Rcode::NOTAUTH());
Michal Vaner's avatar
Michal Vaner committed
433
        dlog("Notify arrived, but we are not authoritative");
434 435
    } else if (query_message->getOpcode() != Opcode::QUERY()) {
        dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
Michal Vaner's avatar
Michal Vaner committed
436
            ", expected: " + Opcode::QUERY().toText());
437 438
        makeErrorMessage(query_message, answer_message,
                         buffer, Rcode::NOTIMP());
439
    } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
Michal Vaner's avatar
Michal Vaner committed
440
        dlog("The query contained " +
441
            boost::lexical_cast<string>(query_message->getRRCount(
Michal Vaner's avatar
Michal Vaner committed
442
            Message::SECTION_QUESTION) + " questions, exactly one expected"));
443 444
        makeErrorMessage(query_message, answer_message,
                         buffer, Rcode::FORMERR());
445
    } else {
446
        ConstQuestionPtr question = *query_message->beginQuestion();
447 448 449
        const RRType &qtype = question->getType();
        if (qtype == RRType::AXFR()) {
            if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
450 451
                makeErrorMessage(query_message, answer_message,
                                 buffer, Rcode::FORMERR());
452
            } else {
453 454
                makeErrorMessage(query_message, answer_message,
                                 buffer, Rcode::NOTIMP());
455 456
            }
        } else if (qtype == RRType::IXFR()) {
457 458
            makeErrorMessage(query_message, answer_message,
                             buffer, Rcode::NOTIMP());
459
        } else {
460 461
            // The RecursiveQuery object will post the "resume" event to the
            // DNSServer when an answer arrives, so we don't have to do it now.
462
            sendAnswer = false;
463 464
            impl_->processNormalQuery(*question, answer_message,
                                      buffer, server);
465 466
        }
    }
467

468 469 470
    if (sendAnswer) {
        server->resume(true);
    }
471 472
}

473 474
void
ResolverImpl::resolve(const QuestionPtr& question,
475
    const isc::resolve::ResolverInterface::CallbackPtr& callback)
476
{
477
    rec_query_->resolve(question, callback);
478 479
}

480
void
481 482 483 484
ResolverImpl::processNormalQuery(const Question& question,
                                 MessagePtr answer_message,
                                 OutputBufferPtr buffer,
                                 DNSServer* server)
485
{
486
    dlog("Processing normal query");
487
    rec_query_->resolve(question, answer_message, buffer, server);
488 489 490
}

ConstElementPtr
491
Resolver::updateConfig(ConstElementPtr config) {
Michal Vaner's avatar
Michal Vaner committed
492 493
    dlog("New config comes: " + config->toWire());

494
    try {
495
        // Parse forward_addresses
chenzhengzhang's avatar
chenzhengzhang committed
496
        ConstElementPtr rootAddressesE(config->get("root_addresses"));
497
        AddressList rootAddresses(parseAddresses(rootAddressesE,
498
                                                    "root_addresses"));
Michal Vaner's avatar
Michal Vaner committed
499
        ConstElementPtr forwardAddressesE(config->get("forward_addresses"));
500 501
        AddressList forwardAddresses(parseAddresses(forwardAddressesE,
                                                       "forward_addresses"));
Michal Vaner's avatar
Michal Vaner committed
502
        ConstElementPtr listenAddressesE(config->get("listen_on"));
503
        AddressList listenAddresses(parseAddresses(listenAddressesE,
504
                                                      "listen_on"));
Michal Vaner's avatar
Michal Vaner committed
505
        bool set_timeouts(false);
506 507 508
        int qtimeout = impl_->query_timeout_;
        int ctimeout = impl_->client_timeout_;
        int ltimeout = impl_->lookup_timeout_;
Michal Vaner's avatar
Michal Vaner committed
509
        unsigned retries = impl_->retries_;
510 511 512 513 514
        ConstElementPtr qtimeoutE(config->get("timeout_query")),
                        ctimeoutE(config->get("timeout_client")),
                        ltimeoutE(config->get("timeout_lookup")),
                        retriesE(config->get("retries"));
        if (qtimeoutE) {
Michal Vaner's avatar
Michal Vaner committed
515 516
            // It should be safe to just get it, the config manager should
            // check for us
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
            qtimeout = qtimeoutE->intValue();
            if (qtimeout < -1) {
                isc_throw(BadValue, "Query timeout too small");
            }
            set_timeouts = true;
        }
        if (ctimeoutE) {
            ctimeout = ctimeoutE->intValue();
            if (ctimeout < -1) {
                isc_throw(BadValue, "Client timeout too small");
            }
            set_timeouts = true;
        }
        if (ltimeoutE) {
            ltimeout = ltimeoutE->intValue();
            if (ltimeout < -1) {
                isc_throw(BadValue, "Lookup timeout too small");
Michal Vaner's avatar
Michal Vaner committed
534 535 536 537 538 539 540 541 542 543
            }
            set_timeouts = true;
        }
        if (retriesE) {
            if (retriesE->intValue() < 0) {
                isc_throw(BadValue, "Negative number of retries");
            }
            retries = retriesE->intValue();
            set_timeouts = true;
        }
544
        // Everything OK, so commit the changes
545
        // listenAddresses can fail to bind, so try them first
546 547
        bool need_query_restart = false;
        
548 549
        if (listenAddressesE) {
            setListenAddresses(listenAddresses);
550
            need_query_restart = true;
551 552 553
        }
        if (forwardAddressesE) {
            setForwardAddresses(forwardAddresses);
554
            need_query_restart = true;
555
        }
chenzhengzhang's avatar
chenzhengzhang committed
556 557
        if (rootAddressesE) {
            setRootAddresses(rootAddresses);
558
            need_query_restart = true;
559
        }
Michal Vaner's avatar
Michal Vaner committed
560
        if (set_timeouts) {
561
            setTimeouts(qtimeout, ctimeout, ltimeout, retries);
562 563 564 565 566
            need_query_restart = true;
        }

        if (need_query_restart) {
            impl_->queryShutdown();
567
            impl_->querySetup(*dnss_, *nsas_, *cache_);
Michal Vaner's avatar
Michal Vaner committed
568
        }
569 570
        return (isc::config::createAnswer());
    } catch (const isc::Exception& error) {
Scott Mann's avatar
Scott Mann committed
571
        dlog(string("error in config: ") + error.what(),true);
572 573 574
        return (isc::config::createAnswer(1, error.what()));
    }
}
575 576

void
577
Resolver::setForwardAddresses(const AddressList& addresses)
578
{
Michal Vaner's avatar
Michal Vaner committed
579
    impl_->setForwardAddresses(addresses, dnss_);
580 581
}

chenzhengzhang's avatar
chenzhengzhang committed
582
void
583
Resolver::setRootAddresses(const AddressList& addresses)
chenzhengzhang's avatar
chenzhengzhang committed
584 585 586 587
{
    impl_->setRootAddresses(addresses, dnss_);
}

588
bool
589
Resolver::isForwarding() const {
590 591 592
    return (!impl_->upstream_.empty());
}

593
AddressList
594
Resolver::getForwardAddresses() const {
595 596
    return (impl_->upstream_);
}
597

598
AddressList
chenzhengzhang's avatar
chenzhengzhang committed
599 600 601 602
Resolver::getRootAddresses() const {
    return (impl_->upstream_root_);
}

603
void
604
Resolver::setListenAddresses(const AddressList& addresses) {
605
    installListenAddresses(addresses, impl_->listen_, *dnss_);
606 607
}

Michal Vaner's avatar
Michal Vaner committed
608
void
609 610 611 612 613 614 615 616 617
Resolver::setTimeouts(int query_timeout, int client_timeout,
                      int lookup_timeout, unsigned retries) {
    dlog("Setting query timeout to " + boost::lexical_cast<string>(query_timeout) +
         ", client timeout to " + boost::lexical_cast<string>(client_timeout) +
         ", lookup timeout to " + boost::lexical_cast<string>(lookup_timeout) +
         " and retry count to " + boost::lexical_cast<string>(retries));
    impl_->query_timeout_ = query_timeout;
    impl_->client_timeout_ = client_timeout;
    impl_->lookup_timeout_ = lookup_timeout;
Michal Vaner's avatar
Michal Vaner committed
618 619
    impl_->retries_ = retries;
}
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638

int
Resolver::getQueryTimeout() const {
    return impl_->query_timeout_;
}

int
Resolver::getClientTimeout() const {
    return impl_->client_timeout_;
}

int
Resolver::getLookupTimeout() const {
    return impl_->lookup_timeout_;
}

int
Resolver::getRetries() const {
    return impl_->retries_;
Michal Vaner's avatar
Michal Vaner committed
639 640
}

641
AddressList
642
Resolver::getListenAddresses() const {
643 644
    return (impl_->listen_);
}