From bc82c65c829cbdebb2034d037e60366b7fc97b26 Mon Sep 17 00:00:00 2001 From: Gilles Grandou Date: Mon, 19 Feb 2018 23:42:57 +0100 Subject: [PATCH] dyndomain script --- dyndomain | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100755 dyndomain diff --git a/dyndomain b/dyndomain new file mode 100755 index 0000000..ac87c83 --- /dev/null +++ b/dyndomain @@ -0,0 +1,216 @@ +#!/usr/bin/python3 + +import sys +import os +import configparser +import subprocess +import smtplib +import time +from email.message import EmailMessage +from pprint import pprint + +rundir=os.path.realpath(os.path.dirname(sys.argv[0])) +os.chdir(rundir) + +from sysbus import sysbus +from ovh import ovh + + +def load_conf(): + global zone_filename, log_filename + global wan_hostname, zone_domain, zone_subdomain + global hosts_list, hosts_ipv4_nat_list + global mail_from, mail_to + + conf = configparser.ConfigParser(allow_no_value=True) + conf.read('home.conf') + + zone_filename = conf['Files']['zonefile'] + log_filename = conf['Files']['logfile'] + + wan_hostname = conf['Wan']['hostname'] + zone_domain = conf['Zone']['domain'] + zone_subdomain = conf['Zone']['subdomain'] + mail_from = conf['Mail']['from'] + mail_to = conf['Mail']['to'] + + hosts_list = [ host for host in conf['Hosts'] ] + hosts_ipv4_nat_list = [ host for host in conf['NatHosts'] ] + + +def ping(hostname): + cmd = "ping -c1 -w3 %s" % hostname + ret = subprocess.run(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return 0 if ret.returncode else 1 + +def get_ipv6_hosts(): + r = sysbus.requete('Hosts:getDevices') + + ipv6_hosts = dict() + for host in r['status']: + hostname=host['hostName'] + for addr in host['addresses']: + ipv6_hosts[hostname] = [] + if addr['family'] == 'IPv6' and addr['scope'] == 'global': + ipv6_hosts[hostname].append(addr['ipAddress']) + return ipv6_hosts + + +def get_wan_addr(): + r = sysbus.requete('NMC:getWANStatus') + wan = dict() + wan['ipv4'] = r['data']['IPAddress'] + wan['ipv6'] = r['data']['IPv6Address'] + return wan + + +def full_name(host, domain): + host = host.replace('_', '-') + return '.'.join([host, domain]) + + +def make_zone_list(wan_hostname, wan_addr, hosts, hosts_nat, domain): + zone = [] + + wan_hostname = full_name(wan_hostname, domain) + + if wan_addr['ipv4'] != '': + zone.append([wan_hostname, 'A', wan_addr['ipv4']]) + for host in hosts_nat: + zone.append([full_name(host, domain), 'A', wan_addr['ipv4']]) + + if wan_addr['ipv6'] != '': + zone.append([wan_hostname, 'AAAA', wan_addr['ipv6']]) + for host in hosts: + for addr in hosts[host]: + if host in hosts_list: + zone.append([full_name(host, domain), 'AAAA', addr]) + + return zone + + +def read_zone_list_from_file(zone_filename): + zone_list = [] + config = configparser.ConfigParser() + config.read(zone_filename) + for host in config: + for p in config[host]: + zone_list.append([host, p.upper(), config[host][p]]) + return zone_list + + +def make_update_zone_list(zone_list, prev_zone_list): + update_zone_list = [] + for entry in zone_list: + if not any(entry == x for x in prev_zone_list): + update_zone_list.append(entry) + return update_zone_list + + +def write_zone_list_to_file(zone_filename, zone_list): + config = configparser.ConfigParser() + for host,typefield,target in zone_list: + if not host in config: + config[host] = {} + config[host][typefield] = target + with open(zone_filename, 'w') as configfile: + config.write(configfile) + + +def log(msg): + stamp = time.strftime("%Y-%m-%d %H:%M:%S") + with open(log_filename, 'a') as logfile: + for line in msg.split('\n'): + logfile.write("%s - %s\n" % (stamp, msg)) + + +def log_update_zone(zone_list): + for host,typea,addr in zone_list: + log("%-20s %-6s %s" % (host, typea, addr)) + + +def ovh_update_zone(domain, zone_list): + if not len(zone_list): + return False + + client = ovh.Client() + + for host, fieldtype, target in zone_list: + result = client.get('/domain/zone/%s/record' % domain, + fieldType=fieldtype, + subDomain=host) + if len(result) == 0: + #print("Create new entry for %s %s %s" % (host, fieldtype, target)) + client.post('/domain/zone/%s/record' % domain, + fieldType=fieldtype, + subDomain=host, + target=target, + ttl=60) + else: + id = result[0] + #print("Update entry for %s %s %s" % (host, fieldtype, target)) + client.put('/domain/zone/%s/record/%ld' % (domain, id), + target=target) + + #print("Refresh zone %s" % domain) + client.post('/domain/zone/%s/refresh' % domain) + return True + + +def send_update_mail(mail_to, mail_from, zone_domain, update_zone_list, wan): + #print('Send email to %s' % mail_to) + msg = EmailMessage() + + msg['Subject'] = "Livebox update in %s" % zone_domain + msg['From'] = mail_from + msg['To' ] = mail_to + + txt = "Livebox update\n\n" + txt = txt + "WAN IPv4 : %s\n" % wan['ipv4'] + txt = txt + "WAN IPv6 : %s\n" % wan['ipv6'] + + txt = txt + "\nZone %s has been updated:\n" % zone_domain + + for host,tp,addr in update_zone_list: + txt = txt + " %-20s %-4s %s\n" % (host,tp,addr) + txt = txt + '\n' + + msg.set_content(txt) + + s = smtplib.SMTP('localhost') + s.send_message(msg) + s.quit() + + +load_conf() + +if not ping(wan_hostname): + log("%s is down" % wan_hostname) + sys.exit(0) + +sysbus.load_conf() +sysbus.auth() + +hosts = get_ipv6_hosts() +wan = get_wan_addr() + +zone_list = make_zone_list(wan_hostname, wan, hosts, hosts_ipv4_nat_list, zone_subdomain) +prev_zone_list = read_zone_list_from_file(zone_filename) +update_zone_list = make_update_zone_list(zone_list, prev_zone_list) + +#print('zone_list:') +#pprint(zone_list) +#print('prev_zone_list:') +#pprint(prev_zone_list) +#print('update_zone_list:') +#pprint(update_zone_list) + +log_update_zone(update_zone_list) + +sucess = ovh_update_zone(zone_domain, update_zone_list) + +if sucess: + new_zone_list = write_zone_list_to_file(zone_filename, prev_zone_list+update_zone_list) + send_update_mail(mail_to, mail_from, zone_domain, update_zone_list, wan) + +