Branch data Line data Source code
1 : : // Copyright (C) 2011 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 <unistd.h> // for some IPC/network system calls
16 : : #include <netinet/in.h>
17 : : #include <sys/socket.h>
18 : : #include <errno.h>
19 : :
20 : : #include <boost/shared_array.hpp>
21 : :
22 : : #include <config.h>
23 : :
24 : : #include <log/dummylog.h>
25 : :
26 : : #include <asio.hpp>
27 : : #include <asio/error.hpp>
28 : : #include <asiolink/dummy_io_cb.h>
29 : : #include <asiolink/udp_endpoint.h>
30 : : #include <asiolink/udp_socket.h>
31 : : #include "udp_server.h"
32 : : #include "logger.h"
33 : :
34 : : #include <dns/opcode.h>
35 : :
36 : : using namespace asio;
37 : : using asio::ip::udp;
38 : : using isc::log::dlog;
39 : :
40 : : using namespace std;
41 : : using namespace isc::dns;
42 : : using namespace isc::util;
43 : : using namespace isc::asiolink;
44 : :
45 : : namespace isc {
46 : : namespace asiodns {
47 : :
48 : : /*
49 : : * Some of the member variables here are shared_ptrs and some are
50 : : * auto_ptrs. There will be one instance of Data for the lifetime
51 : : * of packet. The variables that are state only for a single packet
52 : : * use auto_ptr, as it is more lightweight. In the case of shared
53 : : * configuration (eg. the callbacks, socket), we use shared_ptrs.
54 : : */
55 : 141 : struct UDPServer::Data {
56 : : /*
57 : : * Constructors from parameters passed to UDPServer constructor.
58 : : * This instance will not be used to retrieve and answer the actual
59 : : * query, it will only hold parameters until we wait for the
60 : : * first packet. But we do initialize the socket in here.
61 : : */
62 : : Data(io_service& io_service, const ip::address& addr, const uint16_t port,
63 : : SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
64 : : io_(io_service), done_(false),
65 : : checkin_callback_(checkin),lookup_callback_(lookup),
66 : : answer_callback_(answer)
67 : : {
68 : : // We must use different instantiations for v4 and v6;
69 : : // otherwise ASIO will bind to both
70 : : udp proto = addr.is_v4() ? udp::v4() : udp::v6();
71 : : socket_.reset(new udp::socket(io_service, proto));
72 : : socket_->set_option(socket_base::reuse_address(true));
73 : : if (addr.is_v6()) {
74 : : socket_->set_option(asio::ip::v6_only(true));
75 : : }
76 : : socket_->bind(udp::endpoint(addr, port));
77 : : }
78 : 68 : Data(io_service& io_service, int fd, int af, SimpleCallback* checkin,
79 : : DNSLookup* lookup, DNSAnswer* answer) :
80 : : io_(io_service), done_(false),
81 : : checkin_callback_(checkin),lookup_callback_(lookup),
82 [ + - ]: 68 : answer_callback_(answer)
83 : : {
84 [ + + ]: 68 : if (af != AF_INET && af != AF_INET6) {
85 [ + - ][ + - ]: 2 : isc_throw(InvalidParameter, "Address family must be either AF_INET "
[ + - ]
86 : : "or AF_INET6, not " << af);
87 : : }
88 [ + - ][ + - ]: 67 : LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
[ + - ][ + - ]
[ + - ]
89 : : try {
90 [ + - ][ + - ]: 134 : socket_.reset(new udp::socket(io_service));
91 [ + + ]: 134 : socket_->assign(af == AF_INET6 ? udp::v6() : udp::v4(), fd);
92 [ # # ]: 0 : } catch (const std::exception& exception) {
93 : : // Whatever the thing throws, it is something from ASIO and we
94 : : // convert it
95 [ # # ][ # # ]: 0 : isc_throw(IOError, exception.what());
96 : : }
97 : 67 : }
98 : :
99 : : /*
100 : : * Copy constructor. Default one would probably do, but it is unnecessary
101 : : * to copy many of the member variables every time we fork to handle
102 : : * another packet.
103 : : *
104 : : * We also allocate data for receiving the packet here.
105 : : */
106 : : Data(const Data& other) :
107 : : io_(other.io_), socket_(other.socket_), done_(false),
108 : : checkin_callback_(other.checkin_callback_),
109 : : lookup_callback_(other.lookup_callback_),
110 [ + - ]: 74 : answer_callback_(other.answer_callback_)
111 : : {
112 : : // Instantiate the data buffer and endpoint that will
113 : : // be used by the asynchronous receive call.
114 [ + - ]: 74 : data_.reset(new char[MAX_LENGTH]);
115 [ + - ]: 74 : sender_.reset(new udp::endpoint());
116 : : }
117 : :
118 : : // The ASIO service object
119 : : asio::io_service& io_;
120 : :
121 : : // Class member variables which are dynamic, and changes to which
122 : : // need to accessible from both sides of a coroutine fork or from
123 : : // outside of the coroutine (i.e., from an asynchronous I/O call),
124 : : // should be declared here as pointers and allocated in the
125 : : // constructor or in the coroutine. This allows state information
126 : : // to persist when an individual copy of the coroutine falls out
127 : : // scope while waiting for an event, *so long as* there is another
128 : : // object that is referencing the same data. As a side-benefit, using
129 : : // pointers also reduces copy overhead for coroutine objects.
130 : : //
131 : : // Note: Currently these objects are allocated by "new" in the
132 : : // constructor, or in the function operator while processing a query.
133 : : // Repeated allocations from the heap for every incoming query is
134 : : // clearly a performance issue; this must be optimized in the future.
135 : : // The plan is to have a structure pre-allocate several "Data"
136 : : // objects which can be pulled off a free list and placed on an in-use
137 : : // list whenever a query comes in. This will serve the dual purpose
138 : : // of improving performance and guaranteeing that state information
139 : : // will *not* be destroyed when any one instance of the coroutine
140 : : // falls out of scope while waiting for an event.
141 : : //
142 : : // Socket used to for listen for queries. Created in the
143 : : // constructor and stored in a shared_ptr because socket objects
144 : : // are not copyable.
145 : : boost::shared_ptr<asio::ip::udp::socket> socket_;
146 : :
147 : : // The ASIO-internal endpoint object representing the client
148 : : std::auto_ptr<asio::ip::udp::endpoint> sender_;
149 : :
150 : : // \c IOMessage and \c Message objects to be passed to the
151 : : // DNS lookup and answer providers
152 : : std::auto_ptr<asiolink::IOMessage> io_message_;
153 : :
154 : : // The original query as sent by the client
155 : : isc::dns::MessagePtr query_message_;
156 : :
157 : : // The response message we are building
158 : : isc::dns::MessagePtr answer_message_;
159 : :
160 : : // The buffer into which the response is written
161 : : isc::util::OutputBufferPtr respbuf_;
162 : :
163 : : // The buffer into which the query packet is written
164 : : boost::shared_array<char> data_;
165 : :
166 : : // State information that is entirely internal to a given instance
167 : : // of the coroutine can be declared here.
168 : : size_t bytes_;
169 : : bool done_;
170 : :
171 : :
172 : : // Callback functions provided by the caller
173 : : const SimpleCallback* checkin_callback_;
174 : : const DNSLookup* lookup_callback_;
175 : : const DNSAnswer* answer_callback_;
176 : :
177 : : std::auto_ptr<IOEndpoint> peer_;
178 : : std::auto_ptr<IOSocket> iosock_;
179 : : };
180 : :
181 : : /// The following functions implement the \c UDPServer class.
182 : : ///
183 : : /// The constructor. It just creates new internal state object
184 : : /// and lets it handle the initialization.
185 : 68 : UDPServer::UDPServer(io_service& io_service, int fd, int af,
186 : : SimpleCallback* checkin, DNSLookup* lookup,
187 : : DNSAnswer* answer) :
188 [ + - ][ + + ]: 68 : data_(new Data(io_service, fd, af, checkin, lookup, answer))
[ + - ][ # # ]
189 : 67 : { }
190 : :
191 : : /// The function operator is implemented with the "stackless coroutine"
192 : : /// pattern; see internal/coroutine.h for details.
193 : : void
194 : 105 : UDPServer::operator()(asio::error_code ec, size_t length) {
195 : : /// Because the coroutine reentry block is implemented as
196 : : /// a switch statement, inline variable declarations are not
197 : : /// permitted. Certain variables used below can be declared here.
198 : :
199 [ - + + + : 105 : CORO_REENTER (this) {
- + - + ]
[ # # ]
200 [ + + ]: 26 : do {
201 : : /*
202 : : * This is preparation for receiving a packet. We get a new
203 : : * state object for the lifetime of the next packet to come.
204 : : * It allocates the buffers to receive data into.
205 : : */
206 [ + - ]: 74 : data_.reset(new Data(*data_));
207 : :
208 [ + - ][ + - ]: 13 : do {
[ - + ]
209 : : // Begin an asynchronous receive, then yield.
210 : : // When the receive event is posted, the coroutine
211 : : // will resume immediately after this point.
212 [ + - ]: 222 : CORO_YIELD data_->socket_->async_receive_from(
[ - - - + ]
[ # # ][ - + ]
213 : 74 : buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
214 [ + - ]: 222 : *this);
215 : :
216 : : // Abort on fatal errors
217 : : // TODO: add log
218 [ + + ]: 19 : if (ec) {
219 : : using namespace asio::error;
220 [ + - ][ + - ]: 6 : if (ec.value() != would_block && ec.value() != try_again &&
[ - + ][ - + ]
221 : : ec.value() != interrupted) {
222 : : return;
223 : : }
224 : : }
225 : :
226 : : } while (ec || length == 0);
227 : :
228 : 13 : data_->bytes_ = length;
229 : :
230 : : /*
231 : : * We fork the coroutine now. One (the child) will keep
232 : : * the current state and handle the packet, then die and
233 : : * drop ownership of the state. The other (parent) will just
234 : : * go into the loop again and replace the current state with
235 : : * a new one for a new object.
236 : : *
237 : : * Actually, both of the coroutines will be a copy of this
238 : : * one, but that's just internal implementation detail.
239 : : */
240 [ + + ][ + - ]: 26 : CORO_FORK data_->io_.post(UDPServer(*this));
[ + - ]
241 : 26 : } while (is_parent());
242 : :
243 : : // Create an \c IOMessage object to store the query.
244 : : //
245 : : // (XXX: It would be good to write a factory function
246 : : // that would quickly generate an IOMessage object without
247 : : // all these calls to "new".)
248 [ + - ]: 13 : data_->peer_.reset(new UDPEndpoint(*data_->sender_));
249 : :
250 : : // The UDP socket class has been extended with asynchronous functions
251 : : // and takes as a template parameter a completion callback class. As
252 : : // UDPServer does not use these extended functions (only those defined
253 : : // in the IOSocket base class) - but needs a UDPSocket to get hold of
254 : : // the underlying Boost UDP socket - DummyIOCallback is used. This
255 : : // provides the appropriate operator() but is otherwise functionless.
256 : 13 : data_->iosock_.reset(
257 [ + - ]: 26 : new UDPSocket<DummyIOCallback>(*data_->socket_));
258 : :
259 : 26 : data_->io_message_.reset(new IOMessage(data_->data_.get(),
260 [ + - ]: 13 : data_->bytes_, *data_->iosock_, *data_->peer_));
261 : :
262 : : // Perform any necessary operations prior to processing an incoming
263 : : // query (e.g., checking for queued configuration messages).
264 : : //
265 : : // (XXX: it may be a performance issue to check in for every single
266 : : // incoming query; we may wish to throttle this in the future.)
267 [ + + ]: 13 : if (data_->checkin_callback_ != NULL) {
268 [ + - ]: 9 : (*data_->checkin_callback_)(*data_->io_message_);
269 : : }
270 : :
271 : : // If we don't have a DNS Lookup provider, there's no point in
272 : : // continuing; we exit the coroutine permanently.
273 [ + + ]: 13 : if (data_->lookup_callback_ == NULL) {
274 [ + - ]: 4 : CORO_YIELD return;
[ - - - + ]
[ # # ][ # # ]
275 : : }
276 : :
277 : : // Instantiate objects that will be needed by the
278 : : // asynchronous DNS lookup and/or by the send call.
279 [ + - ][ + - ]: 9 : data_->respbuf_.reset(new OutputBuffer(0));
280 [ + - ][ + - ]: 9 : data_->query_message_.reset(new Message(Message::PARSE));
[ + - ]
281 [ + - ][ + - ]: 9 : data_->answer_message_.reset(new Message(Message::RENDER));
[ + - ]
282 : :
283 : : // Schedule a DNS lookup, and yield. When the lookup is
284 : : // finished, the coroutine will resume immediately after
285 : : // this point.
286 [ + - ]: 27 : CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
[ - - - + ]
[ # # ][ - + ]
287 : :
288 : : // The 'done_' flag indicates whether we have an answer
289 : : // to send back. If not, exit the coroutine permanently.
290 [ + + ]: 7 : if (!data_->done_) {
291 [ + - ]: 2 : CORO_YIELD return;
[ - - - + ]
[ # # ][ # # ]
292 : : }
293 : :
294 : : // Call the DNS answer provider to render the answer into
295 : : // wire format
296 : 15 : (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
297 [ + - ]: 15 : data_->answer_message_, data_->respbuf_);
298 : :
299 : : // Begin an asynchronous send, and then yield. When the
300 : : // send completes, we will resume immediately after this point
301 : : // (though we have nothing further to do, so the coroutine
302 : : // will simply exit at that time).
303 [ + - ]: 15 : CORO_YIELD data_->socket_->async_send_to(
[ - - - + ]
[ # # ][ - + ]
304 : 5 : buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
305 [ + - ]: 15 : *data_->sender_, *this);
306 : : }
307 : : }
308 : :
309 : : /// Call the DNS lookup provider. (Expected to be called by the
310 : : /// AsyncLookup<UDPServer> handler.)
311 : : void
312 : 9 : UDPServer::asyncLookup() {
313 : 18 : (*data_->lookup_callback_)(*data_->io_message_,
314 [ + - ]: 36 : data_->query_message_, data_->answer_message_, data_->respbuf_, this);
315 : 9 : }
316 : :
317 : : /// Stop the UDPServer
318 : : void
319 : 24 : UDPServer::stop() {
320 : : /// Using close instead of cancel, because cancel
321 : : /// will only cancel the asynchornized event already submitted
322 : : /// to io service, the events post to io service after
323 : : /// cancel still can be scheduled by io service, if
324 : : /// the socket is cloesed, all the asynchronized event
325 : : /// for it won't be scheduled by io service not matter it is
326 : : /// submit to io serice before or after close call. And we will
327 : : //. get bad_descriptor error
328 : 24 : data_->socket_->close();
329 : 24 : }
330 : :
331 : : /// Post this coroutine on the ASIO service queue so that it will
332 : : /// resume processing where it left off. The 'done' parameter indicates
333 : : /// whether there is an answer to return to the client.
334 : : void
335 : 9 : UDPServer::resume(const bool done) {
336 : 9 : data_->done_ = done;
337 [ + - ]: 9 : data_->io_.post(*this);
338 : 9 : }
339 : :
340 : : } // namespace asiodns
341 [ + - ][ + - ]: 9 : } // namespace isc
|