2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

Merge branch 'master' into #1483

Conflicts:
	src/lib/datasrc/database.cc
	src/lib/datasrc/database.h

Mostly adding a target parameter to some methods and passing it around.

Compiles, but does not pass tests and documentation is incomplete for
now.
This commit is contained in:
Michal 'vorner' Vaner
2011-12-15 13:46:07 +01:00
73 changed files with 3851 additions and 1028 deletions

View File

@@ -1,6 +1,27 @@
346. [build]* jreed
Renamed libdhcp to libdhcp++.
(Trac #1446, git d394e64f4c44f16027b1e62b4ac34e054b49221d)
345. [func] tomek
dhcp4: Dummy DHCPv4 component implemented. Currently it does
nothing useful, except providing skeleton implementation that can
be expanded in the future.
(Trac #992, git d6e33479365c8f8f62ef2b9aa5548efe6b194601)
344. [func] y-aharen
src/lib/statistics: Added statistics counter library for entire server
items and per zone items. Also, modified b10-auth to use it. It is
also intended to use in the other modules such as b10-resolver.
(Trac #510, git afddaf4c5718c2a0cc31f2eee79c4e0cc625499f)
343. [func] jelte
Added IXFR-out system tests, based on the first two test sets of
http://bind10.isc.org/wiki/IxfrSystemTests.
(Trac #1314, git 1655bed624866a766311a01214597db01b4c7cec)
342. [bug] stephen
In the resolver, a FORMERR received from an upstream nameserver
now rsults in a SERVFAIL being returned as a response to the original
now results in a SERVFAIL being returned as a response to the original
query. Additional debug messages added to distinguish between
different errors in packets received from upstream nameservers.
(Trac #1383, git 9b2b249d23576c999a65d8c338e008cabe45f0c9)
@@ -69,12 +90,12 @@
potential problems and were fixed.
(Trac #1389, git 3fdce88046bdad392bd89ea656ec4ac3c858ca2f)
333. [bug] dvv
Solaris needs "-z now" to force non-lazy binding and prevent g++ static
initialization code from deadlocking.
333. [bug] dvv
Solaris needs "-z now" to force non-lazy binding and prevent
g++ static initialization code from deadlocking.
(Trac #1439, git c789138250b33b6b08262425a08a2a0469d90433)
332. [bug] vorner
332. [bug] vorner
C++ exceptions in the isc.dns.Rdata wrapper are now converted
to python ones instead of just aborting the interpretter.
(Trac #1407, git 5b64e839be2906b8950f5b1e42a3fadd72fca033)
@@ -109,7 +130,7 @@ bind10-devel-20111128 released on November 28, 2011
always respond to IXFR requests according to RFC1995).
(Trac #1371 and #1372, git 80c131f5b0763753d199b0fb9b51f10990bcd92b)
326. [build]* jinmei
326. [build]* jinmei
Added a check script for the SQLite3 schema version. It will be
run at the beginning of 'make install', and if it detects an old
version of schema, installation will stop. You'll then need to

View File

@@ -730,7 +730,7 @@ then
GTEST_FOUND="true"
# There is no gtest-config script on this
# system, which is supposed to inform us
# whether we need pthreads as well (a
# whether we need pthreads as well (a
# gtest compile-time option). So we still
# need to test that manually.
CPPFLAGS_SAVED="$CPPFLAGS"
@@ -892,6 +892,8 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/benchmarks/Makefile
src/bin/dhcp6/Makefile
src/bin/dhcp6/tests/Makefile
src/bin/dhcp4/Makefile
src/bin/dhcp4/tests/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
src/bin/sockcreator/Makefile
@@ -984,6 +986,8 @@ AC_CONFIG_FILES([Makefile
src/lib/util/tests/Makefile
src/lib/acl/Makefile
src/lib/acl/tests/Makefile
src/lib/statistics/Makefile
src/lib/statistics/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
@@ -1031,6 +1035,7 @@ AC_OUTPUT([doc/version.ent
src/bin/msgq/run_msgq.sh
src/bin/auth/auth.spec.pre
src/bin/auth/spec_config.h.pre
src/bin/dhcp4/spec_config.h.pre
src/bin/dhcp6/spec_config.h.pre
src/bin/tests/process_rename_test.py
src/lib/config/tests/data_def_unittests_config.h

View File

@@ -1502,6 +1502,49 @@ what if a NOTIFY is sent?
-->
<section id="zonemgr">
<title>Secondary Manager</title>
<para>
The <command>b10-zonemgr</command> process is started by
<command>bind10</command>.
It keeps track of SOA refresh, retry, and expire timers
and other details for BIND 10 to perform as a slave.
When the <command>b10-auth</command> authoritative DNS server
receives a NOTIFY message, <command>b10-zonemgr</command>
may tell <command>b10-xfrin</command> to do a refresh
to start an inbound zone transfer.
The secondary manager resets its counters when a new zone is
transferred in.
</para>
<note><simpara>
Access control (such as allowing notifies) is not yet provided.
The primary/secondary service is not yet complete.
</simpara></note>
<para>
The following example shows using <command>bindctl</command>
to configure the server to be a secondary for the example zone:
<screen>&gt; <userinput>config add Zonemgr/secondary_zones</userinput>
&gt; <userinput>config set Zonemgr/secondary_zones[0]/name "<option>example.com</option>"</userinput>
&gt; <userinput>config set Zonemgr/secondary_zones[0]/class "<option>IN</option>"</userinput>
&gt; <userinput>config commit</userinput></screen>
<!-- TODO: remove the IN class example above when it is the default -->
</para>
<para>
If the zone does not exist in the data source already
(i.e. no SOA record for it), <command>b10-zonemgr</command>
will automatically tell <command>b10-xfrin</command>
to transfer the zone in.
</para>
</section>
<section>
<title>Trigger an Incoming Zone Transfer Manually</title>
@@ -1514,7 +1557,6 @@ what if a NOTIFY is sent?
</para>
</section>
<!-- TODO: can that retransfer be used to identify a new zone? -->
<!-- TODO: what if doesn't exist at that master IP? -->
@@ -1606,31 +1648,6 @@ what is XfroutClient xfr_client??
</chapter>
<chapter id="zonemgr">
<title>Secondary Manager</title>
<para>
The <command>b10-zonemgr</command> process is started by
<command>bind10</command>.
It keeps track of SOA refresh, retry, and expire timers
and other details for BIND 10 to perform as a slave.
When the <command>b10-auth</command> authoritative DNS server
receives a NOTIFY message, <command>b10-zonemgr</command>
may tell <command>b10-xfrin</command> to do a refresh
to start an inbound zone transfer.
The secondary manager resets its counters when a new zone is
transferred in.
</para>
<note><simpara>
Access control (such as allowing notifies) is not yet provided.
The primary/secondary service is not yet complete.
</simpara></note>
<!-- TODO: lots to describe for zonemgr -->
</chapter>
<chapter id="resolverserver">
<title>Recursive Name Server</title>

View File

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

View File

@@ -71,6 +71,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
b10_auth_LDADD += $(SQLITE_LIBS)
# TODO: config.h.in is wrong because doesn't honor pkgdatadir

View File

@@ -671,9 +671,9 @@ void
AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
counters_.inc(AuthCounters::SERVER_UDP_QUERY);
} else if (protocol == IPPROTO_TCP) {
counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
counters_.inc(AuthCounters::SERVER_TCP_QUERY);
} else {
// unknown protocol
isc_throw(Unexpected, "Unknown protocol: " << protocol);
@@ -766,7 +766,7 @@ bool AuthSrv::submitStatistics() const {
}
uint64_t
AuthSrv::getCounter(const AuthCounters::CounterType type) const {
AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->counters_.getCounter(type));
}

View File

@@ -343,7 +343,7 @@ public:
/// \param type Type of a counter to get the value of
///
/// \return the value of the counter.
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/**
* \brief Set and get the addresses we listen on.

View File

@@ -35,5 +35,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.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 += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
query_bench_LDADD += $(SQLITE_LIBS)

View File

@@ -18,48 +18,67 @@
#include <cc/data.h>
#include <cc/session.h>
#include <statistics/counter.h>
#include <statistics/counter_dict.h>
#include <sstream>
#include <iostream>
#include <boost/noncopyable.hpp>
using namespace isc::auth;
using namespace isc::statistics;
// TODO: We need a namespace ("auth_server"?) to hold
// AuthSrv and AuthCounters.
class AuthCountersImpl {
private:
// prohibit copy
AuthCountersImpl(const AuthCountersImpl& source);
AuthCountersImpl& operator=(const AuthCountersImpl& source);
// TODO: Make use of wrappers like isc::dns::Opcode
// for counter item type.
class AuthCountersImpl : boost::noncopyable {
public:
AuthCountersImpl();
~AuthCountersImpl();
void inc(const AuthCounters::CounterType type);
void inc(const AuthCounters::ServerCounterType type);
void inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type);
bool submitStatistics() const;
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
void registerStatisticsValidator
(AuthCounters::validator_type validator);
// Currently for testing purpose only
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
private:
std::vector<uint64_t> counters_;
Counter server_counter_;
CounterDictionary per_zone_counter_;
isc::cc::AbstractSession* statistics_session_;
AuthCounters::validator_type validator_;
};
AuthCountersImpl::AuthCountersImpl() :
// initialize counter
// size: AuthCounters::COUNTER_TYPES, initial value: 0
counters_(AuthCounters::COUNTER_TYPES, 0),
// size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
// size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
statistics_session_(NULL)
{}
{
per_zone_counter_.addElement("_SERVER_");
}
AuthCountersImpl::~AuthCountersImpl()
{}
void
AuthCountersImpl::inc(const AuthCounters::CounterType type) {
++counters_.at(type);
AuthCountersImpl::inc(const AuthCounters::ServerCounterType type) {
server_counter_.inc(type);
}
void
AuthCountersImpl::inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type)
{
per_zone_counter_[zone].inc(type);
}
bool
@@ -73,9 +92,9 @@ AuthCountersImpl::submitStatistics() const {
<< "{ \"owner\": \"Auth\","
<< " \"data\":"
<< "{ \"queries.udp\": "
<< counters_.at(AuthCounters::COUNTER_UDP_QUERY)
<< server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
<< ", \"queries.tcp\": "
<< counters_.at(AuthCounters::COUNTER_TCP_QUERY)
<< server_counter_.get(AuthCounters::SERVER_TCP_QUERY)
<< " }"
<< "}"
<< "]}";
@@ -126,19 +145,17 @@ AuthCountersImpl::registerStatisticsValidator
// Currently for testing purpose only
uint64_t
AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
return (counters_.at(type));
AuthCountersImpl::getCounter(const AuthCounters::ServerCounterType type) const {
return (server_counter_.get(type));
}
AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
{}
AuthCounters::~AuthCounters() {
delete impl_;
}
AuthCounters::~AuthCounters() {}
void
AuthCounters::inc(const AuthCounters::CounterType type) {
AuthCounters::inc(const AuthCounters::ServerCounterType type) {
impl_->inc(type);
}
@@ -155,7 +172,7 @@ AuthCounters::setStatisticsSession
}
uint64_t
AuthCounters::getCounter(const AuthCounters::CounterType type) const {
AuthCounters::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->getCounter(type));
}

View File

@@ -17,6 +17,7 @@
#include <cc/session.h>
#include <stdint.h>
#include <boost/scoped_ptr.hpp>
class AuthCountersImpl;
@@ -51,13 +52,18 @@ class AuthCountersImpl;
/// \todo Consider overhead of \c AuthCounters::inc()
class AuthCounters {
private:
AuthCountersImpl* impl_;
boost::scoped_ptr<AuthCountersImpl> impl_;
public:
// Enum for the type of counter
enum CounterType {
COUNTER_UDP_QUERY = 0, ///< COUNTER_UDP_QUERY: counter for UDP queries
COUNTER_TCP_QUERY = 1, ///< COUNTER_TCP_QUERY: counter for TCP queries
COUNTER_TYPES = 2 ///< The number of defined counters
enum ServerCounterType {
SERVER_UDP_QUERY, ///< SERVER_UDP_QUERY: counter for UDP queries
SERVER_TCP_QUERY, ///< SERVER_TCP_QUERY: counter for TCP queries
SERVER_COUNTER_TYPES ///< The number of defined counters
};
enum PerZoneCounterType {
ZONE_UDP_QUERY, ///< ZONE_UDP_QUERY: counter for UDP queries
ZONE_TCP_QUERY, ///< ZONE_TCP_QUERY: counter for TCP queries
PER_ZONE_COUNTER_TYPES ///< The number of defined counters
};
/// The constructor.
///
@@ -77,9 +83,9 @@ public:
///
/// \throw std::out_of_range \a type is unknown.
///
/// usage: counter.inc(CounterType::COUNTER_UDP_QUERY);
/// usage: counter.inc(AuthCounters::SERVER_UDP_QUERY);
///
void inc(const CounterType type);
void inc(const ServerCounterType type);
/// \brief Submit statistics counters to statistics module.
///
@@ -130,7 +136,7 @@ public:
///
/// \return the value of the counter specified by \a type.
///
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/// \brief A type of validation function for the specification in
/// isc::config::ModuleSpec.

View File

@@ -65,6 +65,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -779,7 +779,7 @@ TEST_F(AuthSrvTest, cacheSlots) {
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
// Create UDP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
@@ -788,13 +788,13 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing UDP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
// Create TCP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
@@ -803,13 +803,13 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing TCP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
@@ -818,13 +818,13 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// Submit TCP IXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
@@ -833,7 +833,7 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP IXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// class for queryCounterUnexpected test

View File

@@ -150,25 +150,24 @@ AuthCountersTest::MockSession::setThrowSessionTimeout(bool flag) {
TEST_F(AuthCountersTest, incrementUDPCounter) {
// The counter should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_UDP_QUERY));
// After increment, the counter should be 1.
EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
TEST_F(AuthCountersTest, incrementTCPCounter) {
// The counter should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_TCP_QUERY));
// After increment, the counter should be 1.
EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
TEST_F(AuthCountersTest, incrementInvalidCounter) {
// Expect to throw isc::InvalidParameter if the type of the counter is
// invalid.
EXPECT_THROW(counters.inc(AuthCounters::COUNTER_TYPES),
std::out_of_range);
// Expect to throw an isc::OutOfRange
EXPECT_THROW(counters.inc(AuthCounters::SERVER_COUNTER_TYPES),
isc::OutOfRange);
}
TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
@@ -195,14 +194,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
// Validate if it submits correct data.
// Counters should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
// UDP query counter is set to 2.
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
// TCP query counter is set to 1.
counters.inc(AuthCounters::COUNTER_TCP_QUERY);
counters.inc(AuthCounters::SERVER_TCP_QUERY);
counters.submitStatistics();
// Destination is "Stats".
@@ -237,14 +236,14 @@ TEST_F(AuthCountersTest, submitStatisticsWithValidator) {
counters.registerStatisticsValidator(validator);
// Counters should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
// UDP query counter is set to 2.
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
// TCP query counter is set to 1.
counters.inc(AuthCounters::COUNTER_TCP_QUERY);
counters.inc(AuthCounters::SERVER_TCP_QUERY);
// checks the value returned by submitStatistics
EXPECT_TRUE(counters.submitStatistics());

43
src/bin/dhcp4/Makefile.am Normal file
View File

@@ -0,0 +1,43 @@
SUBDIRS = . tests
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 += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = spec_config.h
man_MANS = b10-dhcp4.8
EXTRA_DIST = $(man_MANS) dhcp4.spec
if ENABLE_MAN
b10-dhcp4.8: b10-dhcp4.xml
xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-dhcp4.xml
endif
spec_config.h: spec_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
BUILT_SOURCES = spec_config.h
pkglibexec_PROGRAMS = b10-dhcp4
b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
# TODO: config.h.in is wrong because doesn't honor pkgdatadir
# and can't use @datadir@ because doesn't expand default ${prefix}
b10_dhcp4dir = $(pkgdatadir)
b10_dhcp4_DATA = dhcp4.spec

60
src/bin/dhcp4/b10-dhcp4.8 Normal file
View File

@@ -0,0 +1,60 @@
'\" t
.\" Title: b10-dhcp4
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: October 27, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
.TH "B10\-DHCP4" "8" "October 27, 2011" "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
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
b10-dhcp4 \- DHCPv4 server in BIND 10 architecture
.SH "SYNOPSIS"
.HP \w'\fBb10\-dhcp4\fR\ 'u
\fBb10\-dhcp4\fR [\fB\-v\fR]
.SH "DESCRIPTION"
.PP
The
\fBb10\-dhcp4\fR
daemon will provide the DHCPv4 server implementation when it becomes functional\&.
.SH "ARGUMENTS"
.PP
The arguments are as follows:
.PP
\fB\-v\fR
.RS 4
Enable verbose mode\&.
.RE
.SH "SEE ALSO"
.PP
\fBbind10\fR(8)\&.
.SH "HISTORY"
.PP
The
\fBb10\-dhcp4\fR
daemon was first coded in November 2011 by Tomek Mrugalski\&.
.SH "COPYRIGHT"
.br
Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
.br

View File

@@ -0,0 +1,98 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- 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.
-->
<refentry>
<refentryinfo>
<date>October 27, 2011</date>
</refentryinfo>
<refmeta>
<refentrytitle>b10-dhcp4</refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo>BIND10</refmiscinfo>
</refmeta>
<refnamediv>
<refname>b10-dhcp4</refname>
<refpurpose>DHCPv4 server in BIND 10 architecture</refpurpose>
</refnamediv>
<docinfo>
<copyright>
<year>2011</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
<refsynopsisdiv>
<cmdsynopsis>
<command>b10-dhcp4</command>
<arg><option>-v</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para>
The <command>b10-dhcp4</command> daemon will provide the
DHCPv4 server implementation when it becomes functional.
</para>
</refsect1>
<refsect1>
<title>ARGUMENTS</title>
<para>The arguments are as follows:</para>
<variablelist>
<varlistentry>
<term><option>-v</option></term>
<listitem><para>
Enable verbose mode.
<!-- TODO: what does this do? -->
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>SEE ALSO</title>
<para>
<citerefentry>
<refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>.
</para>
</refsect1>
<refsect1>
<title>HISTORY</title>
<para>
The <command>b10-dhcp4</command> daemon was first coded in
November 2011 by Tomek Mrugalski.
</para>
</refsect1>
</refentry><!--
- Local variables:
- mode: sgml
- End:
-->

14
src/bin/dhcp4/dhcp4.spec Normal file
View File

@@ -0,0 +1,14 @@
{
"module_spec": {
"module_name": "dhcp4",
"module_description": "DHCPv4 server daemon",
"config_data": [
{ "item_name": "interface",
"item_type": "string",
"item_optional": false,
"item_default": "eth0"
}
],
"commands": []
}
}

154
src/bin/dhcp4/dhcp4_srv.cc Normal file
View File

@@ -0,0 +1,154 @@
// 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 <dhcp/dhcp4.h>
#include <dhcp/pkt4.h>
#include <dhcp/iface_mgr.h>
#include <dhcp4/dhcp4_srv.h>
#include <asiolink/io_address.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
cout << "Initialization: opening sockets on port " << port << endl;
// first call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong
IfaceMgr::instance();
/// @todo: instantiate LeaseMgr here once it is imlpemented.
setServerID();
shutdown_ = false;
}
Dhcpv4Srv::~Dhcpv4Srv() {
cout << "DHCPv4 server shutdown." << endl;
}
bool
Dhcpv4Srv::run() {
while (!shutdown_) {
boost::shared_ptr<Pkt4> query; // client's message
boost::shared_ptr<Pkt4> rsp; // server's response
#if 0
// uncomment this once ticket 1239 is merged.
query = IfaceMgr::instance().receive4();
#endif
if (query) {
if (!query->unpack()) {
cout << "Failed to parse incoming packet" << endl;
continue;
}
switch (query->getType()) {
case DHCPDISCOVER:
rsp = processDiscover(query);
break;
case DHCPREQUEST:
rsp = processRequest(query);
break;
case DHCPRELEASE:
processRelease(query);
break;
case DHCPDECLINE:
processDecline(query);
break;
case DHCPINFORM:
processInform(query);
break;
default:
cout << "Unknown pkt type received:"
<< query->getType() << endl;
}
cout << "Received " << query->len() << " bytes packet type="
<< query->getType() << endl;
// TODO: print out received packets only if verbose (or debug)
// mode is enabled
cout << query->toText();
if (rsp) {
rsp->setRemoteAddr(query->getRemoteAddr());
rsp->setLocalAddr(query->getLocalAddr());
rsp->setRemotePort(DHCP4_CLIENT_PORT);
rsp->setLocalPort(DHCP4_SERVER_PORT);
rsp->setIface(query->getIface());
rsp->setIndex(query->getIndex());
cout << "Replying with:" << rsp->getType() << endl;
cout << rsp->toText();
cout << "----" << endl;
if (rsp->pack()) {
cout << "Packet assembled correctly." << endl;
}
#if 0
// uncomment this once ticket 1240 is merged.
IfaceMgr::instance().send4(rsp);
#endif
}
}
// TODO add support for config session (see src/bin/auth/main.cc)
// so this daemon can be controlled from bob
}
return (true);
}
void
Dhcpv4Srv::setServerID() {
/// TODO implement this for real once interface detection (ticket 1237)
/// is done. Use hardcoded server-id for now.
#if 0
// uncomment this once ticket 1350 is merged.
IOAddress srvId("127.0.0.1");
serverid_ = boost::shared_ptr<Option>(
new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
#endif
}
boost::shared_ptr<Pkt4>
Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
/// TODO: Currently implemented echo mode. Implement this for real
return (discover);
}
boost::shared_ptr<Pkt4>
Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
/// TODO: Currently implemented echo mode. Implement this for real
return (request);
}
void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
/// TODO: Implement this.
cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
}
void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4>& decline) {
/// TODO: Implement this.
cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
}
boost::shared_ptr<Pkt4> Dhcpv4Srv::processInform(boost::shared_ptr<Pkt4>& inform) {
/// TODO: Currently implemented echo mode. Implement this for real
return (inform);
}

137
src/bin/dhcp4/dhcp4_srv.h Normal file
View File

@@ -0,0 +1,137 @@
// 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 DHCPV4_SRV_H
#define DHCPV4_SRV_H
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <dhcp/dhcp4.h>
#include <dhcp/pkt4.h>
#include <dhcp/option.h>
#include <iostream>
namespace isc {
namespace dhcp {
/// @brief DHCPv4 server service.
///
/// This singleton class represents DHCPv4 server. It contains all
/// top-level methods and routines necessary for server operation.
/// In particular, it instantiates IfaceMgr, loads or generates DUID
/// that is going to be used as server-identifier, receives incoming
/// packets, processes them, manages leases assignment and generates
/// appropriate responses.
class Dhcpv4Srv : public boost::noncopyable {
public:
/// @brief Default constructor.
///
/// Instantiates necessary services, required to run DHCPv6 server.
/// In particular, creates IfaceMgr that will be responsible for
/// network interaction. Will instantiate lease manager, and load
/// old or create new DUID. It is possible to specify alternate
/// port on which DHCPv4 server will listen on. That is mostly useful
/// for testing purposes.
///
/// @param port specifies port number to listen on
Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT);
/// @brief Destructor. Used during DHCPv6 service shutdown.
~Dhcpv4Srv();
/// @brief Main server processing loop.
///
/// Main server processing loop. Receives incoming packets, verifies
/// their correctness, generates appropriate answer (if needed) and
/// transmits respones.
///
/// @return true, if being shut down gracefully, fail if experienced
/// critical error.
bool run();
protected:
/// @brief Processes incoming DISCOVER and returns response.
///
/// Processes received DISCOVER message and verifies that its sender
/// should be served. In particular, a lease is selected and sent
/// as an offer to a client if it should be served.
///
/// @param solicit DISCOVER message received from client
///
/// @return OFFER message or NULL
boost::shared_ptr<Pkt4>
processDiscover(boost::shared_ptr<Pkt4>& discover);
/// @brief Processes incoming REQUEST and returns REPLY response.
///
/// Processes incoming REQUEST message and verifies that its sender
/// should be served. In particular, verifies that requested lease
/// is valid, not expired, not reserved, not used by other client and
/// that requesting client is allowed to use it.
///
/// Returns ACK message, NACK message, or NULL
///
/// @param request a message received from client
///
/// @return ACK or NACK message
boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request);
/// @brief Stub function that will handle incoming RELEASE messages.
///
/// In DHCPv4, server does not respond to RELEASE messages, therefore
/// this function does not return anything.
///
/// @param release message received from client
void processRelease(boost::shared_ptr<Pkt4>& release);
/// @brief Stub function that will handle incoming DHCPDECLINE messages.
///
/// @param decline message received from client
void processDecline(boost::shared_ptr<Pkt4>& decline);
/// @brief Stub function that will handle incoming INFORM messages.
///
/// @param infRequest message received from client
boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
/// @brief Returns server-intentifier option
///
/// @return server-id option
boost::shared_ptr<isc::dhcp::Option>
getServerID() { return serverid_; }
/// @brief Sets server-identifier.
///
/// This method attempts to set server-identifier DUID. It tries to
/// load previously stored IP from configuration. If there is no previously
/// stored server identifier, it will pick up one address from configured
/// and supported network interfaces.
///
/// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
// previously stored configuration and no network interfaces available)
void setServerID();
/// server DUID (to be sent in server-identifier option)
boost::shared_ptr<isc::dhcp::Option> serverid_;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
volatile bool shutdown_;
};
}; // namespace isc::dhcp
}; // namespace isc
#endif // DHCP4_SRV_H

112
src/bin/dhcp4/main.cc Normal file
View File

@@ -0,0 +1,112 @@
// 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 <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <cassert>
#include <iostream>
#include <exceptions/exceptions.h>
#if 0
// TODO cc is not used yet. It should be eventually
#include <cc/session.h>
#include <config/ccsession.h>
#endif
#include <util/buffer.h>
#include <log/dummylog.h>
#include <dhcp4/spec_config.h>
#include <dhcp4/dhcp4_srv.h>
using namespace std;
using namespace isc::util;
using namespace isc;
using namespace isc::dhcp;
namespace {
bool verbose_mode = false;
void
usage() {
cerr << "Usage: b10-dhcp4 [-v]"
<< endl;
cerr << "\t-v: verbose output" << endl;
exit(1);
}
} // end of anonymous namespace
int
main(int argc, char* argv[]) {
int ch;
while ((ch = getopt(argc, argv, ":v")) != -1) {
switch (ch) {
case 'v':
verbose_mode = true;
isc::log::denabled = true;
break;
case ':':
default:
usage();
}
}
cout << "My pid=" << getpid() << endl;
if (argc - optind > 0) {
usage();
}
int ret = 0;
// TODO remainder of auth to dhcp4 code copy. We need to enable this in
// dhcp4 eventually
#if 0
Session* cc_session = NULL;
Session* statistics_session = NULL;
ModuleCCSession* config_session = NULL;
#endif
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {
specfile = string(getenv("B10_FROM_BUILD")) +
"/src/bin/auth/dhcp4.spec";
} else {
specfile = string(DHCP4_SPECFILE_LOCATION);
}
cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
Dhcpv4Srv* srv = new Dhcpv4Srv();
srv->run();
} catch (const std::exception& ex) {
cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
ret = 1;
}
return (ret);
}

View File

@@ -0,0 +1,15 @@
// 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.
#define DHCP4_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/dhcp4.spec"

View File

@@ -0,0 +1,46 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
endif
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_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
CLEANFILES = $(builddir)/interfaces.txt
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
TESTS =
if HAVE_GTEST
TESTS += dhcp4_unittests
dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc
dhcp4_unittests_SOURCES += dhcp4_unittests.cc
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp4_unittests_LDADD = $(GTEST_LDADD)
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,161 @@
// 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 <config.h>
#include <iostream>
#include <sstream>
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <dhcp/dhcp4.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp/option.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
namespace {
class NakedDhcpv4Srv: public Dhcpv4Srv {
// "naked" DHCPv4 server, exposes internal fields
public:
NakedDhcpv4Srv() { }
boost::shared_ptr<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
return Dhcpv4Srv::processDiscover(discover);
}
boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request) {
return Dhcpv4Srv::processRequest(request);
}
void processRelease(boost::shared_ptr<Pkt4>& release) {
return Dhcpv4Srv::processRelease(release);
}
void processDecline(boost::shared_ptr<Pkt4>& decline) {
Dhcpv4Srv::processDecline(decline);
}
boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform) {
return Dhcpv4Srv::processInform(inform);
}
};
class Dhcpv4SrvTest : public ::testing::Test {
public:
Dhcpv4SrvTest() {
}
~Dhcpv4SrvTest() {
};
};
TEST_F(Dhcpv4SrvTest, basic) {
// nothing to test. DHCPv4_srv instance is created
// in test fixture. It is destroyed in destructor
Dhcpv4Srv* srv = NULL;
ASSERT_NO_THROW({
srv = new Dhcpv4Srv();
});
delete srv;
}
TEST_F(Dhcpv4SrvTest, processDiscover) {
NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
// should not throw
EXPECT_NO_THROW(
srv->processDiscover(pkt);
);
// should return something
EXPECT_TRUE(srv->processDiscover(pkt));
// TODO: Implement more reasonable tests before starting
// work on processSomething() method.
delete srv;
}
TEST_F(Dhcpv4SrvTest, processRequest) {
NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPREQUEST, 1234));
// should not throw
EXPECT_NO_THROW(
srv->processRequest(pkt);
);
// should return something
EXPECT_TRUE(srv->processRequest(pkt));
// TODO: Implement more reasonable tests before starting
// work on processSomething() method.
delete srv;
}
TEST_F(Dhcpv4SrvTest, processRelease) {
NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPRELEASE, 1234));
// should not throw
EXPECT_NO_THROW(
srv->processRelease(pkt);
);
// TODO: Implement more reasonable tests before starting
// work on processSomething() method.
delete srv;
}
TEST_F(Dhcpv4SrvTest, processDecline) {
NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDECLINE, 1234));
// should not throw
EXPECT_NO_THROW(
srv->processDecline(pkt);
);
// TODO: Implement more reasonable tests before starting
// work on processSomething() method.
delete srv;
}
TEST_F(Dhcpv4SrvTest, processInform) {
NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPINFORM, 1234));
// should not throw
EXPECT_NO_THROW(
srv->processInform(pkt);
);
// should return something
EXPECT_TRUE(srv->processInform(pkt));
// TODO: Implement more reasonable tests before starting
// work on processSomething() method.
delete srv;
}
} // end of anonymous namespace

View File

@@ -0,0 +1,28 @@
// 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 <stdio.h>
#include <gtest/gtest.h>
#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::log::initLogger();
int result = RUN_ALL_TESTS();
return (result);
}

View File

@@ -32,10 +32,9 @@ spec_config.h: spec_config.h.pre
BUILT_SOURCES = spec_config.h
pkglibexec_PROGRAMS = b10-dhcp6
b10_dhcp6_SOURCES = main.cc iface_mgr.cc dhcp6_srv.cc
b10_dhcp6_SOURCES += iface_mgr.h dhcp6_srv.h
b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp.la
b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la

View File

@@ -1,7 +1,7 @@
{
"module_spec": {
"module_name": "dhcp6",
"module_description": "DHCPv6 daemon",
"module_description": "DHCPv6 server daemon",
"config_data": [
{ "item_name": "interface",
"item_type": "string",

View File

@@ -14,7 +14,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcp6/iface_mgr.h>
#include <dhcp/iface_mgr.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>

View File

@@ -43,10 +43,8 @@ if HAVE_GTEST
TESTS += dhcp6_unittests
dhcp6_unittests_SOURCES = ../iface_mgr.h ../iface_mgr.cc
dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
dhcp6_unittests_SOURCES += dhcp6_unittests.cc
dhcp6_unittests_SOURCES += iface_mgr_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -54,7 +52,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp6_unittests_LDADD = $(GTEST_LDADD)
dhcp6_unittests_LDADD += $(SQLITE_LIBS)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif

View File

@@ -57,7 +57,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
// interfaces.txt instead. It will pretend to have detected
// fe80::1234 link-local address on eth0 interface. Obviously
// an attempt to bind this socket will fail.
Dhcpv6Srv* srv = 0;
Dhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( {
// open an unpriviledged port
srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
@@ -67,7 +67,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
}
TEST_F(Dhcpv6SrvTest, Solicit_basic) {
NakedDhcpv6Srv * srv = 0;
NakedDhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
@@ -116,7 +116,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
boost::shared_ptr<Option> tmp = reply->getOption(D6O_IA_NA);
ASSERT_TRUE( tmp );
Option6IA * reply_ia = dynamic_cast<Option6IA*> ( tmp.get() );
Option6IA* reply_ia = dynamic_cast<Option6IA*> ( tmp.get() );
EXPECT_EQ( 234, reply_ia->getIAID() );
// check that there's an address included

View File

@@ -59,7 +59,13 @@ class TestDhcpv6Daemon(unittest.TestCase):
# kill this process
# XXX: b10-dhcp6 is too dumb to understand 'shutdown' command for now,
# so let's just kill the bastard
os.kill(pi.pid, signal.SIGTERM)
# TODO: Ignore errors for now. This test will be more thorough once ticket #1503
# (passing port number to b10-dhcp6 daemon) is implemented.
try:
os.kill(pi.pid, signal.SIGTERM)
except OSError:
print("Ignoring failed kill attempt. Process is dead already.")
if __name__ == '__main__':
unittest.main()

View File

@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
<date>May 19, 2011</date>
<date>December 8, 2011</date>
</refentryinfo>
<refmeta>
@@ -107,15 +107,20 @@
<para>
<varname>refresh_jitter</varname>
is used to provide a time range for randomizing the refresh
and retry timers to help avoid many zones needing to do a refresh
or retry at the same time.
This value is a real number.
The maximum amount is 0.5.
The default is 0.25.
The maximum amount is 0.5 (the new timer will be within
half the original time).
The default is 0.25 (up to a quarter sooner).
Set to 0 to disable this jitter.
</para>
<!-- TODO: needs to be documented -->
<!-- TODO: Set to 0 to disable the jitter. -->
<para>
<varname>reload_jitter</varname>
<!-- is used to provide a slight random variation -->
<!-- TODO: ask what the purpose of this is and why 0.75. -->
This value is a real number.
The default is 0.75.
</para>
@@ -224,14 +229,6 @@
</refsect1>
-->
<!--
<refsect1>
<title>FILES</title>
<para>
<filename>/tmp/auth_xfrout_conn</filename>
</para>
</refsect1>
-->
<refsect1>
<title>SEE ALSO</title>
@@ -248,9 +245,6 @@
<citerefentry>
<refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,

View File

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

View File

@@ -16,7 +16,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
run_unittests_LDFLAGS = $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS) $(AM_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD) $(BOTAN_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la

View File

@@ -359,7 +359,7 @@ FINAL_TYPES() {
}
RRsetPtr
ConstRRsetPtr
DatabaseClient::Finder::findNSECCover(const Name& name) {
try {
// Which one should contain the NSEC record?
@@ -394,7 +394,7 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
arg(accessor_->getDBName()).arg(name);
}
// We didn't find it, return nothing
return (RRsetPtr());
return (ConstRRsetPtr());
}
ZoneFinder::FindResult
@@ -416,68 +416,96 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
return (findInternal(name, type, NULL, options));
}
ZoneFinder::FindResult
DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
const isc::dns::RRType& type,
std::vector<isc::dns::ConstRRsetPtr>*
target,
const FindOptions options)
DatabaseClient::Finder::DelegationSearchResult
DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
const FindOptions options)
{
// This variable is used to determine the difference between
// NXDOMAIN and NXRRSET
bool records_found = false;
bool glue_ok((options & FIND_GLUE_OK) != 0);
const bool dnssec_data((options & FIND_DNSSEC) != 0);
bool get_cover(false);
bool any(type == RRType::ANY());
isc::dns::RRsetPtr result_rrset;
// Result of search
isc::dns::ConstRRsetPtr result_rrset;
ZoneFinder::Result result_status = SUCCESS;
FoundRRsets found;
logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
.arg(accessor_->getDBName()).arg(name).arg(type);
// In case we are in GLUE_OK mode and start matching wildcards,
// we can't do it under NS, so we store it here to check
isc::dns::RRsetPtr first_ns;
// First, do we have any kind of delegation (NS/DNAME) here?
const Name origin(getOrigin());
const size_t origin_label_count(origin.getLabelCount());
// Number of labels in the last known non-empty domain
size_t last_known(origin_label_count);
const size_t current_label_count(name.getLabelCount());
// This is how many labels we remove to get origin
const size_t remove_labels(current_label_count - origin_label_count);
// Are we searching for glue?
const bool glue_ok = ((options & FIND_GLUE_OK) != 0);
// Now go trough all superdomains from origin down
for (int i(remove_labels); i > 0; --i) {
Name superdomain(name.split(i));
// Look if there's NS or DNAME (but ignore the NS in origin)
found = getRRsets(superdomain.toText(), DELEGATION_TYPES(),
i != remove_labels);
// This next declaration is an optimisation. When we search the database
// for glue records, we generally ignore delegations. (This allows for
// the case where e.g. the delegation to zone example.com refers to
// nameservers within the zone, e.g. ns1.example.com. When conducting the
// search for ns1.example.com, we have to search past the NS records at
// example.com.)
//
// The one case where this is forbidden is when we search past the zone
// cut but the match we find for the glue is a wildcard match. In that
// case, we return the delegation instead (see RFC 1034, section 4.3.3).
// To save a new search, we record the location of the delegation cut when
// we encounter it here.
isc::dns::ConstRRsetPtr first_ns;
// We want to search from the apex down. We are given the full domain
// name so we have to do some manipulation to ensure that when we start
// checking superdomains, we start from the the domain name of the zone
// (e.g. if the name is b.a.example.com. and we are in the example.com.
// zone, we check example.com., a.example.com. and b.a.example.com. We
// don't need to check com. or .).
//
// Set the number of labels in the origin (i.e. apex of the zone) and in
// the last known non-empty domain (which, at this point, is the origin).
const size_t origin_label_count = getOrigin().getLabelCount();
size_t last_known = origin_label_count;
// Set how many labels we remove to get origin: this is the number of
// labels we have to process in our search.
const size_t remove_labels = name.getLabelCount() - origin_label_count;
// Go through all superdomains from the origin down searching for nodes
// that indicate a delegation (.e. NS or DNAME).
for (int i = remove_labels; i > 0; --i) {
const Name superdomain(name.split(i));
// Note if this is the origin. (We don't count NS records at the origin
// as a delegation so this controls whether NS RRs are included in
// the results of some searches.)
const bool not_origin = (i != remove_labels);
// Look if there's NS or DNAME at this point of the tree, but ignore
// the NS RRs at the apex of the zone.
const FoundRRsets found = getRRsets(superdomain.toText(),
DELEGATION_TYPES(), not_origin);
if (found.first) {
// It contains some RRs, so it exists.
last_known = superdomain.getLabelCount();
// This node contains either NS or DNAME RRs so it does exist.
const FoundIterator nsi(found.second.find(RRType::NS()));
const FoundIterator dni(found.second.find(RRType::DNAME()));
// In case we are in GLUE_OK mode, we want to store the
// highest encountered NS (but not apex)
if (glue_ok && !first_ns && i != remove_labels &&
nsi != found.second.end()) {
// An optimisation. We know that there is an exact match for
// something at this point in the tree so remember it. If we have
// to do a wildcard search, as we search upwards through the tree
// we don't need to pass this point, which is an exact match for
// the domain name.
last_known = superdomain.getLabelCount();
if (glue_ok && !first_ns && not_origin &&
nsi != found.second.end()) {
// If we are searching for glue ("glue OK" mode), store the
// highest NS record that we find that is not the apex. This
// is another optimisation for later, where we need the
// information if the domain we are looking for matches through
// a wildcard.
first_ns = nsi->second;
} else if (!glue_ok && i != remove_labels &&
nsi != found.second.end()) {
// Do a NS delegation, but ignore NS in glue_ok mode. Ignore
// delegation in apex
} else if (!glue_ok && not_origin && nsi != found.second.end()) {
// Not searching for glue and we have found an NS RRset that is
// not at the apex. We have found a delegation - return that
// fact, there is no need to search further down the tree.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION).
arg(accessor_->getDBName()).arg(superdomain);
result_rrset = nsi->second;
result_status = DELEGATION;
// No need to go lower, found
break;
} else if (dni != found.second.end()) {
// Very similar with DNAME
// We have found a DNAME so again stop searching down the tree
// and return the information.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DNAME).
arg(accessor_->getDBName()).arg(superdomain);
@@ -492,227 +520,361 @@ DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
}
}
}
return (DelegationSearchResult(result_status, result_rrset, first_ns,
last_known));
}
if (!result_rrset) { // Only if we didn't find a redirect already
// Try getting the final result and extract it
// It is special if there's a CNAME or NS, DNAME is ignored here
// And we don't consider the NS in origin
// This method is called when we have not found an exact match and when we
// know that the name is not an empty non-terminal. So the only way that
// the name can match something in the zone is through a wildcard match.
//
// During an earlier stage in the search for this name, we made a record of
// the lowest superdomain for which we know an RR exists. (Note the "we
// know" qualification - there may be lower superdomains (ones with more
// labels) that hold an RR, but as we weren't searching for them, we don't
// know about them.)
//
// In the search for a wildcard match (which starts at the given domain
// name and goes up the tree to successive superdomains), this is the level
// at which we can stop - there can't be a wildcard at or beyond that
// point.
//
// At each level that can stop the search, we should consider several cases:
//
// - If we found a wildcard match for a glue record below a
// delegation point, we don't return the match,
// instead we return the delegation. (Note that if we didn't
// a wildcard match at all, we would return NXDOMAIN, not the
// the delegation.)
//
// - If we found a wildcard match and we are sure that the match
// is not an empty non-terminal, return the result taking into account CNAME,
// on a zone cut, and NXRRSET.
// (E.g. searching for a match
// for c.b.a.example.com, we found that b.a.example.com did
// not exist but that *.a.example.com. did. Checking
// b.a.example.com revealed no subdomains, so we can use the
// wilcard match we found.)
//
// - If we found a more specified match, the wildcard search
// is canceled, resulting in NXDOMAIN. (E.g. searching for a match
// for c.b.a.example.com, we found that b.a.example.com did
// not exist but that *.a.example.com. did. Checking
// b.a.example.com found subdomains. So b.example.com is
// an empty non-terminal and so should not be returned in
// the wildcard matching process. In other words,
// b.example.com does exist in the DNS space, it just doesn't
// have any RRs associated with it.)
//
// - If we found a match, but it is an empty non-terminal asterisk (E.g.#
// subdomain.*.example.com. is present, but there is nothing at
// *.example.com.), return an NXRRSET indication;
// the wildcard exists in the DNS space, there's just nothing
// associated with it. If DNSSEC data is required, return the
// covering NSEC record.
//
// If none of the above applies in any level, the search fails with NXDOMAIN.
ZoneFinder::FindResult
DatabaseClient::Finder::findWildcardMatch(
const isc::dns::Name& name, const isc::dns::RRType& type,
const FindOptions options, const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>* target)
{
// Note that during the search we are going to search not only for the
// requested type, but also for types that indicate a delegation -
// NS and DNAME.
WantedTypes final_types(FINAL_TYPES());
final_types.insert(type);
WantedTypes final_types(FINAL_TYPES());
final_types.insert(type);
found = getRRsets(name.toText(), final_types, name != origin, NULL,
any);
records_found = found.first;
for (size_t i = 1; i <= (name.getLabelCount() - dresult.last_known); ++i) {
// NS records, CNAME record and Wanted Type records
const FoundIterator nsi(found.second.find(RRType::NS()));
const FoundIterator cni(found.second.find(RRType::CNAME()));
const FoundIterator wti(found.second.find(type));
if (name != origin && !glue_ok && nsi != found.second.end()) {
// There's a delegation at the exact node.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
arg(accessor_->getDBName()).arg(name);
result_status = DELEGATION;
result_rrset = nsi->second;
} else if (type != isc::dns::RRType::CNAME() &&
cni != found.second.end()) {
// A CNAME here
result_status = CNAME;
result_rrset = cni->second;
if (result_rrset->getRdataCount() != 1) {
isc_throw(DataSourceError, "CNAME with " <<
result_rrset->getRdataCount() <<
" rdata at " << name << ", expected 1");
}
} else if (wti != found.second.end()) {
// Just get the answer
// TODO update here
result_rrset = wti->second;
if (any) {
for (FoundIterator it(found.second.begin());
it != found.second.end(); ++it) {
if (it->second) {
// Skip over the empty ANY
target->push_back(it->second);
}
// Strip off the left-more label(s) in the name and replace with a "*".
const Name superdomain(name.split(i));
const string wildcard("*." + superdomain.toText());
const string construct_name(name.toText());
// TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
// RFC 4592 section 4.4).
// Search for a match. The types are the same as with original query.
FoundRRsets found = getRRsets(wildcard, final_types, true,
&construct_name);
if (found.first) {
// Found something - but what?
if (dresult.first_ns) {
// About to use first_ns. The only way this can be set is if
// we are searching for glue, so do a sanity check.
if ((options & FIND_GLUE_OK) == 0) {
isc_throw(Unexpected, "Inconsistent conditions during "
"cancel of wilcard search for " <<
name.toText() << ": find_ns non-null when not "
"processing glue request");
}
return (FindResult(result_status, result_rrset));
}
} else if (!records_found) {
// Nothing lives here.
// But check if something lives below this
// domain and if so, pretend something is here as well.
if (hasSubdomains(name.toText())) {
// Wildcard match for a glue below a delegation point
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
arg(accessor_->getDBName()).arg(name);
records_found = true;
get_cover = dnssec_data;
} else if ((options & NO_WILDCARD) != 0) {
// If wildcard check is disabled, the search will ultimately
// terminate with NXDOMAIN. If DNSSEC is enabled, flag that
// we need to get the NSEC records to prove this.
if (dnssec_data) {
get_cover = true;
}
} else {
// It's not empty non-terminal. So check for wildcards.
// We remove labels one by one and look for the wildcard there.
// Go up to first non-empty domain.
for (size_t i(1); i + last_known <= current_label_count; ++i) {
// Construct the name with *
const Name superdomain(name.split(i));
const string wildcard("*." + superdomain.toText());
const string construct_name(name.toText());
// TODO What do we do about DNAME here?
// The types are the same as with original query
found = getRRsets(wildcard, final_types, true,
&construct_name, any);
if (found.first) {
if (first_ns) {
// In case we are under NS, we don't
// wildcard-match, but return delegation
result_rrset = first_ns;
result_status = DELEGATION;
records_found = true;
// We pretend to switch to non-glue_ok mode
glue_ok = false;
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_CANCEL_NS).
arg(accessor_->getDBName()).arg(wildcard).
arg(first_ns->getName());
} else if (!hasSubdomains(name.split(i - 1).toText()))
{
// Nothing we added as part of the * can exist
// directly, as we go up only to first existing
// domain, but it could be empty non-terminal. In
// that case, we need to cancel the match.
records_found = true;
const FoundIterator
cni(found.second.find(RRType::CNAME()));
const FoundIterator
nsi(found.second.find(RRType::NS()));
const FoundIterator
nci(found.second.find(RRType::NSEC()));
const FoundIterator wti(found.second.find(type));
if (cni != found.second.end() &&
type != RRType::CNAME()) {
result_rrset = cni->second;
result_status = WILDCARD_CNAME;
} else if (nsi != found.second.end()) {
result_rrset = nsi->second;
result_status = DELEGATION;
} else if (wti != found.second.end()) {
result_rrset = wti->second;
// TODO Update here
result_status = WILDCARD;
if (any) {
for (FoundIterator
it(found.second.begin());
it != found.second.end(); ++it) {
if (it->second) {
// Skip over the empty ANY
target->push_back(it->second);
}
}
return (FindResult(result_status,
result_rrset));
}
} else {
// NXRRSET case in the wildcard
result_status = WILDCARD_NXRRSET;
if (dnssec_data &&
nci != found.second.end()) {
// User wants a proof the wildcard doesn't
// contain it
//
// However, we need to get the RRset in the
// name of the wildcard, not the constructed
// one, so we walk it again
found = getRRsets(wildcard, NSEC_TYPES(),
true);
result_rrset =
found.second.find(RRType::NSEC())->
second;
}
}
DATASRC_DATABASE_WILDCARD_CANCEL_NS).
arg(accessor_->getDBName()).arg(wildcard).
arg(dresult.first_ns->getName());
return (ZoneFinder::FindResult(DELEGATION, dresult.first_ns));
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD).
arg(accessor_->getDBName()).arg(wildcard).
arg(name);
} else {
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
arg(accessor_->getDBName()).arg(wildcard).
arg(name).arg(superdomain);
}
break;
} else if (hasSubdomains(wildcard)) {
// Empty non-terminal asterisk
records_found = true;
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_EMPTY).
arg(accessor_->getDBName()).arg(wildcard).
arg(name);
if (dnssec_data) {
result_rrset = findNSECCover(Name(wildcard));
if (result_rrset) {
result_status = WILDCARD_NXRRSET;
}
}
break;
}
}
// This is the NXDOMAIN case (nothing found anywhere). If
// they want DNSSEC data, try getting the NSEC record
if (dnssec_data && !records_found) {
get_cover = true;
} else if (!hasSubdomains(name.split(i - 1).toText())) {
// The wildcard match is the best one, find the final result
// at it. Note that wildcard should never be the zone origin.
return (findOnNameResult(name, type, options, false,
found, &wildcard, target));
} else {
// more specified match found, cancel wildcard match
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
arg(accessor_->getDBName()).arg(wildcard).
arg(name).arg(superdomain);
return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
}
} else if (hasSubdomains(wildcard)) {
// an empty non-terminal asterisk
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_EMPTY).
arg(accessor_->getDBName()).arg(wildcard).arg(name);
if ((options & FIND_DNSSEC) != 0) {
ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
if (nsec) {
return (ZoneFinder::FindResult(WILDCARD_NXRRSET, nsec));
}
}
} else if (dnssec_data) {
// This is the "usual" NXRRSET case
// So in case they want DNSSEC, provide the NSEC
// (which should be available already here)
result_status = NXRRSET;
const FoundIterator nci(found.second.find(RRType::NSEC()));
if (nci != found.second.end()) {
result_rrset = nci->second;
}
return (ZoneFinder::FindResult(NXRRSET, ConstRRsetPtr()));
}
}
if (!result_rrset) {
if (result_status == SUCCESS) {
// Should we look for NSEC covering the name?
if (get_cover) {
result_rrset = findNSECCover(name);
if (result_rrset) {
result_status = NXDOMAIN;
}
}
// Something is not here and we didn't decide yet what
if (records_found) {
logger.debug(DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_NXRRSET)
.arg(accessor_->getDBName()).arg(name)
.arg(getClass()).arg(type);
result_status = NXRRSET;
} else {
logger.debug(DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_NXDOMAIN)
.arg(accessor_->getDBName()).arg(name)
.arg(getClass()).arg(type);
result_status = NXDOMAIN;
}
// Nothing found at any level.
return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
}
ZoneFinder::FindResult
DatabaseClient::Finder::logAndCreateResult(
const Name& name, const string* wildname, const RRType& type,
ZoneFinder::Result code, ConstRRsetPtr rrset,
const isc::log::MessageID& log_id) const
{
if (rrset) {
if (wildname == NULL) {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
arg(accessor_->getDBName()).arg(name).arg(type).
arg(getClass()).arg(*rrset);
} else {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
arg(accessor_->getDBName()).arg(name).arg(type).
arg(getClass()).arg(*wildname).arg(*rrset);
}
} else {
logger.debug(DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_RRSET)
.arg(accessor_->getDBName()).arg(*result_rrset);
if (wildname == NULL) {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
arg(accessor_->getDBName()).arg(name).arg(type).
arg(getClass());
} else {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
arg(accessor_->getDBName()).arg(name).arg(type).
arg(getClass()).arg(*wildname);
}
}
return (ZoneFinder::FindResult(code, rrset));
}
ZoneFinder::FindResult
DatabaseClient::Finder::findOnNameResult(const Name& name,
const RRType& type,
const FindOptions options,
const bool is_origin,
const FoundRRsets& found,
const string* wildname,
std::vector<isc::dns::ConstRRsetPtr>*
target)
{
const bool wild = (wildname != NULL);
// Get iterators for the different types of records we are interested in -
// CNAME, NS and Wanted types.
const FoundIterator nsi(found.second.find(RRType::NS()));
const FoundIterator cni(found.second.find(RRType::CNAME()));
const FoundIterator wti(found.second.find(type));
if (!is_origin && ((options & FIND_GLUE_OK) == 0) &&
nsi != found.second.end()) {
// A NS RRset was found at the domain we were searching for. As it is
// not at the origin of the zone, it is a delegation and indicates that
// this zone is not authoritative for the data. Just return the
// delegation information.
return (logAndCreateResult(name, wildname, type, DELEGATION,
nsi->second,
wild ? DATASRC_DATABASE_WILDCARD_NS :
DATASRC_DATABASE_FOUND_DELEGATION_EXACT));
} else if (type != RRType::CNAME() && cni != found.second.end()) {
// We are not searching for a CNAME but nevertheless we have found one
// at the name we are searching so we return it. (The caller may
// want to continue the lookup by replacing the query name with the
// canonical name and the original RR type.) First though, do a sanity
// check to ensure that there is only one RR in the CNAME RRset.
if (cni->second->getRdataCount() != 1) {
isc_throw(DataSourceError, "CNAME with " <<
cni->second->getRdataCount() << " rdata at " << name <<
", expected 1");
}
return (logAndCreateResult(name, wildname, type,
wild ? WILDCARD_CNAME : CNAME, cni->second,
wild ? DATASRC_DATABASE_WILDCARD_CNAME :
DATASRC_DATABASE_FOUND_CNAME));
} else if (wti != found.second.end()) {
if (type == RRType::ANY()) {
// An ANY query, copy everything to the target instead of returning
// directly.
for (FoundIterator it(found.second.begin());
it != found.second.end(); ++it) {
if (it->second) {
// Skip over the empty ANY
target->push_back(it->second);
}
}
}
// Found an RR matching the query, so return it. (Note that this
// includes the case where we were explicitly querying for a CNAME and
// found it. It also includes the case where we were querying for an
// NS RRset and found it at the apex of the zone.)
return (logAndCreateResult(name, wildname, type,
wild ? WILDCARD : SUCCESS, wti->second,
wild ? DATASRC_DATABASE_WILDCARD_MATCH :
DATASRC_DATABASE_FOUND_RRSET));
}
// If we get here, we have found something at the requested name but not
// one of the RR types we were interested in. This is the NXRRSET case so
// return the appropriate status. If DNSSEC information was requested,
// provide the NSEC records. If it's for wildcard, we need to get the
// NSEC records in the name of the wildcard, not the substituted one,
// so we need to search the tree again.
ConstRRsetPtr nsec_rrset; // possibly used with DNSSEC, otherwise NULL
if ((options & FIND_DNSSEC) != 0) {
if (wild) {
const FoundRRsets wfound = getRRsets(*wildname, NSEC_TYPES(),
true);
const FoundIterator nci = wfound.second.find(RRType::NSEC());
if (nci != wfound.second.end()) {
nsec_rrset = nci->second;
}
} else {
const FoundIterator nci = found.second.find(RRType::NSEC());
if (nci != found.second.end()) {
nsec_rrset = nci->second;
}
}
}
if (nsec_rrset) {
// This log message covers both normal and wildcard cases, so we pass
// NULL for 'wildname'.
return (logAndCreateResult(name, NULL, type,
wild ? WILDCARD_NXRRSET : NXRRSET,
nsec_rrset,
DATASRC_DATABASE_FOUND_NXRRSET_NSEC));
}
return (logAndCreateResult(name, wildname, type,
wild ? WILDCARD_NXRRSET : NXRRSET, nsec_rrset,
wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
DATASRC_DATABASE_FOUND_NXRRSET));
}
ZoneFinder::FindResult
DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
FindOptions options,
const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>*
target)
{
const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
// On entry to this method, we know that the database doesn't have any
// entry for this name. Before returning NXDOMAIN, we need to check
// for special cases.
if (hasSubdomains(name.toText())) {
// Does the domain have a subdomain (i.e. it is an empty non-terminal)?
// If so, return NXRRSET instead of NXDOMAIN (as although the name does
// not exist in the database, it does exist in the DNS tree).
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
arg(accessor_->getDBName()).arg(name);
return (FindResult(NXRRSET, dnssec_data ? findNSECCover(name) :
ConstRRsetPtr()));
} else if ((options & NO_WILDCARD) == 0) {
// It's not an empty non-terminal and wildcard matching is not
// disabled, so check for wildcards. If there is a wildcard match
// (i.e. all results except NXDOMAIN) return it; otherwise fall
// through to the NXDOMAIN case below.
const ZoneFinder::FindResult wresult =
findWildcardMatch(name, type, options, dresult, target);
if (wresult.code != NXDOMAIN) {
return (FindResult(wresult.code, wresult.rrset));
}
}
// All avenues to find a match are now exhausted, return NXDOMAIN (plus
// NSEC records if requested).
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
return (FindResult(NXDOMAIN, dnssec_data ? findNSECCover(name) :
ConstRRsetPtr()));
}
ZoneFinder::FindResult
DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
const isc::dns::RRType& type,
std::vector<isc::dns::ConstRRsetPtr>* target,
const FindOptions options)
{
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
.arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
// First, go through all superdomains from the origin down, searching for
// nodes that indicate a delegation (i.e. NS or DNAME, ignoring NS records
// at the apex). If one is found, the search stops there.
//
// (In fact there could be RRs in the database corresponding to subdomains
// of the delegation. The reason we do the search for the delegations
// first is because the delegation means that another zone is authoritative
// for the data and so should be consulted to retrieve it. RRs below
// this delegation point can be found in a search for glue but not
// otherwise; in the latter case they are said to be occluded by the
// presence of the delegation.)
const DelegationSearchResult dresult = findDelegationPoint(name, options);
if (dresult.rrset) {
return (FindResult(dresult.code, dresult.rrset));
}
// If there is no delegation, look for the exact match to the request
// name/type/class. However, there are special cases:
// - Requested name has a singleton CNAME record associated with it
// - Requested name is a delegation point (NS only but not at the zone
// apex - DNAME is ignored here as it redirects DNS names subordinate to
// the owner name - the owner name itself is not redirected.)
const bool is_origin = (name == getOrigin());
WantedTypes final_types(FINAL_TYPES());
final_types.insert(type);
const FoundRRsets found = getRRsets(name.toText(), final_types,
!is_origin);
if (found.first) {
// Something found at the domain name. Look into it further to get
// the final result.
return (findOnNameResult(name, type, options, is_origin, found, NULL,
target));
} else {
// Did not find anything at all at the domain name, so check for
// subdomains or wildcards.
return (findNoNameResult(name, type, options, dresult, target));
}
return (FindResult(result_status, result_rrset));
}
Name
@@ -722,10 +884,9 @@ DatabaseClient::Finder::findPreviousName(const Name& name) const {
try {
return (Name(str));
}
/*
* To avoid having the same code many times, we just catch all the
* exceptions and handle them in a common code below
*/
// To avoid having the same code many times, we just catch all the
// exceptions and handle them in a common code below
catch (const isc::dns::EmptyLabel&) {}
catch (const isc::dns::TooLongLabel&) {}
catch (const isc::dns::BadLabelType&) {}
@@ -748,14 +909,12 @@ DatabaseClient::Finder::getClass() const {
namespace {
/*
* This needs, beside of converting all data from textual representation, group
* together rdata of the same RRsets. To do this, we hold one row of data ahead
* of iteration. When we get a request to provide data, we create it from this
* data and load a new one. If it is to be put to the same rrset, we add it.
* Otherwise we just return what we have and keep the row as the one ahead
* for next time.
*/
/// This needs, beside of converting all data from textual representation, group
/// together rdata of the same RRsets. To do this, we hold one row of data ahead
/// of iteration. When we get a request to provide data, we create it from this
/// data and load a new one. If it is to be put to the same rrset, we add it.
/// Otherwise we just return what we have and keep the row as the one ahead
/// for next time.
class DatabaseIterator : public ZoneIterator {
public:
DatabaseIterator(shared_ptr<DatabaseAccessor> accessor,

File diff suppressed because it is too large Load Diff

View File

@@ -68,7 +68,7 @@ The datasource tried to provide an NSEC proof that the named domain does not
exist, but the database backend doesn't support DNSSEC. No proof is included
in the answer as a result.
% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3
% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4
Debug information. The database data source is looking up records with the given
name and type in the database.
@@ -78,11 +78,17 @@ different TTL values. This isn't allowed on the wire and is considered
an error, so we set it to the lowest value we found (but we don't modify the
database). The data in database should be checked and fixed.
% DATASRC_DATABASE_FOUND_CNAME search in datasource %1 for %2/%3/%4 found CNAME, resulting in %5
When searching the domain for a name a CNAME was found at that name.
Even though it was not the RR type being sought, it is returned. (The
caller may want to continue the lookup by replacing the query name with
the canonical name and restarting the query with the original RR type.)
% DATASRC_DATABASE_FOUND_DELEGATION Found delegation at %2 in %1
When searching for a domain, the program met a delegation to a different zone
at the given domain name. It will return that one instead.
% DATASRC_DATABASE_FOUND_DELEGATION_EXACT Found delegation at %2 (exact match) in %1
% DATASRC_DATABASE_FOUND_DELEGATION_EXACT search in datasource %1 for %2/%3/%4 found delegation at %5
The program found the domain requested, but it is a delegation point to a
different zone, therefore it is not authoritative for this domain name.
It will return the NS record instead.
@@ -93,19 +99,25 @@ place in the domain space at the given domain name. It will return that one
instead.
% DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL empty non-terminal %2 in %1
The domain name doesn't have any RRs, so it doesn't exist in the database.
However, it has a subdomain, so it exists in the DNS address space. So we
return NXRRSET instead of NXDOMAIN.
The domain name does not have any RRs associated with it, so it doesn't
exist in the database. However, it has a subdomain, so it does exist
in the DNS address space. This type of domain is known an an "empty
non-terminal" and so we return NXRRSET instead of NXDOMAIN.
% DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4
The data returned by the database backend did not contain any data for the given
domain name, class and type.
% DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 resulted in NXRRSET for %2/%3/%4
% DATASRC_DATABASE_FOUND_NXRRSET search in datasource %1 for %2/%3/%4 resulted in NXRRSET
The data returned by the database backend contained data for the given domain
name and class, but not for the given type.
% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2
% DATASRC_DATABASE_FOUND_NXRRSET_NSEC search in datasource %1 for %2/%3/%4 resulted in RRset %5
A search in the database for RRs for the specified name, type and class has
located RRs that match the name and class but not the type. DNSSEC information
has been requested and returned.
% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %5
The data returned by the database backend contained data for the given domain
name, and it either matches the type or has a relevant type. The RRset that is
returned is printed.
@@ -127,11 +139,46 @@ were found to be different. This isn't allowed on the wire and is considered
an error, so we set it to the lowest value we found (but we don't modify the
database). The data in database should be checked and fixed.
% DATASRC_DATABASE_WILDCARD constructing RRset %3 from wildcard %2 in %1
The database doesn't contain directly matching domain, but it does contain a
wildcard one which is being used to synthesize the answer.
% DATASRC_DATABASE_NO_MATCH not match for %2/%3/%4 in %1
No match (not even a wildcard) was found in the named data source for the given
name/type/class in the data source.
% DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %2 because %3 contains NS in %1
% DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3
Debug information. A set of updates to a zone has been successfully
committed to the corresponding database backend. The zone name,
its class and the database name are printed.
% DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3
Debug information. A zone updater object is created to make updates to
the shown zone on the shown backend database.
% DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3
Debug information. A zone updater object is destroyed, either successfully
or after failure of, making updates to the shown zone on the shown backend
database.
% DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3
A zone updater is being destroyed without committing the changes.
This would typically mean the update attempt was aborted due to some
error, but may also be a bug of the application that forgets committing
the changes. The intermediate changes made through the updater won't
be applied to the underlying database. The zone name, its class, and
the underlying database name are shown in the log message.
% DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4
A zone updater is being destroyed without committing the changes to
the database, and attempts to rollback incomplete updates, but it
unexpectedly fails. The higher level implementation does not expect
it to fail, so this means either a serious operational error in the
underlying data source (such as a system failure of a database) or
software bug in the underlying data source implementation. In either
case if this message is logged the administrator should carefully
examine the underlying data source to see what exactly happens and
whether the data is still valid. The zone name, its class, and the
underlying database name as well as the error message thrown from the
database module are shown in the log message.
% DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %3 because %2 contains NS (data source %1)
The database was queried to provide glue data and it didn't find direct match.
It could create it from given wildcard, but matching wildcards is forbidden
under a zone cut, which was found. Therefore the delegation will be returned
@@ -143,11 +190,31 @@ exists, therefore this name is something like empty non-terminal (actually,
from the protocol point of view, it is empty non-terminal, but the code
discovers it differently).
% DATASRC_DATABASE_WILDCARD_EMPTY implicit wildcard %2 used to construct %3 in %1
The given wildcard exists implicitly in the domainspace, as empty nonterminal
(eg. there's something like subdomain.*.example.org, so *.example.org exists
implicitly, but is empty). This will produce NXRRSET, because the constructed
domain is empty as well as the wildcard.
% DATASRC_DATABASE_WILDCARD_CNAME search in datasource %1 for %2/%3/%4 found wildcard CNAME at %5, resulting in %6
The database doesn't contain directly matching name. When searching
for a wildcard match, a CNAME RR was found at a wildcard record
matching the name. This is returned as the result of the search.
% DATASRC_DATABASE_WILDCARD_EMPTY found subdomains of %2 which is a wildcard match for %3 in %1
The given wildcard matches the name being sough but it as an empty
nonterminal (e.g. there's nothing at *.example.com but something like
subdomain.*.example.org, do exist: so *.example.org exists in the
namespace but has no RRs assopciated with it). This will produce NXRRSET.
% DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %5 with RRset %6
The database doesn't contain directly matching name. When searching
for a wildcard match, a wildcard record matching the name and type of
the query was found. The data at this point is returned.
% DATASRC_DATABASE_WILDCARD_NS search in datasource %1 for %2/%3/%4 found wildcard delegation at %5, resulting in %6
The database doesn't contain directly matching name. When searching
for a wildcard match, an NS RR was found at a wildcard record matching
the name. This is returned as the result of the search.
% DATASRC_DATABASE_WILDCARD_NXRRSET search in datasource %1 for %2/%3/%4 resulted in wildcard NXRRSET at %5
The database doesn't contain directly matching name. When searching
for a wildcard match, a matching wildcard entry was found but it did
not contain RRs the requested type. AN NXRRSET indication is returned.
% DATASRC_DO_QUERY handling query for '%1/%2'
A debug message indicating that a query for the given name and RR type is being
@@ -259,7 +326,7 @@ Debug information. The requested record was found.
% DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty
Debug information. The search stopped at a superdomain of the requested
domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET
domain. The domain is an empty nonterminal, therefore it is treated as NXRRSET
case (eg. the domain exists, but it doesn't have the requested record type).
% DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
@@ -487,12 +554,12 @@ enough information for it. The code is 1 for error, 2 for not implemented.
% DATASRC_SQLITE_CLOSE closing SQLite database
Debug information. The SQLite data source is closing the database file.
% DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'
The database file is being opened so it can start providing data.
% DATASRC_SQLITE_CONNCLOSE Closing sqlite database
The database file is no longer needed and is being closed.
% DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'
The database file is being opened so it can start providing data.
% DATASRC_SQLITE_CREATE SQLite data source created
Debug information. An instance of SQLite data source is being created.

View File

@@ -17,6 +17,7 @@ endif
CLEANFILES = *.gcno *.gcda
TESTS =
noinst_PROGRAMS =
if HAVE_GTEST
TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
@@ -84,25 +85,7 @@ run_unittests_memory_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_memory_LDADD = $(common_ldadd)
endif
noinst_PROGRAMS = $(TESTS)
EXTRA_DIST = testdata/brokendb.sqlite3
EXTRA_DIST += testdata/example.com.signed
EXTRA_DIST += testdata/example.org
EXTRA_DIST += testdata/example.org.sqlite3
EXTRA_DIST += testdata/example2.com
EXTRA_DIST += testdata/example2.com.sqlite3
EXTRA_DIST += testdata/mkbrokendb.c
EXTRA_DIST += testdata/root.zone
EXTRA_DIST += testdata/sql1.example.com.signed
EXTRA_DIST += testdata/sql2.example.com.signed
EXTRA_DIST += testdata/test-root.sqlite3
EXTRA_DIST += testdata/test.sqlite3
EXTRA_DIST += testdata/test.sqlite3.nodiffs
EXTRA_DIST += testdata/rwtest.sqlite3
EXTRA_DIST += testdata/diffs.sqlite3
noinst_PROGRAMS+= $(TESTS)
# For the factory unit tests, we need to specify that we want
# the loadable backend libraries from the build tree, and not from
@@ -121,3 +104,21 @@ run_unittests_factory_LDADD = $(common_ldadd)
check-local:
B10_FROM_BUILD=${abs_top_builddir} ./run_unittests_factory
endif
endif
EXTRA_DIST = testdata/brokendb.sqlite3
EXTRA_DIST += testdata/example.com.signed
EXTRA_DIST += testdata/example.org
EXTRA_DIST += testdata/example.org.sqlite3
EXTRA_DIST += testdata/example2.com
EXTRA_DIST += testdata/example2.com.sqlite3
EXTRA_DIST += testdata/mkbrokendb.c
EXTRA_DIST += testdata/root.zone
EXTRA_DIST += testdata/sql1.example.com.signed
EXTRA_DIST += testdata/sql2.example.com.signed
EXTRA_DIST += testdata/test-root.sqlite3
EXTRA_DIST += testdata/test.sqlite3
EXTRA_DIST += testdata/test.sqlite3.nodiffs
EXTRA_DIST += testdata/rwtest.sqlite3
EXTRA_DIST += testdata/diffs.sqlite3

View File

@@ -261,12 +261,15 @@ public:
/// proof of the non existence of any matching wildcard or non existence
/// of an exact match when a wildcard match is found.
///
/// A derived version of this method may involve internal resource
/// allocation, especially for constructing the resulting RRset, and may
/// throw an exception if it fails.
/// It throws DuplicateRRset exception if there are duplicate rrsets under
/// the same domain.
/// It should not throw other types of exceptions.
/// \exception std::bad_alloc Memory allocation such as for constructing
/// the resulting RRset fails
/// \exception DataSourceError Derived class specific exception, e.g.
/// when encountering a bad zone configuration or database connection
/// failure. Although these are considered rare, exceptional events,
/// it can happen under relatively usual conditions (unlike memory
/// allocation failure). So, in general, the application is expected
/// to catch this exception, either specifically or as a result of
/// catching a base exception class, and handle it gracefully.
///
/// \param name The domain name to be searched for.
/// \param type The RR type to be searched for.

View File

@@ -7,21 +7,22 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libdhcp.la
libdhcp_la_SOURCES =
libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
libdhcp_la_SOURCES += option.cc option.h
libdhcp_la_SOURCES += option6_ia.cc option6_ia.h
libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
libdhcp_la_SOURCES += option4_addrlst.cc option4_addrlst.h
libdhcp_la_SOURCES += dhcp6.h dhcp4.h
libdhcp_la_SOURCES += pkt6.cc pkt6.h
libdhcp_la_SOURCES += pkt4.cc pkt4.h
lib_LTLIBRARIES = libdhcp++.la
libdhcp___la_SOURCES =
libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
libdhcp___la_SOURCES += option.cc option.h
libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libdhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
libdhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
libdhcp___la_SOURCES += dhcp6.h dhcp4.h
libdhcp___la_SOURCES += pkt6.cc pkt6.h
libdhcp___la_SOURCES += pkt4.cc pkt4.h
EXTRA_DIST = README
#EXTRA_DIST += log_messages.mes
libdhcp_la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libdhcp_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
libdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libdhcp___la_LIBADD = $(top_builddir)/src/lib/util/libutil.la

View File

@@ -1,4 +1,4 @@
This directory holds implementation for libdhcp.
This directory holds implementation for libdhcp++.
Basic Ideas
@@ -8,4 +8,4 @@ Basic Ideas
Notes
=====
This work just begun. Don't expect to see much useful code here.
We are working on it.
We are working on it.

View File

@@ -19,7 +19,7 @@
#include <arpa/inet.h>
#include <dhcp/dhcp6.h>
#include <dhcp6/iface_mgr.h>
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
using namespace std;
@@ -340,7 +340,8 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
if (addr.toText() != "::1")
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
memcpy(&addr6.sin6_addr,
addr.getAddress().to_v6().to_bytes().data(),

View File

@@ -184,8 +184,7 @@ public:
/// @return interface with requested index (or NULL if no such
/// interface is present)
///
Iface*
getIface(int ifindex);
Iface* getIface(int ifindex);
/// @brief Returns interface with specified interface name
///

View File

@@ -15,7 +15,7 @@
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <util/buffer.h>
#include <dhcp/libdhcp.h>
#include <dhcp/libdhcp++.h>
#include "config.h"
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>

View File

@@ -22,7 +22,7 @@
#include "util/io_utilities.h"
#include "dhcp/option.h"
#include "dhcp/libdhcp.h"
#include "dhcp/libdhcp++.h"
using namespace std;
using namespace isc::dhcp;

View File

@@ -19,7 +19,7 @@
#include "asiolink/io_address.h"
#include "util/io_utilities.h"
#include "dhcp/libdhcp.h"
#include "dhcp/libdhcp++.h"
#include "dhcp/option6_addrlst.h"
#include "dhcp/dhcp6.h"

View File

@@ -17,7 +17,7 @@
#include <sstream>
#include "exceptions/exceptions.h"
#include "dhcp/libdhcp.h"
#include "dhcp/libdhcp++.h"
#include "dhcp/option6_ia.h"
#include "dhcp/dhcp6.h"
#include "util/io_utilities.h"

View File

@@ -17,7 +17,7 @@
#include <sstream>
#include "exceptions/exceptions.h"
#include "dhcp/libdhcp.h"
#include "dhcp/libdhcp++.h"
#include "dhcp/option6_iaaddr.h"
#include "dhcp/dhcp6.h"
#include "asiolink/io_address.h"

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/pkt4.h>
#include <dhcp/libdhcp.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/dhcp4.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>

View File

@@ -302,17 +302,79 @@ public:
boost::shared_ptr<Option>
getOption(uint8_t opt_type);
/// @brief set interface over which packet should be sent
/// @brief Returns interface name.
///
/// @param interface defines outbound interface
void setIface(const std::string& interface){ iface_ = interface; }
/// @brief gets interface over which packet was received or
/// will be transmitted
/// Returns interface name over which packet was received or is
/// going to be transmitted.
///
/// @return name of the interface
std::string getIface() const { return iface_; }
/// @return interface name
std::string getIface() const { return iface_; };
/// @brief Sets interface name.
///
/// Sets interface name over which packet was received or is
/// going to be transmitted.
///
/// @return interface name
void setIface(const std::string& iface ) { iface_ = iface; };
/// @brief Sets interface index.
///
/// @param ifindex specifies interface index.
void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
/// @brief Returns interface index.
///
/// @return interface index
uint32_t getIndex() const { return (ifindex_); };
/// @brief Sets remote address.
///
/// @params remote specifies remote address
void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
remote_addr_ = remote;
}
/// @brief Returns remote address
///
/// @return remote address
const isc::asiolink::IOAddress& getRemoteAddr() {
return (remote_addr_);
}
/// @brief Sets local address.
///
/// @params local specifies local address
void setLocalAddr(const isc::asiolink::IOAddress& local) {
local_addr_ = local;
}
/// @brief Returns local address.
///
/// @return local address
const isc::asiolink::IOAddress& getLocalAddr() {
return (local_addr_);
}
/// @brief Sets local port.
///
/// @params local specifies local port
void setLocalPort(uint16_t local) { local_port_ = local; }
/// @brief Returns local port.
///
/// @return local port
uint16_t getLocalPort() { return (local_port_); }
/// @brief Sets remote port.
///
/// @params remote specifies remote port
void setRemotePort(uint16_t remote) { remote_port_ = remote; }
/// @brief Returns remote port.
///
/// @return remote port
uint16_t getRemotePort() { return (remote_port_); }
protected:
@@ -338,13 +400,13 @@ protected:
/// Each network interface has assigned unique ifindex. It is functional
/// equvalent of name, but sometimes more useful, e.g. when using crazy
/// systems that allow spaces in interface names e.g. MS Windows)
int ifindex_;
uint32_t ifindex_;
/// local UDP port
int local_port_;
uint16_t local_port_;
/// remote UDP port
int remote_port_;
uint16_t remote_port_;
/// @brief message operation code
///

View File

@@ -15,7 +15,7 @@
#include "dhcp/dhcp6.h"
#include "dhcp/pkt6.h"
#include "dhcp/libdhcp.h"
#include "dhcp/libdhcp++.h"
#include "exceptions/exceptions.h"
#include <iostream>
#include <sstream>

View File

@@ -2,6 +2,10 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
@@ -12,31 +16,33 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += libdhcp_unittests
libdhcp_unittests_SOURCES = run_unittests.cc
libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc
libdhcp_unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
libdhcp_unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
libdhcp_unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
TESTS += libdhcp++_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libdhcp_unittests_CXXFLAGS = $(AM_CXXFLAGS)
libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# This is to workaround unused variables tcout and tcerr in
# log4cplus's streams.h.
libdhcp_unittests_CXXFLAGS += -Wno-unused-variable
libdhcp___unittests_CXXFLAGS += -Wno-unused-variable
endif
libdhcp_unittests_LDADD = $(GTEST_LDADD)
libdhcp_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
libdhcp_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
libdhcp_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
libdhcp___unittests_LDADD = $(GTEST_LDADD)
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -22,7 +22,7 @@
#include <asiolink/io_address.h>
#include <dhcp/pkt6.h>
#include <dhcp6/iface_mgr.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/dhcp4.h>
using namespace std;
@@ -31,7 +31,8 @@ using namespace isc::asiolink;
using namespace isc::dhcp;
// name of loopback interface detection
char LOOPBACK[32] = "lo";
const size_t buf_size = 32;
char LOOPBACK[buf_size] = "lo";
namespace {
const char* const INTERFACE_FILE = TEST_DATA_BUILDDIR "/interfaces.txt";
@@ -69,16 +70,16 @@ TEST_F(IfaceMgrTest, loDetect) {
// poor man's interface detection
// it will go away as soon as proper interface detection
// is implemented
if (if_nametoindex("lo")>0) {
if (if_nametoindex("lo") > 0) {
cout << "This is Linux, using lo as loopback." << endl;
sprintf(LOOPBACK, "lo");
} else if (if_nametoindex("lo0")>0) {
snprintf(LOOPBACK, buf_size - 1, "lo");
} else if (if_nametoindex("lo0") > 0) {
cout << "This is BSD, using lo0 as loopback." << endl;
sprintf(LOOPBACK, "lo0");
snprintf(LOOPBACK, buf_size - 1, "lo0");
} else {
cout << "Failed to detect loopback interface. Neither "
<< "lo or lo0 worked. I give up." << endl;
ASSERT_TRUE(false);
<< "lo nor lo0 worked. I give up." << endl;
FAIL();
}
}
@@ -100,9 +101,9 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
interfaces.close();
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
Pkt6 * pkt = 0;
Pkt6* pkt = NULL;
int cnt = 0;
cout << "---8X-----------------------------------------" << endl;
while (true) {
@@ -154,7 +155,7 @@ TEST_F(IfaceMgrTest, basic) {
TEST_F(IfaceMgrTest, ifaceClass) {
// basic tests for Iface inner class
IfaceMgr::Iface * iface = new IfaceMgr::Iface("eth5", 7);
IfaceMgr::Iface* iface = new IfaceMgr::Iface("eth5", 7);
EXPECT_STREQ("eth5/7", iface->getFullName().c_str());
@@ -167,7 +168,7 @@ TEST_F(IfaceMgrTest, ifaceClass) {
TEST_F(IfaceMgrTest, getIface) {
cout << "Interface checks. Please ignore socket binding errors." << endl;
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
// interface name, ifindex
IfaceMgr::Iface iface1("lo1", 1);
@@ -191,7 +192,7 @@ TEST_F(IfaceMgrTest, getIface) {
// check that interface can be retrieved by ifindex
IfaceMgr::Iface * tmp = ifacemgr->getIface(5);
IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
// ASSERT_NE(NULL, tmp); is not supported. hmmmm.
ASSERT_TRUE( tmp != NULL );
@@ -224,11 +225,11 @@ TEST_F(IfaceMgrTest, detectIfaces) {
// interfaces. Nevertheless, this fake interface should
// be on list, but if_nametoindex() will fail.
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
IfaceMgr::Iface * eth0 = ifacemgr->getIface("eth0");
IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
// there should be one address
IfaceMgr::AddressCollection addrs = eth0->getAddresses();
@@ -239,6 +240,7 @@ TEST_F(IfaceMgrTest, detectIfaces) {
EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
delete ifacemgr;
unlink(INTERFACE_FILE);
}
TEST_F(IfaceMgrTest, sockets6) {
@@ -247,7 +249,7 @@ TEST_F(IfaceMgrTest, sockets6) {
createLoInterfacesTxt();
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
@@ -271,6 +273,7 @@ TEST_F(IfaceMgrTest, sockets6) {
close(socket2);
delete ifacemgr;
unlink(INTERFACE_FILE);
}
// TODO: disabled due to other naming on various systems
@@ -279,7 +282,7 @@ TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
IOAddress mcastAddr("ff02::1:2");
@@ -320,6 +323,9 @@ TEST_F(IfaceMgrTest, sendReceive6) {
socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
);
EXPECT_GT(socket1, 0);
EXPECT_GT(socket2, 0);
boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
// prepare dummy payload
@@ -354,6 +360,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
delete ifacemgr;
unlink(INTERFACE_FILE);
}
TEST_F(IfaceMgrTest, socket4) {
@@ -381,11 +388,12 @@ TEST_F(IfaceMgrTest, socket4) {
close(socket1);
delete ifacemgr;
unlink(INTERFACE_FILE);
}
// Test the Iface structure itself
TEST_F(IfaceMgrTest, iface) {
IfaceMgr::Iface* iface = 0;
IfaceMgr::Iface* iface = NULL;
EXPECT_NO_THROW(
iface = new IfaceMgr::Iface("eth0",1);
);
@@ -442,7 +450,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
// now let's test if IfaceMgr handles socket info properly
createLoInterfacesTxt();
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
ASSERT_TRUE(loopback);
loopback->addSocket(sock1);
@@ -510,6 +518,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
);
delete ifacemgr;
unlink(INTERFACE_FILE);
}
}

View File

@@ -18,7 +18,7 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <util/buffer.h>
#include <dhcp/libdhcp.h>
#include <dhcp/libdhcp++.h>
#include "config.h"
using namespace std;

View File

@@ -506,7 +506,7 @@ TEST(Pkt4Test, unpackOptions) {
vector<uint8_t> expectedFormat = generateTestPacket2();
for (int i=0; i < sizeof(v4Opts); i++) {
for (int i = 0; i < sizeof(v4Opts); i++) {
expectedFormat.push_back(v4Opts[i]);
}
@@ -563,15 +563,19 @@ TEST(Pkt4Test, unpackOptions) {
// This test verifies methods that are used for manipulating meta fields
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
TEST(Pkt4Ttest, metaFields) {
Pkt4 pkt(DHCPDISCOVER, 1234);
TEST(Pkt4Test, metaFields) {
pkt.setIface("lo0");
Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
pkt->setIface("loooopback");
pkt->setIndex(42);
pkt->setRemoteAddr(IOAddress("1.2.3.4"));
pkt->setLocalAddr(IOAddress("4.3.2.1"));
EXPECT_EQ("lo0", pkt.getIface());
EXPECT_EQ("loooopback", pkt->getIface());
EXPECT_EQ(42, pkt->getIndex());
EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
/// TODO: Expand this test once additonal getters/setters are
/// implemented.
}
} // end of anonymous namespace

View File

@@ -633,12 +633,15 @@ private:
case ResponseClassifier::NOTSINGLE:
message_id = RESLIB_NOTSINGLE_RESPONSE;
break;
case ResponseClassifier::OPCODE:
message_id = RESLIB_OPCODE_RESPONSE;
break;
default:
message_id = RESLIB_ERROR_RESPONSE;
break;
}
LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
arg(questionText(question_));

View File

@@ -0,0 +1,24 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/statistics -I$(top_builddir)/src/lib/statistics
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
if USE_CLANGPP
# clang++ complains about unused function parameters in some boost header
# files.
AM_CXXFLAGS += -Wno-unused-parameter
endif
lib_LTLIBRARIES = libstatistics.la
libstatistics_la_SOURCES = counter.h counter.cc
libstatistics_la_SOURCES += counter_dict.h counter_dict.cc
CLEANFILES = *.gcno *.gcda

View File

@@ -0,0 +1,68 @@
#include <vector>
#include <boost/noncopyable.hpp>
#include <statistics/counter.h>
namespace {
const unsigned int InitialValue = 0;
} // namespace
namespace isc {
namespace statistics {
class CounterImpl : boost::noncopyable {
private:
std::vector<Counter::Value> counters_;
public:
CounterImpl(const size_t nelements);
~CounterImpl();
void inc(const Counter::Type&);
const Counter::Value& get(const Counter::Type&) const;
};
CounterImpl::CounterImpl(const size_t items) :
counters_(items, InitialValue)
{
if (items == 0) {
isc_throw(isc::InvalidParameter, "Items must not be 0");
}
}
CounterImpl::~CounterImpl() {}
void
CounterImpl::inc(const Counter::Type& type) {
if(type >= counters_.size()) {
isc_throw(isc::OutOfRange, "Counter type is out of range");
}
++counters_.at(type);
return;
}
const Counter::Value&
CounterImpl::get(const Counter::Type& type) const {
if(type >= counters_.size()) {
isc_throw(isc::OutOfRange, "Counter type is out of range");
}
return (counters_.at(type));
}
Counter::Counter(const size_t items) : impl_(new CounterImpl(items))
{}
Counter::~Counter() {}
void
Counter::inc(const Type& type) {
impl_->inc(type);
return;
}
const Counter::Value&
Counter::get(const Type& type) const {
return (impl_->get(type));
}
} // namespace statistics
} // namespace isc

View File

@@ -0,0 +1,55 @@
#ifndef __COUNTER_H
#define __COUNTER_H 1
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <exceptions/exceptions.h>
namespace isc {
namespace statistics {
// forward declaration for pImpl idiom
class CounterImpl;
class Counter : boost::noncopyable {
private:
boost::scoped_ptr<CounterImpl> impl_;
public:
typedef unsigned int Type;
typedef unsigned int Value;
/// The constructor.
///
/// This constructor is mostly exception free. But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
/// \param items A number of counter items to hold (greater than 0)
///
/// \throw isc::InvalidParameter \a items is 0
Counter(const size_t items);
/// The destructor.
///
/// This method never throws an exception.
~Counter();
/// \brief Increment a counter item specified with \a type.
///
/// \param type %Counter item to increment
///
/// \throw isc::OutOfRange \a type is invalid
void inc(const Type& type);
/// \brief Get the value of a counter item specified with \a type.
///
/// \param type %Counter item to get the value of
///
/// \throw isc::OutOfRange \a type is invalid
const Value& get(const Type& type) const;
};
} // namespace statistics
} // namespace isc
#endif

View File

@@ -0,0 +1,251 @@
#include <cassert>
#include <stdexcept>
#include <iterator>
#include <map>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <statistics/counter_dict.h>
namespace {
typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
typedef std::map<std::string, CounterPtr> DictionaryMap;
}
namespace isc {
namespace statistics {
// Implementation detail class for CounterDictionary::ConstIterator
class CounterDictionaryConstIteratorImpl;
class CounterDictionaryImpl : boost::noncopyable {
private:
DictionaryMap dictionary_;
std::vector<std::string> elements_;
const size_t items_;
// Default constructor is forbidden; number of counter items must be
// specified at the construction of this class.
CounterDictionaryImpl();
public:
CounterDictionaryImpl(const size_t items);
~CounterDictionaryImpl();
void addElement(const std::string& name);
void deleteElement(const std::string& name);
Counter& getElement(const std::string& name);
public:
CounterDictionaryConstIteratorImpl begin() const;
CounterDictionaryConstIteratorImpl end() const;
};
// Constructor with number of items
CounterDictionaryImpl::CounterDictionaryImpl(const size_t items) :
items_(items)
{
// The number of items must not be 0
if (items == 0) {
isc_throw(isc::InvalidParameter, "Items must not be 0");
}
}
// Destructor
CounterDictionaryImpl::~CounterDictionaryImpl() {}
void
CounterDictionaryImpl::addElement(const std::string& name) {
// throw if the element already exists
if (dictionary_.count(name) != 0) {
isc_throw(isc::InvalidParameter,
"Element " << name << " already exists");
}
assert(items_ != 0);
// Create a new Counter and add to the map
dictionary_.insert(
DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
}
void
CounterDictionaryImpl::deleteElement(const std::string& name) {
size_t result = dictionary_.erase(name);
if (result != 1) {
// If an element with specified name does not exist, throw
// isc::OutOfRange.
isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
}
}
Counter&
CounterDictionaryImpl::getElement(const std::string& name) {
DictionaryMap::const_iterator i = dictionary_.find(name);
if (i != dictionary_.end()) {
// the key was found. return the element.
return (*(i->second));
} else {
// If an element with specified name does not exist, throw
// isc::OutOfRange.
isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
}
}
// Constructor
// Initialize impl_
CounterDictionary::CounterDictionary(const size_t items) :
impl_(new CounterDictionaryImpl(items))
{}
// Destructor
// impl_ will be freed automatically with scoped_ptr
CounterDictionary::~CounterDictionary() {}
void
CounterDictionary::addElement(const std::string& name) {
impl_->addElement(name);
}
void
CounterDictionary::deleteElement(const std::string& name) {
impl_->deleteElement(name);
}
Counter&
CounterDictionary::getElement(const std::string& name) const {
return (impl_->getElement(name));
}
Counter&
CounterDictionary::operator[](const std::string& name) const {
return (impl_->getElement(name));
}
// Implementation detail class for CounterDictionary::ConstIterator
class CounterDictionaryConstIteratorImpl {
public:
CounterDictionaryConstIteratorImpl();
~CounterDictionaryConstIteratorImpl();
CounterDictionaryConstIteratorImpl(
const CounterDictionaryConstIteratorImpl &other);
CounterDictionaryConstIteratorImpl &operator=(
const CounterDictionaryConstIteratorImpl &source);
CounterDictionaryConstIteratorImpl(
DictionaryMap::const_iterator iterator);
public:
void increment();
const CounterDictionary::ConstIterator::value_type&
dereference() const;
bool equal(const CounterDictionaryConstIteratorImpl& other) const;
private:
DictionaryMap::const_iterator iterator_;
};
CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl() {}
CounterDictionaryConstIteratorImpl::~CounterDictionaryConstIteratorImpl() {}
// Copy constructor: deep copy of iterator_
CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
const CounterDictionaryConstIteratorImpl &other) :
iterator_(other.iterator_)
{}
// Assignment operator: deep copy of iterator_
CounterDictionaryConstIteratorImpl &
CounterDictionaryConstIteratorImpl::operator=(
const CounterDictionaryConstIteratorImpl &source)
{
iterator_ = source.iterator_;
return (*this);
}
// Constructor from implementation detail DictionaryMap::const_iterator
CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
DictionaryMap::const_iterator iterator) :
iterator_(iterator)
{}
CounterDictionaryConstIteratorImpl
CounterDictionaryImpl::begin() const {
return (CounterDictionaryConstIteratorImpl(dictionary_.begin()));
}
CounterDictionaryConstIteratorImpl
CounterDictionaryImpl::end() const {
return (CounterDictionaryConstIteratorImpl(dictionary_.end()));
}
void
CounterDictionaryConstIteratorImpl::increment() {
++iterator_;
return;
}
const CounterDictionary::ConstIterator::value_type&
CounterDictionaryConstIteratorImpl::dereference() const {
return (iterator_->first);
}
bool
CounterDictionaryConstIteratorImpl::equal(
const CounterDictionaryConstIteratorImpl& other) const
{
return (iterator_ == other.iterator_);
}
CounterDictionary::ConstIterator
CounterDictionary::begin() const {
return (CounterDictionary::ConstIterator(
CounterDictionaryConstIteratorImpl(impl_->begin())));
}
CounterDictionary::ConstIterator
CounterDictionary::end() const {
return (CounterDictionary::ConstIterator(
CounterDictionaryConstIteratorImpl(impl_->end())));
}
CounterDictionary::ConstIterator::ConstIterator() :
impl_(new CounterDictionaryConstIteratorImpl())
{}
CounterDictionary::ConstIterator::~ConstIterator() {}
// Copy constructor: deep copy of impl_
CounterDictionary::ConstIterator::ConstIterator(
const CounterDictionary::ConstIterator& source) :
impl_(new CounterDictionaryConstIteratorImpl(*(source.impl_)))
{}
// Assignment operator: deep copy of impl_
CounterDictionary::ConstIterator &
CounterDictionary::ConstIterator::operator=(
const CounterDictionary::ConstIterator &source)
{
*impl_ = *source.impl_;
return (*this);
}
// The constructor from implementation detail
CounterDictionary::ConstIterator::ConstIterator(
const CounterDictionaryConstIteratorImpl& source) :
impl_(new CounterDictionaryConstIteratorImpl(source))
{}
const CounterDictionary::ConstIterator::value_type&
CounterDictionary::ConstIterator::dereference() const
{
return (impl_->dereference());
}
bool
CounterDictionary::ConstIterator::equal(
CounterDictionary::ConstIterator const& other) const
{
return (impl_->equal(*(other.impl_)));
}
void
CounterDictionary::ConstIterator::increment() {
impl_->increment();
return;
}
} // namespace statistics
} // namespace isc

View File

@@ -0,0 +1,145 @@
#ifndef __COUNTER_DICT_H
#define __COUNTER_DICT_H 1
#include <string>
#include <vector>
#include <utility>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <exceptions/exceptions.h>
#include <statistics/counter.h>
namespace isc {
namespace statistics {
class CounterDictionaryImpl;
class CounterDictionaryConstIteratorImpl;
class CounterDictionary : boost::noncopyable {
private:
boost::scoped_ptr<CounterDictionaryImpl> impl_;
// Default constructor is forbidden; number of counter items must be
// specified at the construction of this class.
CounterDictionary();
public:
/// The constructor.
/// This constructor is mostly exception free. But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
/// \param items A number of counter items to hold (greater than 0)
///
/// \throw isc::InvalidParameter \a items is 0
CounterDictionary(const size_t items);
/// The destructor.
///
/// This method never throws an exception.
~CounterDictionary();
/// \brief Add an element
///
/// \throw isc::InvalidParameter \a element already exists.
///
/// \param name A name of the element to append
void addElement(const std::string& name);
/// \brief Delete
///
/// \throw isc::OutOfRange \a element does not exist.
///
/// \param name A name of the element to delete
void deleteElement(const std::string& name);
/// \brief Lookup
///
/// \throw isc::OutOfRange \a element does not exist.
///
/// \param name A name of the element to get the counters
Counter& getElement(const std::string &name) const;
/// Same as getElement()
Counter& operator[](const std::string &name) const;
/// \brief \c ConstIterator is a constant iterator that provides an
/// interface for enumerating name of zones stored in CounterDictionary.
///
/// This class is derived from boost::iterator_facade and uses pImpl
/// idiom not to expose implementation detail of
/// CounterDictionary::iterator.
///
/// It is intended to walk through the elements when sending the
/// counters to statistics module.
class ConstIterator :
public boost::iterator_facade<ConstIterator,
const std::string,
boost::forward_traversal_tag>
{
private:
boost::scoped_ptr<CounterDictionaryConstIteratorImpl> impl_;
public:
/// The constructor.
///
/// This constructor is mostly exception free. But it may still
/// throw a standard exception if memory allocation fails
/// inside the method.
ConstIterator();
/// The destructor.
///
/// This method never throws an exception.
~ConstIterator();
/// The assignment operator.
///
/// This method is mostly exception free. But it may still
/// throw a standard exception if memory allocation fails
/// inside the method.
ConstIterator& operator=(const ConstIterator &source);
/// The copy constructor.
///
/// This constructor is mostly exception free. But it may still
/// throw a standard exception if memory allocation fails
/// inside the method.
ConstIterator(const ConstIterator& source);
/// The constructor from implementation detail.
///
/// This method is used to create an instance of ConstIterator
/// by CounterDict::begin() and CounterDict::end().
///
/// This constructor is mostly exception free. But it may still
/// throw a standard exception if memory allocation fails
/// inside the method.
ConstIterator(
const CounterDictionaryConstIteratorImpl& source);
private:
/// \brief An internal method to increment this iterator.
void increment();
/// \brief An internal method to check equality.
bool equal(const ConstIterator& other) const;
/// \brief An internal method to dereference this iterator.
const value_type& dereference() const;
private:
friend class boost::iterator_core_access;
};
typedef ConstIterator const_iterator;
/// \brief Return an iterator corresponding to the beginning of the
/// elements stored in CounterDictionary.
///
/// This method is mostly exception free. But it may still throw a
/// standard exception if memory allocation fails inside the method.
const_iterator begin() const;
/// \brief Return an iterator corresponding to the end of the elements
/// stored in CounterDictionary.
///
/// This method is mostly exception free. But it may still throw a
/// standard exception if memory allocation fails inside the method.
const_iterator end() const;
};
} // namespace statistics
} // namespace isc
#endif

View File

@@ -0,0 +1,47 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += counter_unittest.cc
run_unittests_SOURCES += counter_dict_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.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

@@ -0,0 +1,174 @@
// 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 <config.h>
#include <gtest/gtest.h>
#include <set>
#include <boost/foreach.hpp>
#include <statistics/counter_dict.h>
enum CounterItems {
ITEM1 = 0,
ITEM2 = 1,
ITEM3 = 2,
NUMBER_OF_ITEMS = 3
};
using namespace isc::statistics;
TEST(CounterDictionaryCreateTest, invalidCounterSize) {
// Creating counter with 0 elements will cause an isc::InvalidParameter
// exception
EXPECT_THROW(CounterDictionary counters(0), isc::InvalidParameter);
}
// This fixture is for testing CounterDictionary.
class CounterDictionaryTest : public ::testing::Test {
protected:
CounterDictionaryTest() : counters(NUMBER_OF_ITEMS) {
counters.addElement("test");
counters.addElement("sub.test");
}
~CounterDictionaryTest() {}
CounterDictionary counters;
};
TEST_F(CounterDictionaryTest, initializeCheck) {
// Check if the all counters are initialized with 0
EXPECT_EQ(counters["test"].get(ITEM1), 0);
EXPECT_EQ(counters["test"].get(ITEM2), 0);
EXPECT_EQ(counters["test"].get(ITEM3), 0);
}
TEST_F(CounterDictionaryTest, getElement) {
// Another member function to get counters for the element
EXPECT_EQ(counters.getElement("test").get(ITEM1), 0);
EXPECT_EQ(counters.getElement("test").get(ITEM2), 0);
EXPECT_EQ(counters.getElement("test").get(ITEM3), 0);
}
TEST_F(CounterDictionaryTest, incrementCounterItem) {
// Increment counters
counters["test"].inc(ITEM1);
counters["test"].inc(ITEM2);
counters["test"].inc(ITEM2);
counters["test"].inc(ITEM3);
counters["test"].inc(ITEM3);
counters["test"].inc(ITEM3);
// Check if the counters have expected values
EXPECT_EQ(counters["test"].get(ITEM1), 1);
EXPECT_EQ(counters["test"].get(ITEM2), 2);
EXPECT_EQ(counters["test"].get(ITEM3), 3);
EXPECT_EQ(counters["sub.test"].get(ITEM1), 0);
EXPECT_EQ(counters["sub.test"].get(ITEM2), 0);
EXPECT_EQ(counters["sub.test"].get(ITEM3), 0);
}
TEST_F(CounterDictionaryTest, deleteElement) {
// Ensure the element is accessible
EXPECT_EQ(counters["test"].get(ITEM1), 0);
EXPECT_EQ(counters["test"].get(ITEM2), 0);
EXPECT_EQ(counters["test"].get(ITEM3), 0);
// Delete the element
counters.deleteElement("test");
// Accessing to the deleted element will cause an isc::OutOfRange exception
EXPECT_THROW(counters["test"].get(ITEM1), isc::OutOfRange);
// Deleting an element which does not exist will cause an isc::OutOfRange
// exception
EXPECT_THROW(counters.deleteElement("test"), isc::OutOfRange);
}
TEST_F(CounterDictionaryTest, invalidCounterItem) {
// Incrementing out-of-bound counter will cause an isc::OutOfRange
// exception
EXPECT_THROW(counters["test"].inc(NUMBER_OF_ITEMS), isc::OutOfRange);
}
TEST_F(CounterDictionaryTest, uniquenessCheck) {
// Adding an element which already exists will cause an isc::OutOfRange
// exception
EXPECT_THROW(counters.addElement("test"), isc::InvalidParameter);
}
TEST_F(CounterDictionaryTest, iteratorTest) {
// Increment counters
counters["test"].inc(ITEM1);
counters["sub.test"].inc(ITEM2);
counters["sub.test"].inc(ITEM2);
// boolean values to check all of the elements can be accessed through
// the iterator
bool element_test_visited = false;
bool element_sub_test_visited = false;
// Walk through the elements with iterator
// Check if the elements "test" and "sub.test" appears only once
// and the counters have expected value
for (CounterDictionary::ConstIterator i = counters.begin(),
e = counters.end();
i != e;
++i
)
{
const std::string& zone = *i;
if (zone == "test" && element_test_visited == false) {
element_test_visited = true;
// Check if the counters have expected value
EXPECT_EQ(counters[zone].get(ITEM1), 1);
EXPECT_EQ(counters[zone].get(ITEM2), 0);
} else if (zone == "sub.test" &&
element_sub_test_visited == false) {
element_sub_test_visited = true;
// Check if the counters have expected value
EXPECT_EQ(counters[zone].get(ITEM1), 0);
EXPECT_EQ(counters[zone].get(ITEM2), 2);
} else {
// Test fails when reaches here: the element is not expected or
// the element appeared twice
FAIL() << "Unexpected iterator value";
}
}
// Check if the "test" and "sub.test" is accessible
EXPECT_TRUE(element_test_visited);
EXPECT_TRUE(element_sub_test_visited);
}
TEST_F(CounterDictionaryTest, iteratorCopyTest) {
// Increment counters
counters["test"].inc(ITEM1);
counters["sub.test"].inc(ITEM2);
counters["sub.test"].inc(ITEM2);
CounterDictionary::ConstIterator i1 = counters.begin();
CounterDictionary::ConstIterator i2(i1);
CounterDictionary::ConstIterator i3;
i3 = i1;
EXPECT_TRUE(i1 == i2);
EXPECT_TRUE(i1 == i3);
EXPECT_TRUE(i2 == i3);
++i2;
EXPECT_TRUE(i1 != i2);
EXPECT_TRUE(i1 == i3);
EXPECT_TRUE(i2 != i3);
++i3;
EXPECT_TRUE(i1 != i2);
EXPECT_TRUE(i1 != i3);
EXPECT_TRUE(i2 == i3);
}

View File

@@ -0,0 +1,85 @@
// 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 <config.h>
#include <gtest/gtest.h>
#include <statistics/counter.h>
namespace {
enum CounterItems {
ITEM1 = 0,
ITEM2 = 1,
ITEM3 = 2,
NUMBER_OF_ITEMS = 3
};
}
using namespace isc::statistics;
TEST(CounterCreateTest, invalidCounterSize) {
// Creating counter with 0 elements will cause an isc::InvalidParameter
// exception
EXPECT_THROW(Counter counter(0), isc::InvalidParameter);
}
// This fixture is for testing Counter.
class CounterTest : public ::testing::Test {
protected:
CounterTest() : counter(NUMBER_OF_ITEMS) {}
~CounterTest() {}
Counter counter;
};
TEST_F(CounterTest, createCounter) {
// Check if the all counters are initialized with 0
EXPECT_EQ(counter.get(ITEM1), 0);
EXPECT_EQ(counter.get(ITEM2), 0);
EXPECT_EQ(counter.get(ITEM3), 0);
}
TEST_F(CounterTest, incrementCounterItem) {
// Increment counters
counter.inc(ITEM1);
counter.inc(ITEM2);
counter.inc(ITEM2);
counter.inc(ITEM3);
counter.inc(ITEM3);
counter.inc(ITEM3);
// Check if the counters have expected values
EXPECT_EQ(counter.get(ITEM1), 1);
EXPECT_EQ(counter.get(ITEM2), 2);
EXPECT_EQ(counter.get(ITEM3), 3);
// Increment counters once more
counter.inc(ITEM1);
counter.inc(ITEM2);
counter.inc(ITEM2);
counter.inc(ITEM3);
counter.inc(ITEM3);
counter.inc(ITEM3);
// Check if the counters have expected values
EXPECT_EQ(counter.get(ITEM1), 2);
EXPECT_EQ(counter.get(ITEM2), 4);
EXPECT_EQ(counter.get(ITEM3), 6);
}
TEST_F(CounterTest, invalidCounterItem) {
// Incrementing out-of-bound counter will cause an isc::OutOfRange
// exception
EXPECT_THROW(counter.inc(NUMBER_OF_ITEMS), isc::OutOfRange);
// Trying to get out-of-bound counter will cause an isc::OutOfRange
// exception
EXPECT_THROW(counter.get(NUMBER_OF_ITEMS), isc::OutOfRange);
}

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <util/unittests/run_all.h>
#include <log/logger_support.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::initLogger();
return (isc::util::unittests::run_all());
}

View File

@@ -50,7 +50,7 @@ will need to expand these, but we will look at them shortly.
This file defines a feature, just under the feature name we can
provide a description of the feature.
The one scenario we have no has no steps, so if we run it we should
The one scenario we have has no steps, so if we run it we should
see something like:
-- output
@@ -84,7 +84,7 @@ So let's add a step that starts bind10.
When I start bind10 with configuration example.org.config
--
This is not good enough; it will fire of the process, but setting up
This is not good enough; it will start the process, but setting up
b10-auth may take a few moments, so we need to add a step to wait for
it to be started before we continue.
@@ -121,7 +121,7 @@ regular expression itself), so if the step is defined with "do foo bar", the
scenario can add words for readability "When I do foo bar".
Each captured group will be passed as an argument to the function we define.
For bind10, i defined a configuration file, a cmdctl port, and a process
For bind10, I defined a configuration file, a cmdctl port, and a process
name. The first two should be self-evident, and the process name is an
optional name we give it, should we want to address it in the rest of the
tests. This is most useful if we want to start multiple instances. In the

View File

@@ -0,0 +1 @@
{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.slite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]}}

Binary file not shown.

View File

@@ -0,0 +1,195 @@
Feature: IXFR out
Tests for IXFR-out, specific for BIND 10 behaviour.
These are (part of) the tests as described on
http://bind10.isc.org/wiki/IxfrSystemTests
# A lot of these tests test specific UDP behaviour.
#
# Where possible, we use the TCP equivalent. Some of the behaviour
# tested is UDP-specific though. In either case, a comment above
# the test shows how and why it differs from the test specification,
# or why it is commented out for now.
# When we do implement UDP IXFR, we should probably keep the TCP
# tests, and add them to the test specification, so we still have a
# 1-to-1 mapping between these tests and the specification document.
#
# These tests use a zone with just a few records, the first serial
# is 2, and it is incremented in steps of 2, up to serial 22.
# Each updates either deletes or adds the www.example.com A record.
# Version 2 has the record, then the update to version 4 deletes it,
# the update to 6 adds it again, and so on, until version 22 (where
# the last update has added it again)
#
# Some of the tests (scenario 1 tests 3 and 4, and scenario 2 tests 1 and
# 2 may still not work if we replicate BIND 9's behaviour; it always
# responds to UDP IXFR requests with just the SOA, and it does not do
# AXFR-style IXFR if the number of changes exceeds the size of the zone)
#
# So in effect, there is only one test that is currently active (scenario
# 1 test 7)
Scenario: Test Set 1
Given I have bind10 running with configuration ixfr-out/testset1-config.db
Then wait for bind10 xfrout to start
The SOA serial for example.com should be 22
#
# Test 1
#
# We don't support UDP yet, and for TCP we currently return full zone,
# so this test is currently skipped
#
#When I do an IXFR transfer of example.com 123 over udp
#The transfer result should have 1 RRs
#The full result of the last transfer should be
#"""
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#"""
#
# Test 2
#
# Original test specification was for UDP, using TCP for now
#
#When I do an IXFR transfer of example.com 22 over udp
When I do an IXFR transfer of example.com 22 over tcp
The transfer result should have 1 RRs
The full result of the last transfer should be
"""
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
"""
#
# Test 3
#
# Original test specification was for UDP, using TCP for now
#
#When I do an IXFR transfer of example.com 20 over udp
When I do an IXFR transfer of example.com 20 over tcp
The transfer result should have 5 RRs
The full result of the last transfer should be
"""
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
"""
#
# Test 4
#
# Original test specification was for UDP, using TCP for now
#
#When I do an IXFR transfer of example.com 18 over udp
When I do an IXFR transfer of example.com 18 over tcp
The transfer result should have 8 RRs
The full result of the last transfer should be
"""
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
"""
#
# Test 5
#
# This test does not have a TCP equivalent, so it is skipped.
#
#When I do an IXFR transfer of example.com 2 over udp
#The transfer result should have 1 RRs
#The full result of the last transfer should be
#"""
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#"""
#
# Test 6
#
# This test does not have a TCP equivalent, so it is skipped.
#
#When I do an IXFR transfer of example.com 5 over udp
#The transfer result should have 1 RRs
#The full result of the last transfer should be
#"""
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#"""
#
# Test 7
#
When I do an IXFR transfer of example.com 14 over tcp
The transfer result should have 14 RRs
The full result of the last transfer should be
"""
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 14 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 16 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 16 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 18 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 20 28800 7200 604800 18000
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
"""
Scenario: Test Set 2
Given I have bind10 running with configuration ixfr-out/testset1-config.db
Then wait for bind10 xfrout to start
The SOA serial for example.com should be 22
#
# Test 1
#
# Original test specification was for UDP, using TCP for now
#
#When I do an IXFR transfer of example.com 19 over udp
When I do an IXFR transfer of example.com 19 over tcp
The transfer result should have 5 RRs
The full result of the last transfer should be
"""
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
example.com. 3600 IN NS ns.example.com.
ns.example.com. 3600 IN A 192.0.2.1
www.example.com. 3600 IN A 192.0.2.1
example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
"""
#
# Test 2
#
# This test has no TCP equivalent
#
#When I do an IXFR transfer of example.com 6 over udp
#The transfer result should have 5 RRs
#The full result of the last transfer should be
#"""
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#example.com. 3600 IN NS ns.example.com.
#ns.example.com. 3600 IN A 192.0.2.1
#www.example.com. 3600 IN A 192.0.2.1
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#"""
#
# Test 3
#
# This test has no TCP equivalent
#
#When I do an IXFR transfer of example.com 2 over udp
#The transfer result should have 1 RRs
#The full result of the last transfer should be
#"""
#example.com. 3600 IN SOA ns.example.com. admin.example.com. 22 28800 7200 604800 18000
#"""

View File

@@ -79,6 +79,20 @@ def wait_for_auth(step, process_name):
world.processes.wait_for_stderr_str(process_name, ['AUTH_SERVER_STARTED'],
False)
@step('wait for bind10 xfrout (?:of (\w+) )?to start')
def wait_for_xfrout(step, process_name):
"""Wait for b10-xfrout to run. This is done by blocking until the message
XFROUT_NEW_CONFIG_DONE is logged.
Parameters:
process_name ('of <name', optional): The name of the BIND 10 instance
to wait for. Defaults to 'bind10'.
"""
if process_name is None:
process_name = "bind10"
world.processes.wait_for_stderr_str(process_name,
['XFROUT_NEW_CONFIG_DONE'],
False)
@step('have bind10 running(?: with configuration ([\S]+))?' +\
'(?: with cmdctl port (\d+))?' +\
'(?: as ([\S]+))?')

View File

@@ -179,7 +179,7 @@ class QueryResult(object):
"""
pass
@step('A query for ([\w.]+) (?:type ([A-Z]+) )?(?:class ([A-Z]+) )?' +
@step('A query for ([\w.]+) (?:type ([A-Z0-9]+) )?(?:class ([A-Z]+) )?' +
'(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
def query(step, query_name, qtype, qclass, addr, port, rcode):
"""

View File

@@ -0,0 +1,138 @@
# Copyright (C) 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
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM 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.
# This script provides transfer (ixfr/axfr) test functionality
# It provides steps to perform the client side of a transfer,
# and inspect the results.
#
# Like querying.py, it uses dig to do the transfers, and
# places its output in a result structure
#
# This is done in a different file with different steps than
# querying, because the format of dig's output is
# very different than that of normal queries
from lettuce import *
import subprocess
import re
class TransferResult(object):
"""This object stores transfer results, which is essentially simply
a list of RR strings. These are stored, as read from dig's output,
in the list 'records'. So for an IXFR transfer it contains
the exact result as returned by the server.
If this list is empty, the transfer failed for some reason (dig
does not really show error results well, unfortunately).
We may add some smarter inspection functionality to this class
later.
"""
def __init__(self, args):
"""Perform the transfer by calling dig, and store the results.
args is the array of arguments to pass to Popen(), this
is passed as is since for IXFR and AXFR there can be very
different options"""
self.records = []
# Technically, using a pipe here can fail; since we don't expect
# large output right now, this works, but should we get a test
# where we do have a lot of output, this could block, and we will
# need to read the output in a different way.
dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
None)
result = dig_process.wait()
assert result == 0
for l in dig_process.stdout:
line = l.strip()
if len(line) > 0 and line[0] != ';':
self.records.append(line)
@step('An AXFR transfer of ([\w.]+)(?: from ([^:]+)(?::([0-9]+))?)?')
def perform_axfr(step, zone_name, address, port):
"""
Perform an AXFR transfer, and store the result as an instance of
TransferResult in world.transfer_result.
Step definition:
An AXFR transfer of <zone_name> [from <address>:<port>]
Address defaults to 127.0.0.1
Port defaults to 47806
"""
if address is None:
address = "127.0.0.1"
if port is None:
port = 47806
args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
world.transfer_result = TransferResult(args)
@step('An IXFR transfer of ([\w.]+) (\d+)(?: from ([^:]+)(?::([0-9]+))?)?(?: over (tcp|udp))?')
def perform_ixfr(step, zone_name, serial, address, port, protocol):
"""
Perform an IXFR transfer, and store the result as an instance of
TransferResult in world.transfer_result.
Step definition:
An IXFR transfer of <zone_name> <serial> [from <address>:port] [over <tcp|udp>]
Address defaults to 127.0.0.1
Port defaults to 47806
If either tcp or udp is specified, only this protocol will be used.
"""
if address is None:
address = "127.0.0.1"
if port is None:
port = 47806
args = [ 'dig', 'IXFR=' + str(serial), '@' + str(address), '-p', str(port), zone_name ]
if protocol is not None:
assert protocol == 'tcp' or protocol == 'udp', "Unknown protocol: " + protocol
if protocol == 'tcp':
args.append('+tcp')
elif protocol == 'udp':
args.append('+notcp')
world.transfer_result = TransferResult(args)
@step('transfer result should have (\d+) rrs?')
def check_transfer_result_count(step, number_of_rrs):
"""
Check the number of rrs in the transfer result object created by
the AXFR transfer or IXFR transfer step.
Step definition:
transfer result should have <number> rr[s]
Fails if the number of RRs is not equal to number
"""
assert int(number_of_rrs) == len(world.transfer_result.records),\
"Got " + str(len(world.transfer_result.records)) +\
" records, expected " + str(number_of_rrs)
@step('full result of the last transfer should be')
def check_full_transfer_result(step):
"""
Check the complete output from the last transfer call.
Step definition:
full result of the last transfer should be <multiline value>
Whitespace is normalized in both the multiline value and the
output, but the order of the output is not.
Fails if there is any difference between the two. Prints
full output and expected value upon failure.
"""
records_string = "\n".join(world.transfer_result.records)
records_string = re.sub("[ \t]+", " ", records_string)
expect = re.sub("[ \t]+", " ", step.multiline)
assert records_string.strip() == expect.strip(),\
"Got:\n'" + records_string + "'\nExpected:\n'" + expect + "'"

View File

@@ -0,0 +1,196 @@
# 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.
# Reorder Message File
#
# Reads a message file into memory, then outputs it with the messages and
# associated descriptions in alphabetical order.
#
# Invocation:
# The code is invoked using the command line:
#
# python reorder.py message_file
#
# Output is written to stdout.
import sys
def remove_empty_leading_trailing(lines):
"""
Removes leading and trailing empty lines.
A list of strings is passed as argument, some of which may be empty.
This function removes from the start and end of the list a contiguous
sequence of empty lines and returns the result. Embedded sequences of
empty lines are not touched.
Parameters:
lines List of strings to be modified.
Return:
Input list of strings with leading/trailing blank line sequences
removed.
"""
retlines = []
# Dispose of degenerate case of empty array
if len(lines) == 0:
return retlines
# Search for first non-blank line
start = 0
while start < len(lines):
if len(lines[start]) > 0:
break
start = start + 1
# Handle case when entire list is empty
if start >= len(lines):
return retlines
# Search for last non-blank line
finish = len(lines) - 1
while finish >= 0:
if len(lines[finish]) > 0:
break
finish = finish - 1
retlines = lines[start:finish + 1]
return retlines
def canonicalise_message_line(line):
"""
Given a line known to start with the '%' character (i.e. a line
introducing a message), canonicalise it by ensuring that the result
is of the form '%<single-space>MESSAGE_IDENTIFIER<single-space>text'.
Parameters:
line - input line. Known to start with a '%' and to have leading
and trailing spaces removed.
Return:
Canonicalised line.
"""
# Cope with degenerate case of a single "%"
if len(line) == 1:
return line
# Get the rest of the line
line = line[1:].lstrip()
# Extract the first word (the message ID)
words = line.split()
message_line = "% " + words[0]
# ... and now the rest of the line
if len(line) > len(words[0]):
message_line = message_line + " " + line[len(words[0]):].lstrip()
return message_line
def make_dict(lines):
"""
Split the lines into segments starting with the message definition and
place into a dictionary.
Parameters:
lines - list of lines containing the text of the message file (less the
header).
Returns:
dictionary - map of the messages, keyed by the line that holds the message
ID.
"""
dictionary = {}
message_key = canonicalise_message_line(lines[0])
message_lines = [message_key]
index = 1;
while index < len(lines):
if lines[index].startswith("%"):
# Start of new message
dictionary[message_key] = remove_empty_leading_trailing(message_lines)
message_key = canonicalise_message_line(lines[index])
message_lines = [message_key]
else:
message_lines.append(lines[index])
index = index + 1
dictionary[message_key] = remove_empty_leading_trailing(message_lines)
return dictionary
def print_dict(dictionary):
"""
Prints the dictionary with a blank line between entries.
Parameters:
dicitionary - Map holding the message dictionary
"""
count = 0
for msgid in sorted(dictionary):
# Blank line before all entries but the first
if count > 0:
print("")
count = count + 1
# ... and the entry itself.
for l in dictionary[msgid]:
print(l.strip())
def process_file(filename):
"""
Processes a file by reading it and searching for the first line starting
with the '%' sign. Everything before that line is treated as the file
header and is copied to the output with leading and trailing spaces removed.
After that, each message block is read and stored for later sorting.
Parameters:
filename Name of the message file to process
"""
lines = open(filename).read().splitlines()
# Search for the first line starting with the percent character. Everything
# before it is considered the file header and is copied to the output with
# leading and trailing spaces removed.
index = 0
while index < len(lines):
if lines[index].startswith("%"):
break
print(lines[index].strip())
index = index + 1
# Now put the remaining lines into the message dictionary
dictionary = make_dict(lines[index:])
# ...and print it
print_dict(dictionary)
# Main program
if __name__ == "__main__":
# Read the files and load the data
if len(sys.argv) != 2:
print "Usage: python reorder.py message_file"
else:
process_file(sys.argv[1])