From becff39f6df128e04a0e1148c1269500eb1dbb99 Mon Sep 17 00:00:00 2001 From: Victor Wagner Date: Wed, 11 Sep 2019 18:17:43 +0300 Subject: [PATCH] Initial commit. Actual dyndns request is not implemented yet --- README.md | 30 +++++++++++++++++++ dyngo | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ dyngo.conf | 18 ++++++++++++ dyngo.md | 45 ++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 README.md create mode 100644 dyngo create mode 100644 dyngo.conf create mode 100644 dyngo.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b5f951 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +DYNGO +===== + +I like when I can log into my notebook via ssh using some easily +remembered name. + +But it is not always a case in the world of NATs and VPNs. + +If machine always have public IP address, than using some dynamic DNS +service may help (short of neccessity to use full domain name. But +${HOME}/.ssh/config may help). But what if you log into your office +workstation via corporate VPN, and want ssh back home? + +If you have enough power to run (or persuade system administrator to +run) dyndns service in your office network, dyndns can help even there. + +But your setup need to know different dyndns servers depending on IP +address. + +So, I wrote DYNGO is dynamic DNS client which can handle lots of +dynamic DNS servers simultaneously. It reads config file, which +specifies which network ranges correspond to which server, and +then runs as a daemon, periodically polling IP addresses of all +interfaces of the machine. If it finds change, it makes http(s) request +to specified URL. + +See [manual page](dyngo.md) for more info. + + + diff --git a/dyngo b/dyngo new file mode 100644 index 0000000..431e8cf --- /dev/null +++ b/dyngo @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import ipaddress, subprocess, sys, getopt,dbm +from configparser import ConfigParser +class DyndnsNetwork(object): + """ + This class represents dynamic DNS server and network. + It has attributes hostname network server user and password + """ + def __init__(self,config_section): + self.hostname = config_section['hostname'] + self.network = ipaddress.ip_network(config_section['network']) + self.server = config_section['server'] + if user in config_section: + self.user=config_section["user"] + self.password=config_section["password"] + + + def contains(self,address): + if self.network.prefixlen==0 and address.is_private: + return False + if self.network.version != address.version: + return False + return address in self.network + def nsupdate(self, address): + raise NotImplementedError + +def get_current_addresses(): + result=[] + for line in subprocess.run(['ip','-o','addr','show'],check=True,stdout=subprocess.PIPE).stdout.decode('utf-8').split("\n"): + if not len(line): + continue + (no,iface,family,addr,rest)=line.split(maxsplit=4) + address=ipaddress.ip_address(addr.split('/')[0]) + if address.is_loopback or address.is_link_local: + continue + result.append(address) + return result +def check_for_update(): + addrlist=get_current_addresses() + for name,net in networks.items(): + found=False + for a in addrlist: + if net.contains(a): + found =True + if name in database: + old_addr=ipaddress.ip_address(database[name].decode("utf-8")) + if old_addr==a: + # Nothing changed go, to next net + break + # address changed + net.nsupdate(a) + database[name]=str(a) + if not found: + del data[name] + + + + +config=ConfigPaser() +config['dyngo']={'interval':'60','database','/var/lib/dyngo/dyngo.db'} +options=dict(getopt.getopt(sys.argv,"f:")[0]) +if not '-f' in options: + options["-f"]="/etc/dyngo.conf" +if len(config.read(options["-f"]))!=1: + print("Cannot read config %s"%options["-f"],file=sys.stderr) + sys.exit(1) + +conf=config['dyngo'] +interval=int(conf['interval']) +database=dbm.open(conf['database'],"c") +# Convert all other config sections to DyndnsNetwork objects +networks={} +for sect in config.sections(): + if sect == 'dyngo' or sect= 'DEFAULT': + continue + networks[sect]=DyndnsNetwork(config[sect]) +# Remove stale items from persistent database, which are no more +# mentioned in the config file +for i in set([x.decode("utf-8") for x database.keys()])-set(network.keys()): + del database[i] + + +while True: + check_for_update() + time.sleep(interval) + diff --git a/dyngo.conf b/dyngo.conf new file mode 100644 index 0000000..d8c7aba --- /dev/null +++ b/dyngo.conf @@ -0,0 +1,18 @@ +# Config file for dingo dyndns client +# Section name can be anything - it is for your convinience +# parameters - hostname - name to send to server +# network - IP network where this name belonks to +# server - url of dyndns server +[dingo] +interval=60 +database=/var/lib/dingo/dingo.db +[public ipv6] +hostname=antares.wagner.pp.ru +network=::/0 +server=http://www.wagner.pp.ru/cgi-bin/dyndns.cgi +user= +[postgrespro local] +hostname=antares.local.vm +network=192.168.24.0/21 +server=http://fafnir.l.posgrespro.ru/dyndns + diff --git a/dyngo.md b/dyngo.md new file mode 100644 index 0000000..d7ed659 --- /dev/null +++ b/dyngo.md @@ -0,0 +1,45 @@ +% dingo(8) +% Victor Wagner +% September 2019 + +NAME +==== + +dingo - multi-network dynamic DNS client + +SYNOPSIS +======== + +**dyngo** [ **-f** *configuration-file* ] + +DESCRIPTION +=========== + +**dyngo** is dynamic DNS client which periodically tries to check if +some of IP addresses of the current machine change and report to +appropriate dynamic DNS server if so. + +It is primarily intended to let machine be visible in the local DNS +of office or home local networks, VPNs and so on. So it expects that +there could be private dynamic dns servers. Couple of dynamic DNS +scripts is included into distribution to make it easier to set up +dynamic dns. + +But nothing prevent user from defining some of networks as global +ones and put URL of public dyndns server into it. + +If network is defined as global, i.e have netmask /0, private ip ranges +wouldn't be reported to corresponding DNS server. + +CONFIGURATION FILE +================== + +Configuration file **dyngo.conf** is ini-style file. It contains section +**dyngo** with global service parameters and section per dyndns server. + +Names of server-description sections are arbitrary, but should be +unique, because they are used as keys into persistent database. + + + + -- 2.39.2