|
|
@ -27,22 +27,25 @@ RECPAT = re.compile(r''' |
|
|
|
''', re.X) |
|
|
|
|
|
|
|
class Record(object): |
|
|
|
__slots__ = ['name', 'cls', 'tp', 'value'] |
|
|
|
__slots__ = ['file', 'line', 'name', 'cls', 'tp', 'value'] |
|
|
|
|
|
|
|
def __init__(self, name, cls, tp, value): |
|
|
|
def __init__(self, file, line, name, cls, tp, value): |
|
|
|
self.file = file |
|
|
|
self.line = line |
|
|
|
self.name = name |
|
|
|
self.cls = cls |
|
|
|
self.tp = tp |
|
|
|
self.value = value |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def from_match(cls, match): |
|
|
|
def from_match(cls, file, line, match): |
|
|
|
return cls( |
|
|
|
file, line, |
|
|
|
*match.group('name', 'class', 'type', 'value') |
|
|
|
) |
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
return f'Record(name={self.name!r}, cls={self.cls!r}, tp={self.tp!r}, value={self.value!r})' |
|
|
|
return f'Record(file={self.file!r}, line={self.line!r}, name={self.name!r}, cls={self.cls!r}, tp={self.tp!r}, value={self.value!r})' |
|
|
|
|
|
|
|
def __hash__(self): |
|
|
|
return hash((self.name, self.cls, self.tp, self.value)) |
|
|
@ -52,17 +55,23 @@ class Record(object): |
|
|
|
return False |
|
|
|
return (self.name, self.cls, self.tp, self.value) == (other.name, other.cls, other.tp, other.value) |
|
|
|
|
|
|
|
def loc(self): |
|
|
|
return f'{self.file}:{self.line}' |
|
|
|
|
|
|
|
FWD = {} # zonefile -> [record] |
|
|
|
RVS = {} # id |
|
|
|
|
|
|
|
def get_recs_from_file(f): |
|
|
|
rv = [] |
|
|
|
for line in open(fn): |
|
|
|
for lno, line in enumerate(open(fn)): |
|
|
|
line = line.partition(';')[0].strip() |
|
|
|
if not line: |
|
|
|
continue |
|
|
|
match = RECPAT.match(line) |
|
|
|
if not match: |
|
|
|
print(f'rejected line: {line!r}') |
|
|
|
#print(f'rejected line: {line!r}') |
|
|
|
continue |
|
|
|
rec = Record.from_match(match) |
|
|
|
rec = Record.from_match(fn, lno + 1, match) |
|
|
|
rv.append(rec) |
|
|
|
return rv |
|
|
|
|
|
|
@ -108,53 +117,79 @@ for fn, zone in ORIGINS.items(): |
|
|
|
|
|
|
|
for fn, rv in REVERSE.items(): |
|
|
|
RVS[fn] = get_recs_from_file(open(fn)) |
|
|
|
|
|
|
|
print('\n# Comparisons') |
|
|
|
for za in ORIGINS.keys(): |
|
|
|
for zb in ORIGINS.keys(): |
|
|
|
if za <= zb: |
|
|
|
continue |
|
|
|
print('Comparison between', za, 'and', zb) |
|
|
|
print(f'\n## Comparison between `{za}` and `{zb}`') |
|
|
|
sa = set(FWD[za]) |
|
|
|
sb = set(FWD[zb]) |
|
|
|
ma = sb - sa |
|
|
|
mb = sa - sb |
|
|
|
print(f'Missing from {za}:') |
|
|
|
print(f'\nMissing from `{za}`:\n') |
|
|
|
for i in ma: |
|
|
|
print('\t', i) |
|
|
|
print(f'Missing from {zb}:') |
|
|
|
print(f'- `{i}`') |
|
|
|
print(f'\nMissing from `{zb}`:\n') |
|
|
|
for i in mb: |
|
|
|
print('\t', i) |
|
|
|
print(f'- `{i}`') |
|
|
|
|
|
|
|
mrvs = {} # canonaddr -> rec |
|
|
|
seen_addrs = {} # addr -> oldname |
|
|
|
seen_names = {} # name -> firstaddr |
|
|
|
seen_addrs_cls = {'A': {}, 'AAAA': {}} # class -> addr -> oldname |
|
|
|
seen_names_cls = {'A': {}, 'AAAA': {}} # name -> firstaddr |
|
|
|
for z in ORIGINS.keys(): |
|
|
|
for rec in FWD[z]: |
|
|
|
if rec.tp == 'A': |
|
|
|
mrvs[rec.value] = rec |
|
|
|
elif rec.tp == 'AAAA': |
|
|
|
mrvs[canon_ip6(into_digits(expand_ip6(rec.value)))] = rec |
|
|
|
print('Reverse records:') |
|
|
|
print('\n# Reverse records') |
|
|
|
for r, addr in REVERSE.items(): |
|
|
|
cls = 'AAAA' if ':' in addr else 'A' |
|
|
|
seen_addrs = seen_addrs_cls[cls] |
|
|
|
seen_names = seen_names_cls[cls] |
|
|
|
for rec in RVS[r]: |
|
|
|
if rec.tp != 'PTR': |
|
|
|
continue |
|
|
|
a = cons_addr(rec, addr) |
|
|
|
if a in seen_addrs: |
|
|
|
print(f'\t- Rvs {a} (for {rec.value}) has already been seen as {seen_addrs[a]}') |
|
|
|
print(f'- `{rec.loc()}`: Rvs `{a}` (for `{rec.value}`) has already been seen as `{seen_addrs[a]}`') |
|
|
|
else: |
|
|
|
try: |
|
|
|
del mrvs[a] |
|
|
|
except KeyError: |
|
|
|
print(f'\t- Rvs {a} (supposedly {rec.value}) doesn\'t correspond to any forward record') |
|
|
|
print(f'- `{rec.loc()}`: Rvs `{a}` (supposedly `{rec.value}`) doesn\'t correspond to any forward record') |
|
|
|
seen_addrs[a] = rec.value |
|
|
|
if rec.value in seen_names: |
|
|
|
print(f'\t- Name {rec.value} (for {a}) has already been seen as {seen_names[rec.value]}') |
|
|
|
if a == seen_names[rec.value]: |
|
|
|
print(f'- `{rec.loc()}`: Redundant record for `{rec.value}` to `{a}`') |
|
|
|
else: |
|
|
|
print(f'- `{rec.loc()}`: Name `{rec.value}` (for `{a}`) has already been seen as `{seen_names[rec.value]}`') |
|
|
|
else: |
|
|
|
seen_names[rec.value] = a |
|
|
|
|
|
|
|
for addr, rec in mrvs.items(): |
|
|
|
print(f'\t- No rvs for {addr} (in {rec!r})') |
|
|
|
print(f'- `{rec.loc()}`: No rvs for `{addr}` (`{rec.name}.{ORIGINS[rec.file]}`)') |
|
|
|
|
|
|
|
mfwd = {'A': {}, 'AAAA': {}} # class -> nm -> (addr, rec) |
|
|
|
for z in ORIGINS.keys(): |
|
|
|
for rec in FWD[z]: |
|
|
|
if rec.tp == 'A': |
|
|
|
mfwd[rec.tp][rec.name + '.' + ORIGINS[z]] = (rec.value, rec) |
|
|
|
elif rec.tp == 'AAAA': |
|
|
|
mfwd[rec.tp][rec.name + '.' + ORIGINS[z]] = (canon_ip6(into_digits(expand_ip6(rec.value))), rec) |
|
|
|
print('\n# V4/V6') |
|
|
|
names_v4 = set(mfwd['A'].keys()) |
|
|
|
names_v6 = set(mfwd['AAAA'].keys()) |
|
|
|
print('\n## Names not yet added to IPv6') |
|
|
|
for nm in names_v4 - names_v6: |
|
|
|
print(f'- `{nm}` (at `{mfwd["A"][nm][0]}`)') |
|
|
|
print('\n## Names exclusive to IPv6') |
|
|
|
for nm in names_v6 - names_v4: |
|
|
|
print(f'- `{nm}` (at `{mfwd["AAAA"][nm][0]}`)') |
|
|
|
print('\n## Names in both') |
|
|
|
for nm in names_v4 & names_v6: |
|
|
|
print(f'- `{nm}` (at `{mfwd["A"][nm][0]}` and `{mfwd["AAAA"][nm][0]}`)') |
|
|
|
|
|
|
|
#for fn, recs in FWD.items(): |
|
|
|
# for rec in recs: |
|
|
|