Branch data Line data Source code
1 : : // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <sys/types.h>
18 : : #include <netinet/in.h>
19 : :
20 : : #include <algorithm>
21 : : #include <cassert>
22 : : #include <iostream>
23 : : #include <vector>
24 : : #include <memory>
25 : :
26 : : #include <boost/bind.hpp>
27 : :
28 : : #include <asiolink/asiolink.h>
29 : :
30 : : #include <config/ccsession.h>
31 : :
32 : : #include <cc/data.h>
33 : :
34 : : #include <exceptions/exceptions.h>
35 : :
36 : : #include <util/buffer.h>
37 : :
38 : : #include <dns/edns.h>
39 : : #include <dns/exceptions.h>
40 : : #include <dns/messagerenderer.h>
41 : : #include <dns/name.h>
42 : : #include <dns/question.h>
43 : : #include <dns/opcode.h>
44 : : #include <dns/rcode.h>
45 : : #include <dns/rrset.h>
46 : : #include <dns/rrttl.h>
47 : : #include <dns/message.h>
48 : : #include <dns/tsig.h>
49 : :
50 : : #include <asiodns/dns_service.h>
51 : :
52 : : #include <datasrc/query.h>
53 : : #include <datasrc/data_source.h>
54 : : #include <datasrc/memory_datasrc.h>
55 : : #include <datasrc/static_datasrc.h>
56 : : #include <datasrc/sqlite3_datasrc.h>
57 : :
58 : : #include <xfr/xfrout_client.h>
59 : :
60 : : #include <auth/common.h>
61 : : #include <auth/auth_config.h>
62 : : #include <auth/auth_srv.h>
63 : : #include <auth/query.h>
64 : : #include <auth/statistics.h>
65 : : #include <auth/auth_log.h>
66 : :
67 : : using namespace std;
68 : :
69 : : using namespace isc;
70 : : using namespace isc::cc;
71 : : using namespace isc::datasrc;
72 : : using namespace isc::dns;
73 : : using namespace isc::util;
74 : : using namespace isc::auth;
75 : : using namespace isc::dns::rdata;
76 : : using namespace isc::data;
77 : : using namespace isc::config;
78 : : using namespace isc::xfr;
79 : : using namespace isc::asiolink;
80 : : using namespace isc::asiodns;
81 : : using namespace isc::server_common::portconfig;
82 : :
83 : : namespace {
84 : : // A helper class for cleaning up message renderer.
85 : : //
86 : : // A temporary object of this class is expected to be created before starting
87 : : // response message rendering. On construction, it (re)initialize the given
88 : : // message renderer with the given buffer. On destruction, it releases
89 : : // the previously set buffer and then release any internal resource in the
90 : : // renderer, no matter what happened during the rendering, especially even
91 : : // when it resulted in an exception.
92 : : //
93 : : // Note: if we need this helper in many other places we might consider making
94 : : // it visible to other modules. As of this implementation this is the only
95 : : // user of this class, so we hide it within the implementation.
96 : : class RendererHolder {
97 : : public:
98 : : RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer) :
99 : 200 : renderer_(renderer)
100 : : {
101 [ + - ][ + - ]: 200 : renderer.setBuffer(buffer);
[ + - ]
102 : : }
103 : 200 : ~RendererHolder() {
104 : 200 : renderer_.setBuffer(NULL);
105 : 200 : renderer_.clear();
106 : 200 : }
107 : : private:
108 : : MessageRenderer& renderer_;
109 : : };
110 : : }
111 : :
112 : : class AuthSrvImpl {
113 : : private:
114 : : // prohibit copy
115 : : AuthSrvImpl(const AuthSrvImpl& source);
116 : : AuthSrvImpl& operator=(const AuthSrvImpl& source);
117 : : public:
118 : : AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client);
119 : : ~AuthSrvImpl();
120 : : isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
121 : :
122 : : bool processNormalQuery(const IOMessage& io_message, Message& message,
123 : : OutputBuffer& buffer,
124 : : auto_ptr<TSIGContext> tsig_context);
125 : : bool processXfrQuery(const IOMessage& io_message, Message& message,
126 : : OutputBuffer& buffer,
127 : : auto_ptr<TSIGContext> tsig_context);
128 : : bool processNotify(const IOMessage& io_message, Message& message,
129 : : OutputBuffer& buffer,
130 : : auto_ptr<TSIGContext> tsig_context);
131 : :
132 : : IOService io_service_;
133 : :
134 : : MessageRenderer renderer_;
135 : : /// Currently non-configurable, but will be.
136 : : static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
137 : :
138 : : /// These members are public because AuthSrv accesses them directly.
139 : : ModuleCCSession* config_session_;
140 : : AbstractSession* xfrin_session_;
141 : :
142 : : /// In-memory data source. Currently class IN only for simplicity.
143 : : const RRClass memory_client_class_;
144 : : AuthSrv::InMemoryClientPtr memory_client_;
145 : :
146 : : /// Hot spot cache
147 : : isc::datasrc::HotCache cache_;
148 : :
149 : : /// Interval timer for periodic submission of statistics counters.
150 : : IntervalTimer statistics_timer_;
151 : :
152 : : /// Query counters for statistics
153 : : AuthCounters counters_;
154 : :
155 : : /// Addresses we listen on
156 : : AddressList listen_addresses_;
157 : :
158 : : /// The TSIG keyring
159 : : const boost::shared_ptr<TSIGKeyRing>* keyring_;
160 : :
161 : : /// Bind the ModuleSpec object in config_session_ with
162 : : /// isc:config::ModuleSpec::validateStatistics.
163 : : void registerStatisticsValidator();
164 : :
165 : : /// \brief Resume the server
166 : : ///
167 : : /// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
168 : : /// the Rcode set in the given Message is counted in the statistics
169 : : /// counter.
170 : : ///
171 : : /// This method is expected to be called by processMessage()
172 : : ///
173 : : /// \param server The DNSServer as passed to processMessage()
174 : : /// \param message The response as constructed by processMessage()
175 : : /// \param done If true, the Rcode from the given message is counted,
176 : : /// this value is then passed to server->resume(bool)
177 : : void resumeServer(isc::asiodns::DNSServer* server,
178 : : isc::dns::Message& message,
179 : : bool done);
180 : : private:
181 : : std::string db_file_;
182 : :
183 : : MetaDataSrc data_sources_;
184 : : /// We keep a pointer to the currently running sqlite datasource
185 : : /// so that we can specifically remove that one should the database
186 : : /// file change
187 : : ConstDataSrcPtr cur_datasrc_;
188 : :
189 : : bool xfrout_connected_;
190 : : AbstractXfroutClient& xfrout_client_;
191 : :
192 : : /// Increment query counter
193 : : void incCounter(const int protocol);
194 : :
195 : : // validateStatistics
196 : : bool validateStatistics(isc::data::ConstElementPtr data) const;
197 : :
198 : : auth::Query query_;
199 : : };
200 : :
201 : 94 : AuthSrvImpl::AuthSrvImpl(const bool use_cache,
202 : : AbstractXfroutClient& xfrout_client) :
203 : : config_session_(NULL),
204 : : xfrin_session_(NULL),
205 : : memory_client_class_(RRClass::IN()),
206 : : statistics_timer_(io_service_),
207 : : counters_(),
208 : : keyring_(NULL),
209 : : xfrout_connected_(false),
210 [ + - ][ + - ]: 188 : xfrout_client_(xfrout_client)
[ + - ][ + - ]
211 : : {
212 : : // cur_datasrc_ is automatically initialized by the default constructor,
213 : : // effectively being an empty (sqlite) data source. once ccsession is up
214 : : // the datasource will be set by the configuration setting
215 : :
216 : : // add static data source
217 [ + - ][ + - ]: 188 : data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
[ + - ]
218 : :
219 : : // enable or disable the cache
220 [ + - ]: 94 : cache_.setEnabled(use_cache);
221 : 94 : }
222 : :
223 [ + - ][ + - ]: 282 : AuthSrvImpl::~AuthSrvImpl() {
[ + - ][ + - ]
[ + - ][ + - ]
224 [ + + ]: 94 : if (xfrout_connected_) {
225 [ + - ]: 5 : xfrout_client_.disconnect();
226 : 5 : xfrout_connected_ = false;
227 : : }
228 : 94 : }
229 : :
230 : : // This is a derived class of \c DNSLookup, to serve as a
231 : : // callback in the asiolink module. It calls
232 : : // AuthSrv::processMessage() on a single DNS message.
233 : 94 : class MessageLookup : public DNSLookup {
234 : : public:
235 : 188 : MessageLookup(AuthSrv* srv) : server_(srv) {}
236 : 2 : virtual void operator()(const IOMessage& io_message,
237 : : MessagePtr message,
238 : : MessagePtr, // Not used here
239 : : OutputBufferPtr buffer,
240 : : DNSServer* server) const
241 : : {
242 : 2 : server_->processMessage(io_message, *message, *buffer, server);
243 : 2 : }
244 : : private:
245 : : AuthSrv* server_;
246 : : };
247 : :
248 : : // This is a derived class of \c DNSAnswer, to serve as a callback in the
249 : : // asiolink module. We actually shouldn't do anything in this class because
250 : : // we build complete response messages in the process methods; otherwise
251 : : // the response message will contain trailing garbage. In future, we should
252 : : // probably even drop the reliance on DNSAnswer. We don't need the coroutine
253 : : // tricks provided in that framework, and its overhead would be significant
254 : : // in terms of performance consideration for the authoritative server
255 : : // implementation.
256 : 94 : class MessageAnswer : public DNSAnswer {
257 : : public:
258 : 0 : MessageAnswer(AuthSrv*) {}
259 : 2 : virtual void operator()(const IOMessage&, MessagePtr,
260 : : MessagePtr, OutputBufferPtr) const
261 : 2 : {}
262 : : };
263 : :
264 : : // This is a derived class of \c SimpleCallback, to serve
265 : : // as a callback in the asiolink module. It checks for queued
266 : : // configuration messages, and executes them if found.
267 : 94 : class ConfigChecker : public SimpleCallback {
268 : : public:
269 : 188 : ConfigChecker(AuthSrv* srv) : server_(srv) {}
270 : 0 : virtual void operator()(const IOMessage&) const {
271 : 0 : ModuleCCSession* cfg_session = server_->getConfigSession();
272 [ # # ][ # # ]: 0 : if (cfg_session != NULL && cfg_session->hasQueuedMsgs()) {
[ # # ]
273 : 0 : cfg_session->checkCommand();
274 : : }
275 : 0 : }
276 : : private:
277 : : AuthSrv* server_;
278 : : };
279 : :
280 : 94 : AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client)
281 : : {
282 [ + - ]: 94 : impl_ = new AuthSrvImpl(use_cache, xfrout_client);
283 : 94 : checkin_ = new ConfigChecker(this);
284 : 94 : dns_lookup_ = new MessageLookup(this);
285 : 94 : dns_answer_ = new MessageAnswer(this);
286 : 94 : }
287 : :
288 : : void
289 : 4 : AuthSrv::stop() {
290 : 4 : impl_->io_service_.stop();
291 : 4 : }
292 : :
293 : 94 : AuthSrv::~AuthSrv() {
294 [ + - ]: 94 : delete impl_;
295 [ + - ]: 94 : delete checkin_;
296 [ + - ]: 94 : delete dns_lookup_;
297 [ + - ]: 94 : delete dns_answer_;
298 : 94 : }
299 : :
300 : : namespace {
301 : : class QuestionInserter {
302 : : public:
303 : 178 : QuestionInserter(Message& message) : message_(message) {}
304 : : void operator()(const QuestionPtr question) {
305 [ + - ]: 32 : message_.addQuestion(question);
306 : : }
307 : : Message& message_;
308 : : };
309 : :
310 : : void
311 : 178 : makeErrorMessage(MessageRenderer& renderer, Message& message,
312 : : OutputBuffer& buffer, const Rcode& rcode,
313 : : std::auto_ptr<TSIGContext> tsig_context =
314 : : std::auto_ptr<TSIGContext>())
315 : : {
316 : : // extract the parameters that should be kept.
317 : : // XXX: with the current implementation, it's not easy to set EDNS0
318 : : // depending on whether the query had it. So we'll simply omit it.
319 : 178 : const qid_t qid = message.getQid();
320 : 178 : const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
321 : 178 : const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
322 : 178 : const Opcode& opcode = message.getOpcode();
323 : 178 : vector<QuestionPtr> questions;
324 : :
325 : : // If this is an error to a query or notify, we should also copy the
326 : : // question section.
327 [ + - ][ + + ]: 178 : if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
[ + - ][ + + ]
[ + + ]
328 [ + - ][ + - ]: 32 : questions.assign(message.beginQuestion(), message.endQuestion());
[ + - ][ + - ]
[ + - ]
329 : : }
330 : :
331 [ + - ]: 178 : message.clear(Message::RENDER);
332 [ + - ]: 178 : message.setQid(qid);
333 [ + - ]: 178 : message.setOpcode(opcode);
334 [ + - ]: 178 : message.setHeaderFlag(Message::HEADERFLAG_QR);
335 [ - + ]: 178 : if (rd) {
336 [ # # ]: 0 : message.setHeaderFlag(Message::HEADERFLAG_RD);
337 : : }
338 [ - + ]: 178 : if (cd) {
339 [ # # ]: 0 : message.setHeaderFlag(Message::HEADERFLAG_CD);
340 : : }
341 [ + - ]: 178 : for_each(questions.begin(), questions.end(), QuestionInserter(message));
342 : :
343 [ + - ]: 178 : message.setRcode(rcode);
344 : :
345 [ + - ]: 178 : RendererHolder holder(renderer, &buffer);
346 [ + + ]: 178 : if (tsig_context.get() != NULL) {
347 [ + - ]: 3 : message.toWire(renderer, *tsig_context);
348 : : } else {
349 [ + - ]: 175 : message.toWire(renderer);
350 : : }
351 [ + - ][ + - ]: 356 : LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
[ + - ]
352 [ + - ][ + - ]: 178 : .arg(renderer.getLength()).arg(message);
[ + - ]
353 : 178 : }
354 : : }
355 : :
356 : : IOService&
357 : 17 : AuthSrv::getIOService() {
358 : 17 : return (impl_->io_service_);
359 : : }
360 : :
361 : : void
362 : 2 : AuthSrv::setCacheSlots(const size_t slots) {
363 : 2 : impl_->cache_.setSlots(slots);
364 : 2 : }
365 : :
366 : : size_t
367 : 2 : AuthSrv::getCacheSlots() const {
368 : 2 : return (impl_->cache_.getSlots());
369 : : }
370 : :
371 : : void
372 : 57 : AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
373 : 57 : impl_->xfrin_session_ = xfrin_session;
374 : 57 : }
375 : :
376 : : void
377 : 1 : AuthSrv::setConfigSession(ModuleCCSession* config_session) {
378 : 1 : impl_->config_session_ = config_session;
379 : 1 : impl_->registerStatisticsValidator();
380 : 1 : }
381 : :
382 : : void
383 : 69 : AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
384 : 69 : impl_->counters_.setStatisticsSession(statistics_session);
385 : 69 : }
386 : :
387 : : ModuleCCSession*
388 : 11 : AuthSrv::getConfigSession() const {
389 : 11 : return (impl_->config_session_);
390 : : }
391 : :
392 : : AuthSrv::InMemoryClientPtr
393 : 148 : AuthSrv::getInMemoryClient(const RRClass& rrclass) {
394 : : // XXX: for simplicity, we only support the IN class right now.
395 [ + + ]: 148 : if (rrclass != impl_->memory_client_class_) {
396 [ + - ][ + - ]: 4 : isc_throw(InvalidParameter,
397 : : "Memory data source is not supported for RR class "
398 : : << rrclass);
399 : : }
400 : 146 : return (impl_->memory_client_);
401 : : }
402 : :
403 : : void
404 : 74 : AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass,
405 : : InMemoryClientPtr memory_client)
406 : : {
407 : : // XXX: see above
408 [ - + ]: 74 : if (rrclass != impl_->memory_client_class_) {
409 [ # # ][ # # ]: 0 : isc_throw(InvalidParameter,
410 : : "Memory data source is not supported for RR class "
411 : : << rrclass);
412 [ + + ][ + + ]: 128 : } else if (!impl_->memory_client_ && memory_client) {
[ + + ]
413 [ + - ]: 62 : LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
414 [ + - ]: 31 : .arg(rrclass);
415 [ + + ][ + + ]: 43 : } else if (impl_->memory_client_ && !memory_client) {
[ + + ]
416 [ + - ]: 18 : LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
417 [ + - ]: 9 : .arg(rrclass);
418 : : }
419 : 74 : impl_->memory_client_ = memory_client;
420 : 74 : }
421 : :
422 : : uint32_t
423 : 4 : AuthSrv::getStatisticsTimerInterval() const {
424 : 4 : return (impl_->statistics_timer_.getInterval() / 1000);
425 : : }
426 : :
427 : : void
428 : 3 : AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
429 [ + - ]: 3 : if (interval == impl_->statistics_timer_.getInterval()) {
430 : 3 : return;
431 : : }
432 [ - + ]: 3 : if (interval > 86400) {
433 : : // It can't occur since the value is checked in
434 : : // statisticsIntervalConfig::build().
435 [ # # ]: 0 : isc_throw(InvalidParameter, "Too long interval: " << interval);
436 : : }
437 [ + + ]: 3 : if (interval == 0) {
438 : 1 : impl_->statistics_timer_.cancel();
439 [ + - ]: 1 : LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_DISABLED);
440 : : } else {
441 : : impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
442 : 2 : this),
443 [ + - ]: 2 : interval * 1000);
444 [ + - ]: 4 : LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_SET)
445 [ + - ]: 2 : .arg(interval);
446 : : }
447 : : }
448 : :
449 : : void
450 : 212 : AuthSrv::processMessage(const IOMessage& io_message, Message& message,
451 : : OutputBuffer& buffer, DNSServer* server)
452 : : {
453 : 212 : InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
454 : :
455 : : // First, check the header part. If we fail even for the base header,
456 : : // just drop the message.
457 : : try {
458 [ + + ]: 212 : message.parseHeader(request_buffer);
459 : :
460 : : // Ignore all responses.
461 [ + - ][ + + ]: 211 : if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
462 [ + - ][ + - ]: 3 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
[ + - ][ + - ]
463 [ + - ]: 3 : impl_->resumeServer(server, message, false);
464 : : return;
465 : : }
466 [ - + ]: 2 : } catch (const Exception& ex) {
467 [ - + ][ + - ]: 2 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
[ - + ]
468 [ - + ][ - + ]: 1 : .arg(ex.what());
469 [ - + ]: 1 : impl_->resumeServer(server, message, false);
470 : : return;
471 : : }
472 : :
473 : : try {
474 : : // Parse the message.
475 [ + + ]: 208 : message.fromWire(request_buffer);
476 : 6 : } catch (const DNSProtocolError& error) {
477 [ - + ][ + - ]: 6 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
[ - + ]
478 [ - + ][ - + ]: 6 : .arg(error.getRcode().toText()).arg(error.what());
[ - + ][ - + ]
[ - + ]
479 [ - + ][ - + ]: 3 : makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
[ - + ][ - + ]
480 [ - + ]: 3 : impl_->resumeServer(server, message, true);
481 : : return;
482 [ - + - ]: 3 : } catch (const Exception& ex) {
483 [ # # ][ # # ]: 0 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
[ # # ]
484 [ # # ][ # # ]: 0 : .arg(ex.what());
485 [ # # ][ # # ]: 0 : makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
[ # # ][ # # ]
486 [ # # ]: 0 : impl_->resumeServer(server, message, true);
487 : : return;
488 : : } // other exceptions will be handled at a higher layer.
489 : :
490 [ + - ]: 410 : LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
491 [ + - ]: 205 : .arg(message);
492 : :
493 : : // Perform further protocol-level validation.
494 : : // TSIG first
495 : : // If this is set to something, we know we need to answer with TSIG as well
496 : 212 : std::auto_ptr<TSIGContext> tsig_context;
497 [ + - ]: 205 : const TSIGRecord* tsig_record(message.getTSIGRecord());
498 [ + - ]: 205 : TSIGError tsig_error(TSIGError::NOERROR());
499 : :
500 : : // Do we do TSIG?
501 : : // The keyring can be null if we're in test
502 [ + + ]: 205 : if (impl_->keyring_ != NULL && tsig_record != NULL) {
503 : : tsig_context.reset(new TSIGContext(tsig_record->getName(),
504 : 4 : tsig_record->getRdata().
505 : : getAlgorithm(),
506 [ + - ][ + - ]: 8 : **impl_->keyring_));
[ + - ]
507 : : tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
508 [ + - ]: 4 : io_message.getDataSize());
509 : : }
510 : :
511 [ + - ][ + + ]: 205 : if (tsig_error != TSIGError::NOERROR()) {
512 : : makeErrorMessage(impl_->renderer_, message, buffer,
513 [ + - ][ + - ]: 3 : tsig_error.toRcode(), tsig_context);
[ + - ]
514 [ + - ]: 3 : impl_->resumeServer(server, message, true);
515 : : return;
516 : : }
517 : :
518 : 202 : bool send_answer = true;
519 : : try {
520 : : // update per opcode statistics counter. This can only be reliable
521 : : // after TSIG check succeeds.
522 [ + - ][ + - ]: 202 : impl_->counters_.inc(message.getOpcode());
523 : :
524 [ + - ][ + - ]: 202 : if (message.getOpcode() == Opcode::NOTIFY()) {
[ + + ]
525 : : send_answer = impl_->processNotify(io_message, message, buffer,
526 [ + - ][ + - ]: 17 : tsig_context);
527 [ + - ][ + - ]: 185 : } else if (message.getOpcode() != Opcode::QUERY()) {
[ + + ]
528 [ + - ][ + - ]: 290 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
[ + - ]
529 [ + - ][ + - ]: 290 : .arg(message.getOpcode().toText());
[ + - ][ + - ]
530 : : makeErrorMessage(impl_->renderer_, message, buffer,
531 [ + - ][ + - ]: 145 : Rcode::NOTIMP(), tsig_context);
532 [ + - ][ + + ]: 40 : } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
533 : : makeErrorMessage(impl_->renderer_, message, buffer,
534 [ + - ][ + - ]: 1 : Rcode::FORMERR(), tsig_context);
[ + - ]
535 : : } else {
536 [ + - ][ + - ]: 78 : ConstQuestionPtr question = *message.beginQuestion();
[ + - ]
537 : 39 : const RRType &qtype = question->getType();
538 [ + + ]: 39 : if (qtype == RRType::AXFR()) {
539 : : send_answer = impl_->processXfrQuery(io_message, message,
540 [ + + ][ + - ]: 7 : buffer, tsig_context);
541 [ + + ]: 32 : } else if (qtype == RRType::IXFR()) {
542 : : send_answer = impl_->processXfrQuery(io_message, message,
543 [ + + ][ + - ]: 5 : buffer, tsig_context);
544 : : } else {
545 : : send_answer = impl_->processNormalQuery(io_message, message,
546 [ + + ][ + - ]: 27 : buffer, tsig_context);
547 : : }
548 : : }
549 : 16 : } catch (const std::exception& ex) {
550 [ - + ][ + - ]: 16 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
[ - + ]
551 [ - + ][ - + ]: 8 : .arg(ex.what());
552 [ - + ][ - + ]: 8 : makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
[ - + ][ - + ]
553 [ + - ][ # # ]: 8 : } catch (...) {
554 [ # # ][ # # ]: 0 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
[ # # ][ # # ]
555 [ # # ][ # # ]: 0 : makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
[ # # ][ # # ]
556 : : }
557 [ + - ]: 202 : impl_->resumeServer(server, message, send_answer);
558 : : }
559 : :
560 : : bool
561 : 27 : AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
562 : : OutputBuffer& buffer,
563 : : auto_ptr<TSIGContext> tsig_context)
564 : : {
565 : 27 : ConstEDNSPtr remote_edns = message.getEDNS();
566 [ + + ][ - + ]: 38 : const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
567 : 11 : const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
568 [ + + ]: 27 : Message::DEFAULT_MAX_UDPSIZE;
569 : :
570 [ + - ]: 27 : message.makeResponse();
571 [ + - ]: 27 : message.setHeaderFlag(Message::HEADERFLAG_AA);
572 [ + - ][ + - ]: 27 : message.setRcode(Rcode::NOERROR());
573 : :
574 : : // Increment query counter.
575 [ + - ][ + - ]: 27 : incCounter(io_message.getSocket().getProtocol());
576 : :
577 [ + + ]: 27 : if (remote_edns) {
578 [ + - ][ + - ]: 11 : EDNSPtr local_edns = EDNSPtr(new EDNS());
579 : 22 : local_edns->setDNSSECAwareness(dnssec_ok);
580 : 22 : local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
581 [ + - ]: 11 : message.setEDNS(local_edns);
582 : : }
583 : :
584 : : try {
585 : : // If a memory data source is configured call the separate
586 : : // Query::process()
587 [ + - ][ + - ]: 54 : const ConstQuestionPtr question = *message.beginQuestion();
[ + - ]
588 [ + + ][ + + ]: 27 : if (memory_client_ && memory_client_class_ == question->getClass()) {
[ + + ]
589 : 15 : const RRType& qtype = question->getType();
590 : 15 : const Name& qname = question->getName();
591 [ + + ]: 15 : query_.process(*memory_client_, qname, qtype, message, dnssec_ok);
592 : : } else {
593 [ + - ][ + - ]: 24 : datasrc::Query query(message, cache_, dnssec_ok);
594 [ + + ]: 12 : data_sources_.doQuery(query);
595 : : }
596 [ + + ]: 14 : } catch (const Exception& ex) {
597 [ - + ][ + - ]: 5 : LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
[ - + ][ - + ]
[ - + ]
598 [ - + ][ - + ]: 5 : makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
[ - + ][ - + ]
599 : : return (true);
600 : : }
601 : :
602 [ + - ]: 18 : RendererHolder holder(renderer_, &buffer);
603 : : const bool udp_buffer =
604 [ + - ]: 18 : (io_message.getSocket().getProtocol() == IPPROTO_UDP);
605 [ + + ][ + - ]: 18 : renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
606 [ + + ]: 18 : if (tsig_context.get() != NULL) {
607 [ + - ]: 1 : message.toWire(renderer_, *tsig_context);
608 : : } else {
609 [ + + ]: 17 : message.toWire(renderer_);
610 : : }
611 [ + - ][ + - ]: 32 : LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
[ + - ]
612 [ + - ][ + - ]: 16 : .arg(renderer_.getLength()).arg(message);
[ + - ]
613 : : return (true);
614 : : }
615 : :
616 : : bool
617 : 12 : AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
618 : : OutputBuffer& buffer,
619 : : auto_ptr<TSIGContext> tsig_context)
620 : : {
621 : : // Increment query counter.
622 : 12 : incCounter(io_message.getSocket().getProtocol());
623 : :
624 [ + + ]: 12 : if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
625 [ + - ]: 1 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
626 [ + - ]: 1 : makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
627 [ + - ]: 1 : tsig_context);
628 : 1 : return (true);
629 : : }
630 : :
631 : : try {
632 [ + + ]: 11 : if (!xfrout_connected_) {
633 [ + + ]: 9 : xfrout_client_.connect();
634 : 7 : xfrout_connected_ = true;
635 : : }
636 : : xfrout_client_.sendXfroutRequestInfo(
637 : 9 : io_message.getSocket().getNative(),
638 : : io_message.getData(),
639 [ + - ][ + + ]: 14 : io_message.getDataSize());
640 [ - + ]: 12 : } catch (const XfroutError& err) {
641 [ + + ]: 6 : if (xfrout_connected_) {
642 : : // disconnect() may trigger an exception, but since we try it
643 : : // only if we've successfully opened it, it shouldn't happen in
644 : : // normal condition. Should this occur, we'll propagate it to the
645 : : // upper layer.
646 [ + + ]: 4 : xfrout_client_.disconnect();
647 : 2 : xfrout_connected_ = false;
648 : : }
649 : :
650 [ - + ][ + - ]: 8 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
[ - + ]
651 [ - + ][ - + ]: 4 : .arg(err.what());
652 [ - + ]: 4 : makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
653 [ - + ][ - + ]: 4 : tsig_context);
654 : : return (true);
655 : : }
656 : :
657 : : return (false);
658 : : }
659 : :
660 : : bool
661 : 17 : AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
662 : : OutputBuffer& buffer,
663 : : std::auto_ptr<TSIGContext> tsig_context)
664 : : {
665 : : // The incoming notify must contain exactly one question for SOA of the
666 : : // zone name.
667 [ + + ]: 17 : if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
668 [ + - ]: 4 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
669 [ + - ]: 2 : .arg(message.getRRCount(Message::SECTION_QUESTION));
670 [ + - ]: 2 : makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
671 [ + - ]: 2 : tsig_context);
672 : 2 : return (true);
673 : : }
674 [ + - ][ + - ]: 30 : ConstQuestionPtr question = *message.beginQuestion();
675 [ + + ]: 15 : if (question->getType() != RRType::SOA()) {
676 [ + - ][ + - ]: 12 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
[ + - ]
677 [ + - ][ + - ]: 12 : .arg(question->getType().toText());
[ + - ]
678 [ + - ]: 6 : makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
679 [ + - ][ + - ]: 6 : tsig_context);
680 : : return (true);
681 : : }
682 : :
683 : : // According to RFC 1996, rcode should be "no error" and AA bit should be
684 : : // on, but we don't check these conditions. This behavior is compatible
685 : : // with BIND 9.
686 : :
687 : : // TODO check with the conf-mgr whether current server is the auth of the
688 : : // zone
689 : :
690 : : // In the code that follows, we simply ignore the notify if any internal
691 : : // error happens rather than returning (e.g.) SERVFAIL. RFC 1996 is
692 : : // silent about such cases, but there doesn't seem to be anything we can
693 : : // improve at the primary server side by sending an error anyway.
694 [ + + ]: 9 : if (xfrin_session_ == NULL) {
695 [ + - ][ + - ]: 1 : LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
[ + - ][ + - ]
696 : : return (false);
697 : : }
698 : :
699 : : const string remote_ip_address =
700 [ + - ][ + - ]: 16 : io_message.getRemoteEndpoint().getAddress().toText();
701 : : static const string command_template_start =
702 [ + + ][ + - ]: 8 : "{\"command\": [\"notify\", {\"zone_name\" : \"";
[ + - ]
703 [ + + ][ + - ]: 8 : static const string command_template_master = "\", \"master\" : \"";
[ + - ]
704 [ + + ][ + - ]: 8 : static const string command_template_rrclass = "\", \"zone_class\" : \"";
[ + - ]
705 [ + + ][ + - ]: 8 : static const string command_template_end = "\"}]}";
[ + - ]
706 : :
707 : : try {
708 : : ConstElementPtr notify_command = Element::fromJSON(
709 : 16 : command_template_start + question->getName().toText() +
710 : 8 : command_template_master + remote_ip_address +
711 : 24 : command_template_rrclass + question->getClass().toText() +
712 [ + - + - ]: 24 : command_template_end);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
713 : : const unsigned int seq =
714 : : xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
715 [ + - ][ + - ]: 24 : "*", "*");
[ + - ][ + + ]
716 : : ConstElementPtr env, answer, parsed_answer;
717 [ + + ]: 7 : xfrin_session_->group_recvmsg(env, answer, false, seq);
718 : : int rcode;
719 [ + + ][ + - ]: 6 : parsed_answer = parseAnswer(rcode, answer);
720 [ + + ]: 5 : if (rcode != 0) {
721 [ + - ][ + - ]: 2 : LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
[ + - ]
722 [ + - ][ + - ]: 2 : .arg(parsed_answer->str());
[ + - ]
723 : 1 : return (false);
724 : : }
725 [ - + ]: 6 : } catch (const Exception& ex) {
726 [ - + ][ + - ]: 3 : LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
[ - + ][ - + ]
[ - + ]
727 : : return (false);
728 : : }
729 : :
730 [ + - ]: 4 : message.makeResponse();
731 [ + - ]: 4 : message.setHeaderFlag(Message::HEADERFLAG_AA);
732 [ + - ][ + - ]: 4 : message.setRcode(Rcode::NOERROR());
733 : :
734 [ + - ]: 4 : RendererHolder holder(renderer_, &buffer);
735 [ - + ]: 4 : if (tsig_context.get() != NULL) {
736 [ # # ]: 0 : message.toWire(renderer_, *tsig_context);
737 : : } else {
738 [ + - ]: 4 : message.toWire(renderer_);
739 : : }
740 : : return (true);
741 : : }
742 : :
743 : : void
744 : 39 : AuthSrvImpl::incCounter(const int protocol) {
745 : : // Increment query counter.
746 [ + + ]: 39 : if (protocol == IPPROTO_UDP) {
747 : 27 : counters_.inc(AuthCounters::SERVER_UDP_QUERY);
748 [ + - ]: 12 : } else if (protocol == IPPROTO_TCP) {
749 : 12 : counters_.inc(AuthCounters::SERVER_TCP_QUERY);
750 : : } else {
751 : : // unknown protocol
752 [ # # ][ # # ]: 0 : isc_throw(Unexpected, "Unknown protocol: " << protocol);
753 : : }
754 : 39 : }
755 : :
756 : : void
757 : 1 : AuthSrvImpl::registerStatisticsValidator() {
758 : : counters_.registerStatisticsValidator(
759 [ + - ]: 2 : boost::bind(&AuthSrvImpl::validateStatistics, this, _1));
760 : 1 : }
761 : :
762 : : bool
763 : 0 : AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const {
764 [ # # ]: 0 : if (config_session_ == NULL) {
765 : : return (false);
766 : : }
767 : : return (
768 : 0 : config_session_->getModuleSpec().validateStatistics(
769 [ # # ]: 0 : data, true));
770 : : }
771 : :
772 : : ConstElementPtr
773 : 20 : AuthSrvImpl::setDbFile(ConstElementPtr config) {
774 : 20 : ConstElementPtr answer = isc::config::createAnswer();
775 : :
776 [ + - ][ + - ]: 20 : if (config && config->contains("database_file")) {
[ + - ][ + + ]
[ + - ][ + + ]
[ # # ]
777 [ + - ][ + - ]: 12 : db_file_ = config->get("database_file")->stringValue();
[ + - ]
778 [ - + ]: 16 : } else if (config_session_ != NULL) {
779 : : bool is_default;
780 [ # # ]: 0 : string item("database_file");
781 [ # # ]: 0 : ConstElementPtr value = config_session_->getValue(is_default, item);
782 [ # # ]: 0 : ElementPtr final = Element::createMap();
783 : :
784 : : // If the value is the default, and we are running from
785 : : // a specific directory ('from build'), we need to use
786 : : // a different value than the default (which may not exist)
787 : : // (btw, this should not be done here in the end, i think
788 : : // the from-source script should have a check for this,
789 : : // but for that we need offline access to config, so for
790 : : // now this is a decent solution)
791 [ # # ][ # # ]: 0 : if (is_default && getenv("B10_FROM_BUILD")) {
[ # # ]
792 : 0 : value = Element::create(string(getenv("B10_FROM_BUILD")) +
793 [ # # ][ # # ]: 0 : "/bind10_zones.sqlite3");
[ # # ]
794 : : }
795 [ # # ]: 0 : final->set(item, value);
796 [ # # ]: 0 : config = final;
797 : :
798 [ # # ]: 0 : db_file_ = value->stringValue();
799 : : } else {
800 : : return (answer);
801 : : }
802 [ + - ][ + - ]: 4 : LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_DATA_SOURCE).arg(db_file_);
[ + - ][ + - ]
[ + - ]
803 : :
804 : : // create SQL data source
805 : : // Note: the following step is tricky to be exception-safe and to ensure
806 : : // exception guarantee: We first need to perform all operations that can
807 : : // fail, while acquiring resources in the RAII manner. We then perform
808 : : // delete and swap operations which should not fail.
809 [ + - ][ + - ]: 4 : DataSrcPtr datasrc_ptr(DataSrcPtr(new Sqlite3DataSrc));
810 [ + + ]: 4 : datasrc_ptr->init(config);
811 [ + - ]: 3 : data_sources_.addDataSrc(datasrc_ptr);
812 : :
813 : : // The following code should be exception free.
814 [ - + ]: 3 : if (cur_datasrc_ != NULL) {
815 [ # # ]: 0 : data_sources_.removeDataSrc(cur_datasrc_);
816 : : }
817 : 3 : cur_datasrc_ = datasrc_ptr;
818 : :
819 : : return (answer);
820 : : }
821 : :
822 : : void
823 : 212 : AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
824 [ + + ]: 212 : if (done) {
825 : 198 : counters_.inc(message.getRcode());
826 : : }
827 : 212 : server->resume(done);
828 : 212 : }
829 : :
830 : : ConstElementPtr
831 : 27 : AuthSrv::updateConfig(ConstElementPtr new_config) {
832 : : try {
833 : : // the ModuleCCSession has already checked if we have
834 : : // the correct ElementPtr type as specified in our .spec file
835 [ + - ]: 27 : if (new_config) {
836 [ + + ]: 27 : configureAuthServer(*this, new_config);
837 : : }
838 [ + + ]: 20 : return (impl_->setDbFile(new_config));
839 [ - + ]: 16 : } catch (const isc::Exception& error) {
840 [ - + ][ + - ]: 8 : LOG_ERROR(auth_logger, AUTH_CONFIG_UPDATE_FAIL).arg(error.what());
[ - + ][ - + ]
[ - + ]
841 [ - + ][ - + ]: 8 : return (isc::config::createAnswer(1, error.what()));
842 : : }
843 : : }
844 : :
845 : 1 : bool AuthSrv::submitStatistics() const {
846 : 1 : return (impl_->counters_.submitStatistics());
847 : : }
848 : :
849 : : uint64_t
850 : 8 : AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
851 : 8 : return (impl_->counters_.getCounter(type));
852 : : }
853 : :
854 : : uint64_t
855 : 35 : AuthSrv::getCounter(const Opcode opcode) const {
856 : 35 : return (impl_->counters_.getCounter(opcode));
857 : : }
858 : :
859 : : uint64_t
860 : 289 : AuthSrv::getCounter(const Rcode rcode) const {
861 : 289 : return (impl_->counters_.getCounter(rcode));
862 : : }
863 : :
864 : : const AddressList&
865 : 13 : AuthSrv::getListenAddresses() const {
866 : 13 : return (impl_->listen_addresses_);
867 : : }
868 : :
869 : : void
870 : 4 : AuthSrv::setListenAddresses(const AddressList& addresses) {
871 : : // For UDP servers we specify the "SYNC_OK" option because in our usage
872 : : // it can act in the synchronous mode.
873 : : installListenAddresses(addresses, impl_->listen_addresses_, *dnss_,
874 : 4 : DNSService::SERVER_SYNC_OK);
875 : 3 : }
876 : :
877 : : void
878 : 81 : AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
879 : 81 : dnss_ = &dnss;
880 : 81 : }
881 : :
882 : : void
883 : 4 : AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
884 : 4 : impl_->keyring_ = keyring;
885 : 16 : }
|