]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[master] Merge branch 'trac1458' with fixing conflicts.
authorJINMEI Tatuya <jinmei@isc.org>
Wed, 30 May 2012 22:50:17 +0000 (15:50 -0700)
committerJINMEI Tatuya <jinmei@isc.org>
Wed, 30 May 2012 22:50:17 +0000 (15:50 -0700)
1  2 
src/lib/python/isc/ddns/libddns_messages.mes
src/lib/python/isc/ddns/logger.py
src/lib/python/isc/ddns/session.py
src/lib/python/isc/ddns/tests/session_tests.py

index 190a6bf10b6afa9837351f2d8b0344e463e6bf7a,27ab475b0334638fec27f61869f1740afeafacb2..12c3a8c76d2f47b1cb077cb47836eff39567a2e0
  # No namespace declaration - these constants go in the global namespace
  # of the libddns_messages python module.
  
 +% LIBDDNS_PREREQ_FORMERR update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL.
 +The prerequisite with the given name, class and type is not well-formed.
 +The specific prerequisite is shown. In this case, it has a non-zero TTL value.
 +A FORMERR error response is sent to the client.
 +
 +% LIBDDNS_PREREQ_FORMERR_ANY update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.
 +The prerequisite with the given name, class and type is not well-formed.
 +The specific prerequisite is shown. In this case, it either has a non-zero
 +TTL value, or has rdata fields. A FORMERR error response is sent to the client.
 +
 +% LIBDDNS_PREREQ_FORMERR_CLASS update client %1 for zone %2: Format error in prerequisite (%3). Bad class.
 +The prerequisite with the given name, class and type is not well-formed.
 +The specific prerequisite is shown. In this case, the class of the
 +prerequisite should either match the class of the zone in the Zone Section,
 +or it should be ANY or NONE, and it is not. A FORMERR error response is sent
 +to the client.
 +
 +% LIBDDNS_PREREQ_FORMERR_NONE update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.
 +The prerequisite with the given name, class and type is not well-formed.
 +The specific prerequisite is shown. In this case, it either has a non-zero
 +TTL value, or has rdata fields. A FORMERR error response is sent to the client.
 +
 +% LIBDDNS_PREREQ_NAME_IN_USE_FAILED update client %1 for zone %2: 'Name is in use' prerequisite not satisfied (%3), rcode: %4
 +A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
 +was not satisfied is shown. The client is sent an error response with the
 +given rcode.
 +In this case, the specific prerequisite is 'Name is in use'. From RFC2136:
 +Name is in use.  At least one RR with a specified NAME (in
 +the zone and class specified by the Zone Section) must exist.
 +Note that this prerequisite is NOT satisfied by empty
 +nonterminals.
 +
 +% LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED update client %1 for zone %2: 'Name is not in use' (%3) prerequisite not satisfied, rcode: %4
 +A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
 +was not satisfied is shown. The client is sent an error response with the
 +given rcode.
 +In this case, the specific prerequisite is 'Name is not in use'.
 +From RFC2136:
 +Name is not in use.  No RR of any type is owned by a
 +specified NAME.  Note that this prerequisite IS satisfied by
 +empty nonterminals.
 +
 +% LIBDDNS_PREREQ_NOTZONE update client %1 for zone %2: prerequisite not in zone (%3)
 +A DNS UPDATE prerequisite has a name that does not appear to be inside
 +the zone specified in the Zone section of the UPDATE message.
 +The specific prerequisite is shown. A NOTZONE error response is sent to
 +the client.
 +
 +% LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED update client %1 for zone %2: 'RRset does not exist' (%3) prerequisite not satisfied, rcode: %4
 +A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
 +was not satisfied is shown. The client is sent an error response with the
 +given rcode.
 +In this case, the specific prerequisite is 'RRset does not exist'.
 +From RFC2136:
 +RRset does not exist.  No RRs with a specified NAME and TYPE
 +(in the zone and class denoted by the Zone Section) can exist.
 +
 +% LIBDDNS_PREREQ_RRSET_EXISTS_FAILED update client %1 for zone %2: 'RRset exists (value independent)' (%3) prerequisite not satisfied, rcode: %4
 +A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
 +was not satisfied is shown. The client is sent an error response with the
 +given rcode.
 +In this case, the specific prerequisite is 'RRset exists (value independent)'.
 +From RFC2136:
 +RRset exists (value dependent).  A set of RRs with a
 +specified NAME and TYPE exists and has the same members
 +with the same RDATAs as the RRset specified here in this
 +Section.
 +
 +% LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED update client %1 for zone %2: 'RRset exists (value dependent)' (%3) prerequisite not satisfied, rcode: %4
 +A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
 +was not satisfied is shown. The client is sent an error response with the
 +given rcode.
 +In this case, the specific prerequisite is 'RRset exists (value dependent)'.
 +From RFC2136:
 +RRset exists (value independent).  At least one RR with a
 +specified NAME and TYPE (in the zone and class specified by
 +the Zone Section) must exist.
 +
+ % LIBDDNS_UPDATE_APPROVED update client %1 for zone %2 approved
+ Debug message.  An update request was approved in terms of the zone's
+ update ACL.
++% LIBDDNS_UPDATE_DENIED update client %1 for zone %2 denied
++Informational message.  An update request was denied because it was
++rejected by the zone's update ACL.  When this library is used by
++b10-ddns, the server will respond to the request with an RCODE of
++REFUSED as described in Section 3.3 of RFC2136.
++
+ % LIBDDNS_UPDATE_DROPPED update client %1 for zone %2 dropped
+ Informational message.  An update request was denied because it was
+ rejected by the zone's update ACL.  When this library is used by
+ b10-ddns, the server will then completely ignore the request; no
+ response will be sent.
  % 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
@@@ -109,18 -41,17 +125,18 @@@ will simply return a response with an R
  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 doesn't have authority.  In theory this is 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.
+ Debug message.  An update request was received for a zone for which
+ the receiving server doesn't have authority.  In theory this is 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.
  
 -% LIBDDNS_UPDATE_DENIED update client %1 for zone %2 denied
 -Informational message.  An update request was denied because it was
 -rejected by the zone's update ACL.  When this library is used by
 -b10-ddns, the server will respond to the request with an RCODE of
 -REFUSED as described in Section 3.3 of RFC2136.
 +% LIBDDNS_UPDATE_PREREQUISITE_FAILED prerequisite failed in update update client %1 for zone %2: result code %3
 +The handling of the prerequisite section (RFC2136 Section 3.2) found
 +that one of the prerequisites was not satisfied. The result code
 +should give more information on what prerequisite type failed.
 +If the result code is FORMERR, the prerequisite section was not well-formed.
 +An error response with the given result code is sent back to the client.
index be163a8b3c1963f171fbcc64f773fa4cd60da073,656a6df833fde4ecd4fcc680e5a8977ff2a9c811..0f95bd727347316d4510951cf934637bc47d9d2b
@@@ -37,18 -39,12 +39,19 @@@ class ClientFormatter
      message is suppressed because of its log level (which is often the case
      for debug messages).
  
 +    Note: this optimization comes with the cost of instantiating the
 +    formatter object itself.  It's not really clear which overhead is
 +    heavier, and we may conclude it's actually better to just generate
 +    the strings unconditionally.  Alternatively, we can make the stored
 +    address of this object replaceable so that this object can be reused.
 +    Right now this is an open issue.
 +
      """
-     def __init__(self, addr):
+     def __init__(self, addr, tsig_record=None):
          self.__addr = addr
+         self.__tsig_record = tsig_record
  
-     def __str__(self):
+     def __format_addr(self):
          if len(self.__addr) == 2:
              return self.__addr[0] + ':' + str(self.__addr[1])
          elif len(self.__addr) == 4:
index 51da0e9fb4c11f59866b36e812e8b62e9fb88113,4e4255f0a36c087abe75c68589b129bbbf7fbc8b..6a4079ddb5368fda18fda395c821c44899b7b604
  from isc.dns import *
  import isc.ddns.zone_config
  from isc.log import *
 -from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter
 +from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter,\
 +                            RRsetFormatter
  from isc.log_messages.libddns_messages import *
+ from isc.acl.acl import ACCEPT, REJECT, DROP
 +import copy
  
  # Result codes for UpdateSession.handle()
  UPDATE_SUCCESS = 0
@@@ -125,12 -122,8 +124,12 @@@ class UpdateSession
          try:
              datasrc_client, zname, zclass = self.__get_update_zone()
              # conceptual code that would follow
 -            # self.__check_prerequisites()
 +            prereq_result = self.__check_prerequisites(datasrc_client,
 +                                                       zname, zclass)
 +            if prereq_result != Rcode.NOERROR():
 +                self.__make_response(prereq_result)
 +                return UPDATE_ERROR, zname, zclass
-             # self.__check_update_acl()
+             self.__check_update_acl(zname, zclass)
              # self.__do_update()
              # self.__make_response(Rcode.NOERROR())
              return UPDATE_SUCCESS, zname, zclass
index e5b85d7b9c9dba54b3c3f60da8e657fb5b07e7a2,192c025dd002c5529604bfc4e246bc8cb2b20560..7f1548045e049bbde8d5e41ea456d06f7b10efff
@@@ -35,37 -35,47 +35,50 @@@ 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)
+ # TSIG key for tests when needed.  The key name is TEST_ZONE_NAME.
+ TEST_TSIG_KEY = TSIGKey("example.org:SFuWd/q99SzF8Yzd1QbB9g==")
  
- def create_update_msg(zones=[TEST_ZONE_RECORD], prerequisites=[]):
 -def create_update_msg(zones=[TEST_ZONE_RECORD], tsig_key=None):
++def create_update_msg(zones=[TEST_ZONE_RECORD], prerequisites=[],
++                      tsig_key=None):
      msg = Message(Message.RENDER)
      msg.set_qid(5353)           # arbitrary chosen
      msg.set_opcode(Opcode.UPDATE())
      msg.set_rcode(Rcode.NOERROR())
      for z in zones:
          msg.add_question(z)
 +    for p in prerequisites:
 +        msg.add_rrset(SECTION_PREREQUISITE, p)
  
      renderer = MessageRenderer()
-     msg.to_wire(renderer)
+     if tsig_key is not None:
+         msg.to_wire(renderer, TSIGContext(tsig_key))
+     else:
+         msg.to_wire(renderer)
  
      # re-read the created data in the parse mode
      msg.clear(Message.PARSE)
      msg.from_wire(renderer.get_data())
  
-     return renderer.get_data(), msg
+     return msg
  
- class SessionTest(unittest.TestCase):
-     '''Session tests'''
+ class SesseionTestBase(unittest.TestCase):
+     '''Base class for all sesion related tests.
+     It just initializes common test parameters in its setUp() and defines
+     some common utility method(s).
+     '''
      def setUp(self):
          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, TEST_CLIENT4,
-                                        ZoneConfig([], TEST_RRCLASS,
-                                                   self.__datasrc_client))
+         self._datasrc_client = DataSourceClient("sqlite3",
+                                                 WRITE_ZONE_DB_CONFIG)
+         self._update_msg = create_update_msg()
 -        acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
 -                       REQUEST_LOADER.load([{"action": "ACCEPT"}])}
++        self._acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
++                             REQUEST_LOADER.load([{"action": "ACCEPT"}])}
+         self._session = UpdateSession(self._update_msg, TEST_CLIENT4,
 -                                       ZoneConfig([], TEST_RRCLASS,
 -                                                  self._datasrc_client,
 -                                                  acl_map))
++                                      ZoneConfig([], TEST_RRCLASS,
++                                                 self._datasrc_client,
++                                                 self._acl_map))
  
      def check_response(self, msg, expected_rcode):
          '''Perform common checks on update resposne message.'''
@@@ -150,455 -157,60 +160,509 @@@ class SessionTest(SesseionTestBase)
          # zone class doesn't match
          self.check_notauth(Name('example.org'), RRClass.CH())
  
-         '''Calls the given method with self.__datasrc_client
 +    def __prereq_helper(self, method, expected, rrset):
-         self.assertEqual(expected, method(self.__datasrc_client, rrset))
++        '''Calls the given method with self._datasrc_client
 +           and the given rrset, and compares the return value.
 +           Function does not do much but makes the code look nicer'''
-         method = self.__session._UpdateSession__prereq_rrset_exists
++        self.assertEqual(expected, method(self._datasrc_client, rrset))
 +
 +    def __check_prerequisite_exists_combined(self, method, rrclass, expected):
 +        '''shared code for the checks for the very similar (but reversed
 +           in behaviour) methods __prereq_rrset_exists and
 +           __prereq_rrset_does_not_exist.
 +           For rrset_exists, rrclass should be ANY, for rrset_does_not_exist,
 +           it should be NONE.
 +        '''
 +        # Basic existence checks
 +        # www.example.org should have an A, but not an MX
 +        rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +        rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                              rrclass, isc.dns.RRType.MX(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        # example.org should have an MX, but not an A
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              rrclass, isc.dns.RRType.MX(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        # Also check the case where the name does not even exist
 +        rrset = isc.dns.RRset(isc.dns.Name("doesnotexist.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        # Wildcard expansion should not be applied, but literal matches
 +        # should work
 +        rrset = isc.dns.RRset(isc.dns.Name("foo.wildcard.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("*.wildcard.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +        # Likewise, CNAME directly should match, but what it points to should
 +        # not
 +        rrset = isc.dns.RRset(isc.dns.Name("cname.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("cname.example.org"),
 +                              rrclass, isc.dns.RRType.CNAME(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +        # And also make sure a delegation (itself) is not treated as existing
 +        # data
 +        rrset = isc.dns.RRset(isc.dns.Name("foo.sub.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +        # But the delegation data itself should match
 +        rrset = isc.dns.RRset(isc.dns.Name("sub.example.org"),
 +                              rrclass, isc.dns.RRType.NS(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +        # As should glue
 +        rrset = isc.dns.RRset(isc.dns.Name("ns.sub.example.org"),
 +                              rrclass, isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +    def test_check_prerequisite_exists(self):
-         method = self.__session._UpdateSession__prereq_rrset_does_not_exist
++        method = self._session._UpdateSession__prereq_rrset_exists
 +        self.__check_prerequisite_exists_combined(method,
 +                                                  isc.dns.RRClass.ANY(),
 +                                                  True)
 +
 +    def test_check_prerequisite_does_not_exist(self):
-         method = self.__session._UpdateSession__prereq_rrset_exists_value
++        method = self._session._UpdateSession__prereq_rrset_does_not_exist
 +        self.__check_prerequisite_exists_combined(method,
 +                                                  isc.dns.RRClass.NONE(),
 +                                                  False)
 +
 +    def test_check_prerequisite_exists_value(self):
-         method = self.__session._UpdateSession__prereq_name_in_use
++        method = self._session._UpdateSession__prereq_rrset_exists_value
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                              isc.dns.RRClass.IN(), isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        # empty one should not match
 +        self.__prereq_helper(method, False, rrset)
 +
 +        # When the rdata is added, it should match
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "192.0.2.1"))
 +        self.__prereq_helper(method, True, rrset)
 +
 +        # But adding more should not
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "192.0.2.2"))
 +        self.__prereq_helper(method, False, rrset)
 +
 +        # Also test one with more than one RR
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.IN(), isc.dns.RRType.NS(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns1.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns2.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns3.example.org."))
 +        self.__prereq_helper(method, True, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns4.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +
 +        # Repeat that, but try a different order of Rdata addition
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.IN(), isc.dns.RRType.NS(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns3.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns2.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns1.example.org."))
 +        self.__prereq_helper(method, True, rrset)
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "ns4.example.org."))
 +        self.__prereq_helper(method, False, rrset)
 +
 +        # and test one where the name does not even exist
 +        rrset = isc.dns.RRset(isc.dns.Name("doesnotexist.example.org"),
 +                              isc.dns.RRClass.IN(), isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "192.0.2.1"))
 +        self.__prereq_helper(method, False, rrset)
 +
 +    def __check_prerequisite_name_in_use_combined(self, method, rrclass,
 +                                                  expected):
 +        '''shared code for the checks for the very similar (but reversed
 +           in behaviour) methods __prereq_name_in_use and
 +           __prereq_name_not_in_use
 +        '''
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("doesnotexist.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("belowdelegation.sub.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        rrset = isc.dns.RRset(isc.dns.Name("foo.wildcard.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +
 +        # empty nonterminal should not match
 +        rrset = isc.dns.RRset(isc.dns.Name("nonterminal.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, not expected, rrset)
 +        rrset = isc.dns.RRset(isc.dns.Name("empty.nonterminal.example.org"),
 +                              rrclass, isc.dns.RRType.ANY(),
 +                              isc.dns.RRTTL(0))
 +        self.__prereq_helper(method, expected, rrset)
 +
 +    def test_check_prerequisite_name_in_use(self):
-         method = self.__session._UpdateSession__prereq_name_not_in_use
++        method = self._session._UpdateSession__prereq_name_in_use
 +        self.__check_prerequisite_name_in_use_combined(method,
 +                                                       isc.dns.RRClass.ANY(),
 +                                                       True)
 +
 +    def test_check_prerequisite_name_not_in_use(self):
-         msg_data, msg = create_update_msg([TEST_ZONE_RECORD],
-                                           prerequisites)
-         zconfig = ZoneConfig([], TEST_RRCLASS, self.__datasrc_client)
-         session = UpdateSession(msg, msg_data, TEST_CLIENT4, zconfig)
++        method = self._session._UpdateSession__prereq_name_not_in_use
 +        self.__check_prerequisite_name_in_use_combined(method,
 +                                                       isc.dns.RRClass.NONE(),
 +                                                       False)
 +
 +    def check_prerequisite_result(self, expected, prerequisites):
 +        '''Helper method for checking the result of a prerequisite check;
 +           creates an update session, and fills it with the list of rrsets
 +           from 'prerequisites'. Then checks if __check_prerequisites()
 +           returns the Rcode specified in 'expected'.'''
-             session._UpdateSession__check_prerequisites(self.__datasrc_client,
++        msg = create_update_msg([TEST_ZONE_RECORD], prerequisites)
++        zconfig = ZoneConfig([], TEST_RRCLASS, self._datasrc_client,
++                             self._acl_map)
++        session = UpdateSession(msg, TEST_CLIENT4, zconfig)
 +        # compare the to_text output of the rcodes (nicer error messages)
 +        # This call itself should also be done by handle(),
 +        # but just for better failures, it is first called on its own
 +        self.assertEqual(expected.to_text(),
-         data, update = create_update_msg([TEST_ZONE_RECORD],
-                                          [
-                                           rrset_exists_yes,
-                                           rrset_does_not_exist_yes,
-                                           name_in_use_yes,
-                                           name_not_in_use_yes,
-                                           rrset_exists_value_yes,
-                                          ])
++            session._UpdateSession__check_prerequisites(self._datasrc_client,
 +                                                        TEST_ZONE_NAME,
 +                                                        TEST_RRCLASS).to_text())
 +        # Now see if handle finds the same result
 +        (result, _, _) = session.handle()
 +        self.assertEqual(expected,
 +                         session._UpdateSession__message.get_rcode())
 +        # And that the result looks right
 +        if expected == Rcode.NOERROR():
 +            self.assertEqual(UPDATE_SUCCESS, result)
 +        else:
 +            self.assertEqual(UPDATE_ERROR, result)
 +
 +    def test_check_prerequisites(self):
 +        # This test checks if the actual prerequisite-type-specific
 +        # methods are called.
 +        # It does test all types of prerequisites, but it does not test
 +        # every possible result for those types (those are tested above,
 +        # in the specific prerequisite type tests)
 +
 +        # Let's first define a number of prereq's that should succeed
 +        rrset_exists_yes = isc.dns.RRset(isc.dns.Name("example.org"),
 +                                         isc.dns.RRClass.ANY(),
 +                                         isc.dns.RRType.SOA(),
 +                                         isc.dns.RRTTL(0))
 +
 +        rrset_exists_value_yes = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                                               isc.dns.RRClass.IN(),
 +                                               isc.dns.RRType.A(),
 +                                               isc.dns.RRTTL(0))
 +        rrset_exists_value_yes.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                                       isc.dns.RRClass.IN(),
 +                                                       "192.0.2.1"))
 +
 +        rrset_does_not_exist_yes = isc.dns.RRset(isc.dns.Name("foo.example.org"),
 +                                                 isc.dns.RRClass.NONE(),
 +                                                 isc.dns.RRType.SOA(),
 +                                                 isc.dns.RRTTL(0))
 +
 +        name_in_use_yes = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                                        isc.dns.RRClass.ANY(),
 +                                        isc.dns.RRType.ANY(),
 +                                        isc.dns.RRTTL(0))
 +
 +        name_not_in_use_yes = isc.dns.RRset(isc.dns.Name("foo.example.org"),
 +                                            isc.dns.RRClass.NONE(),
 +                                            isc.dns.RRType.ANY(),
 +                                            isc.dns.RRTTL(0))
 +
 +        rrset_exists_value_1 = isc.dns.RRset(isc.dns.Name("example.org"),
 +                                             isc.dns.RRClass.IN(),
 +                                             isc.dns.RRType.NS(),
 +                                             isc.dns.RRTTL(0))
 +        rrset_exists_value_1.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                                     isc.dns.RRClass.IN(),
 +                                                     "ns1.example.org"))
 +        rrset_exists_value_2 = isc.dns.RRset(isc.dns.Name("example.org"),
 +                                             isc.dns.RRClass.IN(),
 +                                             isc.dns.RRType.NS(),
 +                                             isc.dns.RRTTL(0))
 +        rrset_exists_value_2.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                                     isc.dns.RRClass.IN(),
 +                                                     "ns2.example.org"))
 +        rrset_exists_value_3 = isc.dns.RRset(isc.dns.Name("example.org"),
 +                                             isc.dns.RRClass.IN(),
 +                                             isc.dns.RRType.NS(),
 +                                             isc.dns.RRTTL(0))
 +        rrset_exists_value_3.add_rdata(isc.dns.Rdata(isc.dns.RRType.NS(),
 +                                                     isc.dns.RRClass.IN(),
 +                                                     "ns3.example.org"))
 +
 +        # and a number that should not
 +        rrset_exists_no = isc.dns.RRset(isc.dns.Name("foo.example.org"),
 +                                        isc.dns.RRClass.ANY(),
 +                                        isc.dns.RRType.SOA(),
 +                                        isc.dns.RRTTL(0))
 +
 +
 +        rrset_exists_value_no = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                                              isc.dns.RRClass.IN(),
 +                                              isc.dns.RRType.A(),
 +                                              isc.dns.RRTTL(0))
 +        rrset_exists_value_no.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                                      isc.dns.RRClass.IN(),
 +                                                      "192.0.2.2"))
 +
 +        rrset_does_not_exist_no = isc.dns.RRset(isc.dns.Name("example.org"),
 +                                                isc.dns.RRClass.NONE(),
 +                                                isc.dns.RRType.SOA(),
 +                                                isc.dns.RRTTL(0))
 +
 +        name_in_use_no = isc.dns.RRset(isc.dns.Name("foo.example.org"),
 +                                       isc.dns.RRClass.ANY(),
 +                                       isc.dns.RRType.ANY(),
 +                                       isc.dns.RRTTL(0))
 +
 +        name_not_in_use_no = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                                           isc.dns.RRClass.NONE(),
 +                                           isc.dns.RRType.ANY(),
 +                                           isc.dns.RRTTL(0))
 +
 +        # Create an UPDATE with all 5 'yes' prereqs
++        create_update_msg([TEST_ZONE_RECORD],
++                          [rrset_exists_yes,
++                           rrset_does_not_exist_yes,
++                           name_in_use_yes,
++                           name_not_in_use_yes,
++                           rrset_exists_value_yes,
++                           ])
++
 +        # check 'no' result codes
 +        self.check_prerequisite_result(Rcode.NXRRSET(),
 +                                       [ rrset_exists_no ])
 +        self.check_prerequisite_result(Rcode.NXRRSET(),
 +                                       [ rrset_exists_value_no ])
 +        self.check_prerequisite_result(Rcode.YXRRSET(),
 +                                       [ rrset_does_not_exist_no ])
 +        self.check_prerequisite_result(Rcode.NXDOMAIN(),
 +                                       [ name_in_use_no ])
 +        self.check_prerequisite_result(Rcode.YXDOMAIN(),
 +                                       [ name_not_in_use_no ])
 +
 +        # the 'yes' codes should result in ok
 +        self.check_prerequisite_result(Rcode.NOERROR(),
 +                                       [ rrset_exists_yes,
 +                                         rrset_exists_value_yes,
 +                                         rrset_does_not_exist_yes,
 +                                         name_in_use_yes,
 +                                         name_not_in_use_yes,
 +                                         rrset_exists_value_1,
 +                                         rrset_exists_value_2,
 +                                         rrset_exists_value_3])
 +
 +        # try out a permutation, note that one rrset is split up,
 +        # and the order of the RRs should not matter
 +        self.check_prerequisite_result(Rcode.NOERROR(),
 +                                       [ rrset_exists_value_3,
 +                                         rrset_exists_yes,
 +                                         rrset_exists_value_2,
 +                                         name_in_use_yes,
 +                                         rrset_exists_value_1])
 +
 +        # Should fail on the first error, even if most of the
 +        # prerequisites are ok
 +        self.check_prerequisite_result(Rcode.NXDOMAIN(),
 +                                       [ rrset_exists_value_3,
 +                                         rrset_exists_yes,
 +                                         rrset_exists_value_2,
 +                                         name_in_use_yes,
 +                                         name_in_use_no,
 +                                         rrset_exists_value_1])
 +
 +    def test_prerequisite_notzone(self):
 +        rrset = isc.dns.RRset(isc.dns.Name("some.other.zone."),
 +                              isc.dns.RRClass.ANY(),
 +                              isc.dns.RRType.SOA(),
 +                              isc.dns.RRTTL(0))
 +        self.check_prerequisite_result(Rcode.NOTZONE(), [ rrset ])
 +
 +    def test_prerequisites_formerr(self):
 +        # test for form errors in the prerequisite section
 +
 +        # Class ANY, non-zero TTL
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.ANY(),
 +                              isc.dns.RRType.SOA(),
 +                              isc.dns.RRTTL(1))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
 +        # Class ANY, but with rdata
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.ANY(),
 +                              isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.ANY(),
 +                                      "\# 04 00 00 00 00"))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
 +        # Class NONE, non-zero TTL
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.NONE(),
 +                              isc.dns.RRType.SOA(),
 +                              isc.dns.RRTTL(1))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
 +        # Class NONE, but with rdata
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.NONE(),
 +                              isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(0))
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.NONE(),
 +                                      "\# 04 00 00 00 00"))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
 +        # Matching class and type, but non-zero TTL
 +        rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
 +                              isc.dns.RRClass.IN(),
 +                              isc.dns.RRType.A(),
 +                              isc.dns.RRTTL(1))
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.A(),
 +                                      isc.dns.RRClass.IN(),
 +                                      "192.0.2.1"))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
 +        # Completely different class
 +        rrset = isc.dns.RRset(isc.dns.Name("example.org"),
 +                              isc.dns.RRClass.CH(),
 +                              isc.dns.RRType.TXT(),
 +                              isc.dns.RRTTL(0))
 +        rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.TXT(),
 +                                      isc.dns.RRClass.CH(),
 +                                      "foo"))
 +        self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
 +
+ class SessionACLTest(SesseionTestBase):
+     '''ACL related tests for update session.'''
+     def test_update_acl_check(self):
+         '''Test for various ACL checks.
+         Note that accepted cases are covered in the basic tests.
+         '''
+         # create a separate session, with default (empty) ACL map.
+         session = UpdateSession(self._update_msg,
+                                 TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+                                                          self._datasrc_client))
+         # then the request should be rejected.
+         self.assertEqual((UPDATE_ERROR, None, None), session.handle())
+         # recreate the request message, and test with an ACL that would result
+         # in 'DROP'.  get_message() should return None.
+         msg = create_update_msg()
+         acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+                        REQUEST_LOADER.load([{"action": "DROP", "from":
+                                                  TEST_CLIENT4[0]}])}
+         session = UpdateSession(msg, TEST_CLIENT4,
+                                 ZoneConfig([], TEST_RRCLASS,
+                                            self._datasrc_client, acl_map))
+         self.assertEqual((UPDATE_DROP, None, None), session.handle())
+         self.assertEqual(None, session.get_message())
+     def test_update_tsigacl_check(self):
+         '''Test for various ACL checks using TSIG.'''
+         # This ACL will accept requests from TEST_CLIENT4 (any port) *and*
+         # has TSIG signed by TEST_ZONE_NAME; all others will be rejected.
+         acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+                        REQUEST_LOADER.load([{"action": "ACCEPT",
+                                              "from": TEST_CLIENT4[0],
+                                              "key": TEST_ZONE_NAME.to_text()}])}
+         # If the message doesn't contain TSIG, it doesn't match the ACCEPT
+         # ACL entry, and the request should be rejected.
+         session = UpdateSession(self._update_msg,
+                                 TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+                                                          self._datasrc_client,
+                                                          acl_map))
+         self.assertEqual((UPDATE_ERROR, None, None), session.handle())
+         self.check_response(session.get_message(), Rcode.REFUSED())
+         # If the message contains TSIG, it should match the ACCEPT
+         # ACL entry, and the request should be granted.
+         session = UpdateSession(create_update_msg(tsig_key=TEST_TSIG_KEY),
+                                 TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+                                                          self._datasrc_client,
+                                                          acl_map))
+         self.assertEqual((UPDATE_SUCCESS, TEST_ZONE_NAME, TEST_RRCLASS),
+                          session.handle())
  if __name__ == "__main__":
      isc.log.init("bind10")
      isc.log.resetUnitTestRootLogger()