174 lines
4.4 KiB
Python
174 lines
4.4 KiB
Python
import csv
|
|
import ipaddress
|
|
import threading
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
from time import sleep
|
|
import sys
|
|
|
|
import requests
|
|
from lexicon.client import Client
|
|
from lexicon.config import ConfigResolver
|
|
|
|
from oshipka.util.process import process_exp_backoff
|
|
from sensitive import CLOUDFLARE_USERNAME, CLOUDFLARE_AUTH_KEY
|
|
|
|
SLEEP_SECONDS = 60 * 30 # 30 min
|
|
|
|
DEFAULT_TTL = 5 * 60
|
|
|
|
ipv4_sites = [
|
|
"http://www.icanhazip.com",
|
|
"http://ipecho.net/plain",
|
|
"http://checkip.amazonaws.com",
|
|
"http://ipinfo.io/ip",
|
|
"http://ifconfig.me"
|
|
]
|
|
|
|
ipv6_sites = [
|
|
"https://diagnostic.opendns.com/myip",
|
|
"https://ifconfig.co/ip",
|
|
]
|
|
|
|
ip_checkers = ipv4_sites + ipv6_sites
|
|
|
|
dns_to_check = []
|
|
results = []
|
|
real = defaultdict(dict)
|
|
|
|
|
|
def test_ip(ip):
|
|
try:
|
|
ipaddress_info = ipaddress.ip_address(ip)
|
|
except ValueError as e:
|
|
print(e)
|
|
return None
|
|
if ipaddress_info.version == 4:
|
|
version = 'A'
|
|
elif ipaddress_info.version == 6:
|
|
version = 'AAAA'
|
|
else:
|
|
return None
|
|
return {
|
|
"addr": ipaddress_info.exploded,
|
|
"version": version,
|
|
}
|
|
|
|
|
|
def fetch_ip(url):
|
|
try:
|
|
resp = requests.get(url)
|
|
ip = resp.text.strip()
|
|
ip_info = test_ip(ip)
|
|
if ip_info:
|
|
results.append(ip_info)
|
|
except Exception as e:
|
|
print("Error fetching {}: {}".format(url, e))
|
|
|
|
|
|
tso, tsd = [], []
|
|
|
|
|
|
def check_observed():
|
|
for url in ip_checkers:
|
|
t = threading.Thread(target=fetch_ip, args=(url,))
|
|
tso.append(t)
|
|
t.start()
|
|
|
|
|
|
def check_dns():
|
|
for domain, ipv in dns_to_check:
|
|
t = threading.Thread(target=get_dns, args=(domain, ipv,))
|
|
tsd.append(t)
|
|
t.start()
|
|
|
|
|
|
def get_resolver_results(domain, ipv):
|
|
dns_config = ConfigResolver()
|
|
dns_config.with_dict({
|
|
'provider_name': 'cloudflare',
|
|
'action': 'list',
|
|
'type': ipv,
|
|
'domain': domain,
|
|
'cloudflare': {
|
|
'auth_username': CLOUDFLARE_USERNAME,
|
|
'auth_token': CLOUDFLARE_AUTH_KEY,
|
|
}
|
|
})
|
|
client = Client(dns_config)
|
|
return client.execute()
|
|
|
|
|
|
def set_resolver_results(domain, ipv, new):
|
|
dns_config = ConfigResolver()
|
|
dns_config.with_dict({
|
|
'provider_name': 'cloudflare',
|
|
'action': 'create',
|
|
'type': ipv,
|
|
'domain': domain,
|
|
'name': domain,
|
|
'content': new,
|
|
'ttl': DEFAULT_TTL,
|
|
'cloudflare': {
|
|
'auth_username': CLOUDFLARE_USERNAME,
|
|
'auth_token': CLOUDFLARE_AUTH_KEY,
|
|
}
|
|
})
|
|
client = Client(dns_config)
|
|
return client.execute()
|
|
|
|
|
|
def get_dns(domain, ipv):
|
|
results = process_exp_backoff(get_resolver_results, func_args=[domain, ipv])
|
|
res = [x.get('content') for x in results if x.get('name') == domain]
|
|
if len(res) == 1:
|
|
print("Real {}: {}".format(ipv, res[0]))
|
|
real[domain][ipv] = res[0]
|
|
|
|
|
|
def set_dns(domain, ipv, new):
|
|
return process_exp_backoff(set_resolver_results, func_args=[domain, ipv, new])
|
|
|
|
|
|
def get_uniq(_list):
|
|
if len(_list) > 1 and len(set(_list)) == 1:
|
|
return _list[0]
|
|
|
|
|
|
def main():
|
|
with open('dns_addresses.csv') as csvf:
|
|
csvreader = csv.reader(csvf)
|
|
for row in csvreader:
|
|
dns_to_check.append(row)
|
|
check_dns()
|
|
check_observed()
|
|
for t in tso:
|
|
t.join()
|
|
for t in tsd:
|
|
t.join()
|
|
for domain, version in dns_to_check:
|
|
ip_obs = get_uniq([x['addr'] for x in results if x['version'] == version])
|
|
print("{}/{}: {}".format(domain, version, ip_obs))
|
|
ip_real = real.get(domain).get(version)
|
|
sys.stdout.flush()
|
|
if ip_obs == ip_real:
|
|
print('{}/{} is same!'.format(domain, version))
|
|
else:
|
|
print("{}/{} diff on dns: real: {}, obs: {}".format(domain, version, ip_real, ip_obs))
|
|
if set_dns(domain, version, ip_real):
|
|
print("update successful {}/{} -> {}".format(domain, version, ip_real))
|
|
else:
|
|
print("update failed: {}/{} on dns is {}, but obs is {}".format(
|
|
domain, version, ip_real, ip_obs
|
|
))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
while True:
|
|
print("{}: Waking up".format(datetime.utcnow()))
|
|
sys.stdout.flush()
|
|
main()
|
|
print("{}: Sleeping for {}".format(datetime.utcnow(), SLEEP_SECONDS))
|
|
sys.stdout.flush()
|
|
sleep(SLEEP_SECONDS)
|