Compare commits
1 Commits
master
...
no_livebox
Author | SHA1 | Date | |
---|---|---|---|
0e901e0cf5 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
6
.gitmodules
vendored
@ -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
241
dyndomain
@ -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
40
notes.txt
Normal 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
|
||||||
|
|
@ -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
sysbus
@ -1 +0,0 @@
|
|||||||
Subproject commit bf7689ce0abe4be75b77d35010d1d436141e8900
|
|
Loading…
Reference in New Issue
Block a user