From 4d9138b8283eced0c5b47865e2023c63a55558c2 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Fri, 11 Nov 2022 22:34:56 +0100 Subject: ch-pan: deploy new and improved dyndns/server --- roles/dyndns/server/templates/dyndns-regen.py.j2 | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 roles/dyndns/server/templates/dyndns-regen.py.j2 (limited to 'roles/dyndns/server/templates/dyndns-regen.py.j2') diff --git a/roles/dyndns/server/templates/dyndns-regen.py.j2 b/roles/dyndns/server/templates/dyndns-regen.py.j2 new file mode 100644 index 00000000..3611618d --- /dev/null +++ b/roles/dyndns/server/templates/dyndns-regen.py.j2 @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# +# dyndns.py +# +# a simple ssh based dyndns updater for bind zone files +# +# Copyright (C) 2013 Christian Pointner +# +# This file is part of dyndns.py. +# +# dyndns.py is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# any later version. +# +# dyndns.py is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with dyndns.py. If not, see . +# + +import subprocess +import fcntl +import shutil +import hashlib +import tempfile +import sys +import os + +if len(sys.argv) != 2: + print("ERROR: the base-domain-name must be the sole parameter") + sys.exit(1) + + +domain = sys.argv[1] +base_d = '/var/lib/dyndns' +rndc = '/usr/sbin/rndc' + +digest_fn = os.path.join(base_d, 'digest.'+domain) +header_fn = os.path.join(base_d, 'header.'+domain) +records_fn = os.path.join(base_d, 'records.'+domain) +tmpl_fn = os.path.join(base_d, 'tmpl.'+domain) +serial_fn = os.path.join(base_d, 'serial.'+domain) +db_fn = os.path.join(base_d, 'db.'+domain) + +digest_fd = open(digest_fn, 'a+') +fcntl.flock(digest_fd, fcntl.LOCK_EX) +digest_fd.seek(0) + +h = hashlib.blake2b() +with open(tmpl_fn, 'wb+') as tmpl_fd: + with open(header_fn, 'rb') as header_fd: + shutil.copyfileobj(header_fd, tmpl_fd) + try: + with open(records_fn, 'rb') as records_fd: + shutil.copyfileobj(records_fd, tmpl_fd) + except FileNotFoundError: + pass + + tmpl_fd.seek(0) + while True: + data = tmpl_fd.read(64*1024) + if not data: + break + h.update(data) + +old_digest = digest_fd.readline().strip() +new_digest = h.hexdigest() +if old_digest == new_digest: + print("OK: already up to date.") + sys.exit(0) + +serial = 0 +with open(serial_fn, 'a+') as serial_fd: + serial_fd.seek(0) + serial = int(serial_fd.readline()) + 1 + serial_fd.truncate(0) + serial_fd.write(str(serial)+'\n') + +with tempfile.NamedTemporaryFile(dir=base_d, delete=False, mode='w') as out_fd: + with open(tmpl_fn, 'r') as tmpl_fd: + while True: + line = tmpl_fd.readline() + if not line: + break + line = line.replace('__SERIAL__', str(serial)) + out_fd.write(line) + os.chmod(out_fd.name, 0o644) + os.rename(out_fd.name, db_fn) + + +cmd = [rndc, 'reload', domain] +p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) +rndc_output = '' +for line in p.stdout.readlines(): + rndc_output = rndc_output + line.decode('utf-8') + +r = p.wait() +if r != 0: + print("ERROR: rndc returned %d\n\n%s" % (r, rndc_output)) + sys.exit(1) + +digest_fd.truncate(0) +digest_fd.write(new_digest + '\n') + +print("OK: " + rndc_output) -- cgit v1.2.3