recursor.cc 14.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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$

#include <config.h>

#include <netinet/in.h>

#include <algorithm>
#include <iostream>
#include <vector>

#include <asiolink/asiolink.h>

27
28
#include <boost/foreach.hpp>

29
30
31
32
33
34
35
36
37
38
39
#include <config/ccsession.h>

#include <exceptions/exceptions.h>

#include <dns/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/question.h>
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
40
#include <dns/messagerenderer.h>
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

#include <recurse/recursor.h>

using namespace std;

using namespace isc;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
using namespace asiolink;

class RecursorImpl {
private:
    // prohibit copy
    RecursorImpl(const RecursorImpl& source);
    RecursorImpl& operator=(const RecursorImpl& source);
public:
58
59
    RecursorImpl(const char& forward) :
        config_session_(NULL), verbose_mode_(false),
60
        forward_(forward), rec_query_()
61
62
63
64
65
66
    {}

    ~RecursorImpl() {
        queryShutdown();
    }

67
68
    void querySetup(DNSService& dnss) {
        rec_query_ = new RecursiveQuery(dnss, forward_);
69
70
71
    }

    void queryShutdown() {
72
73
        if (rec_query_) {
            delete rec_query_;
74
75
76
        }
    }

77
    void processNormalQuery(const Question& question, MessagePtr message,
78
                            OutputBufferPtr buffer,
79
                            DNSServer* server);
80

Evan Hunt's avatar
Evan Hunt committed
81
82
    /// Currently non-configurable, but will be.
    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
83

Evan Hunt's avatar
Evan Hunt committed
84
85
    /// These members are public because Recursor accesses them directly.
    ModuleCCSession* config_session_;
86
87
    bool verbose_mode_;

Evan Hunt's avatar
Evan Hunt committed
88
private:
89
90
91
    /// Address of the forward nameserver
    const char& forward_;

92
    /// Object to handle upstream queries
93
    RecursiveQuery* rec_query_;
94
95
};

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
class QuestionInserter {
public:
    QuestionInserter(MessagePtr message) : message_(message) {}
    void operator()(const QuestionPtr question) {
        message_->addQuestion(question);
    }
    MessagePtr message_;
};

class SectionInserter {
public:
    SectionInserter(MessagePtr message, const Section& sect, bool sign) :
        message_(message), section_(sect), sign_(sign)
    {}
    void operator()(const RRsetPtr rrset) {
        message_->addRRset(section_, rrset, true);
    }
    MessagePtr message_;
    const Section& section_;
    bool sign_;
};

void
makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                 const Rcode& rcode, const bool verbose_mode)
{
    // 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();
    vector<QuestionPtr> questions;

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

    message->clear(Message::RENDER);
    message->setQid(qid);
    message->setOpcode(opcode);
    message->setHeaderFlag(MessageFlag::QR());
    message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
    if (rd) {
        message->setHeaderFlag(MessageFlag::RD());
    }
    if (cd) {
        message->setHeaderFlag(MessageFlag::CD());
    }
    for_each(questions.begin(), questions.end(), QuestionInserter(message));
    message->setRcode(rcode);
    MessageRenderer renderer(*buffer);
    message->toWire(renderer);

    if (verbose_mode) {
        cerr << "[b10-recurse] sending an error response (" <<
            renderer.getLength() << " bytes):\n" << message->toText() << endl;
    }
}
158

159
// This is a derived class of \c DNSLookup, to serve as a
160
161
// callback in the asiolink module.  It calls
// Recursor::processMessage() on a single DNS message.
162
class MessageLookup : public DNSLookup {
163
public:
164
    MessageLookup(Recursor* srv) : server_(srv) {}
165
166
167

    // \brief Handle the DNS Lookup
    virtual void operator()(const IOMessage& io_message, MessagePtr message,
168
                            OutputBufferPtr buffer, DNSServer* server) const
169
    {
170
        server_->processMessage(io_message, message, buffer, server);
171
172
173
174
175
176
177
178
179
180
181
182
183
    }
private:
    Recursor* server_;
};

// This is a derived class of \c DNSAnswer, to serve as a
// callback in the asiolink module.  It takes a completed
// set of answer data from the DNS lookup and assembles it
// into a wire-format response.
class MessageAnswer : public DNSAnswer {
public:
    MessageAnswer(Recursor* srv) : server_(srv) {}
    virtual void operator()(const IOMessage& io_message,
184
185
                            MessagePtr message,
                            OutputBufferPtr buffer) const
186
    {
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
        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();
        const Rcode& rcode = message->getRcode();
        vector<QuestionPtr> questions;
        questions.assign(message->beginQuestion(), message->endQuestion());

        message->clear(Message::RENDER);
        message->setQid(qid);
        message->setOpcode(opcode);
        message->setRcode(rcode);
        message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);

        message->setHeaderFlag(MessageFlag::QR());
        message->setHeaderFlag(MessageFlag::RA());
        if (rd) {
            message->setHeaderFlag(MessageFlag::RD());
        }
        if (cd) {
            message->setHeaderFlag(MessageFlag::CD());
        }


        // Copy the question section.
        for_each(questions.begin(), questions.end(), QuestionInserter(message));

        // If the buffer already has an answer in it, copy RRsets from
        // that into the new message, then clear the buffer and render
        // the new message into it.
        if (buffer->getLength() != 0) {
            try {
                Message incoming(Message::PARSE);
                InputBuffer ibuf(buffer->getData(), buffer->getLength());
                incoming.fromWire(ibuf);
                for_each(incoming.beginSection(Section::ANSWER()), 
                         incoming.endSection(Section::ANSWER()),
                         SectionInserter(message, Section::ANSWER(), true));
                for_each(incoming.beginSection(Section::ADDITIONAL()), 
                         incoming.endSection(Section::ADDITIONAL()),
                         SectionInserter(message, Section::ADDITIONAL(), true));
                for_each(incoming.beginSection(Section::AUTHORITY()), 
                         incoming.endSection(Section::AUTHORITY()),
                         SectionInserter(message, Section::AUTHORITY(), true));
            } catch (const Exception& ex) {
                // Incoming message couldn't be read, we just SERVFAIL
                message->setRcode(Rcode::SERVFAIL());
            }

        }

        // Now we can clear the buffer and render the new message into it
        buffer->clear();
        MessageRenderer renderer(*buffer);

242
        if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
243
            renderer.setLengthLimit(message->getUDPSize());
244
245
246
        } else {
            renderer.setLengthLimit(65535);
        }
247
248
249

        message->toWire(renderer);

250
        if (server_->getVerbose()) {
251
252
253
            cerr << "[b10-recurse] sending a response ("
                 << renderer.getLength() << " bytes):\n"
                 << message->toText() << endl;
254
        }
255
    }
256

257
258
259
260
private:
    Recursor* server_;
};

261
// This is a derived class of \c SimpleCallback, to serve
262
263
// as a callback in the asiolink module.  It checks for queued
// configuration messages, and executes them if found.
264
class ConfigCheck : public SimpleCallback {
265
public:
266
    ConfigCheck(Recursor* srv) : server_(srv) {}
267
    virtual void operator()(const IOMessage& io_message UNUSED_PARAM) const {
268
269
        if (server_->getConfigSession()->hasQueuedMsgs()) {
            server_->getConfigSession()->checkCommand();
270
271
272
273
274
275
        }
    }
private:
    Recursor* server_;
};

276
277
278
Recursor::Recursor(const char& forward) :
    impl_(new RecursorImpl(forward)),
    checkin_(new ConfigCheck(this)),
279
280
    dns_lookup_(new MessageLookup(this)),
    dns_answer_(new MessageAnswer(this))
281
282
283
284
{}

Recursor::~Recursor() {
    delete impl_;
285
286
287
    delete checkin_;
    delete dns_lookup_;
    delete dns_answer_;
288
289
290
}

void
291
Recursor::setDNSService(asiolink::DNSService& dnss) {
292
    impl_->queryShutdown();
293
294
    impl_->querySetup(dnss);
    dnss_ = &dnss;
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
}

void
Recursor::setVerbose(const bool on) {
    impl_->verbose_mode_ = on;
}

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

void
Recursor::setConfigSession(ModuleCCSession* config_session) {
    impl_->config_session_ = config_session;
}

ModuleCCSession*
313
Recursor::getConfigSession() const {
314
315
316
    return (impl_->config_session_);
}

317
void
318
Recursor::processMessage(const IOMessage& io_message, MessagePtr message,
319
                        OutputBufferPtr buffer, DNSServer* server)
320
321
322
323
324
{
    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
    // First, check the header part.  If we fail even for the base header,
    // just drop the message.
    try {
325
        message->parseHeader(request_buffer);
326
327

        // Ignore all responses.
328
        if (message->getHeaderFlag(MessageFlag::QR())) {
329
330
331
332
            if (impl_->verbose_mode_) {
                cerr << "[b10-recurse] received unexpected response, ignoring"
                     << endl;
            }
333
            server->resume(false);
334
            return;
335
336
        }
    } catch (const Exception& ex) {
337
338
339
        if (impl_->verbose_mode_) {
            cerr << "[b10-recurse] DNS packet exception: " << ex.what() << endl;
        }
340
        server->resume(false);
341
        return;
342
343
344
345
    }

    // Parse the message.  On failure, return an appropriate error.
    try {
346
        message->fromWire(request_buffer);
347
348
349
350
351
    } catch (const DNSProtocolError& error) {
        if (impl_->verbose_mode_) {
            cerr << "[b10-recurse] returning " <<  error.getRcode().toText()
                 << ": " << error.what() << endl;
        }
352
        makeErrorMessage(message, buffer, error.getRcode(),
353
                         impl_->verbose_mode_);
354
        server->resume(true);
355
        return;
356
357
358
359
    } catch (const Exception& ex) {
        if (impl_->verbose_mode_) {
            cerr << "[b10-recurse] returning SERVFAIL: " << ex.what() << endl;
        }
360
        makeErrorMessage(message, buffer, Rcode::SERVFAIL(),
361
                         impl_->verbose_mode_);
362
        server->resume(true);
363
        return;
364
365
366
367
    } // other exceptions will be handled at a higher layer.

    if (impl_->verbose_mode_) {
        cerr << "[b10-recurse] received a message:\n"
368
             << message->toText() << endl;
369
370
371
    }

    // Perform further protocol-level validation.
372
373
374
    bool sendAnswer = true;
    if (message->getOpcode() == Opcode::NOTIFY()) {
        makeErrorMessage(message, buffer, Rcode::NOTAUTH(),
375
                         impl_->verbose_mode_);
376
    } else if (message->getOpcode() != Opcode::QUERY()) {
377
378
379
        if (impl_->verbose_mode_) {
            cerr << "[b10-recurse] unsupported opcode" << endl;
        }
380
        makeErrorMessage(message, buffer, Rcode::NOTIMP(),
381
                         impl_->verbose_mode_);
382
383
    } else if (message->getRRCount(Section::QUESTION()) != 1) {
        makeErrorMessage(message, buffer, Rcode::FORMERR(),
384
                         impl_->verbose_mode_);
385
    } else {
386
        ConstQuestionPtr question = *message->beginQuestion();
387
388
389
        const RRType &qtype = question->getType();
        if (qtype == RRType::AXFR()) {
            if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
390
                makeErrorMessage(message, buffer, Rcode::FORMERR(),
391
392
                                 impl_->verbose_mode_);
            } else {
393
                makeErrorMessage(message, buffer, Rcode::NOTIMP(),
394
395
396
                                 impl_->verbose_mode_);
            }
        } else if (qtype == RRType::IXFR()) {
397
            makeErrorMessage(message, buffer, Rcode::NOTIMP(),
398
                         impl_->verbose_mode_);
399
        } else {
400
401
            // The RecursiveQuery object will post the "resume" event to the
            // DNSServer when an answer arrives, so we don't have to do it now.
402
            sendAnswer = false;
403
            impl_->processNormalQuery(*question, message, buffer, server);
404
405
        }
    }
406

407
408
409
    if (sendAnswer) {
        server->resume(true);
    }
410
411
}

412
void
413
RecursorImpl::processNormalQuery(const Question& question, MessagePtr message,
414
                                 OutputBufferPtr buffer, DNSServer* server)
415
{
416
417
418
419
420
421
422
    const bool dnssec_ok = message->isDNSSECSupported();

    message->makeResponse();
    message->setHeaderFlag(MessageFlag::RA());
    message->setRcode(Rcode::NOERROR());
    message->setDNSSECSupported(dnssec_ok);
    message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
423
    rec_query_->sendQuery(question, buffer, server);
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
}

ConstElementPtr
Recursor::updateConfig(ConstElementPtr new_config UNUSED_PARAM) {
    try {
        // We will do configuration updates here.  None are presently
        // defined, so we just return an empty answer.
        return (isc::config::createAnswer());
    } catch (const isc::Exception& error) {
        if (impl_->verbose_mode_) {
            cerr << "[b10-recurse] error: " << error.what() << endl;
        }
        return (isc::config::createAnswer(1, error.what()));
    }
}