Kea 1.5.0
ncr_udp.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2017 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
10#include <dhcp_ddns/ncr_udp.h>
11
12#include <boost/bind.hpp>
13
14namespace isc {
15namespace dhcp_ddns {
16
17//*************************** UDPCallback ***********************
18UDPCallback::UDPCallback (RawBufferPtr& buffer, const size_t buf_size,
19 UDPEndpointPtr& data_source,
20 const UDPCompletionHandler& handler)
21 : handler_(handler), data_(new Data(buffer, buf_size, data_source)) {
22 if (handler.empty()) {
23 isc_throw(NcrUDPError, "UDPCallback - handler can't be null");
24 }
25
26 if (!buffer) {
27 isc_throw(NcrUDPError, "UDPCallback - buffer can't be null");
28 }
29}
30
31void
32UDPCallback::operator ()(const boost::system::error_code error_code,
33 const size_t bytes_transferred) {
34
35 // Save the result state and number of bytes transferred.
36 setErrorCode(error_code);
37 setBytesTransferred(bytes_transferred);
38
39 // Invoke the NameChangeRequest layer completion handler.
40 // First argument is a boolean indicating success or failure.
41 // The second is a pointer to "this" callback object. By passing
42 // ourself in, we make all of the service related data available
43 // to the completion handler.
44 handler_(!error_code, this);
45}
46
47void
48UDPCallback::putData(const uint8_t* src, size_t len) {
49 if (!src) {
50 isc_throw(NcrUDPError, "UDPCallback putData, data source is NULL");
51 }
52
53 if (len > data_->buf_size_) {
54 isc_throw(NcrUDPError, "UDPCallback putData, data length too large");
55 }
56
57 memcpy (data_->buffer_.get(), src, len);
58 data_->put_len_ = len;
59}
60
61
62//*************************** NameChangeUDPListener ***********************
65 const uint32_t port, const NameChangeFormat format,
66 RequestReceiveHandler& ncr_recv_handler,
67 const bool reuse_address)
68 : NameChangeListener(ncr_recv_handler), ip_address_(ip_address),
69 port_(port), format_(format), reuse_address_(reuse_address) {
70 // Instantiate the receive callback. This gets passed into each receive.
71 // Note that the callback constructor is passed an instance method
72 // pointer to our completion handler method, receiveCompletionHandler.
73 RawBufferPtr buffer(new uint8_t[RECV_BUF_MAX]);
74 UDPEndpointPtr data_source(new asiolink::UDPEndpoint());
75 recv_callback_.reset(new
76 UDPCallback(buffer, RECV_BUF_MAX, data_source,
77 boost::bind(&NameChangeUDPListener::
78 receiveCompletionHandler, this, _1, _2)));
79}
80
82 // Clean up.
84}
85
86void
88 // create our endpoint and bind the the low level socket to it.
89 isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
90
91 // Create the low level socket.
92 try {
93 asio_socket_.reset(new boost::asio::ip::udp::
94 socket(io_service.get_io_service(),
95 (ip_address_.isV4() ? boost::asio::ip::udp::v4() :
96 boost::asio::ip::udp::v6())));
97
98 // Set the socket option to reuse addresses if it is enabled.
99 if (reuse_address_) {
100 asio_socket_->set_option(boost::asio::socket_base::reuse_address(true));
101 }
102
103 // Bind the low level socket to our endpoint.
104 asio_socket_->bind(endpoint.getASIOEndpoint());
105 } catch (boost::system::system_error& ex) {
106 asio_socket_.reset();
107 isc_throw (NcrUDPError, ex.code().message());
108 }
109
110 // Create the asiolink socket from the low level socket.
111 socket_.reset(new NameChangeUDPSocket(*asio_socket_));
112}
113
114
115void
117 // Call the socket's asynchronous receiving, passing ourself in as callback.
118 RawBufferPtr recv_buffer = recv_callback_->getBuffer();
119 socket_->asyncReceive(recv_buffer.get(), recv_callback_->getBufferSize(),
120 0, recv_callback_->getDataSource().get(),
121 *recv_callback_);
122}
123
124void
126 // Whether we think we are listening or not, make sure we aren't.
127 // Since we are managing our own socket, we need to close it ourselves.
128 // NOTE that if there is a pending receive, it will be canceled, which
129 // WILL generate an invocation of the callback with error code of
130 // "operation aborted".
131 if (asio_socket_) {
132 if (asio_socket_->is_open()) {
133 try {
134 asio_socket_->close();
135 } catch (boost::system::system_error& ex) {
136 // It is really unlikely that this will occur.
137 // If we do reopen later it will be with a new socket
138 // instance. Repackage exception as one that is conformant
139 // with the interface.
140 isc_throw (NcrUDPError, ex.code().message());
141 }
142 }
143
144 asio_socket_.reset();
145 }
146
147 socket_.reset();
148}
149
150void
152 const UDPCallback *callback) {
154 Result result = SUCCESS;
155
156 if (successful) {
157 // Make an InputBuffer from our internal array
158 isc::util::InputBuffer input_buffer(callback->getData(),
159 callback->getBytesTransferred());
160
161 try {
162 ncr = NameChangeRequest::fromFormat(format_, input_buffer);
163 } catch (const NcrMessageError& ex) {
164 // log it and go back to listening
165 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_INVALID_NCR).arg(ex.what());
166
167 // Queue up the next receive.
168 // NOTE: We must call the base class, NEVER doReceive
169 receiveNext();
170 return;
171 }
172 } else {
173 boost::system::error_code error_code = callback->getErrorCode();
174 if (error_code.value() == boost::asio::error::operation_aborted) {
175 // A shutdown cancels all outstanding reads. For this reason,
176 // it can be an expected event, so log it as a debug message.
178 DHCP_DDNS_NCR_UDP_RECV_CANCELED);
179 result = STOPPED;
180 } else {
181 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_RECV_ERROR)
182 .arg(error_code.message());
183 result = ERROR;
184 }
185 }
186
187 // Call the application's registered request receive handler.
188 invokeRecvHandler(result, ncr);
189}
190
191
192//*************************** NameChangeUDPSender ***********************
193
196 const uint32_t port,
197 const isc::asiolink::IOAddress& server_address,
198 const uint32_t server_port, const NameChangeFormat format,
199 RequestSendHandler& ncr_send_handler,
200 const size_t send_que_max, const bool reuse_address)
201 : NameChangeSender(ncr_send_handler, send_que_max),
202 ip_address_(ip_address), port_(port), server_address_(server_address),
203 server_port_(server_port), format_(format),
204 reuse_address_(reuse_address) {
205 // Instantiate the send callback. This gets passed into each send.
206 // Note that the callback constructor is passed the an instance method
207 // pointer to our completion handler, sendCompletionHandler.
208 RawBufferPtr buffer(new uint8_t[SEND_BUF_MAX]);
209 UDPEndpointPtr data_source(new asiolink::UDPEndpoint());
210 send_callback_.reset(new UDPCallback(buffer, SEND_BUF_MAX, data_source,
211 boost::bind(&NameChangeUDPSender::
213 _1, _2)));
214}
215
217 // Clean up.
218 stopSending();
219}
220
221void
223 // create our endpoint and bind the the low level socket to it.
224 isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
225
226 // Create the low level socket.
227 try {
228 asio_socket_.reset(new boost::asio::ip::udp::
229 socket(io_service.get_io_service(),
230 (ip_address_.isV4() ? boost::asio::ip::udp::v4() :
231 boost::asio::ip::udp::v6())));
232
233 // Set the socket option to reuse addresses if it is enabled.
234 if (reuse_address_) {
235 asio_socket_->set_option(boost::asio::socket_base::reuse_address(true));
236 }
237
238 // Bind the low level socket to our endpoint.
239 asio_socket_->bind(endpoint.getASIOEndpoint());
240 } catch (boost::system::system_error& ex) {
241 isc_throw (NcrUDPError, ex.code().message());
242 }
243
244 // Create the asiolink socket from the low level socket.
245 socket_.reset(new NameChangeUDPSocket(*asio_socket_));
246
247 // Create the server endpoint
248 server_endpoint_.reset(new isc::asiolink::
249 UDPEndpoint(server_address_, server_port_));
250
251 send_callback_->setDataSource(server_endpoint_);
252
253 closeWatchSocket();
254 watch_socket_.reset(new util::WatchSocket());
255}
256
257void
259 // Whether we think we are sending or not, make sure we aren't.
260 // Since we are managing our own socket, we need to close it ourselves.
261 // NOTE that if there is a pending send, it will be canceled, which
262 // WILL generate an invocation of the callback with error code of
263 // "operation aborted".
264 if (asio_socket_) {
265 if (asio_socket_->is_open()) {
266 try {
267 asio_socket_->close();
268 } catch (boost::system::system_error& ex) {
269 // It is really unlikely that this will occur.
270 // If we do reopen later it will be with a new socket
271 // instance. Repackage exception as one that is conformant
272 // with the interface.
273 isc_throw (NcrUDPError, ex.code().message());
274 }
275 }
276
277 asio_socket_.reset();
278 }
279
280 socket_.reset();
281
282 closeWatchSocket();
283 watch_socket_.reset();
284}
285
286void
288 // Now use the NCR to write JSON to an output buffer.
290 ncr->toFormat(format_, ncr_buffer);
291
292 // Copy the wire-ized request to callback. This way we know after
293 // send completes what we sent (or attempted to send).
294 send_callback_->putData(static_cast<const uint8_t*>(ncr_buffer.getData()),
295 ncr_buffer.getLength());
296
297 // Call the socket's asynchronous send, passing our callback
298 socket_->asyncSend(send_callback_->getData(), send_callback_->getPutLen(),
299 send_callback_->getDataSource().get(), *send_callback_);
300
301 // Set IO ready marker so sender activity is visible to select() or poll().
302 // Note, if this call throws it will manifest itself as a throw from
303 // from sendRequest() which the application calls directly and is documented
304 // as throwing exceptions; or caught inside invokeSendHandler() which
305 // will invoke the application's send_handler with an error status.
306 watch_socket_->markReady();
307}
308
309void
311 const UDPCallback *send_callback) {
312 // Clear the IO ready marker.
313 try {
314 watch_socket_->clearReady();
315 } catch (const std::exception& ex) {
316 // This can only happen if the WatchSocket's select_fd has been
317 // compromised which is a programmatic error. We'll log the error
318 // here, then continue on and process the IO result we were given.
319 // WatchSocket issue will resurface on the next send as a closed
320 // fd in markReady(). This allows application's handler to deal
321 // with watch errors more uniformly.
322 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_CLEAR_READY_ERROR)
323 .arg(ex.what());
324 }
325
326 Result result;
327 if (successful) {
328 result = SUCCESS;
329 }
330 else {
331 // On a failure, log the error and set the result to ERROR.
332 boost::system::error_code error_code = send_callback->getErrorCode();
333 if (error_code.value() == boost::asio::error::operation_aborted) {
334 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_SEND_CANCELED)
335 .arg(error_code.message());
336 result = STOPPED;
337 } else {
338 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_SEND_ERROR)
339 .arg(error_code.message());
340 result = ERROR;
341 }
342 }
343
344 // Call the application's registered request send handler.
345 invokeSendHandler(result);
346}
347
348int
350 if (!amSending()) {
351 isc_throw(NotImplemented, "NameChangeUDPSender::getSelectFd"
352 " not in send mode");
353 }
354
355 return(watch_socket_->getSelectFd());
356}
357
358bool
360 if (watch_socket_) {
361 return (watch_socket_->isReady());
362 }
363
364 return (false);
365}
366
367void
368NameChangeUDPSender::closeWatchSocket() {
369 if (watch_socket_) {
370 std::string error_string;
371 watch_socket_->closeSocket(error_string);
372 if (!error_string.empty()) {
373 LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UDP_SENDER_WATCH_SOCKET_CLOSE_ERROR)
374 .arg(error_string);
375 }
376 }
377}
378
379}; // end of isc::dhcp_ddns namespace
380}; // end of isc namespace
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.
Abstract class for defining application layer receive callbacks.
Definition: ncr_io.h:181
Abstract interface for receiving NameChangeRequests.
Definition: ncr_io.h:165
void stopListening()
Closes the IO source and stops listen logic.
Definition: ncr_io.cc:88
void invokeRecvHandler(const Result result, NameChangeRequestPtr &ncr)
Calls the NCR receive handler registered with the listener.
Definition: ncr_io.cc:105
Result
Defines the outcome of an asynchronous NCR receive.
Definition: ncr_io.h:169
void receiveNext()
Initiates an asynchronous receive.
Definition: ncr_io.cc:82
static NameChangeRequestPtr fromFormat(const NameChangeFormat format, isc::util::InputBuffer &buffer)
Static method for creating a NameChangeRequest from a buffer containing a marshalled request in a giv...
Definition: ncr_msg.cc:230
Abstract class for defining application layer send callbacks.
Definition: ncr_io.h:479
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:457
void stopSending()
Closes the IO sink and stops send logic.
Definition: ncr_io.cc:191
bool amSending() const
Returns true if the sender is in send mode, false otherwise.
Definition: ncr_io.h:671
void invokeSendHandler(const NameChangeSender::Result result)
Calls the NCR send completion handler registered with the sender.
Definition: ncr_io.cc:269
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
Provides the ability to receive NameChangeRequests via UDP socket.
Definition: ncr_udp.h:319
virtual void close()
Closes the UDPSocket.
Definition: ncr_udp.cc:125
virtual ~NameChangeUDPListener()
Destructor.
Definition: ncr_udp.cc:81
static const size_t RECV_BUF_MAX
Defines the maximum size packet that can be received.
Definition: ncr_udp.h:322
virtual void open(isc::asiolink::IOService &io_service)
Opens a UDP socket using the given IOService.
Definition: ncr_udp.cc:87
void receiveCompletionHandler(const bool successful, const UDPCallback *recv_callback)
Implements the NameChangeRequest level receive completion handler.
Definition: ncr_udp.cc:151
void doReceive()
Initiates an asynchronous read on the socket.
Definition: ncr_udp.cc:116
NameChangeUDPListener(const isc::asiolink::IOAddress &ip_address, const uint32_t port, const NameChangeFormat format, RequestReceiveHandler &ncr_recv_handler, const bool reuse_address=false)
Constructor.
Definition: ncr_udp.cc:64
Provides the ability to send NameChangeRequests via UDP socket.
Definition: ncr_udp.h:441
void sendCompletionHandler(const bool successful, const UDPCallback *send_callback)
Implements the NameChangeRequest level send completion handler.
Definition: ncr_udp.cc:310
virtual void open(isc::asiolink::IOService &io_service)
Opens a UDP socket using the given IOService.
Definition: ncr_udp.cc:222
virtual bool ioReady()
Returns whether or not the sender has IO ready to process.
Definition: ncr_udp.cc:359
static const size_t SEND_BUF_MAX
Defines the maximum size packet that can be sent.
Definition: ncr_udp.h:445
virtual int getSelectFd()
Returns a file descriptor suitable for use with select.
Definition: ncr_udp.cc:349
virtual void close()
Closes the UDPSocket.
Definition: ncr_udp.cc:258
NameChangeUDPSender(const isc::asiolink::IOAddress &ip_address, const uint32_t port, const isc::asiolink::IOAddress &server_address, const uint32_t server_port, const NameChangeFormat format, RequestSendHandler &ncr_send_handler, const size_t send_que_max=NameChangeSender::MAX_QUEUE_DEFAULT, const bool reuse_address=false)
Constructor.
Definition: ncr_udp.cc:195
virtual void doSend(NameChangeRequestPtr &ncr)
Sends a given request asynchronously over the socket.
Definition: ncr_udp.cc:287
virtual ~NameChangeUDPSender()
Destructor.
Definition: ncr_udp.cc:216
Exception thrown when NameChangeRequest marshalling error occurs.
Definition: ncr_msg.h:30
Thrown when a UDP level exception occurs.
Definition: ncr_udp.h:122
Implements the callback class passed into UDPSocket calls.
Definition: ncr_udp.h:147
size_t getBytesTransferred() const
Returns the number of bytes transferred by the completed IO service.
Definition: ncr_udp.h:231
void setErrorCode(const boost::system::error_code value)
Sets the completed IO layer service outcome status.
Definition: ncr_udp.h:250
const uint8_t * getData() const
Returns a pointer the data transfer buffer content.
Definition: ncr_udp.h:265
void putData(const uint8_t *src, size_t len)
Copies data into the data transfer buffer.
Definition: ncr_udp.cc:48
UDPCallback(RawBufferPtr &buffer, const size_t buf_size, UDPEndpointPtr &data_source, const UDPCompletionHandler &handler)
Used as the callback object for UDPSocket services.
Definition: ncr_udp.cc:18
void setBytesTransferred(const size_t value)
Sets the number of bytes transferred.
Definition: ncr_udp.h:238
void operator()(const boost::system::error_code error_code, const size_t bytes_transferred)
Operator that will be invoked by the asiolink layer.
Definition: ncr_udp.cc:32
boost::system::error_code getErrorCode() const
Returns the completed IO layer service outcome status.
Definition: ncr_udp.h:243
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
Provides an IO "ready" semaphore for use with select() or poll() WatchSocket exposes a single open fi...
Definition: watch_socket.h:47
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
isc::log::Logger dhcp_ddns_logger("libdhcp-ddns")
Defines the logger used within lib dhcp_ddns.
Definition: dhcp_ddns_log.h:18
NameChangeFormat
Defines the list of data wire formats supported.
Definition: ncr_msg.h:60
boost::function< void(const bool, const UDPCallback *)> UDPCompletionHandler
Defines a function pointer for NameChangeRequest completion handlers.
Definition: ncr_udp.h:131
isc::asiolink::UDPSocket< UDPCallback > NameChangeUDPSocket
Convenience type for UDP socket based listener.
Definition: ncr_udp.h:311
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:214
boost::shared_array< uint8_t > RawBufferPtr
Defines a dynamically allocated shared array.
Definition: ncr_udp.h:134
boost::shared_ptr< asiolink::UDPEndpoint > UDPEndpointPtr
Definition: ncr_udp.h:136
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:65
Defines the logger used by the top-level component of kea-dhcp-ddns.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.
Container class which stores service invocation related data.
Definition: ncr_udp.h:157