#!/usr/bin/env python3

###############
# RFC7050 ipv6 prefix discovery
# Prefixes can have lengths 32, 40, 48, 56, 64, or 96 per RFC6052
# IMPORTANT: THIS DOES NOT HANDLE THE SUFFIX ASPECTS THAT MAY COME INTO PLAY ON SOME NETWORKS
###############

##########
# Dependencies
##########
# pip3 install dnspython

##########
# Sample T-Mobile record
##########
# ipv4only.arpa
#     192.0.0.170
#     192.0.0.171
#     2607:7700:0:26::c000:aa
#     2607:7700:0:26::c000:ab

##########
import ipaddress
import dns.resolver

##########
# Discovery of DNS records
##########
#TODO: Swap this to a well-known address that is ISP independent
#          Sub domain with only an A record somewhere?
           Update to handle the tweaked approach
resolver = dns.resolver.Resolver()
a_recs = resolver.query("ipv4only.arpa", "A")
aaaa_recs = resolver.query("ipv4only.arpa", "AAAA")

##########
# Process DNS records for prefix discovery process
##########
a_addresses = []
aaaa_addresses = []

for a in a_recs:
    print(a)
    a_addresses.append(ipaddress.IPv4Address(a.to_text()))

for aaaa in aaaa_recs:
    print(aaaa)
    aaaa_addresses.append(ipaddress.IPv6Address(aaaa.to_text()))

##########
# Figure out published prefixes
##########
prefixes = []

for aaaa in aaaa_addresses:
    ipv6_bytes = aaaa.packed
    for a in a_addresses:
        ipv4_bytes = a.packed
        # Split the bytes so we can find the prefix minus the ipv4 address and following info
        partition = ipv6_bytes.partition(ipv4_bytes)
        if partition[0] != ipv6_bytes:
            # Padded to a full 16 bytes needed by the Python ipaddress module
            mask_number_bytes = 16 - len(partition[0])
            # Figure out the actual integer mask
            mask = 128 - (mask_number_bytes * 8)
            # Pad the prefix with 0's
            prefix_bytes = partition[0] + bytearray(mask_number_bytes)
            # Get the /128 address
            address = ipaddress.IPv6Address(prefix_bytes)
            # Build the compressed address + mask for output
            prefix = '/'.join([str(address), str(mask)])
            # De-duplicate the prefixes
            #     Some ISPs publish multiple records that result in identical prefixes
            if prefix not in prefixes:
                prefixes.append(prefix)

print(prefixes)
