mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
sync with trunk for merge
git-svn-id: svn://bind10.isc.org/svn/bind10/experiments/python-binding@2346 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
commit
7cdceb252e
72
ChangeLog
72
ChangeLog
@ -1,11 +1,73 @@
|
||||
53. [bug] zhanglikun
|
||||
65. [func] shentingting
|
||||
Added verbose options to exactly what is happening with loadzone.
|
||||
Added loadzone test suite of different file formats to load.
|
||||
(Trac #197, #199, #244, #161, #198, #174, #175, svn r2340)
|
||||
|
||||
64. [func] jerry
|
||||
Added python logging framework. It is for testing and experimenting
|
||||
with logging ideas. Currently, it supports three channels(file,
|
||||
syslog and stderr) and five levels(debug, info, warning, error and
|
||||
critical).
|
||||
(Trac #176, svn r2338)
|
||||
|
||||
63. [func] shane
|
||||
Added initial support for setuid(), using the "-u" flag. This will
|
||||
be replaced in the future, but for now provides a reasonable
|
||||
starting point.
|
||||
(Trac #180, svn r2330)
|
||||
|
||||
62. [func] jelte
|
||||
bin/xfrin: Use the database_file as configured in Auth to transfers
|
||||
bin/xfrout: Use the database_file as configured in Auth to transfers
|
||||
|
||||
61. [bug] jelte
|
||||
bin/auth: Enable b10-auth to be launched in source tree
|
||||
(i.e. use a zone database file relative to that)
|
||||
|
||||
60. [build] jinmei
|
||||
Supported SunStudio C++ compiler. Note: gtest still doesn't work.
|
||||
(Trac #251, svn r2310)
|
||||
|
||||
59. [bug] jinmei
|
||||
lib/datasrc,bin/auth: The authoritative server could return a
|
||||
SERVFAIL with a partial answer if it finds a data source broken
|
||||
while looking for an answer. This can happen, for example, if a
|
||||
zone that doesn't have an NS RR is configured and loaded as a
|
||||
sqlite3 data source. (Trac #249, r2286)
|
||||
|
||||
58. [bug] jinmei
|
||||
Worked around an interaction issue between ASIO and standard C++
|
||||
library headers. Without this ASIO didn't work: sometimes the
|
||||
application crashes, sometimes it blocked in the ASIO module.
|
||||
(Trac #248, svn r2187, r2190)
|
||||
|
||||
57. [func] jinmei
|
||||
lib/datasrc: used a simpler version of Name::split (change 31) for
|
||||
better readability. No behavior change. (Trac #200, svn r2159)
|
||||
|
||||
56. [func]* jinmei
|
||||
lib/dns: renamed the library name to libdns++ to avoid confusion
|
||||
with the same name of library of BIND 9.
|
||||
(Trac #190, svn r2153)
|
||||
|
||||
55. [bug] shane
|
||||
bin/xfrout: xfrout exception on Ctrl-C now no longer generates
|
||||
exception for 'Interrupted system call'
|
||||
(Track #136, svn r2147)
|
||||
|
||||
54. [bug] zhanglikun
|
||||
bin/xfrout: Enable b10-xfrout can be launched in source
|
||||
code tree.
|
||||
(Trac #224, svn r2103)
|
||||
|
||||
53. [bug] zhanglikun
|
||||
bin/bindctl: Generate a unique session ID by using
|
||||
socket.gethostname() instead of socket.gethostbyname(),
|
||||
since the latter one could make bindctl stall if its own
|
||||
host name can't be resolved.
|
||||
(Trac #228, svn r2096)
|
||||
|
||||
52. [func] zhanglikun
|
||||
52. [func] zhanglikun
|
||||
bin/xfrout: When xfrout is launched, check whether the
|
||||
socket file is being used by one running xfrout process,
|
||||
if it is, exit from python. If the file isn't a socket file
|
||||
@ -67,6 +129,10 @@ bind10-devel-20100602 released on June 2, 2010
|
||||
Renamed libauth to libdatasrc.
|
||||
|
||||
38. [bug] zhanglikun
|
||||
Send command 'shutdown' to Xfrin and Xfrout when boss receive SIGINT.
|
||||
Remove unused socket file when Xfrout process exits. Make sure Xfrout
|
||||
exit by itself when it receives SIGINT, instead of being killed by the
|
||||
signal SIGTERM or SIGKILL sent from boss.
|
||||
(Trac #135, #151, #134, svn r1797)
|
||||
|
||||
37. [build] jinmei
|
||||
@ -127,7 +193,7 @@ bind10-devel-20100421 released on April 21, 2010
|
||||
|
||||
24. [func]
|
||||
Support case-sensitive name compression in MessageRenderer.
|
||||
(svn r1704)
|
||||
(Trac #142, svn r1704)
|
||||
|
||||
23. [func]
|
||||
Support a simple name with possible compression. (svn r1701)
|
||||
|
60
configure.ac
60
configure.ac
@ -9,11 +9,23 @@ AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# Use C++ language
|
||||
AC_LANG_CPLUSPLUS
|
||||
AC_LANG([C++])
|
||||
|
||||
# Identify the compiler: this check must be after AC_PROG_CXX and AC_LANG.
|
||||
AM_CONDITIONAL(USE_GXX, test "X${GXX}" = "Xyes")
|
||||
AC_CHECK_DECL([__SUNPRO_CC], [SUNCXX="yes"], [SUNCXX="no"])
|
||||
|
||||
# OS dependent compiler flags
|
||||
case "$host" in
|
||||
*-solaris*)
|
||||
# Solaris requires special definitions to get some standard libraries
|
||||
# (e.g. getopt(3)) available with common used header files.
|
||||
CPPFLAGS="$CPPFLAGS -D_XPG4_2 -D__EXTENSIONS__"
|
||||
;;
|
||||
esac
|
||||
|
||||
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1])
|
||||
AC_ARG_WITH([pythonpath],
|
||||
@ -93,8 +105,8 @@ AC_SUBST(PYTHON_LIB)
|
||||
|
||||
# TODO: check for _sqlite3.py module
|
||||
|
||||
#
|
||||
# B10_CXXFLAGS is the default C++ compiler flags. This will (and should) be
|
||||
# Compiler dependent settings: define some mandatory CXXFLAGS here.
|
||||
# We also use a separate variable B10_CXXFLAGS. This will (and should) be
|
||||
# used as the default value for each specifc AM_CXXFLAGS:
|
||||
# AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
# AM_CXXFLAGS += ... # add module specific flags
|
||||
@ -103,17 +115,24 @@ AC_SUBST(PYTHON_LIB)
|
||||
# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
|
||||
# specify the default warning flags in CXXFLAGS and let specific modules
|
||||
# "override" the default.
|
||||
#
|
||||
B10_CXXFLAGS=
|
||||
|
||||
if test "X$GCC" = "Xyes"; then
|
||||
B10_CXXFLAGS="-g -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
|
||||
CXXFLAGS=-g
|
||||
werror_ok=0
|
||||
|
||||
# SunStudio compiler requires special compiler options for boost
|
||||
# (http://blogs.sun.com/sga/entry/boost_mini_howto)
|
||||
if test "$SUNCXX" = "yes"; then
|
||||
CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
|
||||
fi
|
||||
|
||||
# gcc specific settings:
|
||||
if test "X$GXX" = "Xyes"; then
|
||||
B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
|
||||
UNUSED_PARAM_ATTRIBUTE='__attribute__((unused))'
|
||||
|
||||
# Certain versions of gcc (g++) have a bug that incorrectly warns about
|
||||
# the use of anonymous name spaces even if they're closed in a single
|
||||
# translation unit. For these versions we have to disable -Werror.
|
||||
werror_ok=0
|
||||
CXXFLAGS_SAVED="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
|
||||
AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
|
||||
@ -124,13 +143,13 @@ namespace isc {class Bar {Foo foo_;};} ],,
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
|
||||
[AC_MSG_RESULT(yes)])
|
||||
CXXFLAGS="$CXXFLAGS_SAVED"
|
||||
fi dnl GCC = yes
|
||||
fi dnl GXX = yes
|
||||
|
||||
AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
|
||||
AC_DEFINE_UNQUOTED(UNUSED_PARAM, $UNUSED_PARAM_ATTRIBUTE, Define to compiler keyword indicating a function argument is intentionally unused)
|
||||
|
||||
# produce PIC unless we disable shared libraries. need this for python bindings.
|
||||
if test $enable_shared != "no" -a "X$GCC" = "Xyes"; then
|
||||
if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -fPIC"
|
||||
fi
|
||||
|
||||
@ -270,6 +289,11 @@ AC_SUBST(GTEST_INCLUDES)
|
||||
AC_SUBST(GTEST_LDFLAGS)
|
||||
AC_SUBST(GTEST_LDADD)
|
||||
|
||||
dnl check for pkg-config itself so we don't try the m4 macro without pkg-config
|
||||
AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes, no)
|
||||
if test "x$HAVE_PKG_CONFIG" = "xno" ; then
|
||||
AC_MSG_ERROR(Please install pkg-config)
|
||||
fi
|
||||
PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.3.9, enable_features="$enable_features SQLite3")
|
||||
|
||||
# I can't get some of the #include <asio.hpp> right without this
|
||||
@ -319,7 +343,7 @@ fi
|
||||
# run time performance. Hpefully we can find a better solution or the ASIO
|
||||
# code will be updated by the time we really need it.
|
||||
AC_CHECK_HEADERS(sys/devpoll.h, ac_cv_have_devpoll=yes, ac_cv_have_devpoll=no)
|
||||
if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GCC" = "Xyes"; then
|
||||
if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
|
||||
CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
|
||||
fi
|
||||
|
||||
@ -348,8 +372,11 @@ AC_CONFIG_FILES([Makefile
|
||||
src/bin/bindctl/Makefile
|
||||
src/bin/bindctl/tests/Makefile
|
||||
src/bin/cfgmgr/Makefile
|
||||
src/bin/cfgmgr/tests/Makefile
|
||||
src/bin/host/Makefile
|
||||
src/bin/loadzone/Makefile
|
||||
src/bin/loadzone/tests/correct/Makefile
|
||||
src/bin/loadzone/tests/error/Makefile
|
||||
src/bin/msgq/Makefile
|
||||
src/bin/msgq/tests/Makefile
|
||||
src/bin/auth/Makefile
|
||||
@ -368,6 +395,8 @@ AC_CONFIG_FILES([Makefile
|
||||
src/lib/python/isc/cc/tests/Makefile
|
||||
src/lib/python/isc/config/Makefile
|
||||
src/lib/python/isc/config/tests/Makefile
|
||||
src/lib/python/isc/log/Makefile
|
||||
src/lib/python/isc/log/tests/Makefile
|
||||
src/lib/config/Makefile
|
||||
src/lib/config/tests/Makefile
|
||||
src/lib/dns/Makefile
|
||||
@ -380,6 +409,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/lib/xfr/Makefile
|
||||
])
|
||||
AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
|
||||
src/bin/cfgmgr/tests/b10-cfgmgr_test.py
|
||||
src/bin/cmdctl/cmdctl.py
|
||||
src/bin/cmdctl/run_b10-cmdctl.sh
|
||||
src/bin/cmdctl/tests/cmdctl_test
|
||||
@ -398,6 +428,8 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
|
||||
src/bin/bindctl/bindctl-source.py
|
||||
src/bin/bindctl/tests/bindctl_test
|
||||
src/bin/loadzone/run_loadzone.sh
|
||||
src/bin/loadzone/tests/correct/correct_test.sh
|
||||
src/bin/loadzone/tests/error/error_test.sh
|
||||
src/bin/loadzone/b10-loadzone.py
|
||||
src/bin/usermgr/run_b10-cmdctl-usermgr.sh
|
||||
src/bin/usermgr/b10-cmdctl-usermgr.py
|
||||
@ -409,7 +441,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
|
||||
src/lib/config/tests/data_def_unittests_config.h
|
||||
src/lib/python/isc/config/tests/config_test
|
||||
src/lib/python/isc/cc/tests/cc_test
|
||||
src/lib/dns/python/tests/libdns_python_test
|
||||
src/lib/python/isc/log/tests/log_test
|
||||
src/lib/dns/gen-rdatacode.py
|
||||
src/lib/python/bind10_config.py
|
||||
src/lib/dns/tests/testdata/gen-wiredata.py
|
||||
@ -425,6 +457,8 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
|
||||
chmod +x src/bin/bindctl/tests/bindctl_test
|
||||
chmod +x src/bin/bindctl/run_bindctl.sh
|
||||
chmod +x src/bin/loadzone/run_loadzone.sh
|
||||
chmod +x src/bin/loadzone/tests/correct/correct_test.sh
|
||||
chmod +x src/bin/loadzone/tests/error/error_test.sh
|
||||
chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
|
||||
chmod +x src/bin/msgq/run_msgq.sh
|
||||
chmod +x src/bin/msgq/tests/msgq_test
|
||||
|
@ -36,7 +36,10 @@ lib_LIBRARIES = libasio_link.a
|
||||
libasio_link_a_SOURCES = asio_link.cc asio_link.h
|
||||
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
|
||||
# B10_CXXFLAGS)
|
||||
libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter
|
||||
libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
if USE_GXX
|
||||
libasio_link_a_CXXFLAGS += -Wno-unused-parameter
|
||||
endif
|
||||
libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
|
||||
BUILT_SOURCES = spec_config.h
|
||||
@ -45,9 +48,9 @@ b10_auth_SOURCES = auth_srv.cc auth_srv.h
|
||||
b10_auth_SOURCES += common.h
|
||||
b10_auth_SOURCES += main.cc
|
||||
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
b10_auth_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
|
||||
b10_auth_LDADD += $(SQLITE_LIBS)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
@ -66,7 +67,13 @@ void
|
||||
dispatch_axfr_query(const int tcp_sock, char const axfr_query[],
|
||||
const uint16_t query_len)
|
||||
{
|
||||
string path(UNIX_SOCKET_FILE);
|
||||
string path;
|
||||
if (getenv("B10_FROM_BUILD")) {
|
||||
path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
|
||||
} else {
|
||||
path = UNIX_SOCKET_FILE;
|
||||
}
|
||||
|
||||
if (getenv("B10_FROM_BUILD")) {
|
||||
path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ struct IOServiceImpl;
|
||||
|
||||
class IOService {
|
||||
public:
|
||||
IOService(AuthSrv* auth_server, const char* port,
|
||||
IOService(AuthSrv* auth_server, const char* const port,
|
||||
const bool use_ipv4, const bool use_ipv6);
|
||||
~IOService();
|
||||
void run();
|
||||
|
@ -243,7 +243,8 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message,
|
||||
impl_->data_sources_.doQuery(query);
|
||||
} catch (const Exception& ex) {
|
||||
if (impl_->verbose_mode_) {
|
||||
cerr << "[b10-auth] Internal error, returning SERVFAIL: " << ex.what() << endl;
|
||||
cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
|
||||
ex.what() << endl;
|
||||
}
|
||||
makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
|
||||
impl_->verbose_mode_);
|
||||
@ -273,9 +274,22 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
|
||||
bool is_default;
|
||||
string item("database_file");
|
||||
ElementPtr value = cs_->getValue(is_default, item);
|
||||
db_file_ = value->stringValue();
|
||||
final = Element::createFromString("{}");
|
||||
|
||||
// If the value is the default, and we are running from
|
||||
// a specific directory ('from build'), we need to use
|
||||
// a different value than the default (which may not exist)
|
||||
// (btw, this should not be done here in the end, i think
|
||||
// the from-source script should have a check for this,
|
||||
// but for that we need offline access to config, so for
|
||||
// now this is a decent solution)
|
||||
if (is_default && getenv("B10_FROM_BUILD")) {
|
||||
value = Element::create(string(getenv("B10_FROM_BUILD")) +
|
||||
"/bind10_zones.sqlite3");
|
||||
}
|
||||
final->set(item, value);
|
||||
|
||||
db_file_ = value->stringValue();
|
||||
} else {
|
||||
return (answer);
|
||||
}
|
||||
|
@ -148,12 +148,12 @@ main(int argc, char* argv[]) {
|
||||
io_service = new asio_link::IOService(auth_server, port, use_ipv4,
|
||||
use_ipv6);
|
||||
|
||||
ModuleCCSession cs(specfile, io_service->get_io_service(), my_config_handler, my_command_handler);
|
||||
ModuleCCSession cs(specfile, io_service->get_io_service(),
|
||||
my_config_handler, my_command_handler);
|
||||
|
||||
auth_server->setConfigSession(&cs);
|
||||
auth_server->updateConfig(ElementPtr());
|
||||
|
||||
|
||||
cout << "[b10-auth] Server started." << endl;
|
||||
io_service->run();
|
||||
} catch (const std::exception& ex) {
|
||||
|
@ -20,9 +20,9 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
run_unittests_LDADD = $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(SQLITE_LIBS)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
endif
|
||||
|
||||
|
@ -57,6 +57,9 @@ import time
|
||||
import select
|
||||
import random
|
||||
from optparse import OptionParser, OptionValueError
|
||||
import io
|
||||
import pwd
|
||||
import posix
|
||||
|
||||
import isc.cc
|
||||
|
||||
@ -108,21 +111,38 @@ to avoid being restarted at exactly 10 seconds."""
|
||||
when = time.time()
|
||||
return max(when, self.restart_time)
|
||||
|
||||
class ProcessInfoError(Exception): pass
|
||||
|
||||
class ProcessInfo:
|
||||
"""Information about a process"""
|
||||
|
||||
dev_null = open(os.devnull, "w")
|
||||
|
||||
def __init__(self, name, args, env={}, dev_null_stdout=False,
|
||||
dev_null_stderr=False):
|
||||
dev_null_stderr=False, uid=None, username=None):
|
||||
self.name = name
|
||||
self.args = args
|
||||
self.env = env
|
||||
self.dev_null_stdout = dev_null_stdout
|
||||
self.dev_null_stderr = dev_null_stderr
|
||||
self.restart_schedule = RestartSchedule()
|
||||
self.uid = uid
|
||||
self.username = username
|
||||
self._spawn()
|
||||
|
||||
def _setuid(self):
|
||||
"""Function used before running a program that needs to run as a
|
||||
different user."""
|
||||
if self.uid is not None:
|
||||
try:
|
||||
posix.setuid(self.uid)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EPERM:
|
||||
# if we failed to change user due to permission report that
|
||||
raise ProcessInfoError("Unable to change to user %s (uid %d)" % (self.username, self.uid))
|
||||
else:
|
||||
# otherwise simply re-raise whatever error we found
|
||||
raise
|
||||
|
||||
def _spawn(self):
|
||||
if self.dev_null_stdout:
|
||||
@ -138,14 +158,15 @@ class ProcessInfo:
|
||||
# on construction (self.env).
|
||||
spawn_env = os.environ
|
||||
spawn_env.update(self.env)
|
||||
if not 'B10_FROM_SOURCE' in os.environ:
|
||||
if 'B10_FROM_SOURCE' not in os.environ:
|
||||
spawn_env['PATH'] = "@@LIBEXECDIR@@:" + spawn_env['PATH']
|
||||
self.process = subprocess.Popen(self.args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=spawn_stdout,
|
||||
stderr=spawn_stderr,
|
||||
close_fds=True,
|
||||
env=spawn_env,)
|
||||
env=spawn_env,
|
||||
preexec_fn=self._setuid)
|
||||
self.pid = self.process.pid
|
||||
self.restart_schedule.set_run_start_time()
|
||||
|
||||
@ -155,7 +176,8 @@ class ProcessInfo:
|
||||
class BoB:
|
||||
"""Boss of BIND class."""
|
||||
|
||||
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False):
|
||||
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False,
|
||||
setuid=None, username=None):
|
||||
"""Initialize the Boss of BIND. This is a singleton (only one
|
||||
can run).
|
||||
|
||||
@ -171,6 +193,8 @@ class BoB:
|
||||
self.processes = {}
|
||||
self.dead_processes = {}
|
||||
self.runnable = False
|
||||
self.uid = setuid
|
||||
self.username = username
|
||||
|
||||
def config_handler(self, new_config):
|
||||
if self.verbose:
|
||||
@ -225,12 +249,14 @@ class BoB:
|
||||
sys.stdout.write("[bind10] Starting b10-msgq\n")
|
||||
try:
|
||||
c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
|
||||
True, not self.verbose)
|
||||
True, not self.verbose, uid=self.uid,
|
||||
username=self.username)
|
||||
except Exception as e:
|
||||
return "Unable to start b10-msgq; " + str(e)
|
||||
self.processes[c_channel.pid] = c_channel
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" % c_channel.pid)
|
||||
sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" %
|
||||
c_channel.pid)
|
||||
|
||||
# now connect to the c-channel
|
||||
cc_connect_start = time.time()
|
||||
@ -250,7 +276,8 @@ class BoB:
|
||||
sys.stdout.write("[bind10] Starting b10-cfgmgr\n")
|
||||
try:
|
||||
bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
|
||||
c_channel_env)
|
||||
c_channel_env, uid=self.uid,
|
||||
username=self.username)
|
||||
except Exception as e:
|
||||
c_channel.process.kill()
|
||||
return "Unable to start b10-cfgmgr; " + str(e)
|
||||
@ -272,23 +299,6 @@ class BoB:
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] ccsession started\n")
|
||||
|
||||
# start the xfrout before auth-server, to make sure every xfr-query can
|
||||
# be processed properly.
|
||||
xfrout_args = ['b10-xfrout']
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Starting b10-xfrout\n")
|
||||
xfrout_args += ['-v']
|
||||
try:
|
||||
xfrout = ProcessInfo("b10-xfrout", xfrout_args,
|
||||
c_channel_env )
|
||||
except Exception as e:
|
||||
c_channel.process.kill()
|
||||
bind_cfgd.process.kill()
|
||||
return "Unable to start b10-xfrout; " + str(e)
|
||||
self.processes[xfrout.pid] = xfrout
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % xfrout.pid)
|
||||
|
||||
# start b10-auth
|
||||
# XXX: this must be read from the configuration manager in the future
|
||||
authargs = ['b10-auth', '-p', str(self.auth_port)]
|
||||
@ -308,6 +318,28 @@ class BoB:
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-auth (PID %d)\n" % auth.pid)
|
||||
|
||||
# everything after the authoritative server can run as non-root
|
||||
if self.uid is not None:
|
||||
posix.setuid(self.uid)
|
||||
|
||||
# start the xfrout before auth-server, to make sure every xfr-query can
|
||||
# be processed properly.
|
||||
xfrout_args = ['b10-xfrout']
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Starting b10-xfrout\n")
|
||||
xfrout_args += ['-v']
|
||||
try:
|
||||
xfrout = ProcessInfo("b10-xfrout", xfrout_args,
|
||||
c_channel_env )
|
||||
except Exception as e:
|
||||
c_channel.process.kill()
|
||||
bind_cfgd.process.kill()
|
||||
return "Unable to start b10-xfrout; " + str(e)
|
||||
self.processes[xfrout.pid] = xfrout
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" %
|
||||
xfrout.pid)
|
||||
|
||||
# start b10-xfrin
|
||||
xfrin_args = ['b10-xfrin']
|
||||
if self.verbose:
|
||||
@ -324,7 +356,8 @@ class BoB:
|
||||
return "Unable to start b10-xfrin; " + str(e)
|
||||
self.processes[xfrind.pid] = xfrind
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % xfrind.pid)
|
||||
sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" %
|
||||
xfrind.pid)
|
||||
|
||||
# start the b10-cmdctl
|
||||
# XXX: we hardcode port 8080
|
||||
@ -344,7 +377,8 @@ class BoB:
|
||||
return "Unable to start b10-cmdctl; " + str(e)
|
||||
self.processes[cmd_ctrld.pid] = cmd_ctrld
|
||||
if self.verbose:
|
||||
sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" % cmd_ctrld.pid)
|
||||
sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" %
|
||||
cmd_ctrld.pid)
|
||||
|
||||
self.runnable = True
|
||||
|
||||
@ -435,11 +469,16 @@ class BoB:
|
||||
sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
|
||||
|
||||
def restart_processes(self):
|
||||
"""Restart any dead processes."""
|
||||
"""Restart any dead processes.
|
||||
Returns the time when the next process is ready to be restarted.
|
||||
If the server is shutting down, returns 0.
|
||||
If there are no processes, returns None.
|
||||
The values returned can be safely passed into select() as the
|
||||
timeout value."""
|
||||
next_restart = None
|
||||
# if we're shutting down, then don't restart
|
||||
if not self.runnable:
|
||||
return next_restart
|
||||
return 0
|
||||
# otherwise look through each dead process and try to restart
|
||||
still_dead = {}
|
||||
now = time.time()
|
||||
@ -510,6 +549,10 @@ def check_port(option, opt_str, value, parser):
|
||||
def main():
|
||||
global options
|
||||
global boss_of_bind
|
||||
# Enforce line buffering on stdout, even when not a TTY
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
|
||||
|
||||
|
||||
# Parse any command-line options.
|
||||
parser = OptionParser(version=__version__)
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
|
||||
@ -520,7 +563,42 @@ def main():
|
||||
parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
|
||||
type="string", default=None,
|
||||
help="UNIX domain socket file the b10-msgq daemon will use")
|
||||
parser.add_option("-u", "--user", dest="user",
|
||||
type="string", default=None,
|
||||
help="Change user after startup (must run as root)")
|
||||
(options, args) = parser.parse_args()
|
||||
if args:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Check user ID.
|
||||
setuid = None
|
||||
username = None
|
||||
if options.user:
|
||||
# Try getting information about the user, assuming UID passed.
|
||||
try:
|
||||
pw_ent = pwd.getpwuid(int(options.user))
|
||||
setuid = pw_ent.pw_uid
|
||||
username = pw_ent.pw_name
|
||||
except ValueError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Next try getting information about the user, assuming user name
|
||||
# passed.
|
||||
# If the information is both a valid user name and user number, we
|
||||
# prefer the name because we try it second. A minor point, hopefully.
|
||||
try:
|
||||
pw_ent = pwd.getpwnam(options.user)
|
||||
setuid = pw_ent.pw_uid
|
||||
username = pw_ent.pw_name
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if setuid is None:
|
||||
sys.stderr.write("bind10: invalid user: '%s'\n" % options.user)
|
||||
sys.exit(1)
|
||||
|
||||
# Announce startup.
|
||||
if options.verbose:
|
||||
@ -543,11 +621,12 @@ def main():
|
||||
|
||||
# Go bob!
|
||||
boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
|
||||
options.verbose)
|
||||
options.verbose, setuid, username)
|
||||
startup_result = boss_of_bind.startup()
|
||||
if startup_result:
|
||||
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
|
||||
sys.exit(1)
|
||||
sys.stdout.write("[bind10] BIND 10 started\n")
|
||||
|
||||
# In our main loop, we check for dead processes or messages
|
||||
# on the c-channel.
|
||||
@ -584,6 +663,7 @@ def main():
|
||||
# shutdown
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
boss_of_bind.shutdown()
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
134
src/bin/bind10/tests/args_test.py
Normal file
134
src/bin/bind10/tests/args_test.py
Normal file
@ -0,0 +1,134 @@
|
||||
"""
|
||||
This program tests the boss process to make sure that it runs while
|
||||
dropping permissions. It must be run as a user that can set permission.
|
||||
"""
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import select
|
||||
import time
|
||||
import pwd
|
||||
|
||||
# Set to a valid user name on the system to run setuid() test
|
||||
#SUID_USER=None
|
||||
SUID_USER="shane"
|
||||
|
||||
BIND10_EXE="../run_bind10.sh"
|
||||
TIMEOUT=3
|
||||
|
||||
class TestBossArgs(unittest.TestCase):
|
||||
def _waitForString(self, bob, s):
|
||||
found_string = False
|
||||
start_time = time.time()
|
||||
while time.time() < start_time + TIMEOUT:
|
||||
(r,w,x) = select.select((bob.stdout,), (), (), TIMEOUT)
|
||||
if bob.stdout in r:
|
||||
s = bob.stdout.readline()
|
||||
if s == '':
|
||||
break
|
||||
if s.startswith(s):
|
||||
found_string = True
|
||||
break
|
||||
return found_string
|
||||
|
||||
def testNoArgs(self):
|
||||
"""Run bind10 without any arguments"""
|
||||
bob = subprocess.Popen(args=(BIND10_EXE,),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
started_ok = self._waitForString(bob, '[bind10] BIND 10 started')
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
bob.wait()
|
||||
self.assertTrue(started_ok)
|
||||
|
||||
def testBadOption(self):
|
||||
"""Run bind10 with a bogus option"""
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "--badoption"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
failed = self._waitForString(bob, 'bind10: error: no such option: --badoption')
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
self.assertTrue(bob.wait() == 2)
|
||||
self.assertTrue(failed)
|
||||
|
||||
def testArgument(self):
|
||||
"""Run bind10 with an argument (this is not allowed)"""
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "argument"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
failed = self._waitForString(bob, 'Usage: bind10 [options]')
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
self.assertTrue(bob.wait() == 1)
|
||||
self.assertTrue(failed)
|
||||
|
||||
def testBadUser(self):
|
||||
"""Run bind10 with a bogus user"""
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "-u", "bogus_user"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
failed = self._waitForString(bob, "bind10: invalid user: 'bogus_user'")
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
self.assertTrue(bob.wait() == 1)
|
||||
self.assertTrue(failed)
|
||||
|
||||
def testBadUid(self):
|
||||
"""Run bind10 with a bogus user ID"""
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "-u", "999999999"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
failed = self._waitForString(bob, "bind10: invalid user: '999999999'")
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
self.assertTrue(bob.wait() == 1)
|
||||
self.assertTrue(failed)
|
||||
|
||||
def testFailSetUser(self):
|
||||
"""Try the -u option when we don't run as root"""
|
||||
global SUID_USER
|
||||
if SUID_USER is None:
|
||||
self.skipTest("test needs a valid user (set when run)")
|
||||
if os.getuid() == 0:
|
||||
self.skipTest("test must not be run as root (uid is 0)")
|
||||
# XXX: we depend on the "nobody" user
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "-u", "nobody"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
failed = self._waitForString(bob, "[bind10] Error on startup: Unable to start b10-msgq; Unable to change to user nobody")
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
self.assertTrue(bob.wait() == 1)
|
||||
self.assertTrue(failed)
|
||||
|
||||
def testSetUser(self):
|
||||
"""Try the -u option"""
|
||||
global SUID_USER
|
||||
if SUID_USER is None:
|
||||
self.skipTest("test needs a valid user (set when run)")
|
||||
if os.getuid() != 0:
|
||||
self.skipTest("test must run as root (uid is not 0)")
|
||||
if os.geteuid() != 0:
|
||||
self.skipTest("test must run as root (euid is not 0)")
|
||||
|
||||
bob = subprocess.Popen(args=(BIND10_EXE, "-u", SUID_USER),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
started_ok = self._waitForString(bob, '[bind10] BIND 10 started')
|
||||
self.assertTrue(started_ok)
|
||||
ps = subprocess.Popen(args=("ps", "axo", "user,pid"),
|
||||
stdout=subprocess.PIPE)
|
||||
s = ps.stdout.readline()
|
||||
ps_user = None
|
||||
while True:
|
||||
s = ps.stdout.readline()
|
||||
if s == '': break
|
||||
(user, pid) = s.split()
|
||||
if int(pid) == bob.pid:
|
||||
ps_user = user.decode()
|
||||
break
|
||||
self.assertTrue(ps_user is not None)
|
||||
self.assertTrue(ps_user == SUID_USER)
|
||||
time.sleep(0.1)
|
||||
bob.terminate()
|
||||
x = bob.wait()
|
||||
self.assertTrue(bob.wait() == 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -27,5 +27,6 @@ PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_srcdir@/src/bin/bind10
|
||||
export PYTHONPATH
|
||||
|
||||
cd ${BIND10_PATH}/tests
|
||||
exec ${PYTHON_EXEC} -O bind10_test.py $*
|
||||
${PYTHON_EXEC} -O bind10_test.py $*
|
||||
exec ${PYTHON_EXEC} -O args_test.py $*
|
||||
|
||||
|
@ -87,7 +87,7 @@ class BindCmdInterpreter(Cmd):
|
||||
'''Generate one session id for the connection. '''
|
||||
rand = os.urandom(16)
|
||||
now = time.time()
|
||||
session_id = sha1(("%s%s%s" %(rand, now,
|
||||
session_id = sha1(("%s%s%s" %(rand, now,
|
||||
socket.gethostname())).encode())
|
||||
digest = session_id.hexdigest()
|
||||
return digest
|
||||
|
@ -1,8 +1,10 @@
|
||||
SUBDIRS = tests
|
||||
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
|
||||
pkglibexec_SCRIPTS = b10-cfgmgr
|
||||
|
||||
CLEANFILES = b10-cfgmgr
|
||||
CLEANFILES = b10-cfgmgr b10-cfgmgr.pyc
|
||||
|
||||
b10_cfgmgrdir = @localstatedir@/@PACKAGE@
|
||||
#B10_cfgmgr_DATA =
|
||||
|
@ -15,6 +15,8 @@
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
# $Id$
|
||||
|
||||
import sys; sys.path.append ('@@PYTHONPATH@@')
|
||||
|
||||
from isc.config.cfgmgr import ConfigManager
|
||||
@ -38,7 +40,8 @@ def signal_handler(signal, frame):
|
||||
if cm:
|
||||
cm.running = False
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
global cm
|
||||
try:
|
||||
cm = ConfigManager(DATA_PATH)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
@ -53,3 +56,6 @@ if __name__ == "__main__":
|
||||
print("[b10-cfgmgr] Interrupted, exiting")
|
||||
if cm:
|
||||
cm.write_config()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
13
src/bin/cfgmgr/tests/Makefile.am
Normal file
13
src/bin/cfgmgr/tests/Makefile.am
Normal file
@ -0,0 +1,13 @@
|
||||
PYTESTS = b10-cfgmgr_test.py
|
||||
|
||||
EXTRA_DIST = $(PYTESTS)
|
||||
|
||||
# later will have configure option to choose this, like: coverage run --branch
|
||||
PYCOVERAGE = $(PYTHON)
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr \
|
||||
$(PYCOVERAGE) $(abs_builddir)/$$pytest ; \
|
||||
done
|
95
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
Normal file
95
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (C) 2010 Internet Systems Consortium.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
|
||||
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
||||
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
# $Id: cfgmgr_test.py 2126 2010-06-16 14:40:22Z jelte $
|
||||
|
||||
#
|
||||
# Tests for the configuration manager run script
|
||||
#
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
|
||||
class MyConfigManager:
|
||||
def __init__(self, path):
|
||||
self._path = path
|
||||
self.read_config_called = False
|
||||
self.notify_boss_called = False
|
||||
self.run_called = False
|
||||
self.write_config_called = False
|
||||
self.running = True
|
||||
|
||||
def read_config(self):
|
||||
self.read_config_called = True
|
||||
|
||||
def notify_boss(self):
|
||||
self.notify_boss_called = True
|
||||
|
||||
def run(self):
|
||||
self.run_called = True
|
||||
|
||||
def write_config(self):
|
||||
self.write_config_called = True
|
||||
|
||||
class TestConfigManagerStartup(unittest.TestCase):
|
||||
def test_cfgmgr(self):
|
||||
# some creative module use;
|
||||
# b10-cfgmgr has a hypen, so we use __import__
|
||||
# this also gives us the chance to override the imported
|
||||
# module ConfigManager in it.
|
||||
b = __import__("b10-cfgmgr")
|
||||
b.ConfigManager = MyConfigManager
|
||||
|
||||
b.main()
|
||||
|
||||
self.assertTrue(b.cm.read_config_called)
|
||||
self.assertTrue(b.cm.notify_boss_called)
|
||||
self.assertTrue(b.cm.run_called)
|
||||
self.assertTrue(b.cm.write_config_called)
|
||||
|
||||
self.assertTrue(b.cm.running)
|
||||
b.signal_handler(None, None)
|
||||
self.assertFalse(b.cm.running)
|
||||
|
||||
# TODO: take value from the 'global config module'
|
||||
# (and rename the .in away from this file again)
|
||||
data_path = "@localstatedir@/@PACKAGE@".replace("${prefix}", "@prefix@")
|
||||
self.assertEqual(data_path, b.DATA_PATH)
|
||||
|
||||
# remove the 'module' again, or later tests may fail
|
||||
# (if it is already present it won't be loaded again)
|
||||
sys.modules.pop("b10-cfgmgr")
|
||||
|
||||
def test_cfgmgr_from_source(self):
|
||||
tmp_env_var = "/just/some/dir"
|
||||
env_var = None
|
||||
if "B10_FROM_SOURCE" in os.environ:
|
||||
env_var = os.environ["B10_FROM_SOURCE"]
|
||||
|
||||
os.environ["B10_FROM_SOURCE"] = tmp_env_var
|
||||
b = __import__("b10-cfgmgr", globals(), locals())
|
||||
b.ConfigManager = MyConfigManager
|
||||
self.assertEqual(tmp_env_var, b.DATA_PATH)
|
||||
|
||||
if env_var != None:
|
||||
os.environ["B10_FROM_SOURCE"] = env_var
|
||||
|
||||
sys.modules.pop("b10-cfgmgr")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -416,7 +416,7 @@ def set_signal_handler():
|
||||
def run(addr = 'localhost', port = 8080, idle_timeout = 1200, verbose = False):
|
||||
''' Start cmdctl as one https server. '''
|
||||
if verbose:
|
||||
sys.stdout.write("[b10-cmdctl] starting on :%s port:%d\n" %(addr, port))
|
||||
sys.stdout.write("[b10-cmdctl] starting on %s port:%d\n" %(addr, port))
|
||||
httpd = SecureHTTPServer((addr, port), SecureHTTPRequestHandler, idle_timeout, verbose)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
@ -7,7 +7,7 @@ CLEANFILES = *.gcno *.gcda
|
||||
|
||||
bin_PROGRAMS = host
|
||||
host_SOURCES = host.cc
|
||||
host_LDADD = $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
host_LDADD = $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
host_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
|
||||
#man_MANS = host.1
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <sys/time.h> // for gettimeofday
|
||||
#include <sys/socket.h> // networking functions and definitions on FreeBSD
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
SUBDIRS = tests/correct
|
||||
SUBDIRS += tests/error
|
||||
bin_SCRIPTS = b10-loadzone
|
||||
|
||||
CLEANFILES = b10-loadzone
|
||||
@ -22,23 +24,27 @@ install-data-local:
|
||||
$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
|
||||
# TODO: permissions handled later
|
||||
|
||||
EXTRA_DIST += testdata/README
|
||||
EXTRA_DIST += testdata/dsset-subzone.example.com.
|
||||
EXTRA_DIST += testdata/example.com
|
||||
EXTRA_DIST += testdata/example.com.signed
|
||||
EXTRA_DIST += testdata/Kexample.com.+005+04456.key
|
||||
EXTRA_DIST += testdata/Kexample.com.+005+04456.private
|
||||
EXTRA_DIST += testdata/Kexample.com.+005+33495.key
|
||||
EXTRA_DIST += testdata/Kexample.com.+005+33495.private
|
||||
EXTRA_DIST += testdata/Ksql1.example.com.+005+12447.key
|
||||
EXTRA_DIST += testdata/Ksql1.example.com.+005+12447.private
|
||||
EXTRA_DIST += testdata/Ksql1.example.com.+005+33313.key
|
||||
EXTRA_DIST += testdata/Ksql1.example.com.+005+33313.private
|
||||
EXTRA_DIST += testdata/Ksql2.example.com.+005+38482.key
|
||||
EXTRA_DIST += testdata/Ksql2.example.com.+005+38482.private
|
||||
EXTRA_DIST += testdata/Ksql2.example.com.+005+63192.key
|
||||
EXTRA_DIST += testdata/Ksql2.example.com.+005+63192.private
|
||||
EXTRA_DIST += testdata/sql1.example.com
|
||||
EXTRA_DIST += testdata/sql1.example.com.signed
|
||||
EXTRA_DIST += testdata/sql2.example.com
|
||||
EXTRA_DIST += testdata/sql2.example.com.signed
|
||||
EXTRA_DIST += tests/normal/README
|
||||
EXTRA_DIST += tests/normal/dsset-subzone.example.com
|
||||
EXTRA_DIST += tests/normal/example.com
|
||||
EXTRA_DIST += tests/normal/example.com.signed
|
||||
EXTRA_DIST += tests/normal/Kexample.com.+005+04456.key
|
||||
EXTRA_DIST += tests/normal/Kexample.com.+005+04456.private
|
||||
EXTRA_DIST += tests/normal/Kexample.com.+005+33495.key
|
||||
EXTRA_DIST += tests/normal/Kexample.com.+005+33495.private
|
||||
EXTRA_DIST += tests/normal/Ksql1.example.com.+005+12447.key
|
||||
EXTRA_DIST += tests/normal/Ksql1.example.com.+005+12447.private
|
||||
EXTRA_DIST += tests/normal/Ksql1.example.com.+005+33313.key
|
||||
EXTRA_DIST += tests/normal/Ksql1.example.com.+005+33313.private
|
||||
EXTRA_DIST += tests/normal/Ksql2.example.com.+005+38482.key
|
||||
EXTRA_DIST += tests/normal/Ksql2.example.com.+005+38482.private
|
||||
EXTRA_DIST += tests/normal/Ksql2.example.com.+005+63192.key
|
||||
EXTRA_DIST += tests/normal/Ksql2.example.com.+005+63192.private
|
||||
EXTRA_DIST += tests/normal/sql1.example.com
|
||||
EXTRA_DIST += tests/normal/sql1.example.com.signed
|
||||
EXTRA_DIST += tests/normal/sql2.example.com
|
||||
EXTRA_DIST += tests/normal/sql2.example.com.signed
|
||||
|
||||
pytest:
|
||||
$(SHELL) tests/correct/correct_test.sh
|
||||
$(SHELL) tests/error/error_test.sh
|
||||
|
@ -19,7 +19,8 @@ import sys; sys.path.append ('@@PYTHONPATH@@')
|
||||
import re, getopt
|
||||
import isc.datasrc
|
||||
from isc.datasrc.master import MasterFile
|
||||
|
||||
import time
|
||||
import os
|
||||
#########################################################################
|
||||
# usage: print usage note and exit
|
||||
#########################################################################
|
||||
@ -57,23 +58,32 @@ def main():
|
||||
if len(args) != 1:
|
||||
usage()
|
||||
zonefile = args[0]
|
||||
|
||||
verbose = os.isatty(sys.stdout.fileno())
|
||||
try:
|
||||
master = MasterFile(zonefile, initial_origin)
|
||||
master = MasterFile(zonefile, initial_origin, verbose)
|
||||
except Exception as e:
|
||||
print("Error reading zone file: " + str(e))
|
||||
sys.stderr.write("Error reading zone file: %s\n" % str(e))
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
zone = master.zonename()
|
||||
if verbose:
|
||||
sys.stdout.write("Using SQLite3 database file %s\n" % dbfile)
|
||||
sys.stdout.write("Zone name is %s\n" % zone)
|
||||
sys.stdout.write("Loading file \"%s\"\n" % zonefile)
|
||||
except Exception as e:
|
||||
print("Error reading zone file: " + str(e))
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write("Error reading zone file: %s\n" % str(e))
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
isc.datasrc.sqlite3_ds.load(dbfile, zone, master.zonedata)
|
||||
if verbose:
|
||||
master.closeverbose()
|
||||
sys.stdout.write("\nDone.\n")
|
||||
except Exception as e:
|
||||
print("Error loading database: " + str(e))
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write("Error loading database: %s\n"% str(e))
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
25
src/bin/loadzone/tests/correct/Makefile.am
Normal file
25
src/bin/loadzone/tests/correct/Makefile.am
Normal file
@ -0,0 +1,25 @@
|
||||
PYTESTS = correct_test.sh
|
||||
EXTRA_DIST = get_zonedatas.py
|
||||
EXTRA_DIST += include.db
|
||||
EXTRA_DIST += inclsub.db
|
||||
EXTRA_DIST += known.test.out
|
||||
EXTRA_DIST += mix1.db
|
||||
EXTRA_DIST += mix1sub1.db
|
||||
EXTRA_DIST += mix1sub2.db
|
||||
EXTRA_DIST += mix2.db
|
||||
EXTRA_DIST += mix2sub1.txt
|
||||
EXTRA_DIST += mix2sub2.txt
|
||||
EXTRA_DIST += ttl1.db
|
||||
EXTRA_DIST += ttl2.db
|
||||
EXTRA_DIST += ttlext.db
|
||||
EXTRA_DIST += example.db
|
||||
|
||||
# later will have configure option to choose this, like: coverage run --branch
|
||||
PYCOVERAGE = $(PYTHON)
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/loadzone \
|
||||
$(SHELL) $(abs_builddir)/$$pytest ; \
|
||||
done
|
75
src/bin/loadzone/tests/correct/correct_test.sh.in
Normal file
75
src/bin/loadzone/tests/correct/correct_test.sh.in
Normal file
@ -0,0 +1,75 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2010 Internet Systems Consortium.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
|
||||
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
||||
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
|
||||
export PYTHON_EXEC
|
||||
|
||||
PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
|
||||
export PYTHONPATH
|
||||
|
||||
LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
|
||||
TEST_FILE_PATH=@abs_top_srcdir@/src/bin/loadzone/tests/correct
|
||||
TEST_OUTPUT_PATH=@abs_top_builddir@/src/bin/loadzone//tests/correct
|
||||
|
||||
status=0
|
||||
echo "Loadzone include. from include.db file"
|
||||
cd ${TEST_FILE_PATH}
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 include.db >> /dev/null
|
||||
|
||||
echo "loadzone ttl1. from ttl1.db file"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttl1.db >> /dev/null
|
||||
|
||||
echo "loadzone ttl2. from ttl2.db file"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttl2.db >> /dev/null
|
||||
|
||||
echo "loadzone mix1. from mix1.db"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 mix1.db >> /dev/null
|
||||
|
||||
echo "loadzone mix2. from mix2.db"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 mix2.db >> /dev/null
|
||||
|
||||
echo "loadzone ttlext. from ttlext.db"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttlext.db >> /dev/null
|
||||
|
||||
echo "loadzone example.com. from example.db"
|
||||
${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 example.db >> /dev/null
|
||||
|
||||
echo "I:test master file \$INCLUDE semantics"
|
||||
echo "I:test master file BIND 8 compatibility TTL and \$TTL semantics"
|
||||
echo "I:test master file RFC1035 TTL and \$TTL semantics"
|
||||
echo "I:test master file BIND8 compatibility and mixed \$INCLUDE with \$TTL semantics"
|
||||
echo "I:test master file RFC1035 TTL and mixed \$INCLUDE with \$TTL semantics"
|
||||
echo "I:test master file BIND9 extenstion of TTL"
|
||||
echo "I:test master file RFC1035 missing CLASS, TTL, NAME semantics"
|
||||
|
||||
${PYTHON_EXEC} ${TEST_FILE_PATH}/get_zonedatas.py ${TEST_OUTPUT_PATH}/zone.sqlite3 > ${TEST_OUTPUT_PATH}/test.out
|
||||
echo "Compare test results."
|
||||
diff ${TEST_OUTPUT_PATH}/test.out ${TEST_FILE_PATH}/known.test.out || status=1
|
||||
|
||||
echo "Clean tmp files."
|
||||
rm -f ${TEST_OUTPUT_PATH}/zone.sqlite3
|
||||
rm -f ${TEST_OUTPUT_PATH}/test.out
|
||||
echo "I:exit status: $status"
|
||||
echo "------------------------------------------------------------------------------"
|
||||
echo "Ran 7 test files"
|
||||
echo ""
|
||||
if [ "$status" -eq 1 ] ;then
|
||||
echo "ERROR"
|
||||
else
|
||||
echo "OK"
|
||||
fi
|
||||
exit $status
|
14
src/bin/loadzone/tests/correct/example.db
Normal file
14
src/bin/loadzone/tests/correct/example.db
Normal file
@ -0,0 +1,14 @@
|
||||
;This file includes all kinds of rr form. missing name, ttl and class or not.
|
||||
$ORIGIN example.com.
|
||||
$TTL 60
|
||||
@ IN SOA ns1.example.com. hostmaster.example.com. (1 43200 900 1814400 7200)
|
||||
IN 20 NS ns1
|
||||
NS ns2
|
||||
ns1 IN 30 A 192.168.1.102
|
||||
70 NS ns3
|
||||
IN NS ns4
|
||||
10 IN MX 10 mail.example.com.
|
||||
ns2 80 A 1.1.1.1
|
||||
ns3 IN A 2.2.2.2
|
||||
ns4 A 3.3.3.3
|
||||
ns5 90 IN A 4.4.4.4
|
8
src/bin/loadzone/tests/correct/get_zonedatas.py
Normal file
8
src/bin/loadzone/tests/correct/get_zonedatas.py
Normal file
@ -0,0 +1,8 @@
|
||||
from isc.datasrc import sqlite3_ds
|
||||
import sys
|
||||
ZONE_FILE = sys.argv[1]
|
||||
zonename_set = ["include.", "ttl1.", "ttl2.", "mix1.", "mix2.", "ttlext.", "example.com."]
|
||||
for zone_name in zonename_set:
|
||||
for rr_data in sqlite3_ds.get_zone_datas(zone_name, ZONE_FILE):
|
||||
data_len = len(rr_data[2])
|
||||
sys.stdout.write(rr_data[2] + '\t\t' + str(rr_data[4]) + '\tIN\t' + rr_data[5] + '\t' + rr_data[7] + '\n')
|
4
src/bin/loadzone/tests/correct/inclsub.db
Normal file
4
src/bin/loadzone/tests/correct/inclsub.db
Normal file
@ -0,0 +1,4 @@
|
||||
a 300 A 10.0.1.1
|
||||
$ORIGIN foo
|
||||
b 300 A 10.0.2.2
|
||||
|
23
src/bin/loadzone/tests/correct/include.db
Normal file
23
src/bin/loadzone/tests/correct/include.db
Normal file
@ -0,0 +1,23 @@
|
||||
$ORIGIN include. ; initialize origin
|
||||
$TTL 300
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
|
||||
ns A 127.0.0.1
|
||||
|
||||
a A 10.0.0.1
|
||||
$INCLUDE inclsub.db sub ; a.include. is the relative domain name origin for the included file
|
||||
; use the current domain name
|
||||
A 99.99.99.99
|
||||
b A 10.0.0.2
|
||||
$ORIGIN b
|
||||
$INCLUDE inclsub.db
|
||||
; use the current domain name
|
||||
A 10.0.0.99
|
||||
c A 10.0.0.3
|
79
src/bin/loadzone/tests/correct/known.test.out
Normal file
79
src/bin/loadzone/tests/correct/known.test.out
Normal file
@ -0,0 +1,79 @@
|
||||
include. 300 IN SOA ns.include. hostmaster.include. 1 3600 1800 1814400 3600
|
||||
include. 300 IN NS ns.include.
|
||||
ns.include. 300 IN A 127.0.0.1
|
||||
a.include. 300 IN A 10.0.0.1
|
||||
a.sub.include. 300 IN A 10.0.1.1
|
||||
b.foo.sub.include. 300 IN A 10.0.2.2
|
||||
a.include. 300 IN A 99.99.99.99
|
||||
b.include. 300 IN A 10.0.0.2
|
||||
a.b.include. 300 IN A 10.0.1.1
|
||||
b.foo.b.include. 300 IN A 10.0.2.2
|
||||
b.include. 300 IN A 10.0.0.99
|
||||
c.b.include. 300 IN A 10.0.0.3
|
||||
ttl1. 3 IN SOA ns.ttl1. hostmaster.ttl1. 1 3600 1800 1814400 3
|
||||
ttl1. 3 IN NS ns.ttl1.
|
||||
ns.ttl1. 3 IN A 10.53.0.1
|
||||
a.ttl1. 3 IN TXT "soa minttl 3"
|
||||
b.ttl1. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl1. 3 IN TXT "soa minttl 3"
|
||||
d.ttl1. 1 IN TXT "default ttl 1"
|
||||
e.ttl1. 4 IN TXT "explicit ttl 4"
|
||||
f.ttl1. 1 IN TXT "default ttl 1"
|
||||
ttl2. 1 IN SOA ns.ttl2. hostmaster.ttl2. 1 3600 1800 1814400 3
|
||||
ttl2. 1 IN NS ns.ttl2.
|
||||
ns.ttl2. 1 IN A 10.53.0.1
|
||||
a.ttl2. 1 IN TXT "inherited ttl 1"
|
||||
b.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
c.ttl2. 2 IN TXT "inherited ttl 2"
|
||||
d.ttl2. 3 IN TXT "default ttl 3"
|
||||
e.ttl2. 2 IN TXT "explicit ttl 2"
|
||||
f.ttl2. 3 IN TXT "default ttl 3"
|
||||
mix1. 3 IN SOA ns.mix1. hostmaster.mix1. 1 3600 1800 1814400 3
|
||||
mix1. 3 IN NS ns.mix1.
|
||||
ns.mix1. 3 IN A 10.53.0.1
|
||||
a.mix1. 3 IN TXT "soa minttl 3"
|
||||
b.mix1. 2 IN TXT "explicit ttl 2"
|
||||
a.mix1. 3 IN TXT "soa minttl 3"
|
||||
b.foo.mix1. 3 IN TXT "soa minttl 3"
|
||||
c.mix1. 3 IN TXT "soa minttl 3"
|
||||
d.mix1. 1 IN TXT "default ttl 1"
|
||||
e.mix1. 4 IN TXT "explicit ttl 4"
|
||||
f.mix1. 1 IN TXT "default ttl 1"
|
||||
i.mix1. 1 IN TXT "default ttl 1"
|
||||
g.mix1. 5 IN TXT "default ttl 5"
|
||||
h.mix1. 5 IN TXT "the include ttl 5"
|
||||
mix2. 1 IN SOA ns.mix2. hostmaster.mix2. 1 3600 1800 1814400 3
|
||||
mix2. 1 IN NS ns.mix2.
|
||||
ns.mix2. 1 IN A 10.53.0.1
|
||||
a.mix2. 1 IN TXT "inherited ttl 1"
|
||||
h.mix2. 1 IN TXT "inherited ttl 1"
|
||||
g.mix2. 6 IN TXT "inherited ttl 6"
|
||||
b.mix2. 6 IN TXT "explicit ttl 6"
|
||||
c.mix2. 2 IN TXT "inherited ttl 2"
|
||||
m.mix2. 6 IN TXT "explicit ttl 6"
|
||||
d.mix2. 3 IN TXT "default ttl 3"
|
||||
e.mix2. 2 IN TXT "explicit ttl 2"
|
||||
n.mix2. 3 IN TXT "default ttl 3"
|
||||
f.mix2. 3 IN TXT "default ttl 3"
|
||||
g.mix2. 5 IN TXT "default ttl 5"
|
||||
f.mix2. 5 IN TXT "default ttl 5"
|
||||
ttlext. 3 IN SOA ns.ttlext. hostmaster.ttlext. 1 3600 1800 1814400 3
|
||||
ttlext. 3 IN NS ns.ttlext.
|
||||
ns.ttlext. 3 IN A 10.53.0.1
|
||||
a.ttlext. 3 IN TXT "soa minttl 3"
|
||||
b.ttlext. 2 IN TXT "explicit ttl 2"
|
||||
c.ttlext. 3 IN TXT "soa minttl 3"
|
||||
d.ttlext. 600 IN TXT "default ttl 600"
|
||||
e.ttlext. 4 IN TXT "explicit ttl 4"
|
||||
f.ttlext. 600 IN TXT "default ttl 600"
|
||||
example.com. 60 IN SOA ns1.example.com. hostmaster.example.com. 1 43200 900 1814400 7200
|
||||
example.com. 20 IN NS ns1.example.com.
|
||||
example.com. 60 IN NS ns2.example.com.
|
||||
ns1.example.com. 30 IN A 192.168.1.102
|
||||
ns1.example.com. 70 IN NS ns3.example.com.
|
||||
ns1.example.com. 60 IN NS ns4.example.com.
|
||||
ns1.example.com. 10 IN MX 10 mail.example.com.
|
||||
ns2.example.com. 80 IN A 1.1.1.1
|
||||
ns3.example.com. 60 IN A 2.2.2.2
|
||||
ns4.example.com. 60 IN A 3.3.3.3
|
||||
ns5.example.com. 90 IN A 4.4.4.4
|
21
src/bin/loadzone/tests/correct/mix1.db
Normal file
21
src/bin/loadzone/tests/correct/mix1.db
Normal file
@ -0,0 +1,21 @@
|
||||
; $Id: ttl1.db,v 1.6 2007/06/19 23:47:04 tbox Exp $
|
||||
$ORIGIN mix1.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3
|
||||
)
|
||||
NS ns
|
||||
ns A 10.53.0.1
|
||||
a TXT "soa minttl 3"
|
||||
b 2 TXT "explicit ttl 2"
|
||||
$INCLUDE mix1sub1.db
|
||||
c TXT "soa minttl 3"
|
||||
$TTL 1
|
||||
d TXT "default ttl 1"
|
||||
e 4 TXT "explicit ttl 4"
|
||||
f TXT "default ttl 1"
|
||||
$INCLUDE mix1sub2.db
|
||||
h 5 TXT "the include ttl 5"
|
3
src/bin/loadzone/tests/correct/mix1sub1.db
Normal file
3
src/bin/loadzone/tests/correct/mix1sub1.db
Normal file
@ -0,0 +1,3 @@
|
||||
a TXT "soa minttl 3"
|
||||
$ORIGIN foo
|
||||
b TXT "soa minttl 3"
|
3
src/bin/loadzone/tests/correct/mix1sub2.db
Normal file
3
src/bin/loadzone/tests/correct/mix1sub2.db
Normal file
@ -0,0 +1,3 @@
|
||||
i TXT "default ttl 1"
|
||||
$TTL 5
|
||||
g TXT "default ttl 5"
|
21
src/bin/loadzone/tests/correct/mix2.db
Normal file
21
src/bin/loadzone/tests/correct/mix2.db
Normal file
@ -0,0 +1,21 @@
|
||||
$ORIGIN mix2.
|
||||
@ 1 IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3
|
||||
)
|
||||
NS ns
|
||||
ns A 10.53.0.1
|
||||
a TXT "inherited ttl 1"
|
||||
$INCLUDE mix2sub1.txt
|
||||
b TXT "explicit ttl 6"
|
||||
c 2 TXT "inherited ttl 2"
|
||||
m TXT "explicit ttl 6"
|
||||
$TTL 3
|
||||
d TXT "default ttl 3"
|
||||
e 2 TXT "explicit ttl 2"
|
||||
n TXT "default ttl 3"
|
||||
$INCLUDE mix2sub2.txt
|
||||
f TXT "default ttl 5"
|
3
src/bin/loadzone/tests/correct/mix2sub1.txt
Normal file
3
src/bin/loadzone/tests/correct/mix2sub1.txt
Normal file
@ -0,0 +1,3 @@
|
||||
h TXT "inherited ttl 1"
|
||||
$TTL 6
|
||||
g TXT "inherited ttl 6"
|
3
src/bin/loadzone/tests/correct/mix2sub2.txt
Normal file
3
src/bin/loadzone/tests/correct/mix2sub2.txt
Normal file
@ -0,0 +1,3 @@
|
||||
f TXT "default ttl 3"
|
||||
$TTL 5
|
||||
g TXT "default ttl 5"
|
17
src/bin/loadzone/tests/correct/ttl1.db
Normal file
17
src/bin/loadzone/tests/correct/ttl1.db
Normal file
@ -0,0 +1,17 @@
|
||||
$ORIGIN ttl1.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3
|
||||
)
|
||||
NS ns
|
||||
ns A 10.53.0.1
|
||||
a TXT "soa minttl 3"
|
||||
b 2 TXT "explicit ttl 2"
|
||||
c TXT "soa minttl 3"
|
||||
$TTL 1
|
||||
d TXT "default ttl 1"
|
||||
e 4 TXT "explicit ttl 4"
|
||||
f TXT "default ttl 1"
|
17
src/bin/loadzone/tests/correct/ttl2.db
Normal file
17
src/bin/loadzone/tests/correct/ttl2.db
Normal file
@ -0,0 +1,17 @@
|
||||
$ORIGIN ttl2.
|
||||
@ 1 IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3
|
||||
)
|
||||
NS ns
|
||||
ns A 10.53.0.1
|
||||
a TXT "inherited ttl 1"
|
||||
b 2 TXT "explicit ttl 2"
|
||||
c TXT "inherited ttl 2"
|
||||
$TTL 3 ; a new ttl
|
||||
d TXT "default ttl 3"
|
||||
e 2 TXT "explicit ttl 2"
|
||||
f TXT "default ttl 3"
|
18
src/bin/loadzone/tests/correct/ttlext.db
Normal file
18
src/bin/loadzone/tests/correct/ttlext.db
Normal file
@ -0,0 +1,18 @@
|
||||
; $Id: ttl1.db,v 1.6 2007/06/19 23:47:04 tbox Exp $
|
||||
$ORIGIN ttlext.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3
|
||||
)
|
||||
NS ns
|
||||
ns A 10.53.0.1
|
||||
a TXT "soa minttl 3"
|
||||
b 2S TXT "explicit ttl 2"
|
||||
c TXT "soa minttl 3"
|
||||
$TTL 10M ; bind9 extention ttl
|
||||
d TXT "default ttl 600"
|
||||
e 4 TXT "explicit ttl 4"
|
||||
f TXT "default ttl 600"
|
25
src/bin/loadzone/tests/error/Makefile.am
Normal file
25
src/bin/loadzone/tests/error/Makefile.am
Normal file
@ -0,0 +1,25 @@
|
||||
PYTESTS = error_test.sh
|
||||
|
||||
EXTRA_DIST = error.known
|
||||
EXTRA_DIST += formerr1.db
|
||||
EXTRA_DIST += formerr2.db
|
||||
EXTRA_DIST += formerr3.db
|
||||
EXTRA_DIST += formerr4.db
|
||||
EXTRA_DIST += formerr5.db
|
||||
EXTRA_DIST += include.txt
|
||||
EXTRA_DIST += keyerror1.db
|
||||
EXTRA_DIST += keyerror2.db
|
||||
EXTRA_DIST += keyerror3.db
|
||||
#EXTRA_DIST += nofilenane.db
|
||||
EXTRA_DIST += originerr1.db
|
||||
EXTRA_DIST += originerr2.db
|
||||
|
||||
# later will have configure option to choose this, like: coverage run --branch
|
||||
PYCOVERAGE = $(PYTHON)
|
||||
# test using command-line arguments, so use check-local target instead of TESTS
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/bin/loadzone \
|
||||
$(SHELL) $(abs_builddir)/$$pytest ; \
|
||||
done
|
11
src/bin/loadzone/tests/error/error.known
Normal file
11
src/bin/loadzone/tests/error/error.known
Normal file
@ -0,0 +1,11 @@
|
||||
Error reading zone file: Cannot parse RR, No $ORIGIN: @ IN SOA ns hostmaster 1 3600 1800 1814400 3600
|
||||
Error reading zone file: $ORIGIN is not absolute in record:$ORIGIN com
|
||||
Error reading zone file: Cannot parse RR: $TL 300
|
||||
Error reading zone file: Cannot parse RR: $OIGIN com.
|
||||
Error loading database: Error while loading com.: Cannot parse RR: $INLUDE file.txt
|
||||
Error loading database: Error while loading com.: Invalid $include format
|
||||
Error loading database: Error while loading com.: Cannot parse RR, No $ORIGIN: include.txt sub
|
||||
Error reading zone file: Invalid TTL: ""
|
||||
Error reading zone file: Invalid TTL: "M"
|
||||
Error loading database: Error while loading com.: Cannot parse RR: b "no type error!"
|
||||
Error reading zone file: Could not open bogusfile
|
82
src/bin/loadzone/tests/error/error_test.sh.in
Normal file
82
src/bin/loadzone/tests/error/error_test.sh.in
Normal file
@ -0,0 +1,82 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2010 Internet Systems Consortium.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
|
||||
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
||||
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
|
||||
export PYTHON_EXEC
|
||||
|
||||
PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
|
||||
export PYTHONPATH
|
||||
|
||||
LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
|
||||
TEST_OUTPUT_PATH=@abs_top_builddir@/src/bin/loadzone/tests/error
|
||||
TEST_FILE_PATH=@abs_top_srcdir@/src/bin/loadzone/tests/error
|
||||
|
||||
cd ${LOADZONE_PATH}/tests/error
|
||||
|
||||
export LOADZONE_PATH
|
||||
status=0
|
||||
|
||||
echo "PYTHON PATH: $PYTHONPATH"
|
||||
|
||||
echo "Test no \$ORIGIN error in zone file"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/originerr1.db 1> /dev/null 2> error.out
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/originerr2.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: key word TTL spell error"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/keyerror1.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: key word ORIGIN spell error"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/keyerror2.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: key INCLUDE spell error"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/keyerror3.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: include formal error, miss filename"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/formerr1.db 1> /dev/null 2>>error.out
|
||||
|
||||
echo "Test: include form error, domain is not absolute"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/formerr2.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: TTL form error, no ttl value"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/formerr3.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: TTL form error, ttl value error"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/formerr4.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: rr form error, no type"
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 ${TEST_FILE_PATH}/formerr5.db 1> /dev/null 2>> error.out
|
||||
|
||||
echo "Test: zone file is bogus"
|
||||
# since bogusfile doesn't exist anyway, we *don't* specify the directory
|
||||
${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3 bogusfile 1> /dev/null 2>> error.out
|
||||
|
||||
diff error.out ${TEST_FILE_PATH}/error.known || status=1
|
||||
|
||||
echo "Clean tmp file."
|
||||
rm -f error.out
|
||||
rm -f zone.sqlite3
|
||||
|
||||
echo "I:exit status:$status"
|
||||
echo "-----------------------------------------------------------------------------"
|
||||
echo "Ran 11 test files"
|
||||
echo ""
|
||||
if [ "$status" -eq 1 ];then
|
||||
echo "ERROR"
|
||||
else
|
||||
echo "OK"
|
||||
fi
|
||||
exit $status
|
13
src/bin/loadzone/tests/error/formerr1.db
Normal file
13
src/bin/loadzone/tests/error/formerr1.db
Normal file
@ -0,0 +1,13 @@
|
||||
$TTL 300
|
||||
$ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
$INCLUDE
|
||||
a A 10.0.0.1
|
12
src/bin/loadzone/tests/error/formerr2.db
Normal file
12
src/bin/loadzone/tests/error/formerr2.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TTL 300
|
||||
com. IN SOA ns.com. hostmaster.com. (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns.example.com.
|
||||
ns.com. A 127.0.0.1
|
||||
$INCLUDE include.txt sub
|
||||
a.com. A 10.0.0.1
|
12
src/bin/loadzone/tests/error/formerr3.db
Normal file
12
src/bin/loadzone/tests/error/formerr3.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TTL
|
||||
$ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
12
src/bin/loadzone/tests/error/formerr4.db
Normal file
12
src/bin/loadzone/tests/error/formerr4.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TTL M
|
||||
$ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
13
src/bin/loadzone/tests/error/formerr5.db
Normal file
13
src/bin/loadzone/tests/error/formerr5.db
Normal file
@ -0,0 +1,13 @@
|
||||
$TTL 2M
|
||||
$ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1 ; ip value
|
||||
b "no type error!"
|
||||
a A 10.0.0.1
|
1
src/bin/loadzone/tests/error/include.txt
Normal file
1
src/bin/loadzone/tests/error/include.txt
Normal file
@ -0,0 +1 @@
|
||||
a 300 A 127.0.0.1
|
12
src/bin/loadzone/tests/error/keyerror1.db
Normal file
12
src/bin/loadzone/tests/error/keyerror1.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TL 300
|
||||
@ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
12
src/bin/loadzone/tests/error/keyerror2.db
Normal file
12
src/bin/loadzone/tests/error/keyerror2.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TTL 300
|
||||
$OIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
13
src/bin/loadzone/tests/error/keyerror3.db
Normal file
13
src/bin/loadzone/tests/error/keyerror3.db
Normal file
@ -0,0 +1,13 @@
|
||||
$TTL 300
|
||||
$ORIGIN com.
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
$INLUDE file.txt
|
||||
a A 10.0.0.1
|
11
src/bin/loadzone/tests/error/originerr1.db
Normal file
11
src/bin/loadzone/tests/error/originerr1.db
Normal file
@ -0,0 +1,11 @@
|
||||
$TTL 300
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
12
src/bin/loadzone/tests/error/originerr2.db
Normal file
12
src/bin/loadzone/tests/error/originerr2.db
Normal file
@ -0,0 +1,12 @@
|
||||
$TTL 300
|
||||
$ORIGIN com
|
||||
@ IN SOA ns hostmaster (
|
||||
1 ; serial
|
||||
3600
|
||||
1800
|
||||
1814400
|
||||
3600
|
||||
)
|
||||
NS ns
|
||||
ns A 127.0.0.1
|
||||
a A 10.0.0.1
|
@ -7,6 +7,6 @@ PYCOVERAGE = $(PYTHON)
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python \
|
||||
env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
|
||||
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
|
||||
done
|
||||
|
@ -40,11 +40,14 @@ except ImportError as e:
|
||||
# installed on the system
|
||||
if "B10_FROM_BUILD" in os.environ:
|
||||
SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrin"
|
||||
AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATAROOTDIR = "@datarootdir@"
|
||||
SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
|
||||
AUTH_SPECFILE_PATH = SPECFILE_PATH
|
||||
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
|
||||
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + "/auth.spec"
|
||||
|
||||
|
||||
__version__ = 'BIND10'
|
||||
@ -434,7 +437,18 @@ a separate method for the convenience of unit tests.
|
||||
db_file = args.get('db_file')
|
||||
if not db_file:
|
||||
#TODO, the db file path should be got in auth server's configuration
|
||||
db_file = '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
|
||||
# if we need access to this configuration more often, we
|
||||
# should add it on start, and not remove it here
|
||||
# (or, if we have writable ds, we might not need this in
|
||||
# the first place)
|
||||
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
|
||||
db_file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
|
||||
if is_default and "B10_FROM_BUILD" in os.environ:
|
||||
# this too should be unnecessary, but currently the
|
||||
# 'from build' override isn't stored in the config
|
||||
# (and we don't have writable datasources yet)
|
||||
db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
|
||||
self._cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
|
||||
|
||||
return (zone_name, master_addrinfo, db_file)
|
||||
|
||||
|
@ -7,6 +7,6 @@ PYCOVERAGE = $(PYTHON)
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
|
||||
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
|
||||
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
|
||||
done
|
||||
|
@ -75,7 +75,8 @@ class TestXfroutSession(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
request = MySocket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
self.xfrsess = MyXfroutSession(request, None, None)
|
||||
self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
|
||||
self.xfrsess = MyXfroutSession(request, None, None, self.log)
|
||||
self.xfrsess.server = Dbserver()
|
||||
self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
|
||||
self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
@ -193,7 +194,7 @@ class TestXfroutSession(unittest.TestCase):
|
||||
|
||||
def test_dns_xfrout_start_formerror(self):
|
||||
# formerror
|
||||
self.assertRaises(MessageTooShort, self.xfrsess.dns_xfrout_start, self.sock, b"\xd6=\x00\x00\x00\x01\x00")
|
||||
self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
|
||||
sent_data = self.sock.readsent()
|
||||
self.assertEqual(len(sent_data), 0)
|
||||
|
||||
@ -236,26 +237,33 @@ class TestXfroutSession(unittest.TestCase):
|
||||
reply_msg = self.sock.read_msg()
|
||||
self.assertEqual(reply_msg.get_rr_count(Section.ANSWER()), 2)
|
||||
|
||||
# set event
|
||||
self.xfrsess.server._shutdown_event.set()
|
||||
self.assertRaises(XfroutException, self.xfrsess._reply_xfrout_query, self.getmsg(), self.sock, "example.com.")
|
||||
class MyCCSession():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_remote_config_value(self, module_name, identifier):
|
||||
if module_name == "Auth" and identifier == "database_file":
|
||||
return "initdb.file", False
|
||||
else:
|
||||
return "unknown", False
|
||||
|
||||
|
||||
class MyUnixSockServer(UnixSockServer):
|
||||
def __init__(self):
|
||||
self._lock = threading.Lock()
|
||||
self._transfers_counter = 0
|
||||
self._shutdown_event = threading.Event()
|
||||
self._db_file = "initdb.file"
|
||||
self._max_transfers_out = 10
|
||||
self._cc = MyCCSession()
|
||||
self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
|
||||
|
||||
class TestUnixSockServer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.unix = MyUnixSockServer()
|
||||
|
||||
def test_updata_config_data(self):
|
||||
self.unix.update_config_data({'transfers_out':10, 'db_file':"db.file"})
|
||||
self.unix.update_config_data({'transfers_out':10 })
|
||||
self.assertEqual(self.unix._max_transfers_out, 10)
|
||||
self.assertEqual(self.unix._db_file, "db.file")
|
||||
|
||||
def test_get_db_file(self):
|
||||
self.assertEqual(self.unix.get_db_file(), "initdb.file")
|
||||
|
@ -26,8 +26,10 @@ from isc.datasrc import sqlite3_ds
|
||||
from socketserver import *
|
||||
import os
|
||||
from isc.config.ccsession import *
|
||||
from isc.log.log import *
|
||||
from isc.cc import SessionError
|
||||
import socket
|
||||
import select
|
||||
import errno
|
||||
from optparse import OptionParser, OptionValueError
|
||||
try:
|
||||
@ -40,22 +42,25 @@ except ImportError as e:
|
||||
|
||||
if "B10_FROM_BUILD" in os.environ:
|
||||
SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
|
||||
UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE"] + "/auth_xfrout_conn"
|
||||
AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
|
||||
UNIX_SOCKET_FILE= os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATAROOTDIR = "@datarootdir@"
|
||||
SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
|
||||
AUTH_SPECFILE_PATH = SPECFILE_PATH
|
||||
UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
|
||||
|
||||
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
|
||||
|
||||
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
|
||||
MAX_TRANSFERS_OUT = 10
|
||||
verbose_mode = False
|
||||
|
||||
|
||||
class XfroutException(Exception): pass
|
||||
class TmpException(Exception): pass
|
||||
VERBOSE_MODE = False
|
||||
|
||||
class XfroutSession(BaseRequestHandler):
|
||||
def __init__(self, request, client_address, server, log):
|
||||
BaseRequestHandler.__init__(self, request, client_address, server)
|
||||
self._log = log
|
||||
|
||||
def handle(self):
|
||||
fd = recv_fd(self.request.fileno())
|
||||
|
||||
@ -63,8 +68,7 @@ class XfroutSession(BaseRequestHandler):
|
||||
# This may happen when one xfrout process try to connect to
|
||||
# xfrout unix socket server, to check whether there is another
|
||||
# xfrout running.
|
||||
print("[b10-xfrout] Failed to receive the FD for XFR connection, "
|
||||
"maybe because another xfrout process was started.")
|
||||
self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
|
||||
return
|
||||
|
||||
data_len = self.request.recv(2)
|
||||
@ -73,9 +77,8 @@ class XfroutSession(BaseRequestHandler):
|
||||
sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
self.dns_xfrout_start(sock, msgdata)
|
||||
except TmpException as e:
|
||||
if verbose_mode:
|
||||
self.log_msg(str(e))
|
||||
except Exception as e:
|
||||
self._log.log_message("error", str(e))
|
||||
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
@ -88,9 +91,8 @@ class XfroutSession(BaseRequestHandler):
|
||||
try:
|
||||
msg = Message(Message.PARSE)
|
||||
Message.from_wire(msg, mdata)
|
||||
except TmpException as err:
|
||||
if verbose_mode:
|
||||
self.log_msg(str(err))
|
||||
except Exception as err:
|
||||
self._log.log_message("error", str(err))
|
||||
return Rcode.FORMERR(), None
|
||||
|
||||
return Rcode.NOERROR(), msg
|
||||
@ -180,16 +182,11 @@ class XfroutSession(BaseRequestHandler):
|
||||
return self. _reply_query_with_error_rcode(msg, sock, rcode_)
|
||||
|
||||
try:
|
||||
if verbose_mode:
|
||||
self.log_msg("transfer of '%s/IN': AXFR started" % zone_name)
|
||||
|
||||
self._log.log_message("info", "transfer of '%s/IN': AXFR started" % zone_name)
|
||||
self._reply_xfrout_query(msg, sock, zone_name)
|
||||
|
||||
if verbose_mode:
|
||||
self.log_msg("transfer of '%s/IN': AXFR end" % zone_name)
|
||||
except TmpException as err:
|
||||
if verbose_mode:
|
||||
sys.stderr.write("[b10-xfrout] %s\n" % str(err))
|
||||
self._log.log_message("info", "transfer of '%s/IN': AXFR end" % zone_name)
|
||||
except Exception as err:
|
||||
self._log.log_message("error", str(err))
|
||||
|
||||
self.server.decrease_transfers_counter()
|
||||
return
|
||||
@ -261,7 +258,7 @@ class XfroutSession(BaseRequestHandler):
|
||||
# the message length to know if the rrset has been added sucessfully.
|
||||
for rr_data in sqlite3_ds.get_zone_datas(zone_name, self.server.get_db_file()):
|
||||
if self.server._shutdown_event.is_set(): # Check if xfrout is shutdown
|
||||
raise XfroutException("shutdown!")
|
||||
self._log.log_message("error", "shutdown!")
|
||||
|
||||
# TODO: RRType.SOA() ?
|
||||
if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
|
||||
@ -281,21 +278,24 @@ class XfroutSession(BaseRequestHandler):
|
||||
|
||||
self._send_message_with_last_soa(msg, sock, rrset_soa)
|
||||
|
||||
def log_msg(self, msg):
|
||||
print('[b10-xfrout] ', msg)
|
||||
|
||||
|
||||
class UnixSockServer(ThreadingUnixStreamServer):
|
||||
'''The unix domain socket server which accept xfr query sent from auth server.'''
|
||||
|
||||
def __init__(self, sock_file, handle_class, shutdown_event, config_data):
|
||||
def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc, log):
|
||||
self._remove_unused_sock_file(sock_file)
|
||||
self._sock_file = sock_file
|
||||
ThreadingUnixStreamServer.__init__(self, sock_file, handle_class)
|
||||
self._lock = threading.Lock()
|
||||
self._transfers_counter = 0
|
||||
self._shutdown_event = shutdown_event
|
||||
self._log = log
|
||||
self.update_config_data(config_data)
|
||||
self._cc = cc
|
||||
|
||||
def finish_request(self, request, client_address):
|
||||
'''Finish one request by instantiating RequestHandlerClass.'''
|
||||
self.RequestHandlerClass(request, client_address, self, self._log)
|
||||
|
||||
def _remove_unused_sock_file(self, sock_file):
|
||||
'''Try to remove the socket file. If the file is being used
|
||||
@ -332,23 +332,28 @@ class UnixSockServer(ThreadingUnixStreamServer):
|
||||
ThreadingUnixStreamServer.shutdown(self)
|
||||
try:
|
||||
os.unlink(self._sock_file)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
self._log.log_message("error", str(e))
|
||||
|
||||
def update_config_data(self, new_config):
|
||||
'''Apply the new config setting of xfrout module. '''
|
||||
|
||||
self._log.log_message('info', 'update config data start.')
|
||||
self._lock.acquire()
|
||||
self._max_transfers_out = new_config.get('transfers_out')
|
||||
self._db_file = new_config.get('db_file')
|
||||
self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
|
||||
self._lock.release()
|
||||
self._log.log_message('info', 'update config data complete.')
|
||||
|
||||
def get_db_file(self):
|
||||
self._lock.acquire()
|
||||
file = self._db_file
|
||||
self._lock.release()
|
||||
file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
|
||||
# this too should be unnecessary, but currently the
|
||||
# 'from build' override isn't stored in the config
|
||||
# (and we don't have indirect python access to datasources yet)
|
||||
if is_default and "B10_FROM_BUILD" in os.environ:
|
||||
file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
|
||||
return file
|
||||
|
||||
|
||||
def increase_transfers_counter(self):
|
||||
'''Return False, if counter + 1 > max_transfers_out, or else
|
||||
return True
|
||||
@ -367,29 +372,45 @@ class UnixSockServer(ThreadingUnixStreamServer):
|
||||
self._lock.release()
|
||||
|
||||
def listen_on_xfr_query(unix_socket_server):
|
||||
|
||||
'''Listen xfr query in one single thread. Polls for shutdown
|
||||
every 0.1 seconds, is there a better time?
|
||||
'''
|
||||
unix_socket_server.serve_forever(poll_interval = 0.1)
|
||||
|
||||
while True:
|
||||
try:
|
||||
unix_socket_server.serve_forever(poll_interval = 0.1)
|
||||
except select.error as err:
|
||||
# serve_forever() calls select.select(), which can be
|
||||
# interrupted.
|
||||
# If it is interrupted, it raises select.error with the
|
||||
# errno set to EINTR. We ignore this case, and let the
|
||||
# normal program flow continue by trying serve_forever()
|
||||
# again.
|
||||
if err.args[0] != errno.EINTR: raise
|
||||
|
||||
|
||||
|
||||
class XfroutServer:
|
||||
def __init__(self):
|
||||
self._unix_socket_server = None
|
||||
self._log = None
|
||||
self._listen_sock_file = UNIX_SOCKET_FILE
|
||||
self._shutdown_event = threading.Event()
|
||||
self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
|
||||
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
|
||||
self._config_data = self._cc.get_full_config()
|
||||
self._cc.start()
|
||||
self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
|
||||
self._config_data.get('log_severity'), self._config_data.get('log_versions'),
|
||||
self._config_data.get('log_max_bytes'), True)
|
||||
self._start_xfr_query_listener()
|
||||
|
||||
|
||||
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._shutdown_event, self._config_data,
|
||||
self._cc, self._log);
|
||||
listener = threading.Thread(target = listen_on_xfr_query, args = (self._unix_socket_server,))
|
||||
listener.start()
|
||||
|
||||
@ -403,6 +424,9 @@ class XfroutServer:
|
||||
continue
|
||||
self._config_data[key] = new_config[key]
|
||||
|
||||
if self._log:
|
||||
self._log.update_config(new_config)
|
||||
|
||||
if self._unix_socket_server:
|
||||
self._unix_socket_server.update_config_data(self._config_data)
|
||||
|
||||
@ -428,8 +452,7 @@ class XfroutServer:
|
||||
|
||||
def command_handler(self, cmd, args):
|
||||
if cmd == "shutdown":
|
||||
if verbose_mode:
|
||||
print("[b10-xfrout] Received shutdown command")
|
||||
self._log.log_message("info", "Received shutdown command.")
|
||||
self.shutdown()
|
||||
answer = create_answer(0)
|
||||
else:
|
||||
@ -464,18 +487,18 @@ if '__main__' == __name__:
|
||||
parser = OptionParser()
|
||||
set_cmd_options(parser)
|
||||
(options, args) = parser.parse_args()
|
||||
verbose_mode = options.verbose
|
||||
VERBOSE_MODE = options.verbose
|
||||
|
||||
set_signal_handler()
|
||||
xfrout_server = XfroutServer()
|
||||
xfrout_server.run()
|
||||
except KeyboardInterrupt:
|
||||
print("[b10-xfrout] exit xfrout process")
|
||||
sys.stderr.write("[b10-xfrout] exit xfrout process")
|
||||
except SessionError as e:
|
||||
print('[b10-xfrout] Error creating xfrout, '
|
||||
'is the command channel daemon running?' )
|
||||
sys.stderr.write("[b10-xfrout] Error creating xfrout,"
|
||||
"is the command channel daemon running?")
|
||||
except ModuleCCSessionError as e:
|
||||
print('[b10-xfrout] exit xfrout process:', e)
|
||||
sys.stderr.write("info", '[b10-xfrout] exit xfrout process:', e)
|
||||
|
||||
if xfrout_server:
|
||||
xfrout_server.shutdown()
|
||||
|
@ -12,7 +12,37 @@
|
||||
"item_name": "db_file",
|
||||
"item_type": "string",
|
||||
"item_optional": False,
|
||||
"item_default": '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
|
||||
"item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
|
||||
},
|
||||
{
|
||||
"item_name": "log_name",
|
||||
"item_type": "string",
|
||||
"item_optional": False,
|
||||
"item_default": "Xfrout"
|
||||
},
|
||||
{
|
||||
"item_name": "log_file",
|
||||
"item_type": "string",
|
||||
"item_optional": False,
|
||||
"item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log"
|
||||
},
|
||||
{
|
||||
"item_name": "log_severity",
|
||||
"item_type": "string",
|
||||
"item_optional": False,
|
||||
"item_default": "debug"
|
||||
},
|
||||
{
|
||||
"item_name": "log_versions",
|
||||
"item_type": "integer",
|
||||
"item_optional": False,
|
||||
"item_default": 5
|
||||
},
|
||||
{
|
||||
"item_name": "log_max_bytes",
|
||||
"item_type": "integer",
|
||||
"item_optional": False,
|
||||
"item_default": 1048576
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
|
@ -6,10 +6,12 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
# error. Unfortunately there doesn't seem to be an easy way to selectively
|
||||
# avoid the error. As a short term workaround we suppress this warning
|
||||
# for the entire this module. See also src/bin/auth/Makefile.am.
|
||||
if USE_GXX
|
||||
AM_CXXFLAGS += -Wno-unused-parameter
|
||||
endif
|
||||
|
||||
lib_LIBRARIES = libcc.a
|
||||
libcc_a_SOURCES = data.cc data.h session.cc session.h
|
||||
lib_LTLIBRARIES = libcc.la
|
||||
libcc_la_SOURCES = data.cc data.h session.cc session.h
|
||||
|
||||
CLEANFILES = *.gcno *.gcda session_config.h
|
||||
|
||||
@ -27,8 +29,8 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
# TODO: remove PTHREAD_LDFLAGS (and from configure too)
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
|
||||
|
||||
run_unittests_LDADD = libcc.a $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
run_unittests_LDADD = libcc.la $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
|
||||
endif
|
||||
|
@ -19,6 +19,17 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// XXX: there seems to be a strange dependency between ASIO and std library
|
||||
// definitions. On some platforms if we include std headers before ASIO
|
||||
// headers unexpected behaviors will happen.
|
||||
// A middle term solution is to generalize our local wrapper interface
|
||||
// (currently only available for the auth server), where all such portability
|
||||
// issues are hidden, and to have other modules use the wrapper.
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
#include <asio.hpp>
|
||||
#include <asio/error_code.hpp>
|
||||
#include <asio/system_error.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
@ -29,10 +40,6 @@
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asio/error_code.hpp>
|
||||
#include <asio/system_error.hpp>
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
#include "data.h"
|
||||
|
@ -15,10 +15,13 @@
|
||||
// $Id: data_unittests.cc 1899 2010-05-21 12:03:59Z jelte $
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// XXX: the ASIO header must be included before others. See session.cc.
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <session.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
using namespace isc::cc;
|
||||
|
@ -2,7 +2,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
# see src/lib/cc/Makefile.am for -Wno-unused-parameter
|
||||
if USE_GXX
|
||||
AM_CXXFLAGS += -Wno-unused-parameter
|
||||
endif
|
||||
|
||||
CLEANFILES = *.gcno *.gcda
|
||||
|
||||
@ -20,6 +22,9 @@ run_unittests_LDADD = $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
|
||||
run_unittests_LDADD += libfake_session.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
|
||||
# link *only* to data.o from lib/cc (more importantly, don't link in
|
||||
# the session class provided there, since we use our own fake_session
|
||||
# here)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/cc/data.o
|
||||
|
||||
endif
|
||||
|
@ -192,7 +192,7 @@ refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds,
|
||||
// Lookup failed
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
// Referral bit is expected, so clear it when checking flags
|
||||
if ((newtask.flags & ~DataSrc::REFERRAL) != 0) {
|
||||
return (false);
|
||||
@ -202,18 +202,18 @@ refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds,
|
||||
}
|
||||
|
||||
// Match downward, from the zone apex to the query name, looking for
|
||||
// referrals.
|
||||
// referrals. Note that we exclude the apex name and query name themselves;
|
||||
// they'll be handled in a normal lookup in the zone.
|
||||
inline bool
|
||||
hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
|
||||
QueryTaskPtr task)
|
||||
{
|
||||
const int nlen = task->qname.getLabelCount();
|
||||
const int diff = nlen - zonename->getLabelCount();
|
||||
const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
|
||||
if (diff > 1) {
|
||||
bool found = false;
|
||||
RRsetList ref;
|
||||
for (int i = diff; i > 1; --i) {
|
||||
const Name sub(task->qname.split(i - 1, nlen - i));
|
||||
for (int i = diff - 1; i > 0; --i) {
|
||||
const Name sub(task->qname.split(i));
|
||||
if (refQuery(sub, q.qclass(), ds, zonename, ref)) {
|
||||
found = true;
|
||||
break;
|
||||
@ -360,11 +360,11 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
|
||||
|
||||
// Find the closest provable enclosing name for QNAME
|
||||
Name enclosure(zonename);
|
||||
const int nlen = task->qname.getLabelCount();
|
||||
const int diff = nlen - enclosure.getLabelCount();
|
||||
const int diff = task->qname.getLabelCount() -
|
||||
enclosure.getLabelCount();
|
||||
string hash2;
|
||||
for (int i = 1; i <= diff; ++i) {
|
||||
enclosure = task->qname.split(i, nlen - i);
|
||||
enclosure = task->qname.split(i);
|
||||
string nodehash(nsec3->getHash(enclosure));
|
||||
if (nodehash == hash1) {
|
||||
break;
|
||||
@ -434,8 +434,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
|
||||
return (DataSrc::SUCCESS);
|
||||
}
|
||||
|
||||
const int nlen = task->qname.getLabelCount();
|
||||
const int diff = nlen - zonename->getLabelCount();
|
||||
const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
|
||||
if (diff < 1) {
|
||||
return (DataSrc::SUCCESS);
|
||||
}
|
||||
@ -445,7 +444,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
|
||||
bool cname = false;
|
||||
|
||||
for (int i = 1; i <= diff; ++i) {
|
||||
const Name& wname(star.concatenate(task->qname.split(i, nlen - i)));
|
||||
const Name& wname(star.concatenate(task->qname.split(i)));
|
||||
QueryTask newtask(wname, task->qclass, task->qtype, Section::ANSWER(),
|
||||
QueryTask::AUTH_QUERY);
|
||||
result = doQueryTask(ds, zonename, newtask, wild);
|
||||
@ -541,8 +540,7 @@ DataSrc::doQuery(Query& q) {
|
||||
// (Note that RRtype DS queries need to go to the parent.)
|
||||
const int nlabels = task->qname.getLabelCount() - 1;
|
||||
NameMatch match(nlabels != 0 && task->qtype == RRType::DS() ?
|
||||
task->qname.split(1, task->qname.getLabelCount() - 1) :
|
||||
task->qname);
|
||||
task->qname.split(1) : task->qname);
|
||||
findClosestEnclosure(match, task->qclass);
|
||||
const DataSrc* datasource = match.bestDataSrc();
|
||||
const Name* zonename = match.closestName();
|
||||
@ -615,9 +613,12 @@ DataSrc::doQuery(Query& q) {
|
||||
// the authority section.
|
||||
RRsetList auth;
|
||||
if (!refQuery(*zonename, q.qclass(), datasource, zonename,
|
||||
auth)) {
|
||||
m.setRcode(Rcode::SERVFAIL());
|
||||
return;
|
||||
auth) ||
|
||||
!auth.findRRset(RRType::NS(),
|
||||
datasource->getClass())) {
|
||||
isc_throw(DataSourceError,
|
||||
"NS RR not found in " << *zonename << "/" <<
|
||||
datasource->getClass());
|
||||
}
|
||||
|
||||
copyAuth(q, auth);
|
||||
@ -706,8 +707,9 @@ DataSrc::doQuery(Query& q) {
|
||||
|
||||
result = addSOA(q, zonename, datasource);
|
||||
if (result != SUCCESS) {
|
||||
m.setRcode(Rcode::SERVFAIL());
|
||||
return;
|
||||
isc_throw(DataSourceError,
|
||||
"SOA RR not found in" << *zonename <<
|
||||
"/" << datasource->getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,7 @@ int
|
||||
Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
|
||||
const unsigned int nlabels = name.getLabelCount();
|
||||
for (unsigned int i = 0; i < nlabels; ++i) {
|
||||
const Name matchname(name.split(i, nlabels - i));
|
||||
const Name matchname(name.split(i));
|
||||
const int rc = hasExactZone(matchname.toText().c_str());
|
||||
if (rc >= 0) {
|
||||
if (position != NULL) {
|
||||
@ -356,9 +356,7 @@ Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
|
||||
return;
|
||||
}
|
||||
|
||||
match.update(*this, match.qname().split(position,
|
||||
match.qname().getLabelCount() -
|
||||
position));
|
||||
match.update(*this, match.qname().split(position));
|
||||
}
|
||||
|
||||
DataSrc::Result
|
||||
|
@ -22,8 +22,8 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
run_unittests_LDADD = $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(SQLITE_LIBS)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
endif
|
||||
|
||||
|
@ -540,6 +540,15 @@ TEST_F(DataSrcTest, Dname) {
|
||||
EXPECT_TRUE(it->isLast());
|
||||
}
|
||||
|
||||
TEST_F(DataSrcTest, DnameExact) {
|
||||
// The example.org test zone has a DNAME RR for dname2.foo.example.org.
|
||||
// A query for that name with a different RR type than DNAME shouldn't
|
||||
// confuse delegation processing.
|
||||
createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
|
||||
RRType::A());
|
||||
headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
|
||||
}
|
||||
|
||||
TEST_F(DataSrcTest, Cname) {
|
||||
readAndProcessQuery("q_cname");
|
||||
|
||||
@ -761,7 +770,7 @@ TEST_F(DataSrcTest, DS) {
|
||||
}
|
||||
|
||||
TEST_F(DataSrcTest, CNAMELoop) {
|
||||
createAndProcessQuery(Name("loop1.example.com"), RRClass::IN(),
|
||||
createAndProcessQuery(Name("one.loop.example"), RRClass::IN(),
|
||||
RRType::A());
|
||||
}
|
||||
|
||||
@ -843,8 +852,8 @@ TEST_F(DataSrcTest, AddRemoveDataSrc) {
|
||||
EXPECT_EQ(0, ds.dataSrcCount());
|
||||
}
|
||||
|
||||
#if 0 // currently fails
|
||||
TEST_F(DataSrcTest, synthesizedCnameTooLong) {
|
||||
// currently fails
|
||||
TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
|
||||
// qname has the possible max length (255 octets). it matches a DNAME,
|
||||
// and the synthesized CNAME would exceed the valid length.
|
||||
createAndProcessQuery(
|
||||
@ -854,6 +863,23 @@ TEST_F(DataSrcTest, synthesizedCnameTooLong) {
|
||||
"0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
|
||||
RRClass::IN(), RRType::A());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DataSrcTest, noNSZone) {
|
||||
EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
|
||||
RRClass::IN(), RRType::A()),
|
||||
DataSourceError);
|
||||
}
|
||||
|
||||
TEST_F(DataSrcTest, noNSButDnameZone) {
|
||||
EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
|
||||
RRClass::IN(), RRType::A()),
|
||||
DataSourceError);
|
||||
}
|
||||
|
||||
TEST_F(DataSrcTest, noSOAZone) {
|
||||
EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
|
||||
RRClass::IN(), RRType::A()),
|
||||
DataSourceError);
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -96,6 +96,7 @@ private:
|
||||
ADDRESS,
|
||||
DELEGATION
|
||||
};
|
||||
class RRsetMatch;
|
||||
|
||||
void findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
|
||||
isc::dns::RRsetList& target,
|
||||
|
1
src/lib/datasrc/tests/testdata/example.org
vendored
1
src/lib/datasrc/tests/testdata/example.org
vendored
@ -11,3 +11,4 @@ mail.example.org. A 192.0.2.10
|
||||
sub.example.org. NS ns.sub.example.org.
|
||||
ns.sub.example.org. A 192.0.2.101
|
||||
dname.example.org. DNAME dname.example.info.
|
||||
dname2.foo.example.org. DNAME dname2.example.info.
|
||||
|
BIN
src/lib/datasrc/tests/testdata/example.org.sqlite3
vendored
BIN
src/lib/datasrc/tests/testdata/example.org.sqlite3
vendored
Binary file not shown.
@ -55,52 +55,30 @@ EXTRA_DIST += rdata/hs_4/a_1.h
|
||||
BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc
|
||||
#TODO: check this###BUILT_SOURCES = rdataclass.h rdataclass.cc
|
||||
|
||||
lib_LTLIBRARIES = libdns.la
|
||||
lib_LTLIBRARIES = libdns++.la
|
||||
|
||||
libdns_la_SOURCES = base32.h base32.cc
|
||||
libdns_la_SOURCES += base64.h base64.cc
|
||||
libdns_la_SOURCES += buffer.h
|
||||
libdns_la_SOURCES += dnssectime.h dnssectime.cc
|
||||
libdns_la_SOURCES += exceptions.h exceptions.cc
|
||||
libdns_la_SOURCES += hex.h hex.cc
|
||||
libdns_la_SOURCES += message.h message.cc
|
||||
libdns_la_SOURCES += messagerenderer.h messagerenderer.cc
|
||||
libdns_la_SOURCES += name.h name.cc
|
||||
libdns_la_SOURCES += rdata.h rdata.cc
|
||||
libdns_la_SOURCES += rrclass.cc
|
||||
libdns_la_SOURCES += rrparamregistry.h
|
||||
libdns_la_SOURCES += rrset.h rrset.cc
|
||||
libdns_la_SOURCES += rrsetlist.h rrsetlist.cc
|
||||
libdns_la_SOURCES += rrttl.h rrttl.cc
|
||||
libdns_la_SOURCES += rrtype.cc
|
||||
libdns_la_SOURCES += question.h question.cc
|
||||
libdns_la_SOURCES += sha1.h sha1.cc
|
||||
libdns_la_SOURCES += tsig.h tsig.cc
|
||||
libdns___la_SOURCES = base32.h base32.cc
|
||||
libdns___la_SOURCES += base64.h base64.cc
|
||||
libdns___la_SOURCES += buffer.h
|
||||
libdns___la_SOURCES += dnssectime.h dnssectime.cc
|
||||
libdns___la_SOURCES += exceptions.h exceptions.cc
|
||||
libdns___la_SOURCES += hex.h hex.cc
|
||||
libdns___la_SOURCES += message.h message.cc
|
||||
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
|
||||
libdns___la_SOURCES += name.h name.cc
|
||||
libdns___la_SOURCES += rdata.h rdata.cc
|
||||
libdns___la_SOURCES += rrclass.cc
|
||||
libdns___la_SOURCES += rrparamregistry.h
|
||||
libdns___la_SOURCES += rrset.h rrset.cc
|
||||
libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
|
||||
libdns___la_SOURCES += rrttl.h rrttl.cc
|
||||
libdns___la_SOURCES += rrtype.cc
|
||||
libdns___la_SOURCES += question.h question.cc
|
||||
libdns___la_SOURCES += sha1.h sha1.cc
|
||||
libdns___la_SOURCES += tsig.h tsig.cc
|
||||
|
||||
|
||||
#if HAVE_BOOST_PYTHON
|
||||
## This is a loadable module for python scripts, so we use the prefix "pyexec"
|
||||
## to make sure the object files will be installed in the appropriate place
|
||||
## for this purpose.
|
||||
#pyexec_LTLIBRARIES = bind10_dns.la
|
||||
#bind10_dns_la_SOURCES = python_dns.cc
|
||||
#bind10_dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
|
||||
#bind10_dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(B10_CXXFLAGS)
|
||||
#if GCC_WERROR_OK
|
||||
## XXX: Boost.Python triggers strict aliasing violation, so if we use -Werror
|
||||
## we need to suppress the warnings.
|
||||
#bind10_dns_la_CXXFLAGS += -fno-strict-aliasing
|
||||
#endif
|
||||
#bind10_dns_la_LDFLAGS = $(BOOST_LDFLAGS) $(PYTHON_LDFLAGS)
|
||||
## Python prefers .so, while some OSes (specifically MacOS) use a different
|
||||
## suffix for dynamic objects. -module is necessary to work this around.
|
||||
#bind10_dns_la_LDFLAGS += -module
|
||||
#bind10_dns_la_LIBADD = $(top_builddir)/src/lib/dns/libdns.la
|
||||
#bind10_dns_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
|
||||
#bind10_dns_la_LIBADD += $(BOOST_PYTHON_LIB) $(PYTHON_LIB)
|
||||
#endif
|
||||
|
||||
nodist_libdns_la_SOURCES = rdataclass.cc rrclass.h rrtype.h rrparamregistry.cc
|
||||
nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
|
||||
nodist_libdns___la_SOURCES += rrparamregistry.cc
|
||||
|
||||
rrclass.h: rrclass-placeholder.h
|
||||
rrtype.h: rrtype-placeholder.h
|
||||
@ -108,8 +86,8 @@ rrparamregistry.cc: rrparamregistry-placeholder.cc
|
||||
rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
|
||||
./gen-rdatacode.py
|
||||
|
||||
libdns_includedir = $(includedir)/dns
|
||||
libdns_include_HEADERS = \
|
||||
libdns++_includedir = $(includedir)/dns
|
||||
libdns++_include_HEADERS = \
|
||||
buffer.h \
|
||||
dnssectime.h \
|
||||
exceptions.h \
|
||||
|
@ -923,7 +923,7 @@ SectionIterator<T>::operator*() const {
|
||||
template <typename T>
|
||||
const T*
|
||||
SectionIterator<T>::operator->() const {
|
||||
return (impl_->it_.operator->());
|
||||
return (&(operator*()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -267,7 +267,7 @@ Name::Name(const std::string &namestring, bool downcase) {
|
||||
if (state == ft_ordinary) {
|
||||
assert(count != 0);
|
||||
ndata.at(offsets.back()) = count;
|
||||
|
||||
|
||||
offsets.push_back(ndata.size());
|
||||
// add a trailing \0
|
||||
ndata.push_back('\0');
|
||||
|
@ -13,17 +13,22 @@ libdns_python_la_SOURCES = libdns_python.cc libdns_python_common.cc
|
||||
libdns_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
|
||||
libdns_python_la_LDFLAGS = $(PYTHON_LDFLAGS)
|
||||
|
||||
#libdns_python_rrset_la_SOURCES = rrset_python.cc
|
||||
#libdns_python_rrset_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
|
||||
#libdns_python_rrset_la_LDFLAGS = $(PYTHON_LDFLAGS) libdns_python_name.la
|
||||
|
||||
#libdns_python_rrset_la_SOURCES = rrset_python.cc
|
||||
#libdns_python_rrset_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
|
||||
#libdns_python_rrset_la_LDFLAGS = $(PYTHON_LDFLAGS)
|
||||
# directly included from source files, so these don't have their own
|
||||
# rules
|
||||
EXTRA_DIST = libdns_python_common.h
|
||||
EXTRA_DIST += messagerenderer_python.cc
|
||||
EXTRA_DIST += message_python.cc
|
||||
EXTRA_DIST += rrclass_python.cc
|
||||
EXTRA_DIST += name_python.cc
|
||||
EXTRA_DIST += rrset_python.cc
|
||||
EXTRA_DIST += question_python.cc
|
||||
EXTRA_DIST += rrttl_python.cc
|
||||
EXTRA_DIST += rdata_python.cc
|
||||
EXTRA_DIST += rrtype_python.cc
|
||||
|
||||
# Python prefers .so, while some OSes (specifically MacOS) use a different
|
||||
# suffix for dynamic objects. -module is necessary to work this around.
|
||||
libdns_python_la_LDFLAGS += -module
|
||||
libdns_python_la_LIBADD = $(top_builddir)/src/lib/dns/libdns.la
|
||||
libdns_python_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
|
||||
libdns_python_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
|
||||
libdns_python_la_LIBADD += $(PYTHON_LIB)
|
||||
|
@ -16,7 +16,7 @@ PYCOVERAGE = $(PYTHON)
|
||||
check-local:
|
||||
for pytest in $(PYTESTS) ; do \
|
||||
echo Running test: $$pytest ; \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_srcdir)/src/lib/dns/python/.libs \
|
||||
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
|
||||
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata \
|
||||
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
|
||||
done
|
||||
|
@ -29,8 +29,7 @@ namespace isc {
|
||||
namespace dns {
|
||||
|
||||
void
|
||||
RRsetList::addRRset(RRsetPtr rrsetptr)
|
||||
{
|
||||
RRsetList::addRRset(RRsetPtr rrsetptr) {
|
||||
ConstRRsetPtr rrset_found = findRRset(rrsetptr->getType(),
|
||||
rrsetptr->getClass());
|
||||
if (rrset_found != NULL) {
|
||||
@ -42,8 +41,7 @@ RRsetList::addRRset(RRsetPtr rrsetptr)
|
||||
}
|
||||
|
||||
RRsetPtr
|
||||
RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass)
|
||||
{
|
||||
RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) {
|
||||
BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) {
|
||||
if ((rrsetptr->getClass() == rrclass) &&
|
||||
(rrsetptr->getType() == rrtype)) {
|
||||
|
@ -60,7 +60,7 @@ public:
|
||||
}
|
||||
P operator->() const
|
||||
{
|
||||
return (it_.operator->());
|
||||
return (&(operator*()));
|
||||
}
|
||||
bool operator==(const RRsetListIterator& other)
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ run_unittests_SOURCES += run_unittests.cc
|
||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
run_unittests_LDADD = $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
|
||||
endif
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
SUBDIRS = datasrc cc config # Util
|
||||
SUBDIRS = datasrc cc config log # Util
|
||||
|
||||
python_PYTHON = __init__.py
|
||||
|
||||
|
@ -2,3 +2,4 @@ import isc.datasrc
|
||||
import isc.cc
|
||||
import isc.config
|
||||
#import isc.dns
|
||||
import isc.log
|
||||
|
@ -16,7 +16,8 @@
|
||||
# $Id$
|
||||
|
||||
import sys, re, string
|
||||
|
||||
import time
|
||||
import os
|
||||
#########################################################################
|
||||
# define exceptions
|
||||
#########################################################################
|
||||
@ -102,7 +103,7 @@ def isname(s):
|
||||
# isttl: check whether a string is a valid TTL specifier.
|
||||
# returns: boolean
|
||||
#########################################################################
|
||||
ttl_regex = re.compile('[0-9]+[wdhms]?', re.I)
|
||||
ttl_regex = re.compile('([0-9]+[wdhms]?)+', re.I)
|
||||
def isttl(s):
|
||||
global ttl_regex
|
||||
if ttl_regex.match(s):
|
||||
@ -121,19 +122,26 @@ def isttl(s):
|
||||
# MasterFileError
|
||||
#########################################################################
|
||||
def parse_ttl(s):
|
||||
m = re.match('([0-9]+)(.*)', s)
|
||||
if not m:
|
||||
sum = 0
|
||||
if not isttl(s):
|
||||
raise MasterFileError('Invalid TTL: ' + s)
|
||||
ttl, suffix = int(m.group(1)), m.group(2)
|
||||
if suffix.lower() == 'w':
|
||||
ttl *= 604800
|
||||
elif suffix.lower() == 'd':
|
||||
ttl *= 86400
|
||||
elif suffix.lower() == 'h':
|
||||
ttl *= 3600
|
||||
elif suffix.lower() == 'm':
|
||||
ttl *= 60
|
||||
return str(ttl)
|
||||
for ttl_expr in re.findall('\d+[wdhms]?', s, re.I):
|
||||
if ttl_expr.isdigit():
|
||||
ttl = int(ttl_expr)
|
||||
sum += ttl
|
||||
continue
|
||||
ttl = int(ttl_expr[:-1])
|
||||
suffix = ttl_expr[-1].lower()
|
||||
if suffix == 'w':
|
||||
ttl *= 604800
|
||||
elif suffix == 'd':
|
||||
ttl *= 86400
|
||||
elif suffix == 'h':
|
||||
ttl *= 3600
|
||||
elif suffix == 'm':
|
||||
ttl *= 60
|
||||
sum += ttl
|
||||
return str(sum)
|
||||
|
||||
#########################################################################
|
||||
# records: generator function to return complete RRs from the zone file,
|
||||
@ -147,7 +155,9 @@ def records(input):
|
||||
record = []
|
||||
complete = True
|
||||
paren = 0
|
||||
size = 0
|
||||
for line in input:
|
||||
size += len(line)
|
||||
list = cleanup(line).split()
|
||||
for word in list:
|
||||
if paren == 0:
|
||||
@ -169,10 +179,12 @@ def records(input):
|
||||
|
||||
if paren == 1 or not record:
|
||||
continue
|
||||
|
||||
|
||||
ret = ' '.join(record)
|
||||
record = []
|
||||
yield ret
|
||||
oldsize = size
|
||||
size = 0
|
||||
yield ret, oldsize
|
||||
|
||||
#########################################################################
|
||||
# define the MasterFile class for reading zone master files
|
||||
@ -181,24 +193,62 @@ class MasterFile:
|
||||
__rrclass = 'IN'
|
||||
__maxttl = 0x7fffffff
|
||||
__ttl = ''
|
||||
__lastttl = ''
|
||||
__zonefile = ''
|
||||
__name = ''
|
||||
__file_level = 0
|
||||
__file_type = ""
|
||||
__init_time = time.time()
|
||||
__records_num = 0
|
||||
|
||||
def __init__(self, filename, initial_origin = ''):
|
||||
if initial_origin == '.':
|
||||
initial_origin = ''
|
||||
def __init__(self, filename, initial_origin = '', verbose = False):
|
||||
self.__initial_origin = initial_origin
|
||||
self.__origin = initial_origin
|
||||
self.__datafile = filename
|
||||
|
||||
try:
|
||||
self.__zonefile = open(filename, 'r')
|
||||
except:
|
||||
raise MasterFileError("Could not open " + filename)
|
||||
self.__filesize = os.fstat(self.__zonefile.fileno()).st_size
|
||||
|
||||
self.__cur = 0
|
||||
self.__numback = 0
|
||||
self.__verbose = verbose
|
||||
try:
|
||||
self.__zonefile = open(filename, 'r')
|
||||
except:
|
||||
raise MasterFileError("Could not open " + filename)
|
||||
|
||||
def __status(self):
|
||||
interval = time.time() - MasterFile.__init_time
|
||||
if self.__filesize == 0:
|
||||
percent = 100
|
||||
else:
|
||||
percent = (self.__cur * 100)/self.__filesize
|
||||
|
||||
sys.stdout.write("\r" + (80 * " "))
|
||||
sys.stdout.write("\r%d RR(s) loaded in %d second(s) (%.2f%% of %s%s)"\
|
||||
% (MasterFile.__records_num, interval, percent, MasterFile.__file_type, self.__datafile))
|
||||
|
||||
def __del__(self):
|
||||
if self.__zonefile:
|
||||
self.__zonefile.close()
|
||||
|
||||
#########################################################################
|
||||
########################################################################
|
||||
# check if the zonename is relative
|
||||
# no then return
|
||||
# yes , sets the relative domain name to the stated name
|
||||
#######################################################################
|
||||
def __statedname(self, name, record):
|
||||
if name[-1] != '.':
|
||||
if not self.__origin:
|
||||
raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
|
||||
elif self.__origin == '.':
|
||||
name += '.'
|
||||
else:
|
||||
name += '.' + self.__origin
|
||||
return name
|
||||
#####################################################################
|
||||
# handle $ORIGIN, $TTL and $GENERATE directives
|
||||
# (currently only $ORIGIN and $TTL are implemented)
|
||||
# input:
|
||||
@ -216,20 +266,22 @@ class MasterFile:
|
||||
raise MasterFileError('Invalid $ORIGIN')
|
||||
if more:
|
||||
raise MasterFileError('Invalid $ORIGIN')
|
||||
if second == '.':
|
||||
self.__origin = ''
|
||||
elif second[-1] == '.':
|
||||
if second[-1] == '.':
|
||||
self.__origin = second
|
||||
else:
|
||||
elif not self.__origin:
|
||||
raise MasterFileError("$ORIGIN is not absolute in record:%s" % s)
|
||||
elif self.__origin != '.':
|
||||
self.__origin = second + '.' + self.__origin
|
||||
else:
|
||||
self.__origin = second + '.'
|
||||
return True
|
||||
elif re.match('\$ttl', first, re.I):
|
||||
if not second or not isttl(second):
|
||||
raise MasterFileError('Invalid TTL: "' + second + '"')
|
||||
if more:
|
||||
raise MasterFileError('Invalid $TTL statement')
|
||||
self.__ttl = parse_ttl(second)
|
||||
if int(self.__ttl) > self.__maxttl:
|
||||
MasterFile.__ttl = parse_ttl(second)
|
||||
if int(MasterFile.__ttl) > self.__maxttl:
|
||||
raise MasterFileError('TTL too high: ' + second)
|
||||
return True
|
||||
elif re.match('\$generate', first, re.I):
|
||||
@ -246,14 +298,28 @@ class MasterFile:
|
||||
# throws:
|
||||
# MasterFileError
|
||||
#########################################################################
|
||||
__filename = re.compile('[\"\']*([^\'\"]+)[\"\']*')
|
||||
__include_syntax1 = re.compile('\s+(\S+)(?:\s+(\S+))?$', re.I)
|
||||
__include_syntax2 = re.compile('\s+"([^"]+)"(?:\s+(\S+))?$', re.I)
|
||||
__include_syntax3 = re.compile("\s+'([^']+)'(?:\s+(\S+))?$", re.I)
|
||||
def __include(self, s):
|
||||
first, rest = pop(s)
|
||||
if re.match('\$include', first, re.I):
|
||||
m = self.__filename.match(rest)
|
||||
if m:
|
||||
file = m.group(1)
|
||||
return file
|
||||
if not s.lower().startswith('$include'):
|
||||
return "", ""
|
||||
s = s[len('$include'):]
|
||||
m = self.__include_syntax1.match(s)
|
||||
if not m:
|
||||
m = self.__include_syntax2.match(s)
|
||||
if not m:
|
||||
m = self.__include_syntax3.match(s)
|
||||
if not m:
|
||||
raise MasterFileError('Invalid $include format')
|
||||
file = m.group(1)
|
||||
if m.group(2):
|
||||
if not isname(m.group(2)):
|
||||
raise MasterFileError('Invalid $include format (invalid origin)')
|
||||
origin = self.__statedname(m.group(2), s)
|
||||
else:
|
||||
origin = self.__origin
|
||||
return file, origin
|
||||
|
||||
#########################################################################
|
||||
# try parsing an RR on the assumption that the type is specified in
|
||||
@ -272,6 +338,14 @@ class MasterFile:
|
||||
if istype(list[3]):
|
||||
if isclass(list[2]) and isttl(list[1]) and isname(list[0]):
|
||||
name, ttl, rrclass, rrtype = list[0:4]
|
||||
ttl = parse_ttl(ttl)
|
||||
MasterFile.__lastttl = ttl or MasterFile.__lastttl
|
||||
rdata = ' '.join(list[4:])
|
||||
ret = name, ttl, rrclass, rrtype, rdata
|
||||
elif isclass(list[1]) and isttl(list[2]) and isname(list[0]):
|
||||
name, rrclass, ttl, rrtype = list[0:4]
|
||||
ttl = parse_ttl(ttl)
|
||||
MasterFile.__lastttl = ttl or MasterFile.__lastttl
|
||||
rdata = ' '.join(list[4:])
|
||||
ret = name, ttl, rrclass, rrtype, rdata
|
||||
return ret
|
||||
@ -284,6 +358,9 @@ class MasterFile:
|
||||
# returns:
|
||||
# empty list if parse failed, else name, ttl, class, type, rdata
|
||||
#########################################################################
|
||||
def __getttl(self):
|
||||
return MasterFile.__ttl or MasterFile.__lastttl
|
||||
|
||||
def __three(self, record, curname):
|
||||
ret = ''
|
||||
list = record.split()
|
||||
@ -292,19 +369,25 @@ class MasterFile:
|
||||
if istype(list[2]) and not istype(list[1]):
|
||||
if isclass(list[1]) and not isttl(list[0]) and isname(list[0]):
|
||||
rrclass = list[1]
|
||||
ttl = self.__ttl
|
||||
ttl = self.__getttl()
|
||||
name = list[0]
|
||||
elif not isclass(list[1]) and isttl(list[1]) and isname(list[0]):
|
||||
elif not isclass(list[1]) and isttl(list[1]) and not isclass(list[0]) and isname(list[0]):
|
||||
rrclass = self.__rrclass
|
||||
ttl = parse_ttl(list[1])
|
||||
MasterFile.__lastttl = ttl or MasterFile.__lastttl
|
||||
name = list[0]
|
||||
elif curname and isclass(list[1]) and isttl(list[0]):
|
||||
rrclass = self.__rrclass
|
||||
rrclass = list[1]
|
||||
ttl = parse_ttl(list[0])
|
||||
MasterFile.__lastttl = ttl or MasterFile.__lastttl
|
||||
name = curname
|
||||
elif curname and isttl(list[1]) and isclass(list[0]):
|
||||
rrclass = list[0]
|
||||
ttl = parse_ttl(list[1])
|
||||
MasterFile.__lastttl = ttl or MasterFile.__lastttl
|
||||
name = curname
|
||||
else:
|
||||
return ret
|
||||
|
||||
rrtype = list[2]
|
||||
rdata = ' '.join(list[3:])
|
||||
ret = name, ttl, rrclass, rrtype, rdata
|
||||
@ -325,46 +408,81 @@ class MasterFile:
|
||||
list = record.split()
|
||||
if len(list) <= 2:
|
||||
return ret
|
||||
|
||||
if istype(list[1]):
|
||||
rrclass = self.__rrclass
|
||||
rrtype = list[1]
|
||||
if list[0].lower() == 'rrsig':
|
||||
name = curname
|
||||
ttl = self.__ttl
|
||||
ttl = self.__getttl()
|
||||
rrtype = list[0]
|
||||
rdata = ' '.join(list[1:])
|
||||
elif isttl(list[0]):
|
||||
ttl = parse_ttl(list[0])
|
||||
name = curname
|
||||
rdata = ' '.join(list[2:])
|
||||
elif isclass(list[0]):
|
||||
ttl = self.__getttl()
|
||||
name = curname
|
||||
rdata = ' '.join(list[2:])
|
||||
elif isname(list[0]):
|
||||
name = list[0]
|
||||
ttl = self.__ttl
|
||||
ttl = self.__getttl()
|
||||
rdata = ' '.join(list[2:])
|
||||
else:
|
||||
raise MasterFileError("Cannot parse RR: " + record)
|
||||
|
||||
ret = name, ttl, rrclass, rrtype, rdata
|
||||
|
||||
return ret
|
||||
|
||||
########################################################################
|
||||
#close verbose
|
||||
######################################################################
|
||||
def closeverbose(self):
|
||||
self.__status()
|
||||
|
||||
#########################################################################
|
||||
# zonedata: generator function to parse a zone master file and return
|
||||
# each RR as a (name, ttl, type, class, rdata) tuple
|
||||
#########################################################################
|
||||
def zonedata(self):
|
||||
name = ''
|
||||
last_status = 0.0
|
||||
flag = 1
|
||||
|
||||
for record in records(self.__zonefile):
|
||||
for record, size in records(self.__zonefile):
|
||||
if self.__verbose:
|
||||
now = time.time()
|
||||
if flag == 1:
|
||||
self.__status()
|
||||
flag = 0
|
||||
if now - last_status >= 1.0:
|
||||
self.__status()
|
||||
last_status = now
|
||||
|
||||
self.__cur += size
|
||||
if self.__directive(record):
|
||||
continue
|
||||
|
||||
incl = self.__include(record)
|
||||
incl, suborigin = self.__include(record)
|
||||
if incl:
|
||||
sub = MasterFile(incl, self.__origin)
|
||||
for name, ttl, rrclass, rrtype, rdata in sub.zonedata():
|
||||
yield (name, ttl, rrclass, rrtype, rdata)
|
||||
if self.__filesize == 0:
|
||||
percent = 100
|
||||
else:
|
||||
percent = (self.__cur * 100)/self.__filesize
|
||||
if self.__verbose:
|
||||
sys.stdout.write("\r" + (80 * " "))
|
||||
sys.stdout.write("\rIncluding \"%s\" from \"%s\"\n" % (incl, self.__datafile))
|
||||
MasterFile.__file_level += 1
|
||||
MasterFile.__file_type = "included "
|
||||
sub = MasterFile(incl, suborigin, self.__verbose)
|
||||
|
||||
for rrname, ttl, rrclass, rrtype, rdata in sub.zonedata():
|
||||
yield (rrname, ttl, rrclass, rrtype, rdata)
|
||||
if self.__verbose:
|
||||
sub.closeverbose()
|
||||
MasterFile.__file_level -= 1
|
||||
if MasterFile.__file_level == 0:
|
||||
MasterFile.__file_type = ""
|
||||
del sub
|
||||
continue
|
||||
|
||||
@ -373,7 +491,7 @@ class MasterFile:
|
||||
if rl[0] == '@':
|
||||
rl[0] = self.__origin
|
||||
if not self.__origin:
|
||||
rl[0] = '.'
|
||||
raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
|
||||
record = ' '.join(rl)
|
||||
|
||||
result = self.__four(record, name)
|
||||
@ -387,36 +505,43 @@ class MasterFile:
|
||||
if not result:
|
||||
first, rdata = pop(record)
|
||||
if istype(first):
|
||||
result = name, self.__ttl, self.__rrclass, first, rdata
|
||||
result = name, self.__getttl(), self.__rrclass, first, rdata
|
||||
|
||||
if not result:
|
||||
raise MasterFileError("Cannot parse RR: " + record)
|
||||
|
||||
name, ttl, rrclass, rrtype, rdata = result
|
||||
if name[-1] != '.':
|
||||
name += '.' + self.__origin
|
||||
name = self.__statedname(name, record)
|
||||
|
||||
if rrclass.lower() != 'in':
|
||||
raise MasterFileError("CH and HS zones not supported")
|
||||
|
||||
if not ttl:
|
||||
raise MasterFileError("No TTL specified; zone rejected")
|
||||
|
||||
# add origin to rdata containing names, if necessary
|
||||
if rrtype.lower() in ('cname', 'dname', 'ns', 'ptr'):
|
||||
if not isname(rdata):
|
||||
raise MasterFileError("Invalid " + rrtype + ": " + rdata)
|
||||
if rdata[-1] != '.':
|
||||
rdata += '.' + self.__origin
|
||||
rdata = self.__statedname(rdata, record)
|
||||
|
||||
if rrtype.lower() == 'soa':
|
||||
soa = rdata.split()
|
||||
if len(soa) < 2 or not isname(soa[0]) or not isname(soa[1]):
|
||||
raise MasterFileError("Invalid " + rrtype + ": " + rdata)
|
||||
if soa[0][-1] != '.':
|
||||
soa[0] += '.' + self.__origin
|
||||
if soa[1][-1] != '.':
|
||||
soa[1] += '.' + self.__origin
|
||||
soa[0] = self.__statedname(soa[0], record)
|
||||
soa[1] = self.__statedname(soa[1], record)
|
||||
if not MasterFile.__ttl and not ttl:
|
||||
MasterFile.__ttl = MasterFile.__ttl or parse_ttl(soa[-1])
|
||||
ttl = MasterFile.__ttl
|
||||
|
||||
for index in range(3, len(soa)):
|
||||
if isttl(soa[index]):
|
||||
soa[index] = parse_ttl(soa[index])
|
||||
else :
|
||||
raise MasterFileError("No TTL specified; in soa record!")
|
||||
rdata = ' '.join(soa)
|
||||
|
||||
if not ttl:
|
||||
raise MasterFileError("No TTL specified; zone rejected")
|
||||
|
||||
if rrtype.lower() == 'mx':
|
||||
mx = rdata.split()
|
||||
if len(mx) != 2 or not isname(mx[1]):
|
||||
@ -424,7 +549,7 @@ class MasterFile:
|
||||
if mx[1][-1] != '.':
|
||||
mx[1] += '.' + self.__origin
|
||||
rdata = ' '.join(mx)
|
||||
|
||||
MasterFile.__records_num += 1
|
||||
yield (name, ttl, rrclass, rrtype, rdata)
|
||||
|
||||
#########################################################################
|
||||
@ -436,16 +561,22 @@ class MasterFile:
|
||||
return self.__name
|
||||
old_origin = self.__origin
|
||||
self.__origin = self.__initial_origin
|
||||
cur_value = self.__cur
|
||||
old_location = self.__zonefile.tell()
|
||||
old_verbose = self.__verbose
|
||||
self.__verbose = False
|
||||
self.__zonefile.seek(0)
|
||||
|
||||
for name, ttl, rrclass, rrtype, rdata in self.zonedata():
|
||||
if rrtype.lower() == 'soa':
|
||||
break
|
||||
self.__zonefile.seek(old_location)
|
||||
self.__origin = old_origin
|
||||
self.__cur = cur_value
|
||||
if rrtype.lower() != 'soa':
|
||||
raise MasterFileError("No SOA found")
|
||||
self.__name = name
|
||||
self.__verbose = old_verbose
|
||||
return name
|
||||
|
||||
#########################################################################
|
||||
@ -454,7 +585,8 @@ class MasterFile:
|
||||
def reset(self):
|
||||
self.__zonefile.seek(0)
|
||||
self.__origin = self.__initial_origin
|
||||
self.__ttl = ''
|
||||
MasterFile.__ttl = ''
|
||||
MasterFile.__lastttl = ''
|
||||
|
||||
#########################################################################
|
||||
# main: used for testing; parse a zone file and print out each record
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user