Skip to content

Commit

Permalink
working state
Browse files Browse the repository at this point in the history
  • Loading branch information
artwaw committed Oct 23, 2020
1 parent 249dc8e commit 3a541ff
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## ZONETEST(.py)

Simple tool for testing if zone has been migrated successfully.

#Requirements:
- Python 3.x (it may run on 2.x after some corrections, I don't intend to test it)
- dnspython python3 module
- argparse python3 module (this is default package but still worth mentioning)
- ipaddress python3 module (as above)

#Parameters:
./zonetest.py zonefile targetNS [--coma]

- zonefile - DNS zone as downloaded from the "old" server. Assumed Bind RFC compat.
- targetNS - our "new" name server onto which the zone has been migrated. Can be IP or URL.
- --coma - optional switch allowing to parse csv files (default format is TAB separated)

#Troubleshooting

1) Script complains about missing dnspython

Do `pip3 install dnspython`.
Do not do pip3 as a root!

2) pip3 fails with `ModuleNotFoundError: No module named 'pip._internal.cli.main'` or similar

You are likely running system that provides Python without pip. There are some ways this can be corrected
but the most recommended is via [pyenv](https://github.com/pyenv/pyenv) (this can be also done via Homebrew,
please read the link!)

After installing pyenv just:

`pyenv install [version]` - i.e. `pyenv install 3.9.0`
`pyenv shell 3.9.0`

All is in the docs.

Further reading on python versions management:

https://opensource.com/article/19/5/python-3-default-mac

108 changes: 108 additions & 0 deletions zonetest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python3

import argparse
import dns.resolver
import dns.exception
import ipaddress

def isIP(param):
try:
test = ipaddress.ip_address(param)
return True
except:
return False

params = argparse.ArgumentParser(description='Tester for after zone migration between servers.\nTakes a list of entries in the zone from zone file, queries old server and compares the output with given name server.',)
params.add_argument('zonefile', metavar='FileName', action='store', help='Zone file parsed for entries to be checked.')
params.add_argument('ns_server', metavar='NameServer', action='store', help='Target name server IP or URL')
params.add_argument('--coma', action='store_true', required=False, help='Use coma as delimeter. Otherwise TAB is used. Optional parameter')
args = params.parse_args()
if (args.coma):
delimeter=','
else:
delimeter='\t'
# Loading zone
try:
zfile=open(args.zonefile)
except OSError as e:
print(e.strerror+": "+e.filename)
exit(e.errno)
print("File loaded.")
zone_content = []
targetNS = ''
if isIP(args.ns_server):
targetNS = args.ns_server
else:
ipRes = dns.resolver.Resolver()
targetNS = str(ipRes.resolve(args.ns_server).response.answer[0][0])
if (targetNS == ''):
print("Target name server given as URL ("+args.ns_server+") could not be resolved to IP address. Bailing out...")
exit(1)
print("Target name server: "+targetNS)
sourceNS = ''
for line in zfile:
if line.strip().startswith(';'):
continue
if 'SOA' in line:
domain = line.split(delimeter)[0].removesuffix('.').strip()
print('Detected domain: '+domain)
continue;
parts = line.split(delimeter)
if len(parts) < 5:
continue
if parts[2].strip() == 'IN':
if parts[0].strip() == '@' and parts[3].strip() == 'NS':
if sourceNS == '':
srcCandidate = parts[4].strip().removesuffix('.')
if isIP(srcCandidate):
sourceNS = srcCandidate
else:
ipRes = dns.resolver.Resolver()
answer = ipRes.resolve(srcCandidate)
sourceNS = str(answer.response.answer[0][0])
if sourceNS == '':
print("Source name server found as URL ("+parts[4].strip().removesuffix('.')+") could not be resolved to IP address. Bailing out...")
exit(1)
print("Found \"old\" name server: "+sourceNS)
continue
zone_content.append(line.strip())
zfile.close()
RTypes = {'A':1, 'A6':38, 'AAAA':28, 'AFSDB':18, 'ANY':255, 'APL':42, 'AVC':258, 'AXFR':252, 'CAA':257, 'CDNSKEY':60, 'CDS':59, 'CERT':37, 'CNAME':5, 'CSYNC':62, 'DHCID':49, 'DLV':32769, 'DNAME':39, 'DNSKEY':48, 'DS':43,
'EUI48':108, 'EUI64':109, 'GPOS':27, 'HINFO':13, 'HIP':55, 'IPSECKEY':45, 'ISDN':20, 'IXFR':251, 'KEY':25, 'KX':36, 'LOC':29, 'MAILA':254, 'MAILB':253, 'MB':7, 'MD':3, 'MF':4, 'MG':8, 'MINFO':14, 'MR':9,
'MX':15, 'NAPTR':35, 'NONE':0, 'NS':2, 'NSAP':22, 'NSEC':47, 'NSEC3':50, 'NSEC3PARAM':51, 'NULL':10, 'NXT':30, 'OPT':41, 'PTR':12, 'PX':26, 'RP':17, 'RRSIG':46, 'RT':21, 'SIG':24, 'SOA':6, 'SPF':99, 'SRV':33,
'SSHFP':44, 'TA':32768, 'TKEY':249, 'TLSA':52, 'TSIG':250, 'TXT':16, 'UNSPEC':103, 'URI':256, 'WKS':11, 'X25':19}
# ALl vars known, setting up the dns resolvers
oldDNS = dns.resolver.Resolver()
oldDNS.nameservers = [ sourceNS ]
newDNS = dns.resolver.Resolver()
newDNS.nameservers = [ targetNS ]
# Good to go. Processing...
total = str(len(zone_content))
counter = 0;
print("Setup complete. Processing "+total+" entries...")
for line in zone_content:
counter = counter+1
if line.startswith('*') or line.startswith('@'):
continue
parts = line.split(delimeter)
print("Testing ("+str(counter)+"/"+total+") "+parts[3].strip()+" record "+parts[0].strip()+": ",end='')
print(parts[0].strip()+"."+domain,end=' ')
try:
oldpart = oldDNS.resolve(parts[0].strip()+"."+domain,RTypes[parts[3].strip()]).response.answer[0][0]
except dns.exception.DNSException as e:
print("[FAILED - source DNS]")
print(e.msg)
continue
try:
newpart = newDNS.resolve(parts[0].strip()+'.'+domain,RTypes[parts[3].strip()]).response.answer[0][0]
except dns.exception.DNSException as e:
print("[FAILED - target DNS]")
print(e.msg)
continue
if oldpart == newpart:
print("[CORRECT]")
else:
print("[FAILED]")
print("Old record: "+oldpart)
print("New record: "+newpart)
print("Finished.")

0 comments on commit 3a541ff

Please sign in to comment.