auth_srv.cc 36.5 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

#include <config/ccsession.h>

#include <cc/data.h>
25
#include <cc/proto_defs.h>
26

27
28
#include <exceptions/exceptions.h>

29
#include <util/buffer.h>
30

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

43
44
#include <asiodns/dns_service.h>

45
#include <datasrc/exceptions.h>
46
#include <datasrc/client_list.h>
47

48
49
#include <xfr/xfrout_client.h>

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

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

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

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

71
72
using namespace std;

73
74
using boost::shared_ptr;

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

92
93
94
95
96
97
98
99
100
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.
101
102
103
104
105
//
// 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 {
106
public:
107
108
109
110
    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer,
                   MessageAttributes& stats_attrs) :
        renderer_(renderer),
        stats_attrs_(stats_attrs)
111
112
113
114
    {
        renderer.setBuffer(buffer);
    }
    ~RendererHolder() {
115
        stats_attrs_.setResponseTruncated(renderer_.isTruncated());
116
117
118
119
120
        renderer_.setBuffer(NULL);
        renderer_.clear();
    }
private:
    MessageRenderer& renderer_;
121
    MessageAttributes& stats_attrs_;
122
};
123

124
125
126
127
128
129
130
131
132
133
134
135
// 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_;
};

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

163
164
165
166
167
    ~SocketSessionForwarderHolder() {
        if (connected_) {
            forwarder_.close();
        }
    }
168

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
    /// \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).
199
                arg(message_name_).arg(remote_ep).arg(ex.what());
200
201
202
203
204
205
206
207
208
209
            close();
            throw;
        }
    }

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

210
211
212
213
214
215
216
    void connect() {
        if (!connected_) {
            forwarder_.connectToReceiver();
            connected_ = true;
        }
    }

217
218
219
220
221
222
223
    void close() {
        if (connected_) {
            forwarder_.close();
            connected_ = false;
        }
    }

224
225
226
227
228
229
230
231
232
233
234
    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);
        }
    }
235
};
236
237
}

238
239
240
241
242
243
class AuthSrvImpl {
private:
    // prohibit copy
    AuthSrvImpl(const AuthSrvImpl& source);
    AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
244
    AuthSrvImpl(AbstractXfroutClient& xfrout_client,
245
                BaseSocketSessionForwarder& ddns_forwarder);
Han Feng's avatar
Han Feng committed
246
    ~AuthSrvImpl();
247

248
249
    bool processNormalQuery(const IOMessage& io_message,
                            ConstEDNSPtr remote_edns, Message& message,
250
                            OutputBuffer& buffer,
251
252
                            auto_ptr<TSIGContext> tsig_context,
                            MessageAttributes& stats_attrs);
253
254
    bool processXfrQuery(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer,
255
256
                         auto_ptr<TSIGContext> tsig_context,
                         MessageAttributes& stats_attrs);
257
258
    bool processNotify(const IOMessage& io_message, Message& message,
                       OutputBuffer& buffer,
259
260
                       auto_ptr<TSIGContext> tsig_context,
                       MessageAttributes& stats_attrs);
261
    bool processUpdate(const IOMessage& io_message);
Evan Hunt's avatar
Evan Hunt committed
262

263
264
    IOService io_service_;

265
    MessageRenderer renderer_;
Evan Hunt's avatar
Evan Hunt committed
266
267
268
269
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;

    /// These members are public because AuthSrv accesses them directly.
270
    ModuleCCSession* config_session_;
Evan Hunt's avatar
Evan Hunt committed
271
272
    AbstractSession* xfrin_session_;

273
    /// Query counters for statistics
274
    Counters counters_;
275
276
277

    /// Addresses we listen on
    AddressList listen_addresses_;
278
279

    /// The TSIG keyring
280
    const shared_ptr<TSIGKeyRing>* keyring_;
281

282
    /// The data source client list manager
283
    auth::DataSrcClientsMgr datasrc_clients_mgr_;
284

285
286
287
288
289
290
291
292
    /// 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_;

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

309
    /// Are we currently subscribed to the SegmentReader group?
310
    bool readers_group_subscribed_;
Evan Hunt's avatar
Evan Hunt committed
311
private:
312
    bool xfrout_connected_;
313
    AbstractXfroutClient& xfrout_client_;
314

315
    auth::Query query_;
316
317
};

318
AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
319
                         BaseSocketSessionForwarder& ddns_forwarder) :
320
    config_session_(NULL),
321
    xfrin_session_(NULL),
322
    counters_(),
323
    keyring_(NULL),
324
    datasrc_clients_mgr_(io_service_),
325
    ddns_base_forwarder_(ddns_forwarder),
326
    ddns_forwarder_(NULL),
327
    readers_group_subscribed_(false),
328
329
    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
396
                 isc::util::io::BaseSocketSessionForwarder& ddns_forwarder) :
    dnss_(NULL)
397
{
398
    impl_ = new AuthSrvImpl(xfrout_client, ddns_forwarder);
399
400
401
402
    checkin_ = new ConfigChecker(this);
    dns_lookup_ = new MessageLookup(this);
    dns_answer_ = new MessageAnswer(this);
}
403

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

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

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

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

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

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

460
    message.setRcode(rcode);
461

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

474
475
476
477
478
IOService&
AuthSrv::getIOService() {
    return (impl_->io_service_);
}

479
480
481
482
483
isc::auth::DataSrcClientsMgr&
AuthSrv::getDataSrcClientsMgr() {
    return (impl_->datasrc_clients_mgr_);
}

484
void
485
486
AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
    impl_->xfrin_session_ = xfrin_session;
487
488
}

489
void
490
491
AuthSrv::setConfigSession(ModuleCCSession* config_session) {
    impl_->config_session_ = config_session;
492
493
494
}

ModuleCCSession*
495
AuthSrv::getConfigSession() const {
496
    return (impl_->config_session_);
497
498
}

499
void
500
501
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer, DNSServer* server)
JINMEI Tatuya's avatar
cleanup    
JINMEI Tatuya committed
502
{
503
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
504
    MessageAttributes stats_attrs;
505

506
    stats_attrs.setRequestIPVersion(
507
        io_message.getRemoteEndpoint().getFamily());
508
    stats_attrs.setRequestTransportProtocol(
509
        io_message.getRemoteEndpoint().getProtocol());
510

511
512
513
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
514
        message.parseHeader(request_buffer);
515
516

        // Ignore all responses.
517
        if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
518
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
519
            impl_->resumeServer(server, message, stats_attrs, false);
520
            return;
521
522
        }
    } catch (const Exception& ex) {
523
524
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                  .arg(ex.what());
525
        impl_->resumeServer(server, message, stats_attrs, false);
526
        return;
527
528
    }

529
530
    stats_attrs.setRequestRD(message.getHeaderFlag(Message::HEADERFLAG_RD));

531
    const Opcode& opcode = message.getOpcode();
532
533
    // Get opcode at this point; for all requests regardless of message body
    // sanity check.
534
535
    stats_attrs.setRequestOpCode(opcode);

536
    try {
537
        // Parse the message.
538
        message.fromWire(request_buffer);
539
    } catch (const DNSProtocolError& error) {
540
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_FAILURE)
541
                  .arg(error.getRcode().toText()).arg(error.what());
542
543
        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode(),
                         stats_attrs);
544
        impl_->resumeServer(server, message, stats_attrs, true);
545
        return;
546
    } catch (const Exception& ex) {
547
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_FAILED)
548
                  .arg(ex.what());
549
550
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
                         stats_attrs);
551
        impl_->resumeServer(server, message, stats_attrs, true);
552
        return;
553
    } // other exceptions will be handled at a higher layer.
554

555
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
556
              .arg(message);
557

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

    // Do we do TSIG?
    // The keyring can be null if we're in test
567
    if (impl_->keyring_ != NULL && tsig_record != NULL) {
568
569
570
        tsig_context.reset(new TSIGContext(tsig_record->getName(),
                                           tsig_record->getRdata().
                                                getAlgorithm(),
571
                                           **impl_->keyring_));
572
573
        tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                          io_message.getDataSize());
574
        stats_attrs.setRequestTSIG(true, tsig_error != TSIGError::NOERROR());
575
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
576

577
    if (tsig_error != TSIGError::NOERROR()) {
578
        makeErrorMessage(impl_->renderer_, message, buffer,
579
                         tsig_error.toRcode(), stats_attrs, tsig_context);
580
        impl_->resumeServer(server, message, stats_attrs, true);
581
582
583
584
        return;
    }

    bool send_answer = true;
585
    try {
586
        // note: This can only be reliable after TSIG check succeeds.
587
        ConstEDNSPtr edns = message.getEDNS();
588
        if (edns) {
589
590
            stats_attrs.setRequestEDNS0(true);
            stats_attrs.setRequestDO(edns->getDNSSECAwareness());
591
592
        }

593
        // note: This can only be reliable after TSIG check succeeds.
594
        if (opcode == Opcode::NOTIFY()) {
595
            send_answer = impl_->processNotify(io_message, message, buffer,
596
                                               tsig_context, stats_attrs);
597
        } else if (opcode == Opcode::UPDATE()) {
598
            if (impl_->ddns_forwarder_) {
599
600
601
                send_answer = impl_->processUpdate(io_message);
            } else {
                makeErrorMessage(impl_->renderer_, message, buffer,
602
                                 Rcode::NOTIMP(), stats_attrs, tsig_context);
603
            }
604
        } else if (opcode != Opcode::QUERY()) {
605
            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
606
                      .arg(message.getOpcode().toText());
607
            makeErrorMessage(impl_->renderer_, message, buffer,
608
                             Rcode::NOTIMP(), stats_attrs, tsig_context);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
609
        } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
610
            makeErrorMessage(impl_->renderer_, message, buffer,
611
                             Rcode::FORMERR(), stats_attrs, tsig_context);
612
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
613
            ConstQuestionPtr question = *message.beginQuestion();
614
            const RRType& qtype = question->getType();
615
616
            if (qtype == RRType::AXFR()) {
                send_answer = impl_->processXfrQuery(io_message, message,
617
618
                                                     buffer, tsig_context,
                                                     stats_attrs);
619
620
            } else if (qtype == RRType::IXFR()) {
                send_answer = impl_->processXfrQuery(io_message, message,
621
622
                                                     buffer, tsig_context,
                                                     stats_attrs);
623
            } else {
624
625
                send_answer = impl_->processNormalQuery(io_message, edns,
                                                        message, buffer,
626
627
                                                        tsig_context,
                                                        stats_attrs);
628
            }
629
        }
630
631
632
    } catch (const std::exception& ex) {
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                  .arg(ex.what());
633
634
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
                         stats_attrs);
635
    } catch (...) {
636
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
637
638
        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
                         stats_attrs);
639
    }
640
    impl_->resumeServer(server, message, stats_attrs, send_answer);
641
642
643
}

bool
644
645
AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
                                ConstEDNSPtr remote_edns, Message& message,
646
                                OutputBuffer& buffer,
647
648
                                auto_ptr<TSIGContext> tsig_context,
                                MessageAttributes& stats_attrs)
649
{
650
    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
651
    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
652
        Message::DEFAULT_MAX_UDPSIZE;
653

654
655
656
    message.makeResponse();
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    message.setRcode(Rcode::NOERROR());
657
658
659

    if (remote_edns) {
        EDNSPtr local_edns = EDNSPtr(new EDNS());
660
        local_edns->setDNSSECAwareness(dnssec_ok);
661
        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
662
        message.setEDNS(local_edns);
663
    }
664
665
666
667

    // Get access to data source client list through the holder and keep
    // the holder until the processing and rendering is done to avoid
    // race with any other thread(s) such as the background loader.
668
    auth::DataSrcClientsMgr::Holder datasrc_holder(datasrc_clients_mgr_);
669

670
    try {
671
        const ConstQuestionPtr question = *message.beginQuestion();
672
        const shared_ptr<datasrc::ClientList>
673
            list(datasrc_holder.findClientList(question->getClass()));
674
        if (list) {
675
676
            const RRType& qtype = question->getType();
            const Name& qname = question->getName();
677
            query_.process(*list, qname, qtype, message, dnssec_ok);
678
        } else {
679
680
            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED(),
                             stats_attrs);
681
            return (true);
682
        }
683
    } catch (const Exception& ex) {
684
        LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
685
686
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
                         stats_attrs);
687
        return (true);
688
    }
689

690
    RendererHolder holder(renderer_, &buffer, stats_attrs);
691
692
    const bool udp_buffer =
        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
693
    renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
694
    if (tsig_context.get() != NULL) {
695
        message.toWire(renderer_, *tsig_context);
696
        stats_attrs.setResponseTSIG(true);
697
    } else {
698
        message.toWire(renderer_);
699
    }
700
    LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
701
              .arg(renderer_.getLength()).arg(message);
702
    return (true);
703
704
    // The message can contain some data from the locked resource. But outside
    // this method, we touch only the RCode of it, so it should be safe.
705
706
707

    // Lock on datasrc_clients_mgr_ acquired by datasrc_holder is
    // released here upon its deletion.
708
709
}

710
bool
711
712
AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                             OutputBuffer& buffer,
713
714
                             auto_ptr<TSIGContext> tsig_context,
                             MessageAttributes& stats_attrs)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
715
{
Han Feng's avatar
Han Feng committed
716
    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
717
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
718
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
719
                         stats_attrs, tsig_context);
720
        return (true);
Han Feng's avatar
Han Feng committed
721
    }
722
723

    try {
724
725
726
727
        if (!xfrout_connected_) {
            xfrout_client_.connect();
            xfrout_connected_ = true;
        }
728
729
730
731
        xfrout_client_.sendXfroutRequestInfo(
            io_message.getSocket().getNative(),
            io_message.getData(),
            io_message.getDataSize());
732
    } catch (const XfroutError& err) {
733
        if (xfrout_connected_) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
734
            // disconnect() may trigger an exception, but since we try it
735
736
737
738
            // 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();
739
            xfrout_connected_ = false;
740
        }
741

742
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_PROBLEM)
743
                  .arg(err.what());
744
        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
745
                         stats_attrs, tsig_context);
746
        return (true);
747
    }
748

749
    return (false);
750
751
752
}

bool
753
754
AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                           OutputBuffer& buffer,
755
756
                           std::auto_ptr<TSIGContext> tsig_context,
                           MessageAttributes& stats_attrs)
757
{
758
759
    const IOEndpoint& remote_ep = io_message.getRemoteEndpoint(); // for logs

760
761
    // The incoming notify must contain exactly one question for SOA of the
    // zone name.
762
    if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
763
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
764
                  .arg(message.getRRCount(Message::SECTION_QUESTION));
765
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
766
                         stats_attrs, tsig_context);
767
768
        return (true);
    }
769
    ConstQuestionPtr question = *message.beginQuestion();
770
    if (question->getType() != RRType::SOA()) {
771
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
772
                  .arg(question->getType().toText());
773
        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
774
                         stats_attrs, tsig_context);
775
776
777
778
779
780
781
        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.

782
783
784
785
786
787
788
789
790
791
792
    // See if we have the specified zone in our data sources; if not return
    // NOTAUTH, following BIND 9 (this is not specified in RFC 1996).
    bool is_auth = false;
    {
        auth::DataSrcClientsMgr::Holder datasrc_holder(datasrc_clients_mgr_);
        const shared_ptr<datasrc::ClientList> dsrc_clients =
            datasrc_holder.findClientList(question->getClass());
        is_auth = dsrc_clients &&
            dsrc_clients->find(question->getName(), true, false).exact_match_;
    }
    if (!is_auth) {
793
794
        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY_NOTAUTH)
            .arg(question->getName()).arg(question->getClass()).arg(remote_ep);
795
796
797
798
        makeErrorMessage(renderer_, message, buffer, Rcode::NOTAUTH(),
                         stats_attrs, tsig_context);
        return (true);
    }
799

800
    LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY)
801
        .arg(question->getName()).arg(question->getClass()).arg(remote_ep);
802

803
804
805
806
807
808
    // xfrin_session_ should have been set and never be replaced except in
    // tests; otherwise it's an internal bug.  assert() may be too strong,
    // but processMessage() will catch all exceptions, so there's no better
    // way.
    assert(xfrin_session_);

809
    const string remote_ip_address = remote_ep.getAddress().toText();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
810
811
    static const string command_template_start =
        "{\"command\": [\"notify\", {\"zone_name\" : \"";
812
    static const string command_template_master = "\", \"master\" : \"";
813
    static const string command_template_rrclass = "\", \"zone_class\" : \"";
814
    static const string command_template_end = "\"}]}";
815

Han Feng's avatar
Han Feng committed
816
    try {
817
        ConstElementPtr notify_command = Element::fromJSON(
818
                command_template_start + question->getName().toText() +
819
820
                command_template_master + remote_ip_address +
                command_template_rrclass + question->getClass().toText() +
821
                command_template_end);
Han Feng's avatar
Han Feng committed
822
        const unsigned int seq =
823
            xfrin_session_->group_sendmsg(notify_command, "Zonemgr",