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

// $Id$

17
18
#include <config.h>             // for UNUSED_PARAM

19
20
#include <netinet/in.h>

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

26
27
#include <exceptions/exceptions.h>

28
#include <dns/buffer.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
29
#include <dns/exceptions.h>
30
31
32
33
34
35
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/question.h>
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
36
#include <config/ccsession.h>
37
38
#include <cc/data.h>
#include <exceptions/exceptions.h>
39

Evan Hunt's avatar
Evan Hunt committed
40
41
42
43
#include <datasrc/query.h>
#include <datasrc/data_source.h>
#include <datasrc/static_datasrc.h>
#include <datasrc/sqlite3_datasrc.h>
44

45
#include <cc/data.h>
46

47
48
49
50
51
#if defined(HAVE_BOOST_PYTHON)
#define USE_XFROUT
#include <xfr/xfrout_client.h>
#endif

52
53
54
#include <auth/common.h>
#include <auth/auth_srv.h>
#include <auth/asio_link.h>
55
#include <auth/spec_config.h>
56
57
58
59
60

#include <boost/lexical_cast.hpp>

using namespace std;

61
using namespace isc;
Evan Hunt's avatar
Evan Hunt committed
62
using namespace isc::datasrc;
63
64
65
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::data;
66
using namespace isc::config;
67
68
69
#ifdef USE_XFROUT
using namespace isc::xfr;
#endif
70
using namespace asio_link;
71

72
73
74
75
76
77
class AuthSrvImpl {
private:
    // prohibit copy
    AuthSrvImpl(const AuthSrvImpl& source);
    AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
78
    AuthSrvImpl();
Han Feng's avatar
Han Feng committed
79
    ~AuthSrvImpl();
80
81
    isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);

82
83
    bool processNormalQuery(const IOMessage& io_message, Message& message,
                            MessageRenderer& response_renderer);
Han Feng's avatar
Han Feng committed
84
    bool processAxfrQuery(const IOMessage& io_message, Message& message,
85
                            MessageRenderer& response_renderer) ;
Han Feng's avatar
Han Feng committed
86
87
    bool processNotify(const IOMessage& io_message, Message& message, 
                            MessageRenderer& response_renderer) ;
88
    std::string db_file_;
89
90
    ModuleCCSession* cs_;
    MetaDataSrc data_sources_;
91
92
93
    /// We keep a pointer to the currently running sqlite datasource
    /// so that we can specifically remove that one should the database
    /// file change
94
    ConstDataSrcPtr cur_datasrc_;
95

96
97
    bool verbose_mode_;

98
    bool is_xfrin_session_established_;
Han Feng's avatar
Han Feng committed
99
    isc::cc::Session session_with_xfrin_;
100

101
102
103
    bool is_xfrin_connection_established_;
    XfroutClient axfr_client_;

104
105
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
106
107
};

108
AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false),
109
110
111
                             is_xfrin_session_established_(false),
                             is_xfrin_connection_established_(false),
                             axfr_client_(UNIX_SOCKET_FILE)
112
{
113
114
    // cur_datasrc_ is automatically initialized by the default constructor,
    // effectively being an empty (sqlite) data source.  once ccsession is up
115
    // the datasource will be set by the configuration setting
116

117
    // add static data source
118
    data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
119
120
}

Han Feng's avatar
Han Feng committed
121
AuthSrvImpl::~AuthSrvImpl() {
122
    if (is_xfrin_session_established_) {
Han Feng's avatar
Han Feng committed
123
        session_with_xfrin_.disconnect();
124
125
126
127
128
        is_xfrin_session_established_ = false;
    }

    if (is_xfrin_connection_established_) {
        axfr_client_.disconnect();
Han Feng's avatar
Han Feng committed
129
130
131
132
        is_xfrin_connection_established_ = false;
    }
}

133
134
AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) {
}
135
136

AuthSrv::~AuthSrv() {
137
138
    delete impl_;
}
139

140
namespace {
141
142
143
144
145
146
147
148
149
class QuestionInserter {
public:
    QuestionInserter(Message* message) : message_(message) {}
    void operator()(const QuestionPtr question) {
        message_->addQuestion(question);
    }
    Message* message_;
};

150
void
151
makeErrorMessage(Message& message, MessageRenderer& renderer,
152
                 const Rcode& rcode, const bool verbose_mode)
153
{
154
155
156
157
158
159
160
    // extract the parameters that should be kept.
    // XXX: with the current implementation, it's not easy to set EDNS0
    // depending on whether the query had it.  So we'll simply omit it.
    const qid_t qid = message.getQid();
    const bool rd = message.getHeaderFlag(MessageFlag::RD());
    const bool cd = message.getHeaderFlag(MessageFlag::CD());
    const Opcode& opcode = message.getOpcode();
161
162
163
164
165
166
    vector<QuestionPtr> questions;

    // If this is an error to a query, we should also copy the question section.
    if (opcode == Opcode::QUERY()) {
        questions.assign(message.beginQuestion(), message.endQuestion());
    }
167
168
169
170
171

    message.clear(Message::RENDER);
    message.setQid(qid);
    message.setOpcode(opcode);
    message.setHeaderFlag(MessageFlag::QR());
172
    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
173
174
175
176
177
178
    if (rd) {
        message.setHeaderFlag(MessageFlag::RD());
    }
    if (cd) {
        message.setHeaderFlag(MessageFlag::CD());
    }
179
    for_each(questions.begin(), questions.end(), QuestionInserter(&message));
180
181
    message.setRcode(rcode);
    message.toWire(renderer);
182
183

    if (verbose_mode) {
184
        cerr << "[b10-auth] sending an error response (" <<
185
186
187
            boost::lexical_cast<string>(renderer.getLength())
             << " bytes):\n" << message.toText() << endl;
    }
188
}
189
}
190

191
192
193
194
195
196
197
198
199
200
void
AuthSrv::setVerbose(const bool on) {
    impl_->verbose_mode_ = on;
}

bool
AuthSrv::getVerbose() const {
    return (impl_->verbose_mode_);
}

201
202
203
204
205
206
207
208
209
210
void
AuthSrv::setConfigSession(ModuleCCSession* cs) {
    impl_->cs_ = cs;
}

ModuleCCSession*
AuthSrv::configSession() const {
    return (impl_->cs_);
}

211
bool
212
213
AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                        MessageRenderer& response_renderer)
JINMEI Tatuya's avatar
cleanup    
JINMEI Tatuya committed
214
{
215
216
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());

217
218
219
220
221
222
223
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
        message.parseHeader(request_buffer);

        // Ignore all responses.
        if (message.getHeaderFlag(MessageFlag::QR())) {
224
            if (impl_->verbose_mode_) {
225
                cerr << "[b10-auth] received unexpected response, ignoring" << endl;
226
227
228
229
230
231
232
            }
            return (false);
        }
    } catch (const Exception& ex) {
        return (false);
    }

233
    // Parse the message.  On failure, return an appropriate error.
234
    try {
235
        message.fromWire(request_buffer);
236
    } catch (const DNSProtocolError& error) {
237
        if (impl_->verbose_mode_) {
238
            cerr << "[b10-auth] returning " <<  error.getRcode().toText() << ": "
239
240
                 << error.what() << endl;
        }
241
        makeErrorMessage(message, response_renderer, error.getRcode(),
242
                         impl_->verbose_mode_);
243
        return (true);
244
    } catch (const Exception& ex) {
245
        if (impl_->verbose_mode_) {
246
            cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
247
        }
248
        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
249
                         impl_->verbose_mode_);
250
        return (true);
251
    } // other exceptions will be handled at a higher layer.
252

253
    if (impl_->verbose_mode_) {
254
        cerr << "[b10-auth] received a message:\n" << message.toText() << endl;
255
    }
256

257
    // Perform further protocol-level validation.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
258

259
    // In this implementation, we only support normal queries
Han Feng's avatar
Han Feng committed
260
261
262
    if (message.getOpcode() == Opcode::NOTIFY()) {
        return (impl_->processNotify(io_message, message, response_renderer));
    } else if (message.getOpcode() != Opcode::QUERY()) {
263
        if (impl_->verbose_mode_) {
264
            cerr << "[b10-auth] unsupported opcode" << endl;
265
        }
266
        makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
267
                         impl_->verbose_mode_);
268
        return (true);
269
270
    }

271
    if (message.getRRCount(Section::QUESTION()) != 1) {
272
        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
273
                         impl_->verbose_mode_);
274
        return (true);
275
    }
276

277
278
279
    ConstQuestionPtr question = *message.beginQuestion();
    const RRType &qtype = question->getType();
    if (qtype == RRType::AXFR()) {
Han Feng's avatar
Han Feng committed
280
        return (impl_->processAxfrQuery(io_message, message, response_renderer));
281
    } else if (qtype == RRType::IXFR()) {
Han Feng's avatar
Han Feng committed
282
283
284
        makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
                         impl_->verbose_mode_);
        return (true);
285
286
287
288
289
290
291
292
293
294
    } else {
        return (impl_->processNormalQuery(io_message, message,
                                          response_renderer));
    }
}

bool
AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
                                MessageRenderer& response_renderer)
{
295
296
    const bool dnssec_ok = message.isDNSSECSupported();
    const uint16_t remote_bufsize = message.getUDPSize();
297
298
299
300
301

    message.makeResponse();
    message.setHeaderFlag(MessageFlag::AA());
    message.setRcode(Rcode::NOERROR());
    message.setDNSSECSupported(dnssec_ok);
302
    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
303

304
305
    try {
        Query query(message, dnssec_ok);
306
        data_sources_.doQuery(query);
307
    } catch (const Exception& ex) {
308
309
310
        if (verbose_mode_) {
            cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
                ex.what() << endl;
311
312
        }
        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
313
                         verbose_mode_);
314
        return (true);
315
    }
316

317
318
    const bool udp_buffer =
        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
319
    response_renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
320
    message.toWire(response_renderer);
321
    if (verbose_mode_) {
322
        cerr << "[b10-auth] sending a response (" <<
323
324
325
            boost::lexical_cast<string>(response_renderer.getLength())
             << " bytes):\n" << message.toText() << endl;
    }
326

327
    return (true);
328
329
}

330
331
#ifdef USE_XFROUT
bool
Han Feng's avatar
Han Feng committed
332
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
333
                            MessageRenderer& response_renderer) {
Han Feng's avatar
Han Feng committed
334
335
336
337
338
339
340
341
    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
        if (verbose_mode_) {
            cerr << "[b10-auth] user query axfr through udp which isn't allowed"
                 << endl;
        }
        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),verbose_mode_);
        return true;
    }
342
343

    try {
344
345
346
347
348
        if (!is_xfrin_connection_established_) {
            axfr_client_.connect();
            is_xfrin_connection_established_ = true;
        }
        axfr_client_.sendXfroutRequestInfo(io_message.getSocket().getNative(),
349
350
                                         io_message.getData(),
                                         io_message.getDataSize());
Han Feng's avatar
Han Feng committed
351
    } catch (const XfroutError& err) { 
352
353
354
355
356
        if (is_xfrin_connection_established_) {
            axfr_client_.disconnect();
            is_xfrin_connection_established_ = false;
        }
        
357
        if (verbose_mode_) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
358
            cerr << "[b10-auth] Error in handling XFR request: " << err.what()
359
360
                 << endl;
        }
Han Feng's avatar
Han Feng committed
361
        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),verbose_mode_);
362
    }
Han Feng's avatar
Han Feng committed
363
    return (true);
364
365
366
}
#else
bool
Han Feng's avatar
Han Feng committed
367
368
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message UNUSED_PARAM, Message& message UNUSED_PARAM, 
        MessageRenderer& response_renderer UNUSED_PARAM) const {
369
370
371
372
373
374
375
    // should better to return an error message, but hopefully this case
    // is short term workaround.
    return (false);
}
#endif

bool
Han Feng's avatar
Han Feng committed
376
377
AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message, 
                            MessageRenderer& response_renderer) 
378
379
380
{
    // TODO check with the conf-mgr whether current server is the auth of the
    // zone
381
    if (!is_xfrin_session_established_) {
Han Feng's avatar
Han Feng committed
382
383
        try {
            session_with_xfrin_.establish();
384
            is_xfrin_session_established_ = true;
Han Feng's avatar
Han Feng committed
385
386
387
388
389
        } catch ( isc::cc::SessionError &err) {
            if (verbose_mode_) {
            cerr << "[b10-auth] Error in connection with xfrin module: " << err.what()
                 << endl;
            }
390
            is_xfrin_session_established_ = false;
391
            return (false);
Han Feng's avatar
Han Feng committed
392
393
        }
    }
394

Han Feng's avatar
Han Feng committed
395
    ConstQuestionPtr question = *message.beginQuestion();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
396
397
    const string remote_ip_address =
        io_message.getRemoteEndpoint().getAddress().toText();
398
399
400
401
402
403
    static const string command_template_start = "{\"command\": [\"notify\", {\"zone_name\" : \"";
    static const string command_template_mid = "\", \"master_ip\" : \"";
    static const string command_template_end = "\"}]}";
    ElementPtr notify_command = Element::createFromString(command_template_start + question->getName().toText() + 
                                                         command_template_mid + remote_ip_address +
                                                         command_template_end);
Han Feng's avatar
Han Feng committed
404
405
406
407
408
409
410
411
412
413
414
415
    try {
        const unsigned int seq =
            session_with_xfrin_.group_sendmsg(notify_command, "Xfrin");
        ElementPtr env, answer;
        session_with_xfrin_.group_recvmsg(env, answer, false, seq);
        int rcode;
        parseAnswer(rcode, answer);
    } catch ( isc::cc::SessionError &err) {
        if (verbose_mode_) {
            cerr << "[b10-auth] Send message to xfrin module failed: " << err.what()
                << endl;
        }
416
        return (false);
Han Feng's avatar
Han Feng committed
417
    } catch ( CCSessionError &err) {
418
        if (verbose_mode_) {
Han Feng's avatar
Han Feng committed
419
            cerr << "[b10-auth] Receive wrong response from xfrin module: " << err.what() << endl;
420
        }
421
        return (false);
422
    }
Han Feng's avatar
Han Feng committed
423
424
425
426
427
    
    message.makeResponse();
    message.setRcode(Rcode::NOERROR());
    message.toWire(response_renderer);
    return (true);
428
429
}

430
ElementPtr
431
AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
432
433
434
435
    ElementPtr answer = isc::config::createAnswer();
    ElementPtr final;

    if (config && config->contains("database_file")) {
436
        db_file_ = config->get("database_file")->stringValue();
437
438
439
440
441
442
443
444
445
446
447
448
449
        final = config;
    } else if (cs_ != NULL) {
        bool is_default;
        string item("database_file");
        ElementPtr value = cs_->getValue(is_default, item);
        db_file_ = value->stringValue();
        final = Element::createFromString("{}");
        final->set(item, value);
    } else {
        return (answer);
    }

    if (verbose_mode_) {
450
        cerr << "[b10-auth] Data source database file: " << db_file_ << endl;
451
452
    }

453
454
455
456
457
458
    // create SQL data source
    // Note: the following step is tricky to be exception-safe and to ensure
    // exception guarantee: We first need to perform all operations that can
    // fail, while acquiring resources in the RAII manner.  We then perform
    // delete and swap operations which should not fail.
    DataSrcPtr datasrc_ptr(DataSrcPtr(new Sqlite3DataSrc));
459
    datasrc_ptr->init(final);
460
    data_sources_.addDataSrc(datasrc_ptr);
461

462
463
464
    // The following code should be exception free.
    if (cur_datasrc_ != NULL) {
        data_sources_.removeDataSrc(cur_datasrc_);
465
    }
466
467
    cur_datasrc_ = datasrc_ptr;

468
    return (answer);
469
470
}

471
ElementPtr
472
AuthSrv::updateConfig(isc::data::ElementPtr new_config) {
473
    try {
474
475
        // the ModuleCCSession has already checked if we have
        // the correct ElementPtr type as specified in our .spec file
476
        ElementPtr answer = isc::config::createAnswer();
477
        answer = impl_->setDbFile(new_config);
478

479
480
        return answer;
    } catch (const isc::Exception& error) {
481
        if (impl_->verbose_mode_) {
482
            cerr << "[b10-auth] error: " << error.what() << endl;
483
        }
484
485
        return isc::config::createAnswer(1, error.what());
    }
486
}