From 0e901e0cf510e946bbd32387b1fc555525048746 Mon Sep 17 00:00:00 2001 From: Gilles Grandou Date: Mon, 14 Jan 2019 21:24:59 +0100 Subject: [PATCH] no_livebox version * does not rely on sysbus * relies on: - a 'myip' style service to retrieve WAN IPv4 - livebox dns for IPv6 --- .gitignore | 2 + .gitmodules | 6 -- dyndomain | 241 +++++++++++++++---------------------------- notes.txt | 40 +++++++ register_ovh_account | 2 +- sysbus | 1 - 6 files changed, 128 insertions(+), 164 deletions(-) delete mode 100644 .gitmodules create mode 100644 notes.txt delete mode 160000 sysbus diff --git a/.gitignore b/.gitignore index 7b0d084..302b6a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +cron.log home.conf ovh.conf +ovh.conf.old zone.list zone.log diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 89ed516..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "sysbus"] - path = sysbus - url = https://github.com/rene-d/sysbus.git -[submodule "ovh"] - path = ovh - url = https://github.com/ovh/python-ovh.git diff --git a/dyndomain b/dyndomain index ac87c83..262c75f 100755 --- a/dyndomain +++ b/dyndomain @@ -3,24 +3,23 @@ import sys import os import configparser +import urllib.request +import urllib.parse +import socket 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 + global wan_hostname + global hosts6_list, hosts4_list + global zone_domain, zone_subdomain conf = configparser.ConfigParser(allow_no_value=True) conf.read('home.conf') @@ -29,188 +28,118 @@ def load_conf(): log_filename = conf['Files']['logfile'] wan_hostname = conf['Wan']['hostname'] + + hosts6_list = [ host for host in conf['Hosts'] ] + hosts4_list = [ host for host in conf['NatHosts'] ] + 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_host_ipv6(name): + try: + res = socket.getaddrinfo(name, None, socket.AF_INET6, socket.SOCK_STREAM) + except socket.gaierror: + return [] + ips = [ r[4][0] for r in res ] + return ips -def get_wan_addr(): - r = sysbus.requete('NMC:getWANStatus') - wan = dict() - wan['ipv4'] = r['data']['IPAddress'] - wan['ipv6'] = r['data']['IPv6Address'] - return wan +def get_wan_ipv4(): + try: + f = urllib.request.urlopen('https://ipv4.wtfismyip.com/text') + return f.read().decode('utf-8').split('\n')[0] + except urllib.error.URLError as e: + print(e) + sys.exit(0) -def full_name(host, domain): - host = host.replace('_', '-') - return '.'.join([host, domain]) +def get_wan_ipv6(): + return get_host_ipv6(wan_hostname)[0] -def make_zone_list(wan_hostname, wan_addr, hosts, hosts_nat, domain): +def ping_alive(ip): + cmd = ['fping6', '-q', '-B1', ip] + ret = subprocess.call(cmd) + if not ret: + return True + return False + + +def make_zone_list(wan_ipv4, hosts4, hosts6, subdomain): 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]) - + for host in hosts4: + entry = [ host+'.'+subdomain, 'A', wan_ipv4 ] + zone.append(entry) + for host in hosts6: + for ip in get_host_ipv6(host): + if ping_alive(ip): + entry = [ host+'.'+subdomain, 'AAAA', ip ] + zone.append(entry) + zone.sort() 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 + zone = [] + try: + with open(zone_filename, 'r') as zf: + for line in zf: + line = line.rstrip('\n') + entry = line.split(' ') + if entry[1] not in ['A', 'AAAA']: + continue + zone.append(entry) + except FileNotFoundError: + pass + return zone 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) + with open(zone_filename, 'w') as zf: + for entry in zone_list: + line = ' '.join(entry)+'\n' + zf.write(line) - -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): +def ovh_update_zone(domain, prev_zone_list, zone_list): + if not len(zone_list) and not len(remove_zone_list): return False client = ovh.Client() - for host, fieldtype, target in zone_list: + print("\ndate:", time.strftime("%c")) + for host, fieldtype, target in prev_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) + for id in result: + print("Deleting old entry for %s %s" % (host, fieldtype)) + client.delete('/domain/zone/%s/record/%ld' % (domain, id)) - #print("Refresh zone %s" % domain) + + for host, fieldtype, target in zone_list: + 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) + + 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() +hosts4_list.append(wan_hostname) +hosts6_list.append(wan_hostname) -if not ping(wan_hostname): - log("%s is down" % wan_hostname) - sys.exit(0) +wan_ipv4 = get_wan_ipv4() +wan_ipv6 = get_wan_ipv6() -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) +zone_list = make_zone_list(wan_ipv4, hosts4_list, hosts6_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) - +if zone_list != prev_zone_list: + success = ovh_update_zone(zone_domain, prev_zone_list, zone_list) + write_zone_list_to_file(zone_filename, zone_list) diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..d1b1b6c --- /dev/null +++ b/notes.txt @@ -0,0 +1,40 @@ +get ipv4 wan +------------ + +$ curl -4 http://ifconfig.co +90.116.135.67 +[please limits to 1 req/min] + +$ curl https://ipinfo.io/ip +90.116.135.67 + +$ curl https://ipv4.wtfismyip.com/text +90.116.135.67 +[1 request per minute] + + +get ipv6 wan +------------ + +$ host livebox.home +livebox.home has address 192.168.1.1 +livebox.home has IPv6 address 2a01:cb1d:164:f500:327c:b2ff:fe9e:cf16 + +get ipv6 host +------------- + +$ host arwen.home +arwen.home has address 192.168.1.113 +arwen.home has IPv6 address 2a01:cb1d:164:f500:8ff:3eea:a3b2:714e + +$ host arwen.home +arwen.home has address 192.168.1.113 +arwen.home has IPv6 address 2a01:cb1d:164:f500:3df0:c245:7d7a:26e2 +arwen.home has IPv6 address 2a01:cb1d:164:f500:8ff:3eea:a3b2:714e +arwen.home has IPv6 address 2a01:cb1d:164:f500:28af:1644:6308:f13 + +$ fping6 2a01:cb1d:164:f500:8ff:3eea:a3b2:714e 2a01:cb1d:164:f500:3df0:c245:7d7a:26e2 2a01:cb1d:164:f500:28af:1644:6308:f13 +2a01:cb1d:164:f500:8ff:3eea:a3b2:714e is alive +2a01:cb1d:164:f500:3df0:c245:7d7a:26e2 is unreachable +2a01:cb1d:164:f500:28af:1644:6308:f13 is unreachable + diff --git a/register_ovh_account b/register_ovh_account index b017b64..ba64c80 100755 --- a/register_ovh_account +++ b/register_ovh_account @@ -8,7 +8,7 @@ client = ovh.Client() ck = client.new_consumer_key_request() ck.add_rules(ovh.API_READ_WRITE_SAFE, '/domain/zone/%s/record' % domain) -ck.add_rules(ovh.API_READ_WRITE_SAFE, '/domain/zone/%s/record/*' % domain) +ck.add_rules(ovh.API_READ_WRITE, '/domain/zone/%s/record/*' % domain) ck.add_rules(ovh.API_READ_WRITE_SAFE, '/domain/zone/%s/refresh' % domain) validation = ck.request() diff --git a/sysbus b/sysbus deleted file mode 160000 index bf7689c..0000000 --- a/sysbus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf7689ce0abe4be75b77d35010d1d436141e8900