2
0
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:
Jelte Jansen 2010-06-30 13:47:21 +00:00
commit 7cdceb252e
106 changed files with 2518 additions and 998 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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";
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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) {

View File

@ -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

View File

@ -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()

View 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()

View File

@ -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 $*

View File

@ -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

View File

@ -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 =

View File

@ -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()

View 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

View 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()

View File

@ -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()

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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__":

View 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

View 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

View 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

View 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')

View File

@ -0,0 +1,4 @@
a 300 A 10.0.1.1
$ORIGIN foo
b 300 A 10.0.2.2

View 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

View 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

View 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"

View File

@ -0,0 +1,3 @@
a TXT "soa minttl 3"
$ORIGIN foo
b TXT "soa minttl 3"

View File

@ -0,0 +1,3 @@
i TXT "default ttl 1"
$TTL 5
g TXT "default ttl 5"

View 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"

View File

@ -0,0 +1,3 @@
h TXT "inherited ttl 1"
$TTL 6
g TXT "inherited ttl 6"

View File

@ -0,0 +1,3 @@
f TXT "default ttl 3"
$TTL 5
g TXT "default ttl 5"

View 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"

View 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"

View 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"

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1 @@
a 300 A 127.0.0.1

View 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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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)

View 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

View File

@ -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")

View 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()

View File

@ -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": [

View File

@ -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

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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 \

View File

@ -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>

View File

@ -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');

View File

@ -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)

View File

@ -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

View File

@ -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)) {

View File

@ -60,7 +60,7 @@ public:
}
P operator->() const
{
return (it_.operator->());
return (&(operator*()));
}
bool operator==(const RRsetListIterator& other)
{

View File

@ -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

View File

@ -1,4 +1,4 @@
SUBDIRS = datasrc cc config # Util
SUBDIRS = datasrc cc config log # Util
python_PYTHON = __init__.py

View File

@ -2,3 +2,4 @@ import isc.datasrc
import isc.cc
import isc.config
#import isc.dns
import isc.log

View File

@ -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