mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
Merge branch 'trac2238' into trac2269
This commit is contained in:
20
ChangeLog
20
ChangeLog
@@ -1,11 +1,21 @@
|
||||
4XX. [func] tomek
|
||||
A new library (libb10-dhcpsrv) has been create. Currently its
|
||||
functionality is limited to a Configuration Manager. CfgMgr
|
||||
currently only supports basic configuration storage for DHCPv6
|
||||
server, but that capability is expected to be expanded in a near
|
||||
future.
|
||||
A new library (libb10-dhcpsrv) has been created. At present, it
|
||||
only holds the code for the DHCP Configuration Manager. Currently
|
||||
this object only supports basic configuration storage for the DHCPv6
|
||||
server, but that capability will be expanded.
|
||||
(Trac #2238, git TBD)
|
||||
|
||||
475. [func] naokikambe
|
||||
Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
|
||||
xfrreqdone. These are per-zone type counters. The value of these
|
||||
counters can be seen with zone name by invoking "Stats show Xfrout" via
|
||||
bindctl.
|
||||
(Trac #2158, git e68c127fed52e6034ab5309ddd506da03c37a08a)
|
||||
|
||||
474. [func] stephen
|
||||
DHCP servers now use the BIND 10 logging system for messages.
|
||||
(Trac #1545, git de69a92613b36bd3944cb061e1b7c611c3c85506)
|
||||
|
||||
473. [bug] jelte
|
||||
TCP connections now time out in b10-auth if no (or not all) query
|
||||
data is sent by the client. The timeout value defaults to 5000
|
||||
|
@@ -30,7 +30,7 @@ endif
|
||||
|
||||
check-valgrind-suppress:
|
||||
if HAVE_VALGRIND
|
||||
@VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \
|
||||
@VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \
|
||||
make -C $(abs_top_builddir) check
|
||||
else
|
||||
@echo "*** Valgrind is required for check-valgrind-suppress ***"; exit 1;
|
||||
|
@@ -1188,6 +1188,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/lib/datasrc/Makefile
|
||||
src/lib/datasrc/memory/Makefile
|
||||
src/lib/datasrc/memory/tests/Makefile
|
||||
src/lib/datasrc/memory/tests/testdata/Makefile
|
||||
src/lib/datasrc/memory/benchmarks/Makefile
|
||||
src/lib/datasrc/tests/Makefile
|
||||
src/lib/datasrc/tests/testdata/Makefile
|
||||
|
@@ -24,6 +24,7 @@
|
||||
* - @subpage libdhcp
|
||||
* - @subpage libdhcpIntro
|
||||
* - @subpage libdhcpIfaceMgr
|
||||
* - @subpage perfdhcpInternals
|
||||
*
|
||||
* @section misc Miscellaneous topics
|
||||
* - @subpage LoggingApi
|
||||
@@ -36,4 +37,4 @@
|
||||
* @todo: Move this logo to the right (and possibly up). Not sure what
|
||||
* is the best way to do it in Doxygen, without using CSS hacks.
|
||||
* @image html isc-logo.png
|
||||
*/
|
||||
*/
|
||||
|
4
src/bin/dhcp4/.gitignore
vendored
4
src/bin/dhcp4/.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
/b10-dhcp4
|
||||
/b10-dhcp4.8
|
||||
/dhcp4_messages.cc
|
||||
/dhcp4_messages.h
|
||||
/spec_config.h
|
||||
/spec_config.h.pre
|
||||
/b10-dhcp4.8
|
||||
|
@@ -12,7 +12,7 @@ endif
|
||||
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
|
||||
CLEANFILES = spec_config.h
|
||||
CLEANFILES = *.gcno *.gcda spec_config.h dhcp4_messages.h dhcp4_messages.cc
|
||||
|
||||
man_MANS = b10-dhcp4.8
|
||||
DISTCLEANFILES = $(man_MANS)
|
||||
@@ -35,11 +35,20 @@ endif
|
||||
spec_config.h: spec_config.h.pre
|
||||
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
|
||||
|
||||
BUILT_SOURCES = spec_config.h
|
||||
dhcp4_messages.h dhcp4_messages.cc: dhcp4_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes
|
||||
|
||||
BUILT_SOURCES = spec_config.h dhcp4_messages.h dhcp4_messages.cc
|
||||
|
||||
pkglibexec_PROGRAMS = b10-dhcp4
|
||||
|
||||
b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
|
||||
b10_dhcp4_SOURCES = main.cc
|
||||
b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
|
||||
b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h
|
||||
b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h
|
||||
|
||||
nodist_b10_dhcp4_SOURCES = dhcp4_messages.h dhcp4_messages.cc
|
||||
EXTRA_DIST += dhcp4_messages.mes
|
||||
|
||||
if USE_CLANGPP
|
||||
# Disable unused parameter warning caused by some of the
|
||||
@@ -47,7 +56,7 @@ if USE_CLANGPP
|
||||
b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
|
||||
endif
|
||||
|
||||
b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
|
@@ -13,28 +13,30 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <cc/session.h>
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <cc/data.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <cc/session.h>
|
||||
#include <cc/session.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <util/buffer.h>
|
||||
#include <dhcp4/spec_config.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/spec_config.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::util;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
using namespace isc::data;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::config;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::log;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@@ -43,7 +45,8 @@ ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
|
||||
cout << "b10-dhcp4: Received new config:" << new_config->str() << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
|
||||
.arg(new_config->str());
|
||||
ConstElementPtr answer = isc::config::createAnswer(0,
|
||||
"Thank you for sending config.");
|
||||
return (answer);
|
||||
@@ -51,13 +54,14 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr args) {
|
||||
cout << "b10-dhcp4: Received new command: [" << command << "], args="
|
||||
<< args->str() << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
|
||||
.arg(command).arg(args->str());
|
||||
|
||||
if (command == "shutdown") {
|
||||
if (ControlledDhcpv4Srv::server_) {
|
||||
ControlledDhcpv4Srv::server_->shutdown();
|
||||
} else {
|
||||
cout << "Server not initialized yet or already shut down." << endl;
|
||||
LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
"Shutdown failure.");
|
||||
return (answer);
|
||||
@@ -93,10 +97,9 @@ void ControlledDhcpv4Srv::establishSession() {
|
||||
|
||||
/// @todo: Check if session is not established already. Throw, if it is.
|
||||
|
||||
cout << "b10-dhcp4: my specfile is " << specfile << endl;
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
|
||||
.arg(specfile);
|
||||
cc_session_ = new Session(io_service_.get_io_service());
|
||||
|
||||
config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
||||
dhcp4ConfigHandler,
|
||||
dhcp4CommandHandler, false);
|
||||
@@ -106,8 +109,8 @@ void ControlledDhcpv4Srv::establishSession() {
|
||||
/// control with the "select" model of the DHCP server. This is
|
||||
/// fully explained in \ref dhcpv4Session.
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
cout << "b10-dhcp4: Control session started, socket="
|
||||
<< ctrl_socket << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
|
||||
.arg(ctrl_socket);
|
||||
IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
|
||||
}
|
||||
|
||||
|
26
src/bin/dhcp4/dhcp4_log.cc
Normal file
26
src/bin/dhcp4/dhcp4_log.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2012 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.
|
||||
|
||||
/// Defines the logger used by the top-level component of b10-dhcp4.
|
||||
|
||||
#include "dhcp4_log.h"
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
isc::log::Logger dhcp4_logger("dhcp4");
|
||||
|
||||
} // namespace dhcp
|
||||
} // namespace isc
|
||||
|
59
src/bin/dhcp4/dhcp4_log.h
Normal file
59
src/bin/dhcp4/dhcp4_log.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2012 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 __DHCP4_LOG__H
|
||||
#define __DHCP4_LOG__H
|
||||
|
||||
#include <log/macros.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <dhcp4/dhcp4_messages.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// \brief DHCP4 Logging
|
||||
///
|
||||
/// Defines the levels used to output debug messages in the non-library part of
|
||||
/// the b10-dhcp4 program. Higher numbers equate to more verbose (and detailed)
|
||||
/// output.
|
||||
|
||||
// Debug levels used to log information during startup and shutdown.
|
||||
const int DBG_DHCP4_START = DBGLVL_START_SHUT;
|
||||
const int DBG_DHCP4_SHUT = DBGLVL_START_SHUT;
|
||||
|
||||
// Debug level used to log setting information (such as configuration changes).
|
||||
const int DBG_DHCP4_COMMAND = DBGLVL_COMMAND;
|
||||
|
||||
// Trace basic operations within the code.
|
||||
const int DBG_DHCP4_BASIC = DBGLVL_TRACE_BASIC;
|
||||
|
||||
// Trace detailed operations, including errors raised when processing invalid
|
||||
// packets. (These are not logged at severities of WARN or higher for fear
|
||||
// that a set of deliberately invalid packets set to the server could overwhelm
|
||||
// the logging.)
|
||||
const int DBG_DHCP4_DETAIL = DBGLVL_TRACE_DETAIL;
|
||||
|
||||
// This level is used to log the contents of packets received and sent.
|
||||
const int DBG_DHCP4_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
|
||||
|
||||
/// Define the logger for the "dhcp4" module part of b10-dhcp4. We could define
|
||||
/// a logger in each file, but we would want to define a common name to avoid
|
||||
/// spelling mistakes, so it is just one small step from there to define a
|
||||
/// module-common logger.
|
||||
extern isc::log::Logger dhcp4_logger;
|
||||
|
||||
} // namespace dhcp4
|
||||
} // namespace isc
|
||||
|
||||
#endif // __DHCP4_LOG__H
|
98
src/bin/dhcp4/dhcp4_messages.mes
Normal file
98
src/bin/dhcp4/dhcp4_messages.mes
Normal file
@@ -0,0 +1,98 @@
|
||||
# Copyright (C) 2012 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.
|
||||
|
||||
$NAMESPACE isc::dhcp
|
||||
|
||||
% DHCP4_CCSESSION_STARTED control channel session started on socket %1
|
||||
A debug message issued during startup after the IPv4 DHCP server has
|
||||
successfully established a session with the BIND 10 control channel.
|
||||
|
||||
% DHCP4_CCSESSION_STARTING starting control channel session, specfile: %1
|
||||
This debug message is issued just before the IPv4 DHCP server attempts
|
||||
to establish a session with the BIND 10 control channel.
|
||||
|
||||
% DHCP4_COMMAND_RECEIVED received command %1, arguments: %2
|
||||
A debug message listing the command (and possible arguments) received
|
||||
from the BIND 10 control system by the IPv4 DHCP server.
|
||||
|
||||
% DHCP4_CONFIG_UPDATE updated configuration received: %1
|
||||
A debug message indicating that the IPv4 DHCP server has received an
|
||||
updated configuration from the BIND 10 configuration system.
|
||||
|
||||
% DHCP4_NOT_RUNNING IPv4 DHCP server is not running
|
||||
A warning message is issued when an attempt is made to shut down the
|
||||
IPv4 DHCP server but it is not running.
|
||||
|
||||
% DHCP4_OPEN_SOCKET opening sockets on port %1
|
||||
A debug message issued during startup, this indicates that the IPv4 DHCP
|
||||
server is about to open sockets on the specified port.
|
||||
|
||||
% DHCP4_PACKET_PARSE_FAIL failed to parse incoming packet: %1
|
||||
The IPv4 DHCP server has received a packet that it is unable to
|
||||
interpret. The reason why the packet is invalid is included in the message.
|
||||
|
||||
% DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3
|
||||
A debug message noting that the server has received the specified type of
|
||||
packet on the specified interface. Note that a packet marked as UNKNOWN
|
||||
may well be a valid DHCP packet, just a type not expected by the server
|
||||
(e.g. it will report a received OFFER packet as UNKNOWN).
|
||||
|
||||
% DHCP4_PACK_FAIL failed to assemble response correctly
|
||||
This error is output if the server failed to assemble the data to be
|
||||
returned to the client into a valid packet. The cause is most likely
|
||||
to be a programming error: please raise a bug report.
|
||||
|
||||
% DHCP4_QUERY_DATA received packet type %1, data is <%2>
|
||||
A debug message listing the data received from the client.
|
||||
|
||||
% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
|
||||
A debug message listing the data returned to the client.
|
||||
|
||||
% DHCP4_SERVER_FAILED server failed: %1
|
||||
The IPv4 DHCP server has encountered a fatal error and is terminating.
|
||||
The reason for the failure is included in the message.
|
||||
|
||||
% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
||||
The server has failed to establish communication with the rest of BIND
|
||||
10 and is running in stand-alone mode. (This behavior will change once
|
||||
the IPv4 DHCP server is properly integrated with the rest of BIND 10.)
|
||||
|
||||
% DHCP4_SHUTDOWN server shutdown
|
||||
The IPv4 DHCP server has terminated normally.
|
||||
|
||||
% DHCP4_SHUTDOWN_REQUEST shutdown of server requested
|
||||
This debug message indicates that a shutdown of the IPv4 server has
|
||||
been requested via a call to the 'shutdown' method of the core Dhcpv4Srv
|
||||
object.
|
||||
|
||||
% DHCP4_SRV_CONSTRUCT_ERROR error creating Dhcpv4Srv object, reason: %1
|
||||
This error message indicates that during startup, the construction of a
|
||||
core component within the IPv4 DHCP server (the Dhcpv4 server object)
|
||||
has failed. As a result, the server will exit. The reason for the
|
||||
failure is given within the message.
|
||||
|
||||
% DHCP4_STANDALONE skipping message queue, running standalone
|
||||
This is a debug message indicating that the IPv4 server is running in
|
||||
standalone mode, not connected to the message queue. Standalone mode
|
||||
is only useful during program development, and should not be used in a
|
||||
production environment.
|
||||
|
||||
% DHCP4_STARTING server starting
|
||||
This informational message indicates that the IPv4 DHCP server has
|
||||
processed any command-line switches and is starting.
|
||||
|
||||
% DHCP4_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4
|
||||
This is a debug message issued during the IPv4 DHCP server startup.
|
||||
It lists some information about the parameters with which the server
|
||||
is running.
|
@@ -16,13 +16,15 @@
|
||||
#include <dhcp/pkt4.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp4/dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/option4_addrlst.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::log;
|
||||
using namespace std;
|
||||
|
||||
// These are hardcoded parameters. Currently this is a skeleton server that only
|
||||
// grants those options and a single, fixed, hardcoded lease.
|
||||
@@ -35,20 +37,19 @@ const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
|
||||
const std::string HARDCODED_SERVER_ID = "192.0.2.1";
|
||||
|
||||
Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
|
||||
cout << "Initialization: opening sockets on port " << port << endl;
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
||||
try {
|
||||
// first call to instance() will create IfaceMgr (it's a singleton)
|
||||
// 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.
|
||||
|
||||
IfaceMgr::instance().openSockets4(port);
|
||||
|
||||
setServerID();
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
cerr << "Error during DHCPv4 server startup: " << e.what() << endl;
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
|
||||
shutdown_ = true;
|
||||
return;
|
||||
}
|
||||
@@ -57,12 +58,11 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
|
||||
}
|
||||
|
||||
Dhcpv4Srv::~Dhcpv4Srv() {
|
||||
cout << "b10-dhcp4: DHCPv4 server terminating." << endl;
|
||||
IfaceMgr::instance().closeSockets();
|
||||
}
|
||||
|
||||
void Dhcpv4Srv::shutdown() {
|
||||
cout << "b10-dhcp4: DHCPv4 server shutdown." << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
@@ -79,39 +79,48 @@ Dhcpv4Srv::run() {
|
||||
if (query) {
|
||||
try {
|
||||
query->unpack();
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
/// TODO: Printout reasons of failed parsing
|
||||
cout << "Failed to parse incoming packet " << endl;
|
||||
// Failed to parse the packet.
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
|
||||
DHCP4_PACKET_PARSE_FAIL).arg(e.what());
|
||||
continue;
|
||||
}
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
|
||||
.arg(serverReceivedPacketName(query->getType()))
|
||||
.arg(query->getType())
|
||||
.arg(query->getIface());
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
|
||||
.arg(query->toText());
|
||||
|
||||
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;
|
||||
// Only action is to output a message if debug is enabled,
|
||||
// and that will be covered by the debug statement before
|
||||
// the "switch" statement.
|
||||
;
|
||||
}
|
||||
|
||||
cout << "Received message type " << int(query->getType()) << endl;
|
||||
|
||||
// TODO: print out received packets only if verbose (or debug)
|
||||
// mode is enabled
|
||||
cout << query->toText();
|
||||
|
||||
if (rsp) {
|
||||
if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
|
||||
rsp->setRemoteAddr(query->getRemoteAddr());
|
||||
@@ -127,14 +136,15 @@ Dhcpv4Srv::run() {
|
||||
rsp->setIface(query->getIface());
|
||||
rsp->setIndex(query->getIndex());
|
||||
|
||||
cout << "Replying with message type "
|
||||
<< static_cast<int>(rsp->getType()) << ":" << endl;
|
||||
cout << rsp->toText();
|
||||
cout << "----" << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
|
||||
DHCP4_RESPONSE_DATA)
|
||||
.arg(rsp->getType()).arg(rsp->toText());
|
||||
|
||||
if (rsp->pack()) {
|
||||
cout << "Packet assembled correctly." << endl;
|
||||
IfaceMgr::instance().send(rsp);
|
||||
} else {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
|
||||
}
|
||||
IfaceMgr::instance().send(rsp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,15 +276,44 @@ Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
|
||||
|
||||
void Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
|
||||
/// TODO: Implement this.
|
||||
cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
|
||||
}
|
||||
|
||||
void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
|
||||
/// TODO: Implement this.
|
||||
cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
|
||||
}
|
||||
|
||||
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
|
||||
/// TODO: Currently implemented echo mode. Implement this for real
|
||||
return (inform);
|
||||
}
|
||||
|
||||
const char*
|
||||
Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
|
||||
static const char* DISCOVER = "DISCOVER";
|
||||
static const char* REQUEST = "REQUEST";
|
||||
static const char* RELEASE = "RELEASE";
|
||||
static const char* DECLINE = "DECLINE";
|
||||
static const char* INFORM = "INFORM";
|
||||
static const char* UNKNOWN = "UNKNOWN";
|
||||
|
||||
switch (type) {
|
||||
case DHCPDISCOVER:
|
||||
return (DISCOVER);
|
||||
|
||||
case DHCPREQUEST:
|
||||
return (REQUEST);
|
||||
|
||||
case DHCPRELEASE:
|
||||
return (RELEASE);
|
||||
|
||||
case DHCPDECLINE:
|
||||
return (DECLINE);
|
||||
|
||||
case DHCPINFORM:
|
||||
return (INFORM);
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
return (UNKNOWN);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ class Dhcpv4Srv : public boost::noncopyable {
|
||||
public:
|
||||
/// @brief Default constructor.
|
||||
///
|
||||
/// Instantiates necessary services, required to run DHCPv6 server.
|
||||
/// Instantiates necessary services, required to run DHCPv4 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
|
||||
@@ -54,7 +54,7 @@ class Dhcpv4Srv : public boost::noncopyable {
|
||||
/// @param port specifies port number to listen on
|
||||
Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT);
|
||||
|
||||
/// @brief Destructor. Used during DHCPv6 service shutdown.
|
||||
/// @brief Destructor. Used during DHCPv4 service shutdown.
|
||||
~Dhcpv4Srv();
|
||||
|
||||
/// @brief Main server processing loop.
|
||||
@@ -70,6 +70,23 @@ class Dhcpv4Srv : public boost::noncopyable {
|
||||
/// @brief Instructs the server to shut down.
|
||||
void shutdown();
|
||||
|
||||
/// @brief Return textual type of packet received by server
|
||||
///
|
||||
/// Returns the name of valid packet received by the server (e.g. DISCOVER).
|
||||
/// If the packet is unknown - or if it is a valid DHCP packet but not one
|
||||
/// expected to be received by the server (such as an OFFER), the string
|
||||
/// "UNKNOWN" is returned. This method is used in debug messages.
|
||||
///
|
||||
/// As the operation of the method does not depend on any server state, it
|
||||
/// is declared static.
|
||||
///
|
||||
/// @param type DHCPv4 packet type
|
||||
///
|
||||
/// @return Pointer to "const" string containing the packet name.
|
||||
/// Note that this string is statically allocated and MUST NOT
|
||||
/// be freed by the caller.
|
||||
static const char* serverReceivedPacketName(uint8_t type);
|
||||
|
||||
protected:
|
||||
/// @brief Processes incoming DISCOVER and returns response.
|
||||
///
|
||||
@@ -89,11 +106,11 @@ protected:
|
||||
/// 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
|
||||
/// Returns ACK message, NAK message, or NULL
|
||||
///
|
||||
/// @param request a message received from client
|
||||
///
|
||||
/// @return ACK or NACK message
|
||||
/// @return ACK or NAK message
|
||||
Pkt4Ptr processRequest(Pkt4Ptr& request);
|
||||
|
||||
/// @brief Stub function that will handle incoming RELEASE messages.
|
||||
|
@@ -14,13 +14,15 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <iostream>
|
||||
#include <log/dummylog.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
using namespace std;
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <log/logger_support.h>
|
||||
|
||||
using namespace isc::dhcp;
|
||||
using namespace std;
|
||||
|
||||
/// This file contains entry point (main() function) for standard DHCPv4 server
|
||||
/// component for BIND10 framework. It parses command-line arguments and
|
||||
@@ -37,11 +39,10 @@ const char* const DHCP4_NAME = "b10-dhcp4";
|
||||
|
||||
void
|
||||
usage() {
|
||||
cerr << "Usage: b10-dhcp4 [-v]"
|
||||
<< endl;
|
||||
cerr << "\t-v: verbose output" << endl;
|
||||
cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
|
||||
cerr << "\t-p number: specify non-standard port number 1-65535 "
|
||||
cerr << "Usage: " << DHCP4_NAME << " [-v] [-s] [-p number]" << endl;
|
||||
cerr << " -v: verbose output" << endl;
|
||||
cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl;
|
||||
cerr << " -p number: specify non-standard port number 1-65535 "
|
||||
<< "(useful for testing only)" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -50,20 +51,21 @@ usage() {
|
||||
int
|
||||
main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
bool verbose_mode = false; // should server be verbose?
|
||||
int port_number = DHCP4_SERVER_PORT; // The default. any other values are
|
||||
// useful for testing only.
|
||||
bool stand_alone = false; // should be connect to BIND10 msgq?
|
||||
bool stand_alone = false; // Should be connect to BIND10 msgq?
|
||||
bool verbose_mode = false; // Should server be verbose?
|
||||
|
||||
while ((ch = getopt(argc, argv, "vsp:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'v':
|
||||
verbose_mode = true;
|
||||
isc::log::denabled = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
stand_alone = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
try {
|
||||
port_number = boost::lexical_cast<int>(optarg);
|
||||
@@ -78,50 +80,46 @@ main(int argc, char* argv[]) {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for extraneous parameters.
|
||||
if (argc > optind) {
|
||||
usage();
|
||||
}
|
||||
|
||||
// Initialize logging. If verbose, we'll use maximum verbosity.
|
||||
isc::log::initLogger(DHCP4_NAME,
|
||||
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL);
|
||||
LOG_INFO(dhcp4_logger, DHCP4_STARTING);
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
|
||||
.arg(stand_alone ? "yes" : "no" );
|
||||
|
||||
cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port "
|
||||
<< port_number << ", verbose " << (verbose_mode?"yes":"no")
|
||||
<< ", stand-alone=" << (stand_alone?"yes":"no") << endl;
|
||||
|
||||
if (argc - optind > 0) {
|
||||
usage();
|
||||
}
|
||||
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
try {
|
||||
|
||||
cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
|
||||
|
||||
/// @todo: pass verbose to the actul server once logging is implemented
|
||||
ControlledDhcpv4Srv server(port_number);
|
||||
|
||||
if (!stand_alone) {
|
||||
try {
|
||||
server.establishSession();
|
||||
} catch (const std::exception& ex) {
|
||||
cerr << "Failed to establish BIND10 session. "
|
||||
"Running in stand-alone mode:" << ex.what() << endl;
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what());
|
||||
// Let's continue. It is useful to have the ability to run
|
||||
// DHCP server in stand-alone mode, e.g. for testing
|
||||
}
|
||||
} else {
|
||||
cout << "Skipping connection to the BIND10 msgq." << endl;
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_STANDALONE);
|
||||
}
|
||||
|
||||
server.run();
|
||||
LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
|
||||
LOG_FATAL(dhcp4_logger, DHCP4_SERVER_FAILED).arg(ex.what());
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ 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
|
||||
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
|
||||
@@ -47,9 +47,11 @@ if HAVE_GTEST
|
||||
TESTS += dhcp4_unittests
|
||||
|
||||
dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
|
||||
dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
|
||||
dhcp4_unittests_SOURCES += dhcp4_unittests.cc
|
||||
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
|
||||
dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
|
||||
nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
|
||||
|
||||
if USE_CLANGPP
|
||||
# Disable unused parameter warning caused by some of the
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-2012 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
|
||||
@@ -271,4 +271,36 @@ TEST_F(Dhcpv4SrvTest, processInform) {
|
||||
delete srv;
|
||||
}
|
||||
|
||||
TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
|
||||
// Check all possible packet types
|
||||
for (int itype = 0; itype < 256; ++itype) {
|
||||
uint8_t type = itype;
|
||||
|
||||
switch (type) {
|
||||
case DHCPDECLINE:
|
||||
EXPECT_STREQ("DECLINE", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPDISCOVER:
|
||||
EXPECT_STREQ("DISCOVER", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPINFORM:
|
||||
EXPECT_STREQ("INFORM", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPRELEASE:
|
||||
EXPECT_STREQ("RELEASE", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPREQUEST:
|
||||
EXPECT_STREQ("REQUEST", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
default:
|
||||
EXPECT_STREQ("UNKNOWN", Dhcpv4Srv::serverReceivedPacketName(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
@@ -27,16 +27,27 @@ import fcntl
|
||||
|
||||
class TestDhcpv4Daemon(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# don't redirect stdout/stderr here as we want to print out things
|
||||
# Don't redirect stdout/stderr here as we want to print out things
|
||||
# during the test
|
||||
pass
|
||||
#
|
||||
# However, we do want to set the logging lock directory to somewhere
|
||||
# to which we can write - use the current working directory. We then
|
||||
# set the appropriate environment variable. os.putenv() may be not
|
||||
# supported on some platforms as suggested in
|
||||
# http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ:
|
||||
# "If the platform supports the putenv() function...". It was checked
|
||||
# that it does not work on Ubuntu. To overcome this problem we access
|
||||
# os.environ directly.
|
||||
lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD"
|
||||
if lockdir_envvar not in os.environ:
|
||||
os.environ[lockdir_envvar] = os.getcwd()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def runCommand(self, params, wait=1):
|
||||
"""
|
||||
This method runs dhcp4 and returns a touple: (returncode, stdout, stderr)
|
||||
This method runs dhcp4 and returns a tuple: (returncode, stdout, stderr)
|
||||
"""
|
||||
## @todo: Convert this into generic method and reuse it in dhcp6
|
||||
|
||||
@@ -79,9 +90,9 @@ class TestDhcpv4Daemon(unittest.TestCase):
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
||||
|
||||
# There's potential problem if b10-dhcp4 prints out more
|
||||
# than 4k of text
|
||||
# than 16kB of text
|
||||
try:
|
||||
output = os.read(self.stdout_pipes[0], 4096)
|
||||
output = os.read(self.stdout_pipes[0], 16384)
|
||||
except OSError:
|
||||
print("No data available from stdout")
|
||||
output = ""
|
||||
@@ -91,7 +102,7 @@ class TestDhcpv4Daemon(unittest.TestCase):
|
||||
output = ""
|
||||
|
||||
try:
|
||||
error = os.read(self.stderr_pipes[0], 4096)
|
||||
error = os.read(self.stderr_pipes[0], 16384)
|
||||
except OSError:
|
||||
print("No data available on stderr")
|
||||
error = ""
|
||||
@@ -128,13 +139,13 @@ class TestDhcpv4Daemon(unittest.TestCase):
|
||||
print(" not that is can bind sockets correctly. Please ignore binding errors.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(["../b10-dhcp4", "-v"])
|
||||
|
||||
self.assertEqual( str(output).count("[b10-dhcp4] Initiating DHCPv4 server operation."), 1)
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP4_STARTING"), 1)
|
||||
|
||||
def test_portnumber_0(self):
|
||||
print("Check that specifying port number 0 is not allowed.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p', '0'])
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-p', '0'])
|
||||
|
||||
# When invalid port number is specified, return code must not be success
|
||||
self.assertTrue(returncode != 0)
|
||||
@@ -178,28 +189,19 @@ class TestDhcpv4Daemon(unittest.TestCase):
|
||||
def test_portnumber_nonroot(self):
|
||||
print("Check that specifying unprivileged port number will work.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057'])
|
||||
|
||||
# When invalid port number is specified, return code must not be success
|
||||
# TODO: Temporarily commented out as socket binding on systems that do not have
|
||||
# interface detection implemented currently fails.
|
||||
# self.assertTrue(returncode == 0)
|
||||
|
||||
# Check that there is an error message about invalid port number printed on stderr
|
||||
self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
|
||||
# Check that there is a message about running with an unprivileged port
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP4_OPEN_SOCKET opening sockets on port 10057"), 1)
|
||||
|
||||
def test_skip_msgq(self):
|
||||
print("Check that connection to BIND10 msgq can be disabled.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057'])
|
||||
|
||||
# When invalid port number is specified, return code must not be success
|
||||
# TODO: Temporarily commented out as socket binding on systems that do not have
|
||||
# interface detection implemented currently fails.
|
||||
# self.assertTrue(returncode == 0)
|
||||
|
||||
# Check that there is an error message about invalid port number printed on stderr
|
||||
self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
|
||||
# Check that the system outputs a message on one of its streams about running
|
||||
# standalone.
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP4_STANDALONE"), 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
15
src/bin/dhcp6/.gitignore
vendored
15
src/bin/dhcp6/.gitignore
vendored
@@ -1,11 +1,6 @@
|
||||
*~
|
||||
Makefile
|
||||
Makefile.in
|
||||
*.o
|
||||
.deps
|
||||
.libs
|
||||
b10-dhcp6
|
||||
spec_config.h
|
||||
spec_config.h.pre
|
||||
tests/dhcp6_unittests
|
||||
/b10-dhcp6
|
||||
/b10-dhcp6.8
|
||||
/dhcp6_messages.cc
|
||||
/dhcp6_messages.h
|
||||
/spec_config.h
|
||||
/spec_config.h.pre
|
||||
|
@@ -13,7 +13,7 @@ endif
|
||||
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
|
||||
CLEANFILES = spec_config.h
|
||||
CLEANFILES = spec_config.h dhcp6_messages.h dhcp6_messages.cc
|
||||
|
||||
man_MANS = b10-dhcp6.8
|
||||
DISTCLEANFILES = $(man_MANS)
|
||||
@@ -37,11 +37,20 @@ endif
|
||||
spec_config.h: spec_config.h.pre
|
||||
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
|
||||
|
||||
BUILT_SOURCES = spec_config.h
|
||||
dhcp6_messages.h dhcp6_messages.cc: dhcp6_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes
|
||||
|
||||
BUILT_SOURCES = spec_config.h dhcp6_messages.h dhcp6_messages.cc
|
||||
|
||||
pkglibexec_PROGRAMS = b10-dhcp6
|
||||
|
||||
b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
|
||||
b10_dhcp6_SOURCES = main.cc
|
||||
b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
|
||||
b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
|
||||
b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
|
||||
|
||||
nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc
|
||||
EXTRA_DIST += dhcp6_messages.mes
|
||||
|
||||
if USE_CLANGPP
|
||||
# Disable unused parameter warning caused by some of the
|
||||
@@ -49,7 +58,7 @@ if USE_CLANGPP
|
||||
b10_dhcp6_CXXFLAGS = -Wno-unused-parameter
|
||||
endif
|
||||
|
||||
b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
|
@@ -13,28 +13,30 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <cc/session.h>
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <cc/data.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <cc/session.h>
|
||||
#include <cc/session.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <util/buffer.h>
|
||||
#include <dhcp6/spec_config.h>
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <dhcp6/spec_config.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::util;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
using namespace isc::data;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::config;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::log;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@@ -43,7 +45,8 @@ ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
|
||||
cout << "b10-dhcp6: Received new config:" << new_config->str() << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE)
|
||||
.arg(new_config->str());
|
||||
ConstElementPtr answer = isc::config::createAnswer(0,
|
||||
"Thank you for sending config.");
|
||||
return (answer);
|
||||
@@ -51,13 +54,14 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) {
|
||||
cout << "b10-dhcp6: Received new command: [" << command << "], args="
|
||||
<< args->str() << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED)
|
||||
.arg(command).arg(args->str());
|
||||
|
||||
if (command == "shutdown") {
|
||||
if (ControlledDhcpv6Srv::server_) {
|
||||
ControlledDhcpv6Srv::server_->shutdown();
|
||||
} else {
|
||||
cout << "Server not initialized yet or already shut down." << endl;
|
||||
LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING);
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
"Shutdown failure.");
|
||||
return (answer);
|
||||
@@ -93,10 +97,9 @@ void ControlledDhcpv6Srv::establishSession() {
|
||||
|
||||
/// @todo: Check if session is not established already. Throw, if it is.
|
||||
|
||||
cout << "b10-dhcp6: my specfile is " << specfile << endl;
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING)
|
||||
.arg(specfile);
|
||||
cc_session_ = new Session(io_service_.get_io_service());
|
||||
|
||||
config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
||||
dhcp6ConfigHandler,
|
||||
dhcp6CommandHandler, false);
|
||||
@@ -106,8 +109,8 @@ void ControlledDhcpv6Srv::establishSession() {
|
||||
/// control with the "select" model of the DHCP server. This is
|
||||
/// fully explained in \ref dhcpv6Session.
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
cout << "b10-dhcp6: Control session started, socket="
|
||||
<< ctrl_socket << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED)
|
||||
.arg(ctrl_socket);
|
||||
IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
|
||||
}
|
||||
|
||||
|
26
src/bin/dhcp6/dhcp6_log.cc
Normal file
26
src/bin/dhcp6/dhcp6_log.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2012 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.
|
||||
|
||||
/// Defines the logger used by the top-level component of b10-dhcp6.
|
||||
|
||||
#include "dhcp6_log.h"
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
isc::log::Logger dhcp6_logger("dhcp6");
|
||||
|
||||
} // namespace dhcp
|
||||
} // namespace isc
|
||||
|
59
src/bin/dhcp6/dhcp6_log.h
Normal file
59
src/bin/dhcp6/dhcp6_log.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2012 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 __DHCP6_LOG__H
|
||||
#define __DHCP6_LOG__H
|
||||
|
||||
#include <log/macros.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <dhcp6/dhcp6_messages.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// \brief DHCP6 Logging
|
||||
///
|
||||
/// Defines the levels used to output debug messages in the non-library part of
|
||||
/// the b10-dhcp6 program. Higher numbers equate to more verbose (and detailed)
|
||||
/// output.
|
||||
|
||||
// Debug levels used to log information during startup and shutdown.
|
||||
const int DBG_DHCP6_START = DBGLVL_START_SHUT;
|
||||
const int DBG_DHCP6_SHUT = DBGLVL_START_SHUT;
|
||||
|
||||
// Debug level used to log setting information (such as configuration changes).
|
||||
const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND;
|
||||
|
||||
// Trace basic operations within the code.
|
||||
const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
|
||||
|
||||
// Trace detailed operations, including errors raised when processing invalid
|
||||
// packets. (These are not logged at severities of WARN or higher for fear
|
||||
// that a set of deliberately invalid packets set to the server could overwhelm
|
||||
// the logging.)
|
||||
const int DBG_DHCP6_DETAIL = DBGLVL_TRACE_DETAIL;
|
||||
|
||||
// This level is used to log the contents of packets received and sent.
|
||||
const int DBG_DHCP6_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
|
||||
|
||||
/// Define the logger for the "dhcp6" module part of b10-dhcp6. We could define
|
||||
/// a logger in each file, but we would want to define a common name to avoid
|
||||
/// spelling mistakes, so it is just one small step from there to define a
|
||||
/// module-common logger.
|
||||
extern isc::log::Logger dhcp6_logger;
|
||||
|
||||
} // namespace dhcp6
|
||||
} // namespace isc
|
||||
|
||||
#endif // __DHCP6_LOG__H
|
101
src/bin/dhcp6/dhcp6_messages.mes
Normal file
101
src/bin/dhcp6/dhcp6_messages.mes
Normal file
@@ -0,0 +1,101 @@
|
||||
# Copyright (C) 2012 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.
|
||||
|
||||
$NAMESPACE isc::dhcp
|
||||
|
||||
% DHCP6_CCSESSION_STARTED control channel session started on socket %1
|
||||
A debug message issued during startup after the IPv6 DHCP server has
|
||||
successfully established a session with the BIND 10 control channel.
|
||||
|
||||
% DHCP6_CCSESSION_STARTING starting control channel session, specfile: %1
|
||||
This debug message is issued just before the IPv6 DHCP server attempts
|
||||
to establish a session with the BIND 10 control channel.
|
||||
|
||||
% DHCP6_COMMAND_RECEIVED received command %1, arguments: %2
|
||||
A debug message listing the command (and possible arguments) received
|
||||
from the BIND 10 control system by the IPv6 DHCP server.
|
||||
|
||||
% DHCP6_CONFIG_UPDATE updated configuration received: %1
|
||||
A debug message indicating that the IPv6 DHCP server has received an
|
||||
updated configuration from the BIND 10 configuration system.
|
||||
|
||||
% DHCP6_NOT_RUNNING IPv6 DHCP server is not running
|
||||
A warning message is issued when an attempt is made to shut down the
|
||||
IPv6 DHCP server but it is not running.
|
||||
|
||||
% DHCP6_NO_INTERFACES failed to detect any network interfaces
|
||||
During startup the IPv6 DHCP server failed to detect any network
|
||||
interfaces and is therefore shutting down.
|
||||
|
||||
% DHCP6_OPEN_SOCKET opening sockets on port %1
|
||||
A debug message issued during startup, this indicates that the IPv6 DHCP
|
||||
server is about to open sockets on the specified port.
|
||||
|
||||
% DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet
|
||||
The IPv6 DHCP server has received a packet that it is unable to interpret.
|
||||
|
||||
% DHCP6_PACKET_RECEIVED %1 (type %2) packet received
|
||||
A debug message noting that the server has received the specified type
|
||||
of packet. Note that a packet marked as UNKNOWN may well be a valid
|
||||
DHCP packet, just a type not expected by the server (e.g. it will report
|
||||
a received OFFER packet as UNKNOWN).
|
||||
|
||||
% DHCP6_PACK_FAIL failed to assemble response correctly
|
||||
This error is output if the server failed to assemble the data to be
|
||||
returned to the client into a valid packet. The reason is most likely
|
||||
to be to a programming error: please raise a bug report.
|
||||
|
||||
% DHCP6_QUERY_DATA received packet length %1, data length %2, data is <%3>
|
||||
A debug message listing the data received from the client or relay.
|
||||
|
||||
% DHCP6_RESPONSE_DATA responding with packet type %1 data is <%2>
|
||||
A debug message listing the data returned to the client.
|
||||
|
||||
% DHCP6_SERVER_FAILED server failed: %1
|
||||
The IPv6 DHCP server has encountered a fatal error and is terminating.
|
||||
The reason for the failure is included in the message.
|
||||
|
||||
% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
||||
The server has failed to establish communication with the rest of BIND
|
||||
10 and is running in stand-alone mode. (This behavior will change once
|
||||
the IPv6 DHCP server is properly integrated with the rest of BIND 10.)
|
||||
|
||||
% DHCP6_SHUTDOWN server shutdown
|
||||
The IPv6 DHCP server has terminated normally.
|
||||
|
||||
% DHCP6_SHUTDOWN_REQUEST shutdown of server requested
|
||||
This debug message indicates that a shutdown of the IPv6 server has
|
||||
been requested via a call to the 'shutdown' method of the core Dhcpv6Srv
|
||||
object.
|
||||
|
||||
% DHCP6_SRV_CONSTRUCT_ERROR error creating Dhcpv6Srv object, reason: %1
|
||||
This error message indicates that during startup, the construction of a
|
||||
core component within the IPv6 DHCP server (the Dhcpv6 server object)
|
||||
has failed. As a result, the server will exit. The reason for the
|
||||
failure is given within the message.
|
||||
|
||||
% DHCP6_STANDALONE skipping message queue, running standalone
|
||||
This is a debug message indicating that the IPv6 server is running in
|
||||
standalone mode, not connected to the message queue. Standalone mode
|
||||
is only useful during program development, and should not be used in a
|
||||
production environment.
|
||||
|
||||
% DHCP6_STARTING server starting
|
||||
This informational message indicates that the IPv6 DHCP server has
|
||||
processed any command-line switches and is starting.
|
||||
|
||||
% DHCP6_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4
|
||||
This is a debug message issued during the IPv6 DHCP server startup.
|
||||
It lists some information about the parameters with which the server
|
||||
is running.
|
@@ -14,23 +14,25 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/pkt6.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp6/dhcp6_srv.h>
|
||||
#include <dhcp/option6_ia.h>
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option6_addrlst.h>
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <dhcp6/dhcp6_srv.h>
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp/option6_addrlst.h>
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option6_ia.h>
|
||||
#include <dhcp/pkt6.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
#include <util/range_utilities.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
|
||||
const uint32_t HARDCODED_T1 = 1500; // in seconds
|
||||
@@ -40,14 +42,14 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
|
||||
const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
|
||||
|
||||
Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
|
||||
cout << "Initialization: opening sockets on port " << port << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
|
||||
|
||||
// first call to instance() will create IfaceMgr (it's a singleton)
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
// it may throw something if things go wrong
|
||||
try {
|
||||
|
||||
if (IfaceMgr::instance().countIfaces() == 0) {
|
||||
cout << "Failed to detect any network interfaces. Aborting." << endl;
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
|
||||
shutdown_ = true;
|
||||
return;
|
||||
}
|
||||
@@ -59,7 +61,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
|
||||
/// @todo: instantiate LeaseMgr here once it is imlpemented.
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
cerr << "Error during DHCPv4 server startup: " << e.what() << endl;
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
|
||||
shutdown_ = true;
|
||||
return;
|
||||
}
|
||||
@@ -68,13 +70,11 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
|
||||
}
|
||||
|
||||
Dhcpv6Srv::~Dhcpv6Srv() {
|
||||
cout << "DHCPv6 Srv shutdown." << endl;
|
||||
|
||||
IfaceMgr::instance().closeSockets();
|
||||
}
|
||||
|
||||
void Dhcpv6Srv::shutdown() {
|
||||
cout << "b10-dhcp6: DHCPv6 server shutdown." << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SHUTDOWN_REQUEST);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
@@ -89,42 +89,58 @@ bool Dhcpv6Srv::run() {
|
||||
|
||||
if (query) {
|
||||
if (!query->unpack()) {
|
||||
cout << "Failed to parse incoming packet" << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
||||
DHCP6_PACKET_PARSE_FAIL);
|
||||
continue;
|
||||
}
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
|
||||
.arg(serverReceivedPacketName(query->getType()))
|
||||
.arg(query->getType());
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
|
||||
.arg(query->getType())
|
||||
.arg(query->getBuffer().getLength())
|
||||
.arg(query->toText());
|
||||
|
||||
switch (query->getType()) {
|
||||
case DHCPV6_SOLICIT:
|
||||
rsp = processSolicit(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_REQUEST:
|
||||
rsp = processRequest(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_RENEW:
|
||||
rsp = processRenew(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_REBIND:
|
||||
rsp = processRebind(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_CONFIRM:
|
||||
rsp = processConfirm(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_RELEASE:
|
||||
rsp = processRelease(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_DECLINE:
|
||||
rsp = processDecline(query);
|
||||
break;
|
||||
|
||||
case DHCPV6_INFORMATION_REQUEST:
|
||||
rsp = processInfRequest(query);
|
||||
break;
|
||||
|
||||
default:
|
||||
cout << "Unknown pkt type received:"
|
||||
<< query->getType() << endl;
|
||||
// Only action is to output a message if debug is enabled,
|
||||
// and that will be covered by the debug statement before
|
||||
// the "switch" statement.
|
||||
;
|
||||
}
|
||||
|
||||
cout << "Received " << query->getBuffer().getLength() << " bytes packet type="
|
||||
<< query->getType() << endl;
|
||||
cout << query->toText();
|
||||
if (rsp) {
|
||||
rsp->setRemoteAddr(query->getRemoteAddr());
|
||||
rsp->setLocalAddr(query->getLocalAddr());
|
||||
@@ -132,14 +148,16 @@ bool Dhcpv6Srv::run() {
|
||||
rsp->setLocalPort(DHCP6_SERVER_PORT);
|
||||
rsp->setIndex(query->getIndex());
|
||||
rsp->setIface(query->getIface());
|
||||
cout << "Replying with:" << rsp->getType() << endl;
|
||||
cout << rsp->toText();
|
||||
cout << "----" << endl;
|
||||
if (!rsp->pack()) {
|
||||
cout << "Failed to assemble response packet." << endl;
|
||||
continue;
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
|
||||
DHCP6_RESPONSE_DATA)
|
||||
.arg(rsp->getType()).arg(rsp->toText());
|
||||
|
||||
if (rsp->pack()) {
|
||||
IfaceMgr::instance().send(rsp);
|
||||
} else {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL);
|
||||
}
|
||||
IfaceMgr::instance().send(rsp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,3 +368,46 @@ Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
|
||||
return reply;
|
||||
}
|
||||
|
||||
const char*
|
||||
Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
|
||||
static const char* CONFIRM = "CONFIRM";
|
||||
static const char* DECLINE = "DECLINE";
|
||||
static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST";
|
||||
static const char* REBIND = "REBIND";
|
||||
static const char* RELEASE = "RELEASE";
|
||||
static const char* RENEW = "RENEW";
|
||||
static const char* REQUEST = "REQUEST";
|
||||
static const char* SOLICIT = "SOLICIT";
|
||||
static const char* UNKNOWN = "UNKNOWN";
|
||||
|
||||
switch (type) {
|
||||
case DHCPV6_CONFIRM:
|
||||
return (CONFIRM);
|
||||
|
||||
case DHCPV6_DECLINE:
|
||||
return (DECLINE);
|
||||
|
||||
case DHCPV6_INFORMATION_REQUEST:
|
||||
return (INFORMATION_REQUEST);
|
||||
|
||||
case DHCPV6_REBIND:
|
||||
return (REBIND);
|
||||
|
||||
case DHCPV6_RELEASE:
|
||||
return (RELEASE);
|
||||
|
||||
case DHCPV6_RENEW:
|
||||
return (RENEW);
|
||||
|
||||
case DHCPV6_REQUEST:
|
||||
return (REQUEST);
|
||||
|
||||
case DHCPV6_SOLICIT:
|
||||
return (SOLICIT);
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
return (UNKNOWN);
|
||||
}
|
||||
|
@@ -69,6 +69,24 @@ public:
|
||||
|
||||
/// @brief Instructs the server to shut down.
|
||||
void shutdown();
|
||||
|
||||
/// @brief Return textual type of packet received by server
|
||||
///
|
||||
/// Returns the name of valid packet received by the server (e.g. SOLICIT).
|
||||
/// If the packet is unknown - or if it is a valid DHCP packet but not one
|
||||
/// expected to be received by the server (such as an ADVERTISE), the string
|
||||
/// "UNKNOWN" is returned. This method is used in debug messages.
|
||||
///
|
||||
/// As the operation of the method does not depend on any server state, it
|
||||
/// is declared static.
|
||||
///
|
||||
/// @param type DHCPv4 packet type
|
||||
///
|
||||
/// @return Pointer to "const" string containing the packet name.
|
||||
/// Note that this string is statically allocated and MUST NOT
|
||||
/// be freed by the caller.
|
||||
static const char* serverReceivedPacketName(uint8_t type);
|
||||
|
||||
protected:
|
||||
/// @brief Processes incoming SOLICIT and returns response.
|
||||
///
|
||||
|
@@ -14,13 +14,15 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <iostream>
|
||||
#include <log/dummylog.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
using namespace std;
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <log/logger_support.h>
|
||||
|
||||
using namespace isc::dhcp;
|
||||
using namespace std;
|
||||
|
||||
/// This file contains entry point (main() function) for standard DHCPv6 server
|
||||
/// component for BIND10 framework. It parses command-line arguments and
|
||||
@@ -37,11 +39,10 @@ const char* const DHCP6_NAME = "b10-dhcp6";
|
||||
|
||||
void
|
||||
usage() {
|
||||
cerr << "Usage: b10-dhcp6 [-v]"
|
||||
<< endl;
|
||||
cerr << "\t-v: verbose output" << endl;
|
||||
cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
|
||||
cerr << "\t-p number: specify non-standard port number 1-65535 "
|
||||
cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p number]" << endl;
|
||||
cerr << " -v: verbose output" << endl;
|
||||
cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl;
|
||||
cerr << " -p number: specify non-standard port number 1-65535 "
|
||||
<< "(useful for testing only)" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -52,18 +53,19 @@ main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
|
||||
// useful for testing only.
|
||||
bool stand_alone = false; // Should be connect to BIND10 msgq?
|
||||
bool verbose_mode = false; // Should server be verbose?
|
||||
bool stand_alone = false; // should be connect to BIND10 msgq?
|
||||
|
||||
while ((ch = getopt(argc, argv, "vsp:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'v':
|
||||
verbose_mode = true;
|
||||
isc::log::denabled = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
stand_alone = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
try {
|
||||
port_number = boost::lexical_cast<int>(optarg);
|
||||
@@ -78,51 +80,45 @@ main(int argc, char* argv[]) {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for extraneous parameters.
|
||||
if (argc > optind) {
|
||||
usage();
|
||||
}
|
||||
|
||||
// Initialize logging. If verbose, we'll use maximum verbosity.
|
||||
isc::log::initLogger(DHCP6_NAME,
|
||||
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL);
|
||||
|
||||
cout << "b10-dhcp6: My pid=" << getpid() << ", binding to port "
|
||||
<< port_number << ", verbose " << (verbose_mode?"yes":"no")
|
||||
<< ", stand-alone=" << (stand_alone?"yes":"no") << endl;
|
||||
|
||||
if (argc - optind > 0) {
|
||||
usage();
|
||||
}
|
||||
LOG_INFO(dhcp6_logger, DHCP6_STARTING);
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
|
||||
.arg(stand_alone ? "yes" : "no" );
|
||||
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
try {
|
||||
|
||||
cout << "b10-dhcp6: Initiating DHCPv6 server operation." << endl;
|
||||
|
||||
/// @todo: pass verbose to the actual server once logging is implemented
|
||||
ControlledDhcpv6Srv server(port_number);
|
||||
|
||||
if (!stand_alone) {
|
||||
try {
|
||||
server.establishSession();
|
||||
} catch (const std::exception& ex) {
|
||||
cerr << "Failed to establish BIND10 session. "
|
||||
"Running in stand-alone mode:" << ex.what() << endl;
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what());
|
||||
// Let's continue. It is useful to have the ability to run
|
||||
// DHCP server in stand-alone mode, e.g. for testing
|
||||
}
|
||||
} else {
|
||||
cout << "Skipping connection to the BIND10 msgq." << endl;
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE);
|
||||
}
|
||||
|
||||
server.run();
|
||||
LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;
|
||||
LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what());
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
1
src/bin/dhcp6/tests/.gitignore
vendored
Normal file
1
src/bin/dhcp6/tests/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/dhcp6_unittests
|
@@ -26,7 +26,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/bin
|
||||
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
|
||||
|
||||
CLEANFILES = $(builddir)/interfaces.txt
|
||||
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
|
||||
@@ -42,10 +42,13 @@ if HAVE_GTEST
|
||||
|
||||
TESTS += dhcp6_unittests
|
||||
|
||||
dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc ../ctrl_dhcp6_srv.cc
|
||||
dhcp6_unittests_SOURCES += dhcp6_unittests.cc
|
||||
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
|
||||
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
|
||||
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
|
||||
dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
|
||||
dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
|
||||
dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
|
||||
nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
|
||||
|
||||
if USE_CLANGPP
|
||||
# Disable unused parameter warning caused by some of the
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-2012 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
|
||||
@@ -223,4 +223,49 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
|
||||
// more checks to be implemented
|
||||
}
|
||||
|
||||
TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) {
|
||||
// Check all possible packet types
|
||||
for (int itype = 0; itype < 256; ++itype) {
|
||||
uint8_t type = itype;
|
||||
|
||||
switch (type) {
|
||||
case DHCPV6_CONFIRM:
|
||||
EXPECT_STREQ("CONFIRM", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_DECLINE:
|
||||
EXPECT_STREQ("DECLINE", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_INFORMATION_REQUEST:
|
||||
EXPECT_STREQ("INFORMATION_REQUEST",
|
||||
Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_REBIND:
|
||||
EXPECT_STREQ("REBIND", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_RELEASE:
|
||||
EXPECT_STREQ("RELEASE", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_RENEW:
|
||||
EXPECT_STREQ("RENEW", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_REQUEST:
|
||||
EXPECT_STREQ("REQUEST", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
case DHCPV6_SOLICIT:
|
||||
EXPECT_STREQ("SOLICIT", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
break;
|
||||
|
||||
default:
|
||||
EXPECT_STREQ("UNKNOWN", Dhcpv6Srv::serverReceivedPacketName(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
@@ -27,16 +27,27 @@ import fcntl
|
||||
|
||||
class TestDhcpv6Daemon(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# don't redirect stdout/stderr here as we want to print out things
|
||||
# Don't redirect stdout/stderr here as we want to print out things
|
||||
# during the test
|
||||
pass
|
||||
#
|
||||
# However, we do want to set the logging lock directory to somewhere
|
||||
# to which we can write - use the current working directory. We then
|
||||
# set the appropriate environment variable. os.putenv() may be not
|
||||
# supported on some platforms as suggested in
|
||||
# http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ:
|
||||
# "If the platform supports the putenv() function...". It was checked
|
||||
# that it does not work on Ubuntu. To overcome this problem we access
|
||||
# os.environ directly.
|
||||
lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD"
|
||||
if lockdir_envvar not in os.environ:
|
||||
os.environ[lockdir_envvar] = os.getcwd()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def runCommand(self, params, wait=1):
|
||||
"""
|
||||
This method runs a command and returns a touple: (returncode, stdout, stderr)
|
||||
This method runs a command and returns a tuple: (returncode, stdout, stderr)
|
||||
"""
|
||||
## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6
|
||||
|
||||
@@ -79,9 +90,9 @@ class TestDhcpv6Daemon(unittest.TestCase):
|
||||
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
||||
|
||||
# There's potential problem if b10-dhcp4 prints out more
|
||||
# than 4k of text
|
||||
# than 16k of text
|
||||
try:
|
||||
output = os.read(self.stdout_pipes[0], 4096)
|
||||
output = os.read(self.stdout_pipes[0], 16384)
|
||||
except OSError:
|
||||
print("No data available from stdout")
|
||||
output = ""
|
||||
@@ -91,7 +102,7 @@ class TestDhcpv6Daemon(unittest.TestCase):
|
||||
output = ""
|
||||
|
||||
try:
|
||||
error = os.read(self.stderr_pipes[0], 4096)
|
||||
error = os.read(self.stderr_pipes[0], 16384)
|
||||
except OSError:
|
||||
print("No data available on stderr")
|
||||
error = ""
|
||||
@@ -130,8 +141,8 @@ class TestDhcpv6Daemon(unittest.TestCase):
|
||||
print("Note: Purpose of some of the tests is to check if DHCPv6 server can be started,")
|
||||
print(" not that is can bind sockets correctly. Please ignore binding errors.")
|
||||
(returncode, output, error) = self.runCommand(["../b10-dhcp6", "-v"])
|
||||
|
||||
self.assertEqual( str(output).count("b10-dhcp6: Initiating DHCPv6 server operation."), 1)
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP6_STARTING"), 1)
|
||||
|
||||
def test_portnumber_0(self):
|
||||
print("Check that specifying port number 0 is not allowed.")
|
||||
@@ -180,27 +191,19 @@ class TestDhcpv6Daemon(unittest.TestCase):
|
||||
def test_portnumber_nonroot(self):
|
||||
print("Check that specifying unprivileged port number will work.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
|
||||
|
||||
# When invalid port number is specified, return code must not be success
|
||||
# TODO: Temporarily commented out as socket binding on systems that do not have
|
||||
# interface detection implemented currently fails.
|
||||
# self.assertTrue(returncode == 0)
|
||||
|
||||
self.assertEqual( str(output).count("opening sockets on port 10547"), 1)
|
||||
# Check that there is a message about running with an unprivileged port
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP6_OPEN_SOCKET opening sockets on port 10547"), 1)
|
||||
|
||||
def test_skip_msgq(self):
|
||||
print("Check that connection to BIND10 msgq can be disabled.")
|
||||
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
|
||||
|
||||
# When invalid port number is specified, return code must not be success
|
||||
# TODO: Temporarily commented out as socket binding on systems that do not have
|
||||
# interface detection implemented currently fails.
|
||||
# self.assertTrue(returncode == 0)
|
||||
|
||||
self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
|
||||
|
||||
# Check that the system outputs a message on one of its streams about running
|
||||
# standalone.
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP6_STANDALONE"), 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@@ -153,6 +153,54 @@
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>STATISTICS DATA</title>
|
||||
|
||||
<para>
|
||||
The statistics data collected by the <command>b10-xfrout</command>
|
||||
daemon for <quote>Xfrout</quote> include:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term>notifyoutv4</term>
|
||||
<listitem><simpara>
|
||||
Number of IPv4 notifies per zone name sent out from Xfrout
|
||||
</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>notifyoutv6</term>
|
||||
<listitem><simpara>
|
||||
Number of IPv6 notifies per zone name sent out from Xfrout
|
||||
</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>xfrrej</term>
|
||||
<listitem><simpara>
|
||||
Number of XFR requests per zone name rejected by Xfrout
|
||||
</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>xfrreqdone</term>
|
||||
<listitem><simpara>
|
||||
Number of requested zone transfers per zone name completed
|
||||
</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
In per-zone counters the special zone name '_SERVER_' exists. It doesn't
|
||||
mean a specific zone. It represents an entire server and its value means
|
||||
a total count of all zones.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<!--
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
@@ -277,13 +277,23 @@ class TestXfroutSessionBase(unittest.TestCase):
|
||||
# When not testing ACLs, simply accept
|
||||
isc.acl.dns.REQUEST_LOADER.load(
|
||||
[{"action": "ACCEPT"}]),
|
||||
{})
|
||||
{},
|
||||
counter_xfrrej=self._counter_xfrrej,
|
||||
counter_xfrreqdone=self._counter_xfrreqdone)
|
||||
self.set_request_type(RRType.AXFR()) # test AXFR by default
|
||||
self.mdata = self.create_request_data()
|
||||
self.soa_rrset = create_soa(SOA_CURRENT_VERSION)
|
||||
# some test replaces a module-wide function. We should ensure the
|
||||
# original is used elsewhere.
|
||||
self.orig_get_rrset_len = xfrout.get_rrset_len
|
||||
self._zone_name_xfrrej = None
|
||||
self._zone_name_xfrreqdone = None
|
||||
|
||||
def _counter_xfrrej(self, zone_name):
|
||||
self._zone_name_xfrrej = zone_name
|
||||
|
||||
def _counter_xfrreqdone(self, zone_name):
|
||||
self._zone_name_xfrreqdone = zone_name
|
||||
|
||||
def tearDown(self):
|
||||
xfrout.get_rrset_len = self.orig_get_rrset_len
|
||||
@@ -458,7 +468,28 @@ class TestXfroutSession(TestXfroutSessionBase):
|
||||
# ACL checks only with the default ACL
|
||||
def acl_setter(acl):
|
||||
self.xfrsess._acl = acl
|
||||
self.assertIsNone(self._zone_name_xfrrej)
|
||||
self.check_transfer_acl(acl_setter)
|
||||
self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
|
||||
|
||||
def test_transfer_acl_with_nonetype_xfrrej(self):
|
||||
# ACL checks only with the default ACL and NoneType xfrrej
|
||||
# counter
|
||||
def acl_setter(acl):
|
||||
self.xfrsess._acl = acl
|
||||
self.xfrsess._counter_xfrrej = None
|
||||
self.assertIsNone(self._zone_name_xfrrej)
|
||||
self.check_transfer_acl(acl_setter)
|
||||
self.assertIsNone(self._zone_name_xfrrej)
|
||||
|
||||
def test_transfer_acl_with_notcallable_xfrrej(self):
|
||||
# ACL checks only with the default ACL and not callable xfrrej
|
||||
# counter
|
||||
def acl_setter(acl):
|
||||
self.xfrsess._acl = acl
|
||||
self.xfrsess._counter_xfrrej = 'NOT CALLABLE'
|
||||
self.assertRaises(TypeError,
|
||||
self.check_transfer_acl, acl_setter)
|
||||
|
||||
def test_transfer_zoneacl(self):
|
||||
# ACL check with a per zone ACL + default ACL. The per zone ACL
|
||||
@@ -469,7 +500,9 @@ class TestXfroutSession(TestXfroutSessionBase):
|
||||
self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl
|
||||
self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
|
||||
{"from": "127.0.0.1", "action": "DROP"}])
|
||||
self.assertIsNone(self._zone_name_xfrrej)
|
||||
self.check_transfer_acl(acl_setter)
|
||||
self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
|
||||
|
||||
def test_transfer_zoneacl_nomatch(self):
|
||||
# similar to the previous one, but the per zone doesn't match the
|
||||
@@ -481,7 +514,9 @@ class TestXfroutSession(TestXfroutSessionBase):
|
||||
isc.acl.dns.REQUEST_LOADER.load([
|
||||
{"from": "127.0.0.1", "action": "DROP"}])
|
||||
self.xfrsess._acl = acl
|
||||
self.assertIsNone(self._zone_name_xfrrej)
|
||||
self.check_transfer_acl(acl_setter)
|
||||
self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
|
||||
|
||||
def test_get_transfer_acl(self):
|
||||
# set the default ACL. If there's no specific zone ACL, this one
|
||||
@@ -831,9 +866,39 @@ class TestXfroutSession(TestXfroutSessionBase):
|
||||
def myreply(msg, sock):
|
||||
self.sock.send(b"success")
|
||||
|
||||
self.assertIsNone(self._zone_name_xfrreqdone)
|
||||
self.xfrsess._reply_xfrout_query = myreply
|
||||
self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
|
||||
self.assertEqual(self.sock.readsent(), b"success")
|
||||
self.assertEqual(self._zone_name_xfrreqdone, TEST_ZONE_NAME_STR)
|
||||
|
||||
def test_dns_xfrout_start_with_nonetype_xfrreqdone(self):
|
||||
def noerror(msg, name, rrclass):
|
||||
return Rcode.NOERROR()
|
||||
self.xfrsess._xfrout_setup = noerror
|
||||
|
||||
def myreply(msg, sock):
|
||||
self.sock.send(b"success")
|
||||
|
||||
self.assertIsNone(self._zone_name_xfrreqdone)
|
||||
self.xfrsess._reply_xfrout_query = myreply
|
||||
self.xfrsess._counter_xfrreqdone = None
|
||||
self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
|
||||
self.assertIsNone(self._zone_name_xfrreqdone)
|
||||
|
||||
def test_dns_xfrout_start_with_notcallable_xfrreqdone(self):
|
||||
def noerror(msg, name, rrclass):
|
||||
return Rcode.NOERROR()
|
||||
self.xfrsess._xfrout_setup = noerror
|
||||
|
||||
def myreply(msg, sock):
|
||||
self.sock.send(b"success")
|
||||
|
||||
self.xfrsess._reply_xfrout_query = myreply
|
||||
self.xfrsess._counter_xfrreqdone = 'NOT CALLABLE'
|
||||
self.assertRaises(TypeError,
|
||||
self.xfrsess.dns_xfrout_start, self.sock,
|
||||
self.mdata)
|
||||
|
||||
def test_reply_xfrout_query_axfr(self):
|
||||
self.xfrsess._soa = self.soa_rrset
|
||||
@@ -1153,6 +1218,7 @@ class MyUnixSockServer(UnixSockServer):
|
||||
self._common_init()
|
||||
self._cc = MyCCSession()
|
||||
self.update_config_data(self._cc.get_full_config())
|
||||
self._counters = {}
|
||||
|
||||
class TestUnixSockServer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -1504,6 +1570,80 @@ class MyXfroutServer(XfroutServer):
|
||||
self._unix_socket_server = None
|
||||
# Disable the wait for threads
|
||||
self._wait_for_threads = lambda : None
|
||||
self._cc.get_module_spec = lambda:\
|
||||
isc.config.module_spec_from_file(xfrout.SPECFILE_LOCATION)
|
||||
# setup an XfroutCount object
|
||||
self._counter = XfroutCounter(
|
||||
self._cc.get_module_spec().get_statistics_spec())
|
||||
|
||||
class TestXfroutCounter(unittest.TestCase):
|
||||
def setUp(self):
|
||||
statistics_spec = \
|
||||
isc.config.module_spec_from_file(\
|
||||
xfrout.SPECFILE_LOCATION).get_statistics_spec()
|
||||
self.xfrout_counter = XfroutCounter(statistics_spec)
|
||||
self._counters = isc.config.spec_name_list(\
|
||||
isc.config.find_spec_part(\
|
||||
statistics_spec, XfroutCounter.perzone_prefix)\
|
||||
['named_set_item_spec']['map_item_spec'])
|
||||
self._started = threading.Event()
|
||||
self._number = 3 # number of the threads
|
||||
self._cycle = 10000 # number of counting per thread
|
||||
|
||||
def test_get_default_statistics_data(self):
|
||||
self.assertEqual(self.xfrout_counter._get_default_statistics_data(),
|
||||
{XfroutCounter.perzone_prefix: {
|
||||
XfroutCounter.entire_server: \
|
||||
dict([(cnt, 0) for cnt in self._counters])
|
||||
}})
|
||||
|
||||
def setup_incrementer(self, incrementer):
|
||||
self._started.wait()
|
||||
for i in range(self._cycle): incrementer(TEST_ZONE_NAME_STR)
|
||||
|
||||
def start_incrementer(self, incrementer):
|
||||
threads = []
|
||||
for i in range(self._number):
|
||||
threads.append(threading.Thread(\
|
||||
target=self.setup_incrementer,\
|
||||
args=(incrementer,)\
|
||||
))
|
||||
for th in threads: th.start()
|
||||
self._started.set()
|
||||
for th in threads: th.join()
|
||||
|
||||
def get_count(self, zone_name, counter_name):
|
||||
return isc.cc.data.find(\
|
||||
self.xfrout_counter.get_statistics(),\
|
||||
'%s/%s/%s' % (XfroutCounter.perzone_prefix,\
|
||||
zone_name, counter_name))
|
||||
|
||||
def test_incrementers(self):
|
||||
result = { XfroutCounter.entire_server: {},
|
||||
TEST_ZONE_NAME_STR: {} }
|
||||
for counter_name in self._counters:
|
||||
incrementer = getattr(self.xfrout_counter, 'inc_%s' % counter_name)
|
||||
self.start_incrementer(incrementer)
|
||||
self.assertEqual(self.get_count(\
|
||||
TEST_ZONE_NAME_STR, counter_name), \
|
||||
self._number * self._cycle)
|
||||
self.assertEqual(self.get_count(\
|
||||
XfroutCounter.entire_server, counter_name), \
|
||||
self._number * self._cycle)
|
||||
result[XfroutCounter.entire_server][counter_name] = \
|
||||
result[TEST_ZONE_NAME_STR][counter_name] = \
|
||||
self._number * self._cycle
|
||||
self.assertEqual(
|
||||
self.xfrout_counter.get_statistics(),
|
||||
{XfroutCounter.perzone_prefix: result})
|
||||
|
||||
def test_add_perzone_counter(self):
|
||||
for counter_name in self._counters:
|
||||
self.assertRaises(isc.cc.data.DataNotFoundError,\
|
||||
self.get_count, TEST_ZONE_NAME_STR, counter_name)
|
||||
self.xfrout_counter._add_perzone_counter(TEST_ZONE_NAME_STR)
|
||||
for counter_name in self._counters:
|
||||
self.assertEqual(self.get_count(TEST_ZONE_NAME_STR, counter_name), 0)
|
||||
|
||||
class TestXfroutServer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -1514,6 +1654,11 @@ class TestXfroutServer(unittest.TestCase):
|
||||
self.assertTrue(self.xfrout_server._notifier.shutdown_called)
|
||||
self.assertTrue(self.xfrout_server._cc.stopped)
|
||||
|
||||
def test_getstats(self):
|
||||
self.assertEqual(
|
||||
self.xfrout_server.command_handler('getstats', None), \
|
||||
create_answer(0, {}))
|
||||
|
||||
if __name__== "__main__":
|
||||
isc.log.resetUnitTestRootLogger()
|
||||
unittest.main()
|
||||
|
@@ -153,7 +153,8 @@ def get_soa_serial(soa_rdata):
|
||||
|
||||
class XfroutSession():
|
||||
def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote,
|
||||
default_acl, zone_config, client_class=DataSourceClient):
|
||||
default_acl, zone_config, client_class=DataSourceClient,
|
||||
counter_xfrrej=None, counter_xfrreqdone=None):
|
||||
self._sock_fd = sock_fd
|
||||
self._request_data = request_data
|
||||
self._server = server
|
||||
@@ -168,6 +169,10 @@ class XfroutSession():
|
||||
self.ClientClass = client_class # parameterize this for testing
|
||||
self._soa = None # will be set in _xfrout_setup or in tests
|
||||
self._jnl_reader = None # will be set to a reader for IXFR
|
||||
# Set counter handlers for counting Xfr requests. An argument
|
||||
# is required for zone name.
|
||||
self._counter_xfrrej = counter_xfrrej
|
||||
self._counter_xfrreqdone = counter_xfrreqdone
|
||||
self._handle()
|
||||
|
||||
def create_tsig_ctx(self, tsig_record, tsig_key_ring):
|
||||
@@ -270,6 +275,9 @@ class XfroutSession():
|
||||
format_zone_str(zone_name, zone_class))
|
||||
return None, None
|
||||
elif acl_result == REJECT:
|
||||
if self._counter_xfrrej is not None:
|
||||
# count rejected Xfr request by each zone name
|
||||
self._counter_xfrrej(zone_name.to_text())
|
||||
logger.debug(DBG_XFROUT_TRACE, XFROUT_QUERY_REJECTED,
|
||||
self._request_type, format_addrinfo(self._remote),
|
||||
format_zone_str(zone_name, zone_class))
|
||||
@@ -525,6 +533,9 @@ class XfroutSession():
|
||||
except Exception as err:
|
||||
logger.error(XFROUT_XFR_TRANSFER_ERROR, self._request_typestr,
|
||||
format_addrinfo(self._remote), zone_str, err)
|
||||
if self._counter_xfrreqdone is not None:
|
||||
# count done Xfr requests by each zone name
|
||||
self._counter_xfrreqdone(zone_name.to_text())
|
||||
logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_typestr,
|
||||
format_addrinfo(self._remote), zone_str)
|
||||
|
||||
@@ -634,7 +645,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
|
||||
'''The unix domain socket server which accept xfr query sent from auth server.'''
|
||||
|
||||
def __init__(self, sock_file, handle_class, shutdown_event, config_data,
|
||||
cc):
|
||||
cc, **counters):
|
||||
self._remove_unused_sock_file(sock_file)
|
||||
self._sock_file = sock_file
|
||||
socketserver_mixin.NoPollMixIn.__init__(self)
|
||||
@@ -644,6 +655,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
|
||||
self._common_init()
|
||||
self._cc = cc
|
||||
self.update_config_data(config_data)
|
||||
# handlers for statistics use
|
||||
self._counters = counters
|
||||
|
||||
def _common_init(self):
|
||||
'''Initialization shared with the mock server class used for tests'''
|
||||
@@ -798,7 +811,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
|
||||
self._lock.release()
|
||||
self.RequestHandlerClass(sock_fd, request_data, self,
|
||||
isc.server_common.tsig_keyring.get_keyring(),
|
||||
self._guess_remote(sock_fd), acl, zone_config)
|
||||
self._guess_remote(sock_fd), acl, zone_config,
|
||||
**self._counters)
|
||||
|
||||
def _remove_unused_sock_file(self, sock_file):
|
||||
'''Try to remove the socket file. If the file is being used
|
||||
@@ -926,6 +940,107 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
|
||||
self._transfers_counter -= 1
|
||||
self._lock.release()
|
||||
|
||||
class XfroutCounter:
|
||||
"""A class for handling all statistics counters of Xfrout. In
|
||||
this class, the structure of per-zone counters is assumed to be
|
||||
like this:
|
||||
zones/example.com./notifyoutv4
|
||||
zones/example.com./notifyoutv6
|
||||
zones/example.com./xfrrej
|
||||
zones/example.com./xfrreqdone
|
||||
"""
|
||||
# '_SERVER_' is a special zone name representing an entire
|
||||
# count. It doesn't mean a specific zone, but it means an
|
||||
# entire count in the server.
|
||||
entire_server = '_SERVER_'
|
||||
# zone names are contained under this dirname in the spec file.
|
||||
perzone_prefix = 'zones'
|
||||
def __init__(self, statistics_spec):
|
||||
self._statistics_spec = statistics_spec
|
||||
# holding statistics data for Xfrout module
|
||||
self._statistics_data = {}
|
||||
self._lock = threading.RLock()
|
||||
self._create_perzone_incrementers()
|
||||
|
||||
def get_statistics(self):
|
||||
"""Calculates an entire server counts, and returns statistics
|
||||
data format to send out the stats module including each
|
||||
counter. If there is no counts, then it returns an empty
|
||||
dictionary. Locks the thread because it is considered to be
|
||||
invoked by a multi-threading caller."""
|
||||
# If self._statistics_data contains nothing of zone name, it
|
||||
# returns an empty dict.
|
||||
if len(self._statistics_data) == 0: return {}
|
||||
zones = {}
|
||||
with self._lock:
|
||||
zones = self._statistics_data[self.perzone_prefix].copy()
|
||||
# Start calculation for '_SERVER_' counts
|
||||
attrs = self._get_default_statistics_data()[self.perzone_prefix][self.entire_server]
|
||||
statistics_data = {self.perzone_prefix: {}}
|
||||
for attr in attrs:
|
||||
sum_ = 0
|
||||
for name in zones:
|
||||
if name == self.entire_server: continue
|
||||
if attr in zones[name]:
|
||||
if name not in statistics_data[self.perzone_prefix]:
|
||||
statistics_data[self.perzone_prefix][name] = {}
|
||||
statistics_data[self.perzone_prefix][name].update(
|
||||
{attr: zones[name][attr]}
|
||||
)
|
||||
sum_ += zones[name][attr]
|
||||
if sum_ > 0:
|
||||
if self.entire_server not in statistics_data[self.perzone_prefix]:
|
||||
statistics_data[self.perzone_prefix][self.entire_server] = {}
|
||||
statistics_data[self.perzone_prefix][self.entire_server].update({attr: sum_})
|
||||
return statistics_data
|
||||
|
||||
def _get_default_statistics_data(self):
|
||||
"""Returns default statistics data from the spec file"""
|
||||
statistics_data = {}
|
||||
for id_ in isc.config.spec_name_list(self._statistics_spec):
|
||||
spec = isc.config.find_spec_part(self._statistics_spec, id_)
|
||||
statistics_data.update({id_: spec['item_default']})
|
||||
return statistics_data
|
||||
|
||||
def _create_perzone_incrementers(self):
|
||||
"""Creates increment method of each per-zone counter based on
|
||||
the spec file. Incrementer can be accessed by name
|
||||
"inc_${item_name}".Incrementers are passed to the
|
||||
XfroutSession and NotifyOut class as counter handlers."""
|
||||
# add a new element under the named_set item for the zone
|
||||
zones_spec = isc.config.find_spec_part(
|
||||
self._statistics_spec, self.perzone_prefix)
|
||||
item_list = isc.config.spec_name_list(\
|
||||
zones_spec['named_set_item_spec']['map_item_spec'])
|
||||
# can be accessed by the name 'inc_xxx'
|
||||
for item in item_list:
|
||||
def __perzone_incrementer(zone_name, counter_name=item, step=1):
|
||||
"""A per-zone incrementer for counter_name. Locks the thread
|
||||
because it is considered to be invoked by a multi-threading
|
||||
caller."""
|
||||
with self._lock:
|
||||
self._add_perzone_counter(zone_name)
|
||||
self._statistics_data[self.perzone_prefix][zone_name][counter_name] += step
|
||||
setattr(self, 'inc_%s' % item, __perzone_incrementer)
|
||||
|
||||
|
||||
def _add_perzone_counter(self, zone):
|
||||
"""Adds named_set-type counter for each zone name"""
|
||||
try:
|
||||
self._statistics_data[self.perzone_prefix][zone]
|
||||
except KeyError:
|
||||
# add a new element under the named_set item for the zone
|
||||
map_spec = isc.config.find_spec_part(
|
||||
self._statistics_spec, '%s/%s' % \
|
||||
(self.perzone_prefix, zone))['map_item_spec']
|
||||
id_list = isc.config.spec_name_list(map_spec)
|
||||
for id_ in id_list:
|
||||
spec = isc.config.find_spec_part(map_spec, id_)
|
||||
isc.cc.data.set(self._statistics_data,
|
||||
'%s/%s/%s' % \
|
||||
(self.perzone_prefix, zone, id_),
|
||||
spec['item_default'])
|
||||
|
||||
class XfroutServer:
|
||||
def __init__(self):
|
||||
self._unix_socket_server = None
|
||||
@@ -933,6 +1048,8 @@ class XfroutServer:
|
||||
self._shutdown_event = threading.Event()
|
||||
self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
|
||||
self._config_data = self._cc.get_full_config()
|
||||
self._counter = XfroutCounter(
|
||||
self._cc.get_module_spec().get_statistics_spec())
|
||||
self._cc.start()
|
||||
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
|
||||
isc.server_common.tsig_keyring.init_keyring(self._cc)
|
||||
@@ -941,17 +1058,25 @@ class XfroutServer:
|
||||
|
||||
def _start_xfr_query_listener(self):
|
||||
'''Start a new thread to accept xfr query. '''
|
||||
self._unix_socket_server = UnixSockServer(self._listen_sock_file,
|
||||
XfroutSession,
|
||||
self._shutdown_event,
|
||||
self._config_data,
|
||||
self._cc)
|
||||
self._unix_socket_server = UnixSockServer(
|
||||
self._listen_sock_file,
|
||||
XfroutSession,
|
||||
self._shutdown_event,
|
||||
self._config_data,
|
||||
self._cc,
|
||||
counter_xfrrej=self._counter.inc_xfrrej,
|
||||
counter_xfrreqdone=self._counter.inc_xfrreqdone
|
||||
)
|
||||
listener = threading.Thread(target=self._unix_socket_server.serve_forever)
|
||||
listener.start()
|
||||
|
||||
def _start_notifier(self):
|
||||
datasrc = self._unix_socket_server.get_db_file()
|
||||
self._notifier = notify_out.NotifyOut(datasrc)
|
||||
self._notifier = notify_out.NotifyOut(
|
||||
datasrc,
|
||||
counter_notifyoutv4=self._counter.inc_notifyoutv4,
|
||||
counter_notifyoutv6=self._counter.inc_notifyoutv6
|
||||
)
|
||||
if 'also_notify' in self._config_data:
|
||||
for slave in self._config_data['also_notify']:
|
||||
self._notifier.add_slave(slave['address'], slave['port'])
|
||||
@@ -1027,6 +1152,15 @@ class XfroutServer:
|
||||
else:
|
||||
answer = create_answer(1, "Bad command parameter:" + str(args))
|
||||
|
||||
# return statistics data to the stats daemon
|
||||
elif cmd == "getstats":
|
||||
# The log level is here set to debug in order to avoid
|
||||
# that a log becomes too verbose. Because the b10-stats
|
||||
# daemon is periodically asking to the b10-xfrout daemon.
|
||||
logger.debug(DBG_XFROUT_TRACE, \
|
||||
XFROUT_RECEIVED_GETSTATS_COMMAND)
|
||||
answer = create_answer(0, self._counter.get_statistics())
|
||||
|
||||
else:
|
||||
answer = create_answer(1, "Unknown command:" + str(cmd))
|
||||
|
||||
|
@@ -114,6 +114,65 @@
|
||||
"item_default": "IN"
|
||||
} ]
|
||||
}
|
||||
],
|
||||
"statistics": [
|
||||
{
|
||||
"item_name": "zones",
|
||||
"item_type": "named_set",
|
||||
"item_optional": false,
|
||||
"item_default": {
|
||||
"_SERVER_" : {
|
||||
"notifyoutv4" : 0,
|
||||
"notifyoutv6" : 0,
|
||||
"xfrrej" : 0,
|
||||
"xfrreqdone" : 0
|
||||
}
|
||||
},
|
||||
"item_title": "Zone names",
|
||||
"item_description": "Zone names for Xfrout statistics",
|
||||
"named_set_item_spec": {
|
||||
"item_name": "zonename",
|
||||
"item_type": "map",
|
||||
"item_optional": false,
|
||||
"item_default": {},
|
||||
"item_title": "Zone name",
|
||||
"item_description": "Zone name for Xfrout statistics",
|
||||
"map_item_spec": [
|
||||
{
|
||||
"item_name": "notifyoutv4",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 0,
|
||||
"item_title": "IPv4 notifies",
|
||||
"item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
|
||||
},
|
||||
{
|
||||
"item_name": "notifyoutv6",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 0,
|
||||
"item_title": "IPv6 notifies",
|
||||
"item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
|
||||
},
|
||||
{
|
||||
"item_name": "xfrrej",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 0,
|
||||
"item_title": "XFR rejected requests",
|
||||
"item_description": "Number of XFR requests per zone name rejected by Xfrout"
|
||||
},
|
||||
{
|
||||
"item_name": "xfrreqdone",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 0,
|
||||
"item_title": "Requested zone transfers",
|
||||
"item_description": "Number of requested zone transfers completed per zone name"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -107,6 +107,10 @@ received from the configuration manager.
|
||||
The xfrout daemon received a command on the command channel that
|
||||
NOTIFY packets should be sent for the given zone.
|
||||
|
||||
% XFROUT_RECEIVED_GETSTATS_COMMAND received command to get statistics data
|
||||
The xfrout daemon received a command on the command channel that
|
||||
statistics data should be sent to the stats daemon.
|
||||
|
||||
% XFROUT_PARSE_QUERY_ERROR error parsing query: %1
|
||||
There was a parse error while reading an incoming query. The parse
|
||||
error is shown in the log message. A remote client sent a packet we
|
||||
|
@@ -150,18 +150,15 @@ public:
|
||||
/// Operations within one protocol family are obvious.
|
||||
/// Comparisons between v4 and v6 will allways return v4
|
||||
/// being smaller. This follows boost::asio::ip implementation
|
||||
bool smallerThan(const IOAddress& other) const {
|
||||
if (this->getFamily() < other.getFamily()) {
|
||||
return (true);
|
||||
}
|
||||
if (this->getFamily() > other.getFamily()) {
|
||||
return (false);
|
||||
}
|
||||
if (this->getFamily() == AF_INET6) {
|
||||
return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
|
||||
} else {
|
||||
return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
|
||||
bool lessThan(const IOAddress& other) const {
|
||||
if (this->getFamily() == other.getFamily()) {
|
||||
if (this->getFamily() == AF_INET6) {
|
||||
return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
|
||||
} else {
|
||||
return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
|
||||
}
|
||||
}
|
||||
return (this->getFamily() < other.getFamily());
|
||||
}
|
||||
|
||||
/// \brief Checks if one address is smaller or equal than the other
|
||||
@@ -173,7 +170,7 @@ public:
|
||||
if (equals(other)) {
|
||||
return (true);
|
||||
}
|
||||
return (smallerThan(other));
|
||||
return (lessThan(other));
|
||||
}
|
||||
|
||||
/// \brief Checks if one address is smaller than the other
|
||||
@@ -182,7 +179,7 @@ public:
|
||||
///
|
||||
/// See \ref smaller_than method for details.
|
||||
bool operator<(const IOAddress& other) const {
|
||||
return (smallerThan(other));
|
||||
return (lessThan(other));
|
||||
}
|
||||
|
||||
/// \brief Checks if one address is smaller or equal than the other
|
||||
|
@@ -100,7 +100,7 @@ TEST(IOAddressTest, uint32) {
|
||||
EXPECT_EQ(addr3.toText(), "192.0.2.5");
|
||||
}
|
||||
|
||||
TEST(IOAddressTest, compare) {
|
||||
TEST(IOAddressTest, lessThanEqual) {
|
||||
IOAddress addr1("192.0.2.5");
|
||||
IOAddress addr2("192.0.2.6");
|
||||
IOAddress addr3("0.0.0.0");
|
||||
|
@@ -456,7 +456,7 @@ ModuleCCSession::ModuleCCSession(
|
||||
|
||||
ConstElementPtr answer, env;
|
||||
session_.group_recvmsg(env, answer, false, seq);
|
||||
int rcode;
|
||||
int rcode = -1;
|
||||
ConstElementPtr err = parseAnswer(rcode, answer);
|
||||
if (rcode != 0) {
|
||||
LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
|
||||
@@ -535,7 +535,7 @@ ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) {
|
||||
ConstElementPtr diff = removeIdentical(new_config, getLocalConfig());
|
||||
// handle config update
|
||||
answer = config_handler_(diff);
|
||||
int rcode;
|
||||
int rcode = -1;
|
||||
parseAnswer(rcode, answer);
|
||||
if (rcode == 0) {
|
||||
ElementPtr local_config = getLocalConfig();
|
||||
@@ -652,7 +652,7 @@ ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
|
||||
unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
|
||||
ConstElementPtr env, answer;
|
||||
session_.group_recvmsg(env, answer, false, seq);
|
||||
int rcode;
|
||||
int rcode = -1;
|
||||
ConstElementPtr spec_data = parseAnswer(rcode, answer);
|
||||
if (rcode == 0 && spec_data) {
|
||||
// received OK, construct the spec out of it
|
||||
@@ -689,7 +689,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
|
||||
|
||||
ConstElementPtr env, answer;
|
||||
session_.group_recvmsg(env, answer, false, seq);
|
||||
int rcode;
|
||||
int rcode = -1;
|
||||
ConstElementPtr new_config = parseAnswer(rcode, answer);
|
||||
ElementPtr local_config;
|
||||
if (rcode == 0 && new_config) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = memory . tests
|
||||
SUBDIRS = . memory tests
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
|
||||
@@ -64,7 +64,6 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.
|
||||
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
|
||||
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
|
||||
libb10_datasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library
|
||||
libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
|
||||
|
||||
BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
|
||||
|
2
src/lib/datasrc/memory/.gitignore
vendored
Normal file
2
src/lib/datasrc/memory/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/memory_messages.cc
|
||||
/memory_messages.h
|
@@ -6,7 +6,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
|
||||
CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
|
||||
CLEANFILES = *.gcno *.gcda
|
||||
|
||||
noinst_LTLIBRARIES = libdatasrc_memory.la
|
||||
|
||||
@@ -16,5 +16,17 @@ libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.cc
|
||||
libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
|
||||
libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
|
||||
libdatasrc_memory_la_SOURCES += segment_object_holder.h
|
||||
libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
|
||||
libdatasrc_memory_la_SOURCES += logger.h logger.cc
|
||||
libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
|
||||
libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
|
||||
nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
|
||||
|
||||
EXTRA_DIST = rdata_serialization_priv.cc
|
||||
|
||||
BUILT_SOURCES = memory_messages.h memory_messages.cc
|
||||
memory_messages.h memory_messages.cc: Makefile memory_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/memory/memory_messages.mes
|
||||
|
||||
EXTRA_DIST += memory_messages.mes
|
||||
CLEANFILES += memory_messages.h memory_messages.cc
|
||||
|
1
src/lib/datasrc/memory/benchmarks/.gitignore
vendored
1
src/lib/datasrc/memory/benchmarks/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/rdata_reader_bench
|
||||
/rrset_render_bench
|
||||
|
@@ -1153,9 +1153,11 @@ public:
|
||||
/// Another special feature of this version is the ability to record
|
||||
/// more detailed information regarding the search result.
|
||||
///
|
||||
/// This information will be returned via the \c node_path parameter,
|
||||
/// which is an object of class \c DomainTreeNodeChain.
|
||||
/// The passed parameter must be empty.
|
||||
/// This information will be returned via the \c node_path
|
||||
/// parameter, which is an object of class \c DomainTreeNodeChain.
|
||||
/// The passed parameter must be empty if the label sequence is
|
||||
/// absolute. If the label sequence is not absolute, then find()
|
||||
/// will begin from the top of the node chain.
|
||||
///
|
||||
/// On success, the node sequence stored in \c node_path will contain all
|
||||
/// the ancestor nodes from the found node towards the root.
|
||||
@@ -1462,12 +1464,23 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
|
||||
bool (*callback)(const DomainTreeNode<T>&, CBARG),
|
||||
CBARG callback_arg) const
|
||||
{
|
||||
if (!node_path.isEmpty()) {
|
||||
if (node_path.isEmpty() ^ target_labels_orig.isAbsolute()) {
|
||||
isc_throw(isc::BadValue,
|
||||
"DomainTree::find is given a non empty chain");
|
||||
"DomainTree::find() is given mismatched node chain"
|
||||
" and label sequence");
|
||||
}
|
||||
|
||||
DomainTreeNode<T>* node;
|
||||
|
||||
if (!node_path.isEmpty()) {
|
||||
// Get the top node in the node chain
|
||||
node = const_cast<DomainTreeNode<T>*>(node_path.top());
|
||||
// Start searching from its down pointer
|
||||
node = node->getDown();
|
||||
} else {
|
||||
node = root_.get();
|
||||
}
|
||||
|
||||
DomainTreeNode<T>* node = root_.get();
|
||||
Result ret = NOTFOUND;
|
||||
dns::LabelSequence target_labels(target_labels_orig);
|
||||
|
||||
|
25
src/lib/datasrc/memory/logger.cc
Normal file
25
src/lib/datasrc/memory/logger.cc
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2012 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 <datasrc/memory/logger.h>
|
||||
|
||||
namespace isc {
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
isc::log::Logger logger("datasrc_memory");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
52
src/lib/datasrc/memory/logger.h
Normal file
52
src/lib/datasrc/memory/logger.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2012 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 DATASRC_MEMORY_LOGGER_H
|
||||
#define DATASRC_MEMORY_LOGGER_H
|
||||
|
||||
#include <log/macros.h>
|
||||
#include <datasrc/memory/memory_messages.h>
|
||||
|
||||
/// \file datasrc/memory/logger.h
|
||||
/// \brief Data Source memory library global logger
|
||||
///
|
||||
/// This holds the logger for the data source memory library. It is a
|
||||
/// private header and should not be included in any publicly used
|
||||
/// header, only in local cc files.
|
||||
|
||||
namespace isc {
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
/// \brief The logger for this library
|
||||
extern isc::log::Logger logger;
|
||||
|
||||
/// \brief Trace basic operations
|
||||
const int DBG_TRACE_BASIC = DBGLVL_TRACE_BASIC;
|
||||
|
||||
/// \brief Trace data changes and lookups as well
|
||||
const int DBG_TRACE_DATA = DBGLVL_TRACE_BASIC_DATA;
|
||||
|
||||
/// \brief Detailed even about how the lookups happen
|
||||
const int DBG_TRACE_DETAILED = DBGLVL_TRACE_DETAIL;
|
||||
|
||||
} // namespace memory
|
||||
} // namespace datasrc
|
||||
} // namespace isc
|
||||
|
||||
#endif // DATASRC_MEMORY_LOGGER_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
904
src/lib/datasrc/memory/memory_client.cc
Normal file
904
src/lib/datasrc/memory/memory_client.cc
Normal file
@@ -0,0 +1,904 @@
|
||||
// Copyright (C) 2012 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 <exceptions/exceptions.h>
|
||||
|
||||
#include <datasrc/memory/memory_client.h>
|
||||
#include <datasrc/memory/logger.h>
|
||||
#include <datasrc/memory/zone_data.h>
|
||||
#include <datasrc/memory/rdata_serialization.h>
|
||||
#include <datasrc/memory/rdataset.h>
|
||||
#include <datasrc/memory/domaintree.h>
|
||||
#include <datasrc/memory/segment_object_holder.h>
|
||||
#include <datasrc/memory/treenode_rrset.h>
|
||||
|
||||
#include <util/memory_segment_local.h>
|
||||
|
||||
#include <datasrc/data_source.h>
|
||||
#include <datasrc/factory.h>
|
||||
#include <datasrc/result.h>
|
||||
|
||||
#include <dns/name.h>
|
||||
#include <dns/nsec3hash.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <cctype>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::dns;
|
||||
using namespace isc::dns::rdata;
|
||||
using namespace isc::datasrc::memory;
|
||||
using boost::scoped_ptr;
|
||||
|
||||
namespace isc {
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
using detail::SegmentObjectHolder;
|
||||
|
||||
namespace {
|
||||
// Some type aliases
|
||||
typedef DomainTree<std::string> FileNameTree;
|
||||
typedef DomainTreeNode<std::string> FileNameNode;
|
||||
|
||||
// A functor type used for loading.
|
||||
typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
/// Implementation details for \c InMemoryClient hidden from the public
|
||||
/// interface.
|
||||
///
|
||||
/// For now, \c InMemoryClient only contains a \c ZoneTable object, which
|
||||
/// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
|
||||
/// member variables later for new features.
|
||||
class InMemoryClient::InMemoryClientImpl {
|
||||
private:
|
||||
// The deleter for the filenames stored in the tree.
|
||||
struct FileNameDeleter {
|
||||
FileNameDeleter() {}
|
||||
void operator()(std::string* filename) const {
|
||||
delete filename;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
InMemoryClientImpl(util::MemorySegment& mem_sgmt, RRClass rrclass) :
|
||||
mem_sgmt_(mem_sgmt),
|
||||
rrclass_(rrclass),
|
||||
zone_count_(0),
|
||||
zone_table_(ZoneTable::create(mem_sgmt_, rrclass)),
|
||||
file_name_tree_(FileNameTree::create(mem_sgmt_, false))
|
||||
{}
|
||||
~InMemoryClientImpl() {
|
||||
FileNameDeleter deleter;
|
||||
FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
|
||||
|
||||
ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
|
||||
|
||||
// see above for the assert().
|
||||
assert(mem_sgmt_.allMemoryDeallocated());
|
||||
}
|
||||
|
||||
util::MemorySegment& mem_sgmt_;
|
||||
const RRClass rrclass_;
|
||||
unsigned int zone_count_;
|
||||
ZoneTable* zone_table_;
|
||||
FileNameTree* file_name_tree_;
|
||||
ConstRRsetPtr last_rrset_;
|
||||
|
||||
// Common process for zone load.
|
||||
// rrset_installer is a functor that takes another functor as an argument,
|
||||
// and expected to call the latter for each RRset of the zone. How the
|
||||
// sequence of the RRsets is generated depends on the internal
|
||||
// details of the loader: either from a textual master file or from
|
||||
// another data source.
|
||||
// filename is the file name of the master file or empty if the zone is
|
||||
// loaded from another data source.
|
||||
result::Result load(const Name& zone_name, const string& filename,
|
||||
boost::function<void(LoadCallback)> rrset_installer);
|
||||
|
||||
// Add the necessary magic for any wildcard contained in 'name'
|
||||
// (including itself) to be found in the zone.
|
||||
//
|
||||
// In order for wildcard matching to work correctly in the zone finder,
|
||||
// we must ensure that a node for the wildcarding level exists in the
|
||||
// backend RBTree.
|
||||
// E.g. if the wildcard name is "*.sub.example." then we must ensure
|
||||
// that "sub.example." exists and is marked as a wildcard level.
|
||||
// Note: the "wildcarding level" is for the parent name of the wildcard
|
||||
// name (such as "sub.example.").
|
||||
//
|
||||
// We also perform the same trick for empty wild card names possibly
|
||||
// contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
|
||||
void addWildcards(const Name& zone_name, ZoneData& zone_data,
|
||||
const Name& name)
|
||||
{
|
||||
Name wname(name);
|
||||
const unsigned int labels(wname.getLabelCount());
|
||||
const unsigned int origin_labels(zone_name.getLabelCount());
|
||||
for (unsigned int l = labels;
|
||||
l > origin_labels;
|
||||
--l, wname = wname.split(1)) {
|
||||
if (wname.isWildcard()) {
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
||||
DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);
|
||||
|
||||
// Ensure a separate level exists for the "wildcarding" name,
|
||||
// and mark the node as "wild".
|
||||
ZoneNode* node;
|
||||
zone_data.insertName(mem_sgmt_, wname.split(1), &node);
|
||||
node->setFlag(ZoneData::WILDCARD_NODE);
|
||||
|
||||
// Ensure a separate level exists for the wildcard name.
|
||||
// Note: for 'name' itself we do this later anyway, but the
|
||||
// overhead should be marginal because wildcard names should
|
||||
// be rare.
|
||||
zone_data.insertName(mem_sgmt_, wname, &node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does some checks in context of the data that are already in the zone.
|
||||
* Currently checks for forbidden combinations of RRsets in the same
|
||||
* domain (CNAME+anything, DNAME+NS).
|
||||
*
|
||||
* If such condition is found, it throws AddError.
|
||||
*/
|
||||
void contextCheck(const Name& zone_name, const AbstractRRset& rrset,
|
||||
const RdataSet* set) const {
|
||||
// Ensure CNAME and other type of RR don't coexist for the same
|
||||
// owner name except with NSEC, which is the only RR that can coexist
|
||||
// with CNAME (and also RRSIG, which is handled separately)
|
||||
if (rrset.getType() == RRType::CNAME()) {
|
||||
for (const RdataSet* sp = set; sp != NULL; sp = sp->getNext()) {
|
||||
if (sp->type != RRType::NSEC()) {
|
||||
LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
|
||||
arg(rrset.getName());
|
||||
isc_throw(AddError, "CNAME can't be added with "
|
||||
<< sp->type << " RRType for "
|
||||
<< rrset.getName());
|
||||
}
|
||||
}
|
||||
} else if ((rrset.getType() != RRType::NSEC()) &&
|
||||
(RdataSet::find(set, RRType::CNAME()) != NULL)) {
|
||||
LOG_ERROR(logger,
|
||||
DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
|
||||
isc_throw(AddError, "CNAME and " << rrset.getType() <<
|
||||
" can't coexist for " << rrset.getName());
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar with DNAME, but it must not coexist only with NS and only in
|
||||
* non-apex domains.
|
||||
* RFC 2672 section 3 mentions that it is implied from it and RFC 2181
|
||||
*/
|
||||
if (rrset.getName() != zone_name &&
|
||||
// Adding DNAME, NS already there
|
||||
((rrset.getType() == RRType::DNAME() &&
|
||||
RdataSet::find(set, RRType::NS()) != NULL) ||
|
||||
// Adding NS, DNAME already there
|
||||
(rrset.getType() == RRType::NS() &&
|
||||
RdataSet::find(set, RRType::DNAME()) != NULL)))
|
||||
{
|
||||
LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
|
||||
isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
|
||||
"domain " << rrset.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Validate rrset before adding it to the zone. If something is wrong
|
||||
// it throws an exception. It doesn't modify the zone, and provides
|
||||
// the strong exception guarantee.
|
||||
void addValidation(const Name& zone_name, const ConstRRsetPtr rrset) {
|
||||
if (!rrset) {
|
||||
isc_throw(NullRRset, "The rrset provided is NULL");
|
||||
}
|
||||
if (rrset->getRdataCount() == 0) {
|
||||
isc_throw(AddError, "The rrset provided is empty: " <<
|
||||
rrset->getName() << "/" << rrset->getType());
|
||||
}
|
||||
// Check for singleton RRs. It should probably handled at a different
|
||||
// layer in future.
|
||||
if ((rrset->getType() == RRType::CNAME() ||
|
||||
rrset->getType() == RRType::DNAME()) &&
|
||||
rrset->getRdataCount() > 1)
|
||||
{
|
||||
// XXX: this is not only for CNAME or DNAME. We should generalize
|
||||
// this code for all other "singleton RR types" (such as SOA) in a
|
||||
// separate task.
|
||||
LOG_ERROR(logger,
|
||||
DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
|
||||
arg(rrset->getType());
|
||||
isc_throw(AddError, "multiple RRs of singleton type for "
|
||||
<< rrset->getName());
|
||||
}
|
||||
// NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
|
||||
// implementation requests it be so at the moment.
|
||||
if ((rrset->getType() == RRType::NSEC3() ||
|
||||
rrset->getType() == RRType::NSEC3PARAM()) &&
|
||||
rrset->getRdataCount() > 1) {
|
||||
isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
|
||||
<< rrset->getName() << " which isn't supported");
|
||||
}
|
||||
|
||||
NameComparisonResult compare(zone_name.compare(rrset->getName()));
|
||||
if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
|
||||
compare.getRelation() != NameComparisonResult::EQUAL)
|
||||
{
|
||||
LOG_ERROR(logger,
|
||||
DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
|
||||
arg(zone_name);
|
||||
isc_throw(OutOfZone, "The name " << rrset->getName() <<
|
||||
" is not contained in zone " << zone_name);
|
||||
}
|
||||
|
||||
// Some RR types do not really work well with a wildcard.
|
||||
// Even though the protocol specifically doesn't completely ban such
|
||||
// usage, we refuse to load a zone containing such RR in order to
|
||||
// keep the lookup logic simpler and more predictable.
|
||||
// See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
|
||||
// for more technical background. Note also that BIND 9 refuses
|
||||
// NS at a wildcard, so in that sense we simply provide compatible
|
||||
// behavior.
|
||||
if (rrset->getName().isWildcard()) {
|
||||
if (rrset->getType() == RRType::NS()) {
|
||||
LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
|
||||
arg(rrset->getName());
|
||||
isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
|
||||
rrset->getName());
|
||||
}
|
||||
if (rrset->getType() == RRType::DNAME()) {
|
||||
LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
|
||||
arg(rrset->getName());
|
||||
isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
|
||||
rrset->getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Owner names of NSEC3 have special format as defined in RFC5155,
|
||||
// and cannot be a wildcard name or must be one label longer than
|
||||
// the zone origin. While the RFC doesn't prohibit other forms of
|
||||
// names, no sane zone would have such names for NSEC3.
|
||||
// BIND 9 also refuses NSEC3 at wildcard.
|
||||
if (rrset->getType() == RRType::NSEC3() &&
|
||||
(rrset->getName().isWildcard() ||
|
||||
rrset->getName().getLabelCount() !=
|
||||
zone_name.getLabelCount() + 1)) {
|
||||
LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).
|
||||
arg(rrset->getName());
|
||||
isc_throw(AddError, "Invalid NSEC3 owner name: " <<
|
||||
rrset->getName());
|
||||
}
|
||||
}
|
||||
|
||||
void addNSEC3(const ConstRRsetPtr rrset,
|
||||
const ConstRRsetPtr rrsig,
|
||||
ZoneData& zone_data) {
|
||||
// We know rrset has exactly one RDATA
|
||||
const generic::NSEC3& nsec3_rdata =
|
||||
dynamic_cast<const generic::NSEC3&>(
|
||||
rrset->getRdataIterator()->getCurrent());
|
||||
|
||||
NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
|
||||
if (nsec3_data == NULL) {
|
||||
nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
|
||||
zone_data.setNSEC3Data(nsec3_data);
|
||||
} else {
|
||||
size_t salt_len = nsec3_data->getSaltLen();
|
||||
const uint8_t* salt_data = nsec3_data->getSaltData();
|
||||
const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
|
||||
|
||||
if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
|
||||
(nsec3_rdata.getIterations() != nsec3_data->iterations) ||
|
||||
(salt_data_2.size() != salt_len) ||
|
||||
(std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
|
||||
isc_throw(AddError,
|
||||
"NSEC3 with inconsistent parameters: " <<
|
||||
rrset->toText());
|
||||
}
|
||||
}
|
||||
|
||||
string fst_label = rrset->getName().split(0, 1).toText(true);
|
||||
transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
|
||||
::toupper);
|
||||
|
||||
ZoneNode* node;
|
||||
nsec3_data->insertName(mem_sgmt_, Name(fst_label), &node);
|
||||
|
||||
RdataEncoder encoder;
|
||||
|
||||
// We assume that rrsig has already been checked to match rrset
|
||||
// by the caller.
|
||||
RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
|
||||
RdataSet* old_set = node->setData(set);
|
||||
if (old_set != NULL) {
|
||||
RdataSet::destroy(mem_sgmt_, rrclass_, old_set);
|
||||
}
|
||||
}
|
||||
|
||||
void addRdataSet(const Name& zone_name, ZoneData& zone_data,
|
||||
const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig) {
|
||||
// Only one of these can be passed at a time.
|
||||
assert(!(rrset && rrsig));
|
||||
|
||||
// If rrsig is passed, validate it against the last-saved rrset.
|
||||
if (rrsig) {
|
||||
// The covered RRset should have been saved by now.
|
||||
if (!last_rrset_) {
|
||||
isc_throw(AddError,
|
||||
"RRSIG is being added, "
|
||||
"but doesn't follow its covered RR: "
|
||||
<< rrsig->getName());
|
||||
}
|
||||
|
||||
if (rrsig->getName() != last_rrset_->getName()) {
|
||||
isc_throw(AddError,
|
||||
"RRSIG is being added, "
|
||||
"but doesn't match the last RR's name: "
|
||||
<< last_rrset_->getName() << " vs. "
|
||||
<< rrsig->getName());
|
||||
}
|
||||
|
||||
// Consistency of other types in rrsig are checked in addRRsig().
|
||||
RdataIteratorPtr rit = rrsig->getRdataIterator();
|
||||
const RRType covered = dynamic_cast<const generic::RRSIG&>(
|
||||
rit->getCurrent()).typeCovered();
|
||||
|
||||
if (covered != last_rrset_->getType()) {
|
||||
isc_throw(AddError,
|
||||
"RRSIG is being added, "
|
||||
"but doesn't match the last RR's type: "
|
||||
<< last_rrset_->getType() << " vs. "
|
||||
<< covered);
|
||||
}
|
||||
}
|
||||
|
||||
if (!last_rrset_) {
|
||||
last_rrset_ = rrset;
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_rrset_->getType() == RRType::NSEC3()) {
|
||||
addNSEC3(last_rrset_, rrsig, zone_data);
|
||||
} else {
|
||||
ZoneNode* node;
|
||||
zone_data.insertName(mem_sgmt_, last_rrset_->getName(), &node);
|
||||
|
||||
RdataSet* set = node->getData();
|
||||
|
||||
// Checks related to the surrounding data.
|
||||
// Note: when the check fails and the exception is thrown,
|
||||
// it may break strong exception guarantee. At the moment
|
||||
// we prefer code simplicity and don't bother to introduce
|
||||
// complicated recovery code.
|
||||
contextCheck(zone_name, *last_rrset_, set);
|
||||
|
||||
if (RdataSet::find(set, last_rrset_->getType()) != NULL) {
|
||||
isc_throw(AddError,
|
||||
"RRset of the type already exists: "
|
||||
<< last_rrset_->getName() << " (type: "
|
||||
<< last_rrset_->getType() << ")");
|
||||
}
|
||||
|
||||
RdataEncoder encoder;
|
||||
RdataSet *new_set = RdataSet::create(mem_sgmt_, encoder,
|
||||
last_rrset_, rrsig);
|
||||
new_set->next = set;
|
||||
node->setData(new_set);
|
||||
|
||||
// Ok, we just put it in
|
||||
|
||||
// If this RRset creates a zone cut at this node, mark the
|
||||
// node indicating the need for callback in find().
|
||||
if (last_rrset_->getType() == RRType::NS() &&
|
||||
last_rrset_->getName() != zone_name) {
|
||||
node->setFlag(ZoneNode::FLAG_CALLBACK);
|
||||
// If it is DNAME, we have a callback as well here
|
||||
} else if (last_rrset_->getType() == RRType::DNAME()) {
|
||||
node->setFlag(ZoneNode::FLAG_CALLBACK);
|
||||
}
|
||||
|
||||
// If we've added NSEC3PARAM at zone origin, set up NSEC3
|
||||
// specific data or check consistency with already set up
|
||||
// parameters.
|
||||
if (last_rrset_->getType() == RRType::NSEC3PARAM() &&
|
||||
last_rrset_->getName() == zone_name) {
|
||||
// We know rrset has exactly one RDATA
|
||||
const generic::NSEC3PARAM& param =
|
||||
dynamic_cast<const generic::NSEC3PARAM&>
|
||||
(last_rrset_->getRdataIterator()->getCurrent());
|
||||
|
||||
NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
|
||||
if (nsec3_data == NULL) {
|
||||
nsec3_data = NSEC3Data::create(mem_sgmt_, param);
|
||||
zone_data.setNSEC3Data(nsec3_data);
|
||||
} else {
|
||||
size_t salt_len = nsec3_data->getSaltLen();
|
||||
const uint8_t* salt_data = nsec3_data->getSaltData();
|
||||
const vector<uint8_t>& salt_data_2 = param.getSalt();
|
||||
|
||||
if ((param.getHashalg() != nsec3_data->hashalg) ||
|
||||
(param.getIterations() != nsec3_data->iterations) ||
|
||||
(salt_data_2.size() != salt_len) ||
|
||||
(std::memcmp(&salt_data_2[0],
|
||||
salt_data, salt_len) != 0)) {
|
||||
isc_throw(AddError,
|
||||
"NSEC3PARAM with inconsistent parameters: "
|
||||
<< last_rrset_->toText());
|
||||
}
|
||||
}
|
||||
} else if (last_rrset_->getType() == RRType::NSEC()) {
|
||||
// If it is NSEC signed zone, so we put a flag there
|
||||
// (flag is enough)
|
||||
zone_data.setSigned(true);
|
||||
}
|
||||
}
|
||||
|
||||
last_rrset_ = rrset;
|
||||
}
|
||||
|
||||
result::Result addRRsig(const ConstRRsetPtr sig_rrset,
|
||||
const Name& zone_name, ZoneData& zone_data)
|
||||
{
|
||||
// Check consistency of the type covered.
|
||||
// We know the RRset isn't empty, so the following check is safe.
|
||||
RdataIteratorPtr rit = sig_rrset->getRdataIterator();
|
||||
const RRType covered = dynamic_cast<const generic::RRSIG&>(
|
||||
rit->getCurrent()).typeCovered();
|
||||
for (rit->next(); !rit->isLast(); rit->next()) {
|
||||
if (dynamic_cast<const generic::RRSIG&>(
|
||||
rit->getCurrent()).typeCovered() != covered) {
|
||||
isc_throw(AddError, "RRSIG contains mixed covered types: "
|
||||
<< sig_rrset->toText());
|
||||
}
|
||||
}
|
||||
|
||||
addRdataSet(zone_name, zone_data, ConstRRsetPtr(), sig_rrset);
|
||||
return (result::SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of longer methods. We put them here, because the
|
||||
* access is without the impl_-> and it will get inlined anyway.
|
||||
*/
|
||||
|
||||
// Implementation of InMemoryClient::add()
|
||||
result::Result add(const ConstRRsetPtr& rrset,
|
||||
const Name& zone_name, ZoneData& zone_data)
|
||||
{
|
||||
// Sanitize input. This will cause an exception to be thrown
|
||||
// if the input RRset is empty.
|
||||
addValidation(zone_name, rrset);
|
||||
|
||||
// OK, can add the RRset.
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
|
||||
arg(rrset->getName()).arg(rrset->getType()).arg(zone_name);
|
||||
|
||||
if (rrset->getType() == RRType::NSEC3()) {
|
||||
addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
|
||||
return (result::SUCCESS);
|
||||
}
|
||||
|
||||
// RRSIGs are special in various points, so we handle it in a
|
||||
// separate dedicated method.
|
||||
if (rrset->getType() == RRType::RRSIG()) {
|
||||
return (addRRsig(rrset, zone_name, zone_data));
|
||||
}
|
||||
|
||||
// Add wildcards possibly contained in the owner name to the domain
|
||||
// tree.
|
||||
// Note: this can throw an exception, breaking strong exception
|
||||
// guarantee. (see also the note for contextCheck() below).
|
||||
addWildcards(zone_name, zone_data, rrset->getName());
|
||||
|
||||
addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
|
||||
|
||||
return (result::SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper around above.
|
||||
*/
|
||||
void addFromLoad(const ConstRRsetPtr& set,
|
||||
const Name& zone_name, ZoneData* zone_data)
|
||||
{
|
||||
switch (add(set, zone_name, *zone_data)) {
|
||||
case result::SUCCESS:
|
||||
return;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
result::Result
|
||||
InMemoryClient::InMemoryClientImpl::load(
|
||||
const Name& zone_name,
|
||||
const string& filename,
|
||||
boost::function<void(LoadCallback)> rrset_installer)
|
||||
{
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name),
|
||||
rrclass_);
|
||||
|
||||
assert(!last_rrset_);
|
||||
|
||||
try {
|
||||
rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this,
|
||||
_1, zone_name, holder.get()));
|
||||
// Add any last RRset that was left
|
||||
addRdataSet(zone_name, *holder.get(),
|
||||
ConstRRsetPtr(), ConstRRsetPtr());
|
||||
} catch (...) {
|
||||
last_rrset_ = ConstRRsetPtr();
|
||||
throw;
|
||||
}
|
||||
|
||||
assert(!last_rrset_);
|
||||
|
||||
const ZoneNode* origin_node = holder.get()->getOriginNode();
|
||||
const RdataSet* set = origin_node->getData();
|
||||
// If the zone is NSEC3-signed, check if it has NSEC3PARAM
|
||||
if (holder.get()->isNSEC3Signed()) {
|
||||
// Note: origin_data_ is set on creation of ZoneData, and the load
|
||||
// process only adds new nodes (and their data), so this assertion
|
||||
// should hold.
|
||||
if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) {
|
||||
LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
|
||||
arg(zone_name).arg(rrclass_);
|
||||
}
|
||||
}
|
||||
|
||||
// When an empty zone file is loaded, the origin doesn't even have
|
||||
// an SOA RR. This condition should be avoided, and hence load()
|
||||
// should throw when an empty zone is loaded.
|
||||
if (RdataSet::find(set, RRType::SOA()) == NULL) {
|
||||
isc_throw(EmptyZone,
|
||||
"Won't create an empty zone for: " << zone_name);
|
||||
}
|
||||
|
||||
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
|
||||
arg(zone_name).arg(rrclass_.toText());
|
||||
|
||||
// Set the filename in file_name_tree_ now, so that getFileName()
|
||||
// can use it (during zone reloading).
|
||||
FileNameNode* node(NULL);
|
||||
switch (file_name_tree_->insert(mem_sgmt_, zone_name, &node)) {
|
||||
case FileNameTree::SUCCESS:
|
||||
case FileNameTree::ALREADYEXISTS:
|
||||
// These are OK
|
||||
break;
|
||||
default:
|
||||
// Can Not Happen
|
||||
assert(false);
|
||||
}
|
||||
// node must point to a valid node now
|
||||
assert(node != NULL);
|
||||
|
||||
std::string* tstr = node->setData(new std::string(filename));
|
||||
delete tstr;
|
||||
|
||||
ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_,
|
||||
zone_name));
|
||||
if (result.code == result::SUCCESS) {
|
||||
// Only increment the zone count if the zone doesn't already
|
||||
// exist.
|
||||
++zone_count_;
|
||||
}
|
||||
|
||||
ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name,
|
||||
holder.release()));
|
||||
assert(fr.code == result::SUCCESS);
|
||||
if (fr.zone_data != NULL) {
|
||||
ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_);
|
||||
}
|
||||
|
||||
return (result.code);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A wrapper for dns::masterLoad used by load() below. Essentially it
|
||||
// converts the two callback types. Note the mostly redundant wrapper of
|
||||
// boost::bind. It converts function<void(ConstRRsetPtr)> to
|
||||
// function<void(RRsetPtr)> (masterLoad() expects the latter). SunStudio
|
||||
// doesn't seem to do this conversion if we just pass 'callback'.
|
||||
void
|
||||
masterLoadWrapper(const char* const filename, const Name& origin,
|
||||
const RRClass& zone_class, LoadCallback callback)
|
||||
{
|
||||
masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
|
||||
}
|
||||
|
||||
// The installer called from Impl::load() for the iterator version of load().
|
||||
void
|
||||
generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
|
||||
ConstRRsetPtr rrset;
|
||||
vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
|
||||
|
||||
// The current internal implementation assumes an RRSIG is always added
|
||||
// after the RRset they cover. So we store any RRSIGs in 'rrsigs' until
|
||||
// it's safe to add them; based on our assumption if the owner name
|
||||
// changes, all covered RRsets of the previous name should have been
|
||||
// installed and any pending RRSIGs can be added at that point. RRSIGs
|
||||
// of the last name from the iterator must be added separately.
|
||||
while ((rrset = iterator->getNextRRset()) != NULL) {
|
||||
if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
|
||||
BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
|
||||
callback(sig_rrset);
|
||||
}
|
||||
rrsigs.clear();
|
||||
}
|
||||
if (rrset->getType() == RRType::RRSIG()) {
|
||||
rrsigs.push_back(rrset);
|
||||
} else {
|
||||
callback(rrset);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
|
||||
callback(sig_rrset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InMemoryClient::InMemoryClient(util::MemorySegment& mem_sgmt,
|
||||
RRClass rrclass) :
|
||||
impl_(new InMemoryClientImpl(mem_sgmt, rrclass))
|
||||
{}
|
||||
|
||||
InMemoryClient::~InMemoryClient() {
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
RRClass
|
||||
InMemoryClient::getClass() const {
|
||||
return (impl_->rrclass_);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
InMemoryClient::getZoneCount() const {
|
||||
return (impl_->zone_count_);
|
||||
}
|
||||
|
||||
isc::datasrc::memory::ZoneTable::FindResult
|
||||
InMemoryClient::findZone2(const isc::dns::Name& zone_name) const {
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
||||
DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
|
||||
ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc::datasrc::DataSourceClient::FindResult
|
||||
InMemoryClient::findZone(const isc::dns::Name&) const {
|
||||
// This variant of findZone() is not implemented and should be
|
||||
// removed eventually. It currently throws an exception. It is
|
||||
// required right now to derive from DataSourceClient.
|
||||
isc_throw(isc::NotImplemented,
|
||||
"This variant of findZone() is not implemented.");
|
||||
}
|
||||
|
||||
result::Result
|
||||
InMemoryClient::load(const isc::dns::Name& zone_name,
|
||||
const std::string& filename) {
|
||||
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_LOAD).arg(zone_name).
|
||||
arg(filename);
|
||||
|
||||
return (impl_->load(zone_name, filename,
|
||||
boost::bind(masterLoadWrapper, filename.c_str(),
|
||||
zone_name, getClass(), _1)));
|
||||
}
|
||||
|
||||
result::Result
|
||||
InMemoryClient::load(const isc::dns::Name& zone_name,
|
||||
ZoneIterator& iterator) {
|
||||
return (impl_->load(zone_name, string(),
|
||||
boost::bind(generateRRsetFromIterator,
|
||||
&iterator, _1)));
|
||||
}
|
||||
|
||||
const std::string
|
||||
InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
|
||||
FileNameNode* node(NULL);
|
||||
FileNameTree::Result result = impl_->file_name_tree_->find(zone_name,
|
||||
&node);
|
||||
if (result == FileNameTree::EXACTMATCH) {
|
||||
return (*node->getData());
|
||||
} else {
|
||||
return (std::string());
|
||||
}
|
||||
}
|
||||
|
||||
result::Result
|
||||
InMemoryClient::add(const isc::dns::Name& zone_name,
|
||||
const ConstRRsetPtr& rrset) {
|
||||
assert(!impl_->last_rrset_);
|
||||
|
||||
ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
|
||||
if (result.code != result::SUCCESS) {
|
||||
isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
|
||||
}
|
||||
|
||||
result::Result ret(impl_->add(rrset, zone_name, *result.zone_data));
|
||||
// Add any associated RRSIG too. This has to be done here, as both
|
||||
// the RRset and its RRSIG have to be passed when constructing an
|
||||
// RdataSet.
|
||||
if ((ret == result::SUCCESS) && rrset->getRRsig()) {
|
||||
impl_->add(rrset->getRRsig(), zone_name, *result.zone_data);
|
||||
}
|
||||
|
||||
// Add any last RRset that was left
|
||||
impl_->addRdataSet(zone_name, *result.zone_data,
|
||||
ConstRRsetPtr(), ConstRRsetPtr());
|
||||
|
||||
assert(!impl_->last_rrset_);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MemoryIterator : public ZoneIterator {
|
||||
private:
|
||||
ZoneChain chain_;
|
||||
const RdataSet* set_node_;
|
||||
const RRClass rrclass_;
|
||||
const ZoneTree& tree_;
|
||||
const ZoneNode* node_;
|
||||
// Only used when separate_rrs_ is true
|
||||
ConstRRsetPtr rrset_;
|
||||
RdataIteratorPtr rdata_iterator_;
|
||||
bool separate_rrs_;
|
||||
bool ready_;
|
||||
public:
|
||||
MemoryIterator(const RRClass rrclass,
|
||||
const ZoneTree& tree, const Name& origin,
|
||||
bool separate_rrs) :
|
||||
rrclass_(rrclass),
|
||||
tree_(tree),
|
||||
separate_rrs_(separate_rrs),
|
||||
ready_(true)
|
||||
{
|
||||
// Find the first node (origin) and preserve the node chain for future
|
||||
// searches
|
||||
ZoneTree::Result result(tree_.find(origin, &node_, chain_));
|
||||
// It can't happen that the origin is not in there
|
||||
if (result != ZoneTree::EXACTMATCH) {
|
||||
isc_throw(Unexpected,
|
||||
"In-memory zone corrupted, missing origin node");
|
||||
}
|
||||
// Initialize the iterator if there's somewhere to point to
|
||||
if (node_ != NULL && node_->getData() != NULL) {
|
||||
set_node_ = node_->getData();
|
||||
if (separate_rrs_ && set_node_ != NULL) {
|
||||
rrset_.reset(new TreeNodeRRset(rrclass_,
|
||||
node_, set_node_, true));
|
||||
rdata_iterator_ = rrset_->getRdataIterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual ConstRRsetPtr getNextRRset() {
|
||||
if (!ready_) {
|
||||
isc_throw(Unexpected, "Iterating past the zone end");
|
||||
}
|
||||
/*
|
||||
* This cycle finds the first nonempty node with yet unused
|
||||
* RdataSset. If it is NULL, we run out of nodes. If it is
|
||||
* empty, it doesn't contain any RdataSets. If we are at the
|
||||
* end, just get to next one.
|
||||
*/
|
||||
while (node_ != NULL &&
|
||||
(node_->getData() == NULL || set_node_ == NULL)) {
|
||||
node_ = tree_.nextNode(chain_);
|
||||
// If there's a node, initialize the iterator and check next time
|
||||
// if the map is empty or not
|
||||
if (node_ != NULL && node_->getData() != NULL) {
|
||||
set_node_ = node_->getData();
|
||||
// New RRset, so get a new rdata iterator
|
||||
if (separate_rrs_ && set_node_ != NULL) {
|
||||
rrset_.reset(new TreeNodeRRset(rrclass_,
|
||||
node_, set_node_, true));
|
||||
rdata_iterator_ = rrset_->getRdataIterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node_ == NULL) {
|
||||
// That's all, folks
|
||||
ready_ = false;
|
||||
return (ConstRRsetPtr());
|
||||
}
|
||||
|
||||
if (separate_rrs_) {
|
||||
// For separate rrs, reconstruct a new RRset with just the
|
||||
// 'current' rdata
|
||||
RRsetPtr result(new RRset(rrset_->getName(),
|
||||
rrset_->getClass(),
|
||||
rrset_->getType(),
|
||||
rrset_->getTTL()));
|
||||
result->addRdata(rdata_iterator_->getCurrent());
|
||||
rdata_iterator_->next();
|
||||
if (rdata_iterator_->isLast()) {
|
||||
// all used up, next.
|
||||
set_node_ = set_node_->getNext();
|
||||
// New RRset, so get a new rdata iterator, but only if this
|
||||
// was not the final RRset in the chain
|
||||
if (set_node_ != NULL) {
|
||||
rrset_.reset(new TreeNodeRRset(rrclass_,
|
||||
node_, set_node_, true));
|
||||
rdata_iterator_ = rrset_->getRdataIterator();
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
} else {
|
||||
ConstRRsetPtr result(new TreeNodeRRset(rrclass_,
|
||||
node_, set_node_, true));
|
||||
|
||||
// This one is used, move it to the next time for next call
|
||||
set_node_ = set_node_->getNext();
|
||||
|
||||
return (result);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ConstRRsetPtr getSOA() const {
|
||||
isc_throw(NotImplemented, "Not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
ZoneIteratorPtr
|
||||
InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
|
||||
ZoneTable::FindResult result(impl_->zone_table_->findZone(name));
|
||||
if (result.code != result::SUCCESS) {
|
||||
isc_throw(DataSourceError, "No such zone: " + name.toText());
|
||||
}
|
||||
|
||||
return (ZoneIteratorPtr(new MemoryIterator(
|
||||
getClass(),
|
||||
result.zone_data->getZoneTree(), name,
|
||||
separate_rrs)));
|
||||
}
|
||||
|
||||
ZoneUpdaterPtr
|
||||
InMemoryClient::getUpdater(const isc::dns::Name&, bool, bool) const {
|
||||
isc_throw(isc::NotImplemented, "Update attempt on in memory data source");
|
||||
}
|
||||
|
||||
pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
|
||||
InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
|
||||
uint32_t) const
|
||||
{
|
||||
isc_throw(isc::NotImplemented, "Journaling isn't supported for "
|
||||
"in memory data source");
|
||||
}
|
||||
|
||||
} // end of namespace memory
|
||||
} // end of namespace datasrc
|
||||
} // end of namespace isc
|
257
src/lib/datasrc/memory/memory_client.h
Normal file
257
src/lib/datasrc/memory/memory_client.h
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright (C) 2012 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 DATASRC_MEMORY_CLIENT_H
|
||||
#define DATASRC_MEMORY_CLIENT_H 1
|
||||
|
||||
#include <util/memory_segment.h>
|
||||
|
||||
#include <datasrc/iterator.h>
|
||||
#include <datasrc/client.h>
|
||||
#include <datasrc/memory/zone_table.h>
|
||||
|
||||
// for isc::datasrc::ZoneTable::FindResult returned by findZone(). This
|
||||
// variant of findZone() is not implemented and should be removed
|
||||
// eventually.
|
||||
#include <datasrc/zonetable.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace isc {
|
||||
|
||||
namespace dns {
|
||||
class Name;
|
||||
class RRsetList;
|
||||
};
|
||||
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
/// \brief A data source client that holds all necessary data in memory.
|
||||
///
|
||||
/// The \c InMemoryClient class provides an access to a conceptual data
|
||||
/// source that maintains all necessary data in a memory image, thereby
|
||||
/// allowing much faster lookups. The in memory data is a copy of some
|
||||
/// real physical source - in the current implementation a list of zones
|
||||
/// are populated as a result of \c load() calls; zone data is given in
|
||||
/// a standard master file, or as an iterator of some other datasource
|
||||
/// including database backed ones.
|
||||
///
|
||||
/// The InMemoryClient enforces through its interface that all data
|
||||
/// loaded to the data source is of the same RR class. For example, the
|
||||
/// \c load() method assumes that the zone being loaded belongs to the
|
||||
/// same RR class as the memory::Client instance.
|
||||
class InMemoryClient : public DataSourceClient {
|
||||
public:
|
||||
///
|
||||
/// \name Constructors and Destructor.
|
||||
///
|
||||
//@{
|
||||
|
||||
/// Default constructor.
|
||||
///
|
||||
/// This constructor internally involves resource allocation, and if
|
||||
/// it fails, a corresponding standard exception will be thrown.
|
||||
/// It never throws an exception otherwise.
|
||||
InMemoryClient(util::MemorySegment& mem_sgmt,
|
||||
isc::dns::RRClass rrclass);
|
||||
|
||||
/// The destructor.
|
||||
~InMemoryClient();
|
||||
//@}
|
||||
|
||||
/// \brief Returns the class of the data source client.
|
||||
virtual isc::dns::RRClass getClass() const;
|
||||
|
||||
/// Return the number of zones stored in the client.
|
||||
///
|
||||
/// This method never throws an exception.
|
||||
///
|
||||
/// \return The number of zones stored in the client.
|
||||
virtual unsigned int getZoneCount() const;
|
||||
|
||||
/// \brief Load zone from masterfile.
|
||||
///
|
||||
/// This loads data from masterfile specified by filename. It replaces
|
||||
/// current content. The masterfile parsing ability is kind of limited,
|
||||
/// see isc::dns::masterLoad.
|
||||
///
|
||||
/// This throws isc::dns::MasterLoadError if there is problem with loading
|
||||
/// (missing file, malformed, it contains different zone, etc - see
|
||||
/// isc::dns::masterLoad for details).
|
||||
///
|
||||
/// In case of internal problems, OutOfZone, NullRRset or AssertError could
|
||||
/// be thrown, but they should not be expected. Exceptions caused by
|
||||
/// allocation may be thrown as well.
|
||||
///
|
||||
/// If anything is thrown, the previous content is preserved (so it can
|
||||
/// be used to update the data, but if user makes a typo, the old one
|
||||
/// is kept).
|
||||
///
|
||||
/// \param filename The master file to load.
|
||||
///
|
||||
/// \todo We may need to split it to some kind of build and commit/abort.
|
||||
/// This will probably be needed when a better implementation of
|
||||
/// configuration reloading is written.
|
||||
result::Result load(const isc::dns::Name& zone_name,
|
||||
const std::string& filename);
|
||||
|
||||
/// \brief Load zone from another data source.
|
||||
///
|
||||
/// This is similar to the other version, but zone's RRsets are provided
|
||||
/// by an iterator of another data source. On successful load, the
|
||||
/// internal filename will be cleared.
|
||||
///
|
||||
/// This implementation assumes the iterator produces combined RRsets,
|
||||
/// that is, there should exactly one RRset for the same owner name and
|
||||
/// RR type. This means the caller is expected to create the iterator
|
||||
/// with \c separate_rrs being \c false. This implementation also assumes
|
||||
/// RRsets of different names are not mixed; so if the iterator produces
|
||||
/// an RRset of a different name than that of the previous RRset, that
|
||||
/// previous name must never appear in the subsequent sequence of RRsets.
|
||||
/// Note that the iterator API does not ensure this. If the underlying
|
||||
/// implementation does not follow it, load() will fail. Note, however,
|
||||
/// that this whole interface is tentative. in-memory zone loading will
|
||||
/// have to be revisited fundamentally, and at that point this restriction
|
||||
/// probably won't matter.
|
||||
result::Result load(const isc::dns::Name& zone_name,
|
||||
ZoneIterator& iterator);
|
||||
|
||||
/// Return the master file name of the zone
|
||||
///
|
||||
/// This method returns the name of the zone's master file to be loaded.
|
||||
/// The returned string will be an empty unless the data source client has
|
||||
/// successfully loaded the \c zone_name zone from a file before.
|
||||
///
|
||||
/// This method should normally not throw an exception. But the creation
|
||||
/// of the return string may involve a resource allocation, and if it
|
||||
/// fails, the corresponding standard exception will be thrown.
|
||||
///
|
||||
/// \return The name of the zone file corresponding to the zone, or
|
||||
/// an empty string if the client hasn't loaded the \c zone_name
|
||||
/// zone from a file before.
|
||||
const std::string getFileName(const isc::dns::Name& zone_name) const;
|
||||
|
||||
/// \brief Inserts an rrset into the zone.
|
||||
///
|
||||
/// It puts another RRset into the zone.
|
||||
///
|
||||
/// In the current implementation, this method doesn't allow an existing
|
||||
/// RRset to be updated or overridden. So the caller must make sure that
|
||||
/// all RRs of the same type and name must be given in the form of a
|
||||
/// single RRset. The current implementation will also require that
|
||||
/// when an RRSIG is added, the RRset to be covered has already been
|
||||
/// added. These restrictions are probably too strict when this data
|
||||
/// source accepts various forms of input, so they should be revisited
|
||||
/// later.
|
||||
///
|
||||
/// Except for NullRRset and OutOfZone, this method does not guarantee
|
||||
/// strong exception safety (it is currently not needed, if it is needed
|
||||
/// in future, it should be implemented).
|
||||
///
|
||||
/// \throw NullRRset \c rrset is a NULL pointer.
|
||||
/// \throw OutOfZone The owner name of \c rrset is outside of the
|
||||
/// origin of the zone.
|
||||
/// \throw AddError Other general errors.
|
||||
/// \throw Others This method might throw standard allocation exceptions.
|
||||
///
|
||||
/// \param rrset The set to add.
|
||||
/// \return SUCCESS or EXIST (if an rrset for given name and type already
|
||||
/// exists).
|
||||
result::Result add(const isc::dns::Name& zone_name,
|
||||
const isc::dns::ConstRRsetPtr& rrset);
|
||||
|
||||
/// \brief RRset is NULL exception.
|
||||
///
|
||||
/// This is thrown if the provided RRset parameter is NULL.
|
||||
struct NullRRset : public InvalidParameter {
|
||||
NullRRset(const char* file, size_t line, const char* what) :
|
||||
InvalidParameter(file, line, what)
|
||||
{ }
|
||||
};
|
||||
|
||||
/// \brief Zone is empty exception.
|
||||
///
|
||||
/// This is thrown if we have an empty zone created as a result of
|
||||
/// load().
|
||||
struct EmptyZone : public InvalidParameter {
|
||||
EmptyZone(const char* file, size_t line, const char* what) :
|
||||
InvalidParameter(file, line, what)
|
||||
{ }
|
||||
};
|
||||
|
||||
/// \brief General failure exception for \c add().
|
||||
///
|
||||
/// This is thrown against general error cases in adding an RRset
|
||||
/// to the zone.
|
||||
///
|
||||
/// Note: this exception would cover cases for \c OutOfZone or
|
||||
/// \c NullRRset. We'll need to clarify and unify the granularity
|
||||
/// of exceptions eventually. For now, exceptions are added as
|
||||
/// developers see the need for it.
|
||||
struct AddError : public InvalidParameter {
|
||||
AddError(const char* file, size_t line, const char* what) :
|
||||
InvalidParameter(file, line, what)
|
||||
{ }
|
||||
};
|
||||
|
||||
/// Returns a \c ZoneTable result that best matches the given name.
|
||||
///
|
||||
/// This derived version of the method never throws an exception.
|
||||
/// For other details see \c DataSourceClient::findZone().
|
||||
virtual isc::datasrc::memory::ZoneTable::FindResult
|
||||
findZone2(const isc::dns::Name& name) const;
|
||||
|
||||
// This variant of findZone() is not implemented and should be
|
||||
// removed eventually. It currently throws an exception. It is
|
||||
// required right now to derive from DataSourceClient.
|
||||
virtual isc::datasrc::DataSourceClient::FindResult
|
||||
findZone(const isc::dns::Name& name) const;
|
||||
|
||||
/// \brief Implementation of the getIterator method
|
||||
virtual isc::datasrc::ZoneIteratorPtr
|
||||
getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;
|
||||
|
||||
/// In-memory data source doesn't write back persistently, so this
|
||||
/// derived method will result in a NotImplemented exception.
|
||||
///
|
||||
/// \note We plan to use a database-based data source as a backend
|
||||
/// persistent storage for an in-memory data source. When it's
|
||||
/// implemented we may also want to allow the user of the in-memory client
|
||||
/// to update via its updater (this may or may not be a good idea and
|
||||
/// is subject to further discussions).
|
||||
virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name,
|
||||
bool replace, bool journaling = false)
|
||||
const;
|
||||
|
||||
virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
|
||||
getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
|
||||
uint32_t end_serial) const;
|
||||
|
||||
private:
|
||||
// TODO: Do we still need the PImpl if nobody should manipulate this class
|
||||
// directly any more (it should be handled through DataSourceClient)?
|
||||
class InMemoryClientImpl;
|
||||
InMemoryClientImpl* impl_;
|
||||
};
|
||||
|
||||
} // namespace memory
|
||||
} // namespace datasrc
|
||||
} // namespace isc
|
||||
|
||||
#endif // DATASRC_MEMORY_CLIENT_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
90
src/lib/datasrc/memory/memory_messages.mes
Normal file
90
src/lib/datasrc/memory/memory_messages.mes
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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.
|
||||
|
||||
$NAMESPACE isc::datasrc::memory
|
||||
|
||||
# \brief Messages for the data source memory library
|
||||
|
||||
% DATASRC_MEMORY_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'
|
||||
The software refuses to load NSEC3 records into a wildcard domain or
|
||||
the owner name has two or more labels below the zone origin.
|
||||
It isn't explicitly forbidden, but no sane zone wouldn have such names
|
||||
for NSEC3. BIND 9 also refuses NSEC3 at wildcard, so this behavior is
|
||||
compatible with BIND 9.
|
||||
|
||||
% DATASRC_MEMORY_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
|
||||
Debug information. An RRset is being added to the in-memory data source.
|
||||
|
||||
% DATASRC_MEMORY_MEM_ADD_WILDCARD adding wildcards for '%1'
|
||||
This is a debug message issued during the processing of a wildcard
|
||||
name. The internal domain name tree is scanned and some nodes are
|
||||
specially marked to allow the wildcard lookup to succeed.
|
||||
|
||||
% DATASRC_MEMORY_MEM_ADD_ZONE adding zone '%1/%2'
|
||||
Debug information. A zone is being added into the in-memory data source.
|
||||
|
||||
% DATASRC_MEMORY_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'
|
||||
This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
|
||||
other way around -- adding some other data to CNAME.
|
||||
|
||||
% DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'
|
||||
Someone or something tried to add a CNAME into a domain that already contains
|
||||
some other data. But the protocol forbids coexistence of CNAME with anything
|
||||
(RFC 1034, section 3.6.2). This indicates a problem with provided data.
|
||||
|
||||
% DATASRC_MEMORY_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'
|
||||
A request was made for DNAME and NS records to be put into the same
|
||||
domain which is not the apex (the top of the zone). This is forbidden
|
||||
by RFC 2672 (section 3) and indicates a problem with provided data.
|
||||
|
||||
% DATASRC_MEMORY_MEM_DUP_RRSET duplicate RRset '%1/%2'
|
||||
An RRset is being inserted into in-memory data source for a second time. The
|
||||
original version must be removed first. Note that loading master files where an
|
||||
RRset is split into multiple locations is not supported yet.
|
||||
|
||||
% DATASRC_MEMORY_MEM_FIND_ZONE looking for zone '%1'
|
||||
Debug information. A zone object for this zone is being searched for in the
|
||||
in-memory data source.
|
||||
|
||||
% DATASRC_MEMORY_MEM_LOAD loading zone '%1' from file '%2'
|
||||
Debug information. The content of master file is being loaded into the memory.
|
||||
|
||||
% DATASRC_MEMORY_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2
|
||||
The in-memory data source has loaded a zone signed with NSEC3 RRs,
|
||||
but it doesn't have a NSEC3PARAM RR at the zone origin. It's likely that
|
||||
the zone is somehow broken, but this RR is not necessarily needed for
|
||||
handling lookups with NSEC3 in this data source, so it accepts the given
|
||||
content of the zone. Nevertheless the administrator should look into
|
||||
the integrity of the zone data.
|
||||
|
||||
% DATASRC_MEMORY_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'
|
||||
It was attempted to add the domain into a zone that shouldn't have it
|
||||
(eg. the domain is not subdomain of the zone origin). This indicates a
|
||||
problem with provided data.
|
||||
|
||||
% DATASRC_MEMORY_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'
|
||||
Some resource types are singletons -- only one is allowed in a domain
|
||||
(for example CNAME or SOA). This indicates a problem with provided data.
|
||||
|
||||
% DATASRC_MEMORY_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1'
|
||||
The software refuses to load DNAME records into a wildcard domain. It isn't
|
||||
explicitly forbidden, but the protocol is ambiguous about how this should
|
||||
behave and BIND 9 refuses that as well. Please describe your intention using
|
||||
different tools.
|
||||
|
||||
% DATASRC_MEMORY_MEM_WILDCARD_NS NS record in wildcard domain '%1'
|
||||
The software refuses to load NS records into a wildcard domain. It isn't
|
||||
explicitly forbidden, but the protocol is ambiguous about how this should
|
||||
behave and BIND 9 refuses that as well. Please describe your intention using
|
||||
different tools.
|
@@ -1,6 +1,9 @@
|
||||
SUBDIRS = testdata .
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
|
||||
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
|
||||
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
|
||||
@@ -24,18 +27,23 @@ run_unittests_SOURCES += domaintree_unittest.cc
|
||||
run_unittests_SOURCES += treenode_rrset_unittest.cc
|
||||
run_unittests_SOURCES += zone_table_unittest.cc
|
||||
run_unittests_SOURCES += zone_data_unittest.cc
|
||||
run_unittests_SOURCES += zone_finder_unittest.cc
|
||||
run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
|
||||
run_unittests_SOURCES += memory_segment_test.h
|
||||
run_unittests_SOURCES += segment_object_holder_unittest.cc
|
||||
run_unittests_SOURCES += memory_client_unittest.cc
|
||||
|
||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
|
||||
run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
run_unittests_LDADD += $(GTEST_LDADD)
|
||||
endif
|
||||
|
||||
|
@@ -454,6 +454,142 @@ TEST_F(DomainTreeTest, callbackLabelSequence) {
|
||||
performCallbackTest(dtree, mem_sgmt_, ls1, ls2);
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, findInSubTree) {
|
||||
// For the version that takes a node chain, the chain must be empty.
|
||||
DomainTreeNodeChain<int> chain;
|
||||
bool flag;
|
||||
|
||||
// Searching for a non-absolute (right-stripped) label sequence when
|
||||
// chain is empty should throw.
|
||||
const Name n0("w.y.d.e.f");
|
||||
LabelSequence ls0(n0);
|
||||
ls0.stripRight(1);
|
||||
EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain,
|
||||
testCallback, &flag),
|
||||
isc::BadValue);
|
||||
|
||||
// First, find a sub-tree node
|
||||
chain.clear();
|
||||
const LabelSequence ls1(n0);
|
||||
DomainTree<int>::Result result =
|
||||
dtree_expose_empty_node.find(ls1, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n0, chain.getAbsoluteName());
|
||||
|
||||
// Searching for an absolute label sequence when chain is already
|
||||
// populated should throw.
|
||||
const Name n2a("o");
|
||||
const LabelSequence ls2a(n2a);
|
||||
EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain,
|
||||
testCallback, &flag),
|
||||
isc::BadValue);
|
||||
|
||||
// Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
|
||||
// suffix to "o" (non-absolute).
|
||||
const Name n2("o.w.y.d.e.f");
|
||||
LabelSequence ls2(n2);
|
||||
ls2.stripRight(6);
|
||||
EXPECT_EQ("o", ls2.toText());
|
||||
|
||||
result = dtree_expose_empty_node.find(ls2, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n2, chain.getAbsoluteName());
|
||||
|
||||
// Another test. Start with "d.e.f." node.
|
||||
chain.clear();
|
||||
const Name n3("d.e.f");
|
||||
const LabelSequence ls3(n3);
|
||||
result =
|
||||
dtree_expose_empty_node.find(ls3, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n3, chain.getAbsoluteName());
|
||||
|
||||
// Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
|
||||
// suffix to "o.w.y" (non-absolute).
|
||||
const Name n4("o.w.y.d.e.f");
|
||||
LabelSequence ls4(n2);
|
||||
ls4.stripRight(4);
|
||||
EXPECT_EQ("o.w.y", ls4.toText());
|
||||
|
||||
result = dtree_expose_empty_node.find(ls4, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n4, chain.getAbsoluteName());
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) {
|
||||
// For the version that takes a node chain, the chain must be empty.
|
||||
DomainTreeNodeChain<int> chain;
|
||||
bool flag;
|
||||
|
||||
const Name n1("c.g.h");
|
||||
|
||||
// First insert a "c.g.h." node.
|
||||
dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode);
|
||||
|
||||
/* Now, the tree looks like:
|
||||
*
|
||||
* .
|
||||
* |
|
||||
* b
|
||||
* / \
|
||||
* a d.e.f
|
||||
* / | \____
|
||||
* c | \
|
||||
* | g.h
|
||||
* | |
|
||||
* w.y i
|
||||
* / | \ / \
|
||||
* x | z c k
|
||||
* | |
|
||||
* p j
|
||||
* / \
|
||||
* o q
|
||||
*/
|
||||
|
||||
// Make a non-absolute label sequence. We will search for this same
|
||||
// sequence in two places in the tree.
|
||||
LabelSequence ls1(n1);
|
||||
ls1.stripRight(3);
|
||||
EXPECT_EQ("c", ls1.toText());
|
||||
|
||||
// First, find "g.h."
|
||||
const Name n2("g.h");
|
||||
const LabelSequence ls2(n2);
|
||||
DomainTree<int>::Result result =
|
||||
dtree_expose_empty_node.find(ls2, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n2, chain.getAbsoluteName());
|
||||
|
||||
// Now, find "c.g.h." by searching just the non-absolute ls1 label
|
||||
// sequence.
|
||||
result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n1, chain.getAbsoluteName());
|
||||
|
||||
// Now, find "." (the root node)
|
||||
chain.clear();
|
||||
const Name n3(".");
|
||||
const LabelSequence ls3(n3);
|
||||
result =
|
||||
dtree_expose_empty_node.find(ls3, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(n3, chain.getAbsoluteName());
|
||||
|
||||
// Now, find "c." by searching just the non-absolute ls1 label
|
||||
// sequence.
|
||||
result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
|
||||
testCallback, &flag);
|
||||
EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
|
||||
EXPECT_EQ(Name("c."), chain.getAbsoluteName());
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, chainLevel) {
|
||||
TestDomainTreeNodeChain chain;
|
||||
|
||||
|
740
src/lib/datasrc/memory/tests/memory_client_unittest.cc
Normal file
740
src/lib/datasrc/memory/tests/memory_client_unittest.cc
Normal file
@@ -0,0 +1,740 @@
|
||||
// Copyright (C) 2012 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 <exceptions/exceptions.h>
|
||||
|
||||
#include <util/memory_segment_local.h>
|
||||
|
||||
#include <dns/name.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/masterload.h>
|
||||
#include <dns/nsec3hash.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/rrttl.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
#include <datasrc/result.h>
|
||||
#include <datasrc/data_source.h>
|
||||
#include <datasrc/memory/zone_data.h>
|
||||
#include <datasrc/memory/zone_table.h>
|
||||
#include <datasrc/memory/memory_client.h>
|
||||
|
||||
#include <testutils/dnsmessage_test.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <new> // for bad_alloc
|
||||
|
||||
using namespace isc::dns;
|
||||
using namespace isc::dns::rdata;
|
||||
using namespace isc::datasrc;
|
||||
using namespace isc::datasrc::memory;
|
||||
using namespace isc::testutils;
|
||||
|
||||
namespace {
|
||||
// Memory segment specified for tests. It normally behaves like a "local"
|
||||
// memory segment. If "throw count" is set to non 0 via setThrowCount(),
|
||||
// it continues the normal behavior up to the specified number of calls to
|
||||
// allocate(), and throws an exception at the next call.
|
||||
class TestMemorySegment : public isc::util::MemorySegmentLocal {
|
||||
public:
|
||||
TestMemorySegment() : throw_count_(0) {}
|
||||
virtual void* allocate(size_t size) {
|
||||
if (throw_count_ > 0) {
|
||||
if (--throw_count_ == 0) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
return (isc::util::MemorySegmentLocal::allocate(size));
|
||||
}
|
||||
void setThrowCount(size_t count) { throw_count_ = count; }
|
||||
|
||||
private:
|
||||
size_t throw_count_;
|
||||
};
|
||||
|
||||
const char* rrset_data[] = {
|
||||
"example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600",
|
||||
"a.example.org. 3600 IN A 192.168.0.1",
|
||||
"a.example.org. 3600 IN MX 10 mail.example.org.",
|
||||
NULL
|
||||
};
|
||||
|
||||
class MockIterator : public ZoneIterator {
|
||||
private:
|
||||
MockIterator() :
|
||||
rrset_data_ptr_(rrset_data)
|
||||
{
|
||||
}
|
||||
|
||||
const char** rrset_data_ptr_;
|
||||
|
||||
public:
|
||||
virtual ConstRRsetPtr getNextRRset() {
|
||||
if (*rrset_data_ptr_ == NULL) {
|
||||
return (ConstRRsetPtr());
|
||||
}
|
||||
|
||||
RRsetPtr result(textToRRset(*rrset_data_ptr_,
|
||||
RRClass::IN(), Name("example.org")));
|
||||
rrset_data_ptr_++;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
virtual ConstRRsetPtr getSOA() const {
|
||||
isc_throw(isc::NotImplemented, "Not implemented");
|
||||
}
|
||||
|
||||
static ZoneIteratorPtr makeIterator(void) {
|
||||
return (ZoneIteratorPtr(new MockIterator()));
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryClientTest : public ::testing::Test {
|
||||
protected:
|
||||
MemoryClientTest() : zclass_(RRClass::IN()),
|
||||
client_(new InMemoryClient(mem_sgmt_, zclass_))
|
||||
{}
|
||||
~MemoryClientTest() {
|
||||
if (client_ != NULL) {
|
||||
delete client_;
|
||||
}
|
||||
}
|
||||
void TearDown() {
|
||||
delete client_;
|
||||
client_ = NULL;
|
||||
EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
|
||||
}
|
||||
const RRClass zclass_;
|
||||
TestMemorySegment mem_sgmt_;
|
||||
InMemoryClient* client_;
|
||||
};
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
|
||||
// Attempting to load example.org to example.com zone should result
|
||||
// in an exception.
|
||||
EXPECT_THROW(client_->load(Name("example.com"),
|
||||
TEST_DATA_DIR "/example.org-empty.zone"),
|
||||
MasterLoadError);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
|
||||
// Attempting to load broken example.org zone should result in an
|
||||
// exception. This should not leak ZoneData and other such
|
||||
// allocations.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-broken1.zone"),
|
||||
MasterLoadError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
|
||||
// Attempting to load broken example.org zone should result in an
|
||||
// exception. This should not leak ZoneData and other such
|
||||
// allocations.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-broken2.zone"),
|
||||
MasterLoadError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/somerandomfilename"),
|
||||
MasterLoadError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
|
||||
// When an empty zone file is loaded, the origin doesn't even have
|
||||
// an SOA RR. This condition should be avoided, and hence load()
|
||||
// should throw when an empty zone is loaded.
|
||||
|
||||
EXPECT_EQ(0, client_->getZoneCount());
|
||||
|
||||
EXPECT_THROW(client_->load(Name("."),
|
||||
TEST_DATA_DIR "/empty.zone"),
|
||||
InMemoryClient::EmptyZone);
|
||||
|
||||
EXPECT_EQ(0, client_->getZoneCount());
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, load) {
|
||||
// This is a simple load check for a "full" and correct zone that
|
||||
// should not result in any exceptions.
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org.zone");
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadFromIterator) {
|
||||
client_->load(Name("example.org"),
|
||||
*MockIterator::makeIterator());
|
||||
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// First we have the SOA
|
||||
ConstRRsetPtr rrset(iterator->getNextRRset());
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::SOA(), rrset->getType());
|
||||
|
||||
// RRType::MX() RRset
|
||||
rrset = iterator->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::MX(), rrset->getType());
|
||||
|
||||
// RRType::A() RRset
|
||||
rrset = iterator->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
// There's nothing else in this iterator
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
|
||||
|
||||
// Iterating past the end should result in an exception
|
||||
EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
|
||||
// Just to check that things get cleaned up
|
||||
|
||||
for (int i = 1; i < 16; i++) {
|
||||
mem_sgmt_.setThrowCount(i);
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org.zone"),
|
||||
std::bad_alloc);
|
||||
}
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadNSEC3Signed) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-nsec3-signed.zone");
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadReloadZone) {
|
||||
// Because we reload the same zone, also check that the zone count
|
||||
// doesn't increase.
|
||||
EXPECT_EQ(0, client_->getZoneCount());
|
||||
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-empty.zone");
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
|
||||
// Reload zone with same data
|
||||
|
||||
client_->load(Name("example.org"),
|
||||
client_->getFileName(Name("example.org")));
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
|
||||
isc::datasrc::memory::ZoneTable::FindResult
|
||||
result(client_->findZone2(Name("example.org")));
|
||||
EXPECT_EQ(result::SUCCESS, result.code);
|
||||
EXPECT_NE(static_cast<ZoneData*>(NULL),
|
||||
result.zone_data);
|
||||
|
||||
/* Check SOA */
|
||||
const ZoneNode* node = result.zone_data->getOriginNode();
|
||||
EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
|
||||
|
||||
const RdataSet* set = node->getData();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::SOA(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
|
||||
|
||||
/* Check ns1.example.org */
|
||||
const ZoneTree& tree = result.zone_data->getZoneTree();
|
||||
ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
|
||||
EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
|
||||
|
||||
// Reload zone with different data
|
||||
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
|
||||
isc::datasrc::memory::ZoneTable::FindResult
|
||||
result2(client_->findZone2(Name("example.org")));
|
||||
EXPECT_EQ(result::SUCCESS, result2.code);
|
||||
EXPECT_NE(static_cast<ZoneData*>(NULL),
|
||||
result2.zone_data);
|
||||
|
||||
/* Check SOA */
|
||||
node = result2.zone_data->getOriginNode();
|
||||
EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
|
||||
|
||||
set = node->getData();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::SOA(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
|
||||
|
||||
/* Check ns1.example.org */
|
||||
const ZoneTree& tree2 = result2.zone_data->getZoneTree();
|
||||
ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
|
||||
EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
|
||||
EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
|
||||
|
||||
set = node->getData();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::AAAA(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::A(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadDuplicateType) {
|
||||
// This should not result in any exceptions:
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-duplicate-type.zone");
|
||||
|
||||
// This should throw:
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-duplicate-type-bad.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
|
||||
// Multiple CNAME RRs should throw.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-multiple-cname.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
|
||||
// Multiple DNAME RRs should throw.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-multiple-dname.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
|
||||
// Multiple NSEC3 RRs should throw.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-multiple-nsec3.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
|
||||
// Multiple NSEC3PARAM RRs should throw.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-multiple-nsec3param.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
|
||||
// Out of zone names should throw.
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-out-of-zone.zone"),
|
||||
MasterLoadError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadWildcardNSThrows) {
|
||||
// Wildcard NS names should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-wildcard-ns.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
|
||||
// Wildcard DNAME names should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-wildcard-dname.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
|
||||
// Wildcard NSEC3 names should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-wildcard-nsec3.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
|
||||
// NSEC3 names with labels != (origin_labels + 1) should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-nsec3-fewer-labels.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
|
||||
// NSEC3 names with labels != (origin_labels + 1) should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-nsec3-more-labels.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
|
||||
// CNAME and not NSEC should throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-cname-and-not-nsec-1.zone"),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-cname-and-not-nsec-2.zone"),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
|
||||
// DNAME + NS (apex) is OK
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-dname-ns-apex-1.zone");
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
|
||||
// DNAME + NS (apex) is OK (reverse order)
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-dname-ns-apex-2.zone");
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
|
||||
// DNAME + NS (non-apex) must throw
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-dname-ns-nonapex-1.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
|
||||
// DNAME + NS (non-apex) must throw (reverse order)
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-dname-ns-nonapex-2.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-rrsig-follows-nothing.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGNameUnmatched) {
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-rrsig-name-unmatched.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGTypeUnmatched) {
|
||||
EXPECT_THROW(client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR
|
||||
"/example.org-rrsig-type-unmatched.zone"),
|
||||
InMemoryClient::AddError);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGs) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
RRsetPtr rrset(new RRset(Name("example.org"),
|
||||
RRClass::IN(), RRType::A(), RRTTL(3600)));
|
||||
rrset->addRdata(in::A("192.0.2.1"));
|
||||
rrset->addRdata(in::A("192.0.2.2"));
|
||||
|
||||
RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
|
||||
RRType::RRSIG(), RRTTL(300)));
|
||||
rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
|
||||
"12345 example.org. FAKEFAKEFAKE"));
|
||||
rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
|
||||
"54321 example.org. FAKEFAKEFAKEFAKE"));
|
||||
rrset->addRRsig(rrsig);
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getZoneCount) {
|
||||
EXPECT_EQ(0, client_->getZoneCount());
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
|
||||
// Zone "example.org." doesn't exist
|
||||
EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getFileName) {
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
|
||||
client_->getFileName(Name("example.org")));
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
|
||||
// Zone "." doesn't exist
|
||||
EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getIterator) {
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// First we have the SOA
|
||||
ConstRRsetPtr rrset_soa(iterator->getNextRRset());
|
||||
EXPECT_TRUE(rrset_soa);
|
||||
EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
|
||||
|
||||
// There's nothing else in this iterator
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
|
||||
|
||||
// Iterating past the end should result in an exception
|
||||
EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-multiple.zone");
|
||||
|
||||
// separate_rrs = false
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// First we have the SOA
|
||||
ConstRRsetPtr rrset(iterator->getNextRRset());
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::SOA(), rrset->getType());
|
||||
|
||||
// Only one RRType::A() RRset
|
||||
rrset = iterator->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
// There's nothing else in this zone
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
|
||||
|
||||
|
||||
// separate_rrs = true
|
||||
ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
|
||||
|
||||
// First we have the SOA
|
||||
rrset = iterator2->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::SOA(), rrset->getType());
|
||||
|
||||
// First RRType::A() RRset
|
||||
rrset = iterator2->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
// Second RRType::A() RRset
|
||||
rrset = iterator2->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
// There's nothing else in this iterator
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// This method is not implemented.
|
||||
EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
|
||||
// The zone "example.org" doesn't exist, so we can't add an RRset to
|
||||
// it.
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addOutOfZoneThrows) {
|
||||
// Out of zone names should throw.
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-empty.zone");
|
||||
|
||||
RRsetPtr rrset_a(new RRset(Name("a.example.com"),
|
||||
RRClass::IN(), RRType::A(), RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
|
||||
OutOfZone);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addNullRRsetThrows) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
|
||||
InMemoryClient::NullRRset);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, add) {
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
|
||||
// Add another RRset
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
client_->add(Name("example.org"), rrset_a);
|
||||
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// First we have the SOA
|
||||
ConstRRsetPtr rrset(iterator->getNextRRset());
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
rrset = iterator->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::SOA(), rrset->getType());
|
||||
|
||||
// There's nothing else in this zone
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, findZoneThrowsNotImplemented) {
|
||||
// This method is not implemented.
|
||||
EXPECT_THROW(client_->findZone(Name(".")),
|
||||
isc::NotImplemented);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, findZone2) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
isc::datasrc::memory::ZoneTable::FindResult
|
||||
result(client_->findZone2(Name("example.com")));
|
||||
EXPECT_EQ(result::NOTFOUND, result.code);
|
||||
EXPECT_EQ(static_cast<ZoneData*>(NULL),
|
||||
result.zone_data);
|
||||
|
||||
isc::datasrc::memory::ZoneTable::FindResult
|
||||
result2(client_->findZone2(Name("example.org")));
|
||||
EXPECT_EQ(result::SUCCESS, result2.code);
|
||||
EXPECT_NE(static_cast<ZoneData*>(NULL),
|
||||
result2.zone_data);
|
||||
|
||||
/* Check SOA */
|
||||
const ZoneNode* node = result2.zone_data->getOriginNode();
|
||||
EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
|
||||
|
||||
const RdataSet* set = node->getData();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::SOA(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
|
||||
|
||||
/* Check ns1.example.org */
|
||||
const ZoneTree& tree = result2.zone_data->getZoneTree();
|
||||
ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
|
||||
EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
|
||||
EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
|
||||
|
||||
set = node->getData();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::AAAA(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
|
||||
EXPECT_EQ(RRType::A(), set->type);
|
||||
|
||||
set = set->getNext();
|
||||
EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
|
||||
// This method is not implemented.
|
||||
EXPECT_THROW(client_->getUpdater(Name("."), false, false),
|
||||
isc::NotImplemented);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
|
||||
// This method is not implemented.
|
||||
EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
|
||||
isc::NotImplemented);
|
||||
}
|
||||
}
|
@@ -14,10 +14,13 @@
|
||||
|
||||
#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);
|
||||
|
||||
isc::log::initLogger();
|
||||
|
||||
return (isc::util::unittests::run_all());
|
||||
}
|
||||
|
32
src/lib/datasrc/memory/tests/testdata/Makefile.am
vendored
Normal file
32
src/lib/datasrc/memory/tests/testdata/Makefile.am
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
CLEANFILES = *.copied
|
||||
|
||||
EXTRA_DIST = empty.zone
|
||||
EXTRA_DIST += example.org.zone
|
||||
EXTRA_DIST += example.org-empty.zone
|
||||
|
||||
EXTRA_DIST += example.org-broken1.zone
|
||||
EXTRA_DIST += example.org-broken2.zone
|
||||
EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
|
||||
EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
|
||||
EXTRA_DIST += example.org-dname-ns-apex-1.zone
|
||||
EXTRA_DIST += example.org-dname-ns-apex-2.zone
|
||||
EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
|
||||
EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
|
||||
EXTRA_DIST += example.org-duplicate-type-bad.zone
|
||||
EXTRA_DIST += example.org-duplicate-type.zone
|
||||
EXTRA_DIST += example.org-multiple-cname.zone
|
||||
EXTRA_DIST += example.org-multiple-dname.zone
|
||||
EXTRA_DIST += example.org-multiple-nsec3.zone
|
||||
EXTRA_DIST += example.org-multiple-nsec3param.zone
|
||||
EXTRA_DIST += example.org-multiple.zone
|
||||
EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
|
||||
EXTRA_DIST += example.org-nsec3-signed-no-param.zone
|
||||
EXTRA_DIST += example.org-nsec3-signed.zone
|
||||
EXTRA_DIST += example.org-out-of-zone.zone
|
||||
EXTRA_DIST += example.org-rrsig-follows-nothing.zone
|
||||
EXTRA_DIST += example.org-rrsig-name-unmatched.zone
|
||||
EXTRA_DIST += example.org-rrsig-type-unmatched.zone
|
||||
EXTRA_DIST += example.org-rrsigs.zone
|
||||
EXTRA_DIST += example.org-wildcard-dname.zone
|
||||
EXTRA_DIST += example.org-wildcard-ns.zone
|
||||
EXTRA_DIST += example.org-wildcard-nsec3.zone
|
0
src/lib/datasrc/memory/tests/testdata/empty.zone
vendored
Normal file
0
src/lib/datasrc/memory/tests/testdata/empty.zone
vendored
Normal file
1
src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
vendored
Normal file
1
src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This is a broken zone that should not parse.
|
5
src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
vendored
Normal file
5
src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
;; broken example.org zone, where some RRs are OK, but others aren't
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN A 192.0.2.1
|
||||
ns2.example.org. 3600 IN A 192.0.2.2
|
||||
ns2.a.example.com. 3600 IN AAAA
|
4
src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; CNAME + other is an error
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
|
||||
a.example.org. 7200 IN A 192.168.0.1
|
||||
a.example.org. 3600 IN CNAME foo.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; CNAME + other is an error
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
|
||||
a.example.org. 3600 IN CNAME foo.example.com.
|
||||
a.example.org. 7200 IN A 192.168.0.1
|
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; DNAME + NS (apex)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
|
||||
example.org. 3600 IN DNAME foo.example.com.
|
||||
example.org. 3600 IN NS bar.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; DNAME + NS (apex)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
|
||||
example.org. 3600 IN NS bar.example.com.
|
||||
example.org. 3600 IN DNAME foo.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; DNAME + NS (non-apex)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
|
||||
ns1.example.org. 3600 IN DNAME foo.example.com.
|
||||
ns1.example.org. 3600 IN NS bar.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; DNAME + NS (non-apex)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
|
||||
ns1.example.org. 3600 IN NS bar.example.com.
|
||||
ns1.example.org. 3600 IN DNAME foo.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN A 192.168.0.1
|
||||
ns1.example.org. 3600 IN AAAA ::1
|
||||
ns1.example.org. 3600 IN A 192.168.0.2
|
4
src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN A 192.168.0.1
|
||||
ns1.example.org. 3600 IN A 192.168.0.2
|
||||
ns1.example.org. 3600 IN AAAA ::1
|
2
src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
vendored
Normal file
2
src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
;; empty example.org zone
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
|
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN CNAME foo.example.com.
|
||||
ns1.example.org. 3600 IN CNAME bar.example.com.
|
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN DNAME foo.example.com.
|
||||
ns1.example.org. 3600 IN DNAME bar.example.com.
|
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
|
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
|
||||
example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
|
||||
example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
|
4
src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; Multiple RDATA for testing separate RRs iterator
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
|
||||
a.example.org. 3600 IN A 192.168.0.1
|
||||
a.example.org. 3600 IN A 192.168.0.2
|
3
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
;; NSEC3 names with labels != (origin_labels + 1)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
|
||||
example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
3
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
vendored
Normal file
3
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
;; NSEC3 names with labels != (origin_labels + 1)
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
|
||||
a.b.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
15
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
vendored
Normal file
15
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
|
||||
example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
|
||||
example.org. 86400 IN NS ns.example.org.
|
||||
example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
|
||||
example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
|
||||
example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
|
||||
;; example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
|
||||
;; example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
|
||||
ns.example.org. 86400 IN A 192.0.2.1
|
||||
ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
|
||||
RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
|
||||
RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
|
14
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
vendored
Normal file
14
src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
|
||||
example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
|
||||
example.org. 86400 IN NS ns.example.org.
|
||||
example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
|
||||
example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
|
||||
example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
|
||||
example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
|
||||
example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
|
||||
ns.example.org. 86400 IN A 192.0.2.1
|
||||
ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
||||
09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
|
||||
RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
|
||||
RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
|
5
src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
vendored
Normal file
5
src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 75 3600 300 3600000 3600
|
||||
a.example.com. 3600 IN A 192.168.0.1
|
5
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
vendored
Normal file
5
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
6
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
vendored
Normal file
6
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 70 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN A 192.0.2.1
|
||||
ns2.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
6
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
vendored
Normal file
6
src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 72 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN AAAA 2001:db8::1
|
||||
ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
8
src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
vendored
Normal file
8
src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
|
||||
ns1.example.org. 3600 IN A 192.168.0.1
|
||||
ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
||||
ns1.example.org. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
||||
ns1.example.org. 3600 IN AAAA ::1
|
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; test zone file with wildcard DNAME names
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
|
||||
*.example.org. 3600 IN DNAME dname.example.com.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; test zone file with wildcard NS names
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
|
||||
*.example.org. 3600 IN NS ns1.example.org.
|
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
vendored
Normal file
4
src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
;; test zone file with wildcard NS names
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
|
||||
*.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
|
81
src/lib/datasrc/memory/tests/testdata/example.org.zone
vendored
Normal file
81
src/lib/datasrc/memory/tests/testdata/example.org.zone
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
;; test zone file used for ZoneFinderContext tests.
|
||||
;; RRSIGs are (obviouslly) faked ones for testing.
|
||||
|
||||
example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
|
||||
example.org. 3600 IN NS ns1.example.org.
|
||||
example.org. 3600 IN NS ns2.example.org.
|
||||
example.org. 3600 IN MX 1 mx1.example.org.
|
||||
example.org. 3600 IN MX 2 mx2.example.org.
|
||||
example.org. 3600 IN MX 3 mx.a.example.org.
|
||||
|
||||
ns1.example.org. 3600 IN A 192.0.2.1
|
||||
ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
|
||||
ns1.example.org. 3600 IN AAAA 2001:db8::1
|
||||
ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
|
||||
ns2.example.org. 3600 IN A 192.0.2.2
|
||||
ns2.example.org. 3600 IN TXT "text data"
|
||||
|
||||
mx1.example.org. 3600 IN A 192.0.2.10
|
||||
mx2.example.org. 3600 IN AAAA 2001:db8::10
|
||||
|
||||
;; delegation
|
||||
a.example.org. 3600 IN NS ns1.a.example.org.
|
||||
a.example.org. 3600 IN NS ns2.a.example.org.
|
||||
a.example.org. 3600 IN NS ns.example.com.
|
||||
|
||||
ns1.a.example.org. 3600 IN A 192.0.2.5
|
||||
ns2.a.example.org. 3600 IN A 192.0.2.6
|
||||
ns2.a.example.org. 3600 IN AAAA 2001:db8::6
|
||||
mx.a.example.org. 3600 IN A 192.0.2.7
|
||||
|
||||
;; delegation, one of its NS names is at zone cut.
|
||||
b.example.org. 3600 IN NS ns.b.example.org.
|
||||
b.example.org. 3600 IN NS b.example.org.
|
||||
b.example.org. 3600 IN AAAA 2001:db8::8
|
||||
|
||||
ns.b.example.org. 3600 IN A 192.0.2.9
|
||||
|
||||
;; The MX name is at a zone cut. shouldn't be included in the
|
||||
;; additional section.
|
||||
mxatcut.example.org. 3600 IN MX 1 b.example.org.
|
||||
|
||||
;; delegation, one of its NS names is under a DNAME delegation point;
|
||||
;; another is at that point; and yet another is under DNAME below a
|
||||
;; zone cut.
|
||||
c.example.org. 3600 IN NS ns.dname.example.org.
|
||||
c.example.org. 3600 IN NS dname.example.org.
|
||||
c.example.org. 3600 IN NS ns.deepdname.example.org.
|
||||
ns.dname.example.org. 3600 IN A 192.0.2.11
|
||||
dname.example.org. 3600 IN A 192.0.2.12
|
||||
ns.deepdname.example.org. 3600 IN AAAA 2001:db8::9
|
||||
|
||||
;; delegation, one of its NS name is at an empty non terminal.
|
||||
d.example.org. 3600 IN NS ns.empty.example.org.
|
||||
d.example.org. 3600 IN NS ns1.example.org.
|
||||
;; by adding these two we can create an empty RB node for
|
||||
;; ns.empty.example.org in the in-memory zone
|
||||
foo.ns.empty.example.org. 3600 IN A 192.0.2.13
|
||||
bar.ns.empty.example.org. 3600 IN A 192.0.2.14
|
||||
|
||||
;; delegation; the NS name matches a wildcard (and there's no exact
|
||||
;; match). One of the NS names matches an empty wildcard node, for
|
||||
;; which no additional record should be provided (or any other
|
||||
;; disruption should happen).
|
||||
e.example.org. 3600 IN NS ns.wild.example.org.
|
||||
e.example.org. 3600 IN NS ns.emptywild.example.org.
|
||||
e.example.org. 3600 IN NS ns2.example.org.
|
||||
*.wild.example.org. 3600 IN A 192.0.2.15
|
||||
a.*.emptywild.example.org. 3600 IN AAAA 2001:db8::2
|
||||
|
||||
;; additional for an answer RRset (MX) as a result of wildcard
|
||||
;; expansion
|
||||
*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
|
||||
|
||||
;; CNAME
|
||||
alias.example.org. 3600 IN CNAME cname.example.org.
|
||||
|
||||
;; DNAME
|
||||
dname.example.org. 3600 IN DNAME dname.example.com.
|
||||
|
||||
;; DNAME under a NS (strange one)
|
||||
deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.
|
@@ -143,11 +143,13 @@ protected:
|
||||
void
|
||||
checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
|
||||
const RRClass& expected_class, const RRType& expected_type,
|
||||
const uint32_t expected_ttl,
|
||||
size_t expected_rdatacount, size_t expected_sigcount)
|
||||
{
|
||||
EXPECT_EQ(expected_name, actual_rrset.getName());
|
||||
EXPECT_EQ(expected_class, actual_rrset.getClass());
|
||||
EXPECT_EQ(expected_type, actual_rrset.getType());
|
||||
EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
|
||||
EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
|
||||
EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
|
||||
}
|
||||
@@ -176,30 +178,30 @@ createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
|
||||
TEST_F(TreeNodeRRsetTest, create) {
|
||||
// Constructed with RRSIG, and it should be visible.
|
||||
checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
|
||||
www_name_, rrclass_, RRType::A(), 2, 1);
|
||||
www_name_, rrclass_, RRType::A(), 3600, 2, 1);
|
||||
// Constructed with RRSIG, and it should be invisible.
|
||||
checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
|
||||
www_name_, rrclass_, RRType::A(), 2, 0);
|
||||
www_name_, rrclass_, RRType::A(), 3600, 2, 0);
|
||||
// Constructed without RRSIG, and it would be visible (but of course won't)
|
||||
checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
|
||||
origin_name_, rrclass_, RRType::NS(), 1, 0);
|
||||
origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
|
||||
// Constructed without RRSIG, and it should be visible
|
||||
checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
|
||||
origin_name_, rrclass_, RRType::NS(), 1, 0);
|
||||
origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
|
||||
// RRSIG-only case (note the RRset's type is covered type)
|
||||
checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
|
||||
true),
|
||||
www_name_, rrclass_, RRType::TXT(), 0, 1);
|
||||
www_name_, rrclass_, RRType::TXT(), 3600, 0, 1);
|
||||
// RRSIG-only case (note the RRset's type is covered type), but it's
|
||||
// invisible
|
||||
checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
|
||||
false),
|
||||
www_name_, rrclass_, RRType::TXT(), 0, 0);
|
||||
www_name_, rrclass_, RRType::TXT(), 3600, 0, 0);
|
||||
// Wildcard substitution
|
||||
checkBasicFields(*createRRset(match_name_, rrclass_,
|
||||
wildcard_node_, wildcard_rdataset_,
|
||||
true),
|
||||
match_name_, rrclass_, RRType::A(), 2, 1);
|
||||
match_name_, rrclass_, RRType::A(), 3600, 2, 1);
|
||||
}
|
||||
|
||||
// The following two templated functions are helper to encapsulate the
|
||||
@@ -572,7 +574,6 @@ TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
|
||||
|
||||
TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
|
||||
|
||||
EXPECT_THROW(rrset.getTTL(), isc::Unexpected);
|
||||
EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
|
||||
EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
|
||||
EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
|
||||
|
1481
src/lib/datasrc/memory/tests/zone_finder_unittest.cc
Normal file
1481
src/lib/datasrc/memory/tests/zone_finder_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -58,7 +58,13 @@ TreeNodeRRset::getName() const {
|
||||
|
||||
const RRTTL&
|
||||
TreeNodeRRset::getTTL() const {
|
||||
isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
|
||||
if (ttl_ == NULL) {
|
||||
util::InputBuffer ttl_buffer(rdataset_->getTTLData(),
|
||||
sizeof(uint32_t));
|
||||
ttl_ = new RRTTL(ttl_buffer);
|
||||
}
|
||||
|
||||
return (*ttl_);
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -112,7 +112,7 @@ public:
|
||||
const RdataSet* rdataset, bool dnssec_ok) :
|
||||
node_(node), rdataset_(rdataset),
|
||||
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL)
|
||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL), ttl_(NULL)
|
||||
{}
|
||||
|
||||
/// \brief Constructor for wildcard-expanded owner name.
|
||||
@@ -132,11 +132,13 @@ public:
|
||||
bool dnssec_ok) :
|
||||
node_(node), rdataset_(rdataset),
|
||||
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname))
|
||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
|
||||
ttl_(NULL)
|
||||
{}
|
||||
|
||||
virtual ~TreeNodeRRset() {
|
||||
delete realname_;
|
||||
delete ttl_;
|
||||
delete name_;
|
||||
}
|
||||
|
||||
@@ -154,8 +156,6 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Specialized version of \c getTTL() for \c TreeNodeRRset.
|
||||
///
|
||||
/// It throws \c isc::Unexpected unconditionally.
|
||||
virtual const dns::RRTTL& getTTL() const;
|
||||
|
||||
/// \brief Specialized version of \c setName() for \c TreeNodeRRset.
|
||||
@@ -257,8 +257,11 @@ private:
|
||||
const bool dnssec_ok_;
|
||||
mutable dns::Name* name_;
|
||||
const dns::Name* const realname_;
|
||||
mutable dns::RRTTL* ttl_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<TreeNodeRRset> TreeNodeRRsetPtr;
|
||||
|
||||
} // namespace memory
|
||||
} // namespace datasrc
|
||||
} // namespace isc
|
||||
|
@@ -43,6 +43,7 @@ namespace memory {
|
||||
|
||||
typedef DomainTree<RdataSet> ZoneTree;
|
||||
typedef DomainTreeNode<RdataSet> ZoneNode;
|
||||
typedef DomainTreeNodeChain<RdataSet> ZoneChain;
|
||||
|
||||
/// \brief NSEC3 data for a DNS zone.
|
||||
///
|
||||
|
600
src/lib/datasrc/memory/zone_finder.cc
Normal file
600
src/lib/datasrc/memory/zone_finder.cc
Normal file
@@ -0,0 +1,600 @@
|
||||
// Copyright (C) 2012 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 <datasrc/memory/zone_finder.h>
|
||||
#include <datasrc/memory/domaintree.h>
|
||||
#include <datasrc/memory/treenode_rrset.h>
|
||||
|
||||
#include <datasrc/zone.h>
|
||||
#include <datasrc/data_source.h>
|
||||
#include <dns/labelsequence.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rrset.h>
|
||||
#include <dns/rrtype.h>
|
||||
|
||||
#include <datasrc/logger.h>
|
||||
|
||||
using namespace isc::dns;
|
||||
using namespace isc::datasrc::memory;
|
||||
using namespace isc::datasrc;
|
||||
|
||||
namespace isc {
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
namespace {
|
||||
/// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
|
||||
/// the given RRClass
|
||||
///
|
||||
/// We should probably have some pool so these do not need to be allocated
|
||||
/// dynamically.
|
||||
///
|
||||
/// \param node The ZoneNode found by the find() calls
|
||||
/// \param rdataset The RdataSet to create the RRsetPtr for
|
||||
/// \param rrclass The RRClass as passed by the client
|
||||
/// \param realname If given, the TreeNodeRRset is created with this name
|
||||
/// (e.g. for wildcard substitution)
|
||||
///
|
||||
/// Returns an empty TreeNodeRRsetPtr if node is NULL or if rdataset is NULL.
|
||||
TreeNodeRRsetPtr
|
||||
createTreeNodeRRset(const ZoneNode* node,
|
||||
const RdataSet* rdataset,
|
||||
const RRClass& rrclass,
|
||||
const Name* realname = NULL)
|
||||
{
|
||||
if (node != NULL && rdataset != NULL) {
|
||||
if (realname != NULL) {
|
||||
return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
|
||||
rdataset, true));
|
||||
} else {
|
||||
return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
|
||||
rdataset, true));
|
||||
}
|
||||
} else {
|
||||
return TreeNodeRRsetPtr();
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintain intermediate data specific to the search context used in
|
||||
/// \c find().
|
||||
///
|
||||
/// It will be passed to \c cutCallback() (see below) and record a possible
|
||||
/// zone cut node and related RRset (normally NS or DNAME).
|
||||
struct FindState {
|
||||
FindState(bool glue_ok) :
|
||||
zonecut_node_(NULL),
|
||||
dname_node_(NULL),
|
||||
rrset_(NULL),
|
||||
glue_ok_(glue_ok)
|
||||
{}
|
||||
|
||||
// These will be set to a domain node of the highest delegation point,
|
||||
// if any. In fact, we could use a single variable instead of both.
|
||||
// But then we would need to distinquish these two cases by something
|
||||
// else and it seemed little more confusing when this was written.
|
||||
const ZoneNode* zonecut_node_;
|
||||
const ZoneNode* dname_node_;
|
||||
|
||||
// Delegation RRset (NS or DNAME), if found.
|
||||
const RdataSet* rrset_;
|
||||
|
||||
// Whether to continue search below a delegation point.
|
||||
// Set at construction time.
|
||||
const bool glue_ok_;
|
||||
};
|
||||
|
||||
// A callback called from possible zone cut nodes and nodes with DNAME.
|
||||
// This will be passed from findNode() to \c RBTree::find().
|
||||
bool cutCallback(const ZoneNode& node, FindState* state) {
|
||||
// We need to look for DNAME first, there's allowed case where
|
||||
// DNAME and NS coexist in the apex. DNAME is the one to notice,
|
||||
// the NS is authoritative, not delegation (corner case explicitly
|
||||
// allowed by section 3 of 2672)
|
||||
const RdataSet* found_dname = RdataSet::find(node.getData(),
|
||||
RRType::DNAME());
|
||||
|
||||
if (found_dname != NULL) {
|
||||
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
|
||||
state->dname_node_ = &node;
|
||||
state->rrset_ = found_dname;
|
||||
return (true);
|
||||
}
|
||||
|
||||
// Look for NS
|
||||
const RdataSet* found_ns = RdataSet::find(node.getData(), RRType::NS());
|
||||
if (found_ns != NULL) {
|
||||
// We perform callback check only for the highest zone cut in the
|
||||
// rare case of nested zone cuts.
|
||||
if (state->zonecut_node_ != NULL) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
|
||||
|
||||
// BIND 9 checks if this node is not the origin. That's probably
|
||||
// because it can support multiple versions for dynamic updates
|
||||
// and IXFR, and it's possible that the callback is called at
|
||||
// the apex and the DNAME doesn't exist for a particular version.
|
||||
// It cannot happen for us (at least for now), so we don't do
|
||||
// that check.
|
||||
state->zonecut_node_ = &node;
|
||||
state->rrset_ = found_ns;
|
||||
|
||||
// Unless glue is allowed the search stops here, so we return
|
||||
// false; otherwise return true to continue the search.
|
||||
return (!state->glue_ok_);
|
||||
}
|
||||
|
||||
// This case should not happen because we enable callback only
|
||||
// when we add an RR searched for above.
|
||||
assert(0);
|
||||
// This is here to avoid warning (therefore compilation error)
|
||||
// in case assert is turned off. Otherwise we could get "Control
|
||||
// reached end of non-void function".
|
||||
return (false);
|
||||
}
|
||||
|
||||
// convenience function to fill in the final details
|
||||
//
|
||||
// Set up ZoneFinderResultContext object as a return value of find(),
|
||||
// taking into account wildcard matches and DNSSEC information. We set
|
||||
// the NSEC/NSEC3 flag when applicable regardless of the find option; the
|
||||
// caller would simply ignore these when they didn't request DNSSEC
|
||||
// related results.
|
||||
//
|
||||
// Also performs the conversion of node + RdataSet into a TreeNodeRRsetPtr
|
||||
//
|
||||
// if wild is true, the RESULT_WILDCARD flag will be set.
|
||||
// If qname is not NULL, this is the query name, to be used in wildcard
|
||||
// substitution instead of the Node's name).
|
||||
isc::datasrc::memory::ZoneFinderResultContext
|
||||
createFindResult(const RRClass& rrclass,
|
||||
const ZoneData& zone_data,
|
||||
ZoneFinder::Result code,
|
||||
const RdataSet* rrset,
|
||||
const ZoneNode* node,
|
||||
bool wild = false,
|
||||
const Name* qname = NULL) {
|
||||
ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
|
||||
const Name* rename = NULL;
|
||||
|
||||
if (wild) {
|
||||
flags = flags | ZoneFinder::RESULT_WILDCARD;
|
||||
// only use the rename qname if wild is true
|
||||
rename = qname;
|
||||
}
|
||||
if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) {
|
||||
if (zone_data.isNSEC3Signed()) {
|
||||
flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
|
||||
} else if (zone_data.isSigned()) {
|
||||
flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
|
||||
}
|
||||
}
|
||||
|
||||
return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
|
||||
rrclass, rename),
|
||||
flags, node));
|
||||
}
|
||||
|
||||
// A helper function for NSEC-signed zones. It searches the zone for
|
||||
// the "closest" NSEC corresponding to the search context stored in
|
||||
// node_path (it should contain sufficient information to identify the
|
||||
// previous name of the query name in the zone). In some cases the
|
||||
// immediate closest name may not have NSEC (when it's under a zone cut
|
||||
// for glue records, or even when the zone is partly broken), so this
|
||||
// method continues the search until it finds a name that has NSEC,
|
||||
// and returns the one found first. Due to the prerequisite (see below),
|
||||
// it should always succeed.
|
||||
//
|
||||
// node_path must store valid search context (in practice, it's expected
|
||||
// to be set by findNode()); otherwise the underlying RBTree implementation
|
||||
// throws.
|
||||
//
|
||||
// If the zone is not considered NSEC-signed or DNSSEC records were not
|
||||
// required in the original search context (specified in options), this
|
||||
// method doesn't bother to find NSEC, and simply returns NULL. So, by
|
||||
// definition of "NSEC-signed", when it really tries to find an NSEC it
|
||||
// should succeed; there should be one at least at the zone origin.
|
||||
const RdataSet*
|
||||
getClosestNSEC(const ZoneData& zone_data,
|
||||
ZoneChain& node_path,
|
||||
const ZoneNode** nsec_node,
|
||||
ZoneFinder::FindOptions options)
|
||||
{
|
||||
if (!zone_data.isSigned() ||
|
||||
(options & ZoneFinder::FIND_DNSSEC) == 0 ||
|
||||
zone_data.isNSEC3Signed()) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
const ZoneNode* prev_node;
|
||||
while ((prev_node = zone_data.getZoneTree().previousNode(node_path))
|
||||
!= NULL) {
|
||||
if (!prev_node->isEmpty()) {
|
||||
const RdataSet* found =
|
||||
RdataSet::find(prev_node->getData(), RRType::NSEC());
|
||||
if (found != NULL) {
|
||||
*nsec_node = prev_node;
|
||||
return (found);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This must be impossible and should be an internal bug.
|
||||
// See the description at the method declaration.
|
||||
assert(false);
|
||||
// Even though there is an assert here, strict compilers
|
||||
// will still need some return value.
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
// A helper function for the NXRRSET case in find(). If the zone is
|
||||
// NSEC-signed and DNSSEC records are requested, try to find NSEC
|
||||
// on the given node, and return it if found; return NULL for all other
|
||||
// cases.
|
||||
const RdataSet*
|
||||
getNSECForNXRRSET(const ZoneData& zone_data,
|
||||
ZoneFinder::FindOptions options,
|
||||
const ZoneNode* node)
|
||||
{
|
||||
if (zone_data.isSigned() &&
|
||||
!zone_data.isNSEC3Signed() &&
|
||||
(options & ZoneFinder::FIND_DNSSEC) != 0) {
|
||||
const RdataSet* found = RdataSet::find(node->getData(),
|
||||
RRType::NSEC());
|
||||
if (found != NULL) {
|
||||
return (found);
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
// Structure to hold result data of the findNode() call
|
||||
class FindNodeResult {
|
||||
public:
|
||||
// Bitwise flags to represent supplemental information of the
|
||||
// search result:
|
||||
// Search resulted in a wildcard match.
|
||||
static const unsigned int FIND_WILDCARD = 1;
|
||||
// Search encountered a zone cut due to NS but continued to look for
|
||||
// a glue.
|
||||
static const unsigned int FIND_ZONECUT = 2;
|
||||
|
||||
FindNodeResult(ZoneFinder::Result code_param,
|
||||
const ZoneNode* node_param,
|
||||
const RdataSet* rrset_param,
|
||||
unsigned int flags_param = 0) :
|
||||
code(code_param),
|
||||
node(node_param),
|
||||
rrset(rrset_param),
|
||||
flags(flags_param)
|
||||
{}
|
||||
const ZoneFinder::Result code;
|
||||
const ZoneNode* node;
|
||||
const RdataSet* rrset;
|
||||
const unsigned int flags;
|
||||
};
|
||||
|
||||
// Implementation notes: this method identifies an ZoneNode that best matches
|
||||
// the give name in terms of DNS query handling. In many cases,
|
||||
// DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
|
||||
// the given name is generally expected to be contained in the zone, so
|
||||
// even if it doesn't exist, it should at least match the zone origin).
|
||||
// If it finds an exact match, that's obviously the best one. The partial
|
||||
// match case is more complicated.
|
||||
//
|
||||
// We first need to consider the case where search hits a delegation point,
|
||||
// either due to NS or DNAME. They are indicated as either dname_node_ or
|
||||
// zonecut_node_ being non NULL. Usually at most one of them will be
|
||||
// something else than NULL (it might happen both are NULL, in which case we
|
||||
// consider it NOT FOUND). There's one corner case when both might be
|
||||
// something else than NULL and it is in case there's a DNAME under a zone
|
||||
// cut and we search in glue OK mode ‒ in that case we don't stop on the
|
||||
// domain with NS and ignore it for the answer, but it gets set anyway. Then
|
||||
// we find the DNAME and we need to act by it, therefore we first check for
|
||||
// DNAME and then for NS. In all other cases it doesn't matter, as at least
|
||||
// one of them is NULL.
|
||||
//
|
||||
// Next, we need to check if the ZoneTree search stopped at a node for a
|
||||
// subdomain of the search name (so the comparison result that stopped the
|
||||
// search is "SUPERDOMAIN"), it means the stopping node is an empty
|
||||
// non-terminal node. In this case the search name is considered to exist
|
||||
// but no data should be found there.
|
||||
//
|
||||
// If none of above is the case, we then consider whether there's a matching
|
||||
// wildcard. DomainTree::find() records the node if it encounters a
|
||||
// "wildcarding" node, i.e., the immediate ancestor of a wildcard name
|
||||
// (e.g., wild.example.com for *.wild.example.com), and returns it if it
|
||||
// doesn't find any node that better matches the query name. In this case
|
||||
// we'll check if there's indeed a wildcard below the wildcarding node.
|
||||
//
|
||||
// Note, first, that the wildcard is checked after the empty
|
||||
// non-terminal domain case above, because if that one triggers, it
|
||||
// means we should not match according to 4.3.3 of RFC 1034 (the query
|
||||
// name is known to exist).
|
||||
//
|
||||
// Before we try to find a wildcard, we should check whether there's
|
||||
// an existing node that would cancel the wildcard match. If
|
||||
// DomainTree::find() stopped at a node which has a common ancestor
|
||||
// with the query name, it might mean we are comparing with a
|
||||
// non-wildcard node. In that case, we check which part is common. If
|
||||
// we have something in common that lives below the node we got (the
|
||||
// one above *), then we should cancel the match according to section
|
||||
// 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
|
||||
// query name is known to exist).
|
||||
//
|
||||
// If there's no node below the wildcarding node that shares a common ancestor
|
||||
// of the query name, we can conclude the wildcard is the best match.
|
||||
// We'll then identify the wildcard node via an incremental search. Note that
|
||||
// there's no possibility that the query name is at an empty non terminal
|
||||
// node below the wildcarding node at this stage; that case should have been
|
||||
// caught above.
|
||||
//
|
||||
// If none of the above succeeds, we conclude the name doesn't exist in
|
||||
// the zone, and throw an OutOfZone exception.
|
||||
FindNodeResult findNode(const ZoneData& zone_data,
|
||||
const Name& name,
|
||||
ZoneChain& node_path,
|
||||
ZoneFinder::FindOptions options)
|
||||
{
|
||||
ZoneNode* node = NULL;
|
||||
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
|
||||
|
||||
const ZoneTree& tree(zone_data.getZoneTree());
|
||||
ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
|
||||
&node, node_path, cutCallback,
|
||||
&state);
|
||||
const unsigned int zonecut_flag =
|
||||
(state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
|
||||
if (result == ZoneTree::EXACTMATCH) {
|
||||
return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
|
||||
zonecut_flag));
|
||||
} else if (result == ZoneTree::PARTIALMATCH) {
|
||||
assert(node != NULL);
|
||||
if (state.dname_node_ != NULL) { // DNAME
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
|
||||
arg(state.dname_node_->getName());
|
||||
return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_,
|
||||
state.rrset_));
|
||||
}
|
||||
if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
|
||||
arg(state.zonecut_node_->getName());
|
||||
return (FindNodeResult(ZoneFinder::DELEGATION,
|
||||
state.zonecut_node_,
|
||||
state.rrset_));
|
||||
}
|
||||
if (node_path.getLastComparisonResult().getRelation() ==
|
||||
NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
||||
DATASRC_MEM_SUPER_STOP).arg(name);
|
||||
const ZoneNode* nsec_node;
|
||||
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
|
||||
node_path,
|
||||
&nsec_node,
|
||||
options);
|
||||
return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node,
|
||||
nsec_rds));
|
||||
}
|
||||
// Nothing really matched.
|
||||
|
||||
// May be a wildcard, but check only if not disabled
|
||||
if (node->getFlag(ZoneData::WILDCARD_NODE) &&
|
||||
(options & ZoneFinder::NO_WILDCARD) == 0) {
|
||||
if (node_path.getLastComparisonResult().getRelation() ==
|
||||
NameComparisonResult::COMMONANCESTOR) {
|
||||
// This means, e.g., we have *.wild.example and
|
||||
// bar.foo.wild.example and are looking for
|
||||
// baz.foo.wild.example. The common ancestor, foo.wild.example,
|
||||
// should cancel wildcard. Treat it as NXDOMAIN.
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
||||
DATASRC_MEM_WILDCARD_CANCEL).arg(name);
|
||||
const ZoneNode* nsec_node;
|
||||
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
|
||||
node_path,
|
||||
&nsec_node,
|
||||
options);
|
||||
return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node,
|
||||
nsec_rds));
|
||||
}
|
||||
uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
|
||||
|
||||
// Create the wildcard name (i.e. take "*" and extend it
|
||||
// with all node labels down to the wildcard node
|
||||
LabelSequence wildcard_ls(LabelSequence::WILDCARD(), ls_buf);
|
||||
const ZoneNode* extend_with = node;
|
||||
while (extend_with != NULL) {
|
||||
wildcard_ls.extend(extend_with->getLabels(), ls_buf);
|
||||
extend_with = extend_with->getUpperNode();
|
||||
}
|
||||
|
||||
// Clear the node_path so that we don't keep incorrect (NSEC)
|
||||
// context
|
||||
node_path.clear();
|
||||
ZoneTree::Result result = tree.find(LabelSequence(wildcard_ls),
|
||||
&node, node_path, cutCallback,
|
||||
&state);
|
||||
// Otherwise, why would the domain_flag::WILD be there if
|
||||
// there was no wildcard under it?
|
||||
assert(result == ZoneTree::EXACTMATCH);
|
||||
return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
|
||||
FindNodeResult::FIND_WILDCARD | zonecut_flag));
|
||||
}
|
||||
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
|
||||
const ZoneNode* nsec_node;
|
||||
const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
|
||||
&nsec_node, options);
|
||||
return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
|
||||
} else {
|
||||
// If the name is neither an exact or partial match, it is
|
||||
// out of bailiwick, which is considered an error.
|
||||
isc_throw(OutOfZone, name.toText() << " not in " <<
|
||||
zone_data.getOriginNode()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
// Specialization of the ZoneFinder::Context for the in-memory finder.
|
||||
class InMemoryZoneFinder::Context : public ZoneFinder::Context {
|
||||
public:
|
||||
/// \brief Constructor.
|
||||
///
|
||||
/// Note that we don't have a specific constructor for the findAll() case.
|
||||
/// For (successful) type ANY query, found_node points to the
|
||||
/// corresponding RB node, which is recorded within this specialized
|
||||
/// context.
|
||||
Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
|
||||
ZoneFinderResultContext result) :
|
||||
ZoneFinder::Context(finder, options,
|
||||
ResultContext(result.code, result.rrset,
|
||||
result.flags)),
|
||||
rrset_(result.rrset), found_node_(result.found_node)
|
||||
{}
|
||||
|
||||
private:
|
||||
const TreeNodeRRsetPtr rrset_;
|
||||
const ZoneNode* const found_node_;
|
||||
};
|
||||
|
||||
boost::shared_ptr<ZoneFinder::Context>
|
||||
InMemoryZoneFinder::find(const isc::dns::Name& name,
|
||||
const isc::dns::RRType& type,
|
||||
const FindOptions options)
|
||||
{
|
||||
return ZoneFinderContextPtr(new Context(*this, options,
|
||||
find_internal(name,
|
||||
type,
|
||||
NULL,
|
||||
options)));
|
||||
}
|
||||
|
||||
boost::shared_ptr<ZoneFinder::Context>
|
||||
InMemoryZoneFinder::findAll(const isc::dns::Name& name,
|
||||
std::vector<isc::dns::ConstRRsetPtr>& target,
|
||||
const FindOptions options)
|
||||
{
|
||||
return ZoneFinderContextPtr(new Context(*this, options,
|
||||
find_internal(name,
|
||||
RRType::ANY(),
|
||||
&target,
|
||||
options)));
|
||||
}
|
||||
|
||||
ZoneFinderResultContext
|
||||
InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
|
||||
const isc::dns::RRType& type,
|
||||
std::vector<ConstRRsetPtr>* target,
|
||||
const FindOptions options)
|
||||
{
|
||||
// Get the node. All other cases than an exact match are handled
|
||||
// in findNode(). We simply construct a result structure and return.
|
||||
ZoneChain node_path;
|
||||
const FindNodeResult node_result =
|
||||
findNode(zone_data_, name, node_path, options);
|
||||
if (node_result.code != SUCCESS) {
|
||||
return (createFindResult(rrclass_, zone_data_, node_result.code,
|
||||
node_result.rrset, node_result.node));
|
||||
}
|
||||
|
||||
const ZoneNode* node = node_result.node;
|
||||
assert(node != NULL);
|
||||
|
||||
// We've found an exact match, may or may not be a result of wildcard.
|
||||
const bool wild = ((node_result.flags &
|
||||
FindNodeResult::FIND_WILDCARD) != 0);
|
||||
|
||||
// If there is an exact match but the node is empty, it's equivalent
|
||||
// to NXRRSET.
|
||||
if (node->isEmpty()) {
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
|
||||
arg(name);
|
||||
const ZoneNode* nsec_node;
|
||||
const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
|
||||
&nsec_node, options);
|
||||
return (createFindResult(rrclass_, zone_data_, NXRRSET,
|
||||
nsec_rds,
|
||||
nsec_node,
|
||||
wild));
|
||||
}
|
||||
|
||||
const RdataSet* found;
|
||||
|
||||
// If the node callback is enabled, this may be a zone cut. If it
|
||||
// has a NS RR, we should return a delegation, but not in the apex.
|
||||
// There is one exception: the case for DS query, which should always
|
||||
// be considered in-zone lookup.
|
||||
if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
|
||||
node != zone_data_.getOriginNode() && type != RRType::DS()) {
|
||||
found = RdataSet::find(node->getData(), RRType::NS());
|
||||
if (found != NULL) {
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA,
|
||||
DATASRC_MEM_EXACT_DELEGATION).arg(name);
|
||||
return (createFindResult(rrclass_, zone_data_, DELEGATION,
|
||||
found, node, wild, &name));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle type any query
|
||||
if (target != NULL && node->getData() != NULL) {
|
||||
// Empty domain will be handled as NXRRSET by normal processing
|
||||
const RdataSet* cur_rds = node->getData();
|
||||
while (cur_rds != NULL) {
|
||||
target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
|
||||
&name));
|
||||
cur_rds = cur_rds->getNext();
|
||||
}
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
|
||||
arg(name);
|
||||
return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
|
||||
wild, &name));
|
||||
}
|
||||
|
||||
const RdataSet* currds = node->getData();
|
||||
while (currds != NULL) {
|
||||
currds = currds->getNext();
|
||||
}
|
||||
found = RdataSet::find(node->getData(), type);
|
||||
if (found != NULL) {
|
||||
// Good, it is here
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
|
||||
arg(type);
|
||||
return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
|
||||
wild, &name));
|
||||
} else {
|
||||
// Next, try CNAME.
|
||||
found = RdataSet::find(node->getData(), RRType::CNAME());
|
||||
if (found != NULL) {
|
||||
|
||||
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
|
||||
return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
|
||||
wild, &name));
|
||||
}
|
||||
}
|
||||
// No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
|
||||
return (createFindResult(rrclass_, zone_data_, NXRRSET,
|
||||
getNSECForNXRRSET(zone_data_, options, node),
|
||||
node, wild, &name));
|
||||
}
|
||||
|
||||
isc::datasrc::ZoneFinder::FindNSEC3Result
|
||||
InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
|
||||
(void)name;
|
||||
(void)recursive;
|
||||
isc_throw(isc::NotImplemented, "not completed yet! please implement me");
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
} // namespace datasrc
|
||||
} // namespace isc
|
128
src/lib/datasrc/memory/zone_finder.h
Normal file
128
src/lib/datasrc/memory/zone_finder.h
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2012 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 DATASRC_MEMORY_ZONE_FINDER_H
|
||||
#define DATASRC_MEMORY_ZONE_FINDER_H 1
|
||||
|
||||
#include <datasrc/memory/zone_data.h>
|
||||
#include <datasrc/memory/treenode_rrset.h>
|
||||
|
||||
#include <datasrc/zone.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rrset.h>
|
||||
#include <dns/rrtype.h>
|
||||
|
||||
namespace isc {
|
||||
namespace datasrc {
|
||||
namespace memory {
|
||||
|
||||
class ZoneFinderResultContext {
|
||||
public:
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// The first three parameters correspond to those of
|
||||
/// ZoneFinder::ResultContext. If node is non NULL, it specifies the
|
||||
/// found RBNode in the search.
|
||||
ZoneFinderResultContext(ZoneFinder::Result code_param,
|
||||
TreeNodeRRsetPtr rrset_param,
|
||||
ZoneFinder::FindResultFlags flags_param,
|
||||
const ZoneNode* node) :
|
||||
code(code_param), rrset(rrset_param), flags(flags_param),
|
||||
found_node(node)
|
||||
{}
|
||||
|
||||
const ZoneFinder::Result code;
|
||||
const TreeNodeRRsetPtr rrset;
|
||||
const ZoneFinder::FindResultFlags flags;
|
||||
const ZoneNode* const found_node;
|
||||
};
|
||||
|
||||
/// A derived zone finder class intended to be used with the memory data
|
||||
/// source, using ZoneData for its contents.
|
||||
class InMemoryZoneFinder : boost::noncopyable, public ZoneFinder {
|
||||
public:
|
||||
/// \brief Constructor.
|
||||
///
|
||||
/// Since ZoneData does not keep RRClass information, but this
|
||||
/// information is needed in order to construct actual RRsets,
|
||||
/// this needs to be passed here (the datasource client should
|
||||
/// have this information). In the future, this may be replaced
|
||||
/// by some construction to pull TreeNodeRRsets from a pool, but
|
||||
/// currently, these are created dynamically with the given RRclass
|
||||
///
|
||||
/// \param zone_data The ZoneData containing the zone.
|
||||
/// \param rrclass The RR class of the zone
|
||||
InMemoryZoneFinder(const ZoneData& zone_data,
|
||||
const isc::dns::RRClass& rrclass) :
|
||||
zone_data_(zone_data),
|
||||
rrclass_(rrclass)
|
||||
{}
|
||||
|
||||
/// \brief Find an RRset in the datasource
|
||||
virtual boost::shared_ptr<ZoneFinder::Context> find(
|
||||
const isc::dns::Name& name,
|
||||
const isc::dns::RRType& type,
|
||||
const FindOptions options = FIND_DEFAULT);
|
||||
|
||||
/// \brief Version of find that returns all types at once
|
||||
///
|
||||
/// It acts the same as find, just that when the correct node is found,
|
||||
/// all the RRsets are filled into the target parameter instead of being
|
||||
/// returned by the result.
|
||||
virtual boost::shared_ptr<ZoneFinder::Context> findAll(
|
||||
const isc::dns::Name& name,
|
||||
std::vector<isc::dns::ConstRRsetPtr>& target,
|
||||
const FindOptions options = FIND_DEFAULT);
|
||||
|
||||
/// Look for NSEC3 for proving (non)existence of given name.
|
||||
///
|
||||
/// See documentation in \c Zone.
|
||||
virtual FindNSEC3Result
|
||||
findNSEC3(const isc::dns::Name& name, bool recursive);
|
||||
|
||||
/// \brief Returns the origin of the zone.
|
||||
virtual isc::dns::Name getOrigin() const {
|
||||
return zone_data_.getOriginNode()->getName();
|
||||
}
|
||||
|
||||
/// \brief Returns the RR class of the zone.
|
||||
virtual isc::dns::RRClass getClass() const {
|
||||
return rrclass_;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/// \brief In-memory version of finder context.
|
||||
///
|
||||
/// The implementation (and any specialized interface) is completely local
|
||||
/// to the InMemoryZoneFinder class, so it's defined as private
|
||||
class Context;
|
||||
|
||||
/// Actual implementation for both find() and findAll()
|
||||
ZoneFinderResultContext find_internal(
|
||||
const isc::dns::Name& name,
|
||||
const isc::dns::RRType& type,
|
||||
std::vector<isc::dns::ConstRRsetPtr>* target,
|
||||
const FindOptions options =
|
||||
FIND_DEFAULT);
|
||||
|
||||
const ZoneData& zone_data_;
|
||||
const isc::dns::RRClass& rrclass_;
|
||||
};
|
||||
|
||||
} // namespace memory
|
||||
} // namespace datasrc
|
||||
} // namespace isc
|
||||
|
||||
#endif // DATASRC_MEMORY_ZONE_FINDER_H
|
@@ -132,6 +132,20 @@ ZoneTable::findZone(const Name& name) const {
|
||||
return (FindResult(my_result, node->getData()));
|
||||
}
|
||||
|
||||
ZoneTable::FindResult
|
||||
ZoneTable::setZoneData(const Name& name, ZoneData* data)
|
||||
{
|
||||
ZoneTableNode* node(NULL);
|
||||
|
||||
ZoneTableTree::Result result(zones_->find(name, &node));
|
||||
|
||||
if (result != ZoneTableTree::EXACTMATCH) {
|
||||
return (FindResult(result::NOTFOUND, NULL));
|
||||
} else {
|
||||
return (FindResult(result::SUCCESS, node->setData(data)));
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace memory
|
||||
} // end of namespace datasrc
|
||||
} // end of namespace isc
|
||||
|
@@ -86,11 +86,11 @@ public:
|
||||
/// \brief Result data of findZone() method.
|
||||
struct FindResult {
|
||||
FindResult(result::Result param_code,
|
||||
const ZoneData* param_zone_data) :
|
||||
ZoneData* param_zone_data) :
|
||||
code(param_code), zone_data(param_zone_data)
|
||||
{}
|
||||
const result::Result code;
|
||||
const ZoneData* const zone_data;
|
||||
ZoneData* const zone_data;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -185,6 +185,16 @@ public:
|
||||
/// \return A \c FindResult object enclosing the search result (see above).
|
||||
FindResult findZone(const isc::dns::Name& name) const;
|
||||
|
||||
/// Override the ZoneData for a node (zone) in the zone tree.
|
||||
///
|
||||
/// \throw none
|
||||
///
|
||||
/// \param name A domain name for which the zone data is set.
|
||||
/// \param data The new zone data to set.
|
||||
/// \return A \c FindResult object containing the old data if the
|
||||
/// zone was found.
|
||||
FindResult setZoneData(const isc::dns::Name& name, ZoneData* data);
|
||||
|
||||
private:
|
||||
boost::interprocess::offset_ptr<ZoneTableTree> zones_;
|
||||
};
|
||||
|
@@ -30,6 +30,9 @@ libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
|
||||
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
|
||||
|
||||
libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h
|
||||
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
|
||||
libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
|
||||
libb10_dhcpsrv_la_SOURCES += triplet.h
|
||||
libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
|
||||
libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
|
||||
|
@@ -14,46 +14,80 @@
|
||||
|
||||
#include <dhcp/addr_utilities.h>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
uint8_t len) {
|
||||
|
||||
static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
|
||||
uint8_t packed[16];
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// First we copy the whole address as 16 bytes.
|
||||
memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
|
||||
|
||||
// If the length is divisible by 8, it is simple. We just zero out the host
|
||||
// part. Otherwise we need to handle the byte that has to be partially
|
||||
// zeroed.
|
||||
if (len % 8 != 0) {
|
||||
|
||||
// Get the appropriate mask. It has relevant bits (those that should
|
||||
// stay) set and irrelevant (those that should be wiped) cleared.
|
||||
uint8_t mask = bitMask[len % 8];
|
||||
|
||||
// Let's leave only whatever the mask says should not be cleared.
|
||||
packed[len / 8] = packed[len / 8] & mask;
|
||||
len = (len/8 + 1) * 8;
|
||||
|
||||
// Since we have just dealt with this byte, let's move the prefix length
|
||||
// to the beginning of the next byte (len is expressed in bits).
|
||||
len = (len / 8 + 1) * 8;
|
||||
}
|
||||
for (int i = len / 8; i < 16; ++i) {
|
||||
|
||||
// Clear out the remaining bits.
|
||||
for (int i = len / 8; i < sizeof(packed); ++i) {
|
||||
packed[i] = 0x0;
|
||||
}
|
||||
|
||||
// Finally, let's wrap this into nice and easy IOAddress object.
|
||||
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
uint8_t len) {
|
||||
|
||||
static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
|
||||
uint8_t packed[16];
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// First we copy the whole address as 16 bytes.
|
||||
memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
|
||||
|
||||
// if the length is divisible by 8, it is simple. We just fill the host part
|
||||
// with ones. Otherwise we need to handle the byte that has to be partially
|
||||
// zeroed.
|
||||
if (len % 8 != 0) {
|
||||
// Get the appropriate mask. It has relevant bits (those that should
|
||||
// stay) set and irrelevant (those that should be set to 1) cleared.
|
||||
uint8_t mask = bitMask[len % 8];
|
||||
|
||||
// Let's set those irrelevant bits with 1. It would be perhaps
|
||||
// easier to not use negation here and invert bitMask content. However,
|
||||
// with this approach, we can use the same mask in first and last
|
||||
// address calculations.
|
||||
packed[len / 8] = packed[len / 8] | ~mask;
|
||||
len = (len/8 + 1) * 8;
|
||||
|
||||
// Since we have just dealt with this byte, let's move the prefix length
|
||||
// to the beginning of the next byte (len is expressed in bits).
|
||||
len = (len / 8 + 1) * 8;
|
||||
}
|
||||
for (int i = len / 8; i < 16; ++i) {
|
||||
|
||||
// Finally set remaining bits to 1.
|
||||
for (int i = len / 8; i < sizeof(packed); ++i) {
|
||||
packed[i] = 0xff;
|
||||
}
|
||||
|
||||
// Finally, let's wrap this into nice and easy IOAddress object.
|
||||
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
|
@@ -18,32 +18,36 @@ namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski,
|
||||
/// as a sole creater of that code hereby release it under BSD license for the benefit
|
||||
/// as a sole creator of that code hereby release it under BSD license for the benefit
|
||||
/// of the BIND10 project.
|
||||
|
||||
/// @brief returns a first address in a given prefix
|
||||
///
|
||||
/// Example: For 2001:db8:1::deaf:beef and length /120 the function will return
|
||||
/// 2001:db8:1::dead:bee0. See also @ref lastAddrInPrefix.
|
||||
/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix.
|
||||
///
|
||||
/// @todo It currently works for v6 only and will throw if v4 address is passed.
|
||||
///
|
||||
/// @param prefix and address that belongs to a prefix
|
||||
/// @param len prefix length
|
||||
///
|
||||
/// @return first address from a prefix
|
||||
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len);
|
||||
uint8_t len);
|
||||
|
||||
/// @brief returns a last address in a given prefix
|
||||
///
|
||||
/// Example: For 2001:db8:1::deaf:beef and length /112 the function will return
|
||||
/// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix.
|
||||
///
|
||||
/// @todo It currently works for v6 only and will throw if v4 address is passed.
|
||||
///
|
||||
/// @param prefix and address that belongs to a prefix
|
||||
/// @param len prefix length
|
||||
///
|
||||
/// @return first address from a prefix
|
||||
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len);
|
||||
uint8_t len);
|
||||
|
||||
};
|
||||
};
|
||||
|
@@ -12,7 +12,6 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <dhcp/addr_utilities.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/cfgmgr.h>
|
||||
|
||||
@@ -22,125 +21,7 @@ using namespace isc::util;
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
Pool::Pool(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:id_(getNextID()), first_(first), last_(last) {
|
||||
}
|
||||
|
||||
bool Pool::inRange(const isc::asiolink::IOAddress& addr) {
|
||||
return ( first_.smallerEqual(addr) && addr.smallerEqual(last_) );
|
||||
}
|
||||
|
||||
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:Pool(first, last), type_(type), prefix_len_(0) {
|
||||
|
||||
// check if specified address boundaries are sane
|
||||
if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
|
||||
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
|
||||
}
|
||||
|
||||
if (last < first) {
|
||||
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
|
||||
// This check is a bit strict. If we decide that it is too strict,
|
||||
// we need to comment it and uncomment lines below.
|
||||
// On one hand, letting the user specify 2001::f - 2001::1 is nice, but
|
||||
// on the other hand, 2001::1 may be a typo and the user really meant
|
||||
// 2001::1:0 (or 1something), so a at least a warning would be useful.
|
||||
|
||||
// first_ = last;
|
||||
// last_ = first;
|
||||
}
|
||||
|
||||
// TYPE_PD is not supported by this constructor. first-last style
|
||||
// parameters are for IA and TA only. There is another dedicated
|
||||
// constructor for that (it uses prefix/length)
|
||||
if ((type != TYPE_IA) && (type != TYPE_TA)) {
|
||||
isc_throw(BadValue, "Invalid Pool6 type specified");
|
||||
}
|
||||
}
|
||||
|
||||
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len)
|
||||
:Pool(prefix, IOAddress("::")),
|
||||
type_(type), prefix_len_(prefix_len) {
|
||||
|
||||
// check if the prefix is sane
|
||||
if (prefix.getFamily() != AF_INET6) {
|
||||
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
|
||||
}
|
||||
|
||||
// check if the prefix length is sane
|
||||
if (prefix_len == 0 || prefix_len > 128) {
|
||||
isc_throw(BadValue, "Invalid prefix length");
|
||||
}
|
||||
|
||||
// Let's now calculate the last address in defined pool
|
||||
last_ = lastAddrInPrefix(prefix, prefix_len);
|
||||
}
|
||||
|
||||
Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& valid_lifetime)
|
||||
:id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
|
||||
t2_(t2), valid_(valid_lifetime) {
|
||||
if ( (prefix.getFamily() == AF_INET6 && len > 128) ||
|
||||
(prefix.getFamily() == AF_INET && len > 32) ) {
|
||||
isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
|
||||
}
|
||||
}
|
||||
|
||||
bool Subnet::inRange(const isc::asiolink::IOAddress& addr) {
|
||||
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
|
||||
IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
|
||||
|
||||
return ( (first <= addr) && (addr <= last) );
|
||||
}
|
||||
|
||||
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& preferred_lifetime,
|
||||
const Triplet<uint32_t>& valid_lifetime)
|
||||
:Subnet(prefix, length, t1, t2, valid_lifetime),
|
||||
preferred_(preferred_lifetime){
|
||||
if (prefix.getFamily() != AF_INET6) {
|
||||
isc_throw(BadValue, "Invalid prefix " << prefix.toText()
|
||||
<< " specified in subnet6");
|
||||
}
|
||||
}
|
||||
|
||||
void Subnet6::addPool6(const Pool6Ptr& pool) {
|
||||
IOAddress first_addr = pool->getFirstAddress();
|
||||
IOAddress last_addr = pool->getLastAddress();
|
||||
|
||||
if (!inRange(first_addr) || !inRange(last_addr)) {
|
||||
isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText()
|
||||
<< " does not belong in this (" << prefix_ << "/" << prefix_len_
|
||||
<< ") subnet6");
|
||||
}
|
||||
|
||||
pools_.push_back(pool);
|
||||
}
|
||||
|
||||
Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
|
||||
Pool6Ptr candidate;
|
||||
for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
|
||||
|
||||
// if we won't find anything better, then let's just use the first pool
|
||||
if (!candidate) {
|
||||
candidate = *pool;
|
||||
}
|
||||
|
||||
// if the client provided a pool and there's a pool that hint is valid in,
|
||||
// then let's use that pool
|
||||
if ((*pool)->inRange(hint)) {
|
||||
return (*pool);
|
||||
}
|
||||
}
|
||||
return (candidate);
|
||||
}
|
||||
|
||||
|
||||
CfgMgr&
|
||||
@@ -153,6 +34,13 @@ Subnet6Ptr
|
||||
CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
|
||||
|
||||
// If there's only one subnet configured, let's just use it
|
||||
// The idea is to keep small deployments easy. In a small network - one
|
||||
// router that also runs DHCPv6 server. Users specifies a single pool and
|
||||
// expects it to just work. Without this, the server would complain that it
|
||||
// doesn't have IP address on its interfaces that matches that
|
||||
// configuration. Such requirement makes sense in IPv4, but not in IPv6.
|
||||
// The server does not need to have a global address (using just link-local
|
||||
// is ok for DHCPv6 server) from the pool it serves.
|
||||
if (subnets6_.size() == 1) {
|
||||
return (subnets6_[0]);
|
||||
}
|
||||
|
@@ -23,331 +23,12 @@
|
||||
#include <asiolink/io_address.h>
|
||||
#include <util/buffer.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/pool.h>
|
||||
#include <dhcp/subnet.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
class Pool6;
|
||||
|
||||
class Subnet6;
|
||||
|
||||
/// @brief this template specifes a parameter value
|
||||
///
|
||||
/// This template class is used to store configuration parameters, like lifetime or T1.
|
||||
/// It defines 3 parameters: min, default, and max value. There are 2 constructors:
|
||||
/// - simple (just one value that sets all parameters)
|
||||
/// - extended (that sets default value and two thresholds)
|
||||
/// It will be used with integer types. It provides necessary operators, so
|
||||
/// it can be assigned to a plain integer or integer assigned to a Triplet.
|
||||
/// See TripletTest.operator test for details on an easy Triplet usage.
|
||||
template <class T>
|
||||
class Triplet {
|
||||
public:
|
||||
|
||||
/// @brief base type to Triple conversion
|
||||
///
|
||||
/// Typically: uint32_t to Triplet assignment. It is very convenient
|
||||
/// to be able to simply write Triplet<uint32_t> x = 7;
|
||||
Triplet<T>& operator = (T base_type) {
|
||||
return Triplet<T>(base_type);
|
||||
}
|
||||
|
||||
/// @brief triplet to base type conversion
|
||||
///
|
||||
/// Typically: Triplet to uint32_t assignment. It is very convenient
|
||||
/// to be able to simply write uint32_t z = x; (where x is a Triplet)
|
||||
operator T () const {
|
||||
return (default_);
|
||||
}
|
||||
|
||||
/// @brief sets a fixed value
|
||||
///
|
||||
/// This constructor assigns a fixed (i.e. no range, just a single value)
|
||||
/// value.
|
||||
Triplet(T value)
|
||||
:min_(value), default_(value), max_(value) {
|
||||
}
|
||||
|
||||
/// @brief sets the default value and thresholds
|
||||
///
|
||||
/// @throw BadValue if min <= def <= max rule is violated
|
||||
Triplet(T min, T def, T max)
|
||||
:min_(min), default_(def), max_(max) {
|
||||
if ( (min_>def) || (def > max_) ) {
|
||||
isc_throw(BadValue, "Invalid triplet values.");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief returns a minimum allowed value
|
||||
T getMin() const { return min_;}
|
||||
|
||||
/// @brief returns the default value
|
||||
T get() const { return default_;}
|
||||
|
||||
/// @brief returns value with a hint
|
||||
///
|
||||
/// DHCP protocol treats any values sent by a client as hints.
|
||||
/// This is a method that implements that. We can assign any value
|
||||
/// from configured range that client asks.
|
||||
T get(T hint) const {
|
||||
if (hint <= min_) {
|
||||
return (min_);
|
||||
}
|
||||
|
||||
if (hint >= max_) {
|
||||
return (max_);
|
||||
}
|
||||
|
||||
return (hint);
|
||||
}
|
||||
|
||||
/// @brief returns a maximum allowed value
|
||||
T getMax() const { return max_; }
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief the minimum value
|
||||
T min_;
|
||||
|
||||
/// @brief the default value
|
||||
T default_;
|
||||
|
||||
/// @brief the maximum value
|
||||
T max_;
|
||||
};
|
||||
|
||||
|
||||
/// @brief base class for Pool4 and Pool6
|
||||
///
|
||||
/// Stores information about pool of IPv4 or IPv6 addresses.
|
||||
/// That is a basic component of a configuration.
|
||||
class Pool {
|
||||
|
||||
public:
|
||||
|
||||
/// @brief returns Pool-id
|
||||
///
|
||||
/// Pool-id is an unique value that can be used to identify a pool.
|
||||
uint32_t getId() const {
|
||||
return (id_);
|
||||
}
|
||||
|
||||
/// @brief Returns the first address in a pool.
|
||||
///
|
||||
/// @return first address in a pool
|
||||
const isc::asiolink::IOAddress& getFirstAddress() const {
|
||||
return (first_);
|
||||
}
|
||||
|
||||
/// @brief Returns the last address in a pool.
|
||||
/// @return last address in a pool
|
||||
const isc::asiolink::IOAddress& getLastAddress() const {
|
||||
return (last_);
|
||||
}
|
||||
|
||||
/// @brief Checks if a given address is in the range.
|
||||
///
|
||||
/// @return true, if the address is in pool
|
||||
bool inRange(const isc::asiolink::IOAddress& addr);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief protected constructor
|
||||
///
|
||||
/// This constructor is protected to prevent anyone from instantiating
|
||||
/// Pool class directly. Instances of Pool4 and Pool6 should be created
|
||||
/// instead.
|
||||
Pool(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last);
|
||||
|
||||
/// @brief returns the next unique Pool-ID
|
||||
///
|
||||
/// @return the next unique Pool-ID
|
||||
static uint32_t getNextID() {
|
||||
static uint32_t id = 0;
|
||||
return (id++);
|
||||
}
|
||||
|
||||
/// @brief pool-id
|
||||
///
|
||||
/// This ID is used to indentify this specific pool.
|
||||
uint32_t id_;
|
||||
|
||||
/// @brief The first address in a pool
|
||||
isc::asiolink::IOAddress first_;
|
||||
|
||||
/// @brief The last address in a pool
|
||||
isc::asiolink::IOAddress last_;
|
||||
|
||||
/// @brief Comments field
|
||||
///
|
||||
/// @todo: This field is currently not used.
|
||||
std::string comments_;
|
||||
};
|
||||
|
||||
/// @brief Pool information for IPv6 addresses and prefixes
|
||||
///
|
||||
/// It holds information about pool6, i.e. a range of IPv6 address space that
|
||||
/// is configured for DHCP allocation.
|
||||
class Pool6 : public Pool {
|
||||
public:
|
||||
|
||||
/// @brief specifies Pool type
|
||||
///
|
||||
/// Currently there are 3 pool types defined in DHCPv6:
|
||||
/// - Non-temporary addresses (conveyed in IA_NA)
|
||||
/// - Temporary addresses (conveyed in IA_TA)
|
||||
/// - Delegated Prefixes (conveyed in IA_PD)
|
||||
/// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
|
||||
/// support for it is not planned for now.
|
||||
typedef enum {
|
||||
TYPE_IA,
|
||||
TYPE_TA,
|
||||
TYPE_PD
|
||||
} Pool6Type;
|
||||
|
||||
/// @brief the constructor for Pool6 "min-max" style definition
|
||||
///
|
||||
/// @param first the first address in a pool
|
||||
/// @param last the last address in a pool
|
||||
Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last);
|
||||
|
||||
/// @brief the constructor for Pool6 "prefix/len" style definition
|
||||
///
|
||||
/// @param prefix specifies prefix of the pool
|
||||
/// @param prefix_len specifies length of the prefix of the pool
|
||||
Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len);
|
||||
|
||||
/// @brief returns pool type
|
||||
///
|
||||
/// @return pool type
|
||||
Pool6Type getType() const {
|
||||
return (type_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief defines a pool type
|
||||
Pool6Type type_;
|
||||
|
||||
/// @brief prefix length
|
||||
/// used by TYPE_PD only (zeroed for other types)
|
||||
uint8_t prefix_len_;
|
||||
};
|
||||
|
||||
/// @brief a pointer an IPv6 Pool
|
||||
typedef boost::shared_ptr<Pool6> Pool6Ptr;
|
||||
|
||||
/// @brief a container for IPv6 Pools
|
||||
typedef std::vector<Pool6Ptr> Pool6Collection;
|
||||
|
||||
/// @brief a base class for Subnet4 and Subnet6
|
||||
///
|
||||
/// This class presents a common base for IPv4 and IPv6 subnets.
|
||||
/// In a physical sense, a subnet defines a single network link with all devices
|
||||
/// attached to it. In most cases all devices attached to a single link can
|
||||
/// share the same parameters. Therefore Subnet holds several values that are
|
||||
/// typically shared by all hosts: renew timer (T1), rebind timer (T2) and
|
||||
/// leased addresses lifetime (valid-lifetime).
|
||||
///
|
||||
/// @todo: Implement support for options here
|
||||
class Subnet {
|
||||
public:
|
||||
/// @brief checks if specified address is in range
|
||||
bool inRange(const isc::asiolink::IOAddress& addr);
|
||||
|
||||
/// @brief return valid-lifetime for addresses in that prefix
|
||||
Triplet<uint32_t> getValid() const {
|
||||
return (valid_);
|
||||
}
|
||||
|
||||
/// @brief returns T1 (renew timer), expressed in seconds
|
||||
Triplet<uint32_t> getT1() const {
|
||||
return (t1_);
|
||||
}
|
||||
|
||||
/// @brief returns T2 (rebind timer), expressed in seconds
|
||||
Triplet<uint32_t> getT2() const {
|
||||
return (t2_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief protected constructor
|
||||
//
|
||||
/// By making the constructor protected, we make sure that noone will
|
||||
/// ever instantiate that class. Pool4 and Pool6 should be used instead.
|
||||
Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& valid_lifetime);
|
||||
|
||||
/// @brief returns the next unique Subnet-ID
|
||||
///
|
||||
/// @return the next unique Subnet-ID
|
||||
static uint32_t getNextID() {
|
||||
static uint32_t id = 0;
|
||||
return (id++);
|
||||
}
|
||||
|
||||
/// @brief subnet-id
|
||||
///
|
||||
/// Subnet-id is a unique value that can be used to find or identify
|
||||
/// a Subnet4 or Subnet6.
|
||||
uint32_t id_;
|
||||
|
||||
/// @brief a prefix of the subnet
|
||||
isc::asiolink::IOAddress prefix_;
|
||||
|
||||
/// @brief a prefix length of the subnet
|
||||
uint8_t prefix_len_;
|
||||
|
||||
/// @brief a tripet (min/default/max) holding allowed renew timer values
|
||||
Triplet<uint32_t> t1_;
|
||||
|
||||
/// @brief a tripet (min/default/max) holding allowed rebind timer values
|
||||
Triplet<uint32_t> t2_;
|
||||
|
||||
/// @brief a tripet (min/default/max) holding allowed valid lifetime values
|
||||
Triplet<uint32_t> valid_;
|
||||
};
|
||||
|
||||
/// @brief A configuration holder for IPv6 subnet.
|
||||
///
|
||||
/// This class represents an IPv6 subnet.
|
||||
class Subnet6 : public Subnet {
|
||||
public:
|
||||
Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& preferred_lifetime,
|
||||
const Triplet<uint32_t>& valid_lifetime);
|
||||
|
||||
Triplet<uint32_t> getPreferred() const {
|
||||
return (preferred_);
|
||||
}
|
||||
|
||||
Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint =
|
||||
isc::asiolink::IOAddress("::"));
|
||||
|
||||
void addPool6(const Pool6Ptr& pool);
|
||||
|
||||
const Pool6Collection& getPools() const {
|
||||
return pools_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// collection of pools in that list
|
||||
Pool6Collection pools_;
|
||||
|
||||
Triplet<uint32_t> preferred_;
|
||||
};
|
||||
|
||||
/// @brief A pointer to a Subnet6 object
|
||||
typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
|
||||
|
||||
/// @brief A collection of Subnet6 objects
|
||||
typedef std::vector<Subnet6Ptr> Subnet6Collection;
|
||||
|
||||
/// @brief Configuration Manager
|
||||
///
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-2012 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
|
||||
@@ -727,6 +727,13 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
|
||||
m.msg_control = &control_buf_[0];
|
||||
m.msg_controllen = control_buf_len_;
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
|
||||
|
||||
// FIXME: Code below assumes that cmsg is not NULL, but
|
||||
// CMSG_FIRSTHDR() is coded to return NULL as a possibility. The
|
||||
// following assertion should never fail, but if it did and you came
|
||||
// here, fix the code. :)
|
||||
assert(cmsg != NULL);
|
||||
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
@@ -813,8 +820,12 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
|
||||
|
||||
|
||||
boost::shared_ptr<Pkt4>
|
||||
IfaceMgr::receive4(uint32_t timeout) {
|
||||
|
||||
IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
|
||||
// Sanity check for microsecond timeout.
|
||||
if (timeout_usec >= 1000000) {
|
||||
isc_throw(BadValue, "fractional timeout must be shorter than"
|
||||
" one million microseconds");
|
||||
}
|
||||
const SocketInfo* candidate = 0;
|
||||
IfaceCollection::const_iterator iface;
|
||||
fd_set sockets;
|
||||
@@ -854,13 +865,13 @@ IfaceMgr::receive4(uint32_t timeout) {
|
||||
names << session_socket_ << "(session)";
|
||||
}
|
||||
|
||||
/// @todo: implement sub-second precision one day
|
||||
struct timeval select_timeout;
|
||||
select_timeout.tv_sec = timeout;
|
||||
select_timeout.tv_usec = 0;
|
||||
select_timeout.tv_sec = timeout_sec;
|
||||
select_timeout.tv_usec = timeout_usec;
|
||||
|
||||
cout << "Trying to receive data on sockets: " << names.str()
|
||||
<< ". Timeout is " << timeout << " seconds." << endl;
|
||||
<< ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
|
||||
<< timeout_usec << " seconds." << endl;
|
||||
int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
|
||||
cout << "select returned " << result << endl;
|
||||
|
||||
@@ -983,7 +994,12 @@ IfaceMgr::receive4(uint32_t timeout) {
|
||||
return (pkt);
|
||||
}
|
||||
|
||||
Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
|
||||
Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) {
|
||||
// Sanity check for microsecond timeout.
|
||||
if (timeout_usec >= 1000000) {
|
||||
isc_throw(BadValue, "fractional timeout must be shorter than"
|
||||
" one million microseconds");
|
||||
}
|
||||
|
||||
const SocketInfo* candidate = 0;
|
||||
fd_set sockets;
|
||||
@@ -1023,13 +1039,13 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
|
||||
names << session_socket_ << "(session)";
|
||||
}
|
||||
|
||||
cout << "Trying to receive data on sockets:" << names.str()
|
||||
<< ".Timeout is " << timeout << " seconds." << endl;
|
||||
cout << "Trying to receive data on sockets: " << names.str()
|
||||
<< ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
|
||||
<< timeout_usec << " seconds." << endl;
|
||||
|
||||
/// @todo: implement sub-second precision one day
|
||||
struct timeval select_timeout;
|
||||
select_timeout.tv_sec = timeout;
|
||||
select_timeout.tv_usec = 0;
|
||||
select_timeout.tv_sec = timeout_sec;
|
||||
select_timeout.tv_usec = timeout_usec;
|
||||
|
||||
int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-2012 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
|
||||
@@ -364,10 +364,13 @@ public:
|
||||
/// to not wait infinitely, but rather do something useful
|
||||
/// (e.g. remove expired leases)
|
||||
///
|
||||
/// @param timeout specifies timeout (in seconds)
|
||||
/// @param timeout_sec specifies integral part of the timeout (in seconds)
|
||||
/// @param timeout_usec specifies fractional part of the timeout
|
||||
/// (in microseconds)
|
||||
///
|
||||
/// @throw isc::BadValue if timeout_usec is greater than one million
|
||||
/// @return Pkt6 object representing received packet (or NULL)
|
||||
Pkt6Ptr receive6(uint32_t timeout);
|
||||
Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0);
|
||||
|
||||
/// @brief Tries to receive IPv4 packet over open IPv4 sockets.
|
||||
///
|
||||
@@ -375,10 +378,13 @@ public:
|
||||
/// If reception is successful and all information about its sender
|
||||
/// are obtained, Pkt4 object is created and returned.
|
||||
///
|
||||
/// @param timeout specifies timeout (in seconds)
|
||||
/// @param timeout_sec specifies integral part of the timeout (in seconds)
|
||||
/// @param timeout_usec specifies fractional part of the timeout
|
||||
/// (in microseconds)
|
||||
///
|
||||
/// @throw isc::BadValue if timeout_usec is greater than one million
|
||||
/// @return Pkt4 object representing received packet (or NULL)
|
||||
Pkt4Ptr receive4(uint32_t timeout);
|
||||
Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0);
|
||||
|
||||
/// Opens UDP/IP socket and binds it to address, interface and port.
|
||||
///
|
||||
|
87
src/lib/dhcp/pool.cc
Normal file
87
src/lib/dhcp/pool.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2012 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 <asiolink/io_address.h>
|
||||
#include <dhcp/addr_utilities.h>
|
||||
#include <dhcp/pool.h>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
Pool::Pool(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:id_(getNextID()), first_(first), last_(last) {
|
||||
}
|
||||
|
||||
bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
|
||||
return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
|
||||
}
|
||||
|
||||
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:Pool(first, last), type_(type), prefix_len_(0) {
|
||||
|
||||
// check if specified address boundaries are sane
|
||||
if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
|
||||
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
|
||||
}
|
||||
|
||||
if (last < first) {
|
||||
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
|
||||
// This check is a bit strict. If we decide that it is too strict,
|
||||
// we need to comment it and uncomment lines below.
|
||||
// On one hand, letting the user specify 2001::f - 2001::1 is nice, but
|
||||
// on the other hand, 2001::1 may be a typo and the user really meant
|
||||
// 2001::1:0 (or 1 followed by some hex digit), so a at least a warning
|
||||
// would be useful.
|
||||
|
||||
// first_ = last;
|
||||
// last_ = first;
|
||||
}
|
||||
|
||||
|
||||
// TYPE_PD is not supported by this constructor. first-last style
|
||||
// parameters are for IA and TA only. There is another dedicated
|
||||
// constructor for that (it uses prefix/length)
|
||||
if ((type != TYPE_IA) && (type != TYPE_TA)) {
|
||||
isc_throw(BadValue, "Invalid Pool6 type specified");
|
||||
}
|
||||
}
|
||||
|
||||
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len)
|
||||
:Pool(prefix, IOAddress("::")),
|
||||
type_(type), prefix_len_(prefix_len) {
|
||||
|
||||
// check if the prefix is sane
|
||||
if (prefix.getFamily() != AF_INET6) {
|
||||
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
|
||||
}
|
||||
|
||||
// check if the prefix length is sane
|
||||
if (prefix_len == 0 || prefix_len > 128) {
|
||||
isc_throw(BadValue, "Invalid prefix length");
|
||||
}
|
||||
|
||||
/// @todo: We should probably implement checks against weird addresses
|
||||
/// here, like ::, starting with fe80, starting with ff etc. .
|
||||
|
||||
// Let's now calculate the last address in defined pool
|
||||
last_ = lastAddrInPrefix(prefix, prefix_len);
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
155
src/lib/dhcp/pool.h
Normal file
155
src/lib/dhcp/pool.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (C) 2012 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 POOL_H
|
||||
#define POOL_H
|
||||
|
||||
#include <vector>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief base class for Pool4 and Pool6
|
||||
///
|
||||
/// Stores information about pool of IPv4 or IPv6 addresses.
|
||||
/// That is a basic component of a configuration.
|
||||
class Pool {
|
||||
|
||||
public:
|
||||
|
||||
/// @brief returns Pool-id
|
||||
///
|
||||
/// @return pool-id value
|
||||
/// Pool-id is an unique value that can be used to identify a pool.
|
||||
uint32_t getId() const {
|
||||
return (id_);
|
||||
}
|
||||
|
||||
/// @brief Returns the first address in a pool.
|
||||
///
|
||||
/// @return first address in a pool
|
||||
const isc::asiolink::IOAddress& getFirstAddress() const {
|
||||
return (first_);
|
||||
}
|
||||
|
||||
/// @brief Returns the last address in a pool.
|
||||
/// @return last address in a pool
|
||||
const isc::asiolink::IOAddress& getLastAddress() const {
|
||||
return (last_);
|
||||
}
|
||||
|
||||
/// @brief Checks if a given address is in the range.
|
||||
///
|
||||
/// @return true, if the address is in pool
|
||||
bool inRange(const isc::asiolink::IOAddress& addr) const;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief protected constructor
|
||||
///
|
||||
/// This constructor is protected to prevent anyone from instantiating
|
||||
/// Pool class directly. Instances of Pool4 and Pool6 should be created
|
||||
/// instead.
|
||||
Pool(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last);
|
||||
|
||||
/// @brief returns the next unique Pool-ID
|
||||
///
|
||||
/// @return the next unique Pool-ID
|
||||
static uint32_t getNextID() {
|
||||
static uint32_t id = 0;
|
||||
return (id++);
|
||||
}
|
||||
|
||||
/// @brief pool-id
|
||||
///
|
||||
/// This ID is used to identify this specific pool.
|
||||
uint32_t id_;
|
||||
|
||||
/// @brief The first address in a pool
|
||||
isc::asiolink::IOAddress first_;
|
||||
|
||||
/// @brief The last address in a pool
|
||||
isc::asiolink::IOAddress last_;
|
||||
|
||||
/// @brief Comments field
|
||||
///
|
||||
/// @todo: This field is currently not used.
|
||||
std::string comments_;
|
||||
};
|
||||
|
||||
/// @brief Pool information for IPv6 addresses and prefixes
|
||||
///
|
||||
/// It holds information about pool6, i.e. a range of IPv6 address space that
|
||||
/// is configured for DHCP allocation.
|
||||
class Pool6 : public Pool {
|
||||
public:
|
||||
|
||||
/// @brief specifies Pool type
|
||||
///
|
||||
/// Currently there are 3 pool types defined in DHCPv6:
|
||||
/// - Non-temporary addresses (conveyed in IA_NA)
|
||||
/// - Temporary addresses (conveyed in IA_TA)
|
||||
/// - Delegated Prefixes (conveyed in IA_PD)
|
||||
/// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
|
||||
/// support for it is not planned for now.
|
||||
typedef enum {
|
||||
TYPE_IA,
|
||||
TYPE_TA,
|
||||
TYPE_PD
|
||||
} Pool6Type;
|
||||
|
||||
/// @brief the constructor for Pool6 "min-max" style definition
|
||||
///
|
||||
/// @param first the first address in a pool
|
||||
/// @param last the last address in a pool
|
||||
Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last);
|
||||
|
||||
/// @brief the constructor for Pool6 "prefix/len" style definition
|
||||
///
|
||||
/// @param prefix specifies prefix of the pool
|
||||
/// @param prefix_len specifies length of the prefix of the pool
|
||||
Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len);
|
||||
|
||||
/// @brief returns pool type
|
||||
///
|
||||
/// @return pool type
|
||||
Pool6Type getType() const {
|
||||
return (type_);
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief defines a pool type
|
||||
Pool6Type type_;
|
||||
|
||||
/// @brief prefix length
|
||||
/// used by TYPE_PD only (zeroed for other types)
|
||||
uint8_t prefix_len_;
|
||||
};
|
||||
|
||||
/// @brief a pointer an IPv6 Pool
|
||||
typedef boost::shared_ptr<Pool6> Pool6Ptr;
|
||||
|
||||
/// @brief a container for IPv6 Pools
|
||||
typedef std::vector<Pool6Ptr> Pool6Collection;
|
||||
|
||||
} // end of isc::dhcp namespace
|
||||
} // end of isc namespace
|
||||
|
||||
|
||||
#endif // POOL_H
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user