mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New lookup-plugin: `dig' for performing DNS lookups
add support for argument qtype=XXX remove spurious print add dict returns typo Add dict returns for all major DNS types Finish adding more types. For now. Until somebody complains. I forgot to add NAPTR; here it is.
This commit is contained in:
parent
1cb47c807e
commit
a3618f619e
1 changed files with 212 additions and 0 deletions
212
lib/ansible/runner/lookup_plugins/dig.py
Normal file
212
lib/ansible/runner/lookup_plugins/dig.py
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
# (c) 2015, Jan-Piet Mens <jpmens(at)gmail.com>
|
||||||
|
#
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# Ansible is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Ansible is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from ansible import utils, errors
|
||||||
|
import socket
|
||||||
|
HAVE_DNS=False
|
||||||
|
try:
|
||||||
|
import dns.resolver
|
||||||
|
import dns.reversename
|
||||||
|
from dns.rdatatype import *
|
||||||
|
from dns.exception import DNSException
|
||||||
|
HAVE_DNS=True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def make_rdata_dict(rdata):
|
||||||
|
''' While the 'dig' lookup plugin supports anything which dnspython supports
|
||||||
|
out of the box, the following supported_types list describes which
|
||||||
|
DNS query types we can convert to a dict.
|
||||||
|
|
||||||
|
Note: adding support for RRSIG is hard work. :)
|
||||||
|
'''
|
||||||
|
supported_types = {
|
||||||
|
A : ['address'],
|
||||||
|
AAAA : ['address'],
|
||||||
|
CNAME : ['target'],
|
||||||
|
DNAME : ['target'],
|
||||||
|
DLV : ['algorithm', 'digest_type', 'key_tag', 'digest'],
|
||||||
|
DNSKEY : ['flags', 'algorithm', 'protocol', 'key'],
|
||||||
|
DS : ['algorithm', 'digest_type', 'key_tag', 'digest'],
|
||||||
|
HINFO : ['cpu', 'os'],
|
||||||
|
LOC : ['latitude', 'longitude', 'altitude', 'size', 'horizontal_precision', 'vertical_precision'],
|
||||||
|
MX : ['preference', 'exchange'],
|
||||||
|
NAPTR : ['order', 'preference', 'flags', 'service', 'regexp', 'replacement'],
|
||||||
|
NS : ['target'],
|
||||||
|
NSEC3PARAM : ['algorithm', 'flags', 'iterations', 'salt'],
|
||||||
|
PTR : ['target'],
|
||||||
|
RP : ['mbox', 'txt'],
|
||||||
|
# RRSIG : ['algorithm', 'labels', 'original_ttl', 'expiration', 'inception', 'signature'],
|
||||||
|
SOA : ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', 'minimum'],
|
||||||
|
SPF : ['strings'],
|
||||||
|
SRV : ['priority', 'weight', 'port', 'target'],
|
||||||
|
SSHFP : ['algorithm', 'fp_type', 'fingerprint'],
|
||||||
|
TLSA : ['usage', 'selector', 'mtype', 'cert'],
|
||||||
|
TXT : ['strings'],
|
||||||
|
}
|
||||||
|
|
||||||
|
rd = {}
|
||||||
|
|
||||||
|
if rdata.rdtype in supported_types:
|
||||||
|
fields = supported_types[rdata.rdtype]
|
||||||
|
for f in fields:
|
||||||
|
val = rdata.__getattribute__(f)
|
||||||
|
|
||||||
|
if type(val) == dns.name.Name:
|
||||||
|
val = dns.name.Name.to_text(val)
|
||||||
|
|
||||||
|
if rdata.rdtype == DLV and f == 'digest':
|
||||||
|
val = dns.rdata._hexify(rdata.digest).replace(' ', '')
|
||||||
|
if rdata.rdtype == DS and f == 'digest':
|
||||||
|
val = dns.rdata._hexify(rdata.digest).replace(' ', '')
|
||||||
|
if rdata.rdtype == DNSKEY and f == 'key':
|
||||||
|
val = dns.rdata._base64ify(rdata.key).replace(' ', '')
|
||||||
|
if rdata.rdtype == NSEC3PARAM and f == 'salt':
|
||||||
|
val = dns.rdata._hexify(rdata.salt).replace(' ', '')
|
||||||
|
if rdata.rdtype == SSHFP and f == 'fingerprint':
|
||||||
|
val = dns.rdata._hexify(rdata.fingerprint).replace(' ', '')
|
||||||
|
if rdata.rdtype == TLSA and f == 'cert':
|
||||||
|
val = dns.rdata._hexify(rdata.cert).replace(' ', '')
|
||||||
|
|
||||||
|
|
||||||
|
rd[f] = val
|
||||||
|
|
||||||
|
return rd
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# dig: Lookup DNS records
|
||||||
|
#
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
class LookupModule(object):
|
||||||
|
|
||||||
|
def __init__(self, basedir=None, **kwargs):
|
||||||
|
self.basedir = basedir
|
||||||
|
|
||||||
|
if HAVE_DNS == False:
|
||||||
|
raise errors.AnsibleError("Can't LOOKUP(dig): module dns.resolver is not installed")
|
||||||
|
|
||||||
|
def run(self, terms, inject=None, **kwargs):
|
||||||
|
|
||||||
|
'''
|
||||||
|
terms contains a string with things to `dig' for. We support the
|
||||||
|
following formats:
|
||||||
|
example.com # A record
|
||||||
|
example.com qtype=A # same
|
||||||
|
example.com/TXT # specific qtype
|
||||||
|
example.com qtype=txt # same
|
||||||
|
192.168.1.2/PTR # reverse PTR
|
||||||
|
^^ shortcut for 2.1.168.192.in-addr.arpa/PTR
|
||||||
|
example.net/AAAA @nameserver # query specified server
|
||||||
|
^^^ can be comma-sep list of names/addresses
|
||||||
|
|
||||||
|
... flat=0 # returns a dict; default is 1 == string
|
||||||
|
'''
|
||||||
|
terms = terms.split()
|
||||||
|
|
||||||
|
# Create Resolver object so that we can set NS if necessary
|
||||||
|
myres = dns.resolver.Resolver()
|
||||||
|
edns_size = 4096
|
||||||
|
myres.use_edns(0, ednsflags=dns.flags.DO, payload=edns_size)
|
||||||
|
|
||||||
|
domain = None
|
||||||
|
qtype = 'A'
|
||||||
|
flat = True
|
||||||
|
|
||||||
|
for t in terms:
|
||||||
|
if t.startswith('@'): # e.g. "@10.0.1.2,192.168.1.1" is ok.
|
||||||
|
nsset = t[1:].split(',')
|
||||||
|
nameservers = []
|
||||||
|
for ns in nsset:
|
||||||
|
# Check if we have a valid IP address. If so, use that, otherwise
|
||||||
|
# try to resolve name to address using system's resolver. If that
|
||||||
|
# fails we bail out.
|
||||||
|
try:
|
||||||
|
socket.inet_aton(ns)
|
||||||
|
nameservers.append(ns)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
nsaddr = dns.resolver.query(ns)[0].address
|
||||||
|
nameservers.append(nsaddr)
|
||||||
|
except Exception, e:
|
||||||
|
raise errors.AnsibleError("dns lookup NS: ", str(e))
|
||||||
|
myres.nameservers = nameservers
|
||||||
|
continue
|
||||||
|
if '=' in t:
|
||||||
|
try:
|
||||||
|
opt, arg = t.split('=')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if opt == 'qtype':
|
||||||
|
qtype = arg.upper()
|
||||||
|
elif opt == 'flat':
|
||||||
|
flat = int(arg)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if '/' in t:
|
||||||
|
try:
|
||||||
|
domain, qtype = t.split('/')
|
||||||
|
except:
|
||||||
|
domain = t
|
||||||
|
else:
|
||||||
|
domain = t
|
||||||
|
|
||||||
|
# print "--- domain = {0} qtype={1}".format(domain, qtype)
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
if qtype.upper() == 'PTR':
|
||||||
|
try:
|
||||||
|
n = dns.reversename.from_address(domain)
|
||||||
|
domain = n.to_text()
|
||||||
|
except dns.exception.SyntaxError:
|
||||||
|
pass
|
||||||
|
except Exception, e:
|
||||||
|
raise errors.AnsibleError("dns.reversename unhandled exception", str(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
answers = myres.query(domain, qtype)
|
||||||
|
for rdata in answers:
|
||||||
|
s = rdata.to_text()
|
||||||
|
if qtype.upper() == 'TXT':
|
||||||
|
s = s[1:-1] # Strip outside quotes on TXT rdata
|
||||||
|
|
||||||
|
if flat:
|
||||||
|
ret.append(s)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
rd = make_rdata_dict(rdata)
|
||||||
|
rd['owner'] = answers.canonical_name.to_text()
|
||||||
|
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
|
||||||
|
rd['ttl'] = answers.rrset.ttl
|
||||||
|
|
||||||
|
ret.append(rd)
|
||||||
|
except Exception, e:
|
||||||
|
ret.append(str(e))
|
||||||
|
|
||||||
|
except dns.resolver.NXDOMAIN:
|
||||||
|
ret.append('NXDOMAIN')
|
||||||
|
except dns.resolver.NoAnswer:
|
||||||
|
ret.append("")
|
||||||
|
except dns.resolver.Timeout:
|
||||||
|
ret.append('')
|
||||||
|
except dns.exception.DNSException, e:
|
||||||
|
raise errors.AnsibleError("dns.resolver unhandled exception", e)
|
||||||
|
|
||||||
|
return ret
|
Loading…
Reference in a new issue