Kea 2.0.0
dns_client.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2021 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#include <d2/dns_client.h>
9#include <d2srv/d2_log.h>
10#include <dns/messagerenderer.h>
11#include <stats/stats_mgr.h>
12#include <limits>
13
14namespace isc {
15namespace d2 {
16
17namespace {
18
19// OutputBuffer objects are pre-allocated before data is written to them.
20// This is a default number of bytes for the buffers we create within
21// DNSClient class.
22const size_t DEFAULT_BUFFER_SIZE = 128;
23
24}
25
26using namespace isc::util;
27using namespace isc::asiolink;
28using namespace isc::asiodns;
29using namespace isc::dns;
30using namespace isc::stats;
31
32// This class provides the implementation for the DNSClient. This allows for
33// the separation of the DNSClient interface from the implementation details.
34// Currently, implementation uses IOFetch object to handle asynchronous
35// communication with the DNS. This design may be revisited in the future. If
36// implementation is changed, the DNSClient API will remain unchanged thanks
37// to this separation.
39public:
40 // A buffer holding response from a DNS.
42 // A caller-supplied object which will hold the parsed response from DNS.
43 // The response object is (or descends from) isc::dns::Message and is
44 // populated using Message::fromWire(). This method may only be called
45 // once in the lifetime of a Message instance. Therefore, response_ is a
46 // pointer reference thus allowing this class to replace the object
47 // pointed to with a new Message instance each time a message is
48 // received. This allows a single DNSClientImpl instance to be used for
49 // multiple, sequential IOFetch calls. (@todo Trac# 3286 has been opened
50 // against dns::Message::fromWire. Should the behavior of fromWire change
51 // the behavior here with could be reexamined).
53 // A caller-supplied external callback which is invoked when DNS message
54 // exchange is complete or interrupted.
56 // A Transport Layer protocol used to communicate with a DNS.
58 // TSIG context used to sign outbound and verify inbound messages.
60 // TSIG key name for stats.
61 std::string tsig_key_name_;
62
63 // Constructor and Destructor
64 DNSClientImpl(D2UpdateMessagePtr& response_placeholder,
65 DNSClient::Callback* callback,
66 const DNSClient::Protocol proto);
67 virtual ~DNSClientImpl();
68
69 // This internal callback is called when the DNS update message exchange is
70 // complete. It further invokes the external callback provided by a caller.
71 // Before external callback is invoked, an object of the D2UpdateMessage
72 // type, representing a response from the server is set.
73 virtual void operator()(asiodns::IOFetch::Result result);
74
75 // Starts asynchronous DNS Update using TSIG.
76 void doUpdate(asiolink::IOService& io_service,
77 const asiolink::IOAddress& ns_addr,
78 const uint16_t ns_port,
79 D2UpdateMessage& update,
80 const unsigned int wait,
81 const D2TsigKeyPtr& tsig_key);
82
83 // This function maps the IO error to the DNSClient error.
85
86 // This function updates statistics.
87 void incrStats(const std::string& stat, bool update_key = true);
88};
89
91 DNSClient::Callback* callback,
92 const DNSClient::Protocol proto)
93 : in_buf_(new OutputBuffer(DEFAULT_BUFFER_SIZE)),
94 response_(response_placeholder), callback_(callback), proto_(proto) {
95
96 // Response should be an empty pointer. It gets populated by the
97 // operator() method.
98 if (response_) {
99 isc_throw(isc::BadValue, "Response buffer pointer should be null");
100 }
101
102 // @todo Currently we only support UDP. The support for TCP is planned for
103 // the future release.
104 if (proto_ == DNSClient::TCP) {
105 isc_throw(isc::NotImplemented, "TCP is currently not supported as a"
106 << " Transport protocol for DNS Updates; please use UDP");
107 }
108
109 // Given that we already eliminated the possibility that TCP is used, it
110 // would be sufficient to check that (proto != DNSClient::UDP). But, once
111 // support TCP is added the check above will disappear and the extra check
112 // will be needed here anyway.
113 // Note that cascaded check is used here instead of:
114 // if (proto_ != DNSClient::TCP && proto_ != DNSClient::UDP)..
115 // because some versions of GCC compiler complain that check above would
116 // be always 'false' due to limited range of enumeration. In fact, it is
117 // possible to pass out of range integral value through enum and it should
118 // be caught here.
119 if (proto_ != DNSClient::TCP) {
120 if (proto_ != DNSClient::UDP) {
121 isc_throw(isc::NotImplemented, "invalid transport protocol type '"
122 << proto_ << "' specified for DNS Updates");
123 }
124 }
125}
126
128}
129
130void
132 // Get the status from IO. If no success, we just call user's callback
133 // and pass the status code.
134 DNSClient::Status status = getStatus(result);
135 if (status == DNSClient::SUCCESS) {
136 // Allocate a new response message. (Note that Message::fromWire
137 // may only be run once per message, so we need to start fresh
138 // each time.)
140
141 // Server's response may be corrupted. In such case, fromWire will
142 // throw an exception. We want to catch this exception to return
143 // appropriate status code to the caller and log this event.
144 try {
145 response_->fromWire(in_buf_->getData(), in_buf_->getLength(),
146 tsig_context_.get());
147 incrStats("update-success");
148 } catch (const isc::Exception& ex) {
152 incrStats("update-error");
153 }
154
155 if (tsig_context_) {
156 // Context is a one-shot deal, get rid of it.
157 tsig_context_.reset();
158 }
159 } else if (status == DNSClient::TIMEOUT) {
160 incrStats("update-timeout");
161 } else {
162 incrStats("update-error");
163 }
164
165 // Once we are done with internal business, let's call a callback supplied
166 // by a caller.
167 if (callback_ != NULL) {
168 (*callback_)(status);
169 }
170}
171
174 switch (result) {
175 case IOFetch::SUCCESS:
176 return (DNSClient::SUCCESS);
177
179 return (DNSClient::TIMEOUT);
180
181 case IOFetch::STOPPED:
182 return (DNSClient::IO_STOPPED);
183
184 default:
185 ;
186 }
187 return (DNSClient::OTHER);
188}
189
190void
192 const IOAddress& ns_addr,
193 const uint16_t ns_port,
194 D2UpdateMessage& update,
195 const unsigned int wait,
196 const D2TsigKeyPtr& tsig_key) {
197 // The underlying implementation which we use to send DNS Updates uses
198 // signed integers for timeout. If we want to avoid overflows we need to
199 // respect this limitation here.
200 if (wait > DNSClient::getMaxTimeout()) {
201 isc_throw(isc::BadValue, "A timeout value for DNS Update request must"
202 " not exceed " << DNSClient::getMaxTimeout()
203 << ". Provided timeout value is '" << wait << "'");
204 }
205
206 // Create a TSIG context if we have a key, otherwise clear the context
207 // pointer. Message marshalling uses non-null context is the indicator
208 // that TSIG should be used.
209 if (tsig_key) {
210 tsig_context_ = tsig_key->createContext();
211 tsig_key_name_ = tsig_key->getKeyName().toText();
212 } else {
213 tsig_context_.reset();
214 tsig_key_name_.clear();
215 }
216
217 // A renderer is used by the toWire function which creates the on-wire data
218 // from the DNS Update message. A renderer has its internal buffer where it
219 // renders data by default. However, this buffer can't be directly accessed.
220 // Fortunately, the renderer's API accepts user-supplied buffers. So, let's
221 // create our own buffer and pass it to the renderer so as the message is
222 // rendered to this buffer. Finally, we pass this buffer to IOFetch.
223 dns::MessageRenderer renderer;
224 OutputBufferPtr msg_buf(new OutputBuffer(DEFAULT_BUFFER_SIZE));
225 renderer.setBuffer(msg_buf.get());
226
227 // Render DNS Update message. This may throw a bunch of exceptions if
228 // invalid message object is given.
229 update.toWire(renderer, tsig_context_.get());
230
231 // IOFetch has all the mechanisms that we need to perform asynchronous
232 // communication with the DNS server. The last but one argument points to
233 // this object as a completion callback for the message exchange. As a
234 // result operator()(Status) will be called.
235
236 // Timeout value is explicitly cast to the int type to avoid warnings about
237 // overflows when doing implicit cast. It should have been checked by the
238 // caller that the unsigned timeout value will fit into int.
239 IOFetch io_fetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port,
240 in_buf_, this, static_cast<int>(wait));
241
242 // Post the task to the task queue in the IO service. Caller will actually
243 // run these tasks by executing IOService::run.
244 io_service.post(io_fetch);
245
246 // Update sent statistics.
247 incrStats("update-sent");
248 if (tsig_key) {
249 incrStats("update-signed", false);
250 } else {
251 incrStats("update-unsigned", false);
252 }
253}
254
255void
256DNSClientImpl::incrStats(const std::string& stat, bool update_key) {
258 mgr.addValue(stat, static_cast<int64_t>(1));
259 if (update_key && !tsig_key_name_.empty()) {
261 static_cast<int64_t>(1));
262 }
263}
264
266 Callback* callback, const DNSClient::Protocol proto)
267 : impl_(new DNSClientImpl(response_placeholder, callback, proto)) {
268}
269
271 delete (impl_);
272}
273
274unsigned int
276 static const unsigned int max_timeout = std::numeric_limits<int>::max();
277 return (max_timeout);
278}
279
280void
282 const IOAddress& ns_addr,
283 const uint16_t ns_port,
284 D2UpdateMessage& update,
285 const unsigned int wait,
286 const D2TsigKeyPtr& tsig_key) {
287 impl_->doUpdate(io_service, ns_addr, ns_port, update, wait, tsig_key);
288}
289
290} // namespace d2
291} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when a function is not implemented.
I/O Fetch Callback.
Definition: io_fetch.h:98
Upstream Fetch Processing.
Definition: io_fetch.h:45
Result
Result of Upstream Fetch.
Definition: io_fetch.h:70
@ STOPPED
Control code, fetch has been stopped.
Definition: io_fetch.h:73
@ TIME_OUT
Failure, fetch timed out.
Definition: io_fetch.h:72
@ SUCCESS
Success, fetch completed.
Definition: io_fetch.h:71
The D2UpdateMessage encapsulates a DNS Update message.
void toWire(dns::AbstractMessageRenderer &renderer, dns::TSIGContext *const tsig_ctx=NULL)
Encode outgoing message into wire format.
DNSClient::Status getStatus(const asiodns::IOFetch::Result)
Definition: dns_client.cc:173
void incrStats(const std::string &stat, bool update_key=true)
Definition: dns_client.cc:256
virtual void operator()(asiodns::IOFetch::Result result)
Callback method.
Definition: dns_client.cc:131
std::string tsig_key_name_
Definition: dns_client.cc:61
DNSClient::Callback * callback_
Definition: dns_client.cc:55
dns::TSIGContextPtr tsig_context_
Definition: dns_client.cc:59
D2UpdateMessagePtr & response_
Definition: dns_client.cc:52
DNSClientImpl(D2UpdateMessagePtr &response_placeholder, DNSClient::Callback *callback, const DNSClient::Protocol proto)
Definition: dns_client.cc:90
void doUpdate(asiolink::IOService &io_service, const asiolink::IOAddress &ns_addr, const uint16_t ns_port, D2UpdateMessage &update, const unsigned int wait, const D2TsigKeyPtr &tsig_key)
Definition: dns_client.cc:191
DNSClient::Protocol proto_
Definition: dns_client.cc:57
util::OutputBufferPtr in_buf_
Definition: dns_client.cc:41
Callback for the DNSClient class.
Definition: dns_client.h:74
~DNSClient()
Virtual destructor, does nothing.
Definition: dns_client.cc:270
DNSClient(D2UpdateMessagePtr &response_placeholder, Callback *callback, const Protocol proto=UDP)
Constructor.
Definition: dns_client.cc:265
void doUpdate(asiolink::IOService &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:281
static unsigned int getMaxTimeout()
Returns maximal allowed timeout value accepted by DNSClient::doUpdate.
Definition: dns_client.cc:275
Status
A status code of the DNSClient.
Definition: dns_client.h:60
@ IO_STOPPED
IO was stopped.
Definition: dns_client.h:63
@ TIMEOUT
No response, timeout.
Definition: dns_client.h:62
@ OTHER
Other, unclassified error.
Definition: dns_client.h:65
@ INVALID_RESPONSE
Response received but invalid.
Definition: dns_client.h:64
@ SUCCESS
Response received and is ok.
Definition: dns_client.h:61
Protocol
Transport layer protocol used by a DNS Client to communicate with a server.
Definition: dns_client.h:54
void setBuffer(isc::util::OutputBuffer *buffer)
Set or reset a temporary output buffer.
The MessageRenderer is a concrete derived class of AbstractMessageRenderer as a general purpose imple...
Statistics Manager class.
static StatsMgr & instance()
Statistics Manager accessor method.
static std::string generateName(const std::string &context, Type index, const std::string &stat_name)
Generates statistic name in a given context.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< D2UpdateMessage > D2UpdateMessagePtr
Pointer to the DNS Update Message.
isc::log::Logger d2_to_dns_logger("d2-to-dns")
Definition: d2_log.h:20
const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE
Definition: d2_messages.h:43
boost::shared_ptr< D2TsigKey > D2TsigKeyPtr
Type of pointer to a D2 TSIG key.
Definition: d2_tsig_key.h:71
boost::shared_ptr< TSIGContext > TSIGContextPtr
Definition: tsig.h:435
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
Definition: log_dbglevels.h:75
Definition: edns.h:19
boost::shared_ptr< OutputBuffer > OutputBufferPtr
Definition: buffer.h:599
Defines the logger used by the top-level component of kea-lfc.