summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhamalq <hamalq@verizonmedia.com>2020-07-14 21:05:05 +0000
committerErik Olof Gunnar Andersson <eandersson@blizzard.com>2020-07-30 18:05:05 +0000
commit1fa892ea5a6db061d415a3ebe2b94105aaba57fd (patch)
treeb779741c669f85249d7f821de86489153cf58152
parent45cb4376a24d244fa677f3736299b4effa092657 (diff)
downloaddesignate-1fa892ea5a6db061d415a3ebe2b94105aaba57fd.tar.gz
Fix multi messages AXFR with TSIG
TSIG sign for multi messages AXFR was using the wrong dnspython signing function which does not support sequence messages in response which cause the AXFR error below WARNING -- Some TSIG could not be validated Change-Id: I7ce84ac9cf5bc5f6fca47dc79c51fe0becf96ac6 Closes-Bug: 1886685
-rw-r--r--designate/mdns/handler.py44
-rw-r--r--designate/tests/test_mdns/test_handler.py78
2 files changed, 108 insertions, 14 deletions
diff --git a/designate/mdns/handler.py b/designate/mdns/handler.py
index 315b4c2f..5db9f593 100644
--- a/designate/mdns/handler.py
+++ b/designate/mdns/handler.py
@@ -239,6 +239,10 @@ class RequestHandler(xfr.XFRMixin):
records.insert(0, soa_records[0])
records.append(soa_records[0])
+ # Handle multi message response with tsig
+ multi_messages = False
+ multi_messages_context = None
+
# Render the results, yielding a packet after each TooBig exception.
renderer = None
while records:
@@ -260,6 +264,9 @@ class RequestHandler(xfr.XFRMixin):
renderer.add_rrset(dns.renderer.ANSWER, rrset)
break
except dns.exception.TooBig:
+ # The response will span multiple messages since one
+ # message is not enough
+ multi_messages = True
if renderer.counts[dns.renderer.ANSWER] == 0:
# We've received a TooBig from the first attempted
# RRSet in this packet. Log a warning and abort the
@@ -280,11 +287,16 @@ class RequestHandler(xfr.XFRMixin):
)
return
- yield self._finalize_packet(renderer, request)
+ renderer, multi_messages_context = self._finalize_packet(
+ renderer, request, multi_messages,
+ multi_messages_context)
+ yield renderer
renderer = None
if renderer:
- yield self._finalize_packet(renderer, request)
+ renderer, multi_messages_context = self._finalize_packet(
+ renderer, request, multi_messages, multi_messages_context)
+ yield renderer
return
def _handle_record_query(self, request):
@@ -396,22 +408,26 @@ class RequestHandler(xfr.XFRMixin):
recordset.name, ttl, dns.rdataclass.IN, recordset.type, rdata)
@staticmethod
- def _finalize_packet(renderer, request):
+ def _finalize_packet(renderer, request, multi_messages=False,
+ multi_messages_context=None):
renderer.write_header()
if request.had_tsig:
# Make the space we reserved for TSIG available for use
renderer.max_size += TSIG_RRSIZE
- renderer.add_tsig(
- request.keyname,
- request.keyring[request.keyname],
- request.fudge,
- request.original_id,
- request.tsig_error,
- request.other_data,
- request.mac,
- request.keyalgorithm
- )
- return renderer
+ if multi_messages:
+ # The first message context will be None then the
+ # context for the prev message is used for the next
+ multi_messages_context = renderer.add_multi_tsig(
+ multi_messages_context, request.keyname,
+ request.keyring[request.keyname], request.fudge,
+ request.original_id, request.tsig_error,
+ request.other_data, request.mac, request.keyalgorithm)
+ else:
+ renderer.add_tsig(request.keyname,
+ request.keyring[request.keyname], request.fudge,
+ request.original_id, request.tsig_error,
+ request.other_data, request.mac, request.keyalgorithm)
+ return renderer, multi_messages_context
@staticmethod
def _get_max_message_size(had_tsig):
diff --git a/designate/tests/test_mdns/test_handler.py b/designate/tests/test_mdns/test_handler.py
index 56725505..4ca68e00 100644
--- a/designate/tests/test_mdns/test_handler.py
+++ b/designate/tests/test_mdns/test_handler.py
@@ -642,6 +642,84 @@ class MdnsRequestHandlerTest(MdnsTestCase):
self.assertEqual(
expected_response[1], binascii.b2a_hex(response_two))
+ @mock.patch.object(dns.renderer.Renderer, 'add_multi_tsig')
+ def test_dispatch_opcode_query_AXFR_multiple_messages_with_tsig(self,
+ mock_multi_tsig):
+ # Query is for example.com. IN AXFR
+ # id 18883
+ # opcode QUERY
+ # rcode NOERROR
+ # flags AD
+ # edns 0
+ # payload 4096
+ # ;QUESTION
+ # example.com. IN AXFR
+ # ;ANSWER
+ # ;AUTHORITY
+ # ;ADDITIONAL
+ payload = ("49c300200001000000000001076578616d706c6503636f6d0000fc0001"
+ "0000291000000000000000")
+
+ expected_response = [
+ (b"49c384000001000300000000076578616d706c6503636f6d0000fc0001c00c"
+ b"0006000100000e10002f036e7331076578616d706c65036f726700076578616"
+ b"d706c65c00c551c063900000e10000002580001518000000e10c00c0002000"
+ b"100000e100002c029046d61696cc00c0001000100000e100004c0000201"),
+
+ (b"49c384000001000100000000076578616d706c6503636f6d0000fc0001c00c"
+ b"0006000100000e10002f036e7331076578616d706c65036f72670007657861"
+ b"6d706c65c00c551c063900000e10000002580001518000000e10"),
+ ]
+
+ # Set the max-message-size to 363
+ self.config(max_message_size=363, group='service:mdns')
+
+ zone = objects.Zone.from_dict({
+ 'name': 'example.com.',
+ 'ttl': 3600,
+ 'serial': 1427899961,
+ 'email': 'example@example.com',
+ })
+
+ def _find_recordsets_axfr(context, criterion):
+ if criterion['type'] == 'SOA':
+ return [['UUID1', 'SOA', '3600', 'example.com.',
+ 'ns1.example.org. example.example.com. 1427899961 '
+ '3600 600 86400 3600', 'ACTION']]
+
+ elif criterion['type'] == '!SOA':
+ return [
+ ['UUID2', 'NS', '3600', 'example.com.', 'ns1.example.org.',
+ 'ACTION'],
+ ['UUID3', 'A', '3600', 'mail.example.com.', '192.0.2.1',
+ 'ACTION'],
+ ]
+
+ with mock.patch.object(self.storage, 'find_zone',
+ return_value=zone):
+ with mock.patch.object(self.storage, 'find_recordsets_axfr',
+ side_effect=_find_recordsets_axfr):
+ request = dns.message.from_wire(binascii.a2b_hex(payload))
+ request.environ = {'addr': self.addr, 'context': self.context}
+ request.keyring = {request.keyname: ''}
+ request.had_tsig = True
+ args = [request.keyname, request.keyring[request.keyname],
+ request.fudge, request.original_id, request.tsig_error,
+ request.other_data, request.mac, request.keyalgorithm]
+ response_generator = self.handler(request)
+ # Validate the first response
+ response_one = next(response_generator).get_wire()
+ mock_multi_tsig.assert_called_with(None, *args)
+ self.assertEqual(
+ expected_response[0], binascii.b2a_hex(response_one))
+
+ # Validate the second response
+ response_two = next(response_generator).get_wire()
+ first_msg_ctx = mock_multi_tsig.return_value
+ mock_multi_tsig.assert_called_with(first_msg_ctx, *args)
+ self.assertEqual(
+ expected_response[1], binascii.b2a_hex(response_two))
+
def test_dispatch_opcode_query_AXFR_rrset_over_max_size(self):
# Query is for example.com. IN AXFR
# id 18883