session.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. # Copyright (C) 2012 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. from isc.dns import *
  16. import isc.ddns.zone_config
  17. from isc.log import *
  18. from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter
  19. from isc.log_messages.libddns_messages import *
  20. # Result codes for UpdateSession.handle()
  21. UPDATE_SUCCESS = 0
  22. UPDATE_ERROR = 1
  23. UPDATE_DROP = 2
  24. # Convenient aliases of update-specific section names
  25. SECTION_ZONE = Message.SECTION_QUESTION
  26. SECTION_PREREQUISITE = Message.SECTION_ANSWER
  27. SECTION_UPDATE = Message.SECTION_AUTHORITY
  28. class UpdateError(Exception):
  29. def __init__(self, msg, zname, zclass, rcode):
  30. Exception.__init__(self, msg)
  31. self.zname = zname
  32. self.zclass = zclass
  33. self.rcode = rcode
  34. class UpdateSession:
  35. '''Protocol handling for a single dynamic update request.
  36. TBD
  37. '''
  38. def __init__(self, req_message, req_data, client_addr, zone_config):
  39. self.__message = req_message
  40. self.__client_addr = client_addr
  41. self.__zone_config = zone_config
  42. def get_message(self):
  43. '''Return the update message.
  44. After handle() is called, it's generally transformed to the response
  45. to be returned to the client; otherwise it would be identical to
  46. the request message passed on construction.
  47. '''
  48. return self.__message
  49. def handle(self):
  50. '''Handle the update request according to RFC2136.
  51. This method returns a tuple of the following three elements that
  52. indicate the result of the request.
  53. - Result code of the request processing, which are:
  54. UPDATE_SUCCESS Update request granted and succeeded.
  55. UPDATE_ERROR Some error happened to be reported in the response.
  56. UPDATE_DROP Error happened and no response should be sent.
  57. Except the case of UPDATE_DROP, the UpdateSession object will have
  58. created a response that is to be returned to the request client,
  59. which can be retrieved by get_message().
  60. - The name of the updated zone (isc.dns.Name object) in case of
  61. UPDATE_SUCCESS; otherwise None.
  62. - The RR class of the updated zone (isc.dns.RRClass object) in case
  63. of UPDATE_SUCCESS; otherwise None.
  64. '''
  65. try:
  66. datasrc_client, zname, zclass = self.__get_update_zone()
  67. # conceptual code that would follow
  68. # self.__check_prerequisites()
  69. # self.__check_update_acl()
  70. # self.__do_update()
  71. # self.__make_response(Rcode.NOERROR())
  72. return UPDATE_SUCCESS, zname, zclass
  73. except UpdateError as e:
  74. logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_ERROR,
  75. ClientFormatter(self.__client_addr),
  76. ZoneFormatter(e.zname, e.zclass), e)
  77. self.__make_response(e.rcode)
  78. return UPDATE_ERROR, None, None
  79. def __get_update_zone(self):
  80. '''Parse the zone section and find the zone to be updated.
  81. If the zone section is valid and the specified zone is found in
  82. the configuration, it returns a tuple of:
  83. - A matching data source that contains the specified zone
  84. - The zone name as a Name object
  85. - The zone class as an RRClass object
  86. '''
  87. # Validation: the zone section must contain exactly one question,
  88. # and it must be of type SOA.
  89. n_zones = self.__message.get_rr_count(SECTION_ZONE)
  90. if n_zones != 1:
  91. raise UpdateError('Invalid number of records in zone section: ' +
  92. str(n_zones), None, None, Rcode.FORMERR())
  93. zrecord = self.__message.get_question()[0]
  94. if zrecord.get_type() != RRType.SOA():
  95. raise UpdateError('update zone section contains non-SOA',
  96. None, None, Rcode.FORMERR())
  97. # See if we're serving a primary zone specified in the zone section.
  98. zname = zrecord.get_name()
  99. zclass = zrecord.get_class()
  100. zone_type, datasrc_client = self.__zone_config.find_zone(zname, zclass)
  101. if zone_type == isc.ddns.zone_config.ZONE_PRIMARY:
  102. return datasrc_client, zname, zclass
  103. elif zone_type == isc.ddns.zone_config.ZONE_SECONDARY:
  104. # unconditionally refused forwarding (we don't support it yet)
  105. raise UpdateError('Update forwarding not supported',
  106. zname, zclass, Rcode.REFUSED())
  107. # zone wasn't found
  108. raise UpdateError('not authoritative for update zone',
  109. zname, zclass, Rcode.NOTAUTH())
  110. def __make_response(self, rcode):
  111. '''Transform the internal message to the update response.
  112. According RFC2136 Section 3.8, the zone section will be cleared
  113. as well as other sections. The response Rcode will be set to the
  114. given value.
  115. '''
  116. self.__message.make_response()
  117. self.__message.clear_section(SECTION_ZONE)
  118. self.__message.set_rcode(rcode)