auth_srv.cc 31.8 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
#include <datasrc/data_source.h>
45
#include <datasrc/client_list.h>
46

47
48
#include <xfr/xfrout_client.h>

49
#include <auth/common.h>
50
#include <auth/auth_config.h>
51
#include <auth/auth_srv.h>
52
#include <auth/query.h>
53
#include <auth/statistics.h>
54
#include <auth/auth_log.h>
55

56
57
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
58
#include <boost/scoped_ptr.hpp>
59
60
61
62
63
64
65
66
67
68

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

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

69
70
using namespace std;

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

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

116
117
118
119
120
121
122
123
124
125
126
127
// Similar to Renderer holder, this is a very basic RAII-style class
// that calls clear(Message::PARSE) on the given Message upon destruction
class MessageHolder {
public:
    MessageHolder(Message& message) : message_(message) {}
    ~MessageHolder() {
        message_.clear(Message::PARSE);
    }
private:
    Message& message_;
};

128
129
130
// A helper container of socket session forwarder.
//
// This class provides a simple wrapper interface to SocketSessionForwarder
131
132
// so that the caller doesn't have to worry about connection management,
// exception handling or parameter building.
133
134
//
// It internally maintains whether the underlying forwarder establishes a
135
136
137
138
139
140
// 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.
141
142
class SocketSessionForwarderHolder {
public:
143
144
145
146
147
148
149
150
151
152
    /// \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)
153
    {}
154

155
156
157
158
159
    ~SocketSessionForwarderHolder() {
        if (connected_) {
            forwarder_.close();
        }
    }
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    /// \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).
191
                arg(message_name_).arg(remote_ep).arg(ex.what());
192
193
194
195
196
197
198
199
200
201
            close();
            throw;
        }
    }

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

202
203
204
205
206
207
208
    void connect() {
        if (!connected_) {
            forwarder_.connectToReceiver();
            connected_ = true;
        }
    }

209
210
211
212
213
214
215
    void close() {
        if (connected_) {
            forwarder_.close();
            connected_ = false;
        }
    }

216
217
218
219
220
221
222
223
224
225
226
    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);
        }
    }
227
};
228
229
}

230
231
232
233
234
235
class AuthSrvImpl {
private:
    // prohibit copy
    AuthSrvImpl(const AuthSrvImpl& source);
    AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
236
    AuthSrvImpl(AbstractXfroutClient& xfrout_client,
237
                BaseSocketSessionForwarder& ddns_forwarder);
Han Feng's avatar
Han Feng committed
238
    ~AuthSrvImpl();
239

240
241
    bool processNormalQuery(const IOMessage& io_message,
                            ConstEDNSPtr remote_edns, Message& message,
242
                            OutputBuffer& buffer,
243
                            auto_ptr<TSIGContext> tsig_context);
244
245
    bool processXfrQuery(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer,
246
                         auto_ptr<TSIGContext> tsig_context);
247
248
    bool processNotify(const IOMessage& io_message, Message& message,
                       OutputBuffer& buffer,
249
                       auto_ptr<TSIGContext> tsig_context);
250
    bool processUpdate(const IOMessage& io_message);
Evan Hunt's avatar
Evan Hunt committed
251

252
253
    IOService io_service_;

254
    MessageRenderer renderer_;
Evan Hunt's avatar
Evan Hunt committed
255
256
257
258
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;

    /// These members are public because AuthSrv accesses them directly.
259
    ModuleCCSession* config_session_;
Evan Hunt's avatar
Evan Hunt committed
260
261
    AbstractSession* xfrin_session_;

262
    /// Query counters for statistics
263
    Counters counters_;
264
265
266

    /// Addresses we listen on
    AddressList listen_addresses_;
267
268

    /// The TSIG keyring
269
    const boost::shared_ptr<TSIGKeyRing>* keyring_;
270

271
    /// The client list
272
273
    std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
        client_lists_;
274

275
276
277
    /// Query / Response attributes
    QRAttributes stats_attrs_;

278
279
280
    boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
                                                            rrclass)
    {
281
        const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
282
283
284
285
286
287
288
289
            const_iterator it(client_lists_.find(rrclass));
        if (it == client_lists_.end()) {
            return (boost::shared_ptr<ConfigurableClientList>());
        } else {
            return (it->second);
        }
    }

290
291
292
293
294
295
296
297
    /// 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_;

298
299
    /// \brief Resume the server
    ///
300
301
    /// This is a wrapper call for DNSServer::resume(done). Query/Response
    /// statistics counters are incremented in this method.
302
303
304
305
306
    ///
    /// 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()
307
308
    /// \param done If true, it indicates there is a response.
    ///             this value will be passed to server->resume(bool)
309
    void resumeServer(isc::asiodns::DNSServer* server,
310
                      isc::dns::Message& message,
311
                      const bool done);
312

Evan Hunt's avatar
Evan Hunt committed
313
private:
314
    bool xfrout_connected_;
315
    AbstractXfroutClient& xfrout_client_;
316

317
    auth::Query query_;
318
319
};

320
AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
321
                         BaseSocketSessionForwarder& ddns_forwarder) :
322
    config_session_(NULL),
323
    xfrin_session_(NULL),
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
352
353
354
355
        // Keep a holder on the message, so that it is automatically
        // cleared if processMessage() is done
        // This is not done in processMessage itself (which would be
        // equivalent), to allow tests to inspect the message handling.
        MessageHolder message_holder(*message);
356
        server_->processMessage(io_message, *message, *buffer, server);
357
358
359
360
361
    }
private:
    AuthSrv* server_;
};

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

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

394
AuthSrv::AuthSrv(isc::xfr::AbstractXfroutClient& xfrout_client,
395
                 isc::util::io::BaseSocketSessionForwarder& ddns_forwarder)
396
{
397
    impl_ = new AuthSrvImpl(xfrout_client, ddns_forwarder);
398
399
400
401
    checkin_ = new ConfigChecker(this);
    dns_lookup_ = new MessageLookup(this);
    dns_answer_ = new MessageAnswer(this);
}
402

403
404
void
AuthSrv::stop() {
405
    impl_->io_service_.stop();
406
407
}

408
AuthSrv::~AuthSrv() {
409
    delete impl_;
410
    delete checkin_;
411
412
    delete dns_lookup_;
    delete dns_answer_;
413
414
}

415
namespace {
416
417
class QuestionInserter {
public:
418
    QuestionInserter(Message& message) : message_(message) {}
419
    void operator()(const QuestionPtr question) {
420
        message_.addQuestion(question);
421
    }
422
    Message& message_;
423
424
};

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

440
441
442
    // If this is an error to a query or notify, we should also copy the
    // question section.
    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
443
        questions.assign(message.beginQuestion(), message.endQuestion());
444
    }
445

446
447
448
449
    message.clear(Message::RENDER);
    message.setQid(qid);
    message.setOpcode(opcode);
    message.setHeaderFlag(Message::HEADERFLAG_QR);
450
    if (rd) {
451
        message.setHeaderFlag(Message::HEADERFLAG_RD);
452
453
    }
    if (cd) {
454
        message.setHeaderFlag(Message::HEADERFLAG_CD);
455
    }
456
457
    for_each(questions.begin(), questions.end(), QuestionInserter(message));

458
    message.setRcode(rcode);
459

460
    RendererHolder holder(renderer, &buffer);
461
    if (tsig_context.get() != NULL) {
462
        message.toWire(renderer, *tsig_context);
463
    } else {
464
        message.toWire(renderer);
465
    }
466
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
467
              .arg(renderer.getLength()).arg(message);
468
}
469
}
470

471
472
473
474
475
IOService&
AuthSrv::getIOService() {
    return (impl_->io_service_);
}

476
void
477
478
AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
    impl_->xfrin_session_ = xfrin_session;
479
480
}

481
void
482
483
AuthSrv::setConfigSession(ModuleCCSession* config_session) {
    impl_->config_session_ = config_session;
484
485
486
}

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

491
void
492
493
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer, DNSServer* server)
JINMEI Tatuya's avatar
cleanup    
JINMEI Tatuya committed
494
{
495
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
496

497
498
499
    impl_->stats_attrs_.setQueryIPVersion(
        io_message.getRemoteEndpoint().getFamily());
    impl_->stats_attrs_.setQueryTransportProtocol(
500
        io_message.getRemoteEndpoint().getProtocol());
501

502
503
504
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
505
        message.parseHeader(request_buffer);
506
507

        // Ignore all responses.
508
        if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
509
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
510
            impl_->resumeServer(server, message, false);
511
            return;
512
513
        }
    } catch (const Exception& ex) {
514
515
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                  .arg(ex.what());
516
        impl_->resumeServer(server, message, false);
517
        return;
518
519
    }

520
    try {
521
        // Parse the message.
522
        message.fromWire(request_buffer);
523
    } catch (const DNSProtocolError& error) {
524
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
525
                  .arg(error.getRcode().toText()).arg(error.what());
526
        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
527
        impl_->resumeServer(server, message, true);
528
        return;
529
    } catch (const Exception& ex) {
530
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
531
                  .arg(ex.what());
532
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
533
        impl_->resumeServer(server, message, true);
534
        return;
535
    } // other exceptions will be handled at a higher layer.
536

537
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
538
              .arg(message);
539

540
    // Perform further protocol-level validation.
541
542
543
    // TSIG first
    // If this is set to something, we know we need to answer with TSIG as well
    std::auto_ptr<TSIGContext> tsig_context;
544
    const TSIGRecord* tsig_record(message.getTSIGRecord());
545
546
547
548
    TSIGError tsig_error(TSIGError::NOERROR());

    // Do we do TSIG?
    // The keyring can be null if we're in test
549
    if (impl_->keyring_ != NULL && tsig_record != NULL) {
550
551
552
        tsig_context.reset(new TSIGContext(tsig_record->getName(),
                                           tsig_record->getRdata().
                                                getAlgorithm(),
553
                                           **impl_->keyring_));
554
555
        tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                          io_message.getDataSize());
556
        impl_->stats_attrs_.setQuerySig(true, false,
557
                                        tsig_error != TSIGError::NOERROR());
558
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
559

560
    if (tsig_error != TSIGError::NOERROR()) {
561
562
        makeErrorMessage(impl_->renderer_, message, buffer,
                         tsig_error.toRcode(), tsig_context);
563
        impl_->resumeServer(server, message, true);
564
565
566
        return;
    }

567
    const Opcode opcode = message.getOpcode();
568
    bool send_answer = true;
569
    try {
570
        // note: This can only be reliable after TSIG check succeeds.
571
572
573
574
575
        ConstEDNSPtr edns = message.getEDNS();
        if (edns) {
            impl_->stats_attrs_.setQueryEDNS(true,
                                             edns->getVersion() != 0);
            impl_->stats_attrs_.setQueryDO(edns->getDNSSECAwareness());
576
577
        }

578
        // note: This can only be reliable after TSIG check succeeds.
579
        impl_->stats_attrs_.setQueryOpCode(opcode.getCode());
580

581
        if (opcode == Opcode::NOTIFY()) {
582
583
            send_answer = impl_->processNotify(io_message, message, buffer,
                                               tsig_context);
584
        } else if (opcode == Opcode::UPDATE()) {
585
            if (impl_->ddns_forwarder_) {
586
587
588
589
590
                send_answer = impl_->processUpdate(io_message);
            } else {
                makeErrorMessage(impl_->renderer_, message, buffer,
                                 Rcode::NOTIMP(), tsig_context);
            }
591
        } else if (opcode != Opcode::QUERY()) {
592
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
593
                      .arg(message.getOpcode().toText());
594
595
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::NOTIMP(), tsig_context);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
596
        } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
597
598
            makeErrorMessage(impl_->renderer_, message, buffer,
                             Rcode::FORMERR(), tsig_context);
599
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
600
            ConstQuestionPtr question = *message.beginQuestion();
601
            const RRType& qtype = question->getType();
602
603
604
605
606
607
608
            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 {
609
610
611
                send_answer = impl_->processNormalQuery(io_message, edns,
                                                        message, buffer,
                                                        tsig_context);
612
            }
613
        }
614
615
616
    } catch (const std::exception& ex) {
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                  .arg(ex.what());
617
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
618
    } catch (...) {
619
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
620
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
621
    }
622
    impl_->resumeServer(server, message, send_answer);
623
624
625
}

bool
626
627
AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
                                ConstEDNSPtr remote_edns, Message& message,
628
                                OutputBuffer& buffer,
629
                                auto_ptr<TSIGContext> tsig_context)
630
{
631
    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
632
    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
633
        Message::DEFAULT_MAX_UDPSIZE;
634

635
636
637
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
638
639
640

    if (remote_edns) {
        EDNSPtr local_edns = EDNSPtr(new EDNS());
641
        local_edns->setDNSSECAwareness(dnssec_ok);
642
        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
643
        message.setEDNS(local_edns);
644
    }
645

646
    try {
647
        const ConstQuestionPtr question = *message.beginQuestion();
648
649
650
        const boost::shared_ptr<datasrc::ClientList>
            list(getClientList(question->getClass()));
        if (list) {
651
652
            const RRType& qtype = question->getType();
            const Name& qname = question->getName();
653
            query_.process(*list, qname, qtype, message, dnssec_ok);
654
        } else {
655
            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
656
            return (true);
657
        }
658
    } catch (const Exception& ex) {
659
        LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
660
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
661
        return (true);
662
    }
663

664
    RendererHolder holder(renderer_, &buffer);
665
666
    const bool udp_buffer =
        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
667
    renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
668
    if (tsig_context.get() != NULL) {
669
        message.toWire(renderer_, *tsig_context);
670
    } else {
671
        message.toWire(renderer_);
672
    }
673
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
674
              .arg(renderer_.getLength()).arg(message);
675
    return (true);
676
677
}

678
bool
679
680
AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                             OutputBuffer& buffer,
681
                             auto_ptr<TSIGContext> tsig_context)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
682
{
Han Feng's avatar
Han Feng committed
683
    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
684
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
685
686
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
687
        return (true);
Han Feng's avatar
Han Feng committed
688
    }
689
690

    try {
691
692
693
694
        if (!xfrout_connected_) {
            xfrout_client_.connect();
            xfrout_connected_ = true;
        }
695
696
697
698
        xfrout_client_.sendXfroutRequestInfo(
            io_message.getSocket().getNative(),
            io_message.getData(),
            io_message.getDataSize());
699
    } catch (const XfroutError& err) {
700
        if (xfrout_connected_) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
701
            // disconnect() may trigger an exception, but since we try it
702
703
704
705
            // 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();
706
            xfrout_connected_ = false;
707
        }
708

709
710
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
                  .arg(err.what());
711
712
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
                         tsig_context);
713
        return (true);
714
    }
715

716
    return (false);
717
718
719
}

bool
720
721
AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                           OutputBuffer& buffer,
722
                           std::auto_ptr<TSIGContext> tsig_context)
723
{
724
725
    // The incoming notify must contain exactly one question for SOA of the
    // zone name.
726
    if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
727
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
728
                  .arg(message.getRRCount(Message::SECTION_QUESTION));
729
730
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
731
732
        return (true);
    }
733
    ConstQuestionPtr question = *message.beginQuestion();
734
    if (question->getType() != RRType::SOA()) {
735
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
736
                  .arg(question->getType().toText());
737
738
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
                         tsig_context);
739
740
741
742
743
744
745
        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.

746
747
    // TODO check with the conf-mgr whether current server is the auth of the
    // zone
748
749
750
751
752

    // 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.
753
    if (xfrin_session_ == NULL) {
754
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
755
        return (false);
Han Feng's avatar
Han Feng committed
756
    }
757

758
    LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY)
759
      .arg(question->getName()).arg(question->getClass());
760

JINMEI Tatuya's avatar
JINMEI Tatuya committed
761
762
    const string remote_ip_address =
        io_message.getRemoteEndpoint().getAddress().toText();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
763
764
    static const string command_template_start =
        "{\"command\": [\"notify\", {\"zone_name\" : \"";
765
    static const string command_template_master = "\", \"master\" : \"";
766
    static const string command_template_rrclass = "\", \"zone_class\" : \"";
767
    static const string command_template_end = "\"}]}";
768

Han Feng's avatar
Han Feng committed
769
    try {
770
        ConstElementPtr notify_command = Element::fromJSON(
771
                command_template_start + question->getName().toText() +
772
773
                command_template_master + remote_ip_address +
                command_template_rrclass + question->getClass().toText() +
774
                command_template_end);
Han Feng's avatar
Han Feng committed
775
        const unsigned int seq =
776
            xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
777
                                          "*", "*");
778
        ConstElementPtr env, answer, parsed_answer;
779
        xfrin_session_->group_recvmsg(env, answer, false, seq);
Han Feng's avatar
Han Feng committed
780
        int rcode;
781
782
        parsed_answer = parseAnswer(rcode, answer);
        if (rcode != 0) {
783
            LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
784
                      .arg(parsed_answer->str());
785
            return (false);
Han Feng's avatar
Han Feng committed
786
        }
787
    } catch (const Exception& ex) {
788
        LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
789
        return (false);
790
    }
791

792
793
794
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
795

796
    RendererHolder holder(renderer_, &buffer);
797
    if (tsig_context.get() != NULL) {
798
        message.toWire(renderer_, *tsig_context);
799
    } else {
800
        message.toWire(renderer_);
801
    }
Han Feng's avatar
Han Feng committed
802
    return (true);
803
804
}

805
bool
806
AuthSrvImpl::processUpdate(const IOMessage& io_message) {
807
808
809
    // 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.
810
    ddns_forwarder_->push(io_message);
811
812
813
    return (false);
}

814
void
815
816
AuthSrvImpl::resumeServer(DNSServer* server, Message& message,
                          const bool done) {
817
    if (done) {
818
        // isTruncated from MessageRenderer
819
        stats_attrs_.setResponseTruncated(renderer_.isTruncated());
820
    }
821
    counters_.inc(stats_attrs_, message, done);
822
    stats_attrs_.reset();
823
824
825
    server->resume(done);
}

826
827
ConstElementPtr
AuthSrv::updateConfig(ConstElementPtr new_config) {
828
    try {
829
830
        // the ModuleCCSession has already checked if we have
        // the correct ElementPtr type as specified in our .spec file
JINMEI Tatuya's avatar
JINMEI Tatuya committed
831
832
833
        if (new_config) {
            configureAuthServer(*this, new_config);
        }
834
        return (isc::config::createAnswer());
835
    } catch (const isc::Exception& error) {
836
        LOG_ERROR(auth_logger, AUTH_CONFIG_UPDATE_FAIL).arg(error.what());
JINMEI Tatuya's avatar
JINMEI Tatuya committed
837
        return (isc::config::createAnswer(1, error.what()));
838
    }
839
}
840

841
ConstElementPtr AuthSrv::getStatistics() const {
842
    return (impl_->counters_.get());
843
844
}

845
846
847
848
849
850
851
const AddressList&
AuthSrv::getListenAddresses() const {
    return (impl_->listen_addresses_);
}

void
AuthSrv::setListenAddresses(const AddressList& addresses) {
852
853
854
855
    // For UDP servers we specify the "SYNC_OK" option because in our usage
    // it can act in the synchronous mode.
    installListenAddresses(addresses, impl_->listen_addresses_, *dnss_,
                           DNSService::SERVER_SYNC_OK);
856
857
858
}

void
859
AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
860
861
    dnss_ = &dnss;
}
Michal 'vorner' Vaner's avatar