-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.") |