Parcourir la source

[1455] Main prerequisite algorithm

(cleanup pending)
Jelte Jansen il y a 13 ans
Parent
commit
847501f6af
2 fichiers modifiés avec 116 ajouts et 21 suppressions
  1. 38 16
      src/lib/python/isc/ddns/session.py
  2. 78 5
      src/lib/python/isc/ddns/tests/session_tests.py

+ 38 - 16
src/lib/python/isc/ddns/session.py

@@ -123,7 +123,9 @@ class UpdateSession:
         try:
             datasrc_client, zname, zclass = self.__get_update_zone()
             # conceptual code that would follow
-            self.__check_prerequisites(datasrc_client)
+            prereq_result = self.__check_prerequisites(datasrc_client, zname, zclass)
+            if prereq_result != UPDATE_SUCCESS:
+                return prereq_result, zname, zclass
             # self.__check_update_acl()
             # self.__do_update()
             # self.__make_response(Rcode.NOERROR())
@@ -249,22 +251,42 @@ class UpdateSession:
         '''
         return not self.__check_prerequisite_name_in_use(datasrc_client, rrset)
 
-    def __check_prerequisites(self, datasrc_client):
+    def __check_prerequisites(self, datasrc_client, zname, zclass):
         '''Check the prerequisites section of the UPDATE Message.
            RFC2136 Section 2.4'''
         for rrset in self.__message.get_section(SECTION_PREREQUISITE):
-            # called atm, but not 'handled' yet
-            if rrset.getClass() == RRClass.ANY():
-                # Check for each RR in the 'set' XXX
-                self.__check_prerequisite_exists(rrset)
-            elif rrset.getClass() == datasrc_client.getClass():
-                self.__check_prerequisite_exists_value(datasrc_client, rrset)
-            elif rrset.getClass() == RRClass.NONE():
-                self.__check_prerequisite_does_not_exist(datasrc_client, rrset)
-            elif rrset.getClass() == RRClass.ANY() and rrset.getType() == RRType.ANY():
-                self.__check_prerequisite_name_exists(datasrc_client, rrset)
-            elif rrset.getClass() == RRClass.NONE() and rrset.getType() == RRType.ANY():
-                self.__check_prerequisite_name_does_not_exist(datasrc_client, rrset)
+            # First check if the name is in the zone
+            relation = rrset.get_name().compare(zname).get_relation()
+            if relation != NameComparisonResult.SUBDOMAIN and\
+               relation != NameComparisonResult.EQUAL:
+                return NOTZONE
+
+            # Algorithm taken from RFC2136 Section 3.2
+            if rrset.get_class() == RRClass.ANY():
+                if rrset.get_ttl() != 0 or rrset.get_rdata_count() != 0:
+                    return Rcode.FORMERR()
+                elif rrset.get_type() == RRType.ANY():
+                    if not self.__check_prerequisite_name_in_use(datasrc_client, rrset):
+                        return Rcode.NXDOMAIN()
+                else:
+                    if not self.__check_prerequisite_rrset_exists(datasrc_client, rrset):
+                        return Rcode.NXRRSET()
+            elif rrset.get_class() == RRClass.NONE():
+                if rrset.get_ttl() != 0 or rrset.get_rdata_count() != 0:
+                    return Rcode.FORMERR()
+                elif rrset.get_type() == RRType.ANY():
+                    if not self.__check_prerequisite_name_not_in_use(datasrc_client, rrset):
+                        return Rcode.YXDOMAIN()
+                else:
+                    if not self.__check_prerequisite_rrset_does_not_exist(datasrc_client, rrset):
+                        return Rcode.YXRRSET()
+            elif rrset.get_class() == zclass:
+                if rrset.get_ttl() != 0:
+                    return Rcode.FORMERR
+                else:
+                    if not self.__check_prerequisite_rrset_exists_value(datasrc_client, rrset):
+                        return Rcode.NXRRSET()
             else:
-                print("[XX] ERROR! unknown prerequisite")
-        pass
+                return Rcode.FORMERR
+
+        return Rcode.NOERROR()

+ 78 - 5
src/lib/python/isc/ddns/tests/session_tests.py

@@ -504,7 +504,7 @@ class SessionTest(unittest.TestCase):
                                                  isc.dns.RRClass.NONE(),
                                                  isc.dns.RRType.SOA(),
                                                  isc.dns.RRTTL(0))
-        rrset_does_not_exist_no = isc.dns.RRset(isc.dns.Name("foo.example.org"),
+        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))
@@ -519,9 +519,35 @@ class SessionTest(unittest.TestCase):
                                        isc.dns.RRTTL(0))
 
         name_not_in_use_yes = isc.dns.RRset(isc.dns.Name("foo.example.org"),
-                                            isc.dns.RRClass.ANY(),
+                                            isc.dns.RRClass.NONE(),
                                             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))
+
+        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"))
 
         # Create an UPDATE with all 5 'yes' prereqs
         data, update = create_update_msg([TEST_ZONE_RECORD],
@@ -530,10 +556,57 @@ class SessionTest(unittest.TestCase):
                                           rrset_does_not_exist_yes,
                                           name_in_use_yes,
                                           name_not_in_use_yes,
-                                          rrset_exists_value_yes
+                                          rrset_exists_value_yes,
                                          ])
-        print("[XX]")
-        print(update.to_text())
+        # 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 some permutations, 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
+        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 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'.'''
+        msg_data, msg = create_update_msg([TEST_ZONE_RECORD],
+                                          prerequisites)
+        session = UpdateSession(msg, msg_data, TEST_CLIENT4, None)
+        # compare the to_text output (nicer error messages)
+        self.assertEqual(expected.to_text(),
+            session._UpdateSession__check_prerequisites(self.__datasrc_client, TEST_ZONE_NAME, TEST_RRCLASS).to_text())
 
 if __name__ == "__main__":
     isc.log.init("bind10")