auth_srv.cc 30.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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.

15
16
#include <config.h>

17
#include <sys/types.h>
18
19
#include <netinet/in.h>

20
#include <algorithm>
21
#include <cassert>
22
#include <iostream>
23
#include <vector>
24
#include <memory>
25

26
27
#include <boost/bind.hpp>

28
29
30
31
32
33
#include <asiolink/asiolink.h>

#include <config/ccsession.h>

#include <cc/data.h>

34
35
#include <exceptions/exceptions.h>

36
#include <util/buffer.h>
37

38
#include <dns/edns.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
39
#include <dns/exceptions.h>
40
#include <dns/messagerenderer.h>
41
42
#include <dns/name.h>
#include <dns/question.h>
43
#include <dns/opcode.h>
44
#include <dns/rcode.h>
45
46
47
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
48
#include <dns/tsig.h>
49

50
51
#include <asiodns/dns_service.h>

Evan Hunt's avatar
Evan Hunt committed
52
53
#include <datasrc/query.h>
#include <datasrc/data_source.h>
54
#include <datasrc/memory_datasrc.h>
Evan Hunt's avatar
Evan Hunt committed
55
56
#include <datasrc/static_datasrc.h>
#include <datasrc/sqlite3_datasrc.h>
57

58
59
#include <xfr/xfrout_client.h>

60
#include <auth/common.h>
61
#include <auth/auth_config.h>
62
#include <auth/auth_srv.h>
63
#include <auth/query.h>
64
#include <auth/statistics.h>
65
#include <auth/auth_log.h>
66
67
68

using namespace std;

69
using namespace isc;
70
using namespace isc::cc;
Evan Hunt's avatar
Evan Hunt committed
71
using namespace isc::datasrc;
72
using namespace isc::dns;
73
using namespace isc::util;
74
using namespace isc::auth;
75
76
using namespace isc::dns::rdata;
using namespace isc::data;
77
using namespace isc::config;
78
using namespace isc::xfr;
79
80
using namespace isc::asiolink;
using namespace isc::asiodns;
81
using namespace isc::server_common::portconfig;
82

83
84
85
86
87
88
89
90
91
namespace {
// A helper class for cleaning up message renderer.
//
// A temporary object of this class is expected to be created before starting
// response message rendering.  On construction, it (re)initialize the given
// message renderer with the given buffer.  On destruction, it releases
// the previously set buffer and then release any internal resource in the
// renderer, no matter what happened during the rendering, especially even
// when it resulted in an exception.
92
93
94
95
96
//
// Note: if we need this helper in many other places we might consider making
// it visible to other modules.  As of this implementation this is the only
// user of this class, so we hide it within the implementation.
class RendererHolder {
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public:
    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer) :
        renderer_(renderer)
    {
        renderer.setBuffer(buffer);
    }
    ~RendererHolder() {
        renderer_.setBuffer(NULL);
        renderer_.clear();
    }
private:
    MessageRenderer& renderer_;
};
}

112
113
114
115
116
117
class AuthSrvImpl {
private:
    // prohibit copy
    AuthSrvImpl(const AuthSrvImpl& source);
    AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
118
    AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client);
Han Feng's avatar
Han Feng committed
119
    ~AuthSrvImpl();
120
    isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
121

122
123
    bool processNormalQuery(const IOMessage& io_message, Message& message,
                            OutputBuffer& buffer,
124
                            auto_ptr<TSIGContext> tsig_context);
125
126
    bool processXfrQuery(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer,
127
                         auto_ptr<TSIGContext> tsig_context);
128
129
    bool processNotify(const IOMessage& io_message, Message& message,
                       OutputBuffer& buffer,
130
                       auto_ptr<TSIGContext> tsig_context);
Evan Hunt's avatar
Evan Hunt committed
131

132
133
    IOService io_service_;

134
    MessageRenderer renderer_;
Evan Hunt's avatar
Evan Hunt committed
135
136
137
138
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;

    /// These members are public because AuthSrv accesses them directly.
139
    ModuleCCSession* config_session_;
Evan Hunt's avatar
Evan Hunt committed
140
141
    AbstractSession* xfrin_session_;

142
    /// In-memory data source.  Currently class IN only for simplicity.
143
    const RRClass memory_client_class_;
144
    isc::datasrc::DataSourceClientContainerPtr memory_client_container_;
145

Michal Vaner's avatar
Michal Vaner committed
146
147
    /// Hot spot cache
    isc::datasrc::HotCache cache_;
148

149
150
151
    /// Interval timer for periodic submission of statistics counters.
    IntervalTimer statistics_timer_;

152
    /// Query counters for statistics
153
    AuthCounters counters_;
154
155
156

    /// Addresses we listen on
    AddressList listen_addresses_;
157
158

    /// The TSIG keyring
159
    const boost::shared_ptr<TSIGKeyRing>* keyring_;
160
161
162
163

    /// Bind the ModuleSpec object in config_session_ with
    /// isc:config::ModuleSpec::validateStatistics.
    void registerStatisticsValidator();
164
165
166
167
168
169
170
171
172
173
174
175
176
177

    /// \brief Resume the server
    ///
    /// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
    /// the Rcode set in the given Message is counted in the statistics
    /// counter.
    ///
    /// This method is expected to be called by processMessage()
    ///
    /// \param server The DNSServer as passed to processMessage()
    /// \param message The response as constructed by processMessage()
    /// \param done If true, the Rcode from the given message is counted,
    ///             this value is then passed to server->resume(bool)
    void resumeServer(isc::asiodns::DNSServer* server,
178
                      isc::dns::Message& message,
179
                      bool done);
Evan Hunt's avatar
Evan Hunt committed
180
181
182
private:
    std::string db_file_;

183
    MetaDataSrc data_sources_;
184
185
186
    /// We keep a pointer to the currently running sqlite datasource
    /// so that we can specifically remove that one should the database
    /// file change
187
    ConstDataSrcPtr cur_datasrc_;
188

189
    bool xfrout_connected_;
190
    AbstractXfroutClient& xfrout_client_;
191

192
    /// Increment query counter
193
    void incCounter(const int protocol);
194
195
196

    // validateStatistics
    bool validateStatistics(isc::data::ConstElementPtr data) const;
197
198

    auth::Query query_;
199
200
};

201
202
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
                         AbstractXfroutClient& xfrout_client) :
203
    config_session_(NULL),
204
    xfrin_session_(NULL),
205
    memory_client_class_(RRClass::IN()),
206
    statistics_timer_(io_service_),
207
    counters_(),
208
    keyring_(NULL),
209
    xfrout_connected_(false),
210
    xfrout_client_(xfrout_client)
211
{
212
213
    // cur_datasrc_ is automatically initialized by the default constructor,
    // effectively being an empty (sqlite) data source.  once ccsession is up
214
    // the datasource will be set by the configuration setting
215

216
    // add static data source
217
    data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
Evan Hunt's avatar
Evan Hunt committed
218
219
220

    // enable or disable the cache
    cache_.setEnabled(use_cache);
221
222
}

Han Feng's avatar
Han Feng committed
223
AuthSrvImpl::~AuthSrvImpl() {
224
    if (xfrout_connected_) {
225
        xfrout_client_.disconnect();
226
        xfrout_connected_ = false;
Han Feng's avatar
Han Feng committed
227
    }
228
}
229

230
// This is a derived class of \c DNSLookup, to serve as a
231
// callback in the asiolink module.  It calls
232
// AuthSrv::processMessage() on a single DNS message.
233
class MessageLookup : public DNSLookup {
234
public:
235
    MessageLookup(AuthSrv* srv) : server_(srv) {}
236
237
    virtual void operator()(const IOMessage& io_message,
                            MessagePtr message,
238
                            MessagePtr, // Not used here
239
240
                            OutputBufferPtr buffer,
                            DNSServer* server) const
241
    {
242
        server_->processMessage(io_message, *message, *buffer, server);
243
244
245
246
247
    }
private:
    AuthSrv* server_;
};

248
249
250
251
252
253
254
255
// This is a derived class of \c DNSAnswer, to serve as a callback in the
// asiolink module.  We actually shouldn't do anything in this class because
// we build complete response messages in the process methods; otherwise
// the response message will contain trailing garbage.  In future, we should
// probably even drop the reliance on DNSAnswer.  We don't need the coroutine
// tricks provided in that framework, and its overhead would be significant
// in terms of performance consideration for the authoritative server
// implementation.
256
257
class MessageAnswer : public DNSAnswer {
public:
258
259
    MessageAnswer(AuthSrv*) {}
    virtual void operator()(const IOMessage&, MessagePtr,
260
                            MessagePtr, OutputBufferPtr) const
261
    {}
262
263
};

264
// This is a derived class of \c SimpleCallback, to serve
265
// as a callback in the asiolink module.  It checks for queued
266
// configuration messages, and executes them if found.
267
class ConfigChecker : public SimpleCallback {
268
269
public:
    ConfigChecker(AuthSrv* srv) : server_(srv) {}
Michal Vaner's avatar
Michal Vaner committed
270
    virtual void operator()(const IOMessage&) const {
271
272
273
        ModuleCCSession* cfg_session = server_->getConfigSession();
        if (cfg_session != NULL && cfg_session->hasQueuedMsgs()) {
            cfg_session->checkCommand();
274
275
276
277
278
279
        }
    }
private:
    AuthSrv* server_;
};

280
281
282
283
284
285
286
AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client)
{
    impl_ = new AuthSrvImpl(use_cache, xfrout_client);
    checkin_ = new ConfigChecker(this);
    dns_lookup_ = new MessageLookup(this);
    dns_answer_ = new MessageAnswer(this);
}
287

288
289
void
AuthSrv::stop() {
290
    impl_->io_service_.stop();
291
292
}

293
AuthSrv::~AuthSrv() {
294
    delete impl_;
295
    delete checkin_;
296
297
    delete dns_lookup_;
    delete dns_answer_;
298
299
}

300
namespace {
301
302
class QuestionInserter {
public:
303
    QuestionInserter(Message& message) : message_(message) {}
304
    void operator()(const QuestionPtr question) {
305
        message_.addQuestion(question);
306
    }
307
    Message& message_;
308
309
};

310
void
311
312
makeErrorMessage(MessageRenderer& renderer, Message& message,
                 OutputBuffer& buffer, const Rcode& rcode,
313
314
                 std::auto_ptr<TSIGContext> tsig_context =
                 std::auto_ptr<TSIGContext>())
315
{
316
317
318
    // 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.
319
320
321
322
    const qid_t qid = message.getQid();
    const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
    const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
    const Opcode& opcode = message.getOpcode();
323
324
    vector<QuestionPtr> questions;

325
326
327
    // If this is an error to a query or notify, we should also copy the
    // question section.
    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
328
        questions.assign(message.beginQuestion(), message.endQuestion());
329
    }
330

331
332
333
334
    message.clear(Message::RENDER);
    message.setQid(qid);
    message.setOpcode(opcode);
    message.setHeaderFlag(Message::HEADERFLAG_QR);
335
    if (rd) {
336
        message.setHeaderFlag(Message::HEADERFLAG_RD);
337
338
    }
    if (cd) {
339
        message.setHeaderFlag(Message::HEADERFLAG_CD);
340
    }
341
342
    for_each(questions.begin(), questions.end(), QuestionInserter(message));

343
    message.setRcode(rcode);
344
    
345
    RendererHolder holder(renderer, &buffer);
346
    if (tsig_context.get() != NULL) {
347
        message.toWire(renderer, *tsig_context);
348
    } else {
349
        message.toWire(renderer);
350
    }
351
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
352
              .arg(renderer.getLength()).arg(message);
353
}
354
}
355

356
357
358
359
360
IOService&
AuthSrv::getIOService() {
    return (impl_->io_service_);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
361
362
363
364
365
366
367
368
369
370
void
AuthSrv::setCacheSlots(const size_t slots) {
    impl_->cache_.setSlots(slots);
}

size_t
AuthSrv::getCacheSlots() const {
    return (impl_->cache_.getSlots());
}

371
void
372
373
AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
    impl_->xfrin_session_ = xfrin_session;
374
375
}

376
void
377
378
AuthSrv::setConfigSession(ModuleCCSession* config_session) {
    impl_->config_session_ = config_session;
379
    impl_->registerStatisticsValidator();
380
381
}

382
void
383
384
AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
    impl_->counters_.setStatisticsSession(statistics_session);
385
386
}

387
ModuleCCSession*
388
AuthSrv::getConfigSession() const {
389
    return (impl_->config_session_);
390
391
}

392
393
394
395
396
397
398
399
400
401
isc::datasrc::DataSourceClientContainerPtr
AuthSrv::getInMemoryClientContainer(const RRClass& rrclass) {
    if (rrclass != impl_->memory_client_class_) {
        isc_throw(InvalidParameter,
                  "Memory data source is not supported for RR class "
                  << rrclass);
    }
    return (impl_->memory_client_container_);
}

402
isc::datasrc::DataSourceClient*
403
AuthSrv::getInMemoryClient(const RRClass& rrclass) {
404
405
406
407
    if (hasInMemoryClient()) {
        return (&getInMemoryClientContainer(rrclass)->getInstance());
    } else {
        return (NULL);
408
409
410
    }
}

411
412
bool
AuthSrv::hasInMemoryClient() {
413
414
    return (impl_->memory_client_container_ !=
            isc::datasrc::DataSourceClientContainerPtr());
415
416
}

417
418
419
420
421
422
423
424
void
AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass,
    isc::datasrc::DataSourceClientContainerPtr memory_client)
{
    if (rrclass != impl_->memory_client_class_) {
        isc_throw(InvalidParameter,
                  "Memory data source is not supported for RR class "
                  << rrclass);
425
    } else if (!impl_->memory_client_container_ && memory_client) {
426
427
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
                  .arg(rrclass);
428
    } else if (impl_->memory_client_container_ && !memory_client) {
429
430
431
432
433
434
435
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
                  .arg(rrclass);
    }
    impl_->memory_client_container_ = memory_client;
}


436
437
uint32_t
AuthSrv::getStatisticsTimerInterval() const {
438
    return (impl_->statistics_timer_.getInterval() / 1000);
439
440
441
442
443
444
445
}

void
AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
    if (interval == impl_->statistics_timer_.getInterval()) {
        return;
    }
446
    if (interval > 86400) {
447
        // It can't occur since the value is checked in
448
449
450
        // statisticsIntervalConfig::build().
        isc_throw(InvalidParameter, "Too long interval: " << interval);
    }
451
452
    if (interval == 0) {
        impl_->statistics_timer_.cancel();
453
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_DISABLED);
454
    } else {
455
456
        impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
                                                   this),
457
                                       interval * 1000);
458
459
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_SET)
                  .arg(interval);
460
461
462
    }
}

463
void
464
465
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer, DNSServer* server)
JINMEI Tatuya's avatar
cleanup    
JINMEI Tatuya committed
466
{
467
468
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());

469
470
471
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
472
        message.parseHeader(request_buffer);
473
474

        // Ignore all responses.
475
        if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
476
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
477
            impl_->resumeServer(server, message, false);
478
            return;
479
480
        }
    } catch (const Exception& ex) {
481
482
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                  .arg(ex.what());
483
        impl_->resumeServer(server, message, false);
484
        return;
485
486
    }

487
    try {
488
        // Parse the message.
489
        message.fromWire(request_buffer);
490
    } catch (const DNSProtocolError& error) {
491
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
492
                  .arg(error.getRcode().toText()).arg(error.what());
493
        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
494
        impl_->resumeServer(server, message, true);
495
        return;
496
    } catch (const Exception& ex) {
497
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
498
                  .arg(ex.what());
499
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
500
        impl_->resumeServer(server, message, true);
501
        return;
502
    } // other exceptions will be handled at a higher layer.
503

504
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
505
              .arg(message);
506

507
    // Perform further protocol-level validation.
508
509
510
    // TSIG first
    // If this is set to something, we know we need to answer with TSIG as well
    std::auto_ptr<TSIGContext> tsig_context;
511
    const TSIGRecord* tsig_record(message.getTSIGRecord());
512
513
514
515
    TSIGError tsig_error(TSIGError::NOERROR());

    // Do we do TSIG?
    // The keyring can be null if we're in test
516
    if (impl_->keyring_ != NULL && tsig_record != NULL) {
517
518
519
        tsig_context.reset(new TSIGContext(tsig_record->getName(),
                                           tsig_record->getRdata().
                                                getAlgorithm(),
520
                                           **impl_->keyring_));
521
522
523
        tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                          io_message.getDataSize());
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
524

525
    if (tsig_error != TSIGError::NOERROR()) {
526
527
        makeErrorMessage(impl_->renderer_, message, buffer,
                         tsig_error.toRcode(), tsig_context);
528
        impl_->resumeServer(server, message, true);
529
530
531
532
        return;
    }

    bool send_answer = true;
533
534
535
    try {
        // update per opcode statistics counter.  This can only be reliable
        // after TSIG check succeeds.
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
536
        impl_->counters_.inc(message.getOpcode());
537

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
538
        if (message.getOpcode() == Opcode::NOTIFY()) {
539
540
            send_answer = impl_->processNotify(io_message, message, buffer,
                                               tsig_context);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
541
        } else if (message.getOpcode() != Opcode::QUERY()) {
542
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
543
                      .arg(message.getOpcode().toText());
544
545
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::NOTIMP(), tsig_context);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
546
        } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
547
548
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::FORMERR(), tsig_context);
549
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
550
            ConstQuestionPtr question = *message.beginQuestion();
551
552
553
554
555
556
557
558
559
560
561
            const RRType &qtype = question->getType();
            if (qtype == RRType::AXFR()) {
                send_answer = impl_->processXfrQuery(io_message, message,
                                                     buffer, tsig_context);
            } else if (qtype == RRType::IXFR()) {
                send_answer = impl_->processXfrQuery(io_message, message,
                                                     buffer, tsig_context);
            } else {
                send_answer = impl_->processNormalQuery(io_message, message,
                                                        buffer, tsig_context);
            }
562
        }
563
564
565
    } catch (const std::exception& ex) {
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                  .arg(ex.what());
566
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
567
    } catch (...) {
568
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
569
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
570
    }
571
    impl_->resumeServer(server, message, send_answer);
572
573
574
}

bool
575
576
AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
                                OutputBuffer& buffer,
577
                                auto_ptr<TSIGContext> tsig_context)
578
{
579
    ConstEDNSPtr remote_edns = message.getEDNS();
580
    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
581
    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
582
        Message::DEFAULT_MAX_UDPSIZE;
583

584
585
586
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
587

588
589
590
    // Increment query counter.
    incCounter(io_message.getSocket().getProtocol());

591
592
    if (remote_edns) {
        EDNSPtr local_edns = EDNSPtr(new EDNS());
593
        local_edns->setDNSSECAwareness(dnssec_ok);
594
        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
595
        message.setEDNS(local_edns);
596
    }
597

598
    try {
Jerry's avatar
Jerry committed
599
600
        // If a memory data source is configured call the separate
        // Query::process()
601
        const ConstQuestionPtr question = *message.beginQuestion();
602
603
        if (memory_client_container_ &&
            memory_client_class_ == question->getClass()) {
604
605
            const RRType& qtype = question->getType();
            const Name& qname = question->getName();
606
607
            query_.process(memory_client_container_->getInstance(),
                           qname, qtype, message, dnssec_ok);
608
        } else {
609
            datasrc::Query query(message, cache_, dnssec_ok);
610
611
            data_sources_.doQuery(query);
        }
612
    } catch (const Exception& ex) {
613
        LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
614
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
615
        return (true);
616
    }
617

618
    RendererHolder holder(renderer_, &buffer);
619
620
    const bool udp_buffer =
        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
621
    renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
622
    if (tsig_context.get() != NULL) {
623
        message.toWire(renderer_, *tsig_context);
624
    } else {
625
        message.toWire(renderer_);
626
    }
627
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
628
              .arg(renderer_.getLength()).arg(message);
629
    return (true);
630
631
}

632
bool
633
634
AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                             OutputBuffer& buffer,
635
                             auto_ptr<TSIGContext> tsig_context)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
636
{
637
638
639
    // Increment query counter.
    incCounter(io_message.getSocket().getProtocol());

Han Feng's avatar
Han Feng committed
640
    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
641
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
642
643
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
644
        return (true);
Han Feng's avatar
Han Feng committed
645
    }
646
647

    try {
648
649
650
651
        if (!xfrout_connected_) {
            xfrout_client_.connect();
            xfrout_connected_ = true;
        }
652
653
654
655
        xfrout_client_.sendXfroutRequestInfo(
            io_message.getSocket().getNative(),
            io_message.getData(),
            io_message.getDataSize());
656
    } catch (const XfroutError& err) {
657
        if (xfrout_connected_) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
658
            // disconnect() may trigger an exception, but since we try it
659
660
661
662
            // only if we've successfully opened it, it shouldn't happen in
            // normal condition.  Should this occur, we'll propagate it to the
            // upper layer.
            xfrout_client_.disconnect();
663
            xfrout_connected_ = false;
664
        }
665

666
667
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
                  .arg(err.what());
668
669
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
                         tsig_context);
670
        return (true);
671
    }
672

673
    return (false);
674
675
676
}

bool
677
678
AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                           OutputBuffer& buffer,
679
                           std::auto_ptr<TSIGContext> tsig_context)
680
{
681
682
    // The incoming notify must contain exactly one question for SOA of the
    // zone name.
683
    if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
684
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
685
                  .arg(message.getRRCount(Message::SECTION_QUESTION));
686
687
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
688
689
        return (true);
    }
690
    ConstQuestionPtr question = *message.beginQuestion();
691
    if (question->getType() != RRType::SOA()) {
692
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
693
                  .arg(question->getType().toText());
694
695
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
696
697
698
699
700
701
702
        return (true);
    }

    // According to RFC 1996, rcode should be "no error" and AA bit should be
    // on, but we don't check these conditions.  This behavior is compatible
    // with BIND 9.

703
704
    // TODO check with the conf-mgr whether current server is the auth of the
    // zone
705
706
707
708
709

    // In the code that follows, we simply ignore the notify if any internal
    // error happens rather than returning (e.g.) SERVFAIL.  RFC 1996 is
    // silent about such cases, but there doesn't seem to be anything we can
    // improve at the primary server side by sending an error anyway.
710
    if (xfrin_session_ == NULL) {
711
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
712
        return (false);
Han Feng's avatar
Han Feng committed
713
    }
714

JINMEI Tatuya's avatar
JINMEI Tatuya committed
715
716
    const string remote_ip_address =
        io_message.getRemoteEndpoint().getAddress().toText();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
717
718
    static const string command_template_start =
        "{\"command\": [\"notify\", {\"zone_name\" : \"";
719
    static const string command_template_master = "\", \"master\" : \"";
720
    static const string command_template_rrclass = "\", \"zone_class\" : \"";
721
    static const string command_template_end = "\"}]}";
722

Han Feng's avatar
Han Feng committed
723
    try {
724
        ConstElementPtr notify_command = Element::fromJSON(
725
                command_template_start + question->getName().toText() +
726
727
                command_template_master + remote_ip_address +
                command_template_rrclass + question->getClass().toText() +
728
                command_template_end);
Han Feng's avatar
Han Feng committed
729
        const unsigned int seq =
730
            xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
731
                                          "*", "*");
732
        ConstElementPtr env, answer, parsed_answer;
733
        xfrin_session_->group_recvmsg(env, answer, false, seq);
Han Feng's avatar
Han Feng committed
734
        int rcode;
735
736
        parsed_answer = parseAnswer(rcode, answer);
        if (rcode != 0) {
737
            LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
738
                      .arg(parsed_answer->str());
739
            return (false);
Han Feng's avatar
Han Feng committed
740
        }
741
    } catch (const Exception& ex) {
742
        LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
743
        return (false);
744
    }
745

746
747
748
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
749

750
    RendererHolder holder(renderer_, &buffer);
751
    if (tsig_context.get() != NULL) {
752
        message.toWire(renderer_, *tsig_context);
753
    } else {
754
        message.toWire(renderer_);
755
    }
Han Feng's avatar
Han Feng committed
756
    return (true);
757
758
}

759
void
760
AuthSrvImpl::incCounter(const int protocol) {
761
762
    // Increment query counter.
    if (protocol == IPPROTO_UDP) {
763
        counters_.inc(AuthCounters::SERVER_UDP_QUERY);
764
    } else if (protocol == IPPROTO_TCP) {
765
        counters_.inc(AuthCounters::SERVER_TCP_QUERY);
766
767
768
769
770
771
    } else {
        // unknown protocol
        isc_throw(Unexpected, "Unknown protocol: " << protocol);
    }
}

772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
void
AuthSrvImpl::registerStatisticsValidator() {
    counters_.registerStatisticsValidator(
        boost::bind(&AuthSrvImpl::validateStatistics, this, _1));
}

bool
AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const {
    if (config_session_ == NULL) {
        return (false);
    }
    return (
        config_session_->getModuleSpec().validateStatistics(
            data, true));
}

788
789
790
ConstElementPtr
AuthSrvImpl::setDbFile(ConstElementPtr config) {
    ConstElementPtr answer = isc::config::createAnswer();
791
792

    if (config && config->contains("database_file")) {
793
        db_file_ = config->get("database_file")->stringValue();
794
    } else if (config_session_ != NULL) {
795
796
        bool is_default;
        string item("database_file");
797
798
        ConstElementPtr value = config_session_->getValue(is_default, item);
        ElementPtr final = Element::createMap();
799
800
801
802
803
804
805
806
807
808
809
810

        // If the value is the default, and we are running from
        // a specific directory ('from build'), we need to use
        // a different value than the default (which may not exist)
        // (btw, this should not be done here in the end, i think
        //  the from-source script should have a check for this,
        //  but for that we need offline access to config, so for
        //  now this is a decent solution)
        if (is_default && getenv("B10_FROM_BUILD")) {
            value = Element::create(string(getenv("B10_FROM_BUILD")) +
                                    "/bind10_zones.sqlite3");
        }
811
        final->set(item, value);
812
        config = final;
813
814

        db_file_ = value->stringValue();
815
816
817
    } else {
        return (answer);
    }
818
    LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_DATA_SOURCE).arg(db_file_);
819

820
821
822
823
824
825
    // create SQL data source
    // Note: the following step is tricky to be exception-safe and to ensure
    // exception guarantee: We first need to perform all operations that can
    // fail, while acquiring resources in the RAII manner.  We then perform
    // delete and swap operations which should not fail.
    DataSrcPtr datasrc_ptr(DataSrcPtr<