diff options
author | Jelte Jansen <jelte@isc.org> | 2010-06-09 11:52:34 +0200 |
---|---|---|
committer | Jelte Jansen <jelte@isc.org> | 2010-06-09 11:52:34 +0200 |
commit | 93227d328b0f86ad9c86c95f506587f758a333d9 (patch) | |
tree | 3d9e5e3879beca46ef1683253eac6b7a78cb9738 /src/bin/auth | |
parent | copyright statements had wrong year (diff) | |
parent | Generate a unique session ID by using socket.gethostname() instead of socket.... (diff) | |
download | kea-93227d328b0f86ad9c86c95f506587f758a333d9.tar.xz kea-93227d328b0f86ad9c86c95f506587f758a333d9.zip |
merge to sync with trunk and make later merge back easier
updated additions in tests for wrapper api
also independently came up with the fix attached in ticket #224
git-svn-id: svn://bind10.isc.org/svn/bind10/experiments/python-binding@2097 e5f2f494-b856-4b98-b285-d166d9295462
Diffstat (limited to 'src/bin/auth')
-rw-r--r-- | src/bin/auth/Makefile.am | 30 | ||||
-rw-r--r-- | src/bin/auth/asio_link.cc | 413 | ||||
-rw-r--r-- | src/bin/auth/asio_link.h | 41 | ||||
-rw-r--r-- | src/bin/auth/auth.spec.pre.in | 1 | ||||
-rw-r--r-- | src/bin/auth/auth_srv.cc | 20 | ||||
-rw-r--r-- | src/bin/auth/main.cc | 647 | ||||
-rw-r--r-- | src/bin/auth/spec_config.h.pre.in (renamed from src/bin/auth/spec_config.h.in) | 32 | ||||
-rw-r--r-- | src/bin/auth/tests/Makefile.am | 7 |
8 files changed, 522 insertions, 669 deletions
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index 0f57faaf34..6414dcac29 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -2,13 +2,13 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns -if GCC_WERROR_OK -AM_CPPFLAGS += -Werror -endif +AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc + +AM_CXXFLAGS = $(B10_CXXFLAGS) pkglibexecdir = $(libexecdir)/@PACKAGE@ -CLEANFILES = *.gcno *.gcda auth.spec +CLEANFILES = *.gcno *.gcda auth.spec spec_config.h man_MANS = b10-auth.8 EXTRA_DIST = $(man_MANS) b10-auth.xml @@ -23,6 +23,23 @@ endif auth.spec: auth.spec.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" auth.spec.pre >$@ +spec_config.h: spec_config.h.pre + $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@ + +# This is a wrapper library solely used for b10-auth. The ASIO header files +# have some code fragments that would hit gcc's unused-parameter warning, +# which would make the build fail with -Werror (our default setting). +# We don't want to lower the warning level for our own code just for ASIO, +# so as a workaround we extract the ASIO related code into a separate library, +# only for which we accept the unused-parameter warning. +lib_LIBRARIES = libasio_link.a +libasio_link_a_SOURCES = asio_link.cc asio_link.h +# Note: the ordering matters: -Wno-... must follow -Wextra (defined in +# B10_CXXFLAGS) +libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter +libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS) + +BUILT_SOURCES = spec_config.h pkglibexec_PROGRAMS = b10-auth b10_auth_SOURCES = auth_srv.cc auth_srv.h b10_auth_SOURCES += common.h @@ -32,12 +49,9 @@ b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a b10_auth_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.a b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a +b10_auth_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a b10_auth_LDADD += $(SQLITE_LIBS) -if HAVE_BOOST_SYSTEM b10_auth_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a -endif -b10_auth_LDFLAGS = $(AM_LDFLAGS) $(BOOST_LDFLAGS) -b10_auth_LDADD += $(BOOST_SYSTEM_LIB) # TODO: config.h.in is wrong because doesn't honor pkgdatadir # and can't use @datadir@ because doesn't expand default ${prefix} diff --git a/src/bin/auth/asio_link.cc b/src/bin/auth/asio_link.cc new file mode 100644 index 0000000000..332c92d519 --- /dev/null +++ b/src/bin/auth/asio_link.cc @@ -0,0 +1,413 @@ +// 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 <config.h> + +#include <asio.hpp> +#include <boost/bind.hpp> + +#include <dns/buffer.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> + +#include <xfr/xfrout_client.h> + +#include <asio_link.h> + +#include "spec_config.h" // for XFROUT. should not be here. +#include "auth_srv.h" + +using namespace asio; +using ip::udp; +using ip::tcp; + +using namespace std; +using namespace isc::dns; +using namespace isc::xfr; + +namespace { +// As a short term workaround, we have XFROUT specific code. We should soon +// refactor the code with some abstraction so that we can separate this level +// details from the (AS)IO module. + +// This was contained in an ifdef USE_XFROUT, but we should really check +// live if we do xfrout +//TODO. The sample way for checking axfr query, the code should be merged to auth server class +bool +check_axfr_query(char* const msg_data, const uint16_t msg_len) { + if (msg_len < 15) { + return false; + } + + const uint16_t query_type = *(uint16_t *)(msg_data + (msg_len - 4)); + if ( query_type == 0xFC00) { + return true; + } + + return false; +} + +//TODO. Send the xfr query to xfrout module, the code should be merged to auth server class +//BIGGERTODO: stop using hardcoded install-path locations! +void +dispatch_axfr_query(const int tcp_sock, char const axfr_query[], + const uint16_t query_len) +{ + string path(UNIX_SOCKET_FILE); + if (getenv("B10_FROM_BUILD")) { + path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn"; + } + XfroutClient xfr_client(path); + try { + xfr_client.connect(); + xfr_client.sendXfroutRequestInfo(tcp_sock, (uint8_t *)axfr_query, + query_len); + xfr_client.disconnect(); + } + catch (const exception & err) { + //if (verbose_mode) + cerr << "error handle xfr query " << UNIX_SOCKET_FILE << ":" << err.what() << endl; + } +} +} + +namespace asio_link { +// +// Helper classes for asynchronous I/O using asio +// +class TCPClient { +public: + TCPClient(AuthSrv* auth_server, io_service& io_service) : + auth_server_(auth_server), + socket_(io_service), + response_buffer_(0), + responselen_buffer_(TCP_MESSAGE_LENGTHSIZE), + response_renderer_(response_buffer_), + dns_message_(Message::PARSE) + {} + + void start() { + // Check for queued configuration commands + if (auth_server_->configSession()->hasQueuedMsgs()) { + auth_server_->configSession()->checkCommand(); + } + async_read(socket_, asio::buffer(data_, TCP_MESSAGE_LENGTHSIZE), + boost::bind(&TCPClient::headerRead, this, + placeholders::error, + placeholders::bytes_transferred)); + } + + tcp::socket& getSocket() { return (socket_); } + + void headerRead(const asio::error_code& error, + size_t bytes_transferred) + { + if (!error) { + InputBuffer dnsbuffer(data_, bytes_transferred); + + uint16_t msglen = dnsbuffer.readUint16(); + async_read(socket_, asio::buffer(data_, msglen), + + boost::bind(&TCPClient::requestRead, this, + placeholders::error, + placeholders::bytes_transferred)); + } else { + delete this; + } + } + + void requestRead(const asio::error_code& error, + size_t bytes_transferred) + { + if (!error) { + InputBuffer dnsbuffer(data_, bytes_transferred); + if (check_axfr_query(data_, bytes_transferred)) { + dispatch_axfr_query(socket_.native(), data_, bytes_transferred); + // start to get new query ? + start(); + } else { + if (auth_server_->processMessage(dnsbuffer, dns_message_, + response_renderer_, false)) { + responselen_buffer_.writeUint16( + response_buffer_.getLength()); + async_write(socket_, + asio::buffer( + responselen_buffer_.getData(), + responselen_buffer_.getLength()), + boost::bind(&TCPClient::responseWrite, this, + placeholders::error)); + } else { + delete this; + } + } + } else { + delete this; + } + } + + void responseWrite(const asio::error_code& error) { + if (!error) { + async_write(socket_, + asio::buffer(response_buffer_.getData(), + response_buffer_.getLength()), + boost::bind(&TCPClient::handleWrite, this, + placeholders::error)); + } else { + delete this; + } + } + + void handleWrite(const asio::error_code& error) { + if (!error) { + start(); // handle next request, if any. + } else { + delete this; + } + } + +private: + AuthSrv* auth_server_; + tcp::socket socket_; + OutputBuffer response_buffer_; + OutputBuffer responselen_buffer_; + MessageRenderer response_renderer_; + Message dns_message_; + enum { MAX_LENGTH = 65535 }; + static const size_t TCP_MESSAGE_LENGTHSIZE = 2; + char data_[MAX_LENGTH]; +}; + +class TCPServer { +public: + TCPServer(AuthSrv* auth_server, io_service& io_service, + int af, short port) : + auth_server_(auth_server), io_service_(io_service), + acceptor_(io_service_), listening_(new TCPClient(auth_server_, + io_service_)) + { + tcp::endpoint endpoint(af == AF_INET6 ? tcp::v6() : tcp::v4(), port); + acceptor_.open(endpoint.protocol()); + // Set v6-only (we use a different instantiation for v4, + // otherwise asio will bind to both v4 and v6 + if (af == AF_INET6) { + acceptor_.set_option(ip::v6_only(true)); + } + acceptor_.set_option(tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + acceptor_.async_accept(listening_->getSocket(), + boost::bind(&TCPServer::handleAccept, this, + listening_, placeholders::error)); + } + + ~TCPServer() { delete listening_; } + + void handleAccept(TCPClient* new_client, + const asio::error_code& error) + { + if (!error) { + assert(new_client == listening_); + new_client->start(); + listening_ = new TCPClient(auth_server_, io_service_); + acceptor_.async_accept(listening_->getSocket(), + boost::bind(&TCPServer::handleAccept, + this, listening_, + placeholders::error)); + } else { + delete new_client; + } + } + +private: + AuthSrv* auth_server_; + io_service& io_service_; + tcp::acceptor acceptor_; + TCPClient* listening_; +}; + +class UDPServer { +public: + UDPServer(AuthSrv* auth_server, io_service& io_service, + int af, short port) : + auth_server_(auth_server), + io_service_(io_service), + socket_(io_service, af == AF_INET6 ? udp::v6() : udp::v4()), + response_buffer_(0), + response_renderer_(response_buffer_), + dns_message_(Message::PARSE) + { + // Set v6-only (we use a different instantiation for v4, + // otherwise asio will bind to both v4 and v6 + if (af == AF_INET6) { + socket_.set_option(asio::ip::v6_only(true)); + socket_.bind(udp::endpoint(udp::v6(), port)); + } else { + socket_.bind(udp::endpoint(udp::v4(), port)); + } + startReceive(); + } + + void handleRequest(const asio::error_code& error, + size_t bytes_recvd) + { + // Check for queued configuration commands + if (auth_server_->configSession()->hasQueuedMsgs()) { + auth_server_->configSession()->checkCommand(); + } + if (!error && bytes_recvd > 0) { + InputBuffer request_buffer(data_, bytes_recvd); + + dns_message_.clear(Message::PARSE); + response_renderer_.clear(); + if (auth_server_->processMessage(request_buffer, dns_message_, + response_renderer_, true)) { + socket_.async_send_to( + asio::buffer(response_buffer_.getData(), + response_buffer_.getLength()), + sender_endpoint_, + boost::bind(&UDPServer::sendCompleted, + this, + placeholders::error, + placeholders::bytes_transferred)); + } else { + startReceive(); + } + } else { + startReceive(); + } + } + + void sendCompleted(const asio::error_code& error UNUSED_PARAM, + size_t bytes_sent UNUSED_PARAM) + { + // Even if error occurred there's nothing to do. Simply handle + // the next request. + startReceive(); + } +private: + void startReceive() { + socket_.async_receive_from( + asio::buffer(data_, MAX_LENGTH), sender_endpoint_, + boost::bind(&UDPServer::handleRequest, this, + placeholders::error, + placeholders::bytes_transferred)); + } + +private: + AuthSrv* auth_server_; + io_service& io_service_; + udp::socket socket_; + OutputBuffer response_buffer_; + MessageRenderer response_renderer_; + Message dns_message_; + udp::endpoint sender_endpoint_; + enum { MAX_LENGTH = 4096 }; + char data_[MAX_LENGTH]; +}; + +// This is a helper structure just to make the construction of IOServiceImpl +// exception safe. If the constructor of {UDP/TCP}Server throws an exception, +// the destructor of this class will automatically perform the necessary +// cleanup. +struct ServerSet { + ServerSet() : udp4_server(NULL), udp6_server(NULL), + tcp4_server(NULL), tcp6_server(NULL) + {} + ~ServerSet() { + delete udp4_server; + delete udp6_server; + delete tcp4_server; + delete tcp6_server; + } + UDPServer* udp4_server; + UDPServer* udp6_server; + TCPServer* tcp4_server; + TCPServer* tcp6_server; +}; + +class IOServiceImpl { +public: + IOServiceImpl(AuthSrv* auth_server, const char* port, + const bool use_ipv4, const bool use_ipv6); + ~IOServiceImpl(); + asio::io_service io_service_; + AuthSrv* auth_server_; + UDPServer* udp4_server_; + UDPServer* udp6_server_; + TCPServer* tcp4_server_; + TCPServer* tcp6_server_; +}; + +IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char* const port, + const bool use_ipv4, const bool use_ipv6) : + auth_server_(auth_server), udp4_server_(NULL), udp6_server_(NULL), + tcp4_server_(NULL), tcp6_server_(NULL) +{ + ServerSet servers; + short portnum = atoi(port); + + if (use_ipv4) { + servers.udp4_server = new UDPServer(auth_server, io_service_, + AF_INET, portnum); + servers.tcp4_server = new TCPServer(auth_server, io_service_, + AF_INET, portnum); + } + if (use_ipv6) { + servers.udp6_server = new UDPServer(auth_server, io_service_, + AF_INET6, portnum); + servers.tcp6_server = new TCPServer(auth_server, io_service_, + AF_INET6, portnum); + } + + // Now we don't have to worry about exception, and need to make sure that + // the server objects won't be accidentally cleaned up. + servers.udp4_server = NULL; + servers.udp6_server = NULL; + servers.tcp4_server = NULL; + servers.tcp6_server = NULL; +} + +IOServiceImpl::~IOServiceImpl() { + delete udp4_server_; + delete udp6_server_; + delete tcp4_server_; + delete tcp6_server_; +} + +IOService::IOService(AuthSrv* auth_server, const char* const port, + const bool use_ipv4, const bool use_ipv6) { + impl_ = new IOServiceImpl(auth_server, port, use_ipv4, use_ipv6); +} + +IOService::~IOService() { + delete impl_; +} + +void +IOService::run() { + impl_->io_service_.run(); +} + +void +IOService::stop() { + impl_->io_service_.stop(); +} + +asio::io_service& +IOService::get_io_service() { + return impl_->io_service_; +} +} diff --git a/src/bin/auth/asio_link.h b/src/bin/auth/asio_link.h new file mode 100644 index 0000000000..b5c9153f83 --- /dev/null +++ b/src/bin/auth/asio_link.h @@ -0,0 +1,41 @@ +// 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 __ASIO_LINK_H +#define __ASIO_LINK_H 1 + +class AuthSrv; + +namespace asio_link { +struct IOServiceImpl; + +class IOService { +public: + IOService(AuthSrv* auth_server, const char* port, + const bool use_ipv4, const bool use_ipv6); + ~IOService(); + void run(); + void stop(); + asio::io_service& get_io_service(); +private: + IOServiceImpl* impl_; +}; +} // asio_link +#endif // __ASIO_LINK_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in index 4bde11af50..98d7005974 100644 --- a/src/bin/auth/auth.spec.pre.in +++ b/src/bin/auth/auth.spec.pre.in @@ -1,6 +1,7 @@ { "module_spec": { "module_name": "Auth", + "module_description": "Authoritative service", "config_data": [ { "item_name": "database_file", "item_type": "string", diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index bdb863160e..dee60ef3b2 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -139,7 +139,7 @@ makeErrorMessage(Message& message, MessageRenderer& renderer, message.toWire(renderer); if (verbose_mode) { - cerr << "sending an error response (" << + cerr << "[b10-auth] sending an error response (" << boost::lexical_cast<string>(renderer.getLength()) << " bytes):\n" << message.toText() << endl; } @@ -179,7 +179,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, // Ignore all responses. if (message.getHeaderFlag(MessageFlag::QR())) { if (impl_->verbose_mode_) { - cerr << "received unexpected response, ignoring" << endl; + cerr << "[b10-auth] received unexpected response, ignoring" << endl; } return (false); } @@ -192,7 +192,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, message.fromWire(request_buffer); } catch (const DNSProtocolError& error) { if (impl_->verbose_mode_) { - cerr << "returning " << error.getRcode().toText() << ": " + cerr << "[b10-auth] returning " << error.getRcode().toText() << ": " << error.what() << endl; } makeErrorMessage(message, response_renderer, error.getRcode(), @@ -200,7 +200,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, return (true); } catch (const Exception& ex) { if (impl_->verbose_mode_) { - cerr << "returning SERVFAIL: " << ex.what() << endl; + cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl; } makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(), impl_->verbose_mode_); @@ -208,7 +208,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, } // other exceptions will be handled at a higher layer. if (impl_->verbose_mode_) { - cerr << "[AuthSrv] received a message:\n" << message.toText() << endl; + cerr << "[b10-auth] received a message:\n" << message.toText() << endl; } // Perform further protocol-level validation. @@ -216,7 +216,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, // In this implementation, we only support normal queries if (message.getOpcode() != Opcode::QUERY()) { if (impl_->verbose_mode_) { - cerr << "unsupported opcode" << endl; + cerr << "[b10-auth] unsupported opcode" << endl; } makeErrorMessage(message, response_renderer, Rcode::NOTIMP(), impl_->verbose_mode_); @@ -243,7 +243,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, impl_->data_sources_.doQuery(query); } catch (const Exception& ex) { if (impl_->verbose_mode_) { - cerr << "Internal error, returning SERVFAIL: " << ex.what() << endl; + cerr << "[b10-auth] Internal error, returning SERVFAIL: " << ex.what() << endl; } makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(), impl_->verbose_mode_); @@ -253,7 +253,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message, response_renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535); message.toWire(response_renderer); if (impl_->verbose_mode_) { - cerr << "sending a response (" << + cerr << "[b10-auth] sending a response (" << boost::lexical_cast<string>(response_renderer.getLength()) << " bytes):\n" << message.toText() << endl; } @@ -281,7 +281,7 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) { } if (verbose_mode_) { - cerr << "[AuthSrv] Data source database file: " << db_file_ << endl; + cerr << "[b10-auth] Data source database file: " << db_file_ << endl; } // create SQL data source @@ -313,7 +313,7 @@ AuthSrv::updateConfig(isc::data::ElementPtr new_config) { return answer; } catch (const isc::Exception& error) { if (impl_->verbose_mode_) { - cerr << "[AuthSrv] error: " << error.what() << endl; + cerr << "[b10-auth] error: " << error.what() << endl; } return isc::config::createAnswer(1, error.what()); } diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 7ca6390028..2e7073699f 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -28,10 +28,6 @@ #include <iostream> #include <boost/foreach.hpp> -#ifdef HAVE_BOOST_SYSTEM -#include <boost/bind.hpp> -#include <boost/asio.hpp> -#endif // HAVE_BOOST_SYSTEM #include <exceptions/exceptions.h> @@ -43,26 +39,12 @@ #include <cc/data.h> #include <config/ccsession.h> -#if defined(HAVE_BOOST_SYSTEM) -#define USE_XFROUT -#include <xfr/xfrout_client.h> -#endif - #include "spec_config.h" #include "common.h" #include "auth_srv.h" +#include "asio_link.h" using namespace std; -#ifdef USE_XFROUT -using namespace isc::xfr; -#endif - -#ifdef HAVE_BOOST_SYSTEM -using namespace boost::asio; -using ip::udp; -using ip::tcp; -#endif // HAVE_BOOST_SYSTEM - using namespace isc::data; using namespace isc::cc; using namespace isc::config; @@ -79,13 +61,8 @@ const char* DNSPORT = "5300"; * todo: turn this around, and put handlers in the authserver * class itself? */ AuthSrv *auth_server; -#ifdef HAVE_BOOST_SYSTEM -// TODO: this should be a property of AuthSrv, and AuthSrv needs -// a stop() method (so the shutdown command can be handled) -boost::asio::io_service io_service_; -#else -bool running; -#endif // HAVE_BOOST_SYSTEM + +asio_link::IOService* io_service; ElementPtr my_config_handler(ElementPtr new_config) { @@ -101,605 +78,12 @@ my_command_handler(const string& command, const ElementPtr args) { /* let's add that message to our answer as well */ answer->get("result")->add(args); } else if (command == "shutdown") { -#ifdef HAVE_BOOST_SYSTEM - io_service_.stop(); -#else - running = false; -#endif // HAVE_BOOST_SYSTEM + io_service->stop(); } return answer; } -#ifdef USE_XFROUT -//TODO. The sample way for checking axfr query, the code should be merged to auth server class -static bool -check_axfr_query(char *msg_data, uint16_t msg_len) -{ - if (msg_len < 15) - return false; - - uint16_t query_type = *(uint16_t *)(msg_data + (msg_len - 4)); - if ( query_type == 0xFC00) - return true; - - return false; -} - -//TODO. Send the xfr query to xfrout module, the code should be merged to auth server class -static void -dispatch_axfr_query(int tcp_sock, char axfr_query[], uint16_t query_len) -{ - std::string path; - if (getenv("B10_FROM_SOURCE")) { - path = string(getenv("B10_FROM_SOURCE")) + - "/auth_xfrout_conn"; - } else { - path = string(UNIX_SOCKET_FILE); - } - (void)tcp_sock; - (void)axfr_query; - (void)query_len; - XfroutClient xfr_client(path); - try { - xfr_client.connect(); - xfr_client.sendXfroutRequestInfo(tcp_sock, (uint8_t *)axfr_query, query_len); - xfr_client.disconnect(); - } - catch (const std::exception & err) { - //if (verbose_mode) - cerr << "error handle xfr query:" << err.what() << endl; - } -} -#endif - -#ifdef HAVE_BOOST_SYSTEM -// -// Helper classes for asynchronous I/O using boost::asio -// -class TCPClient { -public: - TCPClient(io_service& io_service) : - socket_(io_service), - response_buffer_(0), - responselen_buffer_(TCP_MESSAGE_LENGTHSIZE), - response_renderer_(response_buffer_), - dns_message_(Message::PARSE) - {} - - void start() { - async_read(socket_, boost::asio::buffer(data_, TCP_MESSAGE_LENGTHSIZE), - boost::bind(&TCPClient::headerRead, this, - placeholders::error, - placeholders::bytes_transferred)); - } - - tcp::socket& getSocket() { return (socket_); } - - void headerRead(const boost::system::error_code& error, - size_t bytes_transferred) - { - if (!error) { - InputBuffer dnsbuffer(data_, bytes_transferred); - - uint16_t msglen = dnsbuffer.readUint16(); - async_read(socket_, boost::asio::buffer(data_, msglen), - - boost::bind(&TCPClient::requestRead, this, - placeholders::error, - placeholders::bytes_transferred)); - } else { - delete this; - } - } - - void requestRead(const boost::system::error_code& error, - size_t bytes_transferred) - { - if (!error) { - InputBuffer dnsbuffer(data_, bytes_transferred); -#ifdef USE_XFROUT - if (check_axfr_query(data_, bytes_transferred)) { - dispatch_axfr_query(socket_.native(), data_, bytes_transferred); - // start to get new query ? - start(); - } else { -#endif - if (auth_server->processMessage(dnsbuffer, dns_message_, - response_renderer_, false)) { - responselen_buffer_.writeUint16(response_buffer_.getLength()); - async_write(socket_, - boost::asio::buffer( - responselen_buffer_.getData(), - responselen_buffer_.getLength()), - boost::bind(&TCPClient::responseWrite, this, - placeholders::error)); - } else { - delete this; - } -#ifdef USE_XFROUT - } -#endif - } else { - delete this; - } - } - - void responseWrite(const boost::system::error_code& error) { - if (!error) { - async_write(socket_, - boost::asio::buffer(response_buffer_.getData(), - response_buffer_.getLength()), - boost::bind(&TCPClient::handleWrite, this, - placeholders::error)); - } else { - delete this; - } - } - - void handleWrite(const boost::system::error_code& error) { - if (!error) { - start(); // handle next request, if any. - } else { - delete this; - } - } - -private: - tcp::socket socket_; - OutputBuffer response_buffer_; - OutputBuffer responselen_buffer_; - MessageRenderer response_renderer_; - Message dns_message_; - enum { MAX_LENGTH = 65535 }; - static const size_t TCP_MESSAGE_LENGTHSIZE = 2; - char data_[MAX_LENGTH]; -}; - -class TCPServer { -public: - TCPServer(io_service& io_service, int af, short port) : - io_service_(io_service), acceptor_(io_service_), - listening_(new TCPClient(io_service_)) - { - tcp::endpoint endpoint(af == AF_INET6 ? tcp::v6() : tcp::v4(), port); - acceptor_.open(endpoint.protocol()); - // Set v6-only (we use a different instantiation for v4, - // otherwise asio will bind to both v4 and v6 - if (af == AF_INET6) { - acceptor_.set_option(ip::v6_only(true)); - } - acceptor_.set_option(tcp::acceptor::reuse_address(true)); - acceptor_.bind(endpoint); - acceptor_.listen(); - acceptor_.async_accept(listening_->getSocket(), - boost::bind(&TCPServer::handleAccept, this, - listening_, placeholders::error)); - } - - ~TCPServer() { delete listening_; } - - void handleAccept(TCPClient* new_client, - const boost::system::error_code& error) - { - if (!error) { - assert(new_client == listening_); - new_client->start(); - listening_ = new TCPClient(io_service_); - acceptor_.async_accept(listening_->getSocket(), - boost::bind(&TCPServer::handleAccept, - this, listening_, - placeholders::error)); - } else { - delete new_client; - } - } - -private: - io_service& io_service_; - tcp::acceptor acceptor_; - TCPClient* listening_; -}; - -class UDPServer { -public: - UDPServer(io_service& io_service, int af, short port) : - io_service_(io_service), - socket_(io_service, af == AF_INET6 ? udp::v6() : udp::v4()), - response_buffer_(0), - response_renderer_(response_buffer_), - dns_message_(Message::PARSE) - { - // Set v6-only (we use a different instantiation for v4, - // otherwise asio will bind to both v4 and v6 - if (af == AF_INET6) { - socket_.set_option(boost::asio::ip::v6_only(true)); - socket_.bind(udp::endpoint(udp::v6(), port)); - } else { - socket_.bind(udp::endpoint(udp::v4(), port)); - } - startReceive(); - } - - void handleRequest(const boost::system::error_code& error, - size_t bytes_recvd) - { - if (!error && bytes_recvd > 0) { - InputBuffer request_buffer(data_, bytes_recvd); - - dns_message_.clear(Message::PARSE); - response_renderer_.clear(); - if (auth_server->processMessage(request_buffer, dns_message_, - response_renderer_, true)) { - socket_.async_send_to( - boost::asio::buffer(response_buffer_.getData(), - response_buffer_.getLength()), - sender_endpoint_, - boost::bind(&UDPServer::sendCompleted, - this, - placeholders::error, - placeholders::bytes_transferred)); - } else { - startReceive(); - } - } else { - startReceive(); - } - } - - void sendCompleted(const boost::system::error_code& error UNUSED_PARAM, - size_t bytes_sent UNUSED_PARAM) - { - // Even if error occurred there's nothing to do. Simply handle - // the next request. - startReceive(); - } -private: - void startReceive() { - socket_.async_receive_from( - boost::asio::buffer(data_, MAX_LENGTH), sender_endpoint_, - boost::bind(&UDPServer::handleRequest, this, - placeholders::error, - placeholders::bytes_transferred)); - } - -private: - io_service& io_service_; - udp::socket socket_; - OutputBuffer response_buffer_; - MessageRenderer response_renderer_; - Message dns_message_; - udp::endpoint sender_endpoint_; - enum { MAX_LENGTH = 4096 }; - char data_[MAX_LENGTH]; -}; - -struct ServerSet { - ServerSet() : udp4_server(NULL), udp6_server(NULL), - tcp4_server(NULL), tcp6_server(NULL) - {} - ~ServerSet() { - delete udp4_server; - delete udp6_server; - delete tcp4_server; - delete tcp6_server; - } - UDPServer* udp4_server; - UDPServer* udp6_server; - TCPServer* tcp4_server; - TCPServer* tcp6_server; -}; - -void -run_server(const char* port, const bool use_ipv4, const bool use_ipv6, - AuthSrv* srv UNUSED_PARAM) -{ - ServerSet servers; - short portnum = atoi(port); - - if (use_ipv4) { - servers.udp4_server = new UDPServer(io_service_, AF_INET, portnum); - servers.tcp4_server = new TCPServer(io_service_, AF_INET, portnum); - } - if (use_ipv6) { - servers.udp6_server = new UDPServer(io_service_, AF_INET6, portnum); - servers.tcp6_server = new TCPServer(io_service_, AF_INET6, portnum); - } - - cout << "Server started." << endl; - io_service_.run(); -} -#else // !HAVE_BOOST_SYSTEM -struct SocketSet { - SocketSet() : ups4(-1), tps4(-1), ups6(-1), tps6(-1) {} - ~SocketSet() { - if (ups4 >= 0) { - close(ups4); - } - if (tps4 >= 0) { - close(tps4); - } - if (ups6 >= 0) { - close(ups6); - } - if (tps4 >= 0) { - close(tps6); - } - } - int ups4, tps4, ups6, tps6; -}; - -int -getUDPSocket(int af, const char* port) { - struct addrinfo hints, *res; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_PASSIVE; - hints.ai_protocol = IPPROTO_UDP; - - int error = getaddrinfo(NULL, port, &hints, &res); - if (error != 0) { - isc_throw(FatalError, "getaddrinfo failed: " << gai_strerror(error)); - } - - int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s < 0) { - isc_throw(FatalError, "failed to open socket"); - } - - if (af == AF_INET6) { - int on = 1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { - cerr << "couldn't set IPV6_V6ONLY socket option" << endl; - // proceed anyway - } - } - - if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { - isc_throw(FatalError, "binding socket failure"); - } - - return (s); -} - -int -getTCPSocket(int af, const char* port) { - struct addrinfo hints, *res; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - hints.ai_protocol = IPPROTO_TCP; - - int error = getaddrinfo(NULL, port, &hints, &res); - if (error != 0) { - isc_throw(FatalError, "getaddrinfo failed: " << gai_strerror(error)); - } - - int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s < 0) { - isc_throw(FatalError, "failed to open socket"); - } - - int on = 1; - if (af == AF_INET6) { - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { - cerr << "couldn't set IPV6_V6ONLY socket option" << endl; - } - // proceed anyway - } - - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { - cerr << "couldn't set SO_REUSEADDR socket option" << endl; - } - - if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { - isc_throw(FatalError, "binding socket failure"); - } - - if (listen(s, 100) < 0) { - isc_throw(FatalError, "failed to listen on a TCP socket"); - } - return (s); -} - -void -processMessageUDP(const int fd, Message& dns_message, - MessageRenderer& response_renderer) -{ - struct sockaddr_storage ss; - socklen_t sa_len = sizeof(ss); - struct sockaddr* sa = static_cast<struct sockaddr*>((void*)&ss); - char recvbuf[4096]; - int cc; - - dns_message.clear(Message::PARSE); - response_renderer.clear(); - if ((cc = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, sa, &sa_len)) > 0) { - InputBuffer buffer(recvbuf, cc); - if (auth_server->processMessage(buffer, dns_message, response_renderer, - true)) { - cc = sendto(fd, response_renderer.getData(), - response_renderer.getLength(), 0, sa, sa_len); - if (cc != response_renderer.getLength()) { - cerr << "UDP send error" << endl; - } - } - } else if (verbose_mode) { - cerr << "UDP receive error" << endl; - } -} - -// XXX: this function does not handle partial reads or partial writes, -// and is VERY UNSAFE - will probably be removed or rewritten -void -processMessageTCP(const int fd, Message& dns_message, - MessageRenderer& response_renderer) -{ - struct sockaddr_storage ss; - socklen_t sa_len = sizeof(ss); - struct sockaddr* sa = static_cast<struct sockaddr*>((void*)&ss); - char sizebuf[2]; - int cc; - - int ts = accept(fd, sa, &sa_len); - if (ts < 0) { - if (verbose_mode) { - cerr << "[XX] TCP accept failure:" << endl; - return; - } - } - - if (verbose_mode) { - cerr << "[XX] process TCP" << endl; - } - cc = recv(ts, sizebuf, 2, 0); - if (cc < 0) { - if (verbose_mode) { - cerr << "[XX] TCP recv failure:" << endl; - } - close(ts); - return; - } - if (verbose_mode) { - cerr << "[XX] got: " << cc << endl; - } - uint16_t size, size_n; - memcpy(&size_n, sizebuf, 2); - size = ntohs(size_n); - if (verbose_mode) { - cerr << "[XX] got: " << size << endl; - } - - vector<char> message_buffer; - message_buffer.reserve(size); - cc = 0; - while (cc < size) { - if (verbose_mode) { - cerr << "[XX] cc now: " << cc << " of " << size << endl; - } - const int cc0 = recv(ts, &message_buffer[0] + cc, size - cc, 0); - if (cc0 < 0) { - if (verbose_mode) { - cerr << "TCP receive error" << endl; - close(ts); - return; - } - } - if (cc0 == 0) { - // client closed connection - close(ts); - return; - } - cc += cc0; - } - - InputBuffer buffer(&message_buffer[0], size); - dns_message.clear(Message::PARSE); - response_renderer.clear(); - if (auth_server->processMessage(buffer, dns_message, response_renderer, - false)) { - size = response_renderer.getLength(); - size_n = htons(size); - if (send(ts, &size_n, 2, 0) == 2) { - cc = send(ts, response_renderer.getData(), - response_renderer.getLength(), 0); - if (cc == -1) { - if (verbose_mode) { - cerr << "[AuthSrv] error in sending TCP response message" << - endl; - } - } else { - if (verbose_mode) { - cerr << "[XX] sent TCP response: " << cc << " bytes" - << endl; - } - } - } else { - if (verbose_mode) { - cerr << "TCP send error" << endl; - } - } - } - - // TODO: we don't check for more queries on the stream atm - close(ts); -} - -void -run_server(const char* port, const bool use_ipv4, const bool use_ipv6, - AuthSrv* srv) -{ - SocketSet socket_set; - fd_set fds_base; - int nfds = -1; - - FD_ZERO(&fds_base); - if (use_ipv4) { - socket_set.ups4 = getUDPSocket(AF_INET, port); - FD_SET(socket_set.ups4, &fds_base); - nfds = max(nfds, socket_set.ups4); - socket_set.tps4 = getTCPSocket(AF_INET, port); - FD_SET(socket_set.tps4, &fds_base); - nfds = max(nfds, socket_set.tps4); - } - if (use_ipv6) { - socket_set.ups6 = getUDPSocket(AF_INET6, port); - FD_SET(socket_set.ups6, &fds_base); - nfds = max(nfds, socket_set.ups6); - socket_set.tps6 = getTCPSocket(AF_INET6, port); - FD_SET(socket_set.tps6, &fds_base); - nfds = max(nfds, socket_set.tps6); - } - ++nfds; - - cout << "Server started." << endl; - - if (srv->configSession() == NULL) { - isc_throw(FatalError, "Config session not initalized"); - } - - int ss = srv->configSession()->getSocket(); - Message dns_message(Message::PARSE); - OutputBuffer resonse_buffer(0); - MessageRenderer response_renderer(resonse_buffer); - - running = true; - while (running) { - fd_set fds = fds_base; - FD_SET(ss, &fds); - ++nfds; - - int n = select(nfds, &fds, NULL, NULL, NULL); - if (n < 0) { - if (errno != EINTR) { - isc_throw(FatalError, "select error"); - } - continue; - } - - if (socket_set.ups4 >= 0 && FD_ISSET(socket_set.ups4, &fds)) { - processMessageUDP(socket_set.ups4, dns_message, response_renderer); - } - if (socket_set.ups6 >= 0 && FD_ISSET(socket_set.ups6, &fds)) { - processMessageUDP(socket_set.ups6, dns_message, response_renderer); - } - if (socket_set.tps4 >= 0 && FD_ISSET(socket_set.tps4, &fds)) { - processMessageTCP(socket_set.tps4, dns_message, response_renderer); - } - if (socket_set.tps6 >= 0 && FD_ISSET(socket_set.tps6, &fds)) { - processMessageTCP(socket_set.tps6, dns_message, response_renderer); - } - if (FD_ISSET(ss, &fds)) { - srv->configSession()->checkCommand(); - } - } -} -#endif // HAVE_BOOST_SYSTEM - void usage() { cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl; @@ -743,7 +127,7 @@ main(int argc, char* argv[]) { } if (!use_ipv4 && !use_ipv6) { - cerr << "-4 and -6 can't coexist" << endl; + cerr << "[b10-auth] Error: -4 and -6 can't coexist" << endl; usage(); } @@ -751,8 +135,8 @@ main(int argc, char* argv[]) { int ret = 0; try { string specfile; - if (getenv("B10_FROM_SOURCE")) { - specfile = string(getenv("B10_FROM_SOURCE")) + + if (getenv("B10_FROM_BUILD")) { + specfile = string(getenv("B10_FROM_BUILD")) + "/src/bin/auth/auth.spec"; } else { specfile = string(AUTH_SPECFILE_LOCATION); @@ -761,22 +145,23 @@ main(int argc, char* argv[]) { auth_server = new AuthSrv; auth_server->setVerbose(verbose_mode); -#ifdef HAVE_BOOST_SYSTEM - ModuleCCSession cs(specfile, io_service_, my_config_handler, - my_command_handler); -#else - ModuleCCSession cs(specfile, my_config_handler, my_command_handler); -#endif + io_service = new asio_link::IOService(auth_server, port, use_ipv4, + use_ipv6); + + ModuleCCSession cs(specfile, io_service->get_io_service(), my_config_handler, my_command_handler); auth_server->setConfigSession(&cs); auth_server->updateConfig(ElementPtr()); - run_server(port, use_ipv4, use_ipv6, auth_server); + + cout << "[b10-auth] Server started." << endl; + io_service->run(); } catch (const std::exception& ex) { - cerr << ex.what() << endl; + cerr << "[b10-auth] " << ex.what() << endl; ret = 1; } + delete io_service; delete auth_server; return (ret); } diff --git a/src/bin/auth/spec_config.h.in b/src/bin/auth/spec_config.h.pre.in index da9d025cdc..52581ddbc6 100644 --- a/src/bin/auth/spec_config.h.in +++ b/src/bin/auth/spec_config.h.pre.in @@ -1,16 +1,16 @@ -// Copyright (C) 2009 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. - -#define AUTH_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/auth.spec" -#define UNIX_SOCKET_FILE "@prefix@/var/auth_xfrout_conn" +// Copyright (C) 2009 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.
+
+#define AUTH_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/auth.spec"
+#define UNIX_SOCKET_FILE "@@LOCALSTATEDIR@@/auth_xfrout_conn"
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index f89803fac8..ed9deb552f 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -1,7 +1,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin +AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\" +AM_CXXFLAGS = $(B10_CXXFLAGS) + CLEANFILES = *.gcno *.gcda TESTS = @@ -21,10 +24,6 @@ run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a run_unittests_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a -if HAVE_BOOST_SYSTEM -run_unittests_LDFLAGS += $(BOOST_LDFLAGS) -run_unittests_LDADD += $(BOOST_SYSTEM_LIB) -endif endif noinst_PROGRAMS = $(TESTS) |