Branch data Line data Source code
1 : : // Copyright (C) 2012 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 <asio.hpp>
18 : : #include <asio/error.hpp>
19 : :
20 : : #include "sync_udp_server.h"
21 : : #include "logger.h"
22 : :
23 : : #include <asiolink/dummy_io_cb.h>
24 : : #include <asiolink/udp_endpoint.h>
25 : : #include <asiolink/udp_socket.h>
26 : :
27 : : #include <boost/bind.hpp>
28 : :
29 : : #include <sys/types.h>
30 : : #include <netinet/in.h>
31 : : #include <sys/socket.h>
32 : : #include <unistd.h> // for some IPC/network system calls
33 : : #include <errno.h>
34 : :
35 : : using namespace std;
36 : : using namespace isc::asiolink;
37 : :
38 : : namespace isc {
39 : : namespace asiodns {
40 : :
41 : 16 : SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
42 : : const int af, asiolink::SimpleCallback* checkin,
43 : : DNSLookup* lookup, DNSAnswer* answer) :
44 : : output_buffer_(new isc::util::OutputBuffer(0)),
45 [ + - ]: 16 : query_(new isc::dns::Message(isc::dns::Message::PARSE)),
46 [ + - ]: 16 : answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
47 : : io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
48 [ + - ][ + - ]: 64 : answer_callback_(answer), stopped_(false)
[ + - ][ + - ]
[ + - ]
49 : : {
50 [ + + ]: 16 : if (af != AF_INET && af != AF_INET6) {
51 [ + - ][ + - ]: 2 : isc_throw(InvalidParameter, "Address family must be either AF_INET "
[ + - ]
52 : : "or AF_INET6, not " << af);
53 : : }
54 [ + - ][ + - ]: 15 : LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
[ + - ][ + - ]
[ + - ]
55 : : try {
56 [ + - ]: 15 : socket_.reset(new asio::ip::udp::socket(io_service));
57 : 15 : socket_->assign(af == AF_INET6 ? asio::ip::udp::v6() :
58 [ + - ]: 15 : asio::ip::udp::v4(), fd);
59 [ # # ]: 0 : } catch (const std::exception& exception) {
60 : : // Whatever the thing throws, it is something from ASIO and we
61 : : // convert it
62 [ # # ][ # # ]: 0 : isc_throw(IOError, exception.what());
63 : : }
64 : 15 : }
65 : :
66 : : void
67 : 11 : SyncUDPServer::scheduleRead() {
68 : 11 : socket_->async_receive_from(asio::buffer(data_, MAX_LENGTH), sender_,
69 : : boost::bind(&SyncUDPServer::handleRead, this,
70 : 11 : _1, _2));
71 : 11 : }
72 : :
73 : : void
74 : 11 : SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
75 : : // Abort on fatal errors
76 [ + + ]: 11 : if (ec) {
77 : : using namespace asio::error;
78 [ + - ][ + - ]: 3 : if (ec.value() != would_block && ec.value() != try_again &&
[ - + ][ - + ]
79 : : ec.value() != interrupted) {
80 : : return;
81 : : }
82 : : }
83 : : // Some kind of interrupt, spurious wakeup, or like that. Just try reading
84 : : // again.
85 [ + - ][ + - ]: 8 : if (ec || length == 0) {
[ - + ]
86 : 0 : scheduleRead();
87 : 0 : return;
88 : : }
89 : : // OK, we have a real packet of data. Let's dig into it!
90 : :
91 : : // XXX: This is taken (and ported) from UDPSocket class. What the hell does
92 : : // it really mean?
93 : :
94 : : // The UDP socket class has been extended with asynchronous functions
95 : : // and takes as a template parameter a completion callback class. As
96 : : // UDPServer does not use these extended functions (only those defined
97 : : // in the IOSocket base class) - but needs a UDPSocket to get hold of
98 : : // the underlying Boost UDP socket - DummyIOCallback is used. This
99 : : // provides the appropriate operator() but is otherwise functionless.
100 : 11 : UDPSocket<DummyIOCallback> socket(*socket_);
101 : 8 : UDPEndpoint endpoint(sender_);
102 : 8 : IOMessage message(data_, length, socket, endpoint);
103 [ + + ]: 8 : if (checkin_callback_ != NULL) {
104 [ + - ]: 6 : (*checkin_callback_)(message);
105 [ + + ]: 6 : if (stopped_) {
106 : : return;
107 : : }
108 : : }
109 : :
110 : : // If we don't have a DNS Lookup provider, there's no point in
111 : : // continuing; we exit the coroutine permanently.
112 [ - + ]: 7 : if (lookup_callback_ == NULL) {
113 [ # # ]: 0 : scheduleRead();
114 : : return;
115 : : }
116 : :
117 : : // Make sure the buffers are fresh
118 : 7 : output_buffer_->clear();
119 [ + - ]: 7 : query_->clear(isc::dns::Message::PARSE);
120 [ + - ]: 7 : answer_->clear(isc::dns::Message::RENDER);
121 : :
122 : : // Mark that we don't have an answer yet.
123 : 7 : done_ = false;
124 : 7 : resume_called_ = false;
125 : :
126 : : // Call the actual lookup
127 [ + - ]: 7 : (*lookup_callback_)(message, query_, answer_, output_buffer_, this);
128 : :
129 [ + + ]: 7 : if (!resume_called_) {
130 [ + - ][ + - ]: 2 : isc_throw(isc::Unexpected,
131 : : "No resume called from the lookup callback");
132 : : }
133 : :
134 [ + + ]: 6 : if (stopped_) {
135 : : return;
136 : : }
137 : :
138 [ + + ]: 4 : if (done_) {
139 : : // Good, there's an answer.
140 : : // Call the answer callback to render it.
141 [ + - ]: 3 : (*answer_callback_)(message, query_, answer_, output_buffer_);
142 : :
143 [ + + ]: 3 : if (stopped_) {
144 : : return;
145 : : }
146 : :
147 : : socket_->send_to(asio::buffer(output_buffer_->getData(),
148 : 2 : output_buffer_->getLength()),
149 [ + - ]: 2 : sender_);
150 : : }
151 : :
152 : : // And schedule handling another socket.
153 [ + - ]: 3 : scheduleRead();
154 : : }
155 : :
156 : : void
157 : 8 : SyncUDPServer::operator()(asio::error_code, size_t) {
158 : : // To start the server, we just schedule reading of data when they
159 : : // arrive.
160 : 8 : scheduleRead();
161 : 8 : }
162 : :
163 : : /// Stop the UDPServer
164 : : void
165 : 26 : SyncUDPServer::stop() {
166 : : /// Using close instead of cancel, because cancel
167 : : /// will only cancel the asynchornized event already submitted
168 : : /// to io service, the events post to io service after
169 : : /// cancel still can be scheduled by io service, if
170 : : /// the socket is cloesed, all the asynchronized event
171 : : /// for it won't be scheduled by io service not matter it is
172 : : /// submit to io serice before or after close call. And we will
173 : : //. get bad_descriptor error
174 : 26 : socket_->close();
175 : 26 : stopped_ = true;
176 : 26 : }
177 : :
178 : : /// Post this coroutine on the ASIO service queue so that it will
179 : : /// resume processing where it left off. The 'done' parameter indicates
180 : : /// whether there is an answer to return to the client.
181 : : void
182 : 6 : SyncUDPServer::resume(const bool done) {
183 : 6 : resume_called_ = true;
184 : 6 : done_ = done;
185 : 6 : }
186 : :
187 : : bool
188 : 0 : SyncUDPServer::hasAnswer() {
189 : 0 : return (done_);
190 : : }
191 : :
192 : : } // namespace asiodns
193 [ + - ][ + - ]: 9 : } // namespace isc
|