From 816a8d3e8bfcef7ce8c25b9d63b9594c0beaf7c1 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 30 Sep 2010 06:40:35 +0000 Subject: [PATCH] More refactoring. TCP- and UDP-specific classes used by asiolink are now defined in tcpdns.cc and udpdns.cc respectively. Their associated header files are in the "internal" subdirectory (which is to be used for include files that define internal-only API that isn't intended to be exposed publicly). coroutine.h has also been moved there. Eliminated yield.h; the reenter, fork and yield psuedo-keywords are gone in favor of CORO_REENTER, CORO_FORK and CORO_YIELD. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac327@3082 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/asiolink/asiolink.cc | 321 +------------------ src/lib/asiolink/{ => internal}/coroutine.h | 2 +- src/lib/asiolink/internal/tcpdns.h | 121 +++++++ src/lib/asiolink/internal/udpdns.h | 113 +++++++ src/lib/asiolink/tcpdns.cc | 164 ++++++++++ src/lib/asiolink/tests/Makefile.am | 6 + src/lib/asiolink/tests/asio_link_unittest.cc | 2 + src/lib/asiolink/udpdns.cc | 152 +++++++++ src/lib/asiolink/unyield.h | 21 -- src/lib/asiolink/yield.h | 23 -- 10 files changed, 562 insertions(+), 363 deletions(-) rename src/lib/asiolink/{ => internal}/coroutine.h (100%) create mode 100644 src/lib/asiolink/internal/tcpdns.h create mode 100644 src/lib/asiolink/internal/udpdns.h create mode 100644 src/lib/asiolink/tcpdns.cc create mode 100644 src/lib/asiolink/udpdns.cc delete mode 100644 src/lib/asiolink/unyield.h delete mode 100644 src/lib/asiolink/yield.h diff --git a/src/lib/asiolink/asiolink.cc b/src/lib/asiolink/asiolink.cc index da711c318..8dafcf382 100644 --- a/src/lib/asiolink/asiolink.cc +++ b/src/lib/asiolink/asiolink.cc @@ -30,10 +30,9 @@ #include #include -#include - -#include "coroutine.h" -#include "yield.h" +#include +#include +#include using namespace asio; using asio::ip::udp; @@ -74,71 +73,6 @@ IOAddress::getFamily() const { } } -// Note: this implementation is optimized for the case where this object -// is created from an ASIO endpoint object in a receiving code path -// by avoiding to make a copy of the base endpoint. For TCP it may not be -// a big deal, but when we receive UDP packets at a high rate, the copy -// overhead might be significant. -class TCPEndpoint : public IOEndpoint { -public: - TCPEndpoint(const IOAddress& address, const unsigned short port) : - asio_endpoint_placeholder_( - new tcp::endpoint(ip::address::from_string(address.toText()), - port)), - asio_endpoint_(*asio_endpoint_placeholder_) - {} - TCPEndpoint(const tcp::endpoint& asio_endpoint) : - asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) - {} - - ~TCPEndpoint() { delete asio_endpoint_placeholder_; } - virtual IOAddress getAddress() const { - return (asio_endpoint_.address()); - } - virtual uint16_t getPort() const { - return (asio_endpoint_.port()); - } - virtual short getProtocol() const { - return (asio_endpoint_.protocol().protocol()); - } - virtual short getFamily() const { - return (asio_endpoint_.protocol().family()); - } -private: - const tcp::endpoint* asio_endpoint_placeholder_; - const tcp::endpoint& asio_endpoint_; -}; - -class UDPEndpoint : public IOEndpoint { -public: - UDPEndpoint(const IOAddress& address, const unsigned short port) : - asio_endpoint_placeholder_( - new udp::endpoint(ip::address::from_string(address.toText()), - port)), - asio_endpoint_(*asio_endpoint_placeholder_) - {} - UDPEndpoint(const udp::endpoint& asio_endpoint) : - asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) - {} - - ~UDPEndpoint() { delete asio_endpoint_placeholder_; } - virtual IOAddress getAddress() const { - return (asio_endpoint_.address()); - } - virtual uint16_t getPort() const { - return (asio_endpoint_.port()); - } - virtual short getProtocol() const { - return (asio_endpoint_.protocol().protocol()); - } - virtual short getFamily() const { - return (asio_endpoint_.protocol().family()); - } -private: - const udp::endpoint* asio_endpoint_placeholder_; - const udp::endpoint& asio_endpoint_; -}; - const IOEndpoint* IOEndpoint::create(const int protocol, const IOAddress& address, const unsigned short port) @@ -153,261 +87,12 @@ IOEndpoint::create(const int protocol, const IOAddress& address, protocol); } -class TCPSocket : public IOSocket { -private: - TCPSocket(const TCPSocket& source); - TCPSocket& operator=(const TCPSocket& source); -public: - TCPSocket(tcp::socket& socket) : socket_(socket) {} - virtual int getNative() const { return (socket_.native()); } - virtual int getProtocol() const { return (IPPROTO_TCP); } -private: - tcp::socket& socket_; -}; - -class UDPSocket : public IOSocket { -private: - UDPSocket(const UDPSocket& source); - UDPSocket& operator=(const UDPSocket& source); -public: - UDPSocket(udp::socket& socket) : socket_(socket) {} - virtual int getNative() const { return (socket_.native()); } - virtual int getProtocol() const { return (IPPROTO_UDP); } -private: - udp::socket& socket_; -}; - IOMessage::IOMessage(const void* data, const size_t data_size, IOSocket& io_socket, const IOEndpoint& remote_endpoint) : data_(data), data_size_(data_size), io_socket_(io_socket), remote_endpoint_(remote_endpoint) {} -// -// Asynchronous TCP server coroutine -// -class TCPServer : public coroutine { -public: - explicit TCPServer(io_service& io_service, - const ip::address& addr, const uint16_t port, - CheckinProvider* checkin = NULL, - DNSProvider* process = NULL) : - checkin_callback_(checkin), dns_callback_(process) - { - - tcp::endpoint endpoint(addr, port); - acceptor_.reset(new tcp::acceptor(io_service)); - acceptor_->open(endpoint.protocol()); - // Set v6-only (we use a different instantiation for v4, - // 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 operator()(error_code ec = error_code(), size_t length = 0) { - if (ec) { - return; - } - - reenter (this) { - do { - socket_.reset(new tcp::socket(acceptor_->get_io_service())); - yield acceptor_->async_accept(*socket_, *this); - fork TCPServer(*this)(); - } while (is_parent()); - - // Perform any necessary operations prior to processing - // an incoming packet (e.g., checking for queued - // configuration messages). - if (checkin_callback_ != NULL) { - (*checkin_callback_)(); - } - - // Instantiate the data buffer that will be used by the - // asynchronous read calls. - // data_.reset(new boost::array); - data_ = boost::shared_ptr(new char[MAX_LENGTH]); - - yield async_read(*socket_, - asio::buffer(data_.get(), TCP_MESSAGE_LENGTHSIZE), - *this); - - yield { - InputBuffer dnsbuffer((const void *) data_.get(), length); - uint16_t msglen = dnsbuffer.readUint16(); - async_read(*socket_, asio::buffer(data_.get(), msglen), *this); - } - - // Stop here if we don't have a DNS callback function - if (dns_callback_ == NULL) { - yield return; - } - - // Instantiate the objects that will be used by the - // asynchronous write calls. - dns_message_.reset(new Message(Message::PARSE)); - response_.reset(new OutputBuffer(0)); - responselen_buffer_.reset(new OutputBuffer(TCP_MESSAGE_LENGTHSIZE)); - renderer_.reset(new MessageRenderer(*response_)); - io_socket_.reset(new TCPSocket(*socket_)); - io_endpoint_.reset(new TCPEndpoint(socket_->remote_endpoint())); - io_message_.reset(new IOMessage(data_.get(), length, *io_socket_, - *io_endpoint_)); - - // Process the DNS message - if (! (*dns_callback_)(*io_message_, *dns_message_, *renderer_)) { - yield return; - } - - responselen_buffer_->writeUint16(response_->getLength()); - yield async_write(*socket_, - asio::buffer(responselen_buffer_->getData(), - responselen_buffer_->getLength()), - *this); - yield async_write(*socket_, - asio::buffer(response_->getData(), - response_->getLength()), - *this); - } - } - -private: - enum { MAX_LENGTH = 65535 }; - static const size_t TCP_MESSAGE_LENGTHSIZE = 2; - - // Class member variables which are dynamic, and changes to which - // are expected to be accessible from both sides of a coroutine fork, - // should be declared here as shared pointers and allocated in the - // constructor or in the coroutine itself. (Forking a new coroutine - // causes class members to be copied, not referenced, so without using - // this approach, when a variable is changed by a "parent" coroutine - // the change might not be visible to the "child". Using shared_ptr<> - // ensures that when all coroutines using this data are deleted, the - // memory will be freed.) - boost::shared_ptr acceptor_; - boost::shared_ptr socket_; - boost::shared_ptr response_; - boost::shared_ptr responselen_buffer_; - boost::shared_ptr renderer_; - boost::shared_ptr dns_message_; - boost::shared_ptr io_message_; - boost::shared_ptr io_socket_; - boost::shared_ptr io_endpoint_; - boost::shared_ptr data_; - - // Callbacks - const CheckinProvider* checkin_callback_; - const DNSProvider* dns_callback_; -}; - -// -// Asynchronous UDP server coroutine -// -class UDPServer : public coroutine { -public: - explicit UDPServer(io_service& io_service, - const ip::address& addr, const uint16_t port, - CheckinProvider* checkin = NULL, - DNSProvider* process = NULL) : - checkin_callback_(checkin), dns_callback_(process) - { - // Wwe use a different instantiation for v4, - // otherwise asio will bind to both v4 and v6 - if (addr.is_v6()) { - socket_.reset(new udp::socket(io_service, udp::v6())); - socket_->set_option(socket_base::reuse_address(true)); - socket_->set_option(asio::ip::v6_only(true)); - socket_->bind(udp::endpoint(udp::v6(), port)); - } else { - socket_.reset(new udp::socket(io_service, udp::v4())); - socket_->set_option(socket_base::reuse_address(true)); - socket_->bind(udp::endpoint(udp::v6(), port)); - } - } - - void operator()(error_code ec = error_code(), size_t length = 0) { - reenter (this) for (;;) { - // Instantiate the data buffer that will be used by the - // asynchronous read calls. - // data_.reset(new boost::array); - data_ = boost::shared_ptr(new char[MAX_LENGTH]); - sender_.reset(new udp::endpoint()); - - do { - yield socket_->async_receive_from(asio::buffer(data_.get(), - MAX_LENGTH), - *sender_, *this); - } while (ec || length == 0); - - bytes_ = length; - fork UDPServer(*this)(); - if (is_parent()) { - continue; - } - - // Perform any necessary operations prior to processing - // an incoming packet (e.g., checking for queued - // configuration messages). - if (checkin_callback_ != NULL) { - (*checkin_callback_)(); - } - - // Stop here if we don't have a DNS callback function - if (dns_callback_ == NULL) { - yield return; - } - - // Instantiate the objects that will be used by the - // asynchronous write calls. - dns_message_.reset(new Message(Message::PARSE)); - response_.reset(new OutputBuffer(0)); - renderer_.reset(new MessageRenderer(*response_)); - io_socket_.reset(new UDPSocket(*socket_)); - io_endpoint_.reset(new UDPEndpoint(*sender_)); - io_message_.reset(new IOMessage(data_.get(), bytes_, - *io_socket_, - *io_endpoint_)); - - // Process the DNS message - if (! (*dns_callback_)(*io_message_, *dns_message_, *renderer_)) - { - yield return; - } - - yield socket_->async_send_to(asio::buffer(response_->getData(), - response_->getLength()), - *sender_, *this); - } - } - -private: - enum { MAX_LENGTH = 4096 }; - - // As mentioned in the comments to TCPServer, class member variables - // which are dynamic, and changes to which are expected to be - // accessible from both sides of a coroutine fork, should be - // declared here as shared pointers and allocated in the - // constructor or in the coroutine. - boost::shared_ptr socket_; - boost::shared_ptr sender_; - boost::shared_ptr io_endpoint_; - boost::shared_ptr response_; - boost::shared_ptr renderer_; - boost::shared_ptr dns_message_; - boost::shared_ptr io_message_; - boost::shared_ptr io_socket_; - boost::shared_ptr data_; - size_t bytes_; - - // Callbacks - const CheckinProvider* checkin_callback_; - const DNSProvider* dns_callback_; -}; - class IOServiceImpl { public: IOServiceImpl(const char& port, diff --git a/src/lib/asiolink/coroutine.h b/src/lib/asiolink/internal/coroutine.h similarity index 100% rename from src/lib/asiolink/coroutine.h rename to src/lib/asiolink/internal/coroutine.h index c290eadf2..fd1841d2b 100644 --- a/src/lib/asiolink/coroutine.h +++ b/src/lib/asiolink/internal/coroutine.h @@ -75,5 +75,5 @@ private: break; \ } \ else - #endif // COROUTINE_HPP + diff --git a/src/lib/asiolink/internal/tcpdns.h b/src/lib/asiolink/internal/tcpdns.h new file mode 100644 index 000000000..f4a4d5799 --- /dev/null +++ b/src/lib/asiolink/internal/tcpdns.h @@ -0,0 +1,121 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// 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. + +// $Id$ + +#ifndef __TCPDNS_H +#define __TCPDNS_H 1 + +#include + + +#include +#include + +#include +#include +#include + +#include +#include + +namespace asiolink { +// Note: this implementation is optimized for the case where this object +// is created from an ASIO endpoint object in a receiving code path +// by avoiding to make a copy of the base endpoint. For TCP it may not be +// a big deal, but when we receive UDP packets at a high rate, the copy +// overhead might be significant. +class TCPEndpoint : public IOEndpoint { +public: + TCPEndpoint(const IOAddress& address, const unsigned short port) : + asio_endpoint_placeholder_( + new asio::ip::tcp::endpoint( + asio::ip::address::from_string(address.toText()), port)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + TCPEndpoint(const asio::ip::tcp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) + {} + + ~TCPEndpoint() { delete asio_endpoint_placeholder_; } + virtual IOAddress getAddress() const; + virtual uint16_t getPort() const; + virtual short getProtocol() const; + virtual short getFamily() const; +private: + const asio::ip::tcp::endpoint* asio_endpoint_placeholder_; + const asio::ip::tcp::endpoint& asio_endpoint_; +}; + +class TCPSocket : public IOSocket { +private: + TCPSocket(const TCPSocket& source); + TCPSocket& operator=(const TCPSocket& source); +public: + TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {} + virtual int getNative() const; + virtual int getProtocol() const; +private: + asio::ip::tcp::socket& socket_; +}; + +// +// Asynchronous TCP server coroutine +// +class TCPServer : public coroutine { +public: + explicit TCPServer(asio::io_service& io_service, + const asio::ip::address& addr, const uint16_t port, + CheckinProvider* checkin = NULL, + DNSProvider* process = NULL); + + void operator()(asio::error_code ec = asio::error_code(), + size_t length = 0); + +private: + enum { MAX_LENGTH = 65535 }; + static const size_t TCP_MESSAGE_LENGTHSIZE = 2; + + // Class member variables which are dynamic, and changes to which + // are expected to be accessible from both sides of a coroutine fork, + // should be declared here as shared pointers and allocated in the + // constructor or in the coroutine itself. (Forking a new coroutine + // causes class members to be copied, not referenced, so without using + // this approach, when a variable is changed by a "parent" coroutine + // the change might not be visible to the "child". Using shared_ptr<> + // ensures that when all coroutines using this data are deleted, the + // memory will be freed.) + boost::shared_ptr acceptor_; + boost::shared_ptr socket_; + boost::shared_ptr response_; + boost::shared_ptr lenbuf_; + boost::shared_ptr renderer_; + boost::shared_ptr dns_message_; + boost::shared_ptr io_message_; + boost::shared_ptr io_socket_; + boost::shared_ptr io_endpoint_; + boost::shared_ptr data_; + + // Callbacks + const CheckinProvider* checkin_callback_; + const DNSProvider* dns_callback_; +}; + +} + +#endif // __TCPDNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/asiolink/internal/udpdns.h b/src/lib/asiolink/internal/udpdns.h new file mode 100644 index 000000000..16d8ed7e8 --- /dev/null +++ b/src/lib/asiolink/internal/udpdns.h @@ -0,0 +1,113 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// 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. + +// $Id$ + +#ifndef __UDPDNS_H +#define __UDPDNS_H 1 + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace asiolink { +// Note: this implementation is optimized for the case where this object +// is created from an ASIO endpoint object in a receiving code path +// by avoiding to make a copy of the base endpoint. For TCP it may not be +// a big deal, but when we receive UDP packets at a high rate, the copy +// overhead might be significant. +class UDPEndpoint : public IOEndpoint { +public: + UDPEndpoint(const IOAddress& address, const unsigned short port) : + asio_endpoint_placeholder_( + new asio::ip::udp::endpoint(asio::ip::address::from_string(address.toText()), + port)), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) : + asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) + {} + + ~UDPEndpoint() { delete asio_endpoint_placeholder_; } + virtual IOAddress getAddress() const; + virtual uint16_t getPort() const; + virtual short getProtocol() const; + virtual short getFamily() const; +private: + const asio::ip::udp::endpoint* asio_endpoint_placeholder_; + const asio::ip::udp::endpoint& asio_endpoint_; +}; + +class UDPSocket : public IOSocket { +private: + UDPSocket(const UDPSocket& source); + UDPSocket& operator=(const UDPSocket& source); +public: + UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {} + virtual int getNative() const; + virtual int getProtocol() const; +private: + asio::ip::udp::socket& socket_; +}; +// +// Asynchronous UDP server coroutine +// +class UDPServer : public coroutine { +public: + explicit UDPServer(asio::io_service& io_service, + const asio::ip::address& addr, const uint16_t port, + CheckinProvider* checkin = NULL, + DNSProvider* process = NULL); + void operator()(asio::error_code ec = asio::error_code(), + size_t length = 0); + +private: + enum { MAX_LENGTH = 4096 }; + + // As mentioned in the comments to TCPServer, class member variables + // which are dynamic, and changes to which are expected to be + // accessible from both sides of a coroutine fork, should be + // declared here as shared pointers and allocated in the + // constructor or in the coroutine. + boost::shared_ptr socket_; + boost::shared_ptr sender_; + boost::shared_ptr response_; + boost::shared_ptr renderer_; + boost::shared_ptr dns_message_; + boost::shared_ptr io_endpoint_; + boost::shared_ptr io_message_; + boost::shared_ptr io_socket_; + boost::shared_ptr data_; + size_t bytes_; + + // Callbacks + const CheckinProvider* checkin_callback_; + const DNSProvider* dns_callback_; +}; + +} + +#endif // __UDPDNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/asiolink/tcpdns.cc b/src/lib/asiolink/tcpdns.cc new file mode 100644 index 000000000..cff23ea06 --- /dev/null +++ b/src/lib/asiolink/tcpdns.cc @@ -0,0 +1,164 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// 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. + +// $Id$ + +#include + +#include // for some IPC/network system calls +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace asio; +using asio::ip::udp; +using asio::ip::tcp; + +using namespace std; +using namespace isc::dns; + +namespace asiolink { + +IOAddress +TCPEndpoint::getAddress() const { + return (asio_endpoint_.address()); +} + +uint16_t +TCPEndpoint::getPort() const { + return (asio_endpoint_.port()); +} + +short +TCPEndpoint::getProtocol() const { + return (asio_endpoint_.protocol().protocol()); +} + +short +TCPEndpoint::getFamily() const { + return (asio_endpoint_.protocol().family()); +} + +int +TCPSocket::getNative() const { + return (socket_.native()); +} + +int +TCPSocket::getProtocol() const { + return (IPPROTO_TCP); +} + +TCPServer::TCPServer(io_service& io_service, + const ip::address& addr, const uint16_t port, + CheckinProvider* checkin, DNSProvider* process) : + checkin_callback_(checkin), dns_callback_(process) +{ + tcp::endpoint endpoint(addr, port); + acceptor_.reset(new tcp::acceptor(io_service)); + acceptor_->open(endpoint.protocol()); + // Set v6-only (we use a different instantiation for v4, + // 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) { + if (ec) { + return; + } + + CORO_REENTER (this) { + do { + socket_.reset(new tcp::socket(acceptor_->get_io_service())); + CORO_YIELD acceptor_->async_accept(*socket_, *this); + CORO_FORK TCPServer(*this)(); + } while (is_parent()); + + // Perform any necessary operations prior to processing + // an 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_)(); + } + + // Instantiate the data buffer that will be used by the + // asynchronous read call. + data_ = boost::shared_ptr(new char[MAX_LENGTH]); + + CORO_YIELD async_read(*socket_, asio::buffer(data_.get(), + TCP_MESSAGE_LENGTHSIZE), + *this); + + CORO_YIELD { + InputBuffer dnsbuffer((const void *) data_.get(), length); + uint16_t msglen = dnsbuffer.readUint16(); + async_read(*socket_, asio::buffer(data_.get(), msglen), *this); + } + + // Stop here if we don't have a DNS callback function + if (dns_callback_ == NULL) { + CORO_YIELD return; + } + + // Instantiate the objects that will be needed by the + // DNS callback and the asynchronous write calls. + dns_message_.reset(new Message(Message::PARSE)); + response_.reset(new OutputBuffer(0)); + lenbuf_.reset(new OutputBuffer(TCP_MESSAGE_LENGTHSIZE)); + renderer_.reset(new MessageRenderer(*response_)); + io_socket_.reset(new TCPSocket(*socket_)); + io_endpoint_.reset(new TCPEndpoint(socket_->remote_endpoint())); + io_message_.reset(new IOMessage(data_.get(), length, *io_socket_, + *io_endpoint_)); + + // Process the DNS message + if (! (*dns_callback_)(*io_message_, *dns_message_, *renderer_)) { + CORO_YIELD return; + } + + lenbuf_->writeUint16(response_->getLength()); + CORO_YIELD async_write(*socket_, + buffer(lenbuf_->getData(), lenbuf_->getLength()), + *this); + CORO_YIELD async_write(*socket_, + buffer(response_->getData(), + response_->getLength()), + *this); + } +} + +} diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am index 563e408f1..4ad911ee7 100644 --- a/src/lib/asiolink/tests/Makefile.am +++ b/src/lib/asiolink/tests/Makefile.am @@ -25,6 +25,12 @@ run_unittests_LDADD += $(SQLITE_LIBS) run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la +# Note: the ordering matters: -Wno-... must follow -Wextra (defined in +# B10_CXXFLAGS) +run_unittests_CXXFLAGS = $(AM_CXXFLAGS) +if USE_GXX +run_unittests_CXXFLAGS += -Wno-unused-parameter +endif endif noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/asiolink/tests/asio_link_unittest.cc b/src/lib/asiolink/tests/asio_link_unittest.cc index 58a8b5a38..5529395c0 100644 --- a/src/lib/asiolink/tests/asio_link_unittest.cc +++ b/src/lib/asiolink/tests/asio_link_unittest.cc @@ -24,6 +24,8 @@ #include #include +#include +#include using isc::UnitTestUtil; using namespace std; diff --git a/src/lib/asiolink/udpdns.cc b/src/lib/asiolink/udpdns.cc new file mode 100644 index 000000000..578ff8040 --- /dev/null +++ b/src/lib/asiolink/udpdns.cc @@ -0,0 +1,152 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// 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. + +// $Id$ + +#include + +#include // for some IPC/network system calls +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace asio; +using asio::ip::udp; +using asio::ip::tcp; + +using namespace std; +using namespace isc::dns; + +namespace asiolink { +IOAddress +UDPEndpoint::getAddress() const { + return (asio_endpoint_.address()); +} + +uint16_t +UDPEndpoint::getPort() const { + return (asio_endpoint_.port()); +} + +short +UDPEndpoint::getProtocol() const { + return (asio_endpoint_.protocol().protocol()); +} + +short +UDPEndpoint::getFamily() const { + return (asio_endpoint_.protocol().family()); +} + +int +UDPSocket::getNative() const { + return (socket_.native()); +} + +int +UDPSocket::getProtocol() const { + return (IPPROTO_UDP); +} + +UDPServer::UDPServer(io_service& io_service, + const ip::address& addr, const uint16_t port, + CheckinProvider* checkin, DNSProvider* process) : + checkin_callback_(checkin), dns_callback_(process) +{ + // Wwe use a different instantiation for v4, + // otherwise asio will bind to both v4 and v6 + if (addr.is_v6()) { + socket_.reset(new udp::socket(io_service, udp::v6())); + socket_->set_option(socket_base::reuse_address(true)); + socket_->set_option(asio::ip::v6_only(true)); + socket_->bind(udp::endpoint(udp::v6(), port)); + } else { + socket_.reset(new udp::socket(io_service, udp::v4())); + socket_->set_option(socket_base::reuse_address(true)); + socket_->bind(udp::endpoint(udp::v6(), port)); + } +} + +void +UDPServer::operator()(error_code ec, size_t length) { + CORO_REENTER (this) for (;;) { + // Instantiate the data buffer that will be used by the + // asynchronous read calls. + data_ = boost::shared_ptr(new char[MAX_LENGTH]); + sender_.reset(new udp::endpoint()); + + do { + CORO_YIELD socket_->async_receive_from(asio::buffer(data_.get(), + MAX_LENGTH), + *sender_, *this); + } while (ec || length == 0); + + bytes_ = length; + CORO_FORK UDPServer(*this)(); + if (is_parent()) { + continue; + } + + // Perform any necessary operations prior to processing + // an 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_)(); + } + + // Stop here if we don't have a DNS callback function + if (dns_callback_ == NULL) { + CORO_YIELD return; + } + + // Instantialize objects that will be needed by the + // DNS callback function and the async write. + dns_message_.reset(new Message(Message::PARSE)); + response_.reset(new OutputBuffer(0)); + renderer_.reset(new MessageRenderer(*response_)); + io_socket_.reset(new UDPSocket(*socket_)); + io_endpoint_.reset(new UDPEndpoint(*sender_)); + io_message_.reset(new IOMessage(data_.get(), bytes_, + *io_socket_, + *io_endpoint_)); + + // Process the DNS message + if (! (*dns_callback_)(*io_message_, *dns_message_, *renderer_)) + { + CORO_YIELD return; + } + + CORO_YIELD socket_->async_send_to(asio::buffer(response_->getData(), + response_->getLength()), + *sender_, *this); + } +} + +} diff --git a/src/lib/asiolink/unyield.h b/src/lib/asiolink/unyield.h deleted file mode 100644 index d51c710de..000000000 --- a/src/lib/asiolink/unyield.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// unyield.hpp -// ~~~~~~~~~~~ -// -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifdef reenter -# undef reenter -#endif - -#ifdef yield -# undef yield -#endif - -#ifdef fork -# undef fork -#endif diff --git a/src/lib/asiolink/yield.h b/src/lib/asiolink/yield.h deleted file mode 100644 index e2dad4aa2..000000000 --- a/src/lib/asiolink/yield.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// yield.h -// ~~~~~~~ -// -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include "coroutine.h" - -#ifndef reenter -# define reenter(c) CORO_REENTER(c) -#endif - -#ifndef yield -# define yield CORO_YIELD -#endif - -#ifndef fork -# define fork CORO_FORK -#endif -- GitLab