| Kea 3.0.2-git

dns_client.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <d2srv/d2_log.h>
10#include <d2srv/dns_client.h>
11#include <dns/messagerenderer.h>
12#include <stats/stats_mgr.h>
13#include <limits>
14
15 namespace isc {
16 namespace d2 {
17
18 namespace {
19
20// OutputBuffer objects are pre-allocated before data is written to them.
21// This is a default number of bytes for the buffers we create within
22// DNSClient class.
23 const size_t DEFAULT_BUFFER_SIZE = 128;
24
25}
26
27 using namespace isc::util;
28 using namespace isc::asiolink;
29 using namespace isc::asiodns;
30 using namespace isc::dns;
31 using namespace isc::stats;
32
33// This class provides the implementation for the DNSClient. This allows for
34// the separation of the DNSClient interface from the implementation details.
35// Currently, implementation uses IOFetch object to handle asynchronous
36// communication with the DNS. This design may be revisited in the future. If
37// implementation is changed, the DNSClient API will remain unchanged thanks
38// to this separation.
39 class DNSClientImpl : public asiodns::IOFetch::Callback {
40 public:
42 util::OutputBufferPtr in_buf_;
43
54 D2UpdateMessagePtr& response_;
55
58 DNSClient::Callback* callback_;
59
61 DNSClient::Protocol proto_;
62
64 dns::TSIGContextPtr tsig_context_;
65
67 std::string tsig_key_name_;
68
70 bool stopped_;
71
73 std::list<IOFetchPtr> io_fetch_list_;
74
84 DNSClientImpl(D2UpdateMessagePtr& response_placeholder,
85 DNSClient::Callback* callback,
86 const DNSClient::Protocol proto);
87
89 virtual ~DNSClientImpl();
90
95 virtual void operator()(asiodns::IOFetch::Result result);
96
109 void doUpdate(const asiolink::IOServicePtr& io_service,
110 const asiolink::IOAddress& ns_addr,
111 const uint16_t ns_port,
112 D2UpdateMessage& update,
113 const unsigned int wait,
114 const D2TsigKeyPtr& tsig_key);
115
120 DNSClient::Status getStatus(const asiodns::IOFetch::Result result);
121
127 void incrStats(const std::string& stat, bool update_key = true);
128
130 void stop();
131};
132
133 DNSClientImpl::DNSClientImpl(D2UpdateMessagePtr& response_placeholder,
134 DNSClient::Callback* callback,
135 const DNSClient::Protocol proto)
136 : in_buf_(new OutputBuffer(DEFAULT_BUFFER_SIZE)),
137 response_(response_placeholder), callback_(callback), proto_(proto),
138 stopped_(false) {
139
140 // Response should be an empty pointer. It gets populated by the
141 // operator() method.
142 if (response_) {
143 isc_throw(isc::BadValue, "Response buffer pointer should be null");
144 }
145
146 // @todo Currently we only support UDP. The support for TCP is planned for
147 // the future release.
148 if (proto_ == DNSClient::TCP) {
149 isc_throw(isc::NotImplemented, "TCP is currently not supported as a"
150 << " Transport protocol for DNS Updates; please use UDP");
151 }
152
153 // Given that we already eliminated the possibility that TCP is used, it
154 // would be sufficient to check that (proto != DNSClient::UDP). But, once
155 // support TCP is added the check above will disappear and the extra check
156 // will be needed here anyway.
157 // Note that cascaded check is used here instead of:
158 // if (proto_ != DNSClient::TCP && proto_ != DNSClient::UDP)..
159 // because some versions of GCC compiler complain that check above would
160 // be always 'false' due to limited range of enumeration. In fact, it is
161 // possible to pass out of range integral value through enum and it should
162 // be caught here.
163 if (proto_ != DNSClient::TCP) {
164 if (proto_ != DNSClient::UDP) {
165 isc_throw(isc::NotImplemented, "invalid transport protocol type '"
166 << proto_ << "' specified for DNS Updates");
167 }
168 }
169}
170
171 void DNSClientImpl::stop() {
172 stopped_ = true;
173 for (auto const& io_fetch : io_fetch_list_) {
174 io_fetch->stop();
175 }
176}
177
178 DNSClientImpl::~DNSClientImpl() {
179}
180
181 void
182 DNSClientImpl::operator()(asiodns::IOFetch::Result result) {
183 if (stopped_) {
184 return;
185 }
186 // Get the status from IO. If no success, we just call user's callback
187 // and pass the status code.
188 DNSClient::Status status = getStatus(result);
189 if (status == DNSClient::SUCCESS) {
190 // Allocate a new response message. (Note that Message::fromWire
191 // may only be run once per message, so we need to start fresh
192 // each time.)
193 response_.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
194
195 // Server's response may be corrupted. In such case, fromWire will
196 // throw an exception. We want to catch this exception to return
197 // appropriate status code to the caller and log this event.
198 try {
199 response_->fromWire(in_buf_->getData(), in_buf_->getLength(),
200 tsig_context_.get());
201 incrStats("update-success");
202 } catch (const isc::Exception& ex) {
203 status = DNSClient::INVALID_RESPONSE;
204 LOG_DEBUG(d2_to_dns_logger, isc::log::DBGLVL_TRACE_DETAIL,
205 DHCP_DDNS_INVALID_RESPONSE).arg(ex.what());
206 incrStats("update-error");
207 }
208
209 if (tsig_context_) {
210 // Context is a one-shot deal, get rid of it.
211 tsig_context_.reset();
212 }
213 } else if (status == DNSClient::TIMEOUT) {
214 incrStats("update-timeout");
215 } else {
216 incrStats("update-error");
217 }
218
219 // Once we are done with internal business, let's call a callback supplied
220 // by a caller.
221 if (callback_ != NULL) {
222 (*callback_)(status);
223 }
224}
225
226 DNSClient::Status
227 DNSClientImpl::getStatus(const asiodns::IOFetch::Result result) {
228 switch (result) {
229 case IOFetch::SUCCESS:
230 return (DNSClient::SUCCESS);
231
232 case IOFetch::TIME_OUT:
233 return (DNSClient::TIMEOUT);
234
235 case IOFetch::STOPPED:
236 return (DNSClient::IO_STOPPED);
237
238 default:
239 ;
240 }
241 return (DNSClient::OTHER);
242}
243
244 void
245 DNSClientImpl::doUpdate(const asiolink::IOServicePtr& io_service,
246 const IOAddress& ns_addr,
247 const uint16_t ns_port,
248 D2UpdateMessage& update,
249 const unsigned int wait,
250 const D2TsigKeyPtr& tsig_key) {
251 if (stopped_) {
252 return;
253 }
254 // The underlying implementation which we use to send DNS Updates uses
255 // signed integers for timeout. If we want to avoid overflows we need to
256 // respect this limitation here.
257 if (wait > DNSClient::getMaxTimeout()) {
258 isc_throw(isc::BadValue, "A timeout value for DNS Update request must"
259 " not exceed " << DNSClient::getMaxTimeout()
260 << ". Provided timeout value is '" << wait << "'");
261 }
262
263 // Create a TSIG context if we have a key, otherwise clear the context
264 // pointer. Message marshalling uses non-null context is the indicator
265 // that TSIG should be used.
266 if (tsig_key) {
267 tsig_context_ = tsig_key->createContext();
268 tsig_key_name_ = tsig_key->getKeyName().toText();
269 } else {
270 tsig_context_.reset();
271 tsig_key_name_.clear();
272 }
273
274 // A renderer is used by the toWire function which creates the on-wire data
275 // from the DNS Update message. A renderer has its internal buffer where it
276 // renders data by default. However, this buffer can't be directly accessed.
277 // Fortunately, the renderer's API accepts user-supplied buffers. So, let's
278 // create our own buffer and pass it to the renderer so as the message is
279 // rendered to this buffer. Finally, we pass this buffer to IOFetch.
280 dns::MessageRenderer renderer;
281 OutputBufferPtr msg_buf(new OutputBuffer(DEFAULT_BUFFER_SIZE));
282 renderer.setBuffer(msg_buf.get());
283
284 // Render DNS Update message. This may throw a bunch of exceptions if
285 // invalid message object is given.
286 update.toWire(renderer, tsig_context_.get());
287
288 // IOFetch has all the mechanisms that we need to perform asynchronous
289 // communication with the DNS server. The last but one argument points to
290 // this object as a completion callback for the message exchange. As a
291 // result operator()(Status) will be called.
292
293 // Timeout value is explicitly cast to the int type to avoid warnings about
294 // overflows when doing implicit cast. It should have been checked by the
295 // caller that the unsigned timeout value will fit into int.
296 IOFetchPtr io_fetch(new IOFetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port,
297 in_buf_, this, static_cast<int>(wait)));
298 io_fetch_list_.push_back(io_fetch);
299
300 // Post the task to the task queue in the IO service. Caller will actually
301 // run these tasks by executing IOService::run.
302 io_service->post(*io_fetch);
303
304 // Update sent statistics.
305 incrStats("update-sent");
306 if (tsig_key) {
307 incrStats("update-signed", false);
308 } else {
309 incrStats("update-unsigned", false);
310 }
311}
312
313 void
314 DNSClientImpl::incrStats(const std::string& stat, bool update_key) {
315 StatsMgr& mgr = StatsMgr::instance();
316 mgr.addValue(stat, static_cast<int64_t>(1));
317 if (update_key && !tsig_key_name_.empty()) {
318 mgr.addValue(StatsMgr::generateName("key", tsig_key_name_, stat),
319 static_cast<int64_t>(1));
320 }
321}
322
323 DNSClient::DNSClient(D2UpdateMessagePtr& response_placeholder,
324 Callback* callback, const DNSClient::Protocol proto)
325 : impl_(new DNSClientImpl(response_placeholder, callback, proto)) {
326}
327
328 DNSClient::~DNSClient() {
329 stop();
330}
331
332 void
333 DNSClient::stop() {
334 impl_->stop();
335}
336
337 unsigned int
338 DNSClient::getMaxTimeout() {
339 static const unsigned int max_timeout = std::numeric_limits<int>::max();
340 return (max_timeout);
341}
342
343 void
344 DNSClient::doUpdate(const asiolink::IOServicePtr& io_service,
345 const IOAddress& ns_addr,
346 const uint16_t ns_port,
347 D2UpdateMessage& update,
348 const unsigned int wait,
349 const D2TsigKeyPtr& tsig_key) {
350 impl_->doUpdate(io_service, ns_addr, ns_port, update, wait, tsig_key);
351}
352
353} // namespace d2
354} // namespace isc
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition exceptions/exceptions.h:132
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition exceptions/exceptions.h:23
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition exceptions/exceptions.cc:32
isc::NotImplemented
A generic exception that is thrown when a function is not implemented.
Definition exceptions/exceptions.h:166
isc::asiodns::IOFetch::Callback
I/O Fetch Callback.
Definition io_fetch.h:85
isc::asiodns::IOFetch
Upstream Fetch Processing.
Definition io_fetch.h:33
isc::asiodns::IOFetch::Result
Result
Result of Upstream Fetch.
Definition io_fetch.h:58
isc::asiodns::IOFetch::STOPPED
@ STOPPED
Definition io_fetch.h:61
isc::asiodns::IOFetch::TIME_OUT
@ TIME_OUT
Definition io_fetch.h:60
isc::asiodns::IOFetch::SUCCESS
@ SUCCESS
Definition io_fetch.h:59
isc::asiodns::IOFetch::UDP
@ UDP
Definition io_fetch.h:37
isc::asiolink::IOAddress
The IOAddress class represents an IP addresses (version agnostic).
Definition io_address.h:45
isc::d2::D2UpdateMessage
The D2UpdateMessage encapsulates a DNS Update message.
Definition d2_update_message.h:89
isc::d2::D2UpdateMessage::toWire
void toWire(dns::AbstractMessageRenderer &renderer, dns::TSIGContext *const tsig_ctx=NULL)
Encode outgoing message into wire format.
Definition d2_update_message.cc:105
isc::d2::D2UpdateMessage::INBOUND
@ INBOUND
Definition d2_update_message.h:95
isc::d2::DNSClientImpl
Definition dns_client.cc:39
isc::d2::DNSClientImpl::incrStats
void incrStats(const std::string &stat, bool update_key=true)
This function updates statistics.
Definition dns_client.cc:314
isc::d2::DNSClientImpl::operator()
virtual void operator()(asiodns::IOFetch::Result result)
This internal callback is called when the DNS update message exchange is complete.
Definition dns_client.cc:182
isc::d2::DNSClientImpl::tsig_key_name_
std::string tsig_key_name_
TSIG key name for stats.
Definition dns_client.cc:67
isc::d2::DNSClientImpl::io_fetch_list_
std::list< IOFetchPtr > io_fetch_list_
The list of IOFetch objects.
Definition dns_client.cc:73
isc::d2::DNSClientImpl::callback_
DNSClient::Callback * callback_
A caller-supplied external callback which is invoked when DNS message exchange is complete or interru...
Definition dns_client.cc:58
isc::d2::DNSClientImpl::tsig_context_
dns::TSIGContextPtr tsig_context_
TSIG context used to sign outbound and verify inbound messages.
Definition dns_client.cc:64
isc::d2::DNSClientImpl::response_
D2UpdateMessagePtr & response_
A caller-supplied object which will hold the parsed response from DNS.
Definition dns_client.cc:54
isc::d2::DNSClientImpl::~DNSClientImpl
virtual ~DNSClientImpl()
Destructor.
Definition dns_client.cc:178
isc::d2::DNSClientImpl::stop
void stop()
This function stops the IOFetch objects.
Definition dns_client.cc:171
isc::d2::DNSClientImpl::DNSClientImpl
DNSClientImpl(D2UpdateMessagePtr &response_placeholder, DNSClient::Callback *callback, const DNSClient::Protocol proto)
Constructor.
Definition dns_client.cc:133
isc::d2::DNSClientImpl::doUpdate
void doUpdate(const asiolink::IOServicePtr &io_service, const asiolink::IOAddress &ns_addr, const uint16_t ns_port, D2UpdateMessage &update, const unsigned int wait, const D2TsigKeyPtr &tsig_key)
Starts asynchronous DNS Update using TSIG.
Definition dns_client.cc:245
isc::d2::DNSClientImpl::stopped_
bool stopped_
Flag which indicates that the client has been stopped.
Definition dns_client.cc:70
isc::d2::DNSClientImpl::proto_
DNSClient::Protocol proto_
A Transport Layer protocol used to communicate with a DNS.
Definition dns_client.cc:61
isc::d2::DNSClientImpl::getStatus
DNSClient::Status getStatus(const asiodns::IOFetch::Result result)
This function maps the IO error to the DNSClient error.
Definition dns_client.cc:227
isc::d2::DNSClientImpl::in_buf_
util::OutputBufferPtr in_buf_
A buffer holding response from a DNS.
Definition dns_client.cc:42
isc::d2::DNSClient::Callback
Callback for the DNSClient class.
Definition dns_client.h:72
isc::d2::DNSClient::doUpdate
void doUpdate(const asiolink::IOServicePtr &io_service, const asiolink::IOAddress &ns_addr, const uint16_t ns_port, D2UpdateMessage &update, const unsigned int wait, const D2TsigKeyPtr &tsig_key=D2TsigKeyPtr())
Start asynchronous DNS Update with TSIG.
Definition dns_client.cc:344
isc::d2::DNSClient::~DNSClient
~DNSClient()
Virtual destructor, does nothing.
Definition dns_client.cc:328
isc::d2::DNSClient::DNSClient
DNSClient(D2UpdateMessagePtr &response_placeholder, Callback *callback, const Protocol proto=UDP)
Constructor.
Definition dns_client.cc:323
isc::d2::DNSClient::stop
void stop()
Stop the client.
Definition dns_client.cc:333
isc::d2::DNSClient::getMaxTimeout
static unsigned int getMaxTimeout()
Returns maximal allowed timeout value accepted by DNSClient::doUpdate.
Definition dns_client.cc:338
isc::d2::DNSClient::Status
Status
A status code of the DNSClient.
Definition dns_client.h:58
isc::d2::DNSClient::IO_STOPPED
@ IO_STOPPED
IO was stopped.
Definition dns_client.h:61
isc::d2::DNSClient::TIMEOUT
@ TIMEOUT
No response, timeout.
Definition dns_client.h:60
isc::d2::DNSClient::OTHER
@ OTHER
Other, unclassified error.
Definition dns_client.h:63
isc::d2::DNSClient::INVALID_RESPONSE
@ INVALID_RESPONSE
Response received but invalid.
Definition dns_client.h:62
isc::d2::DNSClient::SUCCESS
@ SUCCESS
Response received and is ok.
Definition dns_client.h:59
isc::d2::DNSClient::Protocol
Protocol
Transport layer protocol used by a DNS Client to communicate with a server.
Definition dns_client.h:52
isc::d2::DNSClient::TCP
@ TCP
Definition dns_client.h:54
isc::d2::DNSClient::UDP
@ UDP
Definition dns_client.h:53
isc::dns::AbstractMessageRenderer::setBuffer
void setBuffer(isc::util::OutputBuffer *buffer)
Set or reset a temporary output buffer.
Definition messagerenderer.cc:367
isc::dns::MessageRenderer
The MessageRenderer is a concrete derived class of AbstractMessageRenderer as a general purpose imple...
Definition messagerenderer.h:357
isc::stats::StatsMgr
Statistics Manager class.
Definition lib/stats/stats_mgr.h:65
isc::stats::StatsMgr::instance
static StatsMgr & instance()
Statistics Manager accessor method.
Definition lib/stats/stats_mgr.cc:30
isc::stats::StatsMgr::generateName
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
Definition lib/stats/stats_mgr.h:298
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
d2_log.h
dns_client.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
stats_mgr.h
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
messagerenderer.h
isc::asiodns::IOFetchPtr
boost::shared_ptr< IOFetch > IOFetchPtr
Defines a pointer to an IOFetch.
Definition io_fetch.h:247
isc::asiolink::IOServicePtr
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
Definition io_service.h:28
isc::d2
Definition check_exists_add.cc:19
isc::d2::D2UpdateMessagePtr
boost::shared_ptr< D2UpdateMessage > D2UpdateMessagePtr
Pointer to the DNS Update Message.
Definition d2_update_message.h:73
isc::d2::d2_to_dns_logger
isc::log::Logger d2_to_dns_logger("d2-to-dns")
Definition d2_log.h:20
isc::d2::DHCP_DDNS_INVALID_RESPONSE
const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE
Definition d2_messages.h:46
isc::d2::D2TsigKeyPtr
boost::shared_ptr< D2TsigKey > D2TsigKeyPtr
Type of pointer to a D2 TSIG key.
Definition d2_tsig_key.h:71
isc::dns::TSIGContextPtr
boost::shared_ptr< TSIGContext > TSIGContextPtr
Definition tsig.h:435
isc::log::DBGLVL_TRACE_DETAIL
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
Definition log_dbglevels.h:75
isc::util::OutputBufferPtr
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Type of pointers to output buffers.
Definition buffer.h:574
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148