Compare commits

...

1 Commits

Author SHA1 Message Date
0e901e0cf5 no_livebox version
* does not rely on sysbus
* relies on:
  - a 'myip' style service to retrieve WAN IPv4
  - livebox dns for IPv6
2019-01-14 21:29:32 +01:00
6 changed files with 128 additions and 164 deletions

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
cron.log
home.conf home.conf
ovh.conf ovh.conf
ovh.conf.old
zone.list zone.list
zone.log zone.log

6
.gitmodules vendored
View File

@ -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

241
dyndomain
View File

@ -3,24 +3,23 @@
import sys import sys
import os import os
import configparser import configparser
import urllib.request
import urllib.parse
import socket
import subprocess import subprocess
import smtplib
import time import time
from email.message import EmailMessage
from pprint import pprint
rundir=os.path.realpath(os.path.dirname(sys.argv[0])) rundir=os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(rundir) os.chdir(rundir)
from sysbus import sysbus
from ovh import ovh from ovh import ovh
def load_conf(): def load_conf():
global zone_filename, log_filename global zone_filename, log_filename
global wan_hostname, zone_domain, zone_subdomain global wan_hostname
global hosts_list, hosts_ipv4_nat_list global hosts6_list, hosts4_list
global mail_from, mail_to global zone_domain, zone_subdomain
conf = configparser.ConfigParser(allow_no_value=True) conf = configparser.ConfigParser(allow_no_value=True)
conf.read('home.conf') conf.read('home.conf')
@ -29,188 +28,118 @@ def load_conf():
log_filename = conf['Files']['logfile'] log_filename = conf['Files']['logfile']
wan_hostname = conf['Wan']['hostname'] 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_domain = conf['Zone']['domain']
zone_subdomain = conf['Zone']['subdomain'] 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): def get_host_ipv6(name):
cmd = "ping -c1 -w3 %s" % hostname try:
ret = subprocess.run(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = socket.getaddrinfo(name, None, socket.AF_INET6, socket.SOCK_STREAM)
return 0 if ret.returncode else 1 except socket.gaierror:
return []
def get_ipv6_hosts(): ips = [ r[4][0] for r in res ]
r = sysbus.requete('Hosts:getDevices') return ips
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(): def get_wan_ipv4():
r = sysbus.requete('NMC:getWANStatus') try:
wan = dict() f = urllib.request.urlopen('https://ipv4.wtfismyip.com/text')
wan['ipv4'] = r['data']['IPAddress'] return f.read().decode('utf-8').split('\n')[0]
wan['ipv6'] = r['data']['IPv6Address'] except urllib.error.URLError as e:
return wan print(e)
sys.exit(0)
def full_name(host, domain): def get_wan_ipv6():
host = host.replace('_', '-') return get_host_ipv6(wan_hostname)[0]
return '.'.join([host, domain])
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 = [] zone = []
for host in hosts4:
wan_hostname = full_name(wan_hostname, domain) entry = [ host+'.'+subdomain, 'A', wan_ipv4 ]
zone.append(entry)
if wan_addr['ipv4'] != '': for host in hosts6:
zone.append([wan_hostname, 'A', wan_addr['ipv4']]) for ip in get_host_ipv6(host):
for host in hosts_nat: if ping_alive(ip):
zone.append([full_name(host, domain), 'A', wan_addr['ipv4']]) entry = [ host+'.'+subdomain, 'AAAA', ip ]
zone.append(entry)
if wan_addr['ipv6'] != '': zone.sort()
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 return zone
def read_zone_list_from_file(zone_filename): def read_zone_list_from_file(zone_filename):
zone_list = [] zone = []
config = configparser.ConfigParser() try:
config.read(zone_filename) with open(zone_filename, 'r') as zf:
for host in config: for line in zf:
for p in config[host]: line = line.rstrip('\n')
zone_list.append([host, p.upper(), config[host][p]]) entry = line.split(' ')
return zone_list if entry[1] not in ['A', 'AAAA']:
continue
zone.append(entry)
def make_update_zone_list(zone_list, prev_zone_list): except FileNotFoundError:
update_zone_list = [] pass
for entry in zone_list: return zone
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): def write_zone_list_to_file(zone_filename, zone_list):
config = configparser.ConfigParser() with open(zone_filename, 'w') as zf:
for host,typefield,target in zone_list: for entry in zone_list:
if not host in config: line = ' '.join(entry)+'\n'
config[host] = {} zf.write(line)
config[host][typefield] = target
with open(zone_filename, 'w') as configfile:
config.write(configfile)
def ovh_update_zone(domain, prev_zone_list, zone_list):
def log(msg): if not len(zone_list) and not len(remove_zone_list):
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 return False
client = ovh.Client() 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, result = client.get('/domain/zone/%s/record' % domain,
fieldType=fieldtype, fieldType=fieldtype,
subDomain=host) subDomain=host)
if len(result) == 0: for id in result:
#print("Create new entry for %s %s %s" % (host, fieldtype, target)) print("Deleting old entry for %s %s" % (host, fieldtype))
client.post('/domain/zone/%s/record' % domain, client.delete('/domain/zone/%s/record/%ld' % (domain, id))
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)
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) client.post('/domain/zone/%s/refresh' % domain)
return True 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() load_conf()
hosts4_list.append(wan_hostname)
hosts6_list.append(wan_hostname)
if not ping(wan_hostname): wan_ipv4 = get_wan_ipv4()
log("%s is down" % wan_hostname) wan_ipv6 = get_wan_ipv6()
sys.exit(0)
sysbus.load_conf() zone_list = make_zone_list(wan_ipv4, hosts4_list, hosts6_list, zone_subdomain)
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) 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)

40
notes.txt Normal file
View File

@ -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

View File

@ -8,7 +8,7 @@ client = ovh.Client()
ck = client.new_consumer_key_request() 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_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) ck.add_rules(ovh.API_READ_WRITE_SAFE, '/domain/zone/%s/refresh' % domain)
validation = ck.request() validation = ck.request()

1
sysbus

@ -1 +0,0 @@
Subproject commit bf7689ce0abe4be75b77d35010d1d436141e8900