2011-02-14 12:56:49 +01:00
|
|
|
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
2010-09-30 06:40:35 +00:00
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice appear in all copies.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2011-02-21 15:11:07 +00:00
|
|
|
#include <netinet/in.h>
|
2011-02-22 14:27:51 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h> // for some IPC/network system calls
|
2011-03-04 17:28:56 +01:00
|
|
|
#include <errno.h>
|
2011-02-14 21:41:07 +01:00
|
|
|
|
2011-02-22 14:27:51 +00:00
|
|
|
#include <boost/shared_array.hpp>
|
2010-09-30 06:40:35 +00:00
|
|
|
|
2011-02-14 12:56:49 +01:00
|
|
|
#include <log/dummylog.h>
|
2010-09-30 06:40:35 +00:00
|
|
|
|
2011-02-22 14:27:51 +00:00
|
|
|
#include <asio.hpp>
|
2011-02-21 17:58:12 +00:00
|
|
|
#include <asiolink/dummy_io_cb.h>
|
2011-02-14 12:56:49 +01:00
|
|
|
#include <asiolink/tcp_endpoint.h>
|
|
|
|
#include <asiolink/tcp_socket.h>
|
|
|
|
#include <asiolink/tcp_server.h>
|
2010-09-30 06:40:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
using namespace asio;
|
|
|
|
using asio::ip::udp;
|
|
|
|
using asio::ip::tcp;
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace isc::dns;
|
|
|
|
|
|
|
|
namespace asiolink {
|
2011-02-14 12:56:49 +01:00
|
|
|
|
|
|
|
/// The following functions implement the \c TCPServer class.
|
2010-10-05 05:47:44 +00:00
|
|
|
///
|
|
|
|
/// The constructor
|
2010-09-30 06:40:35 +00:00
|
|
|
TCPServer::TCPServer(io_service& io_service,
|
|
|
|
const ip::address& addr, const uint16_t port,
|
2010-10-05 05:47:44 +00:00
|
|
|
const SimpleCallback* checkin,
|
2010-09-30 06:40:49 +00:00
|
|
|
const DNSLookup* lookup,
|
|
|
|
const DNSAnswer* answer) :
|
2011-02-23 10:22:22 +08:00
|
|
|
io_(io_service), done_(false), stopped_by_hand_(false),
|
2010-09-30 06:40:49 +00:00
|
|
|
checkin_callback_(checkin), lookup_callback_(lookup),
|
|
|
|
answer_callback_(answer)
|
2010-09-30 06:40:35 +00:00
|
|
|
{
|
|
|
|
tcp::endpoint endpoint(addr, port);
|
|
|
|
acceptor_.reset(new tcp::acceptor(io_service));
|
|
|
|
acceptor_->open(endpoint.protocol());
|
2010-09-30 06:40:42 +00:00
|
|
|
// Set v6-only (we use a separate instantiation for v4,
|
2010-09-30 06:40:35 +00:00
|
|
|
// otherwise asio will bind to both v4 and v6
|
|
|
|
if (addr.is_v6()) {
|
|
|
|
acceptor_->set_option(ip::v6_only(true));
|
|
|
|
}
|
|
|
|
acceptor_->set_option(tcp::acceptor::reuse_address(true));
|
|
|
|
acceptor_->bind(endpoint);
|
|
|
|
acceptor_->listen();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TCPServer::operator()(error_code ec, size_t length) {
|
2011-03-04 23:31:16 -08:00
|
|
|
/// Because the coroutine reentry block is implemented as
|
2010-10-05 05:47:44 +00:00
|
|
|
/// a switch statement, inline variable declarations are not
|
|
|
|
/// permitted. Certain variables used below can be declared here.
|
2011-02-23 16:08:18 +01:00
|
|
|
|
2011-02-23 10:22:22 +08:00
|
|
|
/// If user has stopped the server, we won't enter the
|
2011-02-23 16:08:18 +01:00
|
|
|
/// coroutine body, just return
|
2011-02-23 10:22:22 +08:00
|
|
|
if (stopped_by_hand_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-30 06:40:49 +00:00
|
|
|
boost::array<const_buffer,2> bufs;
|
2010-11-03 08:42:10 +00:00
|
|
|
OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
|
2010-10-05 05:47:44 +00:00
|
|
|
|
2010-09-30 06:40:35 +00:00
|
|
|
CORO_REENTER (this) {
|
|
|
|
do {
|
2010-10-05 05:47:44 +00:00
|
|
|
/// Create a socket to listen for connections
|
2010-09-30 06:40:35 +00:00
|
|
|
socket_.reset(new tcp::socket(acceptor_->get_io_service()));
|
2010-10-05 05:47:44 +00:00
|
|
|
|
2011-03-04 17:28:56 +01:00
|
|
|
/// Wait for new connections. In the event of non-fatal error,
|
2010-10-11 18:42:55 +00:00
|
|
|
/// try again
|
|
|
|
do {
|
|
|
|
CORO_YIELD acceptor_->async_accept(*socket_, *this);
|
2011-03-04 17:28:56 +01:00
|
|
|
// Abort on fatal errors
|
|
|
|
// TODO: Log error?
|
|
|
|
if (ec) {
|
2011-03-08 21:17:34 +01:00
|
|
|
using namespace asio::error;
|
|
|
|
if (ec.value() != would_block && ec.value() != try_again &&
|
|
|
|
ec.value() != connection_aborted &&
|
|
|
|
ec.value() != interrupted) {
|
2011-03-04 17:28:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (ec);
|
2010-10-05 05:47:44 +00:00
|
|
|
|
|
|
|
/// Fork the coroutine by creating a copy of this one and
|
|
|
|
/// scheduling it on the ASIO service queue. The parent
|
|
|
|
/// will continue listening for DNS connections while the
|
|
|
|
/// handles the one that has just arrived.
|
2010-09-30 06:40:49 +00:00
|
|
|
CORO_FORK io_.post(TCPServer(*this));
|
|
|
|
} while (is_parent());
|
2010-09-30 06:40:35 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
/// Instantiate the data buffer that will be used by the
|
|
|
|
/// asynchronous read call.
|
2010-10-27 15:36:24 +00:00
|
|
|
data_.reset(new char[MAX_LENGTH]);
|
2010-09-30 06:40:49 +00:00
|
|
|
|
2010-10-11 18:42:55 +00:00
|
|
|
/// Read the message, in two parts. First, the message length:
|
2010-09-30 06:40:35 +00:00
|
|
|
CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
|
2010-09-30 06:40:49 +00:00
|
|
|
TCP_MESSAGE_LENGTHSIZE), *this);
|
2010-10-11 18:42:55 +00:00
|
|
|
if (ec) {
|
2011-03-08 14:42:54 +01:00
|
|
|
socket_->close();
|
2010-10-11 18:42:55 +00:00
|
|
|
CORO_YIELD return;
|
|
|
|
}
|
2010-09-30 06:40:35 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
/// Now read the message itself. (This is done in a different scope
|
|
|
|
/// to allow inline variable declarations.)
|
2010-09-30 06:40:35 +00:00
|
|
|
CORO_YIELD {
|
2011-03-04 15:32:43 -08:00
|
|
|
InputBuffer dnsbuffer(data_.get(), length);
|
2010-09-30 06:40:35 +00:00
|
|
|
uint16_t msglen = dnsbuffer.readUint16();
|
|
|
|
async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
|
|
|
|
}
|
|
|
|
|
2010-10-11 18:42:55 +00:00
|
|
|
if (ec) {
|
2011-03-08 14:42:54 +01:00
|
|
|
socket_->close();
|
2010-10-11 18:42:55 +00:00
|
|
|
CORO_YIELD return;
|
|
|
|
}
|
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// Create an \c IOMessage object to store the query.
|
2010-10-09 04:33:04 +00:00
|
|
|
//
|
|
|
|
// (XXX: It would be good to write a factory function
|
|
|
|
// that would quickly generate an IOMessage object without
|
|
|
|
// all these calls to "new".)
|
2010-10-27 15:36:24 +00:00
|
|
|
peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
|
2011-02-18 14:31:20 +00:00
|
|
|
|
|
|
|
// The TCP socket class has been extended with asynchronous functions
|
|
|
|
// and takes as a template parameter a completion callback class. As
|
|
|
|
// TCPServer does not use these extended functions (only those defined
|
|
|
|
// in the IOSocket base class) - but needs a TCPSocket to get hold of
|
2011-02-21 17:58:12 +00:00
|
|
|
// the underlying Boost TCP socket - DummyIOCallback is used. This
|
|
|
|
// provides the appropriate operator() but is otherwise functionless.
|
|
|
|
iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
|
2010-10-27 15:36:24 +00:00
|
|
|
io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
|
2010-10-05 05:47:44 +00:00
|
|
|
bytes_ = length;
|
2010-09-30 06:40:35 +00:00
|
|
|
|
2010-09-30 06:40:49 +00:00
|
|
|
// Perform any necessary operations prior to processing the incoming
|
|
|
|
// packet (e.g., checking for queued configuration messages).
|
|
|
|
//
|
|
|
|
// (XXX: it may be a performance issue to have this called for
|
|
|
|
// every single incoming packet; we may wish to throttle it somehow
|
|
|
|
// in the future.)
|
|
|
|
if (checkin_callback_ != NULL) {
|
|
|
|
(*checkin_callback_)(*io_message_);
|
2010-09-30 06:40:42 +00:00
|
|
|
}
|
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// If we don't have a DNS Lookup provider, there's no point in
|
|
|
|
// continuing; we exit the coroutine permanently.
|
2010-09-30 06:40:49 +00:00
|
|
|
if (lookup_callback_ == NULL) {
|
2011-03-08 14:42:54 +01:00
|
|
|
socket_->close();
|
2010-09-30 06:40:35 +00:00
|
|
|
CORO_YIELD return;
|
|
|
|
}
|
|
|
|
|
2010-09-30 06:40:49 +00:00
|
|
|
// Reset or instantiate objects that will be needed by the
|
|
|
|
// DNS lookup and the write call.
|
2010-10-09 04:33:04 +00:00
|
|
|
respbuf_.reset(new OutputBuffer(0));
|
2011-01-18 12:24:00 +01:00
|
|
|
query_message_.reset(new Message(Message::PARSE));
|
2011-01-18 11:10:00 +01:00
|
|
|
answer_message_.reset(new Message(Message::RENDER));
|
2010-09-30 06:40:49 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// Schedule a DNS lookup, and yield. When the lookup is
|
|
|
|
// finished, the coroutine will resume immediately after
|
|
|
|
// this point.
|
|
|
|
CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
|
2010-09-30 06:40:49 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// The 'done_' flag indicates whether we have an answer
|
|
|
|
// to send back. If not, exit the coroutine permanently.
|
2010-09-30 06:40:49 +00:00
|
|
|
if (!done_) {
|
2011-03-07 17:40:02 +01:00
|
|
|
// TODO: should we keep the connection open for a short time
|
|
|
|
// to see if new requests come in?
|
|
|
|
socket_->close();
|
2010-09-30 06:40:49 +00:00
|
|
|
CORO_YIELD return;
|
2010-09-30 06:40:42 +00:00
|
|
|
}
|
2010-09-30 06:40:49 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// Call the DNS answer provider to render the answer into
|
|
|
|
// wire format
|
2011-01-18 12:24:00 +01:00
|
|
|
(*answer_callback_)(*io_message_, query_message_,
|
|
|
|
answer_message_, respbuf_);
|
2010-10-03 06:44:36 +00:00
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
// Set up the response, beginning with two length bytes.
|
2010-11-03 08:42:10 +00:00
|
|
|
lenbuf.writeUint16(respbuf_->getLength());
|
|
|
|
bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
|
2010-09-30 06:40:49 +00:00
|
|
|
bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
|
2010-10-05 05:47:44 +00:00
|
|
|
|
|
|
|
// Begin an asynchronous send, and then yield. When the
|
|
|
|
// send completes, we will resume immediately after this point
|
|
|
|
// (though we have nothing further to do, so the coroutine
|
|
|
|
// will simply exit at that time).
|
2010-09-30 06:40:49 +00:00
|
|
|
CORO_YIELD async_write(*socket_, bufs, *this);
|
2011-03-14 10:50:32 -05:00
|
|
|
|
2011-03-07 17:40:02 +01:00
|
|
|
// TODO: should we keep the connection open for a short time
|
|
|
|
// to see if new requests come in?
|
|
|
|
socket_->close();
|
2010-09-30 06:40:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-05 05:47:44 +00:00
|
|
|
/// Call the DNS lookup provider. (Expected to be called by the
|
|
|
|
/// AsyncLookup<TCPServer> handler.)
|
2010-09-30 06:40:49 +00:00
|
|
|
void
|
2010-10-05 05:47:44 +00:00
|
|
|
TCPServer::asyncLookup() {
|
2011-01-18 12:24:00 +01:00
|
|
|
(*lookup_callback_)(*io_message_, query_message_,
|
|
|
|
answer_message_, respbuf_, this);
|
2010-09-30 06:40:49 +00:00
|
|
|
}
|
|
|
|
|
2011-02-23 10:22:22 +08:00
|
|
|
void TCPServer::stop() {
|
2011-03-04 23:31:16 -08:00
|
|
|
// server should not be stopped twice
|
|
|
|
if (stopped_by_hand_) {
|
2011-02-24 09:53:36 +08:00
|
|
|
return;
|
2011-03-04 23:31:16 -08:00
|
|
|
}
|
2011-02-24 09:53:36 +08:00
|
|
|
|
2011-02-23 10:22:22 +08:00
|
|
|
stopped_by_hand_ = true;
|
|
|
|
acceptor_->close();
|
|
|
|
socket_->close();
|
|
|
|
}
|
2010-10-05 05:47:44 +00:00
|
|
|
/// Post this coroutine on the ASIO service queue so that it will
|
|
|
|
/// resume processing where it left off. The 'done' parameter indicates
|
|
|
|
/// whether there is an answer to return to the client.
|
2010-09-30 06:40:49 +00:00
|
|
|
void
|
2010-10-03 06:44:36 +00:00
|
|
|
TCPServer::resume(const bool done) {
|
|
|
|
done_ = done;
|
2010-09-30 06:40:49 +00:00
|
|
|
io_.post(*this);
|
|
|
|
}
|
|
|
|
|
2011-02-14 12:56:49 +01:00
|
|
|
} // namespace asiolink
|
|
|
|
|