2013-09-28 13:47:49 +02:00
|
|
|
"""
|
|
|
|
Misc. DNS related code: query, dynamic update, etc.
|
|
|
|
"""
|
|
|
|
|
2013-09-28 15:29:16 +02:00
|
|
|
import dns.name
|
|
|
|
import dns.resolver
|
|
|
|
import dns.query
|
|
|
|
import dns.update
|
|
|
|
import dns.tsig
|
|
|
|
import dns.tsigkeyring
|
|
|
|
|
|
|
|
|
2013-09-28 13:47:49 +02:00
|
|
|
SERVER = '85.10.192.104' # ns1.thinkmo.de (master / dynamic upd server for nsupdate.info)
|
|
|
|
BASEDOMAIN = 'nsupdate.info'
|
|
|
|
|
|
|
|
NONEXISTING_HOST = 'nonexisting.' + BASEDOMAIN
|
|
|
|
WWW_HOST = 'www.' + BASEDOMAIN
|
|
|
|
WWW_IPV4_HOST = 'www.ipv4.' + BASEDOMAIN
|
|
|
|
WWW_IPV6_HOST = 'www.ipv6.' + BASEDOMAIN
|
|
|
|
WWW_IPV4_IP = '178.32.221.14'
|
|
|
|
WWW_IPV6_IP = '2001:41d0:8:e00e::1'
|
|
|
|
|
2013-09-28 15:29:16 +02:00
|
|
|
UPDATE_ALGO = dns.tsig.HMAC_SHA512
|
|
|
|
UPDATE_KEY = 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='
|
|
|
|
|
2013-09-28 13:47:49 +02:00
|
|
|
|
|
|
|
def query_ns(qname, rdtype):
|
|
|
|
"""
|
|
|
|
query a dns name from our master server
|
|
|
|
|
|
|
|
:param qname: the query name
|
|
|
|
:type qname: dns.name.Name object or str
|
|
|
|
:param rdtype: the query type
|
|
|
|
:type rdtype: int or str
|
|
|
|
:return: IP (as str)
|
|
|
|
"""
|
|
|
|
resolver = dns.resolver.Resolver(configure=False)
|
|
|
|
# we do not configure it from resolv.conf, but patch in the values we
|
|
|
|
# want into the documented attributes:
|
|
|
|
resolver.nameservers = [SERVER, ]
|
|
|
|
resolver.search = [dns.name.from_text(BASEDOMAIN), ]
|
|
|
|
answer = resolver.query(qname, rdtype)
|
|
|
|
return str(list(answer)[0])
|
2013-09-28 15:29:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
def parse_name(fqdn, origin=None):
|
|
|
|
"""
|
|
|
|
Parse a fully qualified domain name into a relative name
|
|
|
|
and a origin zone. Please note that the origin return value will
|
|
|
|
have a trailing dot.
|
|
|
|
|
|
|
|
:param fqdn: fully qualified domain name (str)
|
|
|
|
:param origin: origin zone (optional, str)
|
|
|
|
:return: origin, relative name (both dns.name.Name)
|
|
|
|
"""
|
|
|
|
fqdn = dns.name.from_text(fqdn)
|
|
|
|
if origin is None:
|
|
|
|
origin = dns.resolver.zone_for_name(fqdn)
|
|
|
|
rel_name = fqdn.relativize(origin)
|
|
|
|
else:
|
|
|
|
origin = dns.name.from_text(origin)
|
|
|
|
rel_name = fqdn - origin
|
|
|
|
return origin, rel_name
|
|
|
|
|
|
|
|
|
|
|
|
def update_ns(fqdn, rdtype='A', ipaddr=None, origin=None, action='upd', ttl=60):
|
|
|
|
"""
|
|
|
|
update our master server
|
|
|
|
|
|
|
|
:param qname: the name to update
|
|
|
|
:param rdtype: the record type
|
|
|
|
:param action: 'add', 'del' or 'upd'
|
|
|
|
"""
|
|
|
|
assert action in ['add', 'del', 'upd', ]
|
|
|
|
origin, name = parse_name(fqdn, origin)
|
|
|
|
upd = dns.update.Update(origin,
|
|
|
|
keyring=dns.tsigkeyring.from_text({BASEDOMAIN+'.': UPDATE_KEY}),
|
|
|
|
keyalgorithm=UPDATE_ALGO)
|
|
|
|
if action == 'add':
|
|
|
|
assert ipaddr is not None
|
|
|
|
upd.add(name, ttl, rdtype, ipaddr)
|
|
|
|
elif action == 'del':
|
|
|
|
upd.delete(name, rdtype)
|
|
|
|
elif action == 'upd':
|
|
|
|
assert ipaddr is not None
|
|
|
|
upd.replace(name, ttl, rdtype, ipaddr)
|
|
|
|
response = dns.query.tcp(upd, SERVER)
|
|
|
|
return response
|