diff --git a/ChangeLog b/ChangeLog index 06fc0db75c..89e14b49a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,73 @@ - 53. [bug] zhanglikun + 65. [func] shentingting + Added verbose options to exactly what is happening with loadzone. + Added loadzone test suite of different file formats to load. + (Trac #197, #199, #244, #161, #198, #174, #175, svn r2340) + + 64. [func] jerry + Added python logging framework. It is for testing and experimenting + with logging ideas. Currently, it supports three channels(file, + syslog and stderr) and five levels(debug, info, warning, error and + critical). + (Trac #176, svn r2338) + + 63. [func] shane + Added initial support for setuid(), using the "-u" flag. This will + be replaced in the future, but for now provides a reasonable + starting point. + (Trac #180, svn r2330) + + 62. [func] jelte + bin/xfrin: Use the database_file as configured in Auth to transfers + bin/xfrout: Use the database_file as configured in Auth to transfers + + 61. [bug] jelte + bin/auth: Enable b10-auth to be launched in source tree + (i.e. use a zone database file relative to that) + + 60. [build] jinmei + Supported SunStudio C++ compiler. Note: gtest still doesn't work. + (Trac #251, svn r2310) + + 59. [bug] jinmei + lib/datasrc,bin/auth: The authoritative server could return a + SERVFAIL with a partial answer if it finds a data source broken + while looking for an answer. This can happen, for example, if a + zone that doesn't have an NS RR is configured and loaded as a + sqlite3 data source. (Trac #249, r2286) + + 58. [bug] jinmei + Worked around an interaction issue between ASIO and standard C++ + library headers. Without this ASIO didn't work: sometimes the + application crashes, sometimes it blocked in the ASIO module. + (Trac #248, svn r2187, r2190) + + 57. [func] jinmei + lib/datasrc: used a simpler version of Name::split (change 31) for + better readability. No behavior change. (Trac #200, svn r2159) + + 56. [func]* jinmei + lib/dns: renamed the library name to libdns++ to avoid confusion + with the same name of library of BIND 9. + (Trac #190, svn r2153) + + 55. [bug] shane + bin/xfrout: xfrout exception on Ctrl-C now no longer generates + exception for 'Interrupted system call' + (Track #136, svn r2147) + + 54. [bug] zhanglikun + bin/xfrout: Enable b10-xfrout can be launched in source + code tree. + (Trac #224, svn r2103) + + 53. [bug] zhanglikun bin/bindctl: Generate a unique session ID by using socket.gethostname() instead of socket.gethostbyname(), since the latter one could make bindctl stall if its own host name can't be resolved. (Trac #228, svn r2096) - 52. [func] zhanglikun + 52. [func] zhanglikun bin/xfrout: When xfrout is launched, check whether the socket file is being used by one running xfrout process, if it is, exit from python. If the file isn't a socket file @@ -67,6 +129,10 @@ bind10-devel-20100602 released on June 2, 2010 Renamed libauth to libdatasrc. 38. [bug] zhanglikun + Send command 'shutdown' to Xfrin and Xfrout when boss receive SIGINT. + Remove unused socket file when Xfrout process exits. Make sure Xfrout + exit by itself when it receives SIGINT, instead of being killed by the + signal SIGTERM or SIGKILL sent from boss. (Trac #135, #151, #134, svn r1797) 37. [build] jinmei @@ -127,7 +193,7 @@ bind10-devel-20100421 released on April 21, 2010 24. [func] Support case-sensitive name compression in MessageRenderer. - (svn r1704) + (Trac #142, svn r1704) 23. [func] Support a simple name with possible compression. (svn r1701) diff --git a/configure.ac b/configure.ac index d0b899aec0..05e02d2766 100644 --- a/configure.ac +++ b/configure.ac @@ -9,11 +9,23 @@ AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CXX -AC_PROG_CC AC_PROG_LIBTOOL # Use C++ language -AC_LANG_CPLUSPLUS +AC_LANG([C++]) + +# Identify the compiler: this check must be after AC_PROG_CXX and AC_LANG. +AM_CONDITIONAL(USE_GXX, test "X${GXX}" = "Xyes") +AC_CHECK_DECL([__SUNPRO_CC], [SUNCXX="yes"], [SUNCXX="no"]) + +# OS dependent compiler flags +case "$host" in +*-solaris*) + # Solaris requires special definitions to get some standard libraries + # (e.g. getopt(3)) available with common used header files. + CPPFLAGS="$CPPFLAGS -D_XPG4_2 -D__EXTENSIONS__" + ;; +esac m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1]) AC_ARG_WITH([pythonpath], @@ -93,8 +105,8 @@ AC_SUBST(PYTHON_LIB) # TODO: check for _sqlite3.py module -# -# B10_CXXFLAGS is the default C++ compiler flags. This will (and should) be +# Compiler dependent settings: define some mandatory CXXFLAGS here. +# We also use a separate variable B10_CXXFLAGS. This will (and should) be # used as the default value for each specifc AM_CXXFLAGS: # AM_CXXFLAGS = $(B10_CXXFLAGS) # AM_CXXFLAGS += ... # add module specific flags @@ -103,17 +115,24 @@ AC_SUBST(PYTHON_LIB) # gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot # specify the default warning flags in CXXFLAGS and let specific modules # "override" the default. -# -B10_CXXFLAGS= -if test "X$GCC" = "Xyes"; then -B10_CXXFLAGS="-g -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" +CXXFLAGS=-g +werror_ok=0 + +# SunStudio compiler requires special compiler options for boost +# (http://blogs.sun.com/sga/entry/boost_mini_howto) +if test "$SUNCXX" = "yes"; then +CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic" +fi + +# gcc specific settings: +if test "X$GXX" = "Xyes"; then +B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" UNUSED_PARAM_ATTRIBUTE='__attribute__((unused))' # Certain versions of gcc (g++) have a bug that incorrectly warns about # the use of anonymous name spaces even if they're closed in a single # translation unit. For these versions we have to disable -Werror. -werror_ok=0 CXXFLAGS_SAVED="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror" AC_MSG_CHECKING(for in-TU anonymous namespace breakage) @@ -124,13 +143,13 @@ namespace isc {class Bar {Foo foo_;};} ],, B10_CXXFLAGS="$B10_CXXFLAGS -Werror"], [AC_MSG_RESULT(yes)]) CXXFLAGS="$CXXFLAGS_SAVED" -fi dnl GCC = yes +fi dnl GXX = yes AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) AC_DEFINE_UNQUOTED(UNUSED_PARAM, $UNUSED_PARAM_ATTRIBUTE, Define to compiler keyword indicating a function argument is intentionally unused) # produce PIC unless we disable shared libraries. need this for python bindings. -if test $enable_shared != "no" -a "X$GCC" = "Xyes"; then +if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then B10_CXXFLAGS="$B10_CXXFLAGS -fPIC" fi @@ -270,6 +289,11 @@ AC_SUBST(GTEST_INCLUDES) AC_SUBST(GTEST_LDFLAGS) AC_SUBST(GTEST_LDADD) +dnl check for pkg-config itself so we don't try the m4 macro without pkg-config +AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes, no) +if test "x$HAVE_PKG_CONFIG" = "xno" ; then + AC_MSG_ERROR(Please install pkg-config) +fi PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.3.9, enable_features="$enable_features SQLite3") # I can't get some of the #include 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 diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index 6414dcac29..43fc5626d5 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -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) diff --git a/src/bin/auth/asio_link.cc b/src/bin/auth/asio_link.cc index 332c92d519..9a2d7beffb 100644 --- a/src/bin/auth/asio_link.cc +++ b/src/bin/auth/asio_link.cc @@ -16,6 +16,7 @@ #include +#include // for some IPC/network system calls #include #include @@ -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"; } diff --git a/src/bin/auth/asio_link.h b/src/bin/auth/asio_link.h index b5c9153f83..dcb7d8f94f 100644 --- a/src/bin/auth/asio_link.h +++ b/src/bin/auth/asio_link.h @@ -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(); diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index dee60ef3b2..026196afdb 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -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); } diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 2e7073699f..bfd97253ee 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -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) { diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index ed9deb552f..5ce117679d 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -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 diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index 040f14bce1..823c2304bd 100644 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -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() diff --git a/src/bin/bind10/tests/args_test.py b/src/bin/bind10/tests/args_test.py new file mode 100644 index 0000000000..9e38ccd867 --- /dev/null +++ b/src/bin/bind10/tests/args_test.py @@ -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() diff --git a/src/bin/bind10/tests/bind10_test.in b/src/bin/bind10/tests/bind10_test.in index d448bf5294..cbd74524a1 100755 --- a/src/bin/bind10/tests/bind10_test.in +++ b/src/bin/bind10/tests/bind10_test.in @@ -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 $* diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py index 138024f05f..7c87d46d6b 100644 --- a/src/bin/bindctl/bindcmd.py +++ b/src/bin/bindctl/bindcmd.py @@ -87,7 +87,7 @@ class BindCmdInterpreter(Cmd): '''Generate one session id for the connection. ''' rand = os.urandom(16) now = time.time() - session_id = sha1(("%s%s%s" %(rand, now, + session_id = sha1(("%s%s%s" %(rand, now, socket.gethostname())).encode()) digest = session_id.hexdigest() return digest diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am index 695d371ff8..ef9523d263 100644 --- a/src/bin/cfgmgr/Makefile.am +++ b/src/bin/cfgmgr/Makefile.am @@ -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 = diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in index 563bbcdf7e..0956203c1d 100644 --- a/src/bin/cfgmgr/b10-cfgmgr.py.in +++ b/src/bin/cfgmgr/b10-cfgmgr.py.in @@ -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() diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am new file mode 100644 index 0000000000..0a504bb6b2 --- /dev/null +++ b/src/bin/cfgmgr/tests/Makefile.am @@ -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 diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in new file mode 100644 index 0000000000..05339a15b3 --- /dev/null +++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in @@ -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() + diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in index 4e5a73351d..1ca0c37b97 100644 --- a/src/bin/cmdctl/cmdctl.py.in +++ b/src/bin/cmdctl/cmdctl.py.in @@ -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() diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am index 39e4b8928b..ac703b42c9 100644 --- a/src/bin/host/Makefile.am +++ b/src/bin/host/Makefile.am @@ -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 diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc index 35eebc7de9..07dd659b72 100644 --- a/src/bin/host/host.cc +++ b/src/bin/host/host.cc @@ -19,6 +19,8 @@ #include // for gettimeofday #include // networking functions and definitions on FreeBSD +#include + #include #include diff --git a/src/bin/loadzone/Makefile.am b/src/bin/loadzone/Makefile.am index 1a29ef25c7..777ebb25d9 100644 --- a/src/bin/loadzone/Makefile.am +++ b/src/bin/loadzone/Makefile.am @@ -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 diff --git a/src/bin/loadzone/b10-loadzone.py.in b/src/bin/loadzone/b10-loadzone.py.in index 98525985d6..6c0704c514 100644 --- a/src/bin/loadzone/b10-loadzone.py.in +++ b/src/bin/loadzone/b10-loadzone.py.in @@ -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__": diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am new file mode 100644 index 0000000000..213d089191 --- /dev/null +++ b/src/bin/loadzone/tests/correct/Makefile.am @@ -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 diff --git a/src/bin/loadzone/tests/correct/correct_test.sh.in b/src/bin/loadzone/tests/correct/correct_test.sh.in new file mode 100644 index 0000000000..509d8e5f13 --- /dev/null +++ b/src/bin/loadzone/tests/correct/correct_test.sh.in @@ -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 diff --git a/src/bin/loadzone/tests/correct/example.db b/src/bin/loadzone/tests/correct/example.db new file mode 100644 index 0000000000..fe012cf8ec --- /dev/null +++ b/src/bin/loadzone/tests/correct/example.db @@ -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 diff --git a/src/bin/loadzone/tests/correct/get_zonedatas.py b/src/bin/loadzone/tests/correct/get_zonedatas.py new file mode 100644 index 0000000000..faa563449c --- /dev/null +++ b/src/bin/loadzone/tests/correct/get_zonedatas.py @@ -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') diff --git a/src/bin/loadzone/tests/correct/inclsub.db b/src/bin/loadzone/tests/correct/inclsub.db new file mode 100644 index 0000000000..242e5ecb8b --- /dev/null +++ b/src/bin/loadzone/tests/correct/inclsub.db @@ -0,0 +1,4 @@ +a 300 A 10.0.1.1 +$ORIGIN foo +b 300 A 10.0.2.2 + diff --git a/src/bin/loadzone/tests/correct/include.db b/src/bin/loadzone/tests/correct/include.db new file mode 100644 index 0000000000..f60a24007b --- /dev/null +++ b/src/bin/loadzone/tests/correct/include.db @@ -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 diff --git a/src/bin/loadzone/tests/correct/known.test.out b/src/bin/loadzone/tests/correct/known.test.out new file mode 100644 index 0000000000..8e2fe3cf3c --- /dev/null +++ b/src/bin/loadzone/tests/correct/known.test.out @@ -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 diff --git a/src/bin/loadzone/tests/correct/mix1.db b/src/bin/loadzone/tests/correct/mix1.db new file mode 100644 index 0000000000..da562ffc46 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix1.db @@ -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" diff --git a/src/bin/loadzone/tests/correct/mix1sub1.db b/src/bin/loadzone/tests/correct/mix1sub1.db new file mode 100644 index 0000000000..9174a39986 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix1sub1.db @@ -0,0 +1,3 @@ +a TXT "soa minttl 3" +$ORIGIN foo +b TXT "soa minttl 3" diff --git a/src/bin/loadzone/tests/correct/mix1sub2.db b/src/bin/loadzone/tests/correct/mix1sub2.db new file mode 100644 index 0000000000..e704d7f805 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix1sub2.db @@ -0,0 +1,3 @@ +i TXT "default ttl 1" +$TTL 5 +g TXT "default ttl 5" diff --git a/src/bin/loadzone/tests/correct/mix2.db b/src/bin/loadzone/tests/correct/mix2.db new file mode 100644 index 0000000000..2c8153dc65 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix2.db @@ -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" diff --git a/src/bin/loadzone/tests/correct/mix2sub1.txt b/src/bin/loadzone/tests/correct/mix2sub1.txt new file mode 100644 index 0000000000..1db4411bb5 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix2sub1.txt @@ -0,0 +1,3 @@ +h TXT "inherited ttl 1" +$TTL 6 +g TXT "inherited ttl 6" diff --git a/src/bin/loadzone/tests/correct/mix2sub2.txt b/src/bin/loadzone/tests/correct/mix2sub2.txt new file mode 100644 index 0000000000..96d53c1283 --- /dev/null +++ b/src/bin/loadzone/tests/correct/mix2sub2.txt @@ -0,0 +1,3 @@ +f TXT "default ttl 3" +$TTL 5 +g TXT "default ttl 5" diff --git a/src/bin/loadzone/tests/correct/ttl1.db b/src/bin/loadzone/tests/correct/ttl1.db new file mode 100644 index 0000000000..aa6e2bb5d8 --- /dev/null +++ b/src/bin/loadzone/tests/correct/ttl1.db @@ -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" diff --git a/src/bin/loadzone/tests/correct/ttl2.db b/src/bin/loadzone/tests/correct/ttl2.db new file mode 100644 index 0000000000..f7f6eee3b1 --- /dev/null +++ b/src/bin/loadzone/tests/correct/ttl2.db @@ -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" diff --git a/src/bin/loadzone/tests/correct/ttlext.db b/src/bin/loadzone/tests/correct/ttlext.db new file mode 100644 index 0000000000..8ad6103a0c --- /dev/null +++ b/src/bin/loadzone/tests/correct/ttlext.db @@ -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" diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am new file mode 100644 index 0000000000..427260d1c0 --- /dev/null +++ b/src/bin/loadzone/tests/error/Makefile.am @@ -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 diff --git a/src/bin/loadzone/tests/error/error.known b/src/bin/loadzone/tests/error/error.known new file mode 100644 index 0000000000..5961064278 --- /dev/null +++ b/src/bin/loadzone/tests/error/error.known @@ -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 diff --git a/src/bin/loadzone/tests/error/error_test.sh.in b/src/bin/loadzone/tests/error/error_test.sh.in new file mode 100644 index 0000000000..d1d6bd1837 --- /dev/null +++ b/src/bin/loadzone/tests/error/error_test.sh.in @@ -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 diff --git a/src/bin/loadzone/tests/error/formerr1.db b/src/bin/loadzone/tests/error/formerr1.db new file mode 100644 index 0000000000..9bab49fec8 --- /dev/null +++ b/src/bin/loadzone/tests/error/formerr1.db @@ -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 diff --git a/src/bin/loadzone/tests/error/formerr2.db b/src/bin/loadzone/tests/error/formerr2.db new file mode 100644 index 0000000000..3d7dd485a1 --- /dev/null +++ b/src/bin/loadzone/tests/error/formerr2.db @@ -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 diff --git a/src/bin/loadzone/tests/error/formerr3.db b/src/bin/loadzone/tests/error/formerr3.db new file mode 100644 index 0000000000..c1c39755c3 --- /dev/null +++ b/src/bin/loadzone/tests/error/formerr3.db @@ -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 diff --git a/src/bin/loadzone/tests/error/formerr4.db b/src/bin/loadzone/tests/error/formerr4.db new file mode 100644 index 0000000000..d37515ff8c --- /dev/null +++ b/src/bin/loadzone/tests/error/formerr4.db @@ -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 diff --git a/src/bin/loadzone/tests/error/formerr5.db b/src/bin/loadzone/tests/error/formerr5.db new file mode 100644 index 0000000000..fa5983fa22 --- /dev/null +++ b/src/bin/loadzone/tests/error/formerr5.db @@ -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 diff --git a/src/bin/loadzone/tests/error/include.txt b/src/bin/loadzone/tests/error/include.txt new file mode 100644 index 0000000000..9b4c57cbbc --- /dev/null +++ b/src/bin/loadzone/tests/error/include.txt @@ -0,0 +1 @@ +a 300 A 127.0.0.1 diff --git a/src/bin/loadzone/tests/error/keyerror1.db b/src/bin/loadzone/tests/error/keyerror1.db new file mode 100644 index 0000000000..738436280c --- /dev/null +++ b/src/bin/loadzone/tests/error/keyerror1.db @@ -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 diff --git a/src/bin/loadzone/tests/error/keyerror2.db b/src/bin/loadzone/tests/error/keyerror2.db new file mode 100644 index 0000000000..5c97e4ef30 --- /dev/null +++ b/src/bin/loadzone/tests/error/keyerror2.db @@ -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 diff --git a/src/bin/loadzone/tests/error/keyerror3.db b/src/bin/loadzone/tests/error/keyerror3.db new file mode 100644 index 0000000000..eebb0aa385 --- /dev/null +++ b/src/bin/loadzone/tests/error/keyerror3.db @@ -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 diff --git a/src/bin/loadzone/tests/error/originerr1.db b/src/bin/loadzone/tests/error/originerr1.db new file mode 100644 index 0000000000..fc20edc27c --- /dev/null +++ b/src/bin/loadzone/tests/error/originerr1.db @@ -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 diff --git a/src/bin/loadzone/tests/error/originerr2.db b/src/bin/loadzone/tests/error/originerr2.db new file mode 100644 index 0000000000..2cb90eb222 --- /dev/null +++ b/src/bin/loadzone/tests/error/originerr2.db @@ -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 diff --git a/src/bin/loadzone/testdata/Kexample.com.+005+04456.key b/src/bin/loadzone/tests/normal/Kexample.com.+005+04456.key similarity index 100% rename from src/bin/loadzone/testdata/Kexample.com.+005+04456.key rename to src/bin/loadzone/tests/normal/Kexample.com.+005+04456.key diff --git a/src/bin/loadzone/testdata/Kexample.com.+005+04456.private b/src/bin/loadzone/tests/normal/Kexample.com.+005+04456.private similarity index 100% rename from src/bin/loadzone/testdata/Kexample.com.+005+04456.private rename to src/bin/loadzone/tests/normal/Kexample.com.+005+04456.private diff --git a/src/bin/loadzone/testdata/Kexample.com.+005+33495.key b/src/bin/loadzone/tests/normal/Kexample.com.+005+33495.key similarity index 100% rename from src/bin/loadzone/testdata/Kexample.com.+005+33495.key rename to src/bin/loadzone/tests/normal/Kexample.com.+005+33495.key diff --git a/src/bin/loadzone/testdata/Kexample.com.+005+33495.private b/src/bin/loadzone/tests/normal/Kexample.com.+005+33495.private similarity index 100% rename from src/bin/loadzone/testdata/Kexample.com.+005+33495.private rename to src/bin/loadzone/tests/normal/Kexample.com.+005+33495.private diff --git a/src/bin/loadzone/testdata/Ksql1.example.com.+005+12447.key b/src/bin/loadzone/tests/normal/Ksql1.example.com.+005+12447.key similarity index 100% rename from src/bin/loadzone/testdata/Ksql1.example.com.+005+12447.key rename to src/bin/loadzone/tests/normal/Ksql1.example.com.+005+12447.key diff --git a/src/bin/loadzone/testdata/Ksql1.example.com.+005+12447.private b/src/bin/loadzone/tests/normal/Ksql1.example.com.+005+12447.private similarity index 100% rename from src/bin/loadzone/testdata/Ksql1.example.com.+005+12447.private rename to src/bin/loadzone/tests/normal/Ksql1.example.com.+005+12447.private diff --git a/src/bin/loadzone/testdata/Ksql1.example.com.+005+33313.key b/src/bin/loadzone/tests/normal/Ksql1.example.com.+005+33313.key similarity index 100% rename from src/bin/loadzone/testdata/Ksql1.example.com.+005+33313.key rename to src/bin/loadzone/tests/normal/Ksql1.example.com.+005+33313.key diff --git a/src/bin/loadzone/testdata/Ksql1.example.com.+005+33313.private b/src/bin/loadzone/tests/normal/Ksql1.example.com.+005+33313.private similarity index 100% rename from src/bin/loadzone/testdata/Ksql1.example.com.+005+33313.private rename to src/bin/loadzone/tests/normal/Ksql1.example.com.+005+33313.private diff --git a/src/bin/loadzone/testdata/Ksql2.example.com.+005+38482.key b/src/bin/loadzone/tests/normal/Ksql2.example.com.+005+38482.key similarity index 100% rename from src/bin/loadzone/testdata/Ksql2.example.com.+005+38482.key rename to src/bin/loadzone/tests/normal/Ksql2.example.com.+005+38482.key diff --git a/src/bin/loadzone/testdata/Ksql2.example.com.+005+38482.private b/src/bin/loadzone/tests/normal/Ksql2.example.com.+005+38482.private similarity index 100% rename from src/bin/loadzone/testdata/Ksql2.example.com.+005+38482.private rename to src/bin/loadzone/tests/normal/Ksql2.example.com.+005+38482.private diff --git a/src/bin/loadzone/testdata/Ksql2.example.com.+005+63192.key b/src/bin/loadzone/tests/normal/Ksql2.example.com.+005+63192.key similarity index 100% rename from src/bin/loadzone/testdata/Ksql2.example.com.+005+63192.key rename to src/bin/loadzone/tests/normal/Ksql2.example.com.+005+63192.key diff --git a/src/bin/loadzone/testdata/Ksql2.example.com.+005+63192.private b/src/bin/loadzone/tests/normal/Ksql2.example.com.+005+63192.private similarity index 100% rename from src/bin/loadzone/testdata/Ksql2.example.com.+005+63192.private rename to src/bin/loadzone/tests/normal/Ksql2.example.com.+005+63192.private diff --git a/src/bin/loadzone/testdata/README b/src/bin/loadzone/tests/normal/README similarity index 100% rename from src/bin/loadzone/testdata/README rename to src/bin/loadzone/tests/normal/README diff --git a/src/bin/loadzone/testdata/dsset-subzone.example.com. b/src/bin/loadzone/tests/normal/dsset-subzone.example.com similarity index 100% rename from src/bin/loadzone/testdata/dsset-subzone.example.com. rename to src/bin/loadzone/tests/normal/dsset-subzone.example.com diff --git a/src/bin/loadzone/testdata/example.com b/src/bin/loadzone/tests/normal/example.com similarity index 100% rename from src/bin/loadzone/testdata/example.com rename to src/bin/loadzone/tests/normal/example.com diff --git a/src/bin/loadzone/testdata/example.com.signed b/src/bin/loadzone/tests/normal/example.com.signed similarity index 100% rename from src/bin/loadzone/testdata/example.com.signed rename to src/bin/loadzone/tests/normal/example.com.signed diff --git a/src/bin/loadzone/testdata/sql1.example.com b/src/bin/loadzone/tests/normal/sql1.example.com similarity index 100% rename from src/bin/loadzone/testdata/sql1.example.com rename to src/bin/loadzone/tests/normal/sql1.example.com diff --git a/src/bin/loadzone/testdata/sql1.example.com.signed b/src/bin/loadzone/tests/normal/sql1.example.com.signed similarity index 100% rename from src/bin/loadzone/testdata/sql1.example.com.signed rename to src/bin/loadzone/tests/normal/sql1.example.com.signed diff --git a/src/bin/loadzone/testdata/sql2.example.com b/src/bin/loadzone/tests/normal/sql2.example.com similarity index 100% rename from src/bin/loadzone/testdata/sql2.example.com rename to src/bin/loadzone/tests/normal/sql2.example.com diff --git a/src/bin/loadzone/testdata/sql2.example.com.signed b/src/bin/loadzone/tests/normal/sql2.example.com.signed similarity index 100% rename from src/bin/loadzone/testdata/sql2.example.com.signed rename to src/bin/loadzone/tests/normal/sql2.example.com.signed diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am index 1c902c587d..8395f364e9 100644 --- a/src/bin/xfrin/tests/Makefile.am +++ b/src/bin/xfrin/tests/Makefile.am @@ -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 diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index 4e2fccc0b6..2a4f48397f 100644 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -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) diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index a67ba14f7d..c1c613ea89 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -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 diff --git a/src/bin/xfrout/tests/xfrout_test.py b/src/bin/xfrout/tests/xfrout_test.py index 725b31a5fe..ac25ff0bce 100644 --- a/src/bin/xfrout/tests/xfrout_test.py +++ b/src/bin/xfrout/tests/xfrout_test.py @@ -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") diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in index 4c91970a12..b236cd834c 100644 --- a/src/bin/xfrout/xfrout.py.in +++ b/src/bin/xfrout/xfrout.py.in @@ -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() diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in index 495d3fd2a0..c1a5303107 100644 --- a/src/bin/xfrout/xfrout.spec.pre.in +++ b/src/bin/xfrout/xfrout.spec.pre.in @@ -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": [ diff --git a/src/lib/cc/Makefile.am b/src/lib/cc/Makefile.am index fe61cd25f2..320ba9c125 100644 --- a/src/lib/cc/Makefile.am +++ b/src/lib/cc/Makefile.am @@ -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 diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc index 66bc2717ab..3d5d42b75d 100644 --- a/src/lib/cc/session.cc +++ b/src/lib/cc/session.cc @@ -19,6 +19,17 @@ #include +// 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 // for some IPC/network system calls +#include +#include +#include + #include #include #include @@ -29,10 +40,6 @@ #include #include -#include -#include -#include - #include #include "data.h" diff --git a/src/lib/cc/session_unittests.cc b/src/lib/cc/session_unittests.cc index e67363efda..7bccbb1d56 100644 --- a/src/lib/cc/session_unittests.cc +++ b/src/lib/cc/session_unittests.cc @@ -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 + #include #include -#include #include using namespace isc::cc; diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am index 436d962937..ac996e6b4a 100644 --- a/src/lib/config/tests/Makefile.am +++ b/src/lib/config/tests/Makefile.am @@ -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 diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc index 61a11b62f8..b8240e9775 100644 --- a/src/lib/datasrc/data_source.cc +++ b/src/lib/datasrc/data_source.cc @@ -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()); } } diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index 4b68590743..6d5edeb1df 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -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 diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index 382e4885ab..5fed72f84d 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -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 diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc index bee83a18e1..b6385ca4ed 100644 --- a/src/lib/datasrc/tests/datasrc_unittest.cc +++ b/src/lib/datasrc/tests/datasrc_unittest.cc @@ -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); +} } diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc index 39cb40bd54..d92a2da060 100644 --- a/src/lib/datasrc/tests/test_datasrc.cc +++ b/src/lib/datasrc/tests/test_datasrc.cc @@ -18,6 +18,8 @@ #include +#include + #include #include "test_datasrc.h" @@ -45,63 +47,258 @@ namespace isc { namespace datasrc { namespace { -const Name example("example.com"); -const Name sql1("sql1.example.com"); -const Name www_sql1("www.sql1.example.com"); -const Name www("www.example.com"); -const Name foo("foo.example.com"); -const Name dns01("dns01.example.com"); -const Name dns02("dns02.example.com"); -const Name dns03("dns03.example.com"); -const Name cnameint("cname-int.example.com"); -const Name cnameext("cname-ext.example.com"); -const Name dname("dname.example.com"); -const Name wild("*.wild.example.com"); -const Name wild2("*.wild2.example.com"); -const Name wild3("*.wild3.example.com"); -const Name subzone("subzone.example.com"); -const Name loop1("loop1.example.com"); -const Name loop2("loop2.example.com"); -RRsetPtr example_ns; -RRsetPtr example_soa; -RRsetPtr example_nsec; -RRsetPtr www_a; -RRsetPtr www_nsec; -RRsetPtr foo_cname; -RRsetPtr foo_nsec; -RRsetPtr cnameint_cname; -RRsetPtr cnameint_nsec; -RRsetPtr cnameext_cname; -RRsetPtr cnameext_nsec; -RRsetPtr dns01_a; -RRsetPtr dns01_nsec; -RRsetPtr dns02_a; -RRsetPtr dns02_nsec; -RRsetPtr dns03_a; -RRsetPtr dns03_nsec; -RRsetPtr wild_a; -RRsetPtr wild_nsec; -RRsetPtr wild2_cname; -RRsetPtr wild2_nsec; -RRsetPtr wild3_cname; -RRsetPtr wild3_nsec; -RRsetPtr dname_dname; -RRsetPtr dname_nsec; -RRsetPtr sql1_ns; -RRsetPtr sql1_soa; -RRsetPtr sql1_nsec; -RRsetPtr sql1_ds; -RRsetPtr sql1_ds_nsec; -RRsetPtr www_sql1_a; -RRsetPtr www_sql1_nsec; -RRsetPtr subzone_ns; -RRsetPtr subzone_nsec; -RRsetPtr subzone_glue1; -RRsetPtr subzone_glue2; -RRsetPtr subzone_ds; -RRsetPtr loop1_cname; -RRsetPtr loop2_cname; +// This is a mock data source for testing. It can contain multiple zones. +// The content of each zone should be configured in the form of RRData{}. +// Each RRData element is a tuple of char strings, representing +// "name, RRtype, RDATA". For simplicity we use the same single TTL for +// RRs (TEST_TTL) defined below. +// Multiple RRs of the same pair of (name, RRtype) can be defined, but +// they must not be interleaved with other types of pair. For example, +// This is okay: +// {"example.com", "AAAA", "2001:db8::1"}, +// {"example.com", "AAAA", "2001:db8::2"}, +// ... +// but this is invalid: +// {"example.com", "AAAA", "2001:db8::1"}, +// {"example.com", "A", "192.0.2.1"}, +// {"example.com", "AAAA", "2001:db8::2"}, +// ... +// If an RRset is associated with an RRSIG, the RRSIG must immediately follow +// the RRset to be signed. Currently, only one (or zero) RRSIG can be +// specified per RRset. +// +// Names are sorted internally, and don't have to be sorted in the data. +// +// A zone is defined in the form of ZoneData{}, which contains: +// zone name (character string) +// RRclass (character string) +// A pointer to in-zone RRs in the form of RRData{} +// A pointer to glue RRs in the form of RRData{} +// Glues can be omitted, in which case a convenient constant "empty_records" +// can be specified. + +// For simplicity we use the same single TTL for all test RRs. +const uint32_t TEST_TTL = 3600; + +struct RRData { + const char* const name; + const char* const rrtype; + const char* const rdata; +}; + +struct ZoneData { + const char* const zone_name; + const char* const rrclass; + const struct RRData* records; + const struct RRData* glue_records; +}; + +// +// zone data for example.com +// +const struct RRData example_com_records[] = { + // example.com + {"example.com", "NS", "dns01.example.com"}, + {"example.com", "NS", "dns02.example.com"}, + {"example.com", "NS", "dns03.example.com"}, + {"example.com", "RRSIG", "NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="}, + {"example.com", "SOA", "master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"}, + {"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com. KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="}, + {"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"}, + {"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="}, + + // dns01.example.com + {"dns01.example.com", "A", "192.0.2.1"}, + {"dns01.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="}, + {"dns01.example.com", "NSEC", "dns02.example.com. A RRSIG NSEC"}, + {"dns01.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="}, + + // dns02.example.com + {"dns02.example.com", "A", "192.0.2.2"}, + {"dns02.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="}, + {"dns02.example.com", "NSEC", "dns03.example.com. A RRSIG NSEC"}, + {"dns02.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="}, + + // dns03.example.com + {"dns03.example.com", "A", "192.0.2.3"}, + {"dns03.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="}, + {"dns03.example.com", "NSEC", "foo.example.com. A RRSIG NSEC"}, + {"dns03.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="}, + + // www.example.com + {"www.example.com", "A", "192.0.2.1"}, + {"www.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="}, + {"www.example.com", "NSEC", "example.com. A RRSIG NSEC"}, + {"www.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="}, + + // *.wild.example.com + {"*.wild.example.com", "A", "192.0.2.2"}, + {"*.wild.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="}, + {"*.wild.example.com", "NSEC", "*.wild2.example.com. A RRSIG NSEC"}, + {"*.wild.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="}, + + // *.wild2.example.com + {"*.wild2.example.com", "CNAME", "www.example.com"}, + {"*.wild2.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="}, + {"*.wild2.example.com", "NSEC", "*.wild3.example.com. CNAME RRSIG NSEC"}, + {"*.wild2.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="}, + + // *.wild3.example.com -- a wildcard record with a lame CNAME + {"*.wild3.example.com", "CNAME", "spork.example.com"}, + {"*.wild3.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="}, + {"*.wild3.example.com", "NSEC", "www.example.com. CNAME RRSIG NSEC"}, + {"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="}, + + // foo.example.com + {"foo.example.com", "CNAME", "cnametest.flame.org"}, + {"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="}, + {"foo.example.com", "NSEC", "mail.example.com. CNAME RRSIG NSEC"}, + {"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="}, + + // cname-int.example.com + {"cname-int.example.com", "CNAME", "www.example.com."}, + {"cname-int.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="}, + {"cname-int.example.com", "NSEC", "dname.example.com. CNAME RRSIG NSEC"}, + {"cname-int.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="}, + + // cname-ext.example.com + {"cname-ext.example.com", "CNAME", "www.sql1.example.com"}, + {"cname-ext.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="}, + {"cname-ext.example.com", "NSEC", "cname-int.example.com. CNAME RRSIG NSEC"}, + {"cname-ext.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="}, + + // dname.example.com + {"dname.example.com", "DNAME", "sql1.example.com."}, + {"dname.example.com", "RRSIG", "DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="}, + {"dname.example.com", "NSEC", "dns01.example.com. DNAME RRSIG NSEC"}, + {"dname.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="}, + + // subzone.example.com + {"subzone.example.com", "NS", "ns1.subzone.example.com"}, + {"subzone.example.com", "NS", "ns2.subzone.example.com"}, + {"subzone.example.com", "NSEC", "*.wild.example.com. NS DS RRSIG NSEC"}, + {"subzone.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="}, + {"subzone.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"}, + {"subzone.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"}, + {"subzone.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="}, + + // subset of child zone: sql1 + {"sql1.example.com", "NS", "dns01.example.com"}, + {"sql1.example.com", "NS", "dns02.example.com"}, + {"sql1.example.com", "NS", "dns03.example.com"}, + + {"sql1.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"}, + {"sql1.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"}, + {"sql1.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="}, + {"sql1.example.com", "NSEC", "subzone.example.com. NS DS RRSIG NSEC"}, + {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="}, + + {NULL, NULL, NULL} +}; +const struct RRData example_com_glue_records[] = { + {"ns1.subzone.example.com", "A", "192.0.2.1"}, + {"ns2.subzone.example.com", "A", "192.0.2.2"}, + {NULL, NULL, NULL} +}; + +// +// zone data for sql1.example.com +// +const struct RRData sql1_example_com_records[] = { + {"sql1.example.com", "NS", "dns01.example.com"}, + {"sql1.example.com", "NS", "dns02.example.com"}, + {"sql1.example.com", "NS", "dns03.example.com"}, + {"sql1.example.com", "RRSIG", "NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="}, + {"sql1.example.com", "SOA", "master.example.com. admin.example.com. 678 3600 1800 2419200 7200"}, + {"sql1.example.com", "RRSIG", "SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="}, + {"sql1.example.com", "NSEC", "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"}, + {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="}, + + // www.sql1.example.com + {"www.sql1.example.com", "A", "192.0.2.2"}, + {"www.sql1.example.com", "RRSIG", "A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="}, + {"www.sql1.example.com", "NSEC", "sql1.example.com. A RRSIG NSEC"}, + {"www.sql1.example.com", "RRSIG", "NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="}, + + {NULL, NULL, NULL} +}; + +// +// zone data for loop.example +// +const struct RRData loop_example_records[] = { + {"loop.example", "SOA", "master.loop.example admin.loop.example. " + "1234 3600 1800 2419200 7200"}, + {"loop.example", "NS", "ns.loop.example"}, + {"one.loop.example", "CNAME", "two.loop.example"}, + {"two.loop.example", "CNAME", "one.loop.example"}, + {NULL, NULL, NULL} +}; + +// +// zone data for nons.example +// +const struct RRData nons_example_records[] = { + {"nons.example", "SOA", "master.nons.example admin.nons.example. " + "1234 3600 1800 2419200 7200"}, + {"www.nons.example", "A", "192.0.2.1"}, + {"ns.nons.example", "A", "192.0.2.2"}, + {NULL, NULL, NULL} +}; + +// +// zone data for nons-dname.example +// +const struct RRData nonsdname_example_records[] = { + {"nons-dname.example", "SOA", "master.nons-dname.example " + "admin.nons-dname.example. 1234 3600 1800 2419200 7200"}, + {"nons-dname.example", "DNAME", "example.org"}, + {"www.nons-dname.example", "A", "192.0.2.1"}, + {"ns.nons-dname.example", "A", "192.0.2.2"}, + {NULL, NULL, NULL} +}; + +// +// zone data for nosoa.example +// +const struct RRData nosoa_example_records[] = { + {"nosoa.example", "NS", "ns.nosoa.example"}, + {"www.nosoa.example", "A", "192.0.2.1"}, + {"ns.nosoa.example", "A", "192.0.2.2"}, + {NULL, NULL, NULL} +}; + +// +// empty data set, for convenience. +// +const struct RRData empty_records[] = { + {NULL, NULL, NULL} +}; + +// +// test zones +// +const struct ZoneData zone_data[] = { + { "example.com", "IN", example_com_records, example_com_glue_records }, + { "sql1.example.com", "IN", sql1_example_com_records, empty_records }, + { "loop.example", "IN", loop_example_records, empty_records }, + { "nons.example", "IN", nons_example_records, empty_records }, + { "nons-dname.example", "IN", nonsdname_example_records, empty_records }, + { "nosoa.example", "IN", nosoa_example_records, empty_records } +}; +const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]); + +struct Zone { + Zone(const char* const name, const char* const class_txt) : + zone_name(Name(name)), rrclass(class_txt) + {} + Name zone_name; + RRClass rrclass; + vector names; + vector rrsets; +}; +vector zones; } DataSrc::Result @@ -110,359 +307,54 @@ TestDataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM) return init(); } +void +buildZone(Zone& zone, const RRData* records, const bool is_glue) { + RRsetPtr prev_rrset; + for (int i = 0; records[i].name != NULL; ++i) { + Name name(records[i].name); + RRType rrtype(records[i].rrtype); + RRsetPtr rrset; + bool new_name = false; + + if (!prev_rrset || prev_rrset->getName() != name) { + if (!is_glue) { + zone.names.push_back(name); + } + new_name = true; + } + + if (new_name || prev_rrset->getType() != rrtype) { + rrset = RRsetPtr(new RRset(name, zone.rrclass, rrtype, + RRTTL(TEST_TTL))); + if (rrtype != RRType::RRSIG()) { + zone.rrsets.push_back(rrset); + } + } else { + rrset = prev_rrset; + } + rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata)); + if (rrtype == RRType::RRSIG()) { + prev_rrset->addRRsig(rrset); + } + prev_rrset = rrset; + } +} + DataSrc::Result TestDataSrc::init() { if (initialized) { return (SUCCESS); } - RRset* rp; - RRsetPtr rrsig; - - // example.com - example_ns = RRsetPtr(new RRset(example, RRClass::IN(), - RRType::NS(), RRTTL(3600))); - example_ns->addRdata(generic::NS(dns01)); - example_ns->addRdata(generic::NS(dns02)); - example_ns->addRdata(generic::NS(dns03)); - - rp = new RRset(example, RRClass::IN(), RRType::RRSIG(), RRTTL(3600)); - rrsig = RRsetPtr(rp); - rrsig->addRdata(generic::RRSIG("NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw=")); - example_ns->addRRsig(rrsig); - - example_soa = RRsetPtr(new RRset(example, RRClass::IN(), - RRType::SOA(), RRTTL(3600))); - example_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 1234 3600 1800 2419200 7200")); - - rrsig = RRsetPtr(new RRset(example, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("SOA 5 2 3600 20100322084538 20100220084538 33495 example.com. KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4=")); - example_soa->addRRsig(rrsig); - - example_nsec = RRsetPtr(new RRset(example, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - example_nsec->addRdata(generic::NSEC("cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY")); - rrsig = RRsetPtr(new RRset(example, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio=")); - example_nsec->addRRsig(rrsig); - - // sql1.example.com - sql1_ns = RRsetPtr(new RRset(sql1, RRClass::IN(), - RRType::NS(), RRTTL(3600))); - sql1_ns->addRdata(generic::NS(dns01)); - sql1_ns->addRdata(generic::NS(dns02)); - sql1_ns->addRdata(generic::NS(dns03)); - - rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao=")); - sql1_ns->addRRsig(rrsig); - - sql1_soa = RRsetPtr(new RRset(sql1, RRClass::IN(), - RRType::SOA(), RRTTL(3600))); - sql1_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 678 3600 1800 2419200 7200")); - - rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE=")); - sql1_soa->addRRsig(rrsig); - - sql1_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - sql1_nsec->addRdata(generic::NSEC("www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY")); - rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo=")); - sql1_nsec->addRRsig(rrsig); - sql1_ds = RRsetPtr(new RRset(sql1, RRClass::IN(), - RRType::DS(), RRTTL(3600))); - sql1_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D")); - sql1_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB")); - - rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ=")); - sql1_ds->addRRsig(rrsig); - - - sql1_ds_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - sql1_ds_nsec->addRdata(generic::NSEC("subzone.example.com. NS DS RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw=")); - sql1_ds_nsec->addRRsig(rrsig); - - // www.sql1.example.com - www_sql1_a = RRsetPtr(new RRset(www_sql1, - RRClass::IN(), RRType::A(), - RRTTL(3600))); - www_sql1_a->addRdata(in::A("192.0.2.2")); - - rrsig = RRsetPtr(new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0=")); - www_sql1_a->addRRsig(rrsig); - - www_sql1_nsec = RRsetPtr(new RRset(www_sql1, - RRClass::IN(), RRType::NSEC(), - RRTTL(3600))); - www_sql1_nsec->addRdata(generic::NSEC("sql1.example.com. A RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50=")); - www_sql1_nsec->addRRsig(rrsig); - - // dns01.example.com - dns01_a = RRsetPtr(new RRset(dns01, - RRClass::IN(), RRType::A(), - RRTTL(3600))); - dns01_a->addRdata(in::A("192.0.2.1")); - - rrsig = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg=")); - dns01_a->addRRsig(rrsig); - - dns01_nsec = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::NSEC(), RRTTL(3600))); - dns01_nsec->addRdata(generic::NSEC("dns02.example.com. A RRSIG NSEC")); - - rrsig = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0=")); - dns01_nsec->addRRsig(rrsig); - - // dns02.example.com - dns02_a = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::A(), RRTTL(3600))); - dns02_a->addRdata(in::A("192.0.2.2")); - - rrsig = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk=")); - dns02_a->addRRsig(rrsig); - - dns02_nsec = RRsetPtr(new RRset(dns02, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - dns02_nsec->addRdata(generic::NSEC("dns03.example.com. A RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY=")); - dns02_nsec->addRRsig(rrsig); - - // dns03.example.com - dns03_a = RRsetPtr(new RRset(dns03, - RRClass::IN(), RRType::A(), - RRTTL(3600))); - dns03_a->addRdata(in::A("192.0.2.3")); - - rrsig = RRsetPtr(new RRset(dns03, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI=")); - dns03_a->addRRsig(rrsig); - - dns03_nsec = RRsetPtr(new RRset(dns03, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - dns03_nsec->addRdata(generic::NSEC("foo.example.com. A RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(dns03, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g=")); - dns03_nsec->addRRsig(rrsig); - - // www.example.com - www_a = RRsetPtr(new RRset(www, RRClass::IN(), RRType::A(), - RRTTL(3600))); - www_a->addRdata(in::A("192.0.2.1")); - - rrsig = RRsetPtr(new RRset(www, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q=")); - www_a->addRRsig(rrsig); - - www_nsec = RRsetPtr(new RRset(www, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - www_nsec->addRdata(generic::NSEC("example.com. A RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(www, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs=")); - www_nsec->addRRsig(rrsig); - - // *.wild.example.com - wild_a = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::A(), - RRTTL(3600))); - wild_a->addRdata(in::A("192.0.2.2")); - - rrsig = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0=")); - wild_a->addRRsig(rrsig); - - wild_nsec = RRsetPtr(new RRset(wild, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - wild_nsec->addRdata(generic::NSEC("*.wild2.example.com. A RRSIG NSEC")); - - rrsig = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM=")); - wild_nsec->addRRsig(rrsig); - - // *.wild2.example.com - wild2_cname = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::CNAME(), - RRTTL(3600))); - wild2_cname->addRdata(generic::CNAME("www.example.com")); - - rrsig = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI=")); - wild2_cname->addRRsig(rrsig); - - wild2_nsec = RRsetPtr(new RRset(wild2, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - wild2_nsec->addRdata(generic::NSEC("*.wild3.example.com. CNAME RRSIG NSEC")); - - rrsig = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA=")); - wild2_nsec->addRRsig(rrsig); - - // *.wild3.example.com -- a wildcard record with a lame CNAME - wild3_cname = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::CNAME(), - RRTTL(3600))); - wild3_cname->addRdata(generic::CNAME("spork.example.com")); - - rrsig = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI=")); - wild3_cname->addRRsig(rrsig); - - wild3_nsec = RRsetPtr(new RRset(wild3, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - wild3_nsec->addRdata(generic::NSEC("www.example.com. CNAME RRSIG NSEC")); - - rrsig = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA=")); - wild3_nsec->addRRsig(rrsig); - - // foo.example.com - foo_cname = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::CNAME(), - RRTTL(3600))); - foo_cname->addRdata(generic::CNAME("cnametest.flame.org")); - - rrsig = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o=")); - foo_cname->addRRsig(rrsig); - - foo_nsec = RRsetPtr(new RRset(foo, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - foo_nsec->addRdata(generic::NSEC("mail.example.com. CNAME RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso=")); - foo_nsec->addRRsig(rrsig); - - // cname-int.example.com - cnameint_cname = RRsetPtr(new RRset(cnameint, RRClass::IN(), - RRType::CNAME(), RRTTL(3600))); - cnameint_cname->addRdata(generic::CNAME("www.example.com.")); - - rrsig = RRsetPtr(new RRset(cnameint, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw=")); - cnameint_cname->addRRsig(rrsig); - - cnameint_nsec = RRsetPtr(new RRset(cnameint, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - cnameint_nsec->addRdata(generic::NSEC("dname.example.com. CNAME RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(cnameint, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg=")); - cnameint_nsec->addRRsig(rrsig); - - // cname-ext.example.com - cnameext_cname = RRsetPtr(new RRset(cnameext, RRClass::IN(), - RRType::CNAME(), RRTTL(3600))); - cnameext_cname->addRdata(generic::CNAME("www.sql1.example.com")); - - rrsig = RRsetPtr(new RRset(cnameext, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk=")); - cnameext_cname->addRRsig(rrsig); - - cnameext_nsec = RRsetPtr(new RRset(cnameext, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - cnameext_nsec->addRdata(generic::NSEC("cname-int.example.com. CNAME RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(cnameext, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM=")); - cnameext_nsec->addRRsig(rrsig); - - // dname.example.com - dname_dname = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::DNAME(), - RRTTL(3600))); - dname_dname->addRdata(generic::DNAME("sql1.example.com.")); - - rrsig = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug=")); - dname_dname->addRRsig(rrsig); - - dname_nsec = RRsetPtr(new RRset(dname, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - dname_nsec->addRdata(generic::NSEC("dns01.example.com. DNAME RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0=")); - dname_nsec->addRRsig(rrsig); - - // subzone.example.com - subzone_ns = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::NS(), - RRTTL(3600))); - subzone_ns->addRdata(generic::NS(Name("ns1.subzone.example.com"))); - subzone_ns->addRdata(generic::NS(Name("ns2.subzone.example.com"))); - - subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(), - RRTTL(3600))); - - subzone_glue1 = RRsetPtr(new RRset(Name("ns1.subzone.example.com"), - RRClass::IN(), RRType::A(), - RRTTL(3600))); - subzone_glue1->addRdata(in::A("192.0.2.1")); - subzone_glue2 = RRsetPtr(new RRset(Name("ns2.subzone.example.com"), - RRClass::IN(), RRType::A(), - RRTTL(3600))); - subzone_glue2->addRdata(in::A("192.0.2.2")); - - subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(), - RRTTL(3600))); - - subzone_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D")); - subzone_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB")); - - rrsig = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::RRSIG(), - RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ=")); - subzone_ds->addRRsig(rrsig); - - subzone_nsec = RRsetPtr(new RRset(subzone, RRClass::IN(), - RRType::NSEC(), RRTTL(3600))); - subzone_nsec->addRdata(generic::NSEC("*.wild.example.com. NS DS RRSIG NSEC")); - rrsig = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::RRSIG(), RRTTL(3600))); - rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g=")); - subzone_nsec->addRRsig(rrsig); - - loop1_cname = RRsetPtr(new RRset(loop1, RRClass::IN(), RRType::CNAME(), - RRTTL(3600))); - loop1_cname->addRdata(generic::CNAME(loop2)); - loop2_cname = RRsetPtr(new RRset(loop2, RRClass::IN(), RRType::CNAME(), - RRTTL(3600))); - loop2_cname->addRdata(generic::CNAME(loop1)); + if (zones.empty()) { + for (int i = 0; i < NUM_ZONES; ++i) { + Zone zone(zone_data[i].zone_name, zone_data[i].rrclass); + buildZone(zone, zone_data[i].records, false); + buildZone(zone, zone_data[i].glue_records, true); + sort(zone.names.begin(), zone.names.end()); + zones.push_back(zone); + } + } initialized = true; return (SUCCESS); @@ -470,276 +362,160 @@ TestDataSrc::init() { void TestDataSrc::findClosestEnclosure(NameMatch& match, - const RRClass& qclass) const { + const RRClass& qclass) const +{ const Name& qname = match.qname(); - NameComparisonResult::NameRelation cmp; if (qclass != getClass() && qclass != RRClass::ANY()) { return; } - cmp = qname.compare(sql1).getRelation(); - if (cmp == NameComparisonResult::EQUAL || - cmp == NameComparisonResult::SUBDOMAIN) { - match.update(*this, sql1); - return; + vector::const_iterator it; + vector::const_iterator best_it = zones.end(); + unsigned int best_common_labels = 0; + for (it = zones.begin(); it != zones.end(); ++it) { + const NameComparisonResult cmp = qname.compare(it->zone_name); + const NameComparisonResult::NameRelation reln = cmp.getRelation(); + + if ((reln == NameComparisonResult::EQUAL || + reln == NameComparisonResult::SUBDOMAIN) && + cmp.getCommonLabels() > best_common_labels) { + best_it = it; + best_common_labels = cmp.getCommonLabels(); + } } - cmp = qname.compare(example).getRelation(); - if (cmp == NameComparisonResult::EQUAL || - cmp == NameComparisonResult::SUBDOMAIN) { - match.update(*this, example); - return; + if (best_it != zones.end()) { + match.update(*this, best_it->zone_name); } - } +struct ZoneNameMatch : public unary_function { + ZoneNameMatch(const Name& name) : name_(name) {} + bool operator()(const Zone& zone) const { + return (zone.zone_name == name_); + } + const Name& name_; +}; + +// XXX: the main data source module can override the returned RRset. +// That's bad and should be fixed (Trac #254), but for now we work around it. +RRsetPtr +copyRRset(RRsetPtr const source) { + RRsetPtr rrset = RRsetPtr(new RRset(source->getName(), source->getClass(), + source->getType(), source->getTTL())); + RdataIteratorPtr it = source->getRdataIterator(); + for (it->first(); !it->isLast(); it->next()) { + rrset->addRdata(it->getCurrent()); + } + if (source->getRRsig()) { + rrset->addRRsig(copyRRset(source->getRRsig())); + } + + return (rrset); +} + +class TestDataSrc::RRsetMatch { +public: + struct MatchResult { + MatchResult(const bool name_found, const bool has_delegation) : + name_found_(name_found), has_delegation_(has_delegation) + {} + bool name_found_; + bool has_delegation_; + }; + RRsetMatch(const Name& name, const RRType& rrtype, const Mode mode, + RRsetList& target, uint32_t& flags) : + name_(name), rrtype_(rrtype), mode_(mode), target_(target), + flags_(flags), name_found_(false), has_delegation_(false) + {} + void operator()(const RRsetPtr& rrset) { + if (rrset->getName() != name_) { + return; + } + name_found_ = true; + + if (rrset->getType() == RRType::NS() || + rrset->getType() == RRType::DNAME()) { + has_delegation_ = true; + } + + if (mode_ == DELEGATION) { + if (rrset->getType() == RRType::NS() || + rrset->getType() == RRType::DNAME() || + rrset->getType() == RRType::DS()) { + target_.addRRset(copyRRset(rrset)); + } + } else if (mode_ == ADDRESS) { + if (rrset->getType() == RRType::A() || + rrset->getType() == RRType::AAAA()) { + target_.addRRset(copyRRset(rrset)); + } + } else { + if (rrtype_ == RRType::NSEC() && + rrset->getType() == RRType::CNAME()) { + // XXX: ignore CNAME if the qtype is NSEC. + // tricky, but necessary. + return; + } + if (rrtype_ == RRType::ANY() || rrtype_ == rrset->getType() || + rrset->getType() == RRType::CNAME() || + rrset->getType() == RRType::DNAME()) { + target_.addRRset(copyRRset(rrset)); + if (rrset->getType() == RRType::CNAME()) { + flags_ |= CNAME_FOUND; + } + if (rrset->getType() == RRType::DNAME()) { + flags_ |= REFERRAL; + } + } + } + + } + MatchResult getResult() { return (MatchResult(name_found_, + has_delegation_)); } + const Name& name_; + const RRType& rrtype_; + const Mode mode_; + RRsetList& target_; + uint32_t& flags_; + bool name_found_; + bool has_delegation_; +}; + void TestDataSrc::findRecords(const Name& name, const RRType& rdtype, - RRsetList& target, const Name* zonename, const Mode mode, - uint32_t& flags) const + RRsetList& target, const Name* zonename, + const Mode mode, uint32_t& flags) const { - const bool any = (rdtype == RRType::ANY()); flags = 0; assert(zonename != NULL); - if (*zonename == sql1) { - if (name == sql1 && mode == DELEGATION) { - target.addRRset(sql1_ns); - flags |= REFERRAL; - } else if (name == sql1) { - flags |= REFERRAL; - if (any) { - target.addRRset(sql1_ns); - target.addRRset(sql1_nsec); - } else if (rdtype == RRType::NS()) { - target.addRRset(sql1_ns); - } else if (rdtype == RRType::SOA()) { - target.addRRset(sql1_soa); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(sql1_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == www_sql1) { - if (any) { - target.addRRset(www_sql1_a); - target.addRRset(www_sql1_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(www_sql1_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(www_sql1_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else { - flags |= NAME_NOT_FOUND; - } - } else { - if (name == example && mode == DELEGATION) { - target.addRRset(example_ns); - flags |= REFERRAL; - } else if (name == example) { - flags |= REFERRAL; - if (any) { - target.addRRset(example_ns); - target.addRRset(example_soa); - target.addRRset(example_nsec); - } else if (rdtype == RRType::NS()) { - target.addRRset(example_ns); - } else if (rdtype == RRType::SOA()) { - target.addRRset(example_soa); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(example_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == sql1 && mode == DELEGATION) { - target.addRRset(sql1_ns); - target.addRRset(sql1_ds); - target.addRRset(sql1_ds_nsec); - flags |= REFERRAL; - } else if (name == sql1) { - flags |= REFERRAL; - if (any) { - target.addRRset(sql1_ns); - target.addRRset(sql1_ds); - target.addRRset(sql1_ds_nsec); - } else if (rdtype == RRType::DS()) { - target.addRRset(sql1_ds); - } else if (rdtype == RRType::NS()) { - target.addRRset(sql1_ns); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(sql1_ds_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == subzone && mode == DELEGATION) { - target.addRRset(subzone_ns); - target.addRRset(subzone_ds); - flags |= REFERRAL; - } else if (name == subzone) { - flags |= REFERRAL; - if (any) { - target.addRRset(subzone_ns); - target.addRRset(subzone_nsec); - } else if (rdtype == RRType::NS()) { - target.addRRset(subzone_ns); - } else if (rdtype == RRType::DS()) { - target.addRRset(subzone_ds); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(subzone_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == dns01 && mode == ADDRESS) { - target.addRRset(dns01_a); - } else if (name == dns01) { - if (any) { - target.addRRset(dns01_a); - target.addRRset(dns01_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(dns01_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(dns01_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == dns02 && mode == ADDRESS) { - target.addRRset(dns02_a); - } else if (name == dns02) { - if (any) { - target.addRRset(dns02_a); - target.addRRset(dns02_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(dns02_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(dns02_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == dns03 && mode == ADDRESS) { - target.addRRset(dns03_a); - } else if (name == dns03) { - if (any) { - target.addRRset(dns03_a); - target.addRRset(dns03_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(dns03_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(dns03_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == wild) { - if (any) { - target.addRRset(wild_a); - target.addRRset(wild_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(wild_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(wild_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == wild2) { - if (any) { - target.addRRset(wild2_cname); - target.addRRset(wild2_nsec); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(wild2_nsec); - } else { - target.addRRset(wild2_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } - } else if (name == wild3) { - if (any) { - target.addRRset(wild3_cname); - target.addRRset(wild3_nsec); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(wild3_nsec); - } else { - target.addRRset(wild3_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } - } else if (name == www) { - if (any) { - target.addRRset(www_a); - target.addRRset(www_nsec); - } else if (rdtype == RRType::A()) { - target.addRRset(www_a); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(www_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == foo) { - if (rdtype == RRType::NSEC()) { - target.addRRset(foo_nsec); - } else { - target.addRRset(foo_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } - } else if (name == cnameint) { - if (rdtype == RRType::NSEC()) { - target.addRRset(cnameint_nsec); - } else { - target.addRRset(cnameint_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } - } else if (name == cnameext) { - if (rdtype == RRType::NSEC()) { - target.addRRset(cnameext_nsec); - } else { - target.addRRset(cnameext_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } - } else if (name == dname) { - flags |= REFERRAL; - if (any) { - target.addRRset(dname_dname); - target.addRRset(dname_nsec); - } else if (rdtype == RRType::DNAME()) { - target.addRRset(dname_dname); - } else if (rdtype == RRType::NSEC()) { - target.addRRset(dns01_nsec); - } else { - flags |= TYPE_NOT_FOUND; - } - } else if (name == Name("ns1.subzone.example.com") && mode == ADDRESS) { - target.addRRset(subzone_glue1); - } else if (name == Name("ns2.subzone.example.com") && mode == ADDRESS) { - target.addRRset(subzone_glue2); - } else if (name == loop1) { - target.addRRset(loop1_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } - } else if (name == loop2) { - target.addRRset(loop1_cname); - if (rdtype != RRType::CNAME()) { - flags |= CNAME_FOUND; - } + + vector::const_iterator zone = find_if(zones.begin(), zones.end(), + ZoneNameMatch(*zonename)); + if (zone == zones.end()) { + return; + } + + const RRsetMatch::MatchResult match_result = + for_each(zone->rrsets.begin(), zone->rrsets.end(), + RRsetMatch(name, rdtype, mode, target, flags)).getResult(); + if (match_result.has_delegation_) { + flags |= REFERRAL; + } + if (target.size() == 0) { + if (match_result.name_found_) { + flags |= TYPE_NOT_FOUND; } else { flags |= NAME_NOT_FOUND; } } - return; } DataSrc::Result TestDataSrc::findRRset(const Name& qname, - const RRClass& qclass UNUSED_PARAM, + const RRClass& qclass, const RRType& qtype, RRsetList& target, uint32_t& flags, @@ -755,7 +531,7 @@ TestDataSrc::findRRset(const Name& qname, DataSrc::Result TestDataSrc::findExactRRset(const Name& qname, - const RRClass& qclass UNUSED_PARAM, + const RRClass& qclass, const RRType& qtype, RRsetList& target, uint32_t& flags, @@ -770,7 +546,7 @@ TestDataSrc::findExactRRset(const Name& qname, flags &= ~REFERRAL; // CNAMEs don't count in this case - if (flags & CNAME_FOUND) { + if ((flags & CNAME_FOUND) != 0) { flags &= ~CNAME_FOUND; flags |= TYPE_NOT_FOUND; } @@ -780,7 +556,7 @@ TestDataSrc::findExactRRset(const Name& qname, DataSrc::Result TestDataSrc::findAddrs(const Name& qname, - const RRClass& qclass UNUSED_PARAM, + const RRClass& qclass, RRsetList& target, uint32_t& flags, const Name* zonename) const @@ -795,7 +571,7 @@ TestDataSrc::findAddrs(const Name& qname, DataSrc::Result TestDataSrc::findReferral(const Name& qname, - const RRClass& qclass UNUSED_PARAM, + const RRClass& qclass, RRsetList& target, uint32_t& flags, const Name* zonename) const @@ -815,42 +591,30 @@ TestDataSrc::findPreviousName(const Name& qname, { assert(zonename != NULL); - if (*zonename == example) { - if (qname >= example && qname < cnameext) { - target = example; - } else if (qname < cnameint) { - target = cnameext; - } else if (qname < dname) { - target = cnameint; - } else if (qname < dns01) { - target = dname; - } else if (qname < dns02) { - target = dns01; - } else if (qname < dns03) { - target = dns02; - } else if (qname < foo) { - target = dns03; - } else if (qname < sql1) { - target = foo; - } else if (qname < subzone) { - target = sql1; - } else if (qname < wild) { - target = subzone; - } else if (qname < wild2) { - target = wild; - } else if (qname < wild3) { - target = wild2; - } else if (qname < www) { - target = wild3; - } else { - target = www; - } + vector::const_iterator zone = find_if(zones.begin(), zones.end(), + ZoneNameMatch(*zonename)); + if (zone == zones.end()) { + return (ERROR); + } + + if (zone->names.empty()) { + return (ERROR); + } + + // if found, next_name >= qname. + vector::const_iterator next_name = + lower_bound(zone->names.begin(), zone->names.end(), qname); + if (next_name == zone->names.end()) { + // if no such name was found, the previous name is the last name. + target = zone->names.back(); + } else if (*next_name == qname) { + target = *next_name; + } else if (next_name == zone->names.begin()) { + // if qname < first_name, the "previous name" is the last name. + target = zone->names.back(); } else { - if (qname >= sql1 || qname < www_sql1) { - target = sql1; - } else { - target = www_sql1; - } + // otherwise, qname and next_name share the same previous name. + target = *(next_name - 1); } return (SUCCESS); } diff --git a/src/lib/datasrc/tests/test_datasrc.h b/src/lib/datasrc/tests/test_datasrc.h index b625d9ee53..b3ed55a986 100644 --- a/src/lib/datasrc/tests/test_datasrc.h +++ b/src/lib/datasrc/tests/test_datasrc.h @@ -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, diff --git a/src/lib/datasrc/tests/testdata/example.org b/src/lib/datasrc/tests/testdata/example.org index 822a8a22c4..e60f877f2b 100644 --- a/src/lib/datasrc/tests/testdata/example.org +++ b/src/lib/datasrc/tests/testdata/example.org @@ -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. diff --git a/src/lib/datasrc/tests/testdata/example.org.sqlite3 b/src/lib/datasrc/tests/testdata/example.org.sqlite3 index 060de78dec..070012f72d 100644 Binary files a/src/lib/datasrc/tests/testdata/example.org.sqlite3 and b/src/lib/datasrc/tests/testdata/example.org.sqlite3 differ diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 8b4c2ec9ca..f46610a079 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -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 \ diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index 0de7905778..1a45fe5035 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -923,7 +923,7 @@ SectionIterator::operator*() const { template const T* SectionIterator::operator->() const { - return (impl_->it_.operator->()); + return (&(operator*())); } template diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc index e27255526b..4f02e715b0 100644 --- a/src/lib/dns/name.cc +++ b/src/lib/dns/name.cc @@ -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'); diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am index 55f4b7fbc5..21ba0a4ad3 100644 --- a/src/lib/dns/python/Makefile.am +++ b/src/lib/dns/python/Makefile.am @@ -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) diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am index 44db86781f..ae9d150127 100644 --- a/src/lib/dns/python/tests/Makefile.am +++ b/src/lib/dns/python/tests/Makefile.am @@ -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 diff --git a/src/lib/dns/rrsetlist.cc b/src/lib/dns/rrsetlist.cc index 976f420220..1000799829 100644 --- a/src/lib/dns/rrsetlist.cc +++ b/src/lib/dns/rrsetlist.cc @@ -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)) { diff --git a/src/lib/dns/rrsetlist.h b/src/lib/dns/rrsetlist.h index 9e1c52334f..a8c3769d9b 100644 --- a/src/lib/dns/rrsetlist.h +++ b/src/lib/dns/rrsetlist.h @@ -60,7 +60,7 @@ public: } P operator->() const { - return (it_.operator->()); + return (&(operator*())); } bool operator==(const RRsetListIterator& other) { diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 29a174755b..ab765bc834 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -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 diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am index 4c0ccfe5a9..307c22f4b9 100644 --- a/src/lib/python/isc/Makefile.am +++ b/src/lib/python/isc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = datasrc cc config # Util +SUBDIRS = datasrc cc config log # Util python_PYTHON = __init__.py diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py index 8fcbf4256d..9204792236 100644 --- a/src/lib/python/isc/__init__.py +++ b/src/lib/python/isc/__init__.py @@ -2,3 +2,4 @@ import isc.datasrc import isc.cc import isc.config #import isc.dns +import isc.log diff --git a/src/lib/python/isc/datasrc/master.py b/src/lib/python/isc/datasrc/master.py index 55556ccb24..566f1b4673 100644 --- a/src/lib/python/isc/datasrc/master.py +++ b/src/lib/python/isc/datasrc/master.py @@ -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 diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am new file mode 100644 index 0000000000..301dc6b2c8 --- /dev/null +++ b/src/lib/python/isc/log/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = tests + +python_PYTHON = __init__.py log.py + +pythondir = $(pyexecdir)/isc/log + +pytest: + $(SHELL) tests/log_test diff --git a/src/lib/python/isc/log/__init__.py b/src/lib/python/isc/log/__init__.py new file mode 100644 index 0000000000..a34e164d3f --- /dev/null +++ b/src/lib/python/isc/log/__init__.py @@ -0,0 +1 @@ +from isc.log.log import * diff --git a/src/lib/python/isc/log/log.py b/src/lib/python/isc/log/log.py new file mode 100644 index 0000000000..635e4bd29b --- /dev/null +++ b/src/lib/python/isc/log/log.py @@ -0,0 +1,249 @@ +# 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. + +"""This module is to convert python logging module over +to log4python. +Copyright (C) 2010 Internet Systems Consortium. +To use, simply 'import isc.log.log' and log away! +""" +import os +import syslog +import logging +import logging.handlers + +"""LEVELS: logging levels mapping +""" +LEVELS = {'debug' : logging.DEBUG, + 'info' : logging.INFO, + 'warning' : logging.WARNING, + 'error' : logging.ERROR, + 'critical' : logging.CRITICAL} + + +FORMATTER = logging.Formatter("%(name)s: %(levelname)s: %(message)s") +TIME_FORMATTER = logging.Formatter("%(asctime)s.%(msecs)03d %(name)s: %(levelname)s: %(message)s", + "%d-%b-%Y %H:%M:%S") + +class NSFileLogHandler(logging.handlers.RotatingFileHandler): + """RotatingFileHandler: replace RotatingFileHandler with a custom handler""" + + def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0): + dir = os.path.split(filename) + if not (os.path.exists(dir[0])): + os.makedirs(dir[0]) + super(NSFileLogHandler, self).__init__(filename, mode, maxBytes, + backupCount, encoding, delay) + + def shouldRollover(self, record): + """Rewrite RotatingFileHandler.shouldRollover. + + If the log file is deleted at runtime, a new file will be created. + """ + dfn = self.baseFilename + if (self.stream) and (not os.path.exists(dfn)): #Does log file exist? + self.stream.close() + dir = os.path.split(dfn) + if not (os.path.exists(dir[0])): #Does log subdirectory exist? + os.makedirs(dir[0]) + self.stream = self._open() + return super(NSFileLogHandler, self).shouldRollover(record) + + def update_config(self, file_name, backup_count, max_bytes): + """Update RotatingFileHandler configuration. + + If the file path does not exist, we will use the old log file. + input: + log file name + max backup count + predetermined log file size + """ + dir = os.path.split(file_name) + if(os.path.exists(dir[0])): + self.baseFilename = file_name + self.maxBytes = max_bytes + self.backupCount = backup_count + + +class NSSysLogHandler(logging.Handler): + """Replace SysLogHandler with a custom handler + + A handler class which sends formatted logging records to a syslog + server. + """ + def __init__(self, ident, logopt=0, facility=syslog.LOG_USER): + """Initialize a handler. + + If facility is not specified, LOG_USER is used. + """ + super(NSSysLogHandler, self).__init__() + self._ident = ident + self._logopt = logopt + self._facility = facility + self._mappings = { + logging.DEBUG: syslog.LOG_DEBUG, + logging.INFO: syslog.LOG_INFO, + logging.WARNING: syslog.LOG_WARNING, + logging.ERROR: syslog.LOG_ERR, + logging.CRITICAL: syslog.LOG_CRIT, + } + + def _encodeLevel(self, level): + """Encoding the priority.""" + return self._mappings.get(level, syslog.LOG_INFO) + + def emit(self, record): + """Emit a record. + + The record is formatted, and then sent to the syslog server. If + exception information is present, it is NOT sent to the server. + """ + syslog.openlog(self._ident, self._logopt, self._facility) + msg = self.format(record) + prio = self._encodeLevel(record.levelno) + syslog.syslog(prio, msg) + syslog.closelog() + + +class NSLogger(logging.getLoggerClass()): + """Override logging.logger behaviour.""" + def __init__(self, log_name, log_file, severity='debug', versions=0, + max_bytes=0, log_to_console=True): + """Initializes the logger with some specific parameters + + If log_to_console is True, stream handler will be used; + else syslog handler will be used. + + To disable file handler, set log_file = ''. + """ + self._log_name = log_name + self._log_file = log_file + self._severity = severity + self._versions = versions + self._max_bytes = max_bytes + + super(NSLogger, self).__init__(self._log_name) + + # Set up a specific logger with our desired output level + logLevel = LEVELS.get(self._severity, logging.NOTSET) + self.setLevel(logLevel) + + self._file_handler = None + self._stream_handler = None + self._syslog_handler = None + + self._add_rotate_handler(self._log_file, self._versions, self._max_bytes) + if log_to_console: + self._add_stream_handler() + else: + self._add_syslog_handler() + + def _add_rotate_handler(self, log_file, backup_count, max_bytes): + """Add a rotate file handler. + + input: + log_file : the location of log file. Handler will not be created + if log_file='' + max_bytes : limit log growth + backup_count : max backup count + """ + if(log_file != 0 and log_file != ''): + try: + self._file_handler = NSFileLogHandler(filename = log_file, + maxBytes = max_bytes, backupCount = backup_count) + except IOError: + self._file_handler = None + return + self._file_handler.setFormatter(TIME_FORMATTER) + self.addHandler(self._file_handler) + + def _add_stream_handler(self): + """Add a stream handler. + + sys.stderr will be used for logging output. + """ + self._stream_handler = logging.StreamHandler() + self._stream_handler.setFormatter(TIME_FORMATTER) + self.addHandler(self._stream_handler) + + def _add_syslog_handler(self, nsfacility=syslog.LOG_USER): + """Add a syslog handler. + + If facility is not specified, LOG_USER is used. + The default severity level is INFO. + """ + self._syslog_handler = NSSysLogHandler('BIND10', facility = nsfacility) + self._syslog_handler.setFormatter(FORMATTER) + #set syslog handler severity level INFO + self._syslog_handler.setLevel(logging.INFO) + self.addHandler(self._syslog_handler) + + def _update_rotate_handler(self, log_file, backup_count, max_bytes): + """If the rotate file handler has been added to the logger, update its + configuration, or add it to the logger. + """ + if (self._file_handler in self.handlers): + if(log_file != 0 and log_file != ''): + self._file_handler.update_config(log_file, backup_count, max_bytes) + else: + """If log file is empty, the handler will be removed.""" + self._file_handler.flush() + self._file_handler.close() + self.removeHandler(self._file_handler) + else: + self._add_rotate_handler(log_file, backup_count, max_bytes) + + def _get_config(self, config_data): + """Get config data from module configuration""" + + log_file_str = config_data.get('log_file') + if(log_file_str): + self._log_file = log_file_str + + severity_str = config_data.get('log_severity') + if(severity_str): + self._severity = severity_str + + versions_str = config_data.get('log_versions') + if(versions_str): + self._versions = int(versions_str) + + max_bytes_str = config_data.get('log_max_bytes') + if(max_bytes_str): + self._max_bytes = int(max_bytes_str) + + def update_config(self, config_data): + """Update logger's configuration. + + We can update logger's log level and its rotate file handler's configuration. + """ + self._get_config(config_data) + + logLevel = LEVELS.get(self._severity, logging.NOTSET) + if(logLevel != self.getEffectiveLevel()): + self.setLevel(logLevel) + self._update_rotate_handler(self._log_file, self._versions, self._max_bytes) + + def log_message(self, level, msg, *args, **kwargs): + """Log 'msg % args' with the integer severity 'level'. + + To pass exception information, use the keyword argument exc_info with + a true value, e.g. + + logger.log_message('info', "We have a %s", "mysterious problem"). + """ + logLevel = LEVELS.get(level, logging.NOTSET) + self.log(logLevel, msg, *args, **kwargs) + + diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am new file mode 100644 index 0000000000..f7e869a22c --- /dev/null +++ b/src/lib/python/isc/log/tests/Makefile.am @@ -0,0 +1,12 @@ +PYTESTS = log_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/lib/python/isc/log \ + $(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \ + done diff --git a/src/lib/python/isc/log/tests/log_test.in b/src/lib/python/isc/log/tests/log_test.in new file mode 100644 index 0000000000..60e5e3f699 --- /dev/null +++ b/src/lib/python/isc/log/tests/log_test.in @@ -0,0 +1,26 @@ +#! /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 + +TEST_PATH=@abs_top_srcdir@/src/lib/python/isc/log/tests +PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python +export PYTHONPATH + +cd ${TEST_PATH} +exec ${PYTHON_EXEC} -O log_test.py $* diff --git a/src/lib/python/isc/log/tests/log_test.py b/src/lib/python/isc/log/tests/log_test.py new file mode 100644 index 0000000000..ae1fccb605 --- /dev/null +++ b/src/lib/python/isc/log/tests/log_test.py @@ -0,0 +1,178 @@ +from isc.log.log import * +import unittest +import os +import tempfile + + +class TestRotateFileHandler(unittest.TestCase): + + def setUp(self): + self.FILE_LOG1 = tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.FILE_LOG2 = tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.FILE_LOG3 = tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.handler = NSFileLogHandler(filename = self.FILE_LOG1.name, + maxBytes = 1024, + backupCount = 5) + + def test_shouldRollover(self): + if(os.path.exists(self.FILE_LOG1.name)): + os.remove(self.FILE_LOG1.name) + record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None) + self.handler.shouldRollover(record) + self.assertTrue(os.path.exists(self.FILE_LOG1.name)) + + def test_update_config(self): + self.handler.update_config(self.FILE_LOG2.name, 3, 512) + self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name) + self.assertEqual(self.handler.maxBytes, 512) + self.assertEqual(self.handler.backupCount, 3) + + dir = os.path.split(self.FILE_LOG3.name) + path = dir[0] + "path_not_exists" + update_file = os.path.join(path, dir[1]) + + if not os.path.exists(path): + self.handler.update_config(update_file, 4, 1024) + self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name) + self.assertEqual(self.handler.maxBytes, 1024) + self.assertEqual(self.handler.backupCount, 4) + + def tearDown(self): + self.handler.flush() + self.handler.close() + self.FILE_LOG1.close() + self.FILE_LOG2.close() + self.FILE_LOG3.close() + +class TestSysLogHandler(unittest.TestCase): + def setUp(self): + self.handler = NSSysLogHandler("BIND10") + + def test_encodeLevel(self): + sysLevel = self.handler._encodeLevel(logging.ERROR) + self.assertEqual(sysLevel, syslog.LOG_ERR) + + def test_emit(self): + record = logging.LogRecord(None, None, "", 0, "syslog handler", (), None, None) + self.handler.emit(record) + + +class TestLogging(unittest.TestCase): + + def setUp(self): + self.FILE_STREAM_LOG1= tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.FILE_STREAM_LOG2= tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.FILE_STREAM_LOG3= tempfile.NamedTemporaryFile(mode='w', + prefix="b10", + delete=True) + self.file_stream_logger = NSLogger('File_Stream_Logger', + self.FILE_STREAM_LOG1.name, + 'debug', 5, 1024, True) + self.syslog_logger = NSLogger('SysLogger', '', 'info', 5, 1024, False) + + def test_logging_init(self): + self.assertNotEqual(self.file_stream_logger._file_handler, None) + self.assertNotEqual(self.file_stream_logger._stream_handler, None) + self.assertEqual(self.file_stream_logger._syslog_handler, None) + + ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers + self.assertTrue(ret) + ret = self.file_stream_logger._stream_handler in self.file_stream_logger.handlers + self.assertTrue(ret) + ret = self.file_stream_logger._syslog_handler in self.file_stream_logger.handlers + self.assertFalse(ret) + logLevel = LEVELS.get('debug', logging.NOTSET) + self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel) + + self.assertEqual(self.syslog_logger._file_handler, None) + self.assertEqual(self.syslog_logger._stream_handler, None) + self.assertNotEqual(self.syslog_logger._syslog_handler, None) + ret = self.syslog_logger._file_handler in self.syslog_logger.handlers + self.assertFalse(ret) + ret = self.syslog_logger._stream_handler in self.syslog_logger.handlers + self.assertFalse(ret) + ret = self.syslog_logger._syslog_handler in self.syslog_logger.handlers + self.assertTrue(ret) + + logLevel = LEVELS.get('info', logging.NOTSET) + self.assertEqual(self.syslog_logger.getEffectiveLevel(), logLevel) + + def test_add_rotate_handler(self): + if(self.syslog_logger._file_handler in self.syslog_logger.handlers): + self.syslog_logger.removeHandler(self.syslog_logger._file_handler) + + self.syslog_logger._add_rotate_handler('', 5, 1024) + ret = self.syslog_logger._file_handler in self.syslog_logger.handlers + self.assertFalse(ret) + + self.syslog_logger._add_rotate_handler(self.FILE_STREAM_LOG1.name, 5, 1024) + ret = self.syslog_logger._file_handler in self.syslog_logger.handlers + self.assertTrue(ret) + + def test_add_stream_handler(self): + if(self.file_stream_logger._stream_handler in self.file_stream_logger.handlers): + self.file_stream_logger.removeHandler(self.file_stream_logger._stream_handler) + + self.file_stream_logger._add_stream_handler() + ret = self.file_stream_logger._stream_handler in self.file_stream_logger.handlers + self.assertTrue(ret) + + def test_add_syslog_handler(self): + if(self.syslog_logger._syslog_handler in self.syslog_logger.handlers): + self.syslog_logger.removeHandler(self.syslog_logger._syslog_handler) + + self.syslog_logger._add_syslog_handler() + ret = self.syslog_logger._syslog_handler in self.syslog_logger.handlers + self.assertTrue(ret) + + def test_update_rotate_handler(self): + self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG2.name, 4, 1024) + ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers + self.assertTrue(ret) + + self.file_stream_logger._update_rotate_handler('', 5, 1024) + ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers + self.assertFalse(ret) + + self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG1.name, 4, 1024) + ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers + self.assertTrue(ret) + + def test_update_config(self): + update_config = {'log_file' : self.FILE_STREAM_LOG1.name, + 'log_severity' : 'error', + 'log_versions' : 4, + 'log_max_bytes' : 1024} + self.file_stream_logger.update_config(update_config) + logLevel = LEVELS.get('error', logging.NOTSET) + self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel) + + def test_log_message(self): + update_config = {'log_file' : self.FILE_STREAM_LOG3.name, + 'log_severity' : 'critical', + 'log_versions' : 4, + 'log_max_bytes' : 1024} + self.file_stream_logger.update_config(update_config) + self.file_stream_logger.log_message('debug', 'debug message') + self.file_stream_logger.log_message('info', 'info message') + self.file_stream_logger.log_message('warning', 'warning message') + self.file_stream_logger.log_message('error', 'error message') + self.assertTrue(os.path.exists(self.FILE_STREAM_LOG3.name)) + + def tearDown(self): + self.FILE_STREAM_LOG1.close() + self.FILE_STREAM_LOG2.close() + self.FILE_STREAM_LOG3.close() + +if __name__ == '__main__': + unittest.main()