oshipka/provision/auto_dns/check.py
2020-06-21 11:17:34 +02:00

173 lines
4.3 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 secrets import HOVER_USERNAME, HOVER_PASSWORD
SLEEP_SECONDS = 60 * 30 # 30 min
ipv4_sites = [
"http://www.icanhazip.com",
"http://ipecho.net/plain",
"http://checkip.amazonaws.com",
"http://ipinfo.io/ip",
"http://ifconfig.me"
]
ipv6_sites = [
"http://bot.whatismyipaddress.com",
"https://diagnostic.opendns.com/myip",
"https://ifconfig.co/ip",
]
dns_to_check = []
with open('dns_addresses.csv') as csvf:
csvreader = csv.reader(csvf)
for row in csvreader:
dns_to_check.append(row)
ip_checkers = ipv4_sites + ipv6_sites
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_hover_results(domain, ipv):
dns_config = ConfigResolver()
dns_config.with_dict({
'provider_name': 'hover',
'action': 'list',
'type': ipv,
'domain': domain,
'hover': {
'auth_username': HOVER_USERNAME,
'auth_password': HOVER_PASSWORD,
}
})
client = Client(dns_config)
return client.execute()
def set_hover_results(domain, ipv, new):
dns_config = ConfigResolver()
dns_config.with_dict({
'provider_name': 'hover',
'action': 'update',
'type': ipv,
'domain': domain,
'name': domain,
'content': new,
'hover': {
'auth_username': HOVER_USERNAME,
'auth_password': HOVER_PASSWORD,
}
})
client = Client(dns_config)
return client.execute()
def get_dns(domain, ipv):
results = process_exp_backoff(get_hover_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_hover_results, func_args=[domain, ipv, new])
def get_uniq(_list):
if len(_list) > 1 and len(set(_list)) == 1:
return _list[0]
def main():
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)