Fix dns.query.tcp/udp not always handling ipv6 properly

Created a new generic send_dns_msg that properly handles
both ip and hostnames and fully supports ipv4 and ipv6.

Also, moved all usage of dns.query.tcp/udp to a central
location.

Change-Id: I403ed6716b3ceffa1910269adf0e352f75e9dd5b
This commit is contained in:
Erik Olof Gunnar Andersson 2021-10-12 18:33:08 -07:00
parent 7237c3d665
commit acd930d342
10 changed files with 208 additions and 261 deletions

View File

@ -32,18 +32,16 @@ import dns.opcode
import dns.rcode
import dns.rdataclass
import dns.rdatatype
import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend import base
import designate.backend.private_codes as pcodes
from designate.backend import private_codes
from designate.conf.agent import DEFAULT_AGENT_PORT
from designate import dnsutils
from designate import exceptions
from designate.mdns import rpcapi as mdns_api
dns_query = eventlet.import_patched('dns.query')
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@ -72,9 +70,9 @@ class AgentPoolBackend(base.Backend):
response, retry = self._make_and_send_dns_message(
zone.name,
self.timeout,
pcodes.CC,
pcodes.CREATE,
pcodes.CLASSCC,
private_codes.CC,
private_codes.CREATE,
private_codes.CLASSCC,
self.host,
self.port
)
@ -100,9 +98,9 @@ class AgentPoolBackend(base.Backend):
response, retry = self._make_and_send_dns_message(
zone.name,
self.timeout,
pcodes.CC,
pcodes.DELETE,
pcodes.CLASSCC,
private_codes.CC,
private_codes.DELETE,
private_codes.CLASSCC,
self.host,
self.port
)
@ -134,7 +132,7 @@ class AgentPoolBackend(base.Backend):
'port': dest_port, 'timeout': timeout,
'retry': retry})
response = None
elif isinstance(response, dns_query.BadResponse):
elif isinstance(response, dns.query.BadResponse):
LOG.warning("Got BadResponse while trying to send '%(msg)s' for "
"'%(zone)s' to '%(server)s:%(port)d'. "
"Timeout='%(timeout)d' seconds. Retry='%(retry)d'",
@ -173,14 +171,10 @@ class AgentPoolBackend(base.Backend):
def _send_dns_message(self, dns_message, dest_ip, dest_port, timeout):
try:
if not CONF['service:mdns'].all_tcp:
response = dns_query.udp(
dns_message, dest_ip, port=dest_port, timeout=timeout)
else:
response = dns_query.tcp(
dns_message, dest_ip, port=dest_port, timeout=timeout)
return response
return dnsutils.send_dns_message(
dns_message, dest_ip, port=dest_port, timeout=timeout
)
except dns.exception.Timeout as timeout:
return timeout
except dns_query.BadResponse as badResponse:
except dns.query.BadResponse as badResponse:
return badResponse

View File

@ -20,7 +20,8 @@ import time
import dns
import dns.exception
from dns import rdatatype
import dns.query
import dns.rdatatype
import dns.zone
import eventlet
from oslo_log import log as logging
@ -312,7 +313,7 @@ def dnspyrecords_to_recordsetlist(dnspython_records):
def dnspythonrecord_to_recordset(rname, rdataset):
record_type = rdatatype.to_text(rdataset.rdtype)
record_type = dns.rdatatype.to_text(rdataset.rdtype)
name = rname.to_text()
if isinstance(name, bytes):
@ -346,39 +347,122 @@ def do_axfr(zone_name, servers, timeout=None, source=None):
timeout = timeout or CONF["service:mdns"].xfr_timeout
xfr = None
for srv in servers:
to = eventlet.Timeout(timeout)
log_info = {'name': zone_name, 'host': srv}
try:
LOG.info("Doing AXFR for %(name)s from %(host)s", log_info)
xfr = dns.query.xfr(srv['host'], zone_name, relativize=False,
timeout=1, port=srv['port'], source=source)
raw_zone = dns.zone.from_xfr(xfr, relativize=False)
break
except eventlet.Timeout as t:
if t == to:
LOG.error("AXFR timed out for %(name)s from %(host)s",
log_info)
continue
except dns.exception.FormError:
LOG.error("Zone %(name)s is not present on %(host)s."
"Trying next server.", log_info)
except socket.error:
LOG.error("Connection error when doing AXFR for %(name)s from "
"%(host)s", log_info)
except Exception:
LOG.exception("Problem doing AXFR %(name)s from %(host)s. "
for address in get_ip_addresses(srv['host']):
to = eventlet.Timeout(timeout)
log_info = {'name': zone_name, 'host': srv, 'address': address}
try:
LOG.info(
'Doing AXFR for %(name)s from %(host)s %(address)s',
log_info
)
xfr = dns.query.xfr(
address, zone_name, relativize=False, timeout=1,
port=srv['port'], source=source
)
raw_zone = dns.zone.from_xfr(xfr, relativize=False)
LOG.debug("AXFR Successful for %s", raw_zone.origin.to_text())
return raw_zone
except eventlet.Timeout as t:
if t == to:
LOG.error("AXFR timed out for %(name)s from %(host)s",
log_info)
continue
except dns.exception.FormError:
LOG.error("Zone %(name)s is not present on %(host)s."
"Trying next server.", log_info)
finally:
to.cancel()
continue
else:
raise exceptions.XFRFailure(
"XFR failed for %(name)s. No servers in %(servers)s was reached." %
{"name": zone_name, "servers": servers})
except socket.error:
LOG.error("Connection error when doing AXFR for %(name)s from "
"%(host)s", log_info)
except Exception:
LOG.exception("Problem doing AXFR %(name)s from %(host)s. "
"Trying next server.", log_info)
finally:
to.cancel()
LOG.debug("AXFR Successful for %s", raw_zone.origin.to_text())
raise exceptions.XFRFailure(
"XFR failed for %(name)s. No servers in %(servers)s was reached." %
{"name": zone_name, "servers": servers}
)
return raw_zone
def prepare_msg(zone_name, rdatatype=dns.rdatatype.SOA,
dns_opcode=dns.opcode.QUERY):
"""
Do the needful to set up a dns packet with dnspython
"""
dns_message = dns.message.make_query(zone_name, rdatatype)
dns_message.set_opcode(dns_opcode)
return dns_message
def dig(zone_name, host, rdatatype, port=53):
"""
Set up and send a regular dns query, datatype configurable
"""
query = prepare_msg(zone_name, rdatatype=rdatatype)
return send_dns_message(query, host, port=port)
def notify(zone_name, host, port=53):
"""
Set up a notify packet and send it
"""
msg = prepare_msg(zone_name, dns_opcode=dns.opcode.NOTIFY)
return send_dns_message(msg, host, port=port)
def send_dns_message(dns_message, host, port=53, timeout=10):
"""
Send the dns message and return the response
:return: dns.Message of the response to the dns query
"""
ip_address = get_ip_address(host)
# This can raise some exceptions, but we'll catch them elsewhere
if not CONF['service:mdns'].all_tcp:
return dns.query.udp(
dns_message, ip_address, port=port, timeout=timeout)
return dns.query.tcp(
dns_message, ip_address, port=port, timeout=timeout)
def get_serial(zone_name, host, port=53):
"""
Possibly raises dns.exception.Timeout or dns.query.BadResponse.
Possibly returns 0 if, e.g., the answer section is empty.
"""
resp = dig(zone_name, host, dns.rdatatype.SOA, port=port)
if not resp.answer:
return 0
rdataset = resp.answer[0].to_rdataset()
if not rdataset:
return 0
return rdataset[0].serial
def get_ip_address(ip_address_or_hostname):
"""
Provide an ip or hostname and return a valid ip4 or ipv6 address.
:return: ip address
"""
addresses = get_ip_addresses(ip_address_or_hostname)
if not addresses:
return None
return addresses[0]
def get_ip_addresses(ip_address_or_hostname):
"""
Provide an ip or hostname and return all valid ip4 or ipv6 addresses.
:return: ip addresses
"""
addresses = []
for res in socket.getaddrinfo(ip_address_or_hostname, 0):
addresses.append(res[4][0])
return list(set(addresses))

View File

@ -28,6 +28,7 @@ import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from designate import dnsutils
from designate.mdns import base
from designate.metrics import metrics
@ -186,8 +187,9 @@ class NotifyEndpoint(base.BaseEndpoint):
'zone': zone.name, 'server': host,
'port': port})
try:
response = self._send_dns_message(dns_message, host, port,
timeout)
response = dnsutils.send_dns_message(
dns_message, host, port, timeout=timeout
)
except socket.error as e:
if e.errno != socket.errno.EAGAIN:
@ -285,21 +287,3 @@ class NotifyEndpoint(base.BaseEndpoint):
dns_message.flags |= dns.flags.RD
return dns_message
def _send_dns_message(self, dns_message, host, port, timeout):
"""
Send DNS Message over TCP or UDP, return response.
:param dns_message: The dns message that needs to be sent.
:param host: The destination ip of dns_message.
:param port: The destination port of dns_message.
:param timeout: The timeout in seconds to wait for a response.
:return: response
"""
send = dns_query.tcp if CONF['service:mdns'].all_tcp else dns_query.udp
return send(
dns_message,
socket.gethostbyname(host),
port=port,
timeout=timeout
)

View File

@ -14,11 +14,13 @@
from unittest import mock
import dns
import dns.query
import dns.rdataclass
import dns.rdatatype
import designate.backend.agent as agent
import designate.backend.private_codes as pcodes
from designate import dnsutils
from designate import exceptions
from designate.mdns import rpcapi as mdns_api
from designate import objects
@ -130,7 +132,7 @@ class AgentBackendTestCase(tests.TestCase):
def test_make_and_send_dns_message_bad_response(self):
self.backend._make_dns_message = mock.Mock(return_value='')
self.backend._send_dns_message = mock.Mock(
return_value=agent.dns_query.BadResponse())
return_value=dns.query.BadResponse())
out = self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
@ -176,50 +178,16 @@ class AgentBackendTestCase(tests.TestCase):
self.assertEqual((response, 0), out)
@mock.patch.object(agent.dns_query, 'tcp')
@mock.patch.object(agent.dns_query, 'udp')
def test_send_dns_message(self, mock_udp, mock_tcp):
@mock.patch.object(dnsutils, 'get_ip_address')
@mock.patch.object(dns.query, 'tcp')
@mock.patch.object(dns.query, 'udp')
def test_send_dns_message(self, mock_udp, mock_tcp, mock_get_ip_address):
mock_udp.return_value = 'mock udp resp'
mock_get_ip_address.return_value = '10.0.1.39'
out = self.backend._send_dns_message('msg', 'host', 123, 1)
out = self.backend._send_dns_message('msg', '10.0.1.39', 123, 1)
self.assertFalse(agent.dns_query.tcp.called)
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
timeout=1)
self.assertFalse(mock_tcp.called)
mock_udp.assert_called_with('msg', '10.0.1.39', port=123,
timeout=1)
self.assertEqual('mock udp resp', out)
@mock.patch.object(agent.dns_query, 'tcp')
@mock.patch.object(agent.dns_query, 'udp')
def test_send_dns_message_timeout(self, mock_udp, mock_tcp):
mock_udp.side_effect = dns.exception.Timeout
out = self.backend._send_dns_message('msg', 'host', 123, 1)
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
timeout=1)
self.assertIsInstance(out, dns.exception.Timeout)
@mock.patch.object(agent.dns_query, 'tcp')
@mock.patch.object(agent.dns_query, 'udp')
def test_send_dns_message_bad_response(self, mock_udp, mock_tcp):
mock_udp.side_effect = agent.dns_query.BadResponse
out = self.backend._send_dns_message('msg', 'host', 123, 1)
agent.dns_query.udp.assert_called_with('msg', 'host', port=123,
timeout=1)
self.assertIsInstance(out, agent.dns_query.BadResponse)
@mock.patch.object(agent.dns_query, 'tcp')
@mock.patch.object(agent.dns_query, 'udp')
def test_send_dns_message_tcp(self, mock_udp, mock_tcp):
self.CONF.set_override('all_tcp', True, 'service:mdns')
mock_tcp.return_value = 'mock tcp resp'
out = self.backend._send_dns_message('msg', 'host', 123, 1)
self.assertFalse(agent.dns_query.udp.called)
agent.dns_query.tcp.assert_called_with('msg', 'host', port=123,
timeout=1)
self.assertEqual('mock tcp resp', out)

View File

@ -20,6 +20,7 @@ import dns
import dns.rdataclass
import dns.rdatatype
from designate import dnsutils
import designate.mdns.notify as notify
import designate.tests
from designate.tests.unit import RoObject
@ -130,12 +131,11 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual(('ERROR', 310, 0), out)
@mock.patch('time.sleep')
def test_make_and_send_dns_message_timeout(self, mock_sleep):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_timeout(self, mock_send_dns_message,
mock_sleep):
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
self.notify._send_dns_message = mock.Mock(
side_effect=dns.exception.Timeout
)
mock_send_dns_message.side_effect = dns.exception.Timeout
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -143,12 +143,12 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual((None, 3), out)
def test_make_and_send_dns_message_bad_response(self):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_bad_response(self,
mock_send_dns_message):
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
self.notify._send_dns_message = mock.Mock(
side_effect=notify.dns_query.BadResponse
)
mock_send_dns_message.side_effect = notify.dns_query.BadResponse
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -157,15 +157,14 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual((None, 1), out)
@mock.patch('time.sleep')
def test_make_and_send_dns_message_eagain(self, mock_sleep):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_eagain(self, mock_send_dns_message,
mock_sleep):
# bug #1558096
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
socket_error = socket.error()
socket_error.errno = socket.errno.EAGAIN
self.notify._send_dns_message = mock.Mock(
side_effect=socket_error
)
mock_send_dns_message.side_effect = socket_error
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -173,15 +172,15 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual((None, 3), out)
def test_make_and_send_dns_message_econnrefused(self):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_econnrefused(self,
mock_send_dns_message):
# bug #1558096
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
socket_error = socket.error()
socket_error.errno = socket.errno.ECONNREFUSED
# socket errors other than EAGAIN should raise
self.notify._send_dns_message = mock.Mock(
side_effect=socket_error)
mock_send_dns_message.side_effect = socket_error
self.assertRaises(
socket.error,
@ -189,11 +188,11 @@ class MdnsNotifyTest(designate.tests.TestCase):
zone, 'host', 123, 1, 2, 3
)
def test_make_and_send_dns_message_nxdomain(self):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_nxdomain(self, mock_send_dns_message):
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
response = RoObject(rcode=mock.Mock(return_value=dns.rcode.NXDOMAIN))
self.notify._send_dns_message = mock.Mock(return_value=response)
mock_send_dns_message.return_value = response
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -201,17 +200,17 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual((response, 1), out)
def test_make_and_send_dns_message_missing_AA_flags(self):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_missing_AA_flags(self,
mock_send_dns_message):
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
response = RoObject(
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
# rcode is NOERROR but (flags & dns.flags.AA) gives 0
flags=0,
answer=['answer'],
)
self.notify._send_dns_message = mock.Mock(return_value=response)
mock_send_dns_message.return_value = response
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -219,9 +218,10 @@ class MdnsNotifyTest(designate.tests.TestCase):
self.assertEqual((None, 1), out)
def test_make_and_send_dns_message_error_flags(self):
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_error_flags(self,
mock_send_dns_message):
zone = RoObject(name='zn')
self.notify._make_dns_message = mock.Mock(return_value='')
response = RoObject(
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
# rcode is NOERROR but flags are not NOERROR
@ -229,7 +229,7 @@ class MdnsNotifyTest(designate.tests.TestCase):
ednsflags=321,
answer=['answer'],
)
self.notify._send_dns_message = mock.Mock(return_value=response)
mock_send_dns_message.return_value = response
out = self.notify._make_and_send_dns_message(
zone, 'host', 123, 1, 2, 3
@ -266,23 +266,3 @@ class MdnsNotifyTest(designate.tests.TestCase):
';AUTHORITY',
';ADDITIONAL',
], txt)
@mock.patch.object(notify.dns_query, 'udp')
def test_send_udp_dns_message(self, mock_udp):
self.CONF.set_override('all_tcp', False, 'service:mdns')
self.notify._send_dns_message('msg', '192.0.2.1', 1234, 1)
mock_udp.assert_called_with(
'msg', '192.0.2.1', port=1234, timeout=1
)
@mock.patch.object(notify.dns_query, 'tcp')
def test_send_tcp_dns_message(self, mock_tcp):
self.CONF.set_override('all_tcp', True, 'service:mdns')
self.notify._send_dns_message('msg', '192.0.2.1', 1234, 1)
mock_tcp.assert_called_with(
'msg', '192.0.2.1', port=1234, timeout=1
)

View File

@ -23,6 +23,7 @@ import dns.rcode
import dns.rdatatype
import dns.zone
import eventlet
from oslo_config import cfg
import oslotest.base
from designate import dnsutils
@ -30,6 +31,8 @@ from designate import exceptions
from designate import objects
import designate.tests
CONF = cfg.CONF
SAMPLES = {
("cname.example.com.", "CNAME"): {
"ttl": 10800,
@ -319,3 +322,19 @@ class TestDoAfxr(oslotest.base.BaseTestCase):
self.assertTrue(mock_xfr.called)
self.assertTrue(mock_from_xfr.called)
@mock.patch.object(dns.query, 'udp')
def test_send_udp_dns_message(self, mock_udp):
CONF.set_override('all_tcp', False, 'service:mdns')
dnsutils.send_dns_message('msg', '192.0.2.1', 1234, 1)
mock_udp.assert_called_with(
'msg', '192.0.2.1', port=1234, timeout=1
)
@mock.patch.object(dns.query, 'tcp')
def test_send_tcp_dns_message(self, mock_tcp):
CONF.set_override('all_tcp', True, 'service:mdns')
dnsutils.send_dns_message('msg', '192.0.2.1', 1234, 1)
mock_tcp.assert_called_with(
'msg', '192.0.2.1', port=1234, timeout=1
)

View File

@ -20,12 +20,12 @@ from oslo_config import cfg
from oslo_config import fixture as cfg_fixture
import oslotest.base
from designate import dnsutils
from designate import exceptions
from designate import objects
from designate.tests.unit import utils
from designate.worker import processing
from designate.worker.tasks import zone
from designate.worker import utils as wutils
CONF = cfg.CONF
@ -167,7 +167,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
self.context = mock.Mock()
self.executor = mock.Mock()
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_create(self, mock_notify):
self.zone = objects.Zone(name='example.org.', action='CREATE')
self.actor = zone.ZoneActionOnTarget(
@ -185,7 +185,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
port=53
)
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_update(self, mock_notify):
self.zone = objects.Zone(name='example.org.', action='UPDATE')
self.actor = zone.ZoneActionOnTarget(
@ -203,7 +203,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
port=53
)
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_delete(self, mock_notify):
self.zone = objects.Zone(name='example.org.', action='DELETE')
self.actor = zone.ZoneActionOnTarget(
@ -217,7 +217,7 @@ class TestZoneActionOnTarget(oslotest.base.BaseTestCase):
mock_notify.assert_not_called()
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
@mock.patch('time.sleep', mock.Mock())
def test_call_exception_raised(self, mock_notify):
self.backend.create_zone.side_effect = exceptions.BadRequest()
@ -250,7 +250,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
self.executor = mock.Mock()
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_notify(self, mock_notify):
self.zone = objects.Zone(name='example.org.')
self.actor = zone.SendNotify(
@ -267,7 +267,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
port=53
)
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_notify_timeout(self, mock_notify):
mock_notify.side_effect = dns.exception.Timeout()
self.zone = objects.Zone(name='example.org.')
@ -282,7 +282,7 @@ class TestSendNotify(oslotest.base.BaseTestCase):
self.actor
)
@mock.patch.object(wutils, 'notify')
@mock.patch.object(dnsutils, 'notify')
def test_call_dont_notify(self, mock_notify):
CONF.set_override('notify', False, 'service:worker')
@ -668,11 +668,11 @@ class TestPollForZone(oslotest.base.BaseTestCase):
self.task._max_retries = 3
self.task._retry_interval = 2
@mock.patch.object(zone.wutils, 'get_serial', mock.Mock(return_value=10))
@mock.patch.object(dnsutils, 'get_serial', mock.Mock(return_value=10))
def test_get_serial(self):
self.assertEqual(10, self.task._get_serial())
zone.wutils.get_serial.assert_called_with(
dnsutils.get_serial.assert_called_with(
'example.org.',
'ns.example.org',
port=53

View File

@ -29,7 +29,7 @@ class SendNotify(base.Task):
port = int(self.target.options.get('port'))
try:
wutils.notify(self.zone.name, host, port=port)
dnsutils.notify(self.zone.name, host, port=port)
return True
except Exception:
return False

View File

@ -20,10 +20,10 @@ import dns
from oslo_config import cfg
from oslo_log import log as logging
from designate import dnsutils
from designate import exceptions
from designate import utils
from designate.worker.tasks import base
from designate.worker import utils as wutils
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@ -124,7 +124,7 @@ class SendNotify(base.Task):
port = int(self.target.options.get('port'))
try:
wutils.notify(self.zone.name, host, port=port)
dnsutils.notify(self.zone.name, host, port=port)
LOG.debug('Sent NOTIFY to %(host)s:%(port)s for zone %(zone)s',
{
'host': host,
@ -311,7 +311,7 @@ class PollForZone(base.Task):
self.ns = ns
def _get_serial(self):
return wutils.get_serial(
return dnsutils.get_serial(
self.zone.name,
self.ns.host,
port=self.ns.port

View File

@ -1,82 +0,0 @@
# Copyright 2016 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.mport threading
import dns
import dns.exception
import dns.query
from oslo_config import cfg
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
def prepare_msg(zone_name, rdatatype=dns.rdatatype.SOA, notify=False):
"""
Do the needful to set up a dns packet with dnspython
"""
dns_message = dns.message.make_query(zone_name, rdatatype)
if notify:
dns_message.set_opcode(dns.opcode.NOTIFY)
else:
dns_message.set_opcode(dns.opcode.QUERY)
return dns_message
def dig(zone_name, host, rdatatype, port=53):
"""
Set up and send a regular dns query, datatype configurable
"""
query = prepare_msg(zone_name, rdatatype=rdatatype)
return send_dns_msg(query, host, port=port)
def notify(zone_name, host, port=53):
"""
Set up a notify packet and send it
"""
msg = prepare_msg(zone_name, notify=True)
return send_dns_msg(msg, host, port=port)
def send_dns_msg(dns_message, host, port=53):
"""
Send the dns message and return the response
:return: dns.Message of the response to the dns query
"""
# This can raise some exceptions, but we'll catch them elsewhere
if not CONF['service:mdns'].all_tcp:
return dns.query.udp(
dns_message, host, port=port, timeout=10)
else:
return dns.query.tcp(
dns_message, host, port=port, timeout=10)
def get_serial(zone_name, host, port=53):
"""
Possibly raises dns.exception.Timeout or dns.query.BadResponse.
Possibly returns 0 if, e.g., the answer section is empty.
"""
resp = dig(zone_name, host, dns.rdatatype.SOA, port=port)
if not resp.answer:
return 0
rdataset = resp.answer[0].to_rdataset()
if not rdataset:
return 0
return rdataset[0].serial