SECTION_PREREQUISITE = isc.dns.Message.SECTION_ANSWER
SECTION_UPDATE = isc.dns.Message.SECTION_AUTHORITY
+class ZoneError(Exception):
+ def __init__(self, msg, rcode):
+ Exception(msg)
+ self.rcode = rcode
+
class UpdateSession:
'''Protocol handling for a single dynamic update request.
to be taken.
'''
- # Interpret the zone section. It must contain exactly one
- # "question", and it must be of type SOA.
- if self.__message.get_rr_count(SECTION_ZONE) == 0:
- self.__message.make_response()
- self.__message.set_rcode(isc.dns.Rcode.FORMERR())
- return UPDATE_DONE
+ try:
+ zone_record = self.__get_update_zone()
+ except ZoneError as e:
+ self.__make_response(e.rcode)
return UPDATE_DONE
+
+ def __get_update_zone(self):
+ '''Interpret the zone section and return it as a Question object.
+
+ It 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())
+ zone_record = self.__message.get_question()[0]
+ if zone_record.get_type() != isc.dns.RRType.SOA():
+ raise ZoneError('update zone section contains non-SOA',
+ isc.dns.Rcode.FORMERR())
+ return zone_record
+
+ def __make_response(self, rcode):
+ '''Transform the internal message to the update response.
+
+ According RFC2136 Section 3.8, the zone section will be cleared
+ as well as other sections. The response Rcode will be set to the
+ given value.
+
+ '''
+ self.__message.make_response()
+ self.__message.clear_section(SECTION_ZONE)
+ self.__message.set_rcode(rcode)
from isc.ddns.session import *
# Some common test parameters
-TEST_ZONE = Name('example.com')
+TEST_ZONE_NAME = Name('example.com')
UPDATE_RRTYPE = RRType.SOA()
TEST_RRCLASS = RRClass.IN()
+TEST_ZONE_RECORD = Question(TEST_ZONE_NAME, TEST_RRCLASS, UPDATE_RRTYPE)
-def create_update_msg(zone_name=TEST_ZONE):
+def create_update_msg(zones=[TEST_ZONE_RECORD]):
msg = Message(Message.RENDER)
msg.set_qid(5353) # arbitrary chosen
msg.set_opcode(Opcode.UPDATE())
msg.set_rcode(Rcode.NOERROR())
- if zone_name is not None:
- msg.add_question(Question(zone_name, TEST_RRCLASS, UPDATE_RRTYPE))
+ for z in zones:
+ msg.add_question(z)
renderer = MessageRenderer()
msg.to_wire(renderer)
'''Session tests'''
def setUp(self):
self.__client_addr = ('192.0.2.1', 53)
- self.__update_msgdata, self.__update_msg = create_update_msg(TEST_ZONE)
+ self.__update_msgdata, self.__update_msg = create_update_msg()
self.__session = UpdateSession(self.__update_msg,
self.__update_msgdata,
self.__client_addr, None)
def test_broken_request(self):
# Zone section is empty
- msg_data, msg = create_update_msg(zone_name=None)
+ msg_data, msg = create_update_msg(zones=[])
+ session = UpdateSession(msg, msg_data, None, None)
+ self.assertEqual(UPDATE_DONE, session.handle())
+ 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_DONE, session.handle())
+ 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_DONE, session.handle())
self.check_response(session.get_message(), Rcode.FORMERR())