optools/net/dns/rfc4183.py

121 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
# https://tools.ietf.org/html/rfc2317
# https://tools.ietf.org/html/rfc4183
desc = 'Gets the RFC 2317/4183 PTR of given IP addresses or A/AAAA records.'
# stdlib
import argparse
import copy
import ipaddress
import os
# pypi/pip
#try:
# import ipwhois
#except ImportError:
# exit('You need to install the ipwhois module.')
try:
import dns.resolver
import dns.reversename
except ImportError:
exit('You need to install the dnspython module.')
try:
import fqdn
except ImportError:
exit('You need to install the fqdn module.')
def resolveRecord(addr):
r = dns.resolver.Resolver()
ipaddrs = {'A': [],
'AAAA': []}
for rtype in ipaddrs.keys():
for record in r.query(addr, 'A'):
ipaddrs[rtype].append(record)
ipaddrs['ipv4'] = sorted(list(set(copy.deepcopy(ipaddrs['A']))))
ipaddrs['ipv6'] = sorted(list(set(copy.deepcopy(ipaddrs['AAAA']))))
del(ipaddrs['A'], ipaddrs['AAAA'])
if ipaddrs['ipv4'] == ipaddrs['ipv6']:
del(ipaddrs['ipv6'])
return(ipaddrs)
def genPTR(ipaddr, iptype):
_suffix = ''
# TODO: get the current PTR.
# TODO: do this more manually. We should use ipaddress and ipwhois to get
# the proper return for e.g. network gateways.
return(dns.reversename.from_address(ipaddr))
def chkInput(src):
# Determine the input, if we can.
src_out = (None, None)
try:
ipaddress.IPv4Address(src)
return(('ipv4', src))
except ipaddress.AddressValueError:
pass
try:
ipaddress.IPv6Address(src)
return(('ipv6', src))
except ipaddress.AddressValueError:
pass
_p = os.path.abspath(os.path.expanduser(src))
if os.path.isfile(_p):
return(('file', _p))
# Last shot - is it a DNS record?
# Not quite perfect, as it's strictly RFC and there are plenty of
# subdomains out there that break RFC.
f = fqdn.FQDN(src)
if f.is_valid:
return(('dns', src))
return(src_out)
def parseArgs():
def chkArg(src):
src_out = chkInput(src)
if src_out == (None, None):
raise argparse.ArgumentTypeError(('"{0}" does not seem to be a ' +
'path to a file, an A/AAAA ' +
'record, or IPv4/IPv6 ' +
'address.').format(src))
return(src_out)
args = argparse.ArgumentParser(description = desc)
args.add_argument('data_in',
type = chkArg,
metavar = 'ADDRESS_OR_FILE',
help = ('The path to a file containing domains and IP ' +
'addresses OR a single IPv4/IPv6 address or ' +
'A/AAAA record. If an A/AAAA record, your ' +
'machine must be able to resolve it (and it ' +
'must exist)'))
return(args)
def main():
# TODO: clean this up, migrate the duplicated code into a func
args = vars(parseArgs().parse_args())['data_in']
if args[0] == 'dns':
r = resolveRecord(args[1])
for k in r.keys():
for ip in r[k]:
print('IP: {0}'.format(ip))
print('PTR: {0}'.format(genPTR(str(ip), k)))
elif args[0] in ('ipv4', 'ipv6'):
print('PTR: {0}'.format(genPTR(args[1], args[0])))
elif args[0] == 'file':
with open(args[1], 'r') as f:
recordlst = [i.strip() for i in f.readlines()]
for i in recordlst:
ltype, data = chkInput(i)
print('== {0} =='.format(i))
if ltype == 'dns':
r = resolveRecord(data)
for k in r.keys():
for ip in r[k]:
print('IP: {0}'.format(ip))
print('PTR: {0}'.format(genPTR(str(ip), k)))
elif ltype in ('ipv4', 'ipv6'):
print('PTR: {0}'.format(genPTR(data, ltype)))
print()
if __name__ == '__main__':
main()