summaryrefslogtreecommitdiff
path: root/designate/dnsutils.py
diff options
context:
space:
mode:
Diffstat (limited to 'designate/dnsutils.py')
-rw-r--r--designate/dnsutils.py152
1 files changed, 118 insertions, 34 deletions
diff --git a/designate/dnsutils.py b/designate/dnsutils.py
index 6833d542..a3b4d2f9 100644
--- a/designate/dnsutils.py
+++ b/designate/dnsutils.py
@@ -21,9 +21,10 @@ from threading import Lock
import six
import dns
import dns.exception
+import dns.query
+import dns.rdatatype
import dns.zone
import eventlet
-from dns import rdatatype
from oslo_serialization import base64
from oslo_log import log as logging
@@ -313,7 +314,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 six.PY3 and isinstance(name, bytes):
@@ -347,39 +348,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()
+
+ raise exceptions.XFRFailure(
+ "XFR failed for %(name)s. No servers in %(servers)s was reached." %
+ {"name": zone_name, "servers": servers}
+ )
+
+
+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)
- LOG.debug("AXFR Successful for %s", raw_zone.origin.to_text())
- return raw_zone
+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))