BIND 10 trac1512, updated. 5f474b3308a695702e8b9875641bfc378656d035 [1512] added other .py's to python_PYTHON.

BIND 10 source code commits bind10-changes at lists.isc.org
Thu May 17 04:09:06 UTC 2012


The branch, trac1512 has been updated
       via  5f474b3308a695702e8b9875641bfc378656d035 (commit)
       via  c778e983a18659a86333aa626ad500b47a9ab26c (commit)
       via  743fa2c222467587b16bf3c94746ea9e150ce479 (commit)
       via  b25872688315e81de52a5a2c2d12a626b88a8640 (commit)
       via  194808061e1ab60bfabc8683e9f1f779459af0e7 (commit)
       via  5588e70dc9d3b19af357d07639aee92069afa7fd (commit)
       via  59506a667e333362966b4091cb24c10ca4605ec1 (commit)
       via  9246111fe4cb6240ed82483e4ae57b2eaf2da523 (commit)
       via  94fc335cc34ceb93bbc8e7caa66174d6c51038b8 (commit)
       via  4c11d7a7cb7315255ce893d43be15cfaa9aa82af (commit)
       via  d4e28625829cd11e16a93f8ce786b77544d6af1c (commit)
      from  b6453630ee6655c325bd1e833bc34dc4f9a664b2 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5f474b3308a695702e8b9875641bfc378656d035
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 20:47:47 2012 -0700

    [1512] added other .py's to python_PYTHON.
    
    these are necessary for distcheck.

commit c778e983a18659a86333aa626ad500b47a9ab26c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 19:32:35 2012 -0700

    [1512] added ddns to SUBDIRS

commit 743fa2c222467587b16bf3c94746ea9e150ce479
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 19:32:14 2012 -0700

    [1512] more comment (doc) update

commit b25872688315e81de52a5a2c2d12a626b88a8640
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 19:22:31 2012 -0700

    [1512] documentation update

commit 194808061e1ab60bfabc8683e9f1f779459af0e7
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 19:14:18 2012 -0700

    [1512] added detailed log description.

commit 5588e70dc9d3b19af357d07639aee92069afa7fd
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 19:00:12 2012 -0700

    [1512] logged forwarding (fail) and notauth events separately.

commit 59506a667e333362966b4091cb24c10ca4605ec1
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 18:41:40 2012 -0700

    [1512] use NOTIMP instead of REFUSED for denying update forwarding.
    
    this is actually closer to the current implementation status, and is
    compatible BIND 9's behavior when the forwarding ACL is completely NULLified.

commit 9246111fe4cb6240ed82483e4ae57b2eaf2da523
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 18:35:37 2012 -0700

    [1512] supported 'notauth' case.

commit 94fc335cc34ceb93bbc8e7caa66174d6c51038b8
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 18:14:48 2012 -0700

    [1512] introduce a test data source and search it in ZoneConfig.find_zone().

commit 4c11d7a7cb7315255ce893d43be15cfaa9aa82af
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 17:37:39 2012 -0700

    [1512] log events when update request somehow failed.

commit d4e28625829cd11e16a93f8ce786b77544d6af1c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed May 16 15:31:50 2012 -0700

    [1512] let UpdateSession.handle() return zone name and class, too.
    
    the caller (b10-ddns) will use that to notify other processes of updates.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/python/isc/Makefile.am                     |    2 +-
 src/lib/python/isc/ddns/Makefile.am                |    2 +-
 src/lib/python/isc/ddns/libddns_messages.mes       |   25 ++++
 src/lib/python/isc/ddns/logger.py                  |   77 +++++++++++++
 src/lib/python/isc/ddns/session.py                 |  119 +++++++++++++++----
 src/lib/python/isc/ddns/tests/Makefile.am          |    3 +
 src/lib/python/isc/ddns/tests/session_tests.py     |   89 +++++++++++----
 src/lib/python/isc/ddns/zone_config.py             |   31 +++++-
 src/lib/python/isc/log_messages/Makefile.am        |    2 +
 .../python/isc/log_messages/libddns_messages.py    |    1 +
 10 files changed, 296 insertions(+), 55 deletions(-)
 create mode 100644 src/lib/python/isc/ddns/logger.py
 create mode 100644 src/lib/python/isc/log_messages/libddns_messages.py

-----------------------------------------------------------------------
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index aef5dc3..80fd222 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,5 +1,5 @@
 SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
-SUBDIRS += xfrin log_messages server_common
+SUBDIRS += xfrin log_messages server_common ddns
 
 python_PYTHON = __init__.py
 
diff --git a/src/lib/python/isc/ddns/Makefile.am b/src/lib/python/isc/ddns/Makefile.am
index 77b1ca7..1b9b6df 100644
--- a/src/lib/python/isc/ddns/Makefile.am
+++ b/src/lib/python/isc/ddns/Makefile.am
@@ -1,6 +1,6 @@
 SUBDIRS = . tests
 
-python_PYTHON = __init__.py session.py
+python_PYTHON = __init__.py session.py logger.py zone_config.py
 BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/libddns_messages.py
 nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/libddns_messages.py
 pylogmessagedir = $(pyexecdir)/isc/log_messages/
diff --git a/src/lib/python/isc/ddns/libddns_messages.mes b/src/lib/python/isc/ddns/libddns_messages.mes
index 1e2d1e3..83ff816 100644
--- a/src/lib/python/isc/ddns/libddns_messages.mes
+++ b/src/lib/python/isc/ddns/libddns_messages.mes
@@ -14,3 +14,28 @@
 
 # No namespace declaration - these constants go in the global namespace
 # of the libddns_messages python module.
+
+% LIBDDNS_UPDATE_ERROR update client %1 for zone %2: %3
+Debug message.  An error is found in processing a dynamic update
+request.  This log message is used for general errors that are not
+normally expected to happen.  So, in general, it would mean some
+problem in the client implementation or an interoperability issue
+with this implementation.  The client's address, the zone name and
+class, and description of the error are logged.
+
+% LIBDDNS_UPDATE_FORWARD_FAIL update client %1 for zone %2: update forwarding not supported
+Debug message.  An update request is sent to a secondary server.  This
+is not necessarily mean invalid, but this implementation does not yet
+support update forwarding as specified in Section 6 of RFC2136 and it
+will simply return a response with an RCODE of NOTIMP to the client.
+The client's address and the zone name/class are logged.
+
+% LIBDDNS_UPDATE_NOTAUTH update client %1 for zone %2: not authoritative for update zone
+Debug message.  An update request for a zone for which the receiving
+server has authority.  In theory this is not an unexpected event, but
+there are client implementations that could send update requests
+carelessly, so it may not necessarily be so uncommon in practice.  If
+possible, you may want to check the implementation or configuration of
+those clients to suppress the requests.  As specified in Section 3.1
+of RFC2136, the receiving server will return a response with an RCODE
+of NOTAUTH.
diff --git a/src/lib/python/isc/ddns/logger.py b/src/lib/python/isc/ddns/logger.py
new file mode 100644
index 0000000..e4f5ed8
--- /dev/null
+++ b/src/lib/python/isc/ddns/logger.py
@@ -0,0 +1,77 @@
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+""" This is a logging utility module for other modules of the ddns library
+package.
+
+"""
+
+import isc.log
+
+# The logger for this package
+logger = isc.log.Logger('libddns')
+
+class ClientFormatter:
+    """A utility class to convert a client address to string.
+
+    This class is constructed with a Python standard socket address tuple.
+    If it's 2-element tuple, it's assumed to be an IPv4 socket address
+    and will be converted to the form of '<addr>:<port>'.
+    If it's 4-element tuple, it's assumed to be an IPv6 socket address.
+    and will be converted to the form of '[<addr>]:<por>'.
+
+    This class is designed to delay the conversion until it's explicitly
+    requested, so the conversion doesn't happen if the corresponding log
+    message is suppressed because of its log level (which is often the case
+    for debug messages).
+
+    """
+    def __init__(self, addr):
+        self.__addr = addr
+
+    def __str__(self):
+        if len(self.__addr) == 2:
+            return self.__addr[0] + ':' + str(self.__addr[1])
+        elif len(self.__addr) == 4:
+            return '[' + self.__addr[0] + ']:' + str(self.__addr[1])
+        return None
+
+class ZoneFormatter:
+    """A utility class to convert zone name and class to string.
+
+    This class is constructed with a name of a zone (isc.dns.Name object)
+    and its RR class (isc.dns.RRClass object).  Its text conversion method
+    (__str__) converts them into a string in the form of
+    '<zone name>/<zone class>' where the trailing dot of the zone name
+    is omitted.
+
+    If the given zone name on construction is None, it's assumed to be
+    the zone isn't identified but needs to be somehow logged.  The conversion
+    method returns a special string to indicate this case.
+
+    This class is designed to delay the conversion until it's explicitly
+    requested, so the conversion doesn't happen if the corresponding log
+    message is suppressed because of its log level (which is often the case
+    for debug messages).
+
+    """
+    def __init__(self, zname, zclass):
+        self.__zname = zname
+        self.__zclass = zclass
+
+    def __str__(self):
+        if self.__zname is None:
+            return '(zone unknown/not determined)'
+        return self.__zname.to_text(True) + '/' + self.__zclass.to_text()
diff --git a/src/lib/python/isc/ddns/session.py b/src/lib/python/isc/ddns/session.py
index b15e72f..8cb4e5c 100644
--- a/src/lib/python/isc/ddns/session.py
+++ b/src/lib/python/isc/ddns/session.py
@@ -13,32 +13,77 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import isc.dns
+from isc.dns import *
 import isc.ddns.zone_config
+from isc.log import *
+from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter
+from isc.log_messages.libddns_messages import *
 
 # Result codes for UpdateSession.handle()
-UPDATE_SUCCESS = 0     # update request granted and succeeded
-UPDATE_ERROR = 1       # some error happened with a corresponding response
-UPDATE_DROP = 2        # critical error happened, no response should be sent
+UPDATE_SUCCESS = 0
+UPDATE_ERROR = 1
+UPDATE_DROP = 2
 
 # Convenient aliases of update-specific section names
-SECTION_ZONE = isc.dns.Message.SECTION_QUESTION
-SECTION_PREREQUISITE = isc.dns.Message.SECTION_ANSWER
-SECTION_UPDATE = isc.dns.Message.SECTION_AUTHORITY
+SECTION_ZONE = Message.SECTION_QUESTION
+SECTION_PREREQUISITE = Message.SECTION_ANSWER
+SECTION_UPDATE = Message.SECTION_AUTHORITY
 
-class ZoneError(Exception):
-    def __init__(self, msg, rcode):
-        Exception(msg)
+# Shortcut
+DBGLVL_TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
+
+class UpdateError(Exception):
+    '''Exception for general error in update request handling.
+
+    This exception is intended to be used internally within this module.
+    When UpdateSession.handle() encounters an error in handling an update
+    request it can raise this exception to terminate the handling.
+
+    This class is constructed with some information that may be useful for
+    subsequent possible logging:
+    - msg (string) A string explaining the error.
+    - zname (isc.dns.Name) The zone name.  Can be None when not identified.
+    - zclass (isc.dns.RRClass) The zone class.  Like zname, can be None.
+    - rcode (isc.dns.RCode) The RCODE to be set in the response message.
+    - nolog (bool) If True, it indicates there's no more need for logging.
+
+    '''
+    def __init__(self, msg, zname, zclass, rcode, nolog=False):
+        Exception.__init__(self, msg)
+        self.zname = zname
+        self.zclass = zclass
         self.rcode = rcode
+        self.nolog = nolog
 
 class UpdateSession:
     '''Protocol handling for a single dynamic update request.
 
-    TBD
+    This class is instantiated with a request message and some other
+    information that will be used for handling the request.  Its main
+    method, handle(), will process the request, and normally build
+    a response message according to the result.  The application of this
+    class can use the message to send a response to the client.
 
     '''
     def __init__(self, req_message, req_data, client_addr, zone_config):
+        '''Constructor.
+
+        Parameters:
+        - req_message (isc.dns.Message) The request message.  This must be
+          in the PARSE mode.
+        - req_data (binary) Wire format data of the request message.
+          It will be used for TSIG verification if necessary.
+        - client_addr (socket address) The address/port of the update client
+          in the form of Python socket address object.  This is mainly for
+          logging and access control.
+        - zone_config (ZoneConfig) A tentative container that encapsulates
+          the server's zone configuration.  See zone_config.py.
+
+        (It'll soon need to be passed ACL in some way, too)
+
+        '''
         self.__message = req_message
+        self.__client_addr = client_addr
         self.__zone_config = zone_config
 
     def get_message(self):
@@ -54,8 +99,19 @@ class UpdateSession:
     def handle(self):
         '''Handle the update request according to RFC2136.
 
-        Return: the result code of the session, indicating the next action
-                to be taken.
+        This method returns a tuple of the following three elements that
+        indicate the result of the request.
+        - Result code of the request processing, which are:
+          UPDATE_SUCCESS Update request granted and succeeded.
+          UPDATE_ERROR Some error happened to be reported in the response.
+          UPDATE_DROP Error happened and no response should be sent.
+          Except the case of UPDATE_DROP, the UpdateSession object will have
+          created a response that is to be returned to the request client,
+          which can be retrieved by get_message().
+        - The name of the updated zone (isc.dns.Name object) in case of
+          UPDATE_SUCCESS; otherwise None.
+        - The RR class of the updated zone (isc.dns.RRClass object) in case
+          of UPDATE_SUCCESS; otherwise None.
 
         '''
         try:
@@ -65,10 +121,14 @@ class UpdateSession:
             # self.__check_update_acl()
             # self.__do_update()
             # self.__make_response(Rcode.NOERROR())
-            return UPDATE_SUCCESS
-        except ZoneError as e:
+            return UPDATE_SUCCESS, zname, zclass
+        except UpdateError as e:
+            if not e.nolog:
+                logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_ERROR,
+                             ClientFormatter(self.__client_addr),
+                             ZoneFormatter(e.zname, e.zclass), e)
             self.__make_response(e.rcode)
-        return UPDATE_ERROR
+        return UPDATE_ERROR, None, None
 
     def __get_update_zone(self):
         '''Parse the zone section and find the zone to be updated.
@@ -82,13 +142,14 @@ class UpdateSession:
         '''
         # Validation: the zone section must contain exactly one question,
         # and it must be of type SOA.
-        if self.__message.get_rr_count(SECTION_ZONE) != 1:
-            raise ZoneError('Invalid number of records in zone section: ' +
-                            str(1), isc.dns.Rcode.FORMERR())
+        n_zones = self.__message.get_rr_count(SECTION_ZONE)
+        if n_zones != 1:
+            raise UpdateError('Invalid number of records in zone section: ' +
+                              str(n_zones), None, None, Rcode.FORMERR())
         zrecord = self.__message.get_question()[0]
-        if zrecord.get_type() != isc.dns.RRType.SOA():
-            raise ZoneError('update zone section contains non-SOA',
-                            isc.dns.Rcode.FORMERR())
+        if zrecord.get_type() != RRType.SOA():
+            raise UpdateError('update zone section contains non-SOA',
+                              None, None, Rcode.FORMERR())
 
         # See if we're serving a primary zone specified in the zone section.
         zname = zrecord.get_name()
@@ -97,9 +158,17 @@ class UpdateSession:
         if zone_type == isc.ddns.zone_config.ZONE_PRIMARY:
             return datasrc_client, zname, zclass
         elif zone_type == isc.ddns.zone_config.ZONE_SECONDARY:
-            # unconditionally refused forwarding (we don't support it yet)
-            raise ZoneError('Update forwarding not supported',
-                            isc.dns.Rcode.REFUSED())
+            # We are a secondary server; since we don't yet support update
+            # forwarding, we return 'not implemented'.
+            logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_FORWARD_FAIL,
+                         ClientFormatter(self.__client_addr),
+                         ZoneFormatter(zname, zclass))
+            raise UpdateError('forward', zname, zclass, Rcode.NOTIMP(), True)
+        # zone wasn't found
+        logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_NOTAUTH,
+                     ClientFormatter(self.__client_addr),
+                     ZoneFormatter(zname, zclass))
+        raise UpdateError('notauth', zname, zclass, Rcode.NOTAUTH(), True)
 
     def __make_response(self, rcode):
         '''Transform the internal message to the update response.
diff --git a/src/lib/python/isc/ddns/tests/Makefile.am b/src/lib/python/isc/ddns/tests/Makefile.am
index cbf2954..9795099 100644
--- a/src/lib/python/isc/ddns/tests/Makefile.am
+++ b/src/lib/python/isc/ddns/tests/Makefile.am
@@ -1,6 +1,7 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = session_tests.py
 EXTRA_DIST = $(PYTESTS)
+CLEANFILES = $(builddir)/rwtest.sqlite3.copied
 
 # If necessary, explicitly specify paths to dynamic libraries
 # required by loadable python modules.
@@ -19,6 +20,8 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
+	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/testutils/testdata \
+	TESTDATA_WRITE_PATH=$(builddir) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/ddns/tests/session_tests.py b/src/lib/python/isc/ddns/tests/session_tests.py
index 0a1ccd1..0ccd7ba 100644
--- a/src/lib/python/isc/ddns/tests/session_tests.py
+++ b/src/lib/python/isc/ddns/tests/session_tests.py
@@ -13,17 +13,28 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import os
+import shutil
 import isc.log
 import unittest
 from isc.dns import *
+from isc.datasrc import DataSourceClient
 from isc.ddns.session import *
 from isc.ddns.zone_config import *
 
 # Some common test parameters
-TEST_ZONE_NAME = Name('example.com')
+TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+READ_ZONE_DB_FILE = TESTDATA_PATH + "rwtest.sqlite3" # original, to be copied
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
+WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied"
+WRITE_ZONE_DB_CONFIG = "{ \"database_file\": \"" + WRITE_ZONE_DB_FILE + "\"}"
+
+TEST_ZONE_NAME = Name('example.org')
 UPDATE_RRTYPE = RRType.SOA()
 TEST_RRCLASS = RRClass.IN()
 TEST_ZONE_RECORD = Question(TEST_ZONE_NAME, TEST_RRCLASS, UPDATE_RRTYPE)
+TEST_CLIENT6 = ('2001:db8::1', 53, 0, 0)
+TEST_CLIENT4 = ('192.0.2.1', 53)
 
 def create_update_msg(zones=[TEST_ZONE_RECORD]):
     msg = Message(Message.RENDER)
@@ -45,13 +56,14 @@ def create_update_msg(zones=[TEST_ZONE_RECORD]):
 class SessionTest(unittest.TestCase):
     '''Session tests'''
     def setUp(self):
-        self.__client_addr = ('192.0.2.1', 53)
+        shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
+        self.__datasrc_client = DataSourceClient("sqlite3",
+                                                 WRITE_ZONE_DB_CONFIG)
         self.__update_msgdata, self.__update_msg = create_update_msg()
         self.__session = UpdateSession(self.__update_msg,
-                                       self.__update_msgdata,
-                                       self.__client_addr,
-                                       ZoneConfig([(Name("example.org"),
-                                                    TEST_RRCLASS)]))
+                                       self.__update_msgdata, TEST_CLIENT4,
+                                       ZoneConfig([], TEST_RRCLASS,
+                                                  self.__datasrc_client))
 
     def check_response(self, msg, expected_rcode):
         '''Perform common checks on update resposne message.'''
@@ -64,45 +76,76 @@ class SessionTest(unittest.TestCase):
         self.assertEqual(0, msg.get_rr_count(SECTION_UPDATE))
         self.assertEqual(0, msg.get_rr_count(Message.SECTION_ADDITIONAL))
 
+    def test_handle(self):
+        '''Basic update case'''
+        result, zname, zclass = self.__session.handle()
+        self.assertEqual(UPDATE_SUCCESS, result)
+        self.assertEqual(TEST_ZONE_NAME, zname)
+        self.assertEqual(TEST_RRCLASS, zclass)
+
+        # Just checking these are different from the success code.
+        self.assertNotEqual(UPDATE_ERROR, result)
+        self.assertNotEqual(UPDATE_DROP, result)
+
     def test_broken_request(self):
         # Zone section is empty
         msg_data, msg = create_update_msg(zones=[])
-        session = UpdateSession(msg, msg_data, None, None)
-        self.assertEqual(UPDATE_ERROR, session.handle())
+        session = UpdateSession(msg, msg_data, TEST_CLIENT6, None)
+        result, zname, zclass = session.handle()
+        self.assertEqual(UPDATE_ERROR, result)
+        self.assertEqual(None, zname)
+        self.assertEqual(None, zclass)
         self.check_response(session.get_message(), Rcode.FORMERR())
 
         # Zone section contains multiple records
         msg_data, msg = create_update_msg(zones=[TEST_ZONE_RECORD,
                                                  TEST_ZONE_RECORD])
-        session = UpdateSession(msg, msg_data, None, None)
-        self.assertEqual(UPDATE_ERROR, session.handle())
+        session = UpdateSession(msg, msg_data, TEST_CLIENT4, None)
+        self.assertEqual(UPDATE_ERROR, session.handle()[0])
         self.check_response(session.get_message(), Rcode.FORMERR())
 
         # Zone section's type is not SOA
         msg_data, msg = create_update_msg(zones=[Question(TEST_ZONE_NAME,
                                                           TEST_RRCLASS,
                                                           RRType.A())])
-        session = UpdateSession(msg, msg_data, None, None)
-        self.assertEqual(UPDATE_ERROR, session.handle())
+        session = UpdateSession(msg, msg_data, TEST_CLIENT4, None)
+        self.assertEqual(UPDATE_ERROR, session.handle()[0])
         self.check_response(session.get_message(), Rcode.FORMERR())
 
     def test_update_secondary(self):
         # specified zone is configured as a secondary.  Since this
         # implementation doesn't support update forwarding, the result
-        # should be REFUSED.
-        sec_zone = Name("example.org")
-        msg_data, msg = create_update_msg(zones=[Question(sec_zone,
+        # should be NOTIMP.
+        msg_data, msg = create_update_msg(zones=[Question(TEST_ZONE_NAME,
                                                           TEST_RRCLASS,
                                                           RRType.SOA())])
-        session = UpdateSession(msg, msg_data, None,
-                                ZoneConfig([(sec_zone, TEST_RRCLASS)]))
-        self.assertEqual(UPDATE_ERROR, session.handle())
-        self.check_response(session.get_message(), Rcode.REFUSED())
+        session = UpdateSession(msg, msg_data, TEST_CLIENT4,
+                                ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)],
+                                           TEST_RRCLASS,
+                                           self.__datasrc_client))
+        self.assertEqual(UPDATE_ERROR, session.handle()[0])
+        self.check_response(session.get_message(), Rcode.NOTIMP())
 
-    def test_handle(self):
-        self.assertEqual(UPDATE_SUCCESS, self.__session.handle())
-        self.assertNotEqual(UPDATE_ERROR, self.__session.handle())
-        self.assertNotEqual(UPDATE_DROP, self.__session.handle())
+    def check_notauth(self, zname, zclass=TEST_RRCLASS):
+        '''Common test sequence for the 'notauth' test'''
+        msg_data, msg = create_update_msg(zones=[Question(zname, zclass,
+                                                          RRType.SOA())])
+        session = UpdateSession(msg, msg_data, TEST_CLIENT4,
+                                ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)],
+                                           TEST_RRCLASS,
+                                           self.__datasrc_client))
+        self.assertEqual(UPDATE_ERROR, session.handle()[0])
+        self.check_response(session.get_message(), Rcode.NOTAUTH())
+
+    def test_update_notauth(self):
+        '''Update attempt for non authoritative zones'''
+        # zone name doesn't match
+        self.check_notauth(Name('example.com'))
+        # zone name is a subdomain of the actual authoritative zone
+        # (match must be exact)
+        self.check_notauth(Name('sub.example.org'))
+        # zone class doesn't match
+        self.check_notauth(Name('example.org'), RRClass.CH())
 
 if __name__ == "__main__":
     isc.log.init("bind10")
diff --git a/src/lib/python/isc/ddns/zone_config.py b/src/lib/python/isc/ddns/zone_config.py
index a1dd1cc..fee0396 100644
--- a/src/lib/python/isc/ddns/zone_config.py
+++ b/src/lib/python/isc/ddns/zone_config.py
@@ -13,6 +13,9 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import isc.dns
+from isc.datasrc import DataSourceClient
+
 # Constants representing zone types
 ZONE_NOTFOUND = -1              # Zone isn't found in find_zone()
 ZONE_PRIMARY = 0                # Primary zone
@@ -30,14 +33,32 @@ class ZoneConfig:
     until the details are fixed.
 
     '''
-    def __init__(self, secondaries):
+    def __init__(self, secondaries, datasrc_class, datasrc_client):
+        '''Constructor.
+
+        Parameters:
+        - secondaries: a list of 2-element tuple.  Each element is a pair
+          of isc.dns.Name and isc.dns.RRClass, and identifies a single
+          secondary zone.
+        - datasrc_class: isc.dns.RRClass object.  Specifies the RR class
+          of datasrc_client.
+        - datasrc_client: isc.dns.DataSourceClient object.  A data source
+          class for the RR class of datasrc_class.  It's expected to contain
+          a zone that is eventually updated in the ddns package.
+
+        '''
         self.__secondaries = {}
         for (zname, zclass) in secondaries:
             self.__secondaries[(zname, zclass)] = True
+        self.__datasrc_class = datasrc_class
+        self.__datasrc_client = datasrc_client
 
     def find_zone(self, zone_name, zone_class):
         '''Return the type and accessor client object for given zone.'''
-        # Right now, the client is not used, so we simply return None.
-        if (zone_name, zone_class) in self.__secondaries:
-            return ZONE_SECONDARY, None
-        return ZONE_PRIMARY, None
+        if self.__datasrc_class == zone_class and \
+                self.__datasrc_client.find_zone(zone_name)[0] == \
+                DataSourceClient.SUCCESS:
+            if (zone_name, zone_class) in self.__secondaries:
+                return ZONE_SECONDARY, None
+            return ZONE_PRIMARY, self.__datasrc_client
+        return ZONE_NOTFOUND, None
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 6b4be94..6d23df3 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -12,6 +12,7 @@ EXTRA_DIST += zonemgr_messages.py
 EXTRA_DIST += cfgmgr_messages.py
 EXTRA_DIST += config_messages.py
 EXTRA_DIST += notify_out_messages.py
+EXTRA_DIST += libddns_messages.py
 EXTRA_DIST += libxfrin_messages.py
 EXTRA_DIST += server_common_messages.py
 EXTRA_DIST += dbutil_messages.py
@@ -28,6 +29,7 @@ CLEANFILES += zonemgr_messages.pyc
 CLEANFILES += cfgmgr_messages.pyc
 CLEANFILES += config_messages.pyc
 CLEANFILES += notify_out_messages.pyc
+CLEANFILES += libddns_messages.pyc
 CLEANFILES += libxfrin_messages.pyc
 CLEANFILES += server_common_messages.pyc
 CLEANFILES += dbutil_messages.pyc
diff --git a/src/lib/python/isc/log_messages/libddns_messages.py b/src/lib/python/isc/log_messages/libddns_messages.py
new file mode 100644
index 0000000..58d886d
--- /dev/null
+++ b/src/lib/python/isc/log_messages/libddns_messages.py
@@ -0,0 +1 @@
+from work.libddns_messages import *



More information about the bind10-changes mailing list