Parcourir la source

sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-asio@1248 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya il y a 15 ans
Parent
commit
f9129adbd1
40 fichiers modifiés avec 2017 ajouts et 156 suppressions
  1. 7 1
      configure.ac
  2. 1 1
      src/bin/Makefile.am
  3. 3 0
      src/bin/bind10/Makefile.am
  4. 0 1
      src/bin/bind10/TODO
  5. 96 17
      src/bin/bind10/bind10.py.in
  6. 1 1
      src/bin/bind10/run_bind10.sh.in
  7. 2 2
      src/bin/bind10/bind10_test.in
  8. 0 0
      src/bin/bind10/tests/bind10_test.py
  9. 6 1
      src/bin/cmdctl/Makefile.am
  10. 136 0
      src/bin/cmdctl/b10-cmdctl.xml
  11. 3 2
      src/bin/loadzone/b10-loadzone.py.in
  12. 134 0
      src/bin/loadzone/b10-loadzone.xml
  13. 14 0
      src/bin/xfrin/Makefile.am
  14. 12 0
      src/bin/xfrin/run_b10-xfrin.sh.in
  15. 12 0
      src/bin/xfrin/unittest/xfrin_test.in
  16. 45 0
      src/bin/xfrin/unittest/xfrin_test.py
  17. 447 0
      src/bin/xfrin/xfrin.py.in
  18. 66 0
      src/bin/xfrin/xfrin.spec
  19. 27 35
      src/lib/auth/data_source_sqlite3.cc
  20. 1 1
      src/lib/auth/data_source_sqlite3.h
  21. 21 10
      src/lib/auth/data_source_sqlite3_unittest.cc
  22. 36 0
      src/lib/auth/testdata/root.zone
  23. BIN
      src/lib/auth/testdata/test-root.sqlite3
  24. 81 6
      src/lib/config/ccsession.cc
  25. 42 1
      src/lib/config/ccsession.h
  26. 10 0
      src/lib/dns/Makefile.dat
  27. 627 0
      src/lib/dns/message_python.cc
  28. 71 0
      src/lib/dns/message_test.py
  29. 3 6
      src/lib/python/isc/Makefile.am
  30. 2 5
      src/lib/python/isc/Util/Makefile.am
  31. 0 1
      src/lib/python/isc/__init__.py
  32. 2 5
      src/lib/python/isc/auth/Makefile.am
  33. 24 0
      src/lib/python/isc/auth/TODO
  34. 50 21
      src/lib/python/isc/auth/master.py
  35. 28 23
      src/lib/python/isc/auth/sqlite3_ds.py
  36. 2 5
      src/lib/python/isc/cc/Makefile.am
  37. 0 3
      src/lib/python/isc/cc/message.py
  38. 2 5
      src/lib/python/isc/config/Makefile.am
  39. 0 2
      src/lib/python/isc/config/ccsession.py
  40. 3 1
      src/lib/python/isc/config/cfgmgr.py

+ 7 - 1
configure.ac

@@ -200,6 +200,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/loadzone/Makefile
                  src/bin/loadzone/Makefile
                  src/bin/msgq/Makefile
                  src/bin/msgq/Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/Makefile
+                 src/bin/xfrin/Makefile
                  src/lib/Makefile
                  src/lib/Makefile
                  src/lib/cc/Makefile
                  src/lib/cc/Makefile
                  src/lib/python/Makefile
                  src/lib/python/Makefile
@@ -218,8 +219,11 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/unittest/cmdctl_test
            src/bin/cmdctl/unittest/cmdctl_test
+           src/bin/xfrin/unittest/xfrin_test
+           src/bin/xfrin/xfrin.py
+           src/bin/xfrin/run_b10-xfrin.sh
            src/bin/bind10/bind10.py
            src/bin/bind10/bind10.py
-           src/bin/bind10/bind10_test
+           src/bin/bind10/tests/bind10_test
            src/bin/bind10/run_bind10.sh
            src/bin/bind10/run_bind10.sh
            src/bin/bindctl/bindctl
            src/bin/bindctl/bindctl
            src/bin/bindctl/unittest/bindctl_test
            src/bin/bindctl/unittest/bindctl_test
@@ -235,8 +239,10 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/lib/dns/tests/testdata/gen-wiredata.py
            src/lib/dns/tests/testdata/gen-wiredata.py
           ], [
           ], [
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
+           chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/cmdctl/unittest/cmdctl_test
            chmod +x src/bin/cmdctl/unittest/cmdctl_test
+           chmod +x src/bin/xfrin/unittest/xfrin_test
            chmod +x src/bin/bindctl/unittest/bindctl_test
            chmod +x src/bin/bindctl/unittest/bindctl_test
            chmod +x src/bin/bindctl/bindctl
            chmod +x src/bin/bindctl/bindctl
            chmod +x src/bin/loadzone/run_loadzone
            chmod +x src/bin/loadzone/run_loadzone

+ 1 - 1
src/bin/Makefile.am

@@ -1 +1 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth
+SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin

+ 3 - 0
src/bin/bind10/Makefile.am

@@ -12,3 +12,6 @@ bind10: bind10.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10.py >$@
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10.py >$@
 	chmod a+x $@
 	chmod a+x $@
+
+pytest:
+	$(SHELL) tests/bind10_test

+ 0 - 1
src/bin/bind10/TODO

@@ -8,7 +8,6 @@
   - Force-stop a component
   - Force-stop a component
 - Mechanism to wait for child to start before continuing
 - Mechanism to wait for child to start before continuing
 - Way to ask a child to die politely 
 - Way to ask a child to die politely 
-- Back-off mechanism for restarting failed processes
 - Start statistics daemon
 - Start statistics daemon
 - Statistics interaction (?)
 - Statistics interaction (?)
 - Use .spec file to define comands
 - Use .spec file to define comands

+ 96 - 17
src/bin/bind10/bind10.py.in

@@ -2,6 +2,8 @@
 
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import os
 import os
+import time
+import random
 
 
 """\
 """\
 This file implements the Boss of Bind (BoB, or bob) program.
 This file implements the Boss of Bind (BoB, or bob) program.
@@ -50,11 +52,54 @@ import isc.cc
 import isc
 import isc
 
 
 # This is the version that gets displayed to the user.
 # This is the version that gets displayed to the user.
-__version__ = "v20100225"
+__version__ = "v20100309"
 
 
 # Nothing at all to do with the 1990-12-10 article here:
 # Nothing at all to do with the 1990-12-10 article here:
 # http://www.subgenius.com/subg-digest/v2/0056.html
 # http://www.subgenius.com/subg-digest/v2/0056.html
 
 
+class RestartSchedule:
+    """
+Keeps state when restarting something (in this case, a process).
+
+When a process dies unexpectedly, we need to restart it. However, if 
+it fails to restart for some reason, then we should not simply keep
+restarting it at high speed.
+
+A more sophisticated algorithm can be developed, but for now we choose
+a simple set of rules:
+
+  * If a process was been running for >=10 seconds, we restart it
+    right away.
+  * If a process was running for <10 seconds, we wait until 10 seconds
+    after it was started.
+
+To avoid programs getting into lockstep, we use a normal distribution
+to avoid being restarted at exactly 10 seconds."""
+
+    def __init__(self, restart_frequency=10.0):
+        self.restart_frequency = restart_frequency
+        self.run_start_time = None
+        self.run_stop_time = None
+        self.restart_time = None
+    
+    def set_run_start_time(self, when=None):
+        if when is None:
+            when = time.time()
+        self.run_start_time = when
+        sigma = self.restart_frequency * 0.05
+        self.restart_time = when + random.normalvariate(self.restart_frequency, 
+                                                        sigma)
+
+    def set_run_stop_time(self, when=None):
+        if when is None:
+            when = time.time()
+        self.run_stop_time = when
+
+    def get_restart_time(self, when=None):
+        if when is None:
+            when = time.time()
+        return max(when, self.restart_time)
+
 class ProcessInfo:
 class ProcessInfo:
     """Information about a process"""
     """Information about a process"""
 
 
@@ -82,12 +127,14 @@ class ProcessInfo:
                                         close_fds=True,
                                         close_fds=True,
                                         env=spawn_env,)
                                         env=spawn_env,)
         self.pid = self.process.pid
         self.pid = self.process.pid
+        self.restart_schedule.set_run_start_time()
 
 
     def __init__(self, name, args, env={}, dev_null_stdout=False):
     def __init__(self, name, args, env={}, dev_null_stdout=False):
         self.name = name 
         self.name = name 
         self.args = args
         self.args = args
         self.env = env
         self.env = env
         self.dev_null_stdout = dev_null_stdout
         self.dev_null_stdout = dev_null_stdout
+        self.restart_schedule = RestartSchedule()
         self._spawn()
         self._spawn()
 
 
     def respawn(self):
     def respawn(self):
@@ -229,6 +276,20 @@ class BoB:
         if self.verbose:
         if self.verbose:
             sys.stdout.write("Started b10-auth (PID %d)\n" % auth.pid)
             sys.stdout.write("Started b10-auth (PID %d)\n" % auth.pid)
 
 
+        # start the b10-xfrin
+        if self.verbose:
+            sys.stdout.write("Starting b10-xfrin\n")
+        try:
+            xfrind = ProcessInfo("b10-xfrin", ['b10-xfrin'])
+        except Exception as e:
+            c_channel.process.kill()
+            bind_cfgd.process.kill()
+            auth.process.kill()
+            return "Unable to start b10-xfrin; " + str(e)
+        self.processes[xfrind.pid] = xfrind
+        if self.verbose:
+            sys.stdout.write("Started b10-xfrin (PID %d)\n" % xfrind.pid)
+
         # start the b10-cmdctl
         # start the b10-cmdctl
         # XXX: we hardcode port 8080
         # XXX: we hardcode port 8080
         if self.verbose:
         if self.verbose:
@@ -239,6 +300,7 @@ class BoB:
             c_channel.process.kill()
             c_channel.process.kill()
             bind_cfgd.process.kill()
             bind_cfgd.process.kill()
             auth.process.kill()
             auth.process.kill()
+            xfrind.process.kill()
             return "Unable to start b10-cmdctl; " + str(e)
             return "Unable to start b10-cmdctl; " + str(e)
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         if self.verbose:
         if self.verbose:
@@ -317,6 +379,7 @@ class BoB:
             if pid == 0: break
             if pid == 0: break
             if pid in self.processes:
             if pid in self.processes:
                 proc_info = self.processes.pop(pid)
                 proc_info = self.processes.pop(pid)
+                proc_info.restart_schedule.set_run_stop_time()
                 self.dead_processes[proc_info.pid] = proc_info
                 self.dead_processes[proc_info.pid] = proc_info
                 if self.verbose:
                 if self.verbose:
                     sys.stdout.write("Process %s (PID %d) died.\n" % 
                     sys.stdout.write("Process %s (PID %d) died.\n" % 
@@ -386,26 +449,39 @@ class BoB:
 
 
     def restart_processes(self):
     def restart_processes(self):
         """Restart any dead processes."""
         """Restart any dead processes."""
-        # XXX: this needs a back-off algorithm
+        next_restart = None
         # if we're shutting down, then don't restart
         # if we're shutting down, then don't restart
         if not self.runnable:
         if not self.runnable:
-            return
+            return next_restart
         # otherwise look through each dead process and try to restart
         # otherwise look through each dead process and try to restart
         still_dead = {}
         still_dead = {}
+        now = time.time()
         for proc_info in self.dead_processes.values():
         for proc_info in self.dead_processes.values():
-            if self.verbose:
-                sys.stdout.write("Resurrecting dead %s process...\n" % 
-                                 proc_info.name)
-            try:
-                proc_info.respawn()
-                self.processes[proc_info.pid] = proc_info
-                if self.verbose:
-                    sys.stdout.write("Resurrected %s (PID %d)\n" %
-                                     (proc_info.name, proc_info.pid))
-            except:
+            restart_time = proc_info.restart_schedule.get_restart_time(now)
+            if restart_time > now:
+#                if self.verbose:
+#                    sys.stdout.write("Dead %s process waiting %.1f seconds "\
+#                                     "for resurrection\n" % 
+#                                     (proc_info.name, (restart_time-now)))
+                if (next_restart is None) or (next_restart > restart_time):
+                    next_restart = restart_time
                 still_dead[proc_info.pid] = proc_info
                 still_dead[proc_info.pid] = proc_info
+            else:
+                if self.verbose:
+                    sys.stdout.write("Resurrecting dead %s process...\n" % 
+                                     proc_info.name)
+                try:
+                    proc_info.respawn()
+                    self.processes[proc_info.pid] = proc_info
+                    if self.verbose:
+                        sys.stdout.write("Resurrected %s (PID %d)\n" %
+                                         (proc_info.name, proc_info.pid))
+                except:
+                    still_dead[proc_info.pid] = proc_info
         # remember any processes that refuse to be resurrected
         # remember any processes that refuse to be resurrected
         self.dead_processes = still_dead
         self.dead_processes = still_dead
+        # return the time when the next process is ready to be restarted
+        return next_restart
 
 
 def reaper(signal_number, stack_frame):
 def reaper(signal_number, stack_frame):
     """A child process has died (SIGCHLD received)."""
     """A child process has died (SIGCHLD received)."""
@@ -484,15 +560,18 @@ def main():
     while boss_of_bind.runnable:
     while boss_of_bind.runnable:
         # clean up any processes that exited
         # clean up any processes that exited
         boss_of_bind.reap_children()
         boss_of_bind.reap_children()
-        boss_of_bind.restart_processes()
-
-        # XXX: get time for next restart for timeout
+        next_restart = boss_of_bind.restart_processes()
+        if next_restart is None:
+            wait_time = None
+        else:
+            wait_time = max(next_restart - time.time(), 0)
 
 
         # select() can raise EINTR when a signal arrives, 
         # select() can raise EINTR when a signal arrives, 
         # even if they are resumable, so we have to catch
         # even if they are resumable, so we have to catch
         # the exception
         # the exception
         try:
         try:
-            (rlist, wlist, xlist) = select.select([wakeup_fd, ccs_fd], [], [])
+            (rlist, wlist, xlist) = select.select([wakeup_fd, ccs_fd], [], [], 
+                                                  wait_time)
         except select.error as err:
         except select.error as err:
             if err.args[0] == errno.EINTR:
             if err.args[0] == errno.EINTR:
                 (rlist, wlist, xlist) = ([], [], [])
                 (rlist, wlist, xlist) = ([], [], [])

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -5,7 +5,7 @@ export PYTHON_EXEC
 
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:$PATH
 export PATH
 export PATH
 
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python
 PYTHONPATH=@abs_top_builddir@/src/lib/python

+ 2 - 2
src/bin/bind10/bind10_test.in

@@ -8,9 +8,9 @@ BIND10_PATH=@abs_top_srcdir@/src/bin/bind10
 PATH=@abs_top_srcdir@/src/bin/msgq:@abs_top_srcdir@/src/bin/auth:@abs_top_srcdir@/src/bin/bind-cfgd:$PATH
 PATH=@abs_top_srcdir@/src/bin/msgq:@abs_top_srcdir@/src/bin/auth:@abs_top_srcdir@/src/bin/bind-cfgd:$PATH
 export PATH
 export PATH
 
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/python
+PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_srcdir@/src/bin/bind10
 export PYTHONPATH
 export PYTHONPATH
 
 
-cd ${BIND10_PATH}
+cd ${BIND10_PATH}/tests
 exec ${PYTHON_EXEC} -O bind10_test.py $*
 exec ${PYTHON_EXEC} -O bind10_test.py $*
 
 

src/bin/bind10/bind10_test.py → src/bin/bind10/tests/bind10_test.py


+ 6 - 1
src/bin/cmdctl/Makefile.am

@@ -3,12 +3,17 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexec_SCRIPTS = b10-cmdctl
 pkglibexec_SCRIPTS = b10-cmdctl
 
 
 b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
 b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
+# TODO: this is dangerous -- will overwrite!
 b10_cmdctl_DATA = passwd.csv b10-cmdctl.pem
 b10_cmdctl_DATA = passwd.csv b10-cmdctl.pem
 
 
-CLEANFILES=	cmdctl.py
+CLEANFILES=	b10-cmdctl
 
 
 # TODO: does this need $$(DESTDIR) also?
 # TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl: cmdctl.py
 b10-cmdctl: cmdctl.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	chmod a+x $@
 	chmod a+x $@
+
+install-data-local:
+	chmod go-rwx $(DESTDIR)$(pkgdatadir)/passwd.csv
+	chmod go-rwx $(DESTDIR)$(pkgdatadir)/b10-cmdctl.pem

+ 136 - 0
src/bin/cmdctl/b10-cmdctl.xml

@@ -0,0 +1,136 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+  <refentryinfo>
+    <date>March 9, 2010</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-cmdctl</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-cmdctl</refname>
+    <refpurpose>BIND 10 remote control daemon</refpurpose>
+<!-- TODO: is that okay? -->
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2010</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+<!--
+NO command line arguments
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command></command>
+      <arg><option></option></arg>
+      <arg choice="opt"></arg>
+      <arg choice="opt"></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+-->
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-cmdctl</command> daemon provides an entry
+      for commands sent to the BIND 10 services.
+      For example, the <command>bindctl</command> user interface
+      communicates via <command>b10-cmdctl</command>.
+    </para>
+
+    <para>
+      It is a lightweight HTTPS server with HTTP Digest Authentication
+      (username and password validation).
+      It offers a RESTful style interface.
+<!-- TODO: document the RESTful APIs -->
+      It currently runs on port 8080 on localhost (127.0.0.1).
+<!-- TODO: this will be customizable -->
+    </para>
+  </refsect1>
+
+<!--
+  <refsect1>
+    <title>ARGUMENTS</title>
+    <para>
+      <orderedlist numeration="loweralpha">
+        <listitem>
+          <para>
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+          </para>
+        </listitem>
+      </orderedlist>
+    </para>
+
+  </refsect1>
+-->
+
+  <refsect1>
+    <title>FILES</title>
+<!-- TODO: replace /usr/local -->
+<!-- TODO: permissions -->
+<!-- TODO: what about multiple accounts? -->
+<!-- TODO: shouldn't the password file name say cmdctl in it? -->
+    <para><filename>/usr/local/share/bind10/passwd.csv</filename>
+      &mdash; account database containing the name, hashed password,
+      and the salt.
+    </para>
+<!-- TODO: replace /usr/local -->
+<!-- TODO: permissions -->
+<!-- TODO: shouldn't have both in same file, will be configurable -->
+    <para><filename>/usr/local/share/bind10/b10-cmdctl.pem</filename>
+      &mdash; contains the RSA Private key and the Certificate.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHORS</title>
+    <para>
+      The <command>b10-cmdctl</command> daemon was initially designed
+      and coded by Zhang Likun of CNNIC.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 3 - 2
src/bin/loadzone/b10-loadzone.py.in

@@ -40,11 +40,13 @@ def main():
         exit(2)
         exit(2)
 
 
     dbfile = '/tmp/zone.sqlite3'
     dbfile = '/tmp/zone.sqlite3'
-    initial_origin = '.'
+    initial_origin = ''
     for o, a in opts:
     for o, a in opts:
         if o in ("-d", "--dbfile"):
         if o in ("-d", "--dbfile"):
             dbfile = a
             dbfile = a
         elif o in ("-o", "--origin"):
         elif o in ("-o", "--origin"):
+            if a[-1] != '.':
+                a += '.'
             initial_origin = a
             initial_origin = a
         elif o in ("-h", "--help"):
         elif o in ("-h", "--help"):
             usage()
             usage()
@@ -68,7 +70,6 @@ def main():
         exit(1)
         exit(1)
 
 
     try:
     try:
-
         isc.auth.sqlite3_ds.load(dbfile, zone, isc.auth.master.zonedata, zf)
         isc.auth.sqlite3_ds.load(dbfile, zone, isc.auth.master.zonedata, zf)
     except Exception as e:
     except Exception as e:
         print("Error loading database: " + str(e))
         print("Error loading database: " + str(e))

+ 134 - 0
src/bin/loadzone/b10-loadzone.xml

@@ -0,0 +1,134 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+  <refentryinfo>
+    <date>March 8, 2010</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-loadzone</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-loadzone</refname>
+    <refpurpose>Load DNS Zone File</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2010</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-loadzone</command>
+      <arg><option>-d <replaceable class="parameter">database</replaceable></option></arg>
+      <arg><option>-o <replaceable class="parameter">origin</replaceable></option></arg>
+      <arg chose="req">filename</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-loadzone</command> utility
+      loads a RFC 1035 style DNS master zone file and stores it
+      in a BIND 10 ready data source format.
+      Master files are text files that contain DNS Resource Records
+      in text form.
+    </para>
+    <note><simpara>Currently only the SQLITE3 data source is supported.
+    </simpara></note>
+
+    <para>
+    Some control entries (aka directives) are supported.
+    $ORIGIN is followed by a domain name and is used to define the
+    relative domain name.
+    $INCLUDE is followed by a filename to load.
+<!-- TODO: and optionally a
+    domain name used to set the relative domain name origin. -->
+    The previous origin is restored after the file is included.
+<!-- the current domain name is also restored -->
+    $TTL is followed by a time-to-live value which is used
+    by following records that don't have their TTL set.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <variablelist>
+
+      <varlistentry>
+        <term>-d <replaceable class="parameter">database</replaceable> </term>
+<listitem><para>
+  Defines the filename for the database.
+          The default is <filename>/tmp/zone.sqlite3</filename>.
+<!-- TODO: fix filename -->
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-o <replaceable class="parameter">origin</replaceable></term>
+        <listitem><para>
+          Defines the default origin for the zone file records.
+        </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+    <para><filename></filename>
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHORS</title>
+    <para>
+      The <command>b10-loadzone</command> tool was initial written
+      by Evan Hunt of ISC.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 14 - 0
src/bin/xfrin/Makefile.am

@@ -0,0 +1,14 @@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+pkglibexec_SCRIPTS = b10-xfrin
+
+b10_xfrindir = $(DESTDIR)$(pkgdatadir)
+b10_xfrin_DATA = xfrin.spec
+
+CLEANFILES=	b10-xfrin
+
+# TODO: does this need $$(DESTDIR) also?
+# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
+b10-xfrin: xfrin.py
+	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" xfrin.py >$@
+	chmod a+x $@

+ 12 - 0
src/bin/xfrin/run_b10-xfrin.sh.in

@@ -0,0 +1,12 @@
+#! /bin/sh
+
+PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
+export PYTHON_EXEC
+
+MYPATH_PATH=@abs_top_builddir@/src/bin/xfrin
+PYTHONPATH=@abs_top_srcdir@/src/lib/python
+export PYTHONPATH
+
+cd ${MYPATH_PATH}
+${PYTHON_EXEC} b10-xfrin
+

+ 12 - 0
src/bin/xfrin/unittest/xfrin_test.in

@@ -0,0 +1,12 @@
+#! /bin/sh
+
+PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
+export PYTHON_EXEC
+
+TEST_PATH=@abs_top_srcdir@/src/bin/xfrin/unittest
+PYTHONPATH=@abs_top_srcdir@/src/bin/xfrin:@abs_top_srcdir@/src/lib/python
+export PYTHONPATH
+
+cd ${TEST_PATH}
+exec ${PYTHON_EXEC} -O xfrin_test.py $*
+

+ 45 - 0
src/bin/xfrin/unittest/xfrin_test.py

@@ -0,0 +1,45 @@
+# Copyright (C) 2009  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.
+
+
+import unittest
+import socket
+from xfrin import *
+
+# Rewrite the class for unittest.
+class MyXfrin(Xfrin):
+    def __init__(self):
+        pass
+
+
+class TestXfrin(unittest.TestCase):
+    def test_parse_cmd_params(self):
+        xfr = MyXfrin()
+        args = {}
+        args['zone_name'] = 'sd.cn.'
+        args['port'] = '12345'
+        args['master'] = '218.241.108.122'
+
+        name, master, port, db_file = xfr._parse_cmd_params(args)
+        self.assertEqual(port, 12345)
+        self.assertEqual(name, 'sd.cn.')
+        self.assertEqual(master, '218.241.108.122')
+        self.assertEqual(db_file, '/tmp/zone.sqlite3')
+
+
+
+
+if __name__== "__main__":
+    unittest.main()

+ 447 - 0
src/bin/xfrin/xfrin.py.in

@@ -0,0 +1,447 @@
+#!@PYTHON@
+
+# 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.
+
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import os
+import signal
+import isc
+import asyncore
+import struct
+import threading
+import socket
+import random
+from isc.config.ccsession import *
+try:
+    from bind10_message import *
+except:
+    pass
+
+# If B10_FROM_SOURCE is set in the environment, we use data files
+# from a directory relative to that, otherwise we use the ones
+# installed on the system
+if "B10_FROM_SOURCE" in os.environ:
+    SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/xfrin"
+else:
+    PREFIX = "@prefix@"
+    DATAROOTDIR = "@datarootdir@"
+    SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
+
+
+__version__ = 'BIND10'
+# define xfrin rcode
+XFRIN_OK = 0
+XFRIN_RECV_TIMEOUT = 1
+XFRIN_NO_NEWDATA = 2
+XFRIN_QUOTA_ERROR = 3
+XFRIN_IS_DOING = 4
+
+# define xfrin state
+XFRIN_QUERY_SOA = 1
+XFRIN_FIRST_AXFR = 2
+XFRIN_FIRST_IXFR = 3
+
+class XfrinException(Exception): 
+    pass
+
+
+class XfrinConnection(asyncore.dispatcher):
+    '''Do xfrin in this class. '''    
+
+    def __init__(self, zone_name, db_file, 
+                 shutdown_event,
+                 master_addr, 
+                 port = 53, 
+                 check_soa = True, 
+                 idle_timeout = 60):
+        ''' idle_timeout: max idle time for read data from socket.
+            db_file: specify the data source file.
+            check_soa: when it's true, check soa first before sending xfr query
+        '''
+        asyncore.dispatcher.__init__(self)
+        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._zone_name = zone_name
+        self._db_file = db_file
+        self._records = []
+        self._idle_timeout = idle_timeout
+        self.setblocking(1)
+        self.connect((master_addr, port))
+        self._shutdown_event = shutdown_event
+
+
+    def _create_query(self, query_type):
+        '''Create dns query message. '''
+        msg = message(message_mode.RENDER)
+        query_id = random.randint(1, 0xFFFF)
+        self._query_id = query_id
+        msg.set_qid(query_id)
+        msg.set_opcode(op_code.QUERY())
+        msg.set_rcode(rcode.NOERROR())
+        query_question = question(name(self._zone_name), rr_class.IN(), query_type)
+        msg.add_question(query_question)
+        return msg
+
+    def _send_data(self, data):
+        size = len(data)
+        total_count = 0
+        while total_count < size:
+            count = self.send(data[total_count:])
+            total_count += count
+
+
+    def _send_query(self, query_type):
+        '''Send query message over TCP. '''
+        msg = self._create_query(query_type)
+        obuf = output_buffer(0)
+        render = message_render(obuf)
+        msg.to_wire(render)
+        header_len = struct.pack('H', socket.htons(obuf.get_length()))
+
+        self._send_data(header_len)
+        self._send_data(obuf.get_data())
+
+
+    def _get_request_response(self, size):
+        recv_size = 0
+        data = b''
+        while recv_size < size:
+            self._recv_time_out = True
+            self._need_recv_size = size - recv_size
+            asyncore.loop(self._idle_timeout, count = 1)
+            if self._recv_time_out:
+                raise XfrinException('receive data from socket time out.')
+
+            recv_size += self._recvd_size
+            data += self._recvd_data
+
+        return data
+
+
+    def handle_read(self):
+        '''Read query's response from socket. '''
+        self._recvd_data = self.recv(self._need_recv_size)
+        self._recvd_size = len(self._recvd_data)
+        self._recv_time_out = False
+
+
+    def _check_soa_serial(self):
+        ''' Compare the soa serial, if soa serial in master is less than
+        the soa serial in local, Finish xfrin.
+        False: soa serial in master is less or equal to the local one.
+        True: soa serial in master is bigger
+        '''
+        self._send_query(rr_type.SOA())
+        data_size = self._get_request_response(2)
+        soa_reply = self._get_request_response(int(data_size))
+        #TODO, need select soa record from data source then compare the two 
+        #serial 
+        return XFRIN_OK
+
+
+    def do_xfrin(self, check_soa, ixfr_first = False):
+        try:
+            ret = XFRIN_OK
+            if check_soa:
+                ret =  self._check_soa_serial()
+
+            print('[xfrin] transfer of \'%s\': AXFR started' % self._zone_name)
+            if ret == XFRIN_OK:    
+                self._send_query(rr_type.AXFR())
+                ret = self._handle_xfrin_response()
+
+            self._insert_record_to_sqlite3(self._records)
+            print('[xfrin] transfer of \'%s\' AXFR ended' % self._zone_name)
+        except XfrinException as e:
+            print(e)
+            print('[xfrin] Error happened during xfrin!')
+            #TODO, recover data source. 
+        finally:
+           self.close()
+
+        return ret
+    
+    def _check_response_status(self, msg):
+        #TODO, check more?
+        if msg.get_rcode() != rcode.NOERROR():
+            raise XfrinException('error response: ')
+
+        if not msg.get_header_flag(message_flag.QR()):
+            raise XfrinException('response is not a response ')
+
+        if msg.get_qid() != self._query_id:
+            raise XfrinException('bad query id')
+
+        if msg.get_rr_count(section.ANSWER()) == 0:
+            raise XfrinException('answer section is empty')
+
+        if msg.get_rr_count(section.QUESTION()) > 1:
+            raise XfrinException('query section count greater than 1')
+
+
+    def _handle_answer_section(self, rrset_iter):
+        soa_count = 0
+        while not rrset_iter.is_last():
+            rrset = rrset_iter.get_rrset()
+            rrset_iter.next()
+            rrset_name = rrset.get_name().to_text()
+            rrset_ttl = int(rrset.get_ttl().to_text())
+            rrset_class = rrset.get_class().to_text()
+            rrset_type = rrset.get_type().to_text()
+
+            rdata_iter = rrset.get_rdata_iterator()
+            rdata_iter.first()
+            while not rdata_iter.is_last():
+                # Count the soa record count
+                if rrset.get_type() == rr_type.SOA():
+                    soa_count += 1
+
+                rdata_text = rdata_iter.get_current().to_text()
+                rr_data = (rrset_name, rrset_ttl, rrset_class, rrset_type, rdata_text)
+                self._records.append(rr_data)
+                #self._insert_record_to_sqlite3(rr_data) 
+                rdata_iter.next()
+        
+        return soa_count 
+
+
+    def _handle_xfrin_response(self):
+        soa_count = 0
+        while True:
+            data_len = self._get_request_response(2)
+            msg_len = socket.htons(struct.unpack('H', data_len)[0])
+            recvdata = self._get_request_response(msg_len)
+            msg = message(message_mode.PARSE)
+            msg.from_wire(input_buffer(recvdata))
+            self._check_response_status(msg)
+            
+            rrset_iter = section_iter(msg, section.ANSWER())
+            soa_count += self._handle_answer_section(rrset_iter)
+            if soa_count == 2:
+                return XFRIN_OK
+            
+            if self._shutdown_event.is_set():
+                #Check if xfrin process is shutdown.
+                #TODO, xfrin may be blocked in one loop. 
+                raise XfrinException('xfrin is forced to stop')
+
+        return XFRIN_OK
+
+    def _insert_record_to_sqlite3(self, rrs):
+        ''' The interface provided by sqlite3_ds only support insert all the records
+        at the end of AXFR'''
+        isc.auth.sqlite3_ds.load(self._db_file, self._zone_name, rrs)
+
+
+    def writable(self):
+        '''Ignore the writable socket. '''
+        return False
+
+
+    def log_info(self, msg, type = ''):
+        # Overwrite the log function, log nothing
+        pass
+
+
+def process_xfrin(xfrin_recorder, zone_name, db_file, 
+                  shutdown_event, master_addr, port, check_soa):
+    xfrin_recorder.increment(name)
+    try:
+        conn = XfrinConnection(zone_name, db_file, shutdown_event, 
+                           master_addr, int(port), check_soa)
+        conn.do_xfrin(False)
+    except Exception as e:
+        print('[xfrin] Error happened:', e)
+
+    xfrin_recorder.decrement(zone_name)
+
+
+class XfrinRecorder():
+    def __init__(self):
+        self._lock = threading.Lock()
+        self._zones = []
+
+    def increment(self, zone_name):
+        self._lock.acquire()
+        self._zones.append(zone_name)
+        self._lock.release()
+
+    def decrement(self, zone_name):
+        self._lock.acquire()
+        if zone_name in self._zones:
+            self._zones.remove(zone_name)
+        self._lock.release()
+
+    def xfrin_in_progress(self, zone_name):
+        self._lock.acquire()
+        ret = zone_name in self._zones
+        self._lock.release()
+        return ret
+
+    def count(self):
+        self._lock.acquire()
+        ret = len(self._zones)
+        self._lock.release()
+        return ret
+
+class Xfrin():
+    def __init__(self):
+        self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
+        self._cc.start()
+        self._max_transfers_in = 10
+        self.recorder = XfrinRecorder()
+        self._shutdown_event = threading.Event()
+
+
+    def config_handler(self, new_config):
+        answer = create_answer(0, 'ok')
+
+
+    def _print_settings(self):
+        full_config = self._cc.get_full_config()
+        for item in full_config:
+            print(item + ":" + str(full_config[item]))
+
+
+    def shutdown(self):
+        ''' shutdown the xfrin process. the thread which is doing xfrin should be 
+        terminated.
+        '''
+        self._shutdown_event.set()
+        main_thread = threading.currentThread()
+        for th in threading.enumerate():
+            if th is main_thread:
+                continue
+            th.join()
+
+
+    def command_handler(self, command, args):
+        answer = create_answer(0)
+        cmd = command
+        try:
+            if cmd == 'print_message':
+                print(args)
+
+            elif cmd == 'print_settings':
+                self._print_settings()  
+
+            elif cmd == 'shutdown':
+                self._shutdown_event.set()
+
+            elif cmd == 'retransfer':
+                zone_name, master, port, db_file = self._parse_cmd_params(args)
+                ret = self.xfrin_start(zone_name, db_file, master, port, False)
+                answer = create_answer(ret[0], ret[1])
+
+            elif cmd == 'refresh':
+                zone_name, master, port, db_file = self._parse_cmd_params(args)
+                ret = self.xfrin_start(zone_name, db_file, master, port)
+                answer = create_answer(ret[0], ret[1])
+
+        except XfrinException as err:
+            answer = create_answer(1, str(err))
+
+        return answer
+
+    def _parse_cmd_params(self, args):
+        zone_name = args['zone_name']
+        master = args['master']
+        check_addr(master)
+        port = int(args.get('port'))
+        if port:
+            check_port(port)
+        else:
+            port = 53
+
+        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 = '/tmp/zone.sqlite3'
+
+        return (zone_name, master, port, db_file)
+            
+
+    def startup(self):
+        while not self._shutdown_event.is_set():
+            self._cc.check_command()
+
+
+    def xfrin_start(self, zone_name, db_file, master_addr, 
+                    port = 53, 
+                    check_soa = True):
+        if "bind10_message" not in sys.modules:
+            return (1, "xfrin failed, can't load dns message python library: 'bind10_message'")
+
+        # check max_transfer_in, else return quota error
+        if self.recorder.count() >= self._max_transfers_in:
+            return (1, 'xfrin quota error')
+
+        if self.recorder.xfrin_in_progress(zone_name):
+            return (1, 'zone xfrin is in progress')
+
+        xfrin_thread = threading.Thread(target = process_xfrin, 
+                                        args = (self.recorder, 
+                                                zone_name, 
+                                                db_file, 
+                                                self._shutdown_event,
+                                                master_addr, 
+                                                port, check_soa))
+        xfrin_thread.start()
+        return (0, 'zone xfrin is started')
+
+
+xfrind = None
+
+def signal_handler(signal, frame):
+    if xfrind:
+        xfrind.shutdown()
+    sys.exit(0)
+
+def set_signal_handler():
+    signal.signal(signal.SIGTERM, signal_handler)
+    signal.signal(signal.SIGINT, signal_handler)
+
+def check_port(value):
+    if (value < 0) or (value > 65535):
+        raise XfrinException('requires a port number (0-65535)')
+
+def check_addr(ipstr):
+    ip_family = socket.AF_INET
+    if (ipstr.find(':') != -1):
+        ip_family = socket.AF_INET6
+
+    try:
+        socket.inet_pton(ip_family, ipstr)
+    except:
+        raise XfrinException("%s invalid ip address" % ipstr)
+
+
+if __name__ == '__main__':
+    try:
+        set_signal_handler()
+        xfrind = Xfrin()
+        xfrind.startup()
+    except KeyboardInterrupt:
+        print("exit http server")
+    except Exception as e:
+        print(e)
+
+    if xfrind:
+        xfrind.shutdown()
+
+
+

+ 66 - 0
src/bin/xfrin/xfrin.spec

@@ -0,0 +1,66 @@
+{
+  "module_spec": {
+    "module_name": "Xfrin",
+    "config_data": [
+      {
+        "item_name": "transfers_in",
+        "item_type": "integer",
+        "item_optional": False,
+        "item_default": 10
+      }
+    ],
+    "commands": [
+      {
+        "command_name": "print_message",
+        "command_description": "Print the given message to stdout",
+        "command_args": [ {
+          "item_name": "message",
+          "item_type": "string",
+          "item_optional": False,
+          "item_default": ""
+        } ]
+      },
+      {
+        "command_name": "print_settings",
+        "command_description": "Print some_string and some_int to stdout",
+        "command_args": []
+      },
+      {
+        'command_name': 'retransfer',
+        "command_description": 'retransfer a single zone without checking zone serial number',
+        'command_args': [ {
+            "item_name": "zone_name",
+            "item_type": "string",
+            "item_optional": False,
+            "item_default": ""
+          },
+          {
+            "item_name": "master",
+            "item_type": "string",
+            "item_optional": False,
+            "item_default": ""
+          },
+          {
+            "item_name": "port",
+            "item_type": "integer",
+            "item_optional": True,
+            "item_default": 53
+          },
+          {
+            "item_name": "db_file",
+            "item_type": "string",
+            "item_optional": True,
+            "item_default": '/tmp/zone.sqlite3'
+          }
+        ]
+      },
+      {
+        "command_name": "shutdown",
+        "command_description": "Shut down BIND 10",
+        "command_args": []
+      }
+    ]
+  }
+}
+
+

+ 27 - 35
src/lib/auth/data_source_sqlite3.cc

@@ -185,10 +185,15 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
                             RRsetList& target, const Name* zonename,
                             RRsetList& target, const Name* zonename,
                             const Mode mode, uint32_t& flags) const
                             const Mode mode, uint32_t& flags) const
 {
 {
-    const string s_name = name.toText();
-    const char* const c_name = s_name.c_str();
-    sqlite3_stmt* query;
+    flags = 0;
+    int zone_id = (zonename == NULL) ? findClosest(name, NULL) :
+        findClosest(*zonename, NULL);
+    if (zone_id < 0) {
+        flags = NO_SUCH_ZONE;
+        return (0);
+    }
 
 
+    sqlite3_stmt* query;
     switch (mode) {
     switch (mode) {
     case ADDRESS:
     case ADDRESS:
         query = q_addrs;
         query = q_addrs;
@@ -204,15 +209,6 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
         }
         }
         break;
         break;
     }
     }
-
-    flags = 0;
-
-    int zone_id = (zonename == NULL) ? findClosest(c_name, NULL) :
-        findClosest(zonename->toText().c_str(), NULL);
-    if (zone_id < 0) {
-        flags = NO_SUCH_ZONE;
-        return (0);
-    }
     
     
     sqlite3_reset(query);
     sqlite3_reset(query);
     sqlite3_clear_bindings(query);
     sqlite3_clear_bindings(query);
@@ -222,7 +218,8 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
     if (rc != SQLITE_OK) {
     if (rc != SQLITE_OK) {
         throw("Could not bind 1 (query)");
         throw("Could not bind 1 (query)");
     }
     }
-    rc = sqlite3_bind_text(query, 2, c_name, -1, SQLITE_STATIC);
+    const string s_name = name.toText();
+    rc = sqlite3_bind_text(query, 2, s_name.c_str(), -1, SQLITE_STATIC);
     if (rc != SQLITE_OK) {
     if (rc != SQLITE_OK) {
         throw("Could not bind 2 (query)");
         throw("Could not bind 2 (query)");
     }
     }
@@ -282,30 +279,23 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
 //  longest match found.
 //  longest match found.
 //
 //
 int
 int
-Sqlite3DataSrc::findClosest(const char* const name,
-                            const char** position) const
-{
-    const char* current = name;
-
-    while (*current != 0) {
-        const int rc = hasExactZone(current);
+Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
+    const unsigned int nlabels = name.getLabelCount();
+    for (unsigned int i = 0; i < nlabels; ++i) {
+        Name matchname(name.split(i, nlabels - i));
+        const int rc = hasExactZone(matchname.toText().c_str());
         if (rc >= 0) {
         if (rc >= 0) {
             if (position != NULL) {
             if (position != NULL) {
-                *position = current;
+                *position = i;
             }
             }
             return (rc);
             return (rc);
         }
         }
-        while (*current != '.' && *current != 0) {
-            ++current;
-        }
-        if (*current == '.') {
-            ++current;
-        }
     }
     }
 
 
     return (-1);
     return (-1);
 }
 }
 
 
+
 void
 void
 Sqlite3DataSrc::loadVersion(void) {
 Sqlite3DataSrc::loadVersion(void) {
     sqlite3_stmt* prepared = prepare("SELECT version FROM schema_version");
     sqlite3_stmt* prepared = prepare("SELECT version FROM schema_version");
@@ -498,18 +488,20 @@ Sqlite3DataSrc::init(const isc::data::ElementPtr config) {
 
 
 void
 void
 Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
 Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
-                                     const RRClass& qclass) const {
-    const char* position = NULL;
-    
+                                     const RRClass& qclass) const
+{
     if (qclass != getClass()) {
     if (qclass != getClass()) {
         return;
         return;
     }
     }
 
 
-    if (findClosest(match.qname().toText().c_str(), &position) == -1) {
+    unsigned int position;
+    if (findClosest(match.qname(), &position) == -1) {
         return;
         return;
     }
     }
 
 
-    match.update(*this, Name(position));
+    match.update(*this, match.qname().split(position,
+                                            match.qname().getLabelCount() -
+                                            position));
 }
 }
 
 
 DataSrc::Result
 DataSrc::Result
@@ -519,8 +511,8 @@ Sqlite3DataSrc::findPreviousName(const Query& q,
                                  const Name* zonename) const
                                  const Name* zonename) const
 {
 {
     int zone_id = (zonename == NULL) ?
     int zone_id = (zonename == NULL) ?
-        findClosest(qname.toText().c_str(), NULL) :
-        findClosest(zonename->toText().c_str(), NULL);
+        findClosest(qname, NULL) :
+        findClosest(*zonename, NULL);
 
 
     if (zone_id < 0) {
     if (zone_id < 0) {
         return (ERROR);
         return (ERROR);
@@ -557,7 +549,7 @@ Sqlite3DataSrc::findCoveringNSEC3(const Query& q,
                                   string& hashstr,
                                   string& hashstr,
                                   RRsetList& target) const
                                   RRsetList& target) const
 {
 {
-    int zone_id = findClosest(zonename.toText().c_str(), NULL);
+    int zone_id = findClosest(zonename, NULL);
     if (zone_id < 0) {
     if (zone_id < 0) {
         return (ERROR);
         return (ERROR);
     }
     }

+ 1 - 1
src/lib/auth/data_source_sqlite3.h

@@ -121,7 +121,7 @@ private:
     int findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
     int findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
                     isc::dns::RRsetList& target, const isc::dns::Name* zonename,
                     isc::dns::RRsetList& target, const isc::dns::Name* zonename,
                     const Mode mode, uint32_t& flags) const;
                     const Mode mode, uint32_t& flags) const;
-    int findClosest(const char *name, const char **position) const;
+    int findClosest(const isc::dns::Name& name, unsigned int* position) const;
     void loadVersion(void);
     void loadVersion(void);
     void setupPreparedStatements(void);
     void setupPreparedStatements(void);
     void execSetupQuery(const char *query);
     void execSetupQuery(const char *query);

+ 21 - 10
src/lib/auth/data_source_sqlite3_unittest.cc

@@ -46,7 +46,9 @@ static ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
                      "{ \"database_file\": \"testdata/test.sqlite3\"}");
                      "{ \"database_file\": \"testdata/test.sqlite3\"}");
 static ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
 static ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
                      "{ \"database_file\": \"testdata/test2.sqlite3\"}");
                      "{ \"database_file\": \"testdata/test2.sqlite3\"}");
-// The following file must be non existent and mutt be "creatable";
+static ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::createFromString(
+                     "{ \"database_file\": \"testdata/test-root.sqlite3\"}");
+// The following file must be non existent and must be non"creatable";
 // the sqlite3 library will try to create a new DB file if it doesn't exist,
 // the sqlite3 library will try to create a new DB file if it doesn't exist,
 // so to test a failure case the create operation should also fail.
 // so to test a failure case the create operation should also fail.
 // The "nodir", a non existent directory, is inserted for this purpose.
 // The "nodir", a non existent directory, is inserted for this purpose.
@@ -342,9 +344,9 @@ checkFind(FindMode mode, const Sqlite3DataSrc& data_source, const Query& query,
     answers.push_back(&expected_data);
     answers.push_back(&expected_data);
     signatures.push_back(expected_sig_data);
     signatures.push_back(expected_sig_data);
 
 
-    checkFind(mode, data_source, query, expected_name, zone_name, expected_class,
-              expected_type, ttls, expected_flags, types, answers,
-              signatures);
+    checkFind(mode, data_source, query, expected_name, zone_name,
+              expected_class, expected_type, ttls, expected_flags, types,
+              answers, signatures);
 }
 }
 
 
 TEST_F(Sqlite3DataSourceTest, close) {
 TEST_F(Sqlite3DataSourceTest, close) {
@@ -355,11 +357,10 @@ TEST_F(Sqlite3DataSourceTest, reOpen) {
     // Replace the data with a totally different zone.  This should succeed,
     // Replace the data with a totally different zone.  This should succeed,
     // and shouldn't match any names in the previously managed domains.
     // and shouldn't match any names in the previously managed domains.
     EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
     EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-
     EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
     EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
 
 
     NameMatch name_match(www_name);
     NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
 }
 }
@@ -371,23 +372,33 @@ TEST_F(Sqlite3DataSourceTest, openFail) {
 
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
     NameMatch name_match(www_name);
     NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(zone_name, *name_match.closestName());
     EXPECT_EQ(zone_name, *name_match.closestName());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
 }
 }
 
 
+TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
+    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
+    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
+
+    NameMatch name_match(Name("org."));
+    data_source.findClosestEnclosure(name_match, rrclass);
+    EXPECT_EQ(Name("."), *name_match.closestName());
+    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+}
+
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
     // The search name exists both in the parent and child zones, but
     // The search name exists both in the parent and child zones, but
     // child has a better match.
     // child has a better match.
     NameMatch name_match(child_name);
     NameMatch name_match(child_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(child_name, *name_match.closestName());
     EXPECT_EQ(child_name, *name_match.closestName());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
 }
 }
 
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
     NameMatch name_match(nomatch_name);
     NameMatch name_match(nomatch_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
 }
 }
@@ -696,7 +707,7 @@ TEST_F(Sqlite3DataSourceTest, findRRsetNSEC3) {
               data_source.findCoveringNSEC3(*query, nsec3_zonename,
               data_source.findCoveringNSEC3(*query, nsec3_zonename,
                                             hashstr, result_sets));
                                             hashstr, result_sets));
     RRsetList::iterator it = result_sets.begin();
     RRsetList::iterator it = result_sets.begin();
-    checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), RRClass::IN(),
+    checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), rrclass,
                RRType::NSEC3(), RRTTL(7200), nsec3_data, &nsec3_sig_data);
                RRType::NSEC3(), RRTTL(7200), nsec3_data, &nsec3_sig_data);
     ++it;
     ++it;
     EXPECT_TRUE(it == result_sets.end());
     EXPECT_TRUE(it == result_sets.end());

+ 36 - 0
src/lib/auth/testdata/root.zone

@@ -0,0 +1,36 @@
+.			86400	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2010030802 1800 900 604800 86400
+
+.			518400	IN	NS	a.root-servers.net.
+.			518400	IN	NS	e.root-servers.net.
+.			518400	IN	NS	g.root-servers.net.
+.			518400	IN	NS	l.root-servers.net.
+.			518400	IN	NS	f.root-servers.net.
+.			518400	IN	NS	k.root-servers.net.
+.			518400	IN	NS	m.root-servers.net.
+.			518400	IN	NS	d.root-servers.net.
+.			518400	IN	NS	h.root-servers.net.
+.			518400	IN	NS	c.root-servers.net.
+.			518400	IN	NS	b.root-servers.net.
+.			518400	IN	NS	i.root-servers.net.
+.			518400	IN	NS	j.root-servers.net.
+
+a.root-servers.net.	3600000	IN	A	198.41.0.4
+a.root-servers.net.	3600000	IN	AAAA	2001:503:ba3e::2:30
+b.root-servers.net.	3600000	IN	A	192.228.79.201
+c.root-servers.net.	3600000	IN	A	192.33.4.12
+d.root-servers.net.	3600000	IN	A	128.8.10.90
+e.root-servers.net.	3600000	IN	A	192.203.230.10
+f.root-servers.net.	3600000	IN	A	192.5.5.241
+f.root-servers.net.	3600000	IN	AAAA	2001:500:2f::f
+g.root-servers.net.	3600000	IN	A	192.112.36.4
+h.root-servers.net.	3600000	IN	A	128.63.2.53
+h.root-servers.net.	3600000	IN	AAAA	2001:500:1::803f:235
+i.root-servers.net.	3600000	IN	A	192.36.148.17
+j.root-servers.net.	3600000	IN	A	192.58.128.30
+j.root-servers.net.	3600000	IN	AAAA	2001:503:c27::2:30
+k.root-servers.net.	3600000	IN	A	193.0.14.129
+k.root-servers.net.	3600000	IN	AAAA	2001:7fd::1
+l.root-servers.net.	3600000	IN	A	199.7.83.42
+l.root-servers.net.	3600000	IN	AAAA	2001:500:3::42
+m.root-servers.net.	3600000	IN	A	202.12.27.33
+m.root-servers.net.	3600000	IN	AAAA	2001:dc3::35

BIN
src/lib/auth/testdata/test-root.sqlite3


+ 81 - 6
src/lib/config/ccsession.cc

@@ -42,7 +42,6 @@
 #include <cc/session.h>
 #include <cc/session.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
-//#include "common.h"
 #include "ccsession.h"
 #include "ccsession.h"
 #include "config.h"
 #include "config.h"
 
 
@@ -143,10 +142,11 @@ parseCommand(ElementPtr& arg, const ElementPtr command)
     return "";
     return "";
 }
 }
 
 
-void
+ModuleSpec
 ModuleCCSession::read_module_specification(const std::string& filename) {
 ModuleCCSession::read_module_specification(const std::string& filename) {
     std::ifstream file;
     std::ifstream file;
-
+    ModuleSpec module_spec;
+    
     // this file should be declared in a @something@ directive
     // this file should be declared in a @something@ directive
     file.open(filename.c_str());
     file.open(filename.c_str());
     if (!file) {
     if (!file) {
@@ -155,7 +155,7 @@ ModuleCCSession::read_module_specification(const std::string& filename) {
     }
     }
 
 
     try {
     try {
-        module_specification_ = moduleSpecFromFile(file, true);
+        module_spec = moduleSpecFromFile(file, true);
     } catch (ParseError pe) {
     } catch (ParseError pe) {
         cout << "Error parsing module specification file: " << pe.what() << endl;
         cout << "Error parsing module specification file: " << pe.what() << endl;
         exit(1);
         exit(1);
@@ -164,6 +164,7 @@ ModuleCCSession::read_module_specification(const std::string& filename) {
         exit(1);
         exit(1);
     }
     }
     file.close();
     file.close();
+    return module_spec;
 }
 }
 
 
 #ifdef HAVE_BOOSTLIB
 #ifdef HAVE_BOOSTLIB
@@ -211,7 +212,7 @@ ModuleCCSession::init(
         const std::string& command, const isc::data::ElementPtr args)
         const std::string& command, const isc::data::ElementPtr args)
     ) throw (isc::cc::SessionError)
     ) throw (isc::cc::SessionError)
 {
 {
-    read_module_specification(spec_file_name);
+    module_specification_ = read_module_specification(spec_file_name);
     sleep(1);
     sleep(1);
 
 
     module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
     module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
@@ -295,6 +296,7 @@ ModuleCCSession::check_command()
 {
 {
     ElementPtr cmd, routing, data;
     ElementPtr cmd, routing, data;
     if (session_.group_recvmsg(routing, data, true)) {
     if (session_.group_recvmsg(routing, data, true)) {
+        
         /* ignore result messages (in case we're out of sync, to prevent
         /* ignore result messages (in case we're out of sync, to prevent
          * pingpongs */
          * pingpongs */
         if (!data->getType() == Element::map || data->contains("result")) {
         if (!data->getType() == Element::map || data->contains("result")) {
@@ -304,7 +306,16 @@ ModuleCCSession::check_command()
         std::string cmd_str = parseCommand(arg, data);
         std::string cmd_str = parseCommand(arg, data);
         ElementPtr answer;
         ElementPtr answer;
         if (cmd_str == "config_update") {
         if (cmd_str == "config_update") {
-            answer = handleConfigUpdate(arg);
+            std::string target_module = routing->get("group")->stringValue();
+            if (target_module == module_name_) {
+                answer = handleConfigUpdate(arg);
+            } else {
+                // ok this update is not for us, if we have this module
+                // in our remote config list, update that
+                updateRemoteConfig(target_module, arg);
+                // we're not supposed to answer to this, so return
+                return 0;
+            }
         } else {
         } else {
             if (command_handler_) {
             if (command_handler_) {
                 answer = command_handler_(cmd_str, arg);
                 answer = command_handler_(cmd_str, arg);
@@ -318,5 +329,69 @@ ModuleCCSession::check_command()
     return 0;
     return 0;
 }
 }
 
 
+std::string
+ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
+{
+    ModuleSpec rmod_spec = read_module_specification(spec_file_name);
+    std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
+    ConfigData rmod_config = ConfigData(rmod_spec);
+    session_.subscribe(module_name);
+
+    // Get the current configuration values for that module
+    ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
+    ElementPtr env, answer;
+    int rcode;
+    
+    session_.group_sendmsg(cmd, "ConfigManager");
+    session_.group_recvmsg(env, answer, false);
+    ElementPtr new_config = parseAnswer(rcode, answer);
+    if (rcode == 0) {
+        rmod_config.setLocalConfig(new_config);
+    } else {
+        isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+    }
+
+    // all ok, add it
+    remote_module_configs_[module_name] = rmod_config;
+    return module_name;
+}
+
+void
+ModuleCCSession::removeRemoteConfig(const std::string& module_name)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        remote_module_configs_.erase(it);
+        session_.unsubscribe(module_name);
+    }
+}
+
+ElementPtr
+ModuleCCSession::getRemoteConfigValue(const std::string& module_name, const std::string& identifier)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        return remote_module_configs_[module_name].getValue(identifier);
+    } else {
+        isc_throw(CCSessionError, "Remote module " + module_name + " not found.");
+    }
+}
+
+void
+ModuleCCSession::updateRemoteConfig(const std::string& module_name, ElementPtr new_config)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        ElementPtr rconf = (*it).second.getLocalConfig();
+        isc::data::merge(rconf, new_config);
+    }
+}
+
 }
 }
 }
 }

+ 42 - 1
src/lib/config/ccsession.h

@@ -101,6 +101,44 @@ public:
      */
      */
     void set_command_handler(isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)) { command_handler_ = command_handler; };
     void set_command_handler(isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)) { command_handler_ = command_handler; };
 
 
+    /**
+     * Gives access to the configuration values of a different module
+     * Once this function has been called with the name of the specification
+     * file of the module you want the configuration of, you can use
+     * \c getRemoteConfigValue() to get a specific setting.
+     * Changes are automatically updated, but you cannot specify handlers
+     * for those changes, must use \c getRemoteConfigValue() to get a value
+     * This function will subscribe to the relevant module channel.
+     *
+     * \param spec_file_name The path to the specification file of
+     *                       the module we want to have configuration
+     *                       values from
+     * \return The name of the module specified in the given specification
+     *         file
+     */
+    std::string addRemoteConfig(const std::string& spec_file_name);
+
+    /**
+     * Removes the module with the given name from the remote config
+     * settings. If the module was not added with \c addRemoteConfig(),
+     * nothing happens.
+     */
+    void removeRemoteConfig(const std::string& module_name);
+
+    /**
+     * Returns the current configuration value for the given module
+     * name at the given identifier. See \c ConfigData::getValue() for
+     * more details.
+     * Raises a ModuleCCSessionError if the module name is unknown
+     * Raises a DataNotFoundError if the identifier does not exist
+     * in the specification.
+     *
+     * \param module_name The name of the module to get a config value for
+     * \param identifier The identifier of the config value
+     * \return The configuration setting at the given identifier
+     */
+    ElementPtr getRemoteConfigValue(const std::string& module_name, const std::string& identifier);
+    
 private:
 private:
     void init(
     void init(
         std::string spec_file_name,
         std::string spec_file_name,
@@ -109,7 +147,7 @@ private:
         isc::data::ElementPtr(*command_handler)(
         isc::data::ElementPtr(*command_handler)(
             const std::string& command, const isc::data::ElementPtr args)
             const std::string& command, const isc::data::ElementPtr args)
         ) throw (isc::cc::SessionError);
         ) throw (isc::cc::SessionError);
-    void read_module_specification(const std::string& filename);
+    ModuleSpec read_module_specification(const std::string& filename);
     void startCheck();
     void startCheck();
     
     
     std::string module_name_;
     std::string module_name_;
@@ -120,6 +158,9 @@ private:
 
 
     isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
     isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
     isc::data::ElementPtr(*command_handler_)(const std::string& command, const isc::data::ElementPtr args);
     isc::data::ElementPtr(*command_handler_)(const std::string& command, const isc::data::ElementPtr args);
+
+    std::map<std::string, ConfigData> remote_module_configs_;
+    void updateRemoteConfig(const std::string& module_name, ElementPtr new_config);
 };
 };
 
 
 ElementPtr createAnswer(const int rcode);
 ElementPtr createAnswer(const int rcode);

+ 10 - 0
src/lib/dns/Makefile.dat

@@ -0,0 +1,10 @@
+SRC = exceptions.cc name.cc messagerenderer.cc rrtype.cc rdata.cc rrclass.cc  rrsetlist.cc \
+	  rrttl.cc rrset.cc rrparamregistry.cc rdataclass.cc dnstime.cc hex.cc base64.cc question.cc message.cc
+CFLAGS = -I/usr/local/include -I/usr/local/include/python3.1 -I/root/trunk/src/lib/
+all : bind10_message.so message_python.cc
+
+bind10_message.so : message_python.o $(SRC)
+	g++ -shared -o bind10_message.so message_python.o $(CFLAGS) $(SRC) -L/usr/local/lib -lboost_python3
+
+message_python.o : message_python.cc
+	g++ -c -fPIC $(CFLAGS) message_python.cc -L/usr/local/lib -lboost_python3

+ 627 - 0
src/lib/dns/message_python.cc

@@ -0,0 +1,627 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: message_python.cc 2010-03-08 18:44:00 feng $
+
+#include <string>
+#include <boost/python.hpp>
+#include <boost/python/class.hpp>
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/return_internal_reference.hpp>
+#include <boost/python/copy_const_reference.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "exceptions/exceptions.h"
+#include "buffer.h"
+#include "name.h"
+#include "messagerenderer.h"
+#include "rrtype.h"
+#include "rrclass.h"
+#include "rrttl.h"
+#include "rrset.h"
+#include "rdata.h"
+#include "rdataclass.h"
+#include "rrsetlist.h"
+#include "question.h"
+#include "message.h"
+
+using namespace isc::dns;
+using namespace boost::python;
+
+#define DEFINE_EXCEPTION_TRANSLATOR(ex) \
+  void ex##_translator(const Exception &x){ PyErr_SetString(PyExc_UserWarning, x.what()); }
+
+#define REGISTER_EXCEPTION(ex) register_exception_translator<ex>(&ex##_translator)
+
+namespace
+{
+        using isc::Exception;
+        using isc::OutOfRange;
+        using isc::Unexpected;
+
+        DEFINE_EXCEPTION_TRANSLATOR(Exception)
+        DEFINE_EXCEPTION_TRANSLATOR(OutOfRange)
+        DEFINE_EXCEPTION_TRANSLATOR(Unexpected)
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidBufferPosition)
+        DEFINE_EXCEPTION_TRANSLATOR(EmptyLabel)
+        DEFINE_EXCEPTION_TRANSLATOR(TooLongName)
+        DEFINE_EXCEPTION_TRANSLATOR(TooLongLabel)
+        DEFINE_EXCEPTION_TRANSLATOR(BadLabelType)
+        DEFINE_EXCEPTION_TRANSLATOR(BadEscape)
+        DEFINE_EXCEPTION_TRANSLATOR(BadPointer)
+        DEFINE_EXCEPTION_TRANSLATOR(IncompleteName)
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidRRType)
+        DEFINE_EXCEPTION_TRANSLATOR(IncompleteRRType)
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidRRClass)
+        DEFINE_EXCEPTION_TRANSLATOR(IncompleteRRClass)
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidRRTTL)
+        DEFINE_EXCEPTION_TRANSLATOR(IncompleteRRTTL)
+        DEFINE_EXCEPTION_TRANSLATOR(EmptyRRset)
+        DEFINE_EXCEPTION_TRANSLATOR(DuplicateRRset)
+
+        using isc::dns::rdata::InvalidRdataLength;
+        using isc::dns::rdata::InvalidRdataText;
+        using isc::dns::rdata::CharStringTooLong;
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidRdataLength)
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidRdataText)
+        DEFINE_EXCEPTION_TRANSLATOR(CharStringTooLong)
+
+        DEFINE_EXCEPTION_TRANSLATOR(DNSProtocolError);
+        DEFINE_EXCEPTION_TRANSLATOR(DNSMessageFORMERR);
+        DEFINE_EXCEPTION_TRANSLATOR(DNSMessageBADVERS);
+        DEFINE_EXCEPTION_TRANSLATOR(MessageTooShort);
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidMessageSection);
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidMessageOperation);
+        DEFINE_EXCEPTION_TRANSLATOR(InvalidMessageUDPSize);
+
+
+        class PyInputBuffer : public InputBuffer
+        {
+            public:
+            PyInputBuffer(object bytes) : InputBuffer(0,0)
+            {
+                if (PyBytes_Check(bytes.ptr()))
+                    PyBytes_AsStringAndSize(bytes.ptr(), (char **)&data_, (int *)&len_);
+            }
+        };
+
+        class PyOutputBuffer : public OutputBuffer
+        {
+        public:
+            PyOutputBuffer(size_t len) : OutputBuffer(len){}
+            object getBytes() const
+            {
+                PyObject *bytes = PyBytes_FromStringAndSize((char *)getData(), getLength());
+                return object(handle<>(bytes));
+            }
+
+            void writeBytes(object bytes)
+            {
+                if (PyBytes_Check(bytes.ptr()))
+                {
+                    uint8_t *raw_data = NULL;
+                    int raw_data_len = 0;
+                    PyBytes_AsStringAndSize(bytes.ptr(), (char **)&raw_data, &raw_data_len);
+                    writeData(raw_data, raw_data_len);
+
+                }
+            }
+
+
+    };
+
+    class PyMessageRenderer : public MessageRenderer
+    {
+        public:
+            PyMessageRenderer(OutputBuffer &buffer) : MessageRenderer(buffer){}
+            object getBytes() const
+            {
+                PyObject *bytes = PyBytes_FromStringAndSize((char *)getData(), getLength());
+                return object(handle<>(bytes));
+            }
+
+            void writeBytes(object bytes)
+            {
+                if (PyBytes_Check(bytes.ptr()))
+                {
+                    uint8_t *raw_data = NULL;
+                    int raw_data_len = 0;
+                    PyBytes_AsStringAndSize(bytes.ptr(), (char **)&raw_data, &raw_data_len);
+                    writeData(raw_data, raw_data_len);
+                }
+            }
+
+    };
+
+    class RRsetList_iterator_wrapper
+    {
+        public:
+            RRsetList_iterator_wrapper(const RRsetList& list) : cur_(list.begin()), end_(list.end()){}
+            RRsetPtr Next()
+            {
+                if(cur_ == end_) 
+                {
+                    PyErr_SetObject(PyExc_StopIteration, Py_None);
+                    throw_error_already_set();
+                }
+
+                RRsetPtr rrset = *cur_;
+                ++cur_;
+                return rrset;
+            }
+
+            static RRsetList_iterator_wrapper create(RRsetList &list)
+            {
+                return RRsetList_iterator_wrapper(list);
+            }
+
+        private:
+            RRsetList::const_iterator cur_;
+            RRsetList::const_iterator end_;
+    };
+
+    class Question_iterator_wrapper
+    {
+        public:
+            Question_iterator_wrapper(const Message &message) : cur_(message.beginQuestion()), end_(message.endQuestion()){}
+            QuestionPtr getQuestion() const { return *cur_;}
+            bool isLast() {return cur_ == end_;}
+            void next(){ ++cur_;}
+        private:
+            QuestionIterator cur_;
+            QuestionIterator end_;
+    };
+   
+    class Section_iterator_wrapper
+    {
+        public:
+            Section_iterator_wrapper(const Message &message, const Section &section) : cur_(message.beginSection(section)), end_(message.endSection(section)){}
+            RRsetPtr getRRset() const { return *cur_;}
+            bool isLast() {return cur_ == end_;}
+            void next(){ ++cur_;}
+
+        private:
+            RRsetIterator cur_;
+            RRsetIterator end_;
+    };
+
+
+
+    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(write_name_overloads, writeName, 1, 2)
+    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(to_text_overloads, toText, 0, 1)
+    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(find_rrset_overloads, findRRset, 1, 2)
+    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(add_rrset_overloads, addRRset, 2, 3)
+}
+     
+BOOST_PYTHON_MODULE(bind10_message)
+{
+    REGISTER_EXCEPTION(Exception);
+    REGISTER_EXCEPTION(OutOfRange);
+    REGISTER_EXCEPTION(Unexpected);
+    REGISTER_EXCEPTION(InvalidBufferPosition);
+    REGISTER_EXCEPTION(EmptyLabel);
+    REGISTER_EXCEPTION(TooLongName);
+    REGISTER_EXCEPTION(TooLongLabel);
+    REGISTER_EXCEPTION(BadLabelType);
+    REGISTER_EXCEPTION(BadEscape);
+    REGISTER_EXCEPTION(BadPointer);
+    REGISTER_EXCEPTION(IncompleteName);
+    REGISTER_EXCEPTION(InvalidRRClass);
+    REGISTER_EXCEPTION(IncompleteRRClass);
+    REGISTER_EXCEPTION(InvalidRRTTL);
+    REGISTER_EXCEPTION(IncompleteRRTTL);
+    REGISTER_EXCEPTION(EmptyRRset);
+    REGISTER_EXCEPTION(InvalidRdataLength);
+    REGISTER_EXCEPTION(InvalidRdataText);
+    REGISTER_EXCEPTION(CharStringTooLong);
+    REGISTER_EXCEPTION(DuplicateRRset);
+    REGISTER_EXCEPTION(DNSProtocolError);
+    REGISTER_EXCEPTION(DNSMessageFORMERR);
+    REGISTER_EXCEPTION(DNSMessageBADVERS);
+    REGISTER_EXCEPTION(MessageTooShort);
+    REGISTER_EXCEPTION(InvalidMessageSection);
+    REGISTER_EXCEPTION(InvalidMessageOperation);
+    REGISTER_EXCEPTION(InvalidMessageUDPSize);
+
+
+    class_<InputBuffer>("cpp_input_buffer", init<const void *, size_t>())
+       .def("get_length", &InputBuffer::getLength)
+       .def("get_position", &InputBuffer::getPosition)
+       .def("set_position", &InputBuffer::setPosition)
+       .def("read_uint8", &InputBuffer::readUint8)
+       .def("read_uint16", &InputBuffer::readUint16)
+       .def("read_uint32", &InputBuffer::readUint32)
+       .def("read_data", &InputBuffer::readData);
+
+    class_<PyInputBuffer, bases<InputBuffer> >("input_buffer", init<object>());
+
+    class_<OutputBuffer>("cpp_output_buffer", init<size_t>())
+        .def("get_capacity", &OutputBuffer::getCapacity)
+        .def("get_length", &OutputBuffer::getLength)
+        .def("skip", &OutputBuffer::skip)
+        .def("clear", &OutputBuffer::clear)
+        .def("write_uint8", &OutputBuffer::writeUint8)
+        .def("write_uint16", &OutputBuffer::writeUint16)
+        .def("write_uint16_at", &OutputBuffer::writeUint16At)
+        .def("write_uint32", &OutputBuffer::writeUint32);
+
+
+    class_<PyOutputBuffer, bases<OutputBuffer> >("output_buffer", init<size_t>())
+        .def("get_data", &PyOutputBuffer::getBytes)
+        .def("write_data", &PyOutputBuffer::writeBytes);
+
+
+    enum_<NameComparisonResult::NameRelation>("name_relation")
+        .value("SUPER_DOMAIN", NameComparisonResult::SUPERDOMAIN)
+        .value("SUB_DOMAIN", NameComparisonResult::SUBDOMAIN)
+        .value("EQUAL", NameComparisonResult::EQUAL)
+        .value("COMMAON_ANCESTOR", NameComparisonResult::COMMONANCESTOR);
+
+    class_<NameComparisonResult>("name_comparison_result", init<int, unsigned int, NameComparisonResult::NameRelation>())
+        .def("get_order", &NameComparisonResult::getOrder)
+        .def("get_common_labels", &NameComparisonResult::getCommonLabels)
+        .def("get_relation", &NameComparisonResult::getRelation);
+
+    class_<MessageRenderer>("cpp_message_render", init<OutputBuffer &>())
+        .def("get_Length", &MessageRenderer::getLength)
+        .def("skip", &MessageRenderer::skip)
+        .def("clear", &MessageRenderer::clear)
+        .def("write_uint8", &MessageRenderer::writeUint8)
+        .def("write_uint16", &MessageRenderer::writeUint16)
+        .def("write_uint16_at", &MessageRenderer::writeUint16At)
+        .def("write_uint32", &MessageRenderer::writeUint32)
+        .def("write_name", &MessageRenderer::writeName, write_name_overloads(args("name", "compress")));
+
+
+   class_<PyMessageRenderer, bases<MessageRenderer> >("message_render", init<OutputBuffer &>())
+        .def("get_data", &PyMessageRenderer::getBytes)
+        .def("write_data", &PyMessageRenderer::writeBytes);
+
+   class_<Name>("name", init<const std::string &, optional<bool> >())
+       .def(init<InputBuffer &, optional<bool> >())
+       .def("at", &Name::at)
+       .def("get_lenght", &Name::getLength)
+       .def("get_label_count", &Name::getLabelCount)
+       .def("to_text", &Name::toText, to_text_overloads())
+       .def("to_wire", (void (Name::*)(MessageRenderer &)const)&Name::toWire)
+       .def("to_wire", (void (Name::*)(OutputBuffer&)const)&Name::toWire)
+       .def("compare", &Name::compare)
+       .def("split", &Name::split)
+       .def("concatenate", &Name::concatenate)
+       .def("downcase", &Name::downcase, return_value_policy<reference_existing_object>())       
+       .def("is_wildcard", &Name::isWildcard)
+       .def(self == self)
+       .def(self != self)
+       .def(self < self)
+       .def(self <= self)
+       .def(self > self)
+       .def(self >= self);
+
+   class_<RRType>("rr_type", init<uint16_t>())
+       .def(init<const std::string &>())
+       .def(init<InputBuffer &>())
+       .def("to_text", &RRType::toText)
+       .def("to_wire", (void (RRType::*)(MessageRenderer &)const)&RRType::toWire)
+       .def("to_wire", (void (RRType::*)(OutputBuffer &)const )&RRType::toWire)
+       .def("get_code", &RRType::getCode)
+       .def(self == self)
+       .def(self != self)
+       .def(self < self)
+       .def("DNAME", &RRType::DNAME, return_value_policy<copy_const_reference>())
+       .staticmethod("DNAME")
+       .def("MX", &RRType::MX, return_value_policy<copy_const_reference>())
+       .staticmethod("MX")
+       .def("DNSKEY", &RRType::DNSKEY, return_value_policy<copy_const_reference>())
+       .staticmethod("DNSKEY")
+       .def("TXT", &RRType::TXT, return_value_policy<copy_const_reference>())
+       .staticmethod("TXT")
+       .def("RRSIG", &RRType::RRSIG, return_value_policy<copy_const_reference>())
+       .staticmethod("RRSIG")
+       .def("NSEC", &RRType::NSEC, return_value_policy<copy_const_reference>())
+       .staticmethod("NSEC")
+       .def("AAAA", &RRType::AAAA, return_value_policy<copy_const_reference>())
+       .staticmethod("AAAA")
+       .def("DS", &RRType::DS, return_value_policy<copy_const_reference>())
+       .staticmethod("DS")
+       .def("OPT", &RRType::OPT, return_value_policy<copy_const_reference>())
+       .staticmethod("OPT")
+       .def("A", &RRType::A, return_value_policy<copy_const_reference>())
+       .staticmethod("A")
+       .def("NS", &RRType::NS, return_value_policy<copy_const_reference>())
+       .staticmethod("NS")
+       .def("CNAME", &RRType::CNAME, return_value_policy<copy_const_reference>())
+       .staticmethod("CNAME")
+       .def("SOA", &RRType::SOA, return_value_policy<copy_const_reference>())
+       .staticmethod("SOA")
+       .def("IXFR", &RRType::IXFR, return_value_policy<copy_const_reference>())
+       .staticmethod("IXFR")
+       .def("AXFR", &RRType::AXFR, return_value_policy<copy_const_reference>())
+       .staticmethod("AXFR")
+       .def("ANY", &RRType::ANY, return_value_policy<copy_const_reference>())
+       .staticmethod("ANY");
+
+   class_<RRClass>("rr_class", init<uint16_t>())
+       .def(init<const std::string &>())
+       .def(init<InputBuffer &>())
+       .def("to_text", &RRClass::toText)
+       .def("to_wire", (void (RRClass::*)(MessageRenderer &)const)&RRClass::toWire)
+       .def("to_wire", (void (RRClass::*)(OutputBuffer&)const)&RRClass::toWire)
+       .def("get_code", &RRClass::getCode)
+       .def(self == self)
+       .def(self != self)
+       .def(self < self)
+       .def("IN", &RRClass::IN, return_value_policy<copy_const_reference>())
+       .staticmethod("IN")
+       .def("CH", &RRClass::CH, return_value_policy<copy_const_reference>())
+       .staticmethod("CH")
+       .def("HS", &RRClass::HS, return_value_policy<copy_const_reference>())
+       .staticmethod("HS")
+       .def("NONE", &RRClass::NONE, return_value_policy<copy_const_reference>())
+       .staticmethod("NONE")
+       .def("ANY", &RRClass::ANY, return_value_policy<copy_const_reference>())
+       .staticmethod("ANY");
+
+    class_<RRTTL>("rr_ttl", init<uint32_t>())
+       .def(init<const std::string &>())
+       .def(init<InputBuffer &>())
+       .def("to_text", &RRTTL::toText)
+       .def("to_wire", (void (RRTTL::*)(MessageRenderer &)const)&RRTTL::toWire)
+       .def("to_wire", (void (RRTTL::*)(OutputBuffer&)const)&RRTTL::toWire)
+       .def("get_value", &RRTTL::getValue)
+       .def(self == self)
+       .def(self != self)
+       .def(self < self)
+       .def(self <= self)
+       .def(self > self)
+       .def(self >= self);
+
+    using isc::dns::rdata::Rdata;
+    class_<Rdata, boost::shared_ptr<Rdata>, boost::noncopyable>("abstract_rdata", no_init)
+        .def("to_text", &Rdata::toText)
+        .def("to_wire", (void (Rdata::*)(OutputBuffer &)const )&Rdata::toWire)
+        .def("to_wire", (void (Rdata::*)(MessageRenderer &)const)&Rdata::toWire)
+        .def("compare", &Rdata::compare);
+
+    using isc::dns::rdata::generic::Generic;
+    class_<Generic, bases<Rdata> >("rdata", init<const std::string &>())
+        .def(init<InputBuffer &, size_t>())
+        .def("to_text", &Generic::toText)
+        .def("to_wire", (void(Generic:: *)(OutputBuffer &)const)&Generic::toWire)
+        .def("to_wire", (void(Generic:: *)(MessageRenderer &)const)&Generic::toWire)
+        .def("compare", &Generic::compare);
+
+    using isc::dns::rdata::ch::A;
+    class_<A, bases<Rdata> >("a_rdata", init<const std::string &>()).def(init<InputBuffer &, size_t>())
+        .def("to_text", &A::toText)
+        .def("to_wire", (void(A:: *)(OutputBuffer &)const)&A::toWire)
+        .def("to_wire", (void(A:: *)(MessageRenderer &)const)&A::toWire)
+        .def("compare", &A::compare);
+
+
+
+    def("create_rdata", (rdata::RdataPtr (*)(const RRType &, const RRClass &, const std::string &))rdata::createRdata);
+    def("create_rdata", (rdata::RdataPtr (*)(const RRType &, const RRClass &, InputBuffer &, size_t))rdata::createRdata);
+    def("create_rdata", (rdata::RdataPtr (*)(const RRType &, const RRClass &, const Rdata &))rdata::createRdata);
+    
+    class_<AbstractRRset, boost::noncopyable>("abstract_rrset", no_init)
+        .def("get_rdata_count", &AbstractRRset::getRdataCount)
+        .def("get_name", &AbstractRRset::getName, return_internal_reference<>())
+        .def("get_class", &AbstractRRset::getClass, return_internal_reference<>())
+        .def("get_type", &AbstractRRset::getType, return_internal_reference<>())
+        .def("get_ttl", &AbstractRRset::getTTL, return_internal_reference<>())
+        .def("set_name", &AbstractRRset::setName)
+        .def("set_ttl", &AbstractRRset::setTTL)
+        .def("to_text", &AbstractRRset::toText)
+        .def("to_wire", (unsigned int (AbstractRRset::*)(MessageRenderer &)const)&AbstractRRset::toWire) 
+        .def("to_wire", (unsigned int (AbstractRRset::*)(OutputBuffer&)const)&AbstractRRset::toWire) 
+        .def("add_rdata", (void (AbstractRRset::*)(rdata::ConstRdataPtr))&AbstractRRset::addRdata)
+        .def("add_rdata", (void (AbstractRRset::*)(const rdata::Rdata &))&AbstractRRset::addRdata)
+        .def("get_rdata_iterator", &AbstractRRset::getRdataIterator);
+    
+    class_<RdataIterator, boost::shared_ptr<RdataIterator>, boost::noncopyable>("rdata_iter",no_init)
+        .def("first", &RdataIterator::first)
+        .def("next", &RdataIterator::next)
+        .def("get_current", &RdataIterator::getCurrent, return_internal_reference<>())
+        .def("is_last", &RdataIterator::isLast);
+    
+    class_<BasicRRset, bases<AbstractRRset>, boost::noncopyable>("basic_rrset", init<const Name &, const RRClass &, const RRType &, const RRTTL &>())
+        .def("get_rdata_count", &BasicRRset::getRdataCount)
+        .def("get_name", &BasicRRset::getName, return_internal_reference<>())
+        .def("get_class", &BasicRRset::getClass, return_internal_reference<>())
+        .def("get_type", &BasicRRset::getType, return_internal_reference<>())
+        .def("get_ttl", &BasicRRset::getTTL, return_internal_reference<>())
+        .def("set_name", &BasicRRset::setName)
+        .def("set_ttl", &BasicRRset::setTTL)
+        .def("to_text", &BasicRRset::toText)
+        .def("to_wire", (unsigned int (BasicRRset::*)(MessageRenderer &)const)&BasicRRset::toWire) 
+        .def("to_wire", (unsigned int (BasicRRset::*)(OutputBuffer&)const)&BasicRRset::toWire)
+        .def("add_rdata", (void (BasicRRset::*)(rdata::ConstRdataPtr))&BasicRRset::addRdata)
+        .def("add_rdata", (void (BasicRRset::*)(const rdata::Rdata &))&BasicRRset::addRdata)
+        .def("get_rdata_iterator", &BasicRRset::getRdataIterator);
+
+    class_<RRset, boost::shared_ptr<RRset>, bases<BasicRRset>, boost::noncopyable>("rrset", init<const Name&, const RRClass &, const RRType &, const RRTTL &>())
+        .def("set_name", &RRset::setName)
+        .def("set_ttl", &RRset::setTTL)
+        .def("add_rrsig", (void (RRset::*)(const rdata::RdataPtr))&RRset::addRRsig)
+        .def("add_rrsig", (void (RRset::*)(AbstractRRset &))&RRset::addRRsig)
+        .def("add_rrsig", (void (RRset::*)(RRsetPtr))&RRset::addRRsig)
+        .def("remove_rrsig", &RRset::removeRRsig)
+        .def("get_rrsig", &RRset::getRRsig);
+   
+    class_<RRsetList, boost::noncopyable>("rrset_list", init<>())
+        .def("add_rrset", &RRsetList::addRRset)
+        .def("find_rrset", (RRsetPtr(RRsetList::*)(ConstRRsetPtr))&RRsetList::findRRset)
+        .def("find_rrset", (RRsetPtr(RRsetList::*)(const RRType &, const RRClass &))&RRsetList::findRRset, find_rrset_overloads(args("rrtype","rrclass")))
+        .def("__iter__", &RRsetList_iterator_wrapper::create)
+        .def("__len__", &RRsetList::size);
+
+
+    class_<Question, boost::shared_ptr<Question> >("question", init<InputBuffer &>())
+        .def(init<const Name &, const RRClass &, const RRType &>())
+        .def("get_name", &Question::getName, return_internal_reference<>())
+        .def("get_type", &Question::getType, return_internal_reference<>())
+        .def("get_class", &Question::getClass, return_internal_reference<>())
+        .def("to_text", &Question::toText)
+        .def("to_wire", (unsigned int(Question::*)(MessageRenderer &)const)&Question::toWire)
+        .def("to_wire", (unsigned int (Question::*)(OutputBuffer&)const)&Question::toWire);
+
+   class_<MessageFlag>("message_flag", no_init)
+        .def("get_bit", &MessageFlag::getBit)
+        .def("QR", &MessageFlag::QR, return_value_policy<copy_const_reference>())
+        .staticmethod("QR")
+        .def("AA", &MessageFlag::AA, return_value_policy<copy_const_reference>())
+        .staticmethod("AA")
+        .def("TC", &MessageFlag::TC, return_value_policy<copy_const_reference>())
+        .staticmethod("TC")
+        .def("RD", &MessageFlag::RD, return_value_policy<copy_const_reference>())
+        .staticmethod("RD")
+        .def("RA", &MessageFlag::RA, return_value_policy<copy_const_reference>())
+        .staticmethod("RA")
+        .def("AD", &MessageFlag::AD, return_value_policy<copy_const_reference>())
+        .staticmethod("AD")
+        .def("CD", &MessageFlag::CD, return_value_policy<copy_const_reference>())
+        .staticmethod("CD");
+      
+       class_<Opcode>("op_code", no_init)
+        .def("get_code", &Opcode::getCode)
+        .def(self == self)
+        .def("to_text", &Opcode::toText)
+        .def("QUERY", &Opcode::QUERY, return_value_policy<copy_const_reference>())
+        .staticmethod("QUERY")
+        .def("IQUERY", &Opcode::IQUERY, return_value_policy<copy_const_reference>())
+        .staticmethod("IQUERY")
+        .def("STATUS", &Opcode::STATUS, return_value_policy<copy_const_reference>())
+        .staticmethod("STATUS")
+        .def("RESERVED3", &Opcode::RESERVED3, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED3")
+        .def("NOTIFY", &Opcode::NOTIFY, return_value_policy<copy_const_reference>())
+        .staticmethod("NOTIFY")
+        .def("UPDATE", &Opcode::UPDATE, return_value_policy<copy_const_reference>())
+        .staticmethod("UPDATE")
+        .def("RESERVED6", &Opcode::RESERVED6, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED6")
+        .def("RESERVED7", &Opcode::RESERVED7, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED7")
+        .def("RESERVED8", &Opcode::RESERVED8, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED8")
+        .def("RESERVED9", &Opcode::RESERVED9, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED9")
+        .def("RESERVED10", &Opcode::RESERVED10, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED10")
+        .def("RESERVED11", &Opcode::RESERVED11, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED11")
+        .def("RESERVED12", &Opcode::RESERVED12, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED12")
+        .def("RESERVED13", &Opcode::RESERVED13, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED13")
+        .def("RESERVED14", &Opcode::RESERVED14, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED14")
+        .def("RESERVED15", &Opcode::RESERVED15, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED15");
+      
+    class_<Rcode>("rcode", init<uint16_t>())
+        .def("get_code", &Rcode::getCode)
+        .def(self == self)
+        .def("to_text", &Rcode::toText)
+        .def("NOERROR", &Rcode::NOERROR, return_value_policy<copy_const_reference>())
+        .staticmethod("NOERROR")
+        .def("FORMERR", &Rcode::FORMERR, return_value_policy<copy_const_reference>())
+        .staticmethod("FORMERR")
+        .def("SERVFAIL", &Rcode::SERVFAIL, return_value_policy<copy_const_reference>())
+        .staticmethod("SERVFAIL")
+        .def("NXDOMAIN", &Rcode::NXDOMAIN, return_value_policy<copy_const_reference>())
+        .staticmethod("NXDOMAIN")
+        .def("NOTIMP", &Rcode::NOTIMP, return_value_policy<copy_const_reference>())
+        .staticmethod("NOTIMP")
+        .def("REFUSED", &Rcode::REFUSED, return_value_policy<copy_const_reference>())
+        .staticmethod("REFUSED")
+        .def("YXDOMAIN", &Rcode::YXDOMAIN, return_value_policy<copy_const_reference>())
+        .staticmethod("YXDOMAIN")
+        .def("YXRRSET", &Rcode::YXRRSET, return_value_policy<copy_const_reference>())
+        .staticmethod("YXRRSET")
+        .def("NXRRSET", &Rcode::NXRRSET, return_value_policy<copy_const_reference>())
+        .staticmethod("NXRRSET")
+        .def("NOTAUTH", &Rcode::NOTAUTH, return_value_policy<copy_const_reference>())
+        .staticmethod("NOTAUTH")
+        .def("NOTZONE", &Rcode::NOTZONE, return_value_policy<copy_const_reference>())
+        .staticmethod("NOTZONE")
+        .def("RESERVED11", &Rcode::RESERVED11, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED11")
+        .def("RESERVED12", &Rcode::RESERVED12, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED12")
+        .def("RESERVED13", &Rcode::RESERVED13, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED13")
+        .def("RESERVED14", &Rcode::RESERVED14, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED14")
+        .def("RESERVED15", &Rcode::RESERVED15, return_value_policy<copy_const_reference>())
+        .staticmethod("RESERVED15")
+        .def("BADVERS", &Rcode::BADVERS, return_value_policy<copy_const_reference>())
+        .staticmethod("BADVERS");
+      
+    class_<Section>("section", no_init)
+        .def("get_code", &Section::getCode)
+        .def(self == self)
+        .def(self != self)
+        .def("QUESTION", &Section::QUESTION, return_value_policy<copy_const_reference>())
+        .staticmethod("QUESTION")
+        .def("ANSWER", &Section::ANSWER, return_value_policy<copy_const_reference>())
+        .staticmethod("ANSWER")
+        .def("AUTHORITY", &Section::AUTHORITY, return_value_policy<copy_const_reference>())
+        .staticmethod("AUTHORITY")
+        .def("ADDITIONAL", &Section::ADDITIONAL, return_value_policy<copy_const_reference>())
+        .staticmethod("ADDITIONAL");
+
+    enum_<Message::Mode>("message_mode")
+        .value("PARSE", Message::PARSE)
+        .value("RENDER", Message::RENDER);
+
+    class_<Message, boost::noncopyable>("message", init<Message::Mode>())
+        .def("get_header_flag", &Message::getHeaderFlag)
+        .def("set_header_flag", &Message::setHeaderFlag)
+        .def("clear_header_flag", &Message::clearHeaderFlag)
+        .def("is_dnsssec_supported", &Message::isDNSSECSupported)
+        .def("set_dnssec_supported", &Message::setDNSSECSupported)
+        .def("get_udp_size", &Message::getUDPSize)
+        .def("set_udp_size", &Message::setUDPSize)
+        .def("get_qid", &Message::getQid)
+        .def("set_qid", &Message::setQid)
+        .def("get_rcode", &Message::getRcode, return_internal_reference<>())
+        .def("set_rcode", &Message::setRcode)
+        .def("get_opcode", &Message::getOpcode, return_internal_reference<>())
+        .def("set_opcode", &Message::setOpcode)
+        .def("to_text", &Message::toText)
+        .def("get_rr_count", &Message::getRRCount)
+        .def("add_question", (void (Message:: *)(QuestionPtr))&Message::addQuestion)
+        .def("add_question", (void (Message:: *)(const Question&))&Message::addQuestion)
+        //.def("remove_question", &Message::removeQuestion)
+        .def("add_rrset", &Message::addRRset, add_rrset_overloads(args("section","rrset","sign")))
+        //.def("remove_rrset", &Message::removeRRset)
+        .def("clear", &Message::clear)
+        .def("make_response", &Message::makeResponse)
+        .def("to_wire", &Message::toWire)
+        .def("from_wire", &Message::fromWire);
+
+    class_<Question_iterator_wrapper>("question_iter", init<const Message &>())
+        .def("get_question", &Question_iterator_wrapper::getQuestion)
+        .def("is_last", &Question_iterator_wrapper::isLast)
+        .def("next", &Question_iterator_wrapper::next);
+
+    class_<Section_iterator_wrapper>("section_iter", init<const Message &, const Section &>())
+        .def("get_rrset", &Section_iterator_wrapper::getRRset)
+        .def("is_last", &Section_iterator_wrapper::isLast)
+        .def("next", &Section_iterator_wrapper::next);
+}
+ 

+ 71 - 0
src/lib/dns/message_test.py

@@ -0,0 +1,71 @@
+from bind10_message import *
+from struct import *
+
+id = ["10","35"]
+flags = ["85", "00"]
+sections = ["00","01","00","02","00","00","00","00"]
+query_raw_name = ["04","74","65","73","74","07","65","78","61","6d","70","6c","65","03","63","6f","6d","00"]
+query_type_class = ["00","01","00","01"]
+answer_compress = ["c0","0c"]
+answer1_ttl_type_class_rdlen_rdata = ["00","01","00","01","00","00","0e","10","00","04","c0","00","02","01"]
+answer2_ttl_type_class_rdlen_rdata = ["00","01","00","01","00","00","1c","20","00","04","c0","00","02","02"]
+
+bytes = pack("B" * len(query_raw_name), *[int(i,16) for i in query_raw_name])
+query_name = name(input_buffer(bytes))
+print("query name is ", query_name.to_text())
+
+message_raw_data = id + flags + sections + query_raw_name + query_type_class + answer_compress + answer1_ttl_type_class_rdlen_rdata + answer_compress + \
+        answer2_ttl_type_class_rdlen_rdata
+
+m = message(message_mode.PARSE)
+message_bytes = pack("B" * len(message_raw_data), *[int(i,16) for i in message_raw_data])
+m.from_wire(input_buffer(message_bytes))
+if m.get_qid() == int("1035", 16):
+    print("id is correct")
+
+if m.get_opcode() == op_code.QUERY():
+    print("opcode is correct")
+
+if m.get_rcode() == rcode.NOERROR():
+    print("rcode is correct")
+
+if m.get_header_flag(message_flag.QR()):
+    print("qr is correct")
+
+if m.get_header_flag(message_flag.RD()):
+    print("rd is correct")
+
+if m.get_header_flag(message_flag.AA()):
+    print("aa is correct")
+
+if m.get_rr_count(section.QUESTION()) == 1:
+    print("qustion rr count is correct")
+
+if m.get_rr_count(section.ANSWER()) == 2:
+    print("answer rr count is ok")
+
+if m.get_rr_count(section.AUTHORITY()) == 0 and m.get_rr_count(section.ADDITIONAL()) == 0:
+    print("authority and additional rr count is ok")
+
+question_iter = question_iter(m)
+question = question_iter.get_question()
+print("question name is ", question.get_name().to_text())
+if question.get_type() == rr_type.A():
+    print("question rr type is A")
+if question.get_class() == rr_class.IN():
+    print("question rr class is IN")
+
+answer_rrset_iter = section_iter(m, section.ANSWER())
+answer = answer_rrset_iter.get_rrset()
+print("answer part name is ", answer.get_name().to_text())
+if answer.get_type() == rr_type.A() and answer.get_class() == rr_class.IN():
+    print("answer part is A record and class IN")
+
+rdata_iter = answer.get_rdata_iterator()
+rdata_iter.first()
+print("first part of rdata is ", rdata_iter.get_current().to_text())
+rdata_iter.next()
+print("second part of rdata is", rdata_iter.get_current().to_text())
+rdata_iter.next()
+if rdata_iter.is_last():
+    print("answer part has two rr")

+ 3 - 6
src/lib/python/isc/Makefile.am

@@ -1,8 +1,5 @@
-SUBDIRS = auth cc Util config
+SUBDIRS = auth cc config # Util
 
 
-PY_MODULES=	__init__.py
+python_PYTHON = __init__.py
 
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/ ; done)
+pythondir = $(pyexecdir)/isc

+ 2 - 5
src/lib/python/isc/Util/Makefile.am

@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py hexdump.py
+python_PYTHON = __init__.py hexdump.py
 
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/Util
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/Util/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/Util/ ; done)
+pythondir = $(pyexecdir)/isc/Util

+ 0 - 1
src/lib/python/isc/__init__.py

@@ -1,4 +1,3 @@
 import isc.auth
 import isc.auth
 import isc.cc
 import isc.cc
-import isc.Util
 import isc.config
 import isc.config

+ 2 - 5
src/lib/python/isc/auth/Makefile.am

@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py master.py sqlite3_ds.py
+python_PYTHON = __init__.py master.py sqlite3_ds.py
 
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/auth
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/auth/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/auth/; done)
+pythondir = $(pyexecdir)/isc/auth

+ 24 - 0
src/lib/python/isc/auth/TODO

@@ -0,0 +1,24 @@
+Support optional origin in $INCLUDE:
+$INCLUDE filename origin
+
+Support optional comment in $INCLUDE:
+$INCLUDE filename origin comment
+
+Support optional comment in $TTL (RFC 2308):
+$TTL number comment
+
+Do not assume "." is origin if origin is not set and sees a @ or
+a label without a ".". It should probably fail.  (Don't assume a
+mistake means it is a root level label.)
+
+Add verbose option to show what it is adding, not necessarily
+in master file format, but in the context of the data source.
+
+Add "check" option to check sanity of master file but don't really
+add to data source.
+
+Open questions to document in manual page:
+
+What happens in the database? replaces existing? What if a.foo
+existed but new zone file didn't have a.foo, would previous a.foo
+in database be removed?

+ 50 - 21
src/lib/python/isc/auth/master.py

@@ -90,8 +90,11 @@ def records(input):
 #########################################################################
 #########################################################################
 def pop(line):
 def pop(line):
     list = line.split()
     list = line.split()
-    first = list[0]
-    rest = ' '.join(list[1:])
+    first, rest = '', ''
+    if len(list) != 0:
+        first = list[0]
+    if len(list) > 1:
+        rest = ' '.join(list[1:])
     return first, rest
     return first, rest
 
 
 #########################################################################
 #########################################################################
@@ -133,7 +136,7 @@ def isclass(s):
 name_regex = re.compile('[-\w\$\d\/*]+(?:\.[-\w\$\d\/]+)*\.?')
 name_regex = re.compile('[-\w\$\d\/*]+(?:\.[-\w\$\d\/]+)*\.?')
 def isname(s):
 def isname(s):
     global name_regex
     global name_regex
-    if name_regex.match(s):
+    if s == '.' or name_regex.match(s):
         return True
         return True
     else:
     else:
         return False
         return False
@@ -190,18 +193,20 @@ def directive(s):
     first, more = pop(s)
     first, more = pop(s)
     second, more = pop(more)
     second, more = pop(more)
     if re.match('\$origin', first, re.I):
     if re.match('\$origin', first, re.I):
-        if not isname(second):
+        if not second or not isname(second):
             raise MasterFileError('Invalid $ORIGIN')
             raise MasterFileError('Invalid $ORIGIN')
         if more:
         if more:
             raise MasterFileError('Invalid $ORIGIN')
             raise MasterFileError('Invalid $ORIGIN')
-        if second[-1] == '.':
+        if second == '.':
+            origin = ''
+        elif second[-1] == '.':
             origin = second
             origin = second
         else:
         else:
             origin = second + '.' + origin
             origin = second + '.' + origin
         return True
         return True
     elif re.match('\$ttl', first, re.I):
     elif re.match('\$ttl', first, re.I):
-        if not isttl(second):
-            raise MasterFileError('Invalid $TTL: ' + second)
+        if not second or not isttl(second):
+            raise MasterFileError('Invalid TTL: "' + second + '"')
         if more:
         if more:
             raise MasterFileError('Invalid $TTL statement')
             raise MasterFileError('Invalid $TTL statement')
         defttl = parse_ttl(second)
         defttl = parse_ttl(second)
@@ -335,17 +340,20 @@ def two(record, curname):
 #########################################################################
 #########################################################################
 def reset():
 def reset():
     global defttl, origin
     global defttl, origin
-    defttl = -1
-    origin=''
+    defttl = ''
+    origin = ''
 
 
 #########################################################################
 #########################################################################
 # openzone: open a zone master file, set initial origin, return descriptor
 # openzone: open a zone master file, set initial origin, return descriptor
 #########################################################################
 #########################################################################
-def openzone(filename, initial_origin = '.'):
+def openzone(filename, initial_origin = ''):
+    global origin
     try:
     try:
         zf = open(filename, 'r')
         zf = open(filename, 'r')
     except:
     except:
-        return
+        raise MasterFileError("Could not open " + filename)
+    if initial_origin == '.':
+        initial_origin = ''
     origin = initial_origin
     origin = initial_origin
     return zf
     return zf
 
 
@@ -370,10 +378,13 @@ def zonedata(zone):
             sub.close()
             sub.close()
             continue
             continue
 
 
-        first = record.split()[0]
-        if first == '@':
-            name = origin
-            at, record = pop(record)
+        # replace @ with origin
+        rl = record.split()
+        if rl[0] == '@':
+            rl[0] = origin
+            if not origin:
+                rl[0] = '.'
+            record = ' '.join(rl)
 
 
         result = four(record, name)
         result = four(record, name)
 
 
@@ -398,15 +409,31 @@ def zonedata(zone):
         if rrclass.upper() != 'IN':
         if rrclass.upper() != 'IN':
             raise MasterFileError("CH and HS zones not supported")
             raise MasterFileError("CH and HS zones not supported")
 
 
-        # add origin to rdata if necessary
-        if rrtype.lower() in ('cname', 'dname', 'ns'):
+        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):
             if not isname(rdata):
                 raise MasterFileError("Invalid " + rrtype + ": " + rdata)
                 raise MasterFileError("Invalid " + rrtype + ": " + rdata)
             if rdata[-1] != '.':
             if rdata[-1] != '.':
                 rdata += '.' + origin
                 rdata += '.' + origin
-
-        if (ttl == -1):
-            raise MasterFileError("No TTL specified; zone rejected")
+        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] += '.' + origin
+            if soa[1][-1] != '.':
+                soa[1] += '.' + origin
+            rdata = ' '.join(soa)
+        if rrtype.lower() == 'mx':
+            mx = rdata.split()
+            if len(mx) != 2 or not isname(mx[1]):
+                raise MasterFileError("Invalid " + rrtype + ": " + rdata)
+            if mx[1][-1] != '.':
+                mx[1] += '.' + origin
+                rdata = ' '.join(mx)
 
 
         yield (name, ttl, rrclass, rrtype, rdata)
         yield (name, ttl, rrclass, rrtype, rdata)
 
 
@@ -414,9 +441,11 @@ def zonedata(zone):
 # zonename: scans zone data for an SOA record, returns its name, restores
 # zonename: scans zone data for an SOA record, returns its name, restores
 # the zone file to its prior state
 # the zone file to its prior state
 #########################################################################
 #########################################################################
-def zonename(zone, initial_origin = '.'):
+def zonename(zone, initial_origin = ''):
     global origin
     global origin
     old_origin = origin
     old_origin = origin
+    if initial_origin == '.':
+        initial_origin = ''
     origin = initial_origin
     origin = initial_origin
     old_location = zone.tell()
     old_location = zone.tell()
     zone.seek(0)
     zone.seek(0)

+ 28 - 23
src/lib/python/isc/auth/sqlite3_ds.py

@@ -129,29 +129,34 @@ def load(dbfile, zone, reader, file):
     cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
     cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
     new_zone_id = cur.lastrowid
     new_zone_id = cur.lastrowid
 
 
-    for name, ttl, rdclass, rdtype, rdata in reader(file):
-        sigtype = ''
-        if rdtype.lower() == 'rrsig':
-            sigtype = rdata.split()[0]
-
-        if rdtype.lower() == 'nsec3' or sigtype.lower() == 'nsec3':
-            hash = name.split('.')[0]
-            cur.execute("""INSERT INTO nsec3
-                           (zone_id, hash, owner, ttl, rdtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, hash, name, ttl, rdtype, rdata])
-        elif rdtype.lower() == 'rrsig':
-            cur.execute("""INSERT INTO records
-                           (zone_id, name, rname, ttl, rdtype, sigtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, name, reverse_name(name), ttl,
-                        rdtype, sigtype, rdata])
-        else:
-            cur.execute("""INSERT INTO records
-                           (zone_id, name, rname, ttl, rdtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, name, reverse_name(name), ttl,
-                        rdtype, rdata])
+    try:
+        for name, ttl, rdclass, rdtype, rdata in reader(file):
+            sigtype = ''
+            if rdtype.lower() == 'rrsig':
+                sigtype = rdata.split()[0]
+
+            if rdtype.lower() == 'nsec3' or sigtype.lower() == 'nsec3':
+                hash = name.split('.')[0]
+                cur.execute("""INSERT INTO nsec3
+                               (zone_id, hash, owner, ttl, rdtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, hash, name, ttl, rdtype, rdata])
+            elif rdtype.lower() == 'rrsig':
+                cur.execute("""INSERT INTO records
+                               (zone_id, name, rname, ttl,
+                                rdtype, sigtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, name, reverse_name(name), ttl,
+                            rdtype, sigtype, rdata])
+            else:
+                cur.execute("""INSERT INTO records
+                               (zone_id, name, rname, ttl, rdtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, name, reverse_name(name), ttl,
+                            rdtype, rdata])
+    except Exception as e:
+        fail = "Error while loading " + zone + ": " + e.args[0]
+        raise Sqlite3DSError(fail)
 
 
     if old_zone_id:
     if old_zone_id:
         cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])
         cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])

+ 2 - 5
src/lib/python/isc/cc/Makefile.am

@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py data.py session.py message.py
+python_PYTHON =	__init__.py data.py session.py message.py
 
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/cc
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/cc/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/cc/ ; done)
+pythondir = $(pyexecdir)/isc/cc

+ 0 - 3
src/lib/python/isc/cc/message.py

@@ -13,9 +13,6 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-# XXX
-import isc.Util
-
 import sys
 import sys
 import struct
 import struct
 
 

+ 2 - 5
src/lib/python/isc/config/Makefile.am

@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
+python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
 
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/config
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/config/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/config/; done)
+pythondir = $(pyexecdir)/isc/config

+ 0 - 2
src/lib/python/isc/config/ccsession.py

@@ -195,8 +195,6 @@ class ModuleCCSession(ConfigData):
                             newc = self._remote_module_configs[module_name].get_local_config()
                             newc = self._remote_module_configs[module_name].get_local_config()
                             isc.cc.data.merge(newc, new_config)
                             isc.cc.data.merge(newc, new_config)
                             self._remote_module_configs[module_name].set_local_config(newc)
                             self._remote_module_configs[module_name].set_local_config(newc)
-                            print("[XX] updated remote config value: ")
-                            print(newc)
                             return
                             return
 
 
                     # ok, so apparently this update is for us.
                     # ok, so apparently this update is for us.

+ 3 - 1
src/lib/python/isc/config/cfgmgr.py

@@ -267,8 +267,10 @@ class ConfigManager:
             got_error = False
             got_error = False
             err_list = []
             err_list = []
             for module in self.config.data:
             for module in self.config.data:
-                if module != "version":
+                if module != "version" and self.config.data[module] != old_data[module]:
                     update_cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, self.config.data[module])
                     update_cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, self.config.data[module])
+                    print("[XX] send update: " + str(update_cmd))
+                    print("[XX] to: " + str(module))
                     self.cc.group_sendmsg(update_cmd, module)
                     self.cc.group_sendmsg(update_cmd, module)
                     answer, env = self.cc.group_recvmsg(False)
                     answer, env = self.cc.group_recvmsg(False)
                     if answer == None:
                     if answer == None: