auth_srv.cc 33 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 <util/io/socketsession.h>
18

19
#include <asiolink/asiolink.h>
20
#include <asiolink/io_endpoint.h>
21
22
23
24
25

#include <config/ccsession.h>

#include <cc/data.h>

26
27
#include <exceptions/exceptions.h>

28
#include <util/buffer.h>
29

30
#include <dns/edns.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
31
#include <dns/exceptions.h>
32
#include <dns/messagerenderer.h>
33
34
#include <dns/name.h>
#include <dns/question.h>
35
#include <dns/opcode.h>
36
#include <dns/rcode.h>
37
38
39
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
40
#include <dns/tsig.h>
41

42
43
#include <asiodns/dns_service.h>

Evan Hunt's avatar
Evan Hunt committed
44
45
46
47
#include <datasrc/query.h>
#include <datasrc/data_source.h>
#include <datasrc/static_datasrc.h>
#include <datasrc/sqlite3_datasrc.h>
48
#include <datasrc/client_list.h>
49

50
51
#include <xfr/xfrout_client.h>

52
#include <auth/common.h>
53
#include <auth/auth_config.h>
54
#include <auth/auth_srv.h>
55
#include <auth/query.h>
56
#include <auth/statistics.h>
57
#include <auth/auth_log.h>
58

59
60
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
61
#include <boost/scoped_ptr.hpp>
62
63
64
65
66
67
68
69
70
71

#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
#include <memory>

#include <sys/types.h>
#include <netinet/in.h>

72
73
using namespace std;

74
using namespace isc;
75
using namespace isc::cc;
Evan Hunt's avatar
Evan Hunt committed
76
using namespace isc::datasrc;
77
using namespace isc::dns;
78
using namespace isc::util;
79
using namespace isc::util::io;
80
using namespace isc::auth;
81
82
using namespace isc::dns::rdata;
using namespace isc::data;
83
using namespace isc::config;
84
using namespace isc::xfr;
85
86
using namespace isc::asiolink;
using namespace isc::asiodns;
87
using namespace isc::server_common::portconfig;
88

89
90
91
92
93
94
95
96
97
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.
98
99
100
101
102
//
// 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 {
103
104
105
106
107
108
109
110
111
112
113
114
115
public:
    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer) :
        renderer_(renderer)
    {
        renderer.setBuffer(buffer);
    }
    ~RendererHolder() {
        renderer_.setBuffer(NULL);
        renderer_.clear();
    }
private:
    MessageRenderer& renderer_;
};
116

117
118
119
// A helper container of socket session forwarder.
//
// This class provides a simple wrapper interface to SocketSessionForwarder
120
121
// so that the caller doesn't have to worry about connection management,
// exception handling or parameter building.
122
123
//
// It internally maintains whether the underlying forwarder establishes a
124
125
126
127
128
129
// connection to the receiver.  On a forwarding request, if the connection
// hasn't been established yet, it automatically opens a new one, then
// pushes the session over it.  It also closes the connection on destruction,
// or a non-recoverable error happens, automatically.  So the only thing
// the application has to do is to create this object and push any session
// to be forwarded.
130
131
class SocketSessionForwarderHolder {
public:
132
133
134
135
136
137
138
139
140
141
    /// \brief The constructor.
    ///
    /// \param message_name Any string that can identify the type of messages
    /// to be forwarded via this session.  It will be only used as part of
    /// log message, so it can be anything, but in practice something like
    /// "update" or "xfr" is expected.
    /// \param forwarder The underlying socket session forwarder.
    SocketSessionForwarderHolder(const string& message_name,
                                 BaseSocketSessionForwarder& forwarder) :
        message_name_(message_name), forwarder_(forwarder), connected_(false)
142
    {}
143

144
145
146
147
148
    ~SocketSessionForwarderHolder() {
        if (connected_) {
            forwarder_.close();
        }
    }
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    /// \brief Push a socket session corresponding to given IOMessage.
    ///
    /// If the connection with the receiver process hasn't been established,
    /// it automatically establishes one, then push the session over it.
    ///
    /// If either connect or push fails, the underlying forwarder object should
    /// throw an exception.  This method logs the event, and propagates the
    /// exception to the caller, which will eventually result in SERVFAIL.
    /// The connection, if established, is automatically closed, so the next
    /// forward request will trigger reopening a new connection.
    ///
    /// \note: Right now, there's no API to retrieve the local address from
    /// the IOMessage.  Until it's added, we pass the remote address as
    /// local.
    ///
    /// \param io_message The request message to be forwarded as a socket
    /// session.  It will be converted to the parameters that the underlying
    /// SocketSessionForwarder expects.
    void push(const IOMessage& io_message) {
        const IOEndpoint& remote_ep = io_message.getRemoteEndpoint();
        const int protocol = remote_ep.getProtocol();
        const int sock_type = getSocketType(protocol);
        try {
            connect();
            forwarder_.push(io_message.getSocket().getNative(),
                            remote_ep.getFamily(), sock_type, protocol,
                            remote_ep.getSockAddr(), remote_ep.getSockAddr(),
                            io_message.getData(), io_message.getDataSize());
        } catch (const SocketSessionError& ex) {
            LOG_ERROR(auth_logger, AUTH_MESSAGE_FORWARD_ERROR).
180
                arg(message_name_).arg(remote_ep).arg(ex.what());
181
182
183
184
185
186
187
188
189
190
            close();
            throw;
        }
    }

private:
    const string message_name_;
    BaseSocketSessionForwarder& forwarder_;
    bool connected_;

191
192
193
194
195
196
197
    void connect() {
        if (!connected_) {
            forwarder_.connectToReceiver();
            connected_ = true;
        }
    }

198
199
200
201
202
203
204
    void close() {
        if (connected_) {
            forwarder_.close();
            connected_ = false;
        }
    }

205
206
207
208
209
210
211
212
213
214
215
    static int getSocketType(int protocol) {
        switch (protocol) {
        case IPPROTO_UDP:
            return (SOCK_DGRAM);
        case IPPROTO_TCP:
            return (SOCK_STREAM);
        default:
            isc_throw(isc::InvalidParameter,
                      "Unexpected socket address family: " << protocol);
        }
    }
216
};
217
218
}

219
220
221
222
223
224
class AuthSrvImpl {
private:
    // prohibit copy
    AuthSrvImpl(const AuthSrvImpl& source);
    AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
225
    AuthSrvImpl(AbstractXfroutClient& xfrout_client,
226
                BaseSocketSessionForwarder& ddns_forwarder);
Han Feng's avatar
Han Feng committed
227
    ~AuthSrvImpl();
228

229
230
    bool processNormalQuery(const IOMessage& io_message, Message& message,
                            OutputBuffer& buffer,
231
                            auto_ptr<TSIGContext> tsig_context);
232
233
    bool processXfrQuery(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer,
234
                         auto_ptr<TSIGContext> tsig_context);
235
236
    bool processNotify(const IOMessage& io_message, Message& message,
                       OutputBuffer& buffer,
237
                       auto_ptr<TSIGContext> tsig_context);
238
    bool processUpdate(const IOMessage& io_message);
Evan Hunt's avatar
Evan Hunt committed
239

240
241
    IOService io_service_;

242
    MessageRenderer renderer_;
Evan Hunt's avatar
Evan Hunt committed
243
244
245
246
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;

    /// These members are public because AuthSrv accesses them directly.
247
    ModuleCCSession* config_session_;
Evan Hunt's avatar
Evan Hunt committed
248
249
    AbstractSession* xfrin_session_;

250
251
252
    /// Interval timer for periodic submission of statistics counters.
    IntervalTimer statistics_timer_;

253
    /// Query counters for statistics
254
    AuthCounters counters_;
255
256
257

    /// Addresses we listen on
    AddressList listen_addresses_;
258
259

    /// The TSIG keyring
260
    const boost::shared_ptr<TSIGKeyRing>* keyring_;
261

262
    /// The client list
263
264
    std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
        client_lists_;
265

266
267
268
    boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
                                                            rrclass)
    {
269
        const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
270
271
272
273
274
275
276
277
            const_iterator it(client_lists_.find(rrclass));
        if (it == client_lists_.end()) {
            return (boost::shared_ptr<ConfigurableClientList>());
        } else {
            return (it->second);
        }
    }

278
279
280
    /// Bind the ModuleSpec object in config_session_ with
    /// isc:config::ModuleSpec::validateStatistics.
    void registerStatisticsValidator();
281

282
283
284
285
286
287
288
289
    /// Socket session forwarder for dynamic update requests
    BaseSocketSessionForwarder& ddns_base_forwarder_;

    /// Holder for the DDNS Forwarder, which is used to send
    /// DDNS messages to b10-ddns, but can be set to empty if
    /// b10-ddns is not running
    boost::scoped_ptr<SocketSessionForwarderHolder> ddns_forwarder_;

290
291
292
293
294
295
296
297
298
299
300
301
302
    /// \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,
303
                      isc::dns::Message& message,
304
                      bool done);
305

Evan Hunt's avatar
Evan Hunt committed
306
private:
307
    bool xfrout_connected_;
308
    AbstractXfroutClient& xfrout_client_;
309

310
    /// Increment query counter
311
    void incCounter(const int protocol);
312
313
314

    // validateStatistics
    bool validateStatistics(isc::data::ConstElementPtr data) const;
315
316

    auth::Query query_;
317
318
};

319
AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
320
                         BaseSocketSessionForwarder& ddns_forwarder) :
321
    config_session_(NULL),
322
    xfrin_session_(NULL),
323
    statistics_timer_(io_service_),
324
    counters_(),
325
    keyring_(NULL),
326
    ddns_base_forwarder_(ddns_forwarder),
327
328
329
    ddns_forwarder_(NULL),
    xfrout_connected_(false),
    xfrout_client_(xfrout_client)
330
{}
331

Han Feng's avatar
Han Feng committed
332
AuthSrvImpl::~AuthSrvImpl() {
333
    if (xfrout_connected_) {
334
        xfrout_client_.disconnect();
335
        xfrout_connected_ = false;
Han Feng's avatar
Han Feng committed
336
    }
337
}
338

339
// This is a derived class of \c DNSLookup, to serve as a
340
// callback in the asiolink module.  It calls
341
// AuthSrv::processMessage() on a single DNS message.
342
class MessageLookup : public DNSLookup {
343
public:
344
    MessageLookup(AuthSrv* srv) : server_(srv) {}
345
346
    virtual void operator()(const IOMessage& io_message,
                            MessagePtr message,
347
                            MessagePtr, // Not used here
348
349
                            OutputBufferPtr buffer,
                            DNSServer* server) const
350
    {
351
        server_->processMessage(io_message, *message, *buffer, server);
352
353
354
355
356
    }
private:
    AuthSrv* server_;
};

357
358
359
360
361
362
363
364
// 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.
365
366
class MessageAnswer : public DNSAnswer {
public:
367
368
    MessageAnswer(AuthSrv*) {}
    virtual void operator()(const IOMessage&, MessagePtr,
369
                            MessagePtr, OutputBufferPtr) const
370
    {}
371
372
};

373
// This is a derived class of \c SimpleCallback, to serve
374
// as a callback in the asiolink module.  It checks for queued
375
// configuration messages, and executes them if found.
376
class ConfigChecker : public SimpleCallback {
377
378
public:
    ConfigChecker(AuthSrv* srv) : server_(srv) {}
Michal Vaner's avatar
Michal Vaner committed
379
    virtual void operator()(const IOMessage&) const {
380
381
382
        ModuleCCSession* cfg_session = server_->getConfigSession();
        if (cfg_session != NULL && cfg_session->hasQueuedMsgs()) {
            cfg_session->checkCommand();
383
384
385
386
387
388
        }
    }
private:
    AuthSrv* server_;
};

389
AuthSrv::AuthSrv(isc::xfr::AbstractXfroutClient& xfrout_client,
390
                 isc::util::io::BaseSocketSessionForwarder& ddns_forwarder)
391
{
392
    impl_ = new AuthSrvImpl(xfrout_client, ddns_forwarder);
393
394
395
396
    checkin_ = new ConfigChecker(this);
    dns_lookup_ = new MessageLookup(this);
    dns_answer_ = new MessageAnswer(this);
}
397

398
399
void
AuthSrv::stop() {
400
    impl_->io_service_.stop();
401
402
}

403
AuthSrv::~AuthSrv() {
404
    delete impl_;
405
    delete checkin_;
406
407
    delete dns_lookup_;
    delete dns_answer_;
408
409
}

410
namespace {
411
412
class QuestionInserter {
public:
413
    QuestionInserter(Message& message) : message_(message) {}
414
    void operator()(const QuestionPtr question) {
415
        message_.addQuestion(question);
416
    }
417
    Message& message_;
418
419
};

420
void
421
422
makeErrorMessage(MessageRenderer& renderer, Message& message,
                 OutputBuffer& buffer, const Rcode& rcode,
423
424
                 std::auto_ptr<TSIGContext> tsig_context =
                 std::auto_ptr<TSIGContext>())
425
{
426
427
428
    // 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.
429
430
431
432
    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();
433
434
    vector<QuestionPtr> questions;

435
436
437
    // If this is an error to a query or notify, we should also copy the
    // question section.
    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
438
        questions.assign(message.beginQuestion(), message.endQuestion());
439
    }
440

441
442
443
444
    message.clear(Message::RENDER);
    message.setQid(qid);
    message.setOpcode(opcode);
    message.setHeaderFlag(Message::HEADERFLAG_QR);
445
    if (rd) {
446
        message.setHeaderFlag(Message::HEADERFLAG_RD);
447
448
    }
    if (cd) {
449
        message.setHeaderFlag(Message::HEADERFLAG_CD);
450
    }
451
452
    for_each(questions.begin(), questions.end(), QuestionInserter(message));

453
    message.setRcode(rcode);
454

455
    RendererHolder holder(renderer, &buffer);
456
    if (tsig_context.get() != NULL) {
457
        message.toWire(renderer, *tsig_context);
458
    } else {
459
        message.toWire(renderer);
460
    }
461
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
462
              .arg(renderer.getLength()).arg(message);
463
}
464
}
465

466
467
468
469
470
IOService&
AuthSrv::getIOService() {
    return (impl_->io_service_);
}

471
void
472
473
AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
    impl_->xfrin_session_ = xfrin_session;
474
475
}

476
void
477
478
AuthSrv::setConfigSession(ModuleCCSession* config_session) {
    impl_->config_session_ = config_session;
479
    impl_->registerStatisticsValidator();
480
481
}

482
void
483
484
AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
    impl_->counters_.setStatisticsSession(statistics_session);
485
486
}

487
ModuleCCSession*
488
AuthSrv::getConfigSession() const {
489
    return (impl_->config_session_);
490
491
}

492
493
uint32_t
AuthSrv::getStatisticsTimerInterval() const {
494
    return (impl_->statistics_timer_.getInterval() / 1000);
495
496
497
498
499
500
501
}

void
AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
    if (interval == impl_->statistics_timer_.getInterval()) {
        return;
    }
502
    if (interval > 86400) {
503
        // It can't occur since the value is checked in
504
505
506
        // statisticsIntervalConfig::build().
        isc_throw(InvalidParameter, "Too long interval: " << interval);
    }
507
508
    if (interval == 0) {
        impl_->statistics_timer_.cancel();
509
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_DISABLED);
510
    } else {
511
512
        impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
                                                   this),
513
                                       interval * 1000);
514
515
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_SET)
                  .arg(interval);
516
517
518
    }
}

519
void
520
521
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer, DNSServer* server)
JINMEI Tatuya's avatar
cleanup    
JINMEI Tatuya committed
522
{
523
524
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());

525
526
527
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
528
        message.parseHeader(request_buffer);
529
530

        // Ignore all responses.
531
        if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
532
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
533
            impl_->resumeServer(server, message, false);
534
            return;
535
536
        }
    } catch (const Exception& ex) {
537
538
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                  .arg(ex.what());
539
        impl_->resumeServer(server, message, false);
540
        return;
541
542
    }

543
    try {
544
        // Parse the message.
545
        message.fromWire(request_buffer);
546
    } catch (const DNSProtocolError& error) {
547
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
548
                  .arg(error.getRcode().toText()).arg(error.what());
549
        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
550
        impl_->resumeServer(server, message, true);
551
        return;
552
    } catch (const Exception& ex) {
553
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
554
                  .arg(ex.what());
555
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
556
        impl_->resumeServer(server, message, true);
557
        return;
558
    } // other exceptions will be handled at a higher layer.
559

560
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
561
              .arg(message);
562

563
    // Perform further protocol-level validation.
564
565
566
    // TSIG first
    // If this is set to something, we know we need to answer with TSIG as well
    std::auto_ptr<TSIGContext> tsig_context;
567
    const TSIGRecord* tsig_record(message.getTSIGRecord());
568
569
570
571
    TSIGError tsig_error(TSIGError::NOERROR());

    // Do we do TSIG?
    // The keyring can be null if we're in test
572
    if (impl_->keyring_ != NULL && tsig_record != NULL) {
573
574
575
        tsig_context.reset(new TSIGContext(tsig_record->getName(),
                                           tsig_record->getRdata().
                                                getAlgorithm(),
576
                                           **impl_->keyring_));
577
578
579
        tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                          io_message.getDataSize());
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
580

581
    if (tsig_error != TSIGError::NOERROR()) {
582
583
        makeErrorMessage(impl_->renderer_, message, buffer,
                         tsig_error.toRcode(), tsig_context);
584
        impl_->resumeServer(server, message, true);
585
586
587
        return;
    }

588
    const Opcode opcode = message.getOpcode();
589
    bool send_answer = true;
590
591
592
    try {
        // update per opcode statistics counter.  This can only be reliable
        // after TSIG check succeeds.
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
593
        impl_->counters_.inc(message.getOpcode());
594

595
        if (opcode == Opcode::NOTIFY()) {
596
597
            send_answer = impl_->processNotify(io_message, message, buffer,
                                               tsig_context);
598
        } else if (opcode == Opcode::UPDATE()) {
599
            if (impl_->ddns_forwarder_) {
600
601
602
603
604
                send_answer = impl_->processUpdate(io_message);
            } else {
                makeErrorMessage(impl_->renderer_, message, buffer,
                                 Rcode::NOTIMP(), tsig_context);
            }
605
        } else if (opcode != Opcode::QUERY()) {
606
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
607
                      .arg(message.getOpcode().toText());
608
609
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::NOTIMP(), tsig_context);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
610
        } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
611
612
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::FORMERR(), tsig_context);
613
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
614
            ConstQuestionPtr question = *message.beginQuestion();
615
            const RRType& qtype = question->getType();
616
617
618
619
620
621
622
623
624
625
            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);
            }
626
        }
627
628
629
    } catch (const std::exception& ex) {
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                  .arg(ex.what());
630
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
631
    } catch (...) {
632
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
633
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
634
    }
635
    impl_->resumeServer(server, message, send_answer);
636
637
638
}

bool
639
640
AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
                                OutputBuffer& buffer,
641
                                auto_ptr<TSIGContext> tsig_context)
642
{
643
    ConstEDNSPtr remote_edns = message.getEDNS();
644
    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
645
    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
646
        Message::DEFAULT_MAX_UDPSIZE;
647

648
649
650
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
651

652
653
654
    // Increment query counter.
    incCounter(io_message.getSocket().getProtocol());

655
656
    if (remote_edns) {
        EDNSPtr local_edns = EDNSPtr(new EDNS());
657
        local_edns->setDNSSECAwareness(dnssec_ok);
658
        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
659
        message.setEDNS(local_edns);
660
    }
661

662
    try {
663
        const ConstQuestionPtr question = *message.beginQuestion();
664
665
666
        const boost::shared_ptr<datasrc::ClientList>
            list(getClientList(question->getClass()));
        if (list) {
667
668
            const RRType& qtype = question->getType();
            const Name& qname = question->getName();
669
            query_.process(*list, qname, qtype, message, dnssec_ok);
670
        } else {
671
            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
672
            return (true);
673
        }
674
    } catch (const Exception& ex) {
675
        LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
676
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
677
        return (true);
678
    }
679

680
    RendererHolder holder(renderer_, &buffer);
681
682
    const bool udp_buffer =
        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
683
    renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
684
    if (tsig_context.get() != NULL) {
685
        message.toWire(renderer_, *tsig_context);
686
    } else {
687
        message.toWire(renderer_);
688
    }
689
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
690
              .arg(renderer_.getLength()).arg(message);
691
    return (true);
692
693
}

694
bool
695
696
AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                             OutputBuffer& buffer,
697
                             auto_ptr<TSIGContext> tsig_context)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
698
{
699
700
701
    // Increment query counter.
    incCounter(io_message.getSocket().getProtocol());

Han Feng's avatar
Han Feng committed
702
    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
703
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
704
705
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
706
        return (true);
Han Feng's avatar
Han Feng committed
707
    }
708
709

    try {
710
711
712
713
        if (!xfrout_connected_) {
            xfrout_client_.connect();
            xfrout_connected_ = true;
        }
714
715
716
717
        xfrout_client_.sendXfroutRequestInfo(
            io_message.getSocket().getNative(),
            io_message.getData(),
            io_message.getDataSize());
718
    } catch (const XfroutError& err) {
719
        if (xfrout_connected_) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
720
            // disconnect() may trigger an exception, but since we try it
721
722
723
724
            // 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();
725
            xfrout_connected_ = false;
726
        }
727

728
729
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
                  .arg(err.what());
730
731
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
                         tsig_context);
732
        return (true);
733
    }
734

735
    return (false);
736
737
738
}

bool
739
740
AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                           OutputBuffer& buffer,
741
                           std::auto_ptr<TSIGContext> tsig_context)
742
{
743
744
    // The incoming notify must contain exactly one question for SOA of the
    // zone name.
745
    if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
746
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
747
                  .arg(message.getRRCount(Message::SECTION_QUESTION));
748
749
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
750
751
        return (true);
    }
752
    ConstQuestionPtr question = *message.beginQuestion();
753
    if (question->getType() != RRType::SOA()) {
754
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
755
                  .arg(question->getType().toText());
756
757
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
758
759
760
761
762
763
764
        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.

765
766
    // TODO check with the conf-mgr whether current server is the auth of the
    // zone
767
768
769
770
771

    // 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.
772
    if (xfrin_session_ == NULL) {
773
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
774
        return (false);
Han Feng's avatar
Han Feng committed
775
    }
776

777
    LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY)
778
      .arg(question->getName()).arg(question->getClass());
779

JINMEI Tatuya's avatar
JINMEI Tatuya committed
780
781
    const string remote_ip_address =
        io_message.getRemoteEndpoint().getAddress().toText();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
782
783
    static const string command_template_start =
        "{\"command\": [\"notify\", {\"zone_name\" : \"";
784
    static const string command_template_master = "\", \"master\" : \"";
785
    static const string command_template_rrclass = "\", \"zone_class\" : \"";
786
    static const string command_template_end = "\"}]}";
787

Han Feng's avatar
Han Feng committed
788
    try {
789
        ConstElementPtr notify_command = Element::fromJSON(
790
                command_template_start + question->getName().toText() +
791
792
                command_template_master + remote_ip_address +
                command_template_rrclass + question->getClass().toText() +
793
                command_template_end);
Han Feng's avatar
Han Feng committed
794
        const unsigned int seq =
795
            xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
796
                                          "*", "*");
797
        ConstElementPtr env, answer, parsed_answer;
798
        xfrin_session_->group_recvmsg(env, answer, false, seq);
Han Feng's avatar
Han Feng committed
799
        int rcode;
800
801
        parsed_answer = parseAnswer(rcode, answer);
        if (rcode != 0) {
802
            LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
803
                      .arg(parsed_answer->str());
804
            return (false);
Han Feng's avatar
Han Feng committed
805
        }
806
    } catch (const Exception& ex) {
807
        LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
808
        return (false);
809
    }
810

811
812
813
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
814

815
    RendererHolder holder(renderer_, &buffer);
816
    if (tsig_context.get() != NULL) {
817
        message.toWire(renderer_, *tsig_context);
818
    } else {
819
        message.toWire(renderer_);
820
    }
Han Feng's avatar
Han Feng committed
821
    return (true);
822
823
}

824
bool
825
AuthSrvImpl::processUpdate(const IOMessage& io_message) {
826
827
828
    // Push the update request to a separate process via the forwarder.
    // On successful push, the request shouldn't be responded from b10-auth,
    // so we return false.
829
    ddns_forwarder_->push(io_message);
830
831
832
    return (false);
}

833
void
834
AuthSrvImpl::incCounter(const int protocol) {
835
836
    // Increment query counter.
    if (protocol == IPPROTO_UDP) {
837
        counters_.inc(AuthCounters::SERVER_UDP_QUERY);
838
    } else if (protocol == IPPROTO_TCP) {
839
        counters_.inc<