2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-04 16:05:17 +00:00

[trac749] Merge 'master' into trac749

Conflicts:
	src/bin/auth/auth_srv.h
	src/bin/resolver/resolver.h
	src/lib/Makefile.am
	src/lib/asiodns/dns_lookup.h
	src/lib/asiodns/io_fetch.cc
	src/lib/asiodns/io_fetch.h
	src/lib/asiodns/qid_gen.cc
	src/lib/asiodns/qid_gen.h
	src/lib/asiodns/tcp_server.cc
	src/lib/asiodns/tests/qid_gen_unittest.cc
	src/lib/asiodns/udp_server.cc
	src/lib/asiolink/Makefile.am
	src/lib/asiolink/qid_gen.cc
	src/lib/asiolink/qid_gen.h
	src/lib/asiolink/tests/Makefile.am
	src/lib/asiolink/tests/qid_gen_unittest.cc
	src/lib/asiolink/tests/tcp_socket_unittest.cc
	src/lib/asiolink/tests/udp_socket_unittest.cc
	src/lib/dns/rdata.h
	src/lib/resolve/recursive_query.cc
	src/lib/resolve/recursive_query.h
	src/lib/resolve/tests/recursive_query_unittest_2.cc
	src/lib/testutils/srv_test.cc
	src/lib/util/Makefile.am
	src/lib/util/io_utilities.h
	src/lib/util/random/qid_gen.cc
	src/lib/util/random/qid_gen.h
	src/lib/util/tests/io_utilities_unittest.cc
	src/lib/util/tests/qid_gen_unittest.cc
This commit is contained in:
chenzhengzhang
2011-04-19 13:56:30 +08:00
219 changed files with 16434 additions and 1058 deletions

514
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@@ -624,6 +624,8 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/benchmarks/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
src/bin/sockcreator/Makefile
src/bin/sockcreator/tests/Makefile
src/bin/xfrin/Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
@@ -642,6 +644,8 @@ AC_CONFIG_FILES([Makefile
src/lib/Makefile
src/lib/asiolink/Makefile
src/lib/asiolink/tests/Makefile
src/lib/asiodns/Makefile
src/lib/asiodns/tests/Makefile
src/lib/bench/Makefile
src/lib/bench/example/Makefile
src/lib/bench/tests/Makefile
@@ -667,6 +671,11 @@ AC_CONFIG_FILES([Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
src/lib/util/Makefile
src/lib/util/io/Makefile
src/lib/util/io/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/util/tests/Makefile
src/lib/dns/Makefile
src/lib/dns/tests/Makefile
src/lib/dns/tests/testdata/Makefile
@@ -677,8 +686,6 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/Makefile
src/lib/datasrc/tests/Makefile
src/lib/xfr/Makefile
src/lib/util/Makefile
src/lib/util/tests/Makefile
src/lib/log/Makefile
src/lib/log/compiler/Makefile
src/lib/log/tests/Makefile
@@ -694,6 +701,9 @@ AC_CONFIG_FILES([Makefile
src/lib/server_common/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
tests/tools/badpacket/Makefile
tests/tools/badpacket/tests/Makefile
])
AC_OUTPUT([doc/version.ent
src/bin/cfgmgr/b10-cfgmgr.py
@@ -708,6 +718,7 @@ AC_OUTPUT([doc/version.ent
src/bin/xfrout/xfrout.py
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
src/bin/xfrout/tests/xfrout_test.py
src/bin/xfrout/run_b10-xfrout.sh
src/bin/resolver/resolver.spec.pre
src/bin/resolver/spec_config.h.pre

View File

@@ -568,7 +568,11 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns \
../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth \
../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ \
../src/lib/nsas ../src/lib/testutils ../src/lib/cache \
../src/lib/server_common/ ../src/bin/sockcreator/ ../src/lib/util/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@@ -1,4 +1,4 @@
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
usermgr zonemgr stats tests resolver
usermgr zonemgr stats tests resolver sockcreator
check-recursive: all-recursive

View File

@@ -39,9 +39,9 @@ pkglibexec_PROGRAMS = b10-auth
b10_auth_SOURCES = query.cc query.h
b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += auth_config.cc auth_config.h
b10_auth_SOURCES += command.cc command.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
@@ -49,6 +49,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la

View File

@@ -29,7 +29,7 @@
#include <datasrc/zonetable.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/common.h>
#include <server_common/portconfig.h>

View File

@@ -53,7 +53,7 @@
#include <xfr/xfrout_client.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
#include <auth/statistics.h>
@@ -70,7 +70,8 @@ using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::config;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace isc::server_common::portconfig;
class AuthSrvImpl {
@@ -768,6 +769,6 @@ AuthSrv::setListenAddresses(const AddressList& addresses) {
}
void
AuthSrv::setDNSService(asiolink::DNSService& dnss) {
AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}

View File

@@ -23,6 +23,15 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <dns/message.h>
#include <util/buffer.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#include <asiolink/io_message.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
#include <asiolink/asiolink.h>
#include <server_common/portconfig.h>
@@ -107,10 +116,10 @@ public:
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const asiolink::IOMessage& io_message,
void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::util::OutputBufferPtr buffer,
asiolink::DNSServer* server);
isc::asiodns::DNSServer* server);
/// \brief Set verbose flag
///
@@ -193,16 +202,16 @@ public:
void setConfigSession(isc::config::ModuleCCSession* config_session);
/// \brief Return this object's ASIO IO Service queue
asiolink::IOService& getIOService();
isc::asiolink::IOService& getIOService();
/// \brief Return pointer to the DNS Lookup callback function
asiolink::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
isc::asiodns::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
asiolink::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
isc::asiodns::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
isc::asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
/// \brief Set or update the size (number of slots) of hot spot cache.
///
@@ -363,15 +372,15 @@ public:
const;
/// \brief Assign an ASIO DNS Service queue to this Auth object
void setDNSService(asiolink::DNSService& dnss);
void setDNSService(isc::asiodns::DNSService& dnss);
private:
AuthSrvImpl* impl_;
asiolink::SimpleCallback* checkin_;
asiolink::DNSLookup* dns_lookup_;
asiolink::DNSAnswer* dns_answer_;
asiolink::DNSService* dnss_;
isc::asiolink::SimpleCallback* checkin_;
isc::asiodns::DNSLookup* dns_lookup_;
isc::asiodns::DNSAnswer* dns_answer_;
isc::asiodns::DNSService* dnss_;
};
#endif // __AUTH_SRV_H

View File

@@ -10,7 +10,7 @@ noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
query_bench_SOURCES += ../auth_config.h ../auth_config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
@@ -22,6 +22,7 @@ query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(SQLITE_LIBS)

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <stdlib.h>
#include <iostream>
@@ -31,9 +33,10 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/query.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
using namespace std;
@@ -44,7 +47,8 @@ using namespace isc::dns;
using namespace isc::util;
using namespace isc::xfr;
using namespace isc::bench;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {
// Commonly used constant:

36
src/bin/auth/common.cc Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (C) 2009-2011 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.
#include <auth/common.h>
#include <auth/spec_config.h>
#include <stdlib.h>
using std::string;
string getXfroutSocketPath() {
if (getenv("B10_FROM_BUILD") != NULL) {
if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
return (string(getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) +
"/auth_xfrout_conn");
} else {
return (string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn");
}
} else {
if (getenv("BIND10_XFROUT_SOCKET_FILE")) {
return (getenv("BIND10_XFROUT_SOCKET_FILE"));
} else {
return (UNIX_SOCKET_FILE);
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2009-2011 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
@@ -29,6 +29,15 @@ public:
{}
};
/// \short Get the path of socket to talk to xfrout
///
/// It takes some environment variables into account (B10_FROM_BUILD,
/// B10_FROM_SOURCE_LOCALSTATEDIR and BIND10_XFROUT_SOCKET_FILE). It
/// also considers the installation prefix.
///
/// The logic should be the same as in b10-xfrout, so they find each other.
std::string getXfroutSocketPath();
#endif // __COMMON_H
// Local Variables:

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2009-2011 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
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -38,10 +40,11 @@
#include <auth/spec_config.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/command.h>
#include <auth/change_user.h>
#include <auth/auth_srv.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <log/dummylog.h>
@@ -52,7 +55,8 @@ using namespace isc::config;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
namespace {
@@ -122,19 +126,7 @@ main(int argc, char* argv[]) {
bool xfrin_session_established = false; // XXX (see Trac #287)
bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
string xfrout_socket_path;
if (getenv("B10_FROM_BUILD") != NULL) {
if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
xfrout_socket_path = string("B10_FROM_SOURCE_LOCALSTATEDIR") +
"/auth_xfrout_conn";
} else {
xfrout_socket_path = string(getenv("B10_FROM_BUILD")) +
"/auth_xfrout_conn";
}
} else {
xfrout_socket_path = UNIX_SOCKET_FILE;
}
XfroutClient xfrout_client(xfrout_socket_path);
XfroutClient xfrout_client(getXfroutSocketPath());
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {

View File

@@ -1,4 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,18 +17,23 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
BUILT_SOURCES = ../spec_config.h
TESTS += run_unittests
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../config.h ../config.cc
run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
run_unittests_SOURCES += ../command.h ../command.cc
run_unittests_SOURCES += ../common.h ../common.cc
run_unittests_SOURCES += ../spec_config.h
run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += command_unittest.cc
run_unittests_SOURCES += common_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
@@ -39,6 +45,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

View File

@@ -45,7 +45,8 @@ using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::testutils;
using namespace isc::server_common::portconfig;
using isc::UnitTestUtil;

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <cassert>
#include <cstdlib>
#include <string>
@@ -32,7 +34,7 @@
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/command.h>
#include <asiolink/asiolink.h>
@@ -97,7 +99,7 @@ AuthConmmandTest::stopServer() {
}
TEST_F(AuthConmmandTest, shutdown) {
asiolink::IntervalTimer itimer(server.getIOService());
isc::asiolink::IntervalTimer itimer(server.getIOService());
itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1);
server.getIOService().run();
EXPECT_EQ(0, rcode);

View File

@@ -0,0 +1,96 @@
// Copyright (C) 2011 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.
#include <gtest/gtest.h>
#include <auth/common.h>
#include <auth/spec_config.h>
#include <vector>
#include <string>
#include <cstdio>
#include <boost/foreach.hpp>
using std::pair;
using std::vector;
using std::string;
namespace {
class Paths : public ::testing::Test {
private:
typedef pair<string, string*> Environ;
vector<Environ> restoreEnviron;
public:
void TearDown() {
// Restore the original environment
BOOST_FOREACH(const Environ &env, restoreEnviron) {
if (env.second == NULL) {
EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
"Couldn't restore environment, results of other tests"
"are uncertain";
} else {
EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
1)) << "Couldn't restore environment, "
"results of other tests are uncertain";
}
}
}
protected:
// Sets a temporary value into environment. If value is empty, it deletes
// the variable from environment (just for simplicity).
void setEnv(const string& name, const string& value) {
// Backup the original environment
char* env(getenv(name.c_str()));
restoreEnviron.push_back(Environ(name, env == NULL ? NULL :
new string(env)));
// Set the new value
if (value.empty()) {
EXPECT_EQ(0, unsetenv(name.c_str()));
} else {
EXPECT_EQ(0, setenv(name.c_str(), value.c_str(), 1));
}
}
// Test getXfroutSocketPath under given environment
void testXfrout(const string& fromBuild, const string& localStateDir,
const string& socketFile, const string& expected)
{
setEnv("B10_FROM_BUILD", fromBuild);
setEnv("B10_FROM_SOURCE_LOCALSTATEDIR", localStateDir);
setEnv("BIND10_XFROUT_SOCKET_FILE", socketFile);
EXPECT_EQ(expected, getXfroutSocketPath());
}
};
// Test that when we have no special environment, we get the default from prefix
TEST_F(Paths, xfroutNoEnv) {
testXfrout("", "", "", UNIX_SOCKET_FILE);
}
// Override by B10_FROM_BUILD
TEST_F(Paths, xfroutFromBuild) {
testXfrout("/from/build", "", "/wrong/path",
"/from/build/auth_xfrout_conn");
}
// Override by B10_FROM_SOURCE_LOCALSTATEDIR
TEST_F(Paths, xfroutLocalStatedir) {
testXfrout("/wrong/path", "/state/dir", "/wrong/path",
"/state/dir/auth_xfrout_conn");
}
// Override by BIND10_XFROUT_SOCKET_FILE explicitly
TEST_F(Paths, xfroutFromEnv) {
testXfrout("", "", "/the/path/to/file", "/the/path/to/file");
}
}

View File

@@ -26,7 +26,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/common.h>
#include <testutils/mockups.h>
@@ -35,7 +35,8 @@
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {
class AuthConfigTest : public ::testing::Test {

View File

@@ -2,12 +2,12 @@
.\" Title: bind10
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: February 22, 2011
.\" Date: March 31, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
.TH "BIND10" "8" "February 22, 2011" "BIND10" "BIND10"
.TH "BIND10" "8" "March 31, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
bind10 \- BIND 10 boss process
.SH "SYNOPSIS"
.HP \w'\fBbind10\fR\ 'u
\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-brittle\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
.SH "DESCRIPTION"
.PP
The
@@ -32,6 +32,34 @@ daemon starts up other BIND 10 required daemons\&. It handles restarting of exit
.PP
The arguments are as follows:
.PP
\fB\-\-brittle\fR
.RS 4
Shutdown if any of the child processes of
\fBbind10\fR
exit\&. This is intended to help developers debug the server, and should not be used in production\&.
.RE
.PP
\fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-file\fR \fIconfig\-filename\fR
.RS 4
The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&.
.sp
Defaults to b10\-config\&.db\&.
.RE
.PP
\fB\-\-cmdctl\-port\fR \fIport\fR
.RS 4
The
\fBb10\-cmdctl\fR
daemon will listen on this port\&. (See
b10\-cmdctl(8)
for the default\&.)
.RE
.PP
\fB\-p\fR \fIdirectory\fR, \fB\-\-data\-path\fR \fIdirectory\fR
.RS 4
The path where BIND 10 programs look for various data files\&. Currently only b10\-cfgmgr uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
.RE
.PP
\fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
.RS 4
The UNIX domain socket file for the
@@ -57,6 +85,13 @@ to run as\&.
must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
.RE
.PP
\fB\-\-pid\-file\fR \fIfilename\fR
.RS 4
If defined, the PID of the
\fBbind10\fR
is stored in this file\&. This is used for testing purposes\&.
.RE
.PP
\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
.RS 4
The name this process should have in tools like

View File

@@ -202,7 +202,7 @@ class BoB:
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, nocache=False, verbose=False, setuid=None,
username=None, cmdctl_port=None):
username=None, cmdctl_port=None, brittle=False):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
@@ -235,6 +235,7 @@ class BoB:
self.data_path = data_path
self.config_filename = config_filename
self.cmdctl_port = cmdctl_port
self.brittle = brittle
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
@@ -296,7 +297,7 @@ class BoB:
def command_handler(self, command, args):
if self.verbose:
sys.stdout.write("[bind10] Boss got command: " + command + "\n")
sys.stdout.write("[bind10] Boss got command: " + str(command) + "\n")
answer = isc.config.ccsession.create_answer(1, "command not implemented")
if type(command) != str:
answer = isc.config.ccsession.create_answer(1, "bad command")
@@ -304,6 +305,15 @@ class BoB:
if command == "shutdown":
self.runnable = False
answer = isc.config.ccsession.create_answer(0)
elif command == "sendstats":
# send statistics data to the stats daemon immediately
cmd = isc.config.ccsession.create_command(
'set', { "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}})
seq = self.cc_session.group_sendmsg(cmd, 'Stats')
self.cc_session.group_recvmsg(True, seq)
answer = isc.config.ccsession.create_answer(0)
elif command == "ping":
answer = isc.config.ccsession.create_answer(0, "pong")
elif command == "show_processes":
@@ -710,20 +720,22 @@ class BoB:
if self.verbose:
sys.stdout.write("[bind10] All processes ended, server done.\n")
def _get_process_exit_status(self):
return os.waitpid(-1, os.WNOHANG)
def reap_children(self):
"""Check to see if any of our child processes have exited,
and note this for later handling.
"""
while True:
try:
(pid, exit_status) = os.waitpid(-1, os.WNOHANG)
(pid, exit_status) = self._get_process_exit_status()
except OSError as o:
if o.errno == errno.ECHILD: break
# XXX: should be impossible to get any other error here
raise
if pid == 0: break
if pid in self.processes:
# One of the processes we know about. Get information on it.
proc_info = self.processes.pop(pid)
proc_info.restart_schedule.set_run_stop_time()
@@ -747,6 +759,11 @@ class BoB:
sys.stdout.write(
"[bind10] The b10-msgq process died, shutting down.\n")
self.runnable = False
# If we're in 'brittle' mode, we want to shutdown after
# any process dies.
if self.brittle:
self.runnable = False
else:
sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
@@ -856,6 +873,8 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
parser.add_option("--brittle", dest="brittle", action="store_true",
help="debugging flag: exit if any component dies")
(options, args) = parser.parse_args(args)
@@ -959,7 +978,7 @@ def main():
# Go bob!
boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
options.config_file, options.nocache, options.verbose,
setuid, username, options.cmdctl_port)
setuid, username, options.cmdctl_port, options.brittle)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
@@ -967,17 +986,6 @@ def main():
sys.stdout.write("[bind10] BIND 10 started\n")
dump_pid(options.pid_file)
# send "bind10.boot_time" to b10-stats
time.sleep(1) # wait a second
if options.verbose:
sys.stdout.write("[bind10] send \"bind10.boot_time\" to b10-stats\n")
cmd = isc.config.ccsession.create_command('set',
{ "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}
})
boss_of_bind.cc_session.group_sendmsg(cmd, 'Stats')
# In our main loop, we check for dead processes or messages
# on the c-channel.
wakeup_fd = wakeup_pipe[0]

View File

@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
<date>February 22, 2011</date>
<date>March 31, 2011</date>
</refentryinfo>
<refmeta>
@@ -44,16 +44,21 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>bind10</command>
<arg><option>-c <replaceable>config-filename</replaceable></option></arg>
<arg><option>-m <replaceable>file</replaceable></option></arg>
<arg><option>-n</option></arg>
<arg><option>-p <replaceable>data_path</replaceable></option></arg>
<arg><option>-u <replaceable>user</replaceable></option></arg>
<arg><option>-v</option></arg>
<arg><option>-c<replaceable>config-filename</replaceable></option></arg>
<arg><option>-p<replaceable>data_path</replaceable></option></arg>
<arg><option>--brittle</option></arg>
<arg><option>--cmdctl-port</option> <replaceable>port</replaceable></arg>
<arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
<arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
<arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
<arg><option>--no-cache</option></arg>
<arg><option>--user <replaceable>user</replaceable></option></arg>
<arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
<arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
<arg><option>--user <replaceable>user</replaceable></option></arg>
<arg><option>--verbose</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -82,9 +87,24 @@
<para>The arguments are as follows:</para>
<variablelist>
<varlistentry>
<term>
<option>-c</option><replaceable>config-filename</replaceable>,
<option>--brittle</option>
</term>
<listitem>
<para>
Shutdown if any of the child processes of
<command>bind10</command> exit. This is intended to
help developers debug the server, and should not be
used in production.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-c</option> <replaceable>config-filename</replaceable>,
<option>--config-file</option> <replaceable>config-filename</replaceable>
</term>
<listitem>
@@ -97,8 +117,22 @@
<varlistentry>
<term>
<option>-p</option><replaceable>data-path</replaceable>,
<option>--data-path</option> <replaceable>data-path</replaceable>
<option>--cmdctl-port</option> <replaceable>port</replaceable>
</term>
<listitem>
<para>The <command>b10-cmdctl</command> daemon will listen
on this port.
(See
<refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
for the default.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-p</option> <replaceable>directory</replaceable>,
<option>--data-path</option> <replaceable>directory</replaceable>
</term>
<listitem>
<para>The path where BIND 10 programs look for various data files.
@@ -134,7 +168,6 @@
<varlistentry>
<term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
<listitem>
<para>The username for <command>bind10</command> to run as.
<!-- TODO: example more detail. -->
@@ -144,6 +177,16 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pid-file</option> <replaceable>filename</replaceable></term>
<listitem>
<para>If defined, the PID of the <command>bind10</command> is stored
in this file.
This is used for testing purposes.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pretty-name <replaceable>name</replaceable></option></term>

View File

@@ -22,6 +22,11 @@
"command_description": "Shut down BIND 10",
"command_args": []
},
{
"command_name": "sendstats",
"command_description": "Send data to a statistics module at once",
"command_args": []
},
{
"command_name": "ping",
"command_description": "Ping the boss process",

View File

@@ -1,5 +1,6 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
#PYTESTS = args_test.py bind10_test.py
# NOTE: this has a generated test found in the builddir
PYTESTS = bind10_test.py
EXTRA_DIST = $(PYTESTS)
@@ -14,5 +15,5 @@ endif
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done

View File

@@ -13,7 +13,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file
from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
@@ -24,6 +24,9 @@ import os
import signal
import socket
from isc.net.addr import IPAddr
import time
import isc
from isc.testutils.parse_args import TestOptParser, OptsError
class TestProcessInfo(unittest.TestCase):
@@ -123,6 +126,41 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False)
def test_command_handler(self):
class DummySession():
def group_sendmsg(self, msg, group):
(self.msg, self.group) = (msg, group)
def group_recvmsg(self, nonblock, seq): pass
bob = BoB()
bob.verbose = True
bob.cc_session = DummySession()
# a bad command
self.assertEqual(bob.command_handler(-1, None),
isc.config.ccsession.create_answer(1, "bad command"))
# "shutdown" command
self.assertEqual(bob.command_handler("shutdown", None),
isc.config.ccsession.create_answer(0))
self.assertFalse(bob.runnable)
# "sendstats" command
self.assertEqual(bob.command_handler("sendstats", None),
isc.config.ccsession.create_answer(0))
self.assertEqual(bob.cc_session.group, "Stats")
self.assertEqual(bob.cc_session.msg,
isc.config.ccsession.create_command(
'set', { "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}}))
# "ping" command
self.assertEqual(bob.command_handler("ping", None),
isc.config.ccsession.create_answer(0, "pong"))
# "show_processes" command
self.assertEqual(bob.command_handler("show_processes", None),
isc.config.ccsession.create_answer(0,
bob.get_processes()))
# an unknown command
self.assertEqual(bob.command_handler("__UNKNOWN__", None),
isc.config.ccsession.create_answer(1, "Unknown command"))
# Class for testing the BoB without actually starting processes.
# This is used for testing the start/stop components routines and
# the BoB commands.
@@ -156,42 +194,52 @@ class MockBob(BoB):
def start_msgq(self, c_channel_env):
self.msgq = True
self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
self.processes[2].pid = 2
def start_cfgmgr(self, c_channel_env):
self.cfgmgr = True
self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
self.processes[3].pid = 3
def start_ccsession(self, c_channel_env):
self.ccsession = True
self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
self.processes[4].pid = 4
def start_auth(self, c_channel_env):
self.auth = True
self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
self.processes[5].pid = 5
def start_resolver(self, c_channel_env):
self.resolver = True
self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
self.processes[6].pid = 6
def start_xfrout(self, c_channel_env):
self.xfrout = True
self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
self.processes[7].pid = 7
def start_xfrin(self, c_channel_env):
self.xfrin = True
self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
self.processes[8].pid = 8
def start_zonemgr(self, c_channel_env):
self.zonemgr = True
self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
self.processes[9].pid = 9
def start_stats(self, c_channel_env):
self.stats = True
self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
self.processes[10].pid = 10
def start_cmdctl(self, c_channel_env):
self.cmdctl = True
self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false'])
self.processes[11].pid = 11
# We don't really use all of these stop_ methods. But it might turn out
# someone would add some stop_ method to BoB and we want that one overriden
@@ -550,6 +598,15 @@ class TestParseArgs(unittest.TestCase):
options = parse_args(['--cmdctl-port=1234'], TestOptParser)
self.assertEqual(1234, options.cmdctl_port)
def test_brittle(self):
"""
Test we can use the "brittle" flag.
"""
options = parse_args([], TestOptParser)
self.assertFalse(options.brittle)
options = parse_args(['--brittle'], TestOptParser)
self.assertTrue(options.brittle)
class TestPIDFile(unittest.TestCase):
def setUp(self):
self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
@@ -597,5 +654,34 @@ class TestPIDFile(unittest.TestCase):
self.assertRaises(IOError, dump_pid,
'nonexistent_dir' + os.sep + 'bind10.pid')
class TestBrittle(unittest.TestCase):
def test_brittle_disabled(self):
bob = MockBob()
bob.start_all_processes()
bob.runnable = True
bob.reap_children()
self.assertTrue(bob.runnable)
def simulated_exit(self):
ret_val = self.exit_info
self.exit_info = (0, 0)
return ret_val
def test_brittle_enabled(self):
bob = MockBob()
bob.start_all_processes()
bob.runnable = True
bob.brittle = True
self.exit_info = (5, 0)
bob._get_process_exit_status = self.simulated_exit
old_stdout = sys.stdout
sys.stdout = open("/dev/null", "w")
bob.reap_children()
sys.stdout = old_stdout
self.assertFalse(bob.runnable)
if __name__ == '__main__':
unittest.main()

View File

@@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiodns
AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiodns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -45,6 +47,7 @@ b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -25,6 +27,7 @@
#include <boost/foreach.hpp>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <exceptions/exceptions.h>
@@ -56,7 +59,8 @@ using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
using isc::log::dlog;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {

View File

@@ -20,6 +20,7 @@
#include <vector>
#include <cassert>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <boost/foreach.hpp>
@@ -56,7 +57,8 @@ using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
using isc::log::dlog;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::server_common::portconfig;
class ResolverImpl {
@@ -347,7 +349,7 @@ Resolver::~Resolver() {
}
void
Resolver::setDNSService(asiolink::DNSService& dnss) {
Resolver::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}

View File

@@ -21,8 +21,16 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <dns/message.h>
#include <util/buffer.h>
#include <asiolink/asiolink.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_service.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#include <asiolink/io_message.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
@@ -74,11 +82,11 @@ public:
/// shall return to the client
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
void processMessage(const asiolink::IOMessage& io_message,
void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr query_message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer,
asiolink::DNSServer* server);
isc::asiodns::DNSServer* server);
/// \brief Set and get the config session
isc::config::ModuleCCSession* getConfigSession() const;
@@ -88,7 +96,7 @@ public:
isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
/// \brief Assign an ASIO IO Service queue to this Resolver object
void setDNSService(asiolink::DNSService& dnss);
void setDNSService(isc::asiodns::DNSService& dnss);
/// \brief Assign a NameserverAddressStore to this Resolver object
void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
@@ -97,7 +105,7 @@ public:
void setCache(isc::cache::ResolverCache& cache);
/// \brief Return this object's ASIO IO Service queue
asiolink::DNSService& getDNSService() const { return (*dnss_); }
isc::asiodns::DNSService& getDNSService() const { return (*dnss_); }
/// \brief Returns this object's NSAS
isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
@@ -110,13 +118,13 @@ public:
};
/// \brief Return pointer to the DNS Lookup callback function
asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
isc::asiodns::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
asiolink::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
isc::asiodns::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
isc::asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
/**
* \brief Tell the Resolver that is has already been configured
@@ -230,10 +238,10 @@ public:
private:
ResolverImpl* impl_;
asiolink::DNSService* dnss_;
asiolink::SimpleCallback* checkin_;
asiolink::DNSLookup* dns_lookup_;
asiolink::DNSAnswer* dns_answer_;
isc::asiodns::DNSService* dnss_;
isc::asiolink::SimpleCallback* checkin_;
isc::asiodns::DNSLookup* dns_lookup_;
isc::asiodns::DNSAnswer* dns_answer_;
isc::nsas::NameserverAddressStore* nsas_;
isc::cache::ResolverCache* cache_;
// This value is initally false, and will be set to true

View File

@@ -26,7 +26,7 @@ using namespace std;
// Compare addresses etc.
ResponseScrubber::Category ResponseScrubber::addressCheck(
const asiolink::IOEndpoint& to, const asiolink::IOEndpoint& from)
const isc::asiolink::IOEndpoint& to, const isc::asiolink::IOEndpoint& from)
{
if (from.getProtocol() == to.getProtocol()) {
if (from.getAddress() == to.getAddress()) {

View File

@@ -282,8 +282,8 @@ public:
///
/// \return SUCCESS if the two endpoints match, otherwise an error status
/// indicating what was incorrect.
static Category addressCheck(const asiolink::IOEndpoint& to,
const asiolink::IOEndpoint& from);
static Category addressCheck(const isc::asiolink::IOEndpoint& to,
const isc::asiolink::IOEndpoint& from);
/// \brief Check QID
///

View File

@@ -31,6 +31,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

View File

@@ -12,12 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <string>
#include <gtest/gtest.h>
#include <cc/data.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <resolver/resolver.h>
@@ -29,7 +32,8 @@
using namespace std;
using namespace isc::data;
using namespace isc::testutils;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using isc::UnitTestUtil;
namespace {

View File

@@ -41,6 +41,7 @@
// Class for endpoint checks. The family of the endpoint is set in the
// constructor; the address family by the string provided for the address.
namespace isc {
namespace asiolink {
class GenericEndpoint : public IOEndpoint {
@@ -73,13 +74,14 @@ private:
short protocol_; // Protocol of the endpoint
};
}
}
using namespace asio::ip;
using namespace isc::dns;
using namespace rdata;
using namespace isc::dns::rdata::generic;
using namespace isc::dns::rdata::in;
using namespace asiolink;
using namespace isc::asiolink;
// Test class

View File

@@ -0,0 +1,18 @@
SUBDIRS = tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = *.gcno *.gcda
pkglibexec_PROGRAMS = b10-sockcreator
b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la

View File

@@ -0,0 +1,49 @@
The socket creator
==================
The only thing we need higher rights than standard user is binding sockets to
ports lower than 1024. So we will have a separate process that keeps the
rights, while the rests drop them for security reasons.
This process is the socket creator. Its goal is to be as simple as possible
and to contain as little code as possible to minimise the amount of code
running with higher privileges (to minimize the number of bugs and make
checking/auditing it easier). It uses low-level OS API instead of some
fancy library for that reason. It has only fixed-length reads so there's no
place for buffer overruns.
Protocol
--------
It talks with whoever started it by its stdin/stdout. It reads simple
binary protocol from stdin and does what the commands ask. Command is a single
byte (usually from the printable range, so it is easier to debug and guess
what it does), followed by parameters.
Note that as send_fd and recv_fd works only with unix domain socket, it's stdio
must be a socket, not pipe.
* 'T': It has no parameters. It asks the socket creator to terminate.
* 'S' 'U|T' '4|6' port address: Asks it to create a port. First parameter
tels the socket type (either UDP or TCP). The second one is address family
(either IPv4 or IPv6). Then there's 2 bytes of the port number, in the
network byte order. The last one is either 4 or 16 bytes of address, as
they would be passed to bind (note that both parameters are already prepared,
like hton called on them).
The answer to this is either 'S' directly followed by the socket (using
sendmsg) if it is successful. If it fails, 'E' is returned instead, followed
by either 'S' or 'B' (either socket() or bind() call failed). Then there is
one int (architecture-dependent length and endianess), which is the errno
value after the failure.
The creator may also send these messages at any time (but not in the middle
of another message):
* 'F': A fatal error has been detected. It is followed by one byte of error
condition code and then the creator terminates with non-zero status.
The conditions are:
* 'I': Invalid input (eg. someone sent a wrong letter and it does not
understand it).

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2011 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.
#include "sockcreator.h"
using namespace isc::socket_creator;
int
main() {
/*
* TODO Maybe use some OS-specific caps interface and drop everything
* but ability to bind ports? It would be nice.
*/
return run(0, 1); // Read commands from stdin, output to stdout
}

View File

@@ -0,0 +1,151 @@
// Copyright (C) 2011 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.
#include "sockcreator.h"
#include <util/io/fd.h>
#include <unistd.h>
#include <cerrno>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
using namespace isc::util::io;
namespace isc {
namespace socket_creator {
int
get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
{
int sock(socket(bind_addr->sa_family, type, 0));
if (sock == -1) {
return -1;
}
if (bind(sock, bind_addr, addr_len) == -1) {
return -2;
}
return sock;
}
// These are macros so they can exit the function
#define READ(WHERE, HOW_MANY) do { \
size_t how_many = (HOW_MANY); \
if (read_data(input_fd, (WHERE), how_many) < how_many) { \
return 1; \
} \
} while (0)
#define WRITE(WHAT, HOW_MANY) do { \
if (!write_data(output_fd, (WHAT), (HOW_MANY))) { \
return 2; \
} \
} while (0)
#define DEFAULT \
default: /* Unrecognized part of protocol */ \
WRITE("FI", 2); \
return 3;
int
run(const int input_fd, const int output_fd, const get_sock_t get_sock,
const send_fd_t send_fd)
{
for (;;) {
// Read the command
char command;
READ(&command, 1);
switch (command) {
case 'T': // The "terminate" command
return 0;
case 'S': { // Create a socket
// Read what type of socket they want
char type[2];
READ(type, 2);
// Read the address they ask for
struct sockaddr *addr(NULL);
size_t addr_len(0);
struct sockaddr_in addr_in;
struct sockaddr_in6 addr_in6;
switch (type[1]) { // The address family
/*
* Here are some casts. They are required by C++ and
* the low-level interface (they are implicit in C).
*/
case '4':
addr = static_cast<struct sockaddr *>(
static_cast<void *>(&addr_in));
addr_len = sizeof addr_in;
memset(&addr_in, 0, sizeof addr_in);
addr_in.sin_family = AF_INET;
READ(static_cast<char *>(static_cast<void *>(
&addr_in.sin_port)), 2);
READ(static_cast<char *>(static_cast<void *>(
&addr_in.sin_addr.s_addr)), 4);
break;
case '6':
addr = static_cast<struct sockaddr *>(
static_cast<void *>(&addr_in6));
addr_len = sizeof addr_in6;
memset(&addr_in6, 0, sizeof addr_in6);
addr_in6.sin6_family = AF_INET6;
READ(static_cast<char *>(static_cast<void *>(
&addr_in6.sin6_port)), 2);
READ(static_cast<char *>(static_cast<void *>(
&addr_in6.sin6_addr.s6_addr)), 16);
break;
DEFAULT
}
int sock_type;
switch (type[0]) { // Translate the type
case 'T':
sock_type = SOCK_STREAM;
break;
case 'U':
sock_type = SOCK_DGRAM;
break;
DEFAULT
}
int result(get_sock(sock_type, addr, addr_len));
if (result >= 0) { // We got the socket
WRITE("S", 1);
// FIXME: Check the output and write a test for it
send_fd(output_fd, result);
} else {
WRITE("E", 1);
switch (result) {
case -1:
WRITE("S", 1);
break;
case -2:
WRITE("B", 1);
break;
default:
return 4;
}
int error(errno);
WRITE(static_cast<char *>(static_cast<void *>(&error)),
sizeof error);
}
break;
}
DEFAULT
}
}
}
} // End of the namespaces
}

View File

@@ -0,0 +1,100 @@
// Copyright (C) 2011 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.
/**
* \file sockcreator.h
* \short Socket creator functionality.
*
* This module holds the functionality of the socket creator. It is
* a separate module from main to ease up the tests.
*/
#ifndef __SOCKCREATOR_H
#define __SOCKCREATOR_H 1
#include <util/io/fd_share.h>
#include <sys/types.h>
#include <sys/socket.h>
namespace isc {
namespace socket_creator {
/**
* \short Create a socket and bind it.
*
* This is just a bundle of socket() and bind() calls. The sa_family of
* bind_addr is used to determine the domain of the socket.
*
* \return The file descriptor of the newly created socket, if everything
* goes well. A negative number is returned if an error occurs -
* -1 if the socket() call fails or -2 if bind() fails. In case of error,
* errno is set (or better, left intact from socket() or bind()).
* \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
* \param bind_addr The address to bind.
* \param addr_len The actual length of bind_addr.
*/
int
get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len);
/**
* Type of the get_sock function, to pass it as parameter.
*/
typedef
int
(*get_sock_t)(const int, struct sockaddr *, const socklen_t);
/**
* Type of the send_fd() function, so it can be passed as a parameter.
*/
typedef
int
(*send_fd_t)(const int, const int);
/**
* \short Infinite loop parsing commands and returning the sockets.
*
* This reads commands and socket descriptions from the input_fd
* file descriptor, creates sockets and writes the results (socket or
* error) to output_fd.
*
* Current errors are:
* - 1: Read error
* - 2: Write error
* - 3: Protocol error (unknown command, etc)
* - 4: Some internal inconsistency detected
*
* It terminates either if a command asks it to or when unrecoverable
* error happens.
*
* \return Like a return value of a main - 0 means everything OK, anything
* else is error.
* \param input_fd Here is where it reads the commads.
* \param output_fd Here is where it writes the results.
* \param get_sock_fun The function that is used to create the sockets.
* This should be left on the default value, the parameter is here
* for testing purposes.
* \param send_fd_fun The function that is used to send the socket over
* a file descriptor. This should be left on the default value, it is
* here for testing purposes.
*/
int
run(const int input_fd, const int output_fd,
const get_sock_t get_sock_fun = get_sock,
const send_fd_t send_fd_fun = isc::util::io::send_fd);
} // End of the namespaces
}
#endif // __SOCKCREATOR_H

View File

@@ -0,0 +1,25 @@
CLEANFILES = *.gcno *.gcda
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = ../sockcreator.cc ../sockcreator.h
run_unittests_SOURCES += sockcreator_tests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
run_unittests_LDADD += \
$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,22 @@
// Copyright (C) 2011 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.
#include <gtest/gtest.h>
int
main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,273 @@
// Copyright (C) 2011 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.
#include "../sockcreator.h"
#include <util/unittests/fork.h>
#include <util/io/fd.h>
#include <gtest/gtest.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>
using namespace isc::socket_creator;
using namespace isc::util::unittests;
using namespace isc::util::io;
namespace {
/*
* Generic version of the creation of socket test. It just tries to
* create the socket and checks the result is not negative (eg.
* it is valid descriptor) and that it can listen.
*
* This is a macro so ASSERT_* does abort the TEST, not just the
* function inside.
*/
#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
ADDR_SET, CHECK_SOCK) \
do { \
/*
* This should create an address that binds on all interfaces
* and lets the OS choose a free port.
*/ \
struct ADDR_TYPE addr; \
memset(&addr, 0, sizeof addr); \
ADDR_SET(addr); \
addr.FAMILY_FIELD = ADDR_FAMILY; \
struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
static_cast<void *>(&addr)); \
\
int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
/* Provide even nice error message. */ \
ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
#SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
<< socket << " and error " << strerror(errno); \
CHECK_SOCK(ADDR_TYPE, socket); \
EXPECT_EQ(0, close(socket)); \
} while (0)
// Just helper macros
#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_loopback; } while (0)
// If the get_sock returned something useful, listen must work
#define TCP_CHECK(UNUSED, SOCKET) do { \
EXPECT_EQ(0, listen(SOCKET, 1)); \
} while (0)
// More complicated with UDP, so we send a packet to ourselfs and se if it
// arrives
#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
struct ADDR_TYPE addr; \
memset(&addr, 0, sizeof addr); \
struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
static_cast<void *>(&addr)); \
\
socklen_t len = sizeof addr; \
ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)) << \
"Send failed with error " << strerror(errno) << " on socket " << \
SOCKET; \
char buffer[5]; \
ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)) << \
"Recv failed with error " << strerror(errno) << " on socket " << \
SOCKET; \
EXPECT_STREQ("test", buffer); \
} while (0)
/*
* Several tests to ensure we can create the sockets.
*/
TEST(get_sock, udp4_create) {
TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
UDP_CHECK);
}
TEST(get_sock, tcp4_create) {
TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
TCP_CHECK);
}
TEST(get_sock, udp6_create) {
TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
IN6ADDR_SET, UDP_CHECK);
}
TEST(get_sock, tcp6_create) {
TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
IN6ADDR_SET, TCP_CHECK);
}
/*
* Try to ask the get_sock function some nonsense and test if it
* is able to report error.
*/
TEST(get_sock, fail_with_nonsense) {
struct sockaddr addr;
memset(&addr, 0, sizeof addr);
ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
}
/*
* Helper functions to pass to run during testing.
*/
int
get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
{
int result(0);
int port(0);
/*
* We encode the type and address family into the int and return it.
* Lets ignore the port and address for now
* First bit is 1 if it is known type. Second tells if TCP or UDP.
* The familly is similar - third bit is known address family,
* the fourth is the family.
*/
switch (type) {
case SOCK_STREAM:
result += 1;
break;
case SOCK_DGRAM:
result += 3;
break;
}
switch (addr->sa_family) {
case AF_INET:
result += 4;
port = static_cast<struct sockaddr_in *>(
static_cast<void *>(addr))->sin_port;
break;
case AF_INET6:
result += 12;
port = static_cast<struct sockaddr_in6 *>(
static_cast<void *>(addr))->sin6_port;
break;
}
/*
* The port should be 0xffff. If it's not, we change the result.
* The port of 0xbbbb means bind should fail and 0xcccc means
* socket should fail.
*/
if (port != 0xffff) {
errno = 0;
if (port == 0xbbbb) {
return -2;
} else if (port == 0xcccc) {
return -1;
} else {
result += 16;
}
}
return result;
}
int
send_fd_dummy(const int destination, const int what)
{
/*
* Make sure it is 1 byte so we know the length. We do not use more during
* the test anyway.
*/
char fd_data(what);
if (!write_data(destination, &fd_data, 1)) {
return -1;
} else {
return 0;
}
}
/*
* Generic test that it works, with various inputs and outputs.
* It uses different functions to create the socket and send it and pass
* data to it and check it returns correct data back, to see if the run()
* parses the commands correctly.
*/
void run_test(const char *input_data, const size_t input_size,
const char *output_data, const size_t output_size,
bool should_succeed = true)
{
// Prepare the input feeder and output checker processes
int input_fd(0), output_fd(0);
pid_t input(provide_input(&input_fd, input_data, input_size)),
output(check_output(&output_fd, output_data, output_size));
ASSERT_NE(-1, input) << "Couldn't start input feeder";
ASSERT_NE(-1, output) << "Couldn't start output checker";
// Run the body
int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
// Close the pipes
close(input_fd);
close(output_fd);
// Did it run well?
if (should_succeed) {
EXPECT_EQ(0, result);
} else {
EXPECT_NE(0, result);
}
// Check the subprocesses say everything is OK too
EXPECT_TRUE(process_ok(input));
EXPECT_TRUE(process_ok(output));
}
/*
* Check it terminates successfully when asked to.
*/
TEST(run, terminate) {
run_test("T", 1, NULL, 0);
}
/*
* Check it rejects incorrect input.
*/
TEST(run, bad_input) {
run_test("XXX", 3, "FI", 2, false);
}
/*
* Check it correctly parses queries to create sockets.
*/
TEST(run, sockets) {
run_test(
"SU4\xff\xff\0\0\0\0" // This has 9 bytes
"ST4\xff\xff\0\0\0\0" // This has 9 bytes
"ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
"SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
"T", 61,
"S\x07S\x05S\x0dS\x0f", 8);
}
/*
* Check if failures of get_socket are handled correctly.
*/
TEST(run, bad_sockets) {
// We need to construct the answer, but it depends on int length.
size_t int_len(sizeof(int));
size_t result_len(4 + 2 * int_len);
char result[4 + sizeof(int) * 2];
// Both errno parts should be 0
memset(result, 0, result_len);
// Fill the 2 control parts
strcpy(result, "EB");
strcpy(result + 2 + int_len, "ES");
// Run the test
run_test(
"SU4\xbb\xbb\0\0\0\0"
"SU4\xcc\xcc\0\0\0\0"
"T", 19,
result, result_len);
}
}

View File

@@ -1,7 +1,7 @@
'\" t
.\" Title: b10-stats
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: Oct 15, 2010
.\" Manual: BIND10
.\" Source: BIND10
@@ -9,6 +9,15 @@
.\"
.TH "B10\-STATS" "8" "Oct 15, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
@@ -35,6 +44,11 @@ with other modules like
\fBbind10\fR,
\fBb10\-auth\fR
and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. Stats module collects data and aggregates it\&.
\fBb10\-stats\fR
invokes "sendstats" command for
\fBbind10\fR
after its initial starting because it\*(Aqs sure to collect statistics data from
\fBbind10\fR\&.
.SH "OPTIONS"
.PP
The arguments are as follows:
@@ -49,7 +63,8 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&.
.PP
/usr/local/share/bind10\-devel/stats\&.spec
\(em This is a spec file for
\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received vi bindctl\&.
\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received via
bindctl(1)\&.
.SH "SEE ALSO"
.PP

View File

@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
- Copyright (C) 2010,2011 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
@@ -64,7 +64,9 @@
send stats data to stats module independently from
implementation of stats module, so the frequency of sending data
may not be constant. Stats module collects data and aggregates
it.
it. <command>b10-stats</command> invokes "sendstats" command
for <command>bind10</command> after its initial starting because it's
sure to collect statistics data from <command>bind10</command>.
</para>
</refsect1>

View File

@@ -1,6 +1,6 @@
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
# Copyright (C) 2010, 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -15,8 +15,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
__version__ = "$Revision$"
import sys; sys.path.append ('@@PYTHONPATH@@')
import os
import signal
@@ -220,7 +218,13 @@ class CCSessionListener(Listener):
self.stats_data['stats.start_time'] = get_datetime()
self.stats_data['stats.last_update_time'] = get_datetime()
self.stats_data['stats.lname'] = self.session.lname
return self.cc_session.start()
self.cc_session.start()
# request Bob to send statistics data
if self.verbose:
sys.stdout.write("[b10-stats] request Bob to send statistics data\n")
cmd = isc.config.ccsession.create_command("sendstats", None)
seq = self.session.group_sendmsg(cmd, 'Boss')
self.session.group_recvmsg(True, seq)
def stop(self):
"""

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2010 Internet Systems Consortium.
# Copyright (C) 2010,2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -13,8 +13,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
__version__ = "$Revision$"
#
# Tests for the stats module
#
@@ -504,6 +502,13 @@ class TestStats(unittest.TestCase):
self.assertEqual(result_ok(),
self.session.get_message("Stats", None))
def test_for_boss(self):
last_queue = self.session.old_message_queue.pop()
self.assertEqual(
last_queue.msg, {'command': ['sendstats']})
self.assertEqual(
last_queue.env['group'], 'Boss')
class TestStats2(unittest.TestCase):
def setUp(self):

View File

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done

View File

@@ -21,6 +21,7 @@ import os
from isc.cc.session import *
from pydnspp import *
from xfrout import *
import xfrout
# our fake socket, where we can read and insert messages
class MySocket():
@@ -433,5 +434,36 @@ class TestUnixSockServer(unittest.TestCase):
sys.stdout = old_stdout
os.rmdir(dir_name)
class TestInitialization(unittest.TestCase):
def setEnv(self, name, value):
if value is None:
if name in os.environ:
del os.environ[name]
else:
os.environ[name] = value
def setUp(self):
self._oldSocket = os.getenv("BIND10_XFROUT_SOCKET_FILE")
self._oldFromBuild = os.getenv("B10_FROM_BUILD")
def tearDown(self):
self.setEnv("B10_FROM_BUILD", self._oldFromBuild)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", self._oldSocket)
# Make sure even the computed values are back
xfrout.init_paths()
def testNoEnv(self):
self.setEnv("B10_FROM_BUILD", None)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", None)
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE,
"@@LOCALSTATEDIR@@/auth_xfrout_conn")
def testProvidedSocket(self):
self.setEnv("B10_FROM_BUILD", None)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", "The/Socket/File")
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
if __name__== "__main__":
unittest.main()

View File

@@ -37,16 +37,20 @@ from optparse import OptionParser, OptionValueError
from isc.util import socketserver_mixin
try:
from libxfr_python import *
from libutil_io_python import *
from pydnspp import *
except ImportError as e:
# C++ loadable module may not be installed; even so the xfrout process
# must keep running, so we warn about it and move forward.
sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
sys.stderr.write('[b10-xfrout] failed to import DNS or isc.util.io module: %s\n' % str(e))
isc.util.process.rename()
if "B10_FROM_BUILD" in os.environ:
def init_paths():
global SPECFILE_PATH
global AUTH_SPECFILE_PATH
global UNIX_SOCKET_FILE
if "B10_FROM_BUILD" in os.environ:
SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
@@ -54,13 +58,18 @@ if "B10_FROM_BUILD" in os.environ:
"/auth_xfrout_conn"
else:
UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
else:
else:
PREFIX = "@prefix@"
DATAROOTDIR = "@datarootdir@"
SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
AUTH_SPECFILE_PATH = SPECFILE_PATH
if "BIND10_XFROUT_SOCKET_FILE" in os.environ:
UNIX_SOCKET_FILE = os.environ["BIND10_XFROUT_SOCKET_FILE"]
else:
UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
init_paths()
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
MAX_TRANSFERS_OUT = 10
@@ -376,7 +385,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
# This may happen when one xfrout process try to connect to
# xfrout unix socket server, to check whether there is another
# xfrout running.
if sock_fd == XFR_FD_RECEIVE_FAIL:
if sock_fd == FD_COMM_ERROR:
self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
return

View File

@@ -22,10 +22,10 @@ import tempfile
from zonemgr import *
ZONE_NAME_CLASS1_IN = ("sd.cn.", "IN")
ZONE_NAME_CLASS2_CH = ("tw.cn", "CH")
ZONE_NAME_CLASS2_CH = ("tw.cn.", "CH")
ZONE_NAME_CLASS3_IN = ("example.com", "IN")
ZONE_NAME_CLASS1_CH = ("sd.cn.", "CH")
ZONE_NAME_CLASS2_IN = ("tw.cn", "IN")
ZONE_NAME_CLASS2_IN = ("tw.cn.", "IN")
MAX_TRANSFER_TIMEOUT = 14400
LOWERBOUND_REFRESH = 10
@@ -46,9 +46,13 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
class MyZonemgrRefresh(ZonemgrRefresh):
class FakeConfig:
def __init__(self):
class FakeConfig:
self.zone_list = []
self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
ZONE_NAME_CLASS2_CH])
def set_zone_list_from_name_classes(self, zones):
self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
def get(self, name):
if name == 'lowerbound_refresh':
return LOWERBOUND_REFRESH
@@ -58,9 +62,27 @@ class MyZonemgrRefresh(ZonemgrRefresh):
return MAX_TRANSFER_TIMEOUT
elif name == 'jitter_scope':
return JITTER_SCOPE
elif name == 'secondary_zones':
return self.zone_list
else:
raise ValueError('Uknown config option')
class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self):
self._master_socket, self._slave_socket = socket.socketpair()
self._zonemgr_refresh_info = {}
def get_zone_soa(zone_name, db_file):
if zone_name == 'sd.cn.':
return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073106 7200 3600 2419200 21600')
elif zone_name == 'tw.cn.':
return (1, 2, 'tw.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600')
else:
return None
sqlite3_ds.get_zone_soa = get_zone_soa
ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
self._slave_socket, FakeConfig())
current_time = time.time()
@@ -70,7 +92,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
'next_refresh_time': current_time + 6500,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
'zone_state': 0},
('tw.cn', 'CH'): {
('tw.cn.', 'CH'): {
'last_refresh_time': current_time,
'next_refresh_time': current_time + 6900,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600',
@@ -272,28 +294,6 @@ class TestZonemgrRefresh(unittest.TestCase):
ZONE_NAME_CLASS1_IN)
sqlite3_ds.get_zone_soa = old_get_zone_soa
def test_build_zonemgr_refresh_info(self):
soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
def get_zones_info(db_file):
return [("sd.cn.", "IN")]
def get_zone_soa(zone_name, db_file):
return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
sqlite3_ds.get_zones_info = get_zones_info
sqlite3_ds.get_zone_soa = get_zone_soa
self.zone_refresh._zonemgr_refresh_info = {}
self.zone_refresh._build_zonemgr_refresh_info()
self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
self.assertEqual(soa_rdata, zone_soa_rdata)
self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
self.assertTrue("last_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
self.assertTrue("next_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
def test_zone_handle_notify(self):
self.zone_refresh.zone_handle_notify(ZONE_NAME_CLASS1_IN,"127.0.0.1")
notify_master = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"]
@@ -356,7 +356,7 @@ class TestZonemgrRefresh(unittest.TestCase):
'next_refresh_time': time1 + 7200,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
'zone_state': ZONE_OK},
("tw.cn","CH"):{
("tw.cn.","CH"):{
'last_refresh_time': time1 - 7200,
'next_refresh_time': time1,
'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT,
@@ -424,7 +424,8 @@ class TestZonemgrRefresh(unittest.TestCase):
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
"max_transfer_timeout" : 19800,
"jitter_scope" : 0.25
"jitter_scope" : 0.25,
"secondary_zones": []
}
self.zone_refresh.update_config_data(config_data)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
@@ -440,6 +441,31 @@ class TestZonemgrRefresh(unittest.TestCase):
self.zone_refresh.shutdown()
self.assertFalse(listener.is_alive())
def test_secondary_zones(self):
"""Test that we can modify the list of secondary zones"""
config = FakeConfig()
config.zone_list = []
# First, remove everything
self.zone_refresh.update_config_data(config)
self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
# Put something in
config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
self.zone_refresh.update_config_data(config)
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# This one does not exist
config.set_zone_list_from_name_classes(["example.net", "CH"])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data, config)
# So it should not affect the old ones
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# Make sure it works even when we "accidentally" forget the final dot
config.set_zone_list_from_name_classes([("sd.cn", "IN")])
self.zone_refresh.update_config_data(config)
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
def tearDown(self):
sys.stderr= self.stderr_backup
@@ -467,7 +493,8 @@ class MyZonemgr(Zonemgr):
"lowerbound_refresh" : 10,
"lowerbound_retry" : 5,
"max_transfer_timeout" : 14400,
"jitter_scope" : 0.1
"jitter_scope" : 0.1,
"secondary_zones": []
}
def _start_zone_refresh_timer(self):
@@ -483,9 +510,11 @@ class TestZonemgr(unittest.TestCase):
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
"max_transfer_timeout" : 14400,
"jitter_scope" : 0.1
"jitter_scope" : 0.1,
"secondary_zones": []
}
self.zonemgr.config_handler(config_data1)
self.assertEqual(self.zonemgr.config_handler(config_data1),
{"result": [0]})
self.assertEqual(config_data1, self.zonemgr._config_data)
config_data2 = {"zone_name" : "sd.cn.", "port" : "53", "master" : "192.168.1.1"}
self.zonemgr.config_handler(config_data2)
@@ -494,6 +523,15 @@ class TestZonemgr(unittest.TestCase):
config_data3 = {"jitter_scope" : 0.7}
self.zonemgr.config_handler(config_data3)
self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
# The zone doesn't exist in database, it should be rejected
self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
config_data1)
config_data1["secondary_zones"] = [{"name": "nonexistent.example",
"class": "IN"}]
self.assertNotEqual(self.zonemgr.config_handler(config_data1),
{"result": [0]})
# As it is rejected, the old value should be kept
self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
def test_get_db_file(self):
self.assertEqual("initdb.file", self.zonemgr.get_db_file())

View File

@@ -100,9 +100,8 @@ class ZonemgrRefresh:
self._cc = cc
self._check_sock = slave_socket
self._db_file = db_file
self.update_config_data(config_data)
self._zonemgr_refresh_info = {}
self._build_zonemgr_refresh_info()
self.update_config_data(config_data)
self._running = False
def _random_jitter(self, max, jitter):
@@ -148,16 +147,13 @@ class ZonemgrRefresh:
def _zone_not_exist(self, zone_name_class):
""" Zone doesn't belong to zonemgr"""
if zone_name_class in self._zonemgr_refresh_info.keys():
return False
return True
return not zone_name_class in self._zonemgr_refresh_info
def zone_refresh_success(self, zone_name_class):
"""Update zone info after zone refresh success"""
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
return
self.zonemgr_reload_zone(zone_name_class)
self._set_zone_refresh_timer(zone_name_class)
self._set_zone_state(zone_name_class, ZONE_OK)
@@ -168,7 +164,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
return
# Is zone expired?
if (self._zone_is_expired(zone_name_class)):
self._set_zone_state(zone_name_class, ZONE_EXPIRED)
@@ -181,7 +176,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
"doesn't belong to zonemgr" % zone_name_class)
return
self._set_zone_notifier_master(zone_name_class, master)
self._set_zone_notify_timer(zone_name_class)
@@ -192,6 +186,7 @@ class ZonemgrRefresh:
def zonemgr_add_zone(self, zone_name_class):
""" Add a zone into zone manager."""
log_msg("Loading zone (%s, %s)" % zone_name_class)
zone_info = {}
zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
if not zone_soa:
@@ -203,14 +198,6 @@ class ZonemgrRefresh:
float(zone_soa[7].split(" ")[REFRESH_OFFSET])
self._zonemgr_refresh_info[zone_name_class] = zone_info
def _build_zonemgr_refresh_info(self):
""" Build zonemgr refresh info map."""
log_msg("Start loading zone into zonemgr.")
for zone_name, zone_class in sqlite3_ds.get_zones_info(self._db_file):
zone_name_class = (zone_name, zone_class)
self.zonemgr_add_zone(zone_name_class)
log_msg("Finish loading zone into zonemgr.")
def _zone_is_expired(self, zone_name_class):
"""Judge whether a zone is expired or not."""
zone_expired_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[EXPIRED_OFFSET])
@@ -415,6 +402,32 @@ class ZonemgrRefresh:
def update_config_data(self, new_config):
""" update ZonemgrRefresh config """
backup = self._zonemgr_refresh_info.copy()
try:
required = {}
# Add new zones
for secondary_zone in new_config.get('secondary_zones'):
name = secondary_zone['name']
# Be tolerant to sclerotic users who forget the final dot
if name[-1] != '.':
name = name + '.'
name_class = (name, secondary_zone['class'])
required[name_class] = True
# Add it only if it isn't there already
if not name_class in self._zonemgr_refresh_info:
self.zonemgr_add_zone(name_class)
# Drop the zones that are no longer there
# Do it in two phases, python doesn't like deleting while iterating
to_drop = []
for old_zone in self._zonemgr_refresh_info:
if not old_zone in required:
to_drop.append(old_zone)
for drop in to_drop:
del self._zonemgr_refresh_info[drop]
# If we are not able to find it in database, restore the original
except:
self._zonemgr_refresh_info = backup
raise
self._lowerbound_refresh = new_config.get('lowerbound_refresh')
self._lowerbound_retry = new_config.get('lowerbound_retry')
self._max_transfer_timeout = new_config.get('max_transfer_timeout')
@@ -471,27 +484,38 @@ class Zonemgr:
def config_handler(self, new_config):
""" Update config data. """
answer = create_answer(0)
ok = True
complete = self._config_data.copy()
for key in new_config:
if key not in self._config_data:
if key not in complete:
answer = create_answer(1, "Unknown config data: " + str(key))
ok = False
continue
self._config_data[key] = new_config[key]
complete[key] = new_config[key]
self._config_data_check(self._config_data)
if (self._zone_refresh):
self._zone_refresh.update_config_data(self._config_data)
self._config_data_check(complete)
if self._zone_refresh is not None:
try:
self._zone_refresh.update_config_data(complete)
except Exception as e:
answer = create_answer(1, str(e))
ok = False
if ok:
self._config_data = complete
return answer
def _config_data_check(self, config_data):
"""Check whether the new config data is valid or
not. """
not. It contains only basic logic, not full check against
database."""
# jitter should not be bigger than half of the original value
if config_data.get('jitter_scope') > 0.5:
config_data['jitter_scope'] = 0.5
log_msg("[b10-zonemgr] jitter_scope is too big, its value will "
"be set to 0.5")
def _parse_cmd_params(self, args, command):
zone_name = args.get("zone_name")
if not zone_name:

View File

@@ -25,6 +25,32 @@
"item_type": "real",
"item_optional": false,
"item_default": 0.25
},
{
"item_name": "secondary_zones",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "secondary_zone",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "class",
"item_type": "string",
"item_optional": false,
"item_default": "IN"
},
{
"item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
]
}
}
],
"commands": [

View File

@@ -1,2 +1,2 @@
SUBDIRS = exceptions util dns cc config python xfr bench log \
asiolink nsas cache resolve testutils datasrc server_common
SUBDIRS = exceptions util dns cc config python xfr bench log asiolink \
asiodns nsas cache resolve testutils datasrc server_common

View File

@@ -0,0 +1,34 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libasiodns.la
libasiodns_la_SOURCES = dns_answer.h
libasiodns_la_SOURCES += asiodns.h
libasiodns_la_SOURCES += asiodef.cc asiodef.h
libasiodns_la_SOURCES += dns_lookup.h
libasiodns_la_SOURCES += dns_server.h
libasiodns_la_SOURCES += dns_service.cc dns_service.h
libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
libasiodns_la_SOURCES += udp_server.cc udp_server.h
libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
EXTRA_DIST = asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
libasiodns_la_CXXFLAGS += -Wno-error
endif
libasiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
libasiodns_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la

157
src/lib/asiodns/README Normal file
View File

@@ -0,0 +1,157 @@
The asiodns library is intended to provide an abstraction layer between
BIND10 modules and asiolink library.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.

View File

@@ -4,18 +4,20 @@
#include <log/message_types.h>
#include <log/message_initializer.h>
namespace asiolink {
namespace isc {
namespace asiodns {
extern const isc::log::MessageID ASIO_FETCHCOMP = "FETCHCOMP";
extern const isc::log::MessageID ASIO_FETCHSTOP = "FETCHSTOP";
extern const isc::log::MessageID ASIO_OPENSOCK = "OPENSOCK";
extern const isc::log::MessageID ASIO_RECVSOCK = "RECVSOCK";
extern const isc::log::MessageID ASIO_RECVTMO = "RECVTMO";
extern const isc::log::MessageID ASIO_SENDSOCK = "SENDSOCK";
extern const isc::log::MessageID ASIO_UNKORIGIN = "UNKORIGIN";
extern const isc::log::MessageID ASIO_UNKRESULT = "UNKRESULT";
extern const isc::log::MessageID ASIODNS_FETCHCOMP = "FETCHCOMP";
extern const isc::log::MessageID ASIODNS_FETCHSTOP = "FETCHSTOP";
extern const isc::log::MessageID ASIODNS_OPENSOCK = "OPENSOCK";
extern const isc::log::MessageID ASIODNS_RECVSOCK = "RECVSOCK";
extern const isc::log::MessageID ASIODNS_RECVTMO = "RECVTMO";
extern const isc::log::MessageID ASIODNS_SENDSOCK = "SENDSOCK";
extern const isc::log::MessageID ASIODNS_UNKORIGIN = "UNKORIGIN";
extern const isc::log::MessageID ASIODNS_UNKRESULT = "UNKRESULT";
} // namespace asiolink
} // namespace asiodns
} // namespace isc
namespace {

23
src/lib/asiodns/asiodef.h Normal file
View File

@@ -0,0 +1,23 @@
// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
#ifndef __ASIODEF_H
#define __ASIODEF_H
#include <log/message_types.h>
namespace isc {
namespace asiodns {
extern const isc::log::MessageID ASIODNS_FETCHCOMP;
extern const isc::log::MessageID ASIODNS_FETCHSTOP;
extern const isc::log::MessageID ASIODNS_OPENSOCK;
extern const isc::log::MessageID ASIODNS_RECVSOCK;
extern const isc::log::MessageID ASIODNS_RECVTMO;
extern const isc::log::MessageID ASIODNS_SENDSOCK;
extern const isc::log::MessageID ASIODNS_UNKORIGIN;
extern const isc::log::MessageID ASIODNS_UNKRESULT;
} // namespace asiodns
} // namespace isc
#endif // __ASIODEF_H

View File

@@ -12,8 +12,8 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
$PREFIX ASIO_
$NAMESPACE asiolink
$PREFIX ASIODNS_
$NAMESPACE isc::asiodns
FETCHCOMP upstream fetch to %s(%d) has now completed
+ A debug message, this records the the upstream fetch (a query made by the

23
src/lib/asiodns/asiodns.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (C) 2011 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.
#ifndef __ASIODNS_H
#define __ASIODNS_H 1
#include <asiodns/dns_service.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#endif // __ASIODNS_H

View File

@@ -16,8 +16,11 @@
#define __ASIOLINK_DNS_ANSWER_H 1
#include <asiolink/io_message.h>
#include <util/buffer.h>
#include <dns/message.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSAnswer class is an abstract base class for a DNS
/// Answer provider function.
@@ -63,11 +66,12 @@ public:
/// \param answer_message The DNS MessagePtr of the answer we are
/// building
/// \param buffer Intermediate data results are put here
virtual void operator()(const IOMessage& io_message,
virtual void operator()(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr query_message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer) const = 0;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_ANSWER_H

View File

@@ -16,11 +16,12 @@
#define __ASIOLINK_DNS_LOOKUP_H 1
#include <asiolink/io_message.h>
#include <asiolink/dns_server.h>
#include <util/buffer.h>
#include <asiodns/dns_server.h>
#include <dns/message.h>
#include <util/buffer.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSLookup class is an abstract base class for a DNS
/// Lookup provider function.
@@ -67,7 +68,7 @@ public:
/// this MessagePtr
/// \param buffer The final answer is put here
/// \param server DNSServer object to use
virtual void operator()(const IOMessage& io_message,
virtual void operator()(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer,
@@ -79,5 +80,6 @@ private:
DNSLookup* self_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_LOOKUP_H

View File

@@ -17,7 +17,8 @@
#include <asiolink/io_message.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSServer class is a wrapper (and base class) for
/// classes which provide DNS server functionality.
@@ -34,7 +35,7 @@ namespace asiolink {
/// instantiated through a base class) are sometimes passed by
/// reference (as this superclass); calls to methods in the base
/// class are then rerouted via this pointer to methods in the derived
/// class. This allows code from outside asiolink, with no specific
/// class. This allows code from outside asiodns, with no specific
/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
///
/// This class is both assignable and copy-constructable. Its subclasses
@@ -151,5 +152,6 @@ private:
};
} // asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_SERVER_H

View File

@@ -23,11 +23,11 @@
#include <log/dummylog.h>
#include <asio.hpp>
#include <asiolink/dns_service.h>
#include <dns_service.h>
#include <asiolink/io_service.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_server.h>
#include <asiolink/udp_server.h>
#include <tcp_server.h>
#include <udp_server.h>
#include <log/dummylog.h>
@@ -36,9 +36,11 @@
using isc::log::dlog;
namespace asiolink {
using namespace isc::asiolink;
namespace isc {
namespace asiodns {
class SimpleCallback;
class DNSLookup;
class DNSAnswer;
@@ -195,6 +197,5 @@ DNSService::clearServers() {
impl_->servers_.clear();
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -18,10 +18,11 @@
#include <resolve/resolver_interface.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
namespace asiolink {
namespace isc {
namespace asiodns {
class SimpleCallback;
class DNSLookup;
class DNSAnswer;
class DNSServiceImpl;
@@ -54,8 +55,8 @@ public:
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
/// \param lookup The lookup provider (see \c DNSLookup)
/// \param answer The answer provider (see \c DNSAnswer)
DNSService(IOService& io_service, const char& port,
const char& address, SimpleCallback* checkin,
DNSService(asiolink::IOService& io_service, const char& port,
const char& address, isc::asiolink::SimpleCallback* checkin,
DNSLookup* lookup, DNSAnswer* answer);
/// \brief The constructor with a specific port on which the services
/// listen on.
@@ -71,14 +72,14 @@ public:
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
/// \param lookup The lookup provider (see \c DNSLookup)
/// \param answer The answer provider (see \c DNSAnswer)
DNSService(IOService& io_service, const char& port,
DNSService(asiolink::IOService& io_service, const char& port,
const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin, DNSLookup* lookup,
isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
DNSAnswer* answer);
/// \brief The constructor without any servers.
///
/// Use addServer() to add some servers.
DNSService(IOService& io_service, SimpleCallback* checkin,
DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
DNSLookup* lookup, DNSAnswer* answer);
/// \brief The destructor.
~DNSService();
@@ -105,8 +106,9 @@ public:
private:
DNSServiceImpl* impl_;
IOService& io_service_;
asiolink::IOService& io_service_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_SERVICE_H

View File

@@ -14,47 +14,50 @@
#include <config.h>
#include <unistd.h> // for some IPC/network system calls
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <asio.hpp>
#include <asio/deadline_timer.hpp>
#include <asiolink/io_address.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <log/logger.h>
#include <asiodns/asiodef.h>
#include <asiodns/io_fetch.h>
#include <util/buffer.h>
#include <util/random/qid_gen.h>
#include <asio.hpp>
#include <asio/deadline_timer.hpp>
#include <asiolink/asiodef.h>
#include <asiolink/io_address.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_fetch.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <stdint.h>
using namespace asio;
using namespace isc::asiolink;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::util::random;
using namespace isc::log;
using namespace std;
namespace asiolink {
namespace isc {
namespace asiodns {
/// Use the ASIO logger
@@ -88,6 +91,7 @@ struct IOFetchData {
size_t offset; ///< Offset to receive data
bool stopped; ///< Have we stopped running?
int timeout; ///< Timeout in ms
bool packet; ///< true if packet was supplied
// In case we need to log an error, the origin of the last asynchronous
// I/O is recorded. To save time and simplify the code, this is recorded
@@ -138,7 +142,6 @@ struct IOFetchData {
question(query),
msgbuf(new OutputBuffer(512)),
received(buff),
callback(cb),
timer(service.get_io_service()),
protocol(proto),
@@ -147,7 +150,8 @@ struct IOFetchData {
offset(0),
stopped(false),
timeout(wait),
origin(ASIO_UNKORIGIN),
packet(false),
origin(ASIODNS_UNKORIGIN),
staging(),
qid(QidGenerator::getInstance().generateQid())
{}
@@ -176,6 +180,19 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
{
}
IOFetch::IOFetch(Protocol protocol, IOService& service,
OutputBufferPtr& outpkt, const IOAddress& address, uint16_t port,
OutputBufferPtr& buff, Callback* cb, int wait)
:
data_(new IOFetchData(protocol, service,
isc::dns::Question(isc::dns::Name("dummy.example.org"),
isc::dns::RRClass::IN(), isc::dns::RRType::A()),
address, port, buff, cb, wait))
{
data_->msgbuf = outpkt;
data_->packet = true;
}
// Return protocol in use.
IOFetch::Protocol
@@ -202,6 +219,13 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
/// This is done in a different scope to allow inline variable
/// declarations.
{
if (data_->packet) {
// A packet was given, overwrite the QID (which is in the
// first two bytes of the packet).
data_->msgbuf->writeUint16At(data_->qid, 0);
} else {
// A question was given, construct the packet
Message msg(Message::RENDER);
msg.setQid(data_->qid);
msg.setOpcode(Opcode::QUERY());
@@ -211,6 +235,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
MessageRenderer renderer(*data_->msgbuf);
msg.toWire(renderer);
}
}
// If we timeout, we stop, which will can cancel outstanding I/Os and
// shutdown everything.
@@ -223,7 +248,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Open a connection to the target system. For speed, if the operation
// is synchronous (i.e. UDP operation) we bypass the yield.
data_->origin = ASIO_OPENSOCK;
data_->origin = ASIODNS_OPENSOCK;
if (data_->socket->isOpenSynchronous()) {
data_->socket->open(data_->remote_snd.get(), *this);
} else {
@@ -233,7 +258,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
do {
// Begin an asynchronous send, and then yield. When the send completes,
// we will resume immediately after this point.
data_->origin = ASIO_SENDSOCK;
data_->origin = ASIODNS_SENDSOCK;
CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
data_->msgbuf->getLength(), data_->remote_snd.get(), *this);
@@ -256,7 +281,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// received all the data before copying it back to the user's buffer.
// And we want to minimise the amount of copying...
data_->origin = ASIO_RECVSOCK;
data_->origin = ASIODNS_RECVSOCK;
data_->cumulative = 0; // No data yet received
data_->offset = 0; // First data into start of buffer
data_->received->clear(); // Clear the receive buffer
@@ -272,7 +297,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Finished with this socket, so close it. This will not generate an
// I/O error, but reset the origin to unknown in case we change this.
data_->origin = ASIO_UNKORIGIN;
data_->origin = ASIODNS_UNKORIGIN;
data_->socket->close();
/// We are done
@@ -315,7 +340,7 @@ IOFetch::stop(Result result) {
switch (result) {
case TIME_OUT:
if (logger.isDebugEnabled(1)) {
logger.debug(20, ASIO_RECVTMO,
logger.debug(20, ASIODNS_RECVTMO,
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
}
@@ -323,7 +348,7 @@ IOFetch::stop(Result result) {
case SUCCESS:
if (logger.isDebugEnabled(50)) {
logger.debug(30, ASIO_FETCHCOMP,
logger.debug(30, ASIODNS_FETCHCOMP,
data_->remote_rcv->getAddress().toText().c_str(),
static_cast<int>(data_->remote_rcv->getPort()));
}
@@ -333,13 +358,13 @@ IOFetch::stop(Result result) {
// Fetch has been stopped for some other reason. This is
// allowed but as it is unusual it is logged, but with a lower
// debug level than a timeout (which is totally normal).
logger.debug(1, ASIO_FETCHSTOP,
logger.debug(1, ASIODNS_FETCHSTOP,
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
break;
default:
logger.error(ASIO_UNKRESULT, static_cast<int>(result),
logger.error(ASIODNS_UNKRESULT, static_cast<int>(result),
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
}
@@ -363,10 +388,10 @@ IOFetch::stop(Result result) {
void IOFetch::logIOFailure(asio::error_code ec) {
// Should only get here with a known error code.
assert((data_->origin == ASIO_OPENSOCK) ||
(data_->origin == ASIO_SENDSOCK) ||
(data_->origin == ASIO_RECVSOCK) ||
(data_->origin == ASIO_UNKORIGIN));
assert((data_->origin == ASIODNS_OPENSOCK) ||
(data_->origin == ASIODNS_SENDSOCK) ||
(data_->origin == ASIODNS_RECVSOCK) ||
(data_->origin == ASIODNS_UNKORIGIN));
static const char* PROTOCOL[2] = {"TCP", "UDP"};
logger.error(data_->origin,
@@ -377,5 +402,6 @@ void IOFetch::logIOFailure(asio::error_code ec) {
static_cast<int>(data_->remote_snd->getPort()));
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc {

View File

@@ -24,16 +24,17 @@
#include <coroutine.h>
#include <asio/error_code.hpp>
#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <util/buffer.h>
#include <dns/question.h>
namespace asiolink {
namespace isc {
namespace asiodns {
// Forward declarations
class IOAddress;
class IOFetchData;
class IOService;
/// \brief Upstream Fetch Processing
///
@@ -116,12 +117,34 @@ public:
///
/// Creates the object that will handle the upstream fetch.
///
/// TODO: Need to randomise the source port
///
/// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
/// \param service I/O Service object to handle the asynchronous
/// operations.
/// \param question DNS question to send to the upstream server.
/// \param address IP address of upstream server
/// \param port Port to which to connect on the upstream server
/// \param buff Output buffer into which the response (in wire format)
/// is written (if a response is received).
/// \param cb Callback object containing the callback to be called when we
/// terminate. The caller is responsible for managing this object
/// and deleting it if necessary.
/// \param wait Timeout for the fetch (in ms). The default value of
/// -1 indicates no timeout.
IOFetch(Protocol protocol, isc::asiolink::IOService& service,
const isc::dns::Question& question,
const isc::asiolink::IOAddress& address,
uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
int wait = -1);
/// \brief Constructor.
///
/// Creates the object that will handle the upstream fetch.
///
/// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
/// \param service I/O Service object to handle the asynchronous
/// operations.
/// \param outpkt Packet to send to upstream server. Note that the
/// QID (first two bytes of the packet) may be altered in the sending.
/// \param buff Output buffer into which the response (in wire format)
/// is written (if a response is received).
/// \param cb Callback object containing the callback to be called
@@ -132,8 +155,9 @@ public:
/// (default = 53)
/// \param wait Timeout for the fetch (in ms). The default value of
/// -1 indicates no timeout.
IOFetch(Protocol protocol, IOService& service,
const isc::dns::Question& question, const IOAddress& address,
IOFetch(Protocol protocol, isc::asiolink::IOService& service,
isc::util::OutputBufferPtr& outpkt,
const isc::asiolink::IOAddress& address,
uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
int wait = -1);
@@ -174,6 +198,7 @@ private:
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __IO_FETCH_H

View File

@@ -29,7 +29,7 @@
#include <asiolink/dummy_io_cb.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/tcp_server.h>
#include <tcp_server.h>
using namespace asio;
@@ -39,8 +39,10 @@ using asio::ip::tcp;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
/// The following functions implement the \c TCPServer class.
///
@@ -238,5 +240,5 @@ TCPServer::resume(const bool done) {
io_.post(*this);
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -24,9 +24,12 @@
#include <asiolink/asiolink.h>
#include <coroutine.h>
#include "dns_server.h"
#include "dns_lookup.h"
#include "dns_answer.h"
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief A TCP-specific \c DNSServer object.
///
@@ -36,7 +39,7 @@ class TCPServer : public virtual DNSServer, public virtual coroutine {
public:
explicit TCPServer(asio::io_service& io_service,
const asio::ip::address& addr, const uint16_t port,
const SimpleCallback* checkin = NULL,
const isc::asiolink::SimpleCallback* checkin = NULL,
const DNSLookup* lookup = NULL,
const DNSAnswer* answer = NULL);
@@ -95,7 +98,7 @@ private:
// \c IOMessage and \c Message objects to be passed to the
// DNS lookup and answer providers
boost::shared_ptr<asiolink::IOMessage> io_message_;
boost::shared_ptr<isc::asiolink::IOMessage> io_message_;
isc::dns::MessagePtr query_message_;
isc::dns::MessagePtr answer_message_;
@@ -108,13 +111,14 @@ private:
bool done_;
// Callback functions provided by the caller
const SimpleCallback* checkin_callback_;
const isc::asiolink::SimpleCallback* checkin_callback_;
const DNSLookup* lookup_callback_;
const DNSAnswer* answer_callback_;
boost::shared_ptr<IOEndpoint> peer_;
boost::shared_ptr<IOSocket> iosock_;
boost::shared_ptr<isc::asiolink::IOEndpoint> peer_;
boost::shared_ptr<isc::asiolink::IOSocket> iosock_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __TCP_SERVER_H

View File

@@ -0,0 +1,50 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += dns_server_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
# 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
if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
run_unittests_CXXFLAGS += -Wno-error
endif
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -18,10 +18,10 @@
#include <asio.hpp>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
#include <asiolink/udp_server.h>
#include <asiolink/tcp_server.h>
#include <asiolink/dns_answer.h>
#include <asiolink/dns_lookup.h>
#include <asiodns/udp_server.h>
#include <asiodns/tcp_server.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
#include <string>
#include <csignal>
#include <unistd.h> //for alarm
@@ -65,7 +65,8 @@
/// involved so the message sending between client and server is plain text
/// And the valid checker, question lookup and answer composition are dummy.
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace asio;
namespace {

View File

@@ -38,16 +38,18 @@
#include <asiolink/io_address.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_fetch.h>
#include <asiolink/io_service.h>
#include <asiodns/io_fetch.h>
using namespace asio;
using namespace isc::dns;
using namespace isc::util;
using namespace asio::ip;
using namespace std;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
const uint16_t TEST_PORT(5301);
@@ -722,4 +724,5 @@ TEST_F(IOFetchTest, TcpSendReceive8192ShortSend) {
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -17,8 +17,10 @@
#include <asio.hpp>
#include <asiolink/asiolink.h>
#include <asiodns/asiodns.h>
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
const char* const TEST_SERVER_PORT = "53535";
const char* const TEST_CLIENT_PORT = "53536";

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// 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
@@ -12,21 +12,17 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <boost/python.hpp>
#include <boost/python/class.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/exception_translator.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <xfr/fd_share.h>
#include <log/root_logger_name.h>
#include <dns/tests/unittest_util.h>
using namespace isc::xfr;
using namespace boost::python;
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
BOOST_PYTHON_MODULE(bind10_xfr) {
def("recv_fd", &recv_fd);
def("send_fd", &send_fd);
return (RUN_ALL_TESTS());
}

View File

@@ -27,8 +27,8 @@
#include <asio/error.hpp>
#include <asiolink/dummy_io_cb.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_server.h>
#include <asiolink/udp_socket.h>
#include "udp_server.h"
#include <dns/opcode.h>
@@ -39,8 +39,10 @@ using isc::log::dlog;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
/*
* Some of the member variables here are shared_ptrs and some are
@@ -319,4 +321,5 @@ UDPServer::hasAnswer() {
return (data_->done_);
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -19,14 +19,15 @@
#error "asio.hpp must be included before including this, see asiolink.h as to why"
#endif
#include <asiolink/dns_server.h>
#include <asiolink/simple_callback.h>
#include <asiolink/dns_lookup.h>
#include <asiolink/dns_answer.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_server.h>
#include <coroutine.h>
namespace asiolink {
namespace isc {
namespace asiodns {
//
// Asynchronous UDP server coroutine
@@ -47,7 +48,7 @@ public:
/// \param answer the callbackprovider for DNS answer events
explicit UDPServer(asio::io_service& io_service,
const asio::ip::address& addr, const uint16_t port,
SimpleCallback* checkin = NULL,
isc::asiolink::SimpleCallback* checkin = NULL,
DNSLookup* lookup = NULL,
DNSAnswer* answer = NULL);
@@ -102,5 +103,6 @@ private:
boost::shared_ptr<Data> data_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __UDP_SERVER_H

View File

@@ -13,31 +13,21 @@ CLEANFILES = *.gcno *.gcda
# which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h
libasiolink_la_SOURCES += asiodef.cc asiodef.h
libasiolink_la_SOURCES += dns_answer.h
libasiolink_la_SOURCES += dns_lookup.h
libasiolink_la_SOURCES += dns_server.h
libasiolink_la_SOURCES += dns_service.cc dns_service.h
libasiolink_la_SOURCES += dummy_io_cb.h
libasiolink_la_SOURCES += interval_timer.cc interval_timer.h
libasiolink_la_SOURCES += io_address.cc io_address.h
libasiolink_la_SOURCES += io_asio_socket.h
libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
libasiolink_la_SOURCES += io_error.h
libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
libasiolink_la_SOURCES += io_message.h
libasiolink_la_SOURCES += io_service.h io_service.cc
libasiolink_la_SOURCES += io_socket.h io_socket.cc
libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += tcp_endpoint.h
libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
libasiolink_la_SOURCES += tcp_socket.h
libasiolink_la_SOURCES += udp_endpoint.h
libasiolink_la_SOURCES += udp_server.cc udp_server.h
libasiolink_la_SOURCES += udp_socket.h
EXTRA_DIST = asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)

View File

@@ -16,167 +16,7 @@ including:
them in only one place allows us to relax strictness here, while
leaving it in place elsewhere.
Currently, the asiolink library only supports DNS servers (i.e., b10-auth
and b10-resolver). The plan is to make it more generic and allow it to
support other modules as well.
Some of the classes defined here--for example, IOSocket, IOEndpoint,
and IOAddress--are to be used by BIND 10 modules as wrappers around
ASIO-specific classes.
Other classes implement the DNS protocol on behalf of BIND 10 modules.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.

View File

@@ -1,21 +0,0 @@
// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
#ifndef __ASIODEF_H
#define __ASIODEF_H
#include <log/message_types.h>
namespace asiolink {
extern const isc::log::MessageID ASIO_FETCHCOMP;
extern const isc::log::MessageID ASIO_FETCHSTOP;
extern const isc::log::MessageID ASIO_OPENSOCK;
extern const isc::log::MessageID ASIO_RECVSOCK;
extern const isc::log::MessageID ASIO_RECVTMO;
extern const isc::log::MessageID ASIO_SENDSOCK;
extern const isc::log::MessageID ASIO_UNKORIGIN;
extern const isc::log::MessageID ASIO_UNKRESULT;
} // namespace asiolink
#endif // __ASIODEF_H

View File

@@ -20,10 +20,6 @@
// See the description of the namespace below.
#include <asiolink/io_service.h>
#include <asiolink/dns_service.h>
#include <asiolink/dns_server.h>
#include <asiolink/dns_lookup.h>
#include <asiolink/dns_answer.h>
#include <asiolink/simple_callback.h>
#include <asiolink/interval_timer.h>
@@ -62,11 +58,6 @@
/// this module. The resulting interfaces are thus straightforward mapping
/// to the ASIO counterparts.
///
/// Notes to developers:
/// Currently the wrapper interface is fairly specific to use by a
/// DNS server, i.e., b10-auth or b10-resolver. But the plan is to
/// generalize it and have other modules use it as well.
///
/// One obvious drawback of this approach is performance overhead
/// due to the additional layer. We should eventually evaluate the cost
/// of the wrapper abstraction in benchmark tests. Another drawback is

View File

@@ -20,6 +20,7 @@
#include <asio/error.hpp>
#include <asio/error_code.hpp>
namespace isc {
namespace asiolink {
/// \brief Asynchronous I/O Completion Callback
@@ -55,5 +56,6 @@ public:
};
} // namespace asiolink
} // namespace isc
#endif // __DUMMY_IO_CB_H

View File

@@ -26,6 +26,7 @@
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
class IntervalTimerImpl {
@@ -133,4 +134,5 @@ IntervalTimer::getInterval() const {
return (impl_->getInterval());
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -19,6 +19,7 @@
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
struct IntervalTimerImpl;
@@ -130,4 +131,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_INTERVAL_TIMER_H

View File

@@ -31,6 +31,7 @@ using asio::ip::tcp;
using namespace std;
namespace isc {
namespace asiolink {
// XXX: we cannot simply construct the address in the initialization list,
@@ -62,4 +63,5 @@ IOAddress::getFamily() const {
}
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -26,6 +26,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOAddress class represents an IP addresses (version
@@ -119,5 +120,6 @@ private:
asio::ip::address asio_address_;
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ADDRESS_H

View File

@@ -31,7 +31,7 @@
#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>
namespace isc {
namespace asiolink {
/// \brief Socket not open
@@ -395,5 +395,6 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __IO_ASIO_SOCKET_H

View File

@@ -28,6 +28,7 @@
using namespace std;
namespace isc {
namespace asiolink {
const IOEndpoint*
@@ -57,4 +58,5 @@ IOEndpoint::operator!=(const IOEndpoint& other) const {
return (!operator==(other));
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -26,6 +26,7 @@
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOEndpoint class is an abstract base class to represent
@@ -117,5 +118,6 @@ public:
const unsigned short port);
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ENDPOINT_H

View File

@@ -18,6 +18,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief An exception that is thrown if an error occurs within the IO
@@ -30,6 +31,7 @@ public:
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ERROR_H

View File

@@ -28,6 +28,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_socket.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOMessage class encapsulates an incoming message received
@@ -96,5 +97,6 @@ private:
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_MESSAGE_H

View File

@@ -21,6 +21,7 @@
#include <asio.hpp>
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
class IOServiceImpl {
@@ -95,4 +96,5 @@ IOService::get_io_service() {
return (io_impl_->get_io_service());
}
} // namepsace asiolink
} // namespace asiolink
} // namespace isc

View File

@@ -19,6 +19,7 @@ namespace asio {
class io_service;
}
namespace isc {
namespace asiolink {
struct IOServiceImpl;
@@ -74,4 +75,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_IO_SERVICE_H

View File

@@ -16,8 +16,7 @@
#include <asio.hpp>
using namespace asio;
namespace isc {
namespace asiolink {
/// \brief The \c DummySocket class is a concrete derived class of
@@ -62,4 +61,5 @@ IOSocket::getDummyTCPSocket() {
return (socket);
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -25,6 +25,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOSocket class is an abstract base class to represent
@@ -120,5 +121,6 @@ public:
};
} // namespace asiolink
} // namespace isc
#endif // __IO_SOCKET_H

View File

@@ -17,6 +17,7 @@
#include <asiolink/io_message.h>
namespace isc {
namespace asiolink {
/// \brief The \c SimpleCallback class is an abstract base class for a
@@ -68,4 +69,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_SIMPLE_CALLBACK_H

View File

@@ -21,6 +21,7 @@
#include <asiolink/io_endpoint.h>
namespace isc {
namespace asiolink {
/// \brief The \c TCPEndpoint class is a concrete derived class of
@@ -110,4 +111,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __TCP_ENDPOINT_H

View File

@@ -41,6 +41,7 @@
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
namespace isc {
namespace asiolink {
/// \brief Buffer Too Large
@@ -412,5 +413,6 @@ TCPSocket<C>::close() {
}
} // namespace asiolink
} // namespace isc
#endif // __TCP_SOCKET_H

View File

@@ -21,15 +21,12 @@ run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_address_unittest.cc
run_unittests_SOURCES += io_endpoint_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_SOURCES += io_socket_unittest.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += interval_timer_unittest.cc
run_unittests_SOURCES += tcp_endpoint_unittest.cc
run_unittests_SOURCES += tcp_socket_unittest.cc
run_unittests_SOURCES += udp_endpoint_unittest.cc
run_unittests_SOURCES += udp_socket_unittest.cc
run_unittests_SOURCES += dns_server_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

View File

@@ -26,7 +26,7 @@ const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
boost::posix_time::milliseconds(50);
}
using namespace asiolink;
using namespace isc::asiolink;
// This fixture is for testing IntervalTimer. Some callback functors are
// registered as callback function of the timer to test if they are called

View File

@@ -18,7 +18,7 @@
#include <asiolink/io_error.h>
#include <asiolink/io_address.h>
using namespace asiolink;
using namespace isc::asiolink;
TEST(IOAddressTest, fromText) {
IOAddress io_address_v4("192.0.2.1");

View File

@@ -18,7 +18,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
using namespace asiolink;
using namespace isc::asiolink;
TEST(IOEndpointTest, createUDPv4) {
const IOEndpoint* ep;

Some files were not shown because too many files have changed in this diff Show More