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:
33
ChangeLog
33
ChangeLog
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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>> <userinput>config add Zonemgr/secondary_zones</userinput>
|
||||
> <userinput>config set Zonemgr/secondary_zones[0]/name "<option>example.com</option>"</userinput>
|
||||
> <userinput>config set Zonemgr/secondary_zones[0]/class "<option>IN</option>"</userinput>
|
||||
> <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>
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
43
src/bin/dhcp4/Makefile.am
Normal 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
60
src/bin/dhcp4/b10-dhcp4.8
Normal 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
|
98
src/bin/dhcp4/b10-dhcp4.xml
Normal file
98
src/bin/dhcp4/b10-dhcp4.xml
Normal 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 "—">]>
|
||||
<!--
|
||||
- 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
14
src/bin/dhcp4/dhcp4.spec
Normal 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
154
src/bin/dhcp4/dhcp4_srv.cc
Normal 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
137
src/bin/dhcp4/dhcp4_srv.h
Normal 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
112
src/bin/dhcp4/main.cc
Normal 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);
|
||||
}
|
15
src/bin/dhcp4/spec_config.h.pre.in
Normal file
15
src/bin/dhcp4/spec_config.h.pre.in
Normal 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"
|
46
src/bin/dhcp4/tests/Makefile.am
Normal file
46
src/bin/dhcp4/tests/Makefile.am
Normal 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)
|
161
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
Normal file
161
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
Normal 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
|
28
src/bin/dhcp4/tests/dhcp4_unittests.cc
Normal file
28
src/bin/dhcp4/tests/dhcp4_unittests.cc
Normal 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);
|
||||
}
|
@@ -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
|
||||
|
@@ -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",
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -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>,
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
@@ -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.
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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(),
|
@@ -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
|
||||
///
|
@@ -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>
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
///
|
||||
|
@@ -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>
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
@@ -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
|
||||
|
@@ -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_));
|
||||
|
24
src/lib/statistics/Makefile.am
Normal file
24
src/lib/statistics/Makefile.am
Normal 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
|
68
src/lib/statistics/counter.cc
Normal file
68
src/lib/statistics/counter.cc
Normal 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
|
55
src/lib/statistics/counter.h
Normal file
55
src/lib/statistics/counter.h
Normal 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
|
251
src/lib/statistics/counter_dict.cc
Normal file
251
src/lib/statistics/counter_dict.cc
Normal 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
|
145
src/lib/statistics/counter_dict.h
Normal file
145
src/lib/statistics/counter_dict.h
Normal 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
|
47
src/lib/statistics/tests/Makefile.am
Normal file
47
src/lib/statistics/tests/Makefile.am
Normal 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)
|
174
src/lib/statistics/tests/counter_dict_unittest.cc
Normal file
174
src/lib/statistics/tests/counter_dict_unittest.cc
Normal 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);
|
||||
}
|
85
src/lib/statistics/tests/counter_unittest.cc
Normal file
85
src/lib/statistics/tests/counter_unittest.cc
Normal 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);
|
||||
}
|
25
src/lib/statistics/tests/run_unittests.cc
Normal file
25
src/lib/statistics/tests/run_unittests.cc
Normal 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());
|
||||
}
|
@@ -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
|
||||
|
1
tests/lettuce/configurations/ixfr-out/testset1-config.db
Normal file
1
tests/lettuce/configurations/ixfr-out/testset1-config.db
Normal 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"}]}}
|
BIN
tests/lettuce/data/ixfr-out/zones.slite3
Normal file
BIN
tests/lettuce/data/ixfr-out/zones.slite3
Normal file
Binary file not shown.
195
tests/lettuce/features/ixfr_out_bind10.feature
Normal file
195
tests/lettuce/features/ixfr_out_bind10.feature
Normal 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
|
||||
#"""
|
@@ -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]+))?')
|
||||
|
@@ -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):
|
||||
"""
|
||||
|
138
tests/lettuce/features/terrain/transfer.py
Normal file
138
tests/lettuce/features/terrain/transfer.py
Normal 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 + "'"
|
196
tools/reorder_message_file.py
Normal file
196
tools/reorder_message_file.py
Normal 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])
|
Reference in New Issue
Block a user