From 98f1e96d1a3814a1217f99cc4ae6da7907599689 Mon Sep 17 00:00:00 2001 From: Andreas Oberritter Date: Sat, 2 Feb 2019 13:28:56 +0100 Subject: [PATCH] dyndns: accept and validate both A and AAAA records; default to client address --- app/lib/utils.py | 12 ++++++++ app/models.py | 2 +- app/views.py | 80 ++++++++++++++++++++++++++++++------------------ 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/app/lib/utils.py b/app/lib/utils.py index 95eaa9c..034fc6d 100644 --- a/app/lib/utils.py +++ b/app/lib/utils.py @@ -2,6 +2,7 @@ import re import json import requests import hashlib +import ipaddress from app import app from distutils.version import StrictVersion @@ -291,3 +292,14 @@ def display_setting_state(value): return "OFF" else: return "UNKNOWN" + + +def validate_ipaddress(address): + try: + ip = ipaddress.ip_address(address) + except ValueError: + pass + else: + if isinstance(ip, (ipaddress.IPv4Address, ipaddress.IPv6Address)): + return [ip] + return [] diff --git a/app/models.py b/app/models.py index e7941f8..5422129 100644 --- a/app/models.py +++ b/app/models.py @@ -1666,7 +1666,7 @@ class Record(object): jrecords = jdata['records'] for jr in jrecords: - if jr['name'] == self.name: + if jr['name'] == self.name and jr['type'] == self.type: self.name = jr['name'] self.type = jr['type'] self.status = jr['disabled'] diff --git a/app/views.py b/app/views.py index 3362f3a..e42d4ca 100755 --- a/app/views.py +++ b/app/views.py @@ -5,6 +5,7 @@ import traceback import re import datetime import json +import ipaddress from distutils.util import strtobool from distutils.version import StrictVersion from functools import wraps @@ -1742,6 +1743,11 @@ def dyndns_update(): hostname = request.args.get('hostname') myip = request.args.get('myip') + if not hostname: + history = History(msg="DynDNS update: missing hostname parameter", created_by=current_user.username) + history.add() + return render_template('dyndns.html', response='nohost'), 200 + try: # get all domains owned by the current user domains = User(id=current_user.id).get_domain() @@ -1765,37 +1771,51 @@ def dyndns_update(): history.add() return render_template('dyndns.html', response='nohost'), 200 - r = Record() - r.name = hostname - # check if the user requested record exists within this domain - if r.exists(domain.name) and r.is_allowed_edit(): - if r.data == myip: - # record content did not change, return 'nochg' - history = History(msg="DynDNS update: attempted update of {0} but record did not change".format(hostname), created_by=current_user.username) - history.add() - return render_template('dyndns.html', response='nochg'), 200 - else: - oldip = r.data - result = r.update(domain.name, myip) - if result['status'] == 'ok': - history = History(msg='DynDNS update: updated record {0} in zone {1}, it changed from {2} to {3}'.format(hostname,domain.name,oldip,myip), detail=str(result), created_by=current_user.username) - history.add() - return render_template('dyndns.html', response='good'), 200 - else: - return render_template('dyndns.html', response='911'), 200 - elif r.is_allowed_edit(): - ondemand_creation = DomainSetting.query.filter(DomainSetting.domain == domain).filter(DomainSetting.setting == 'create_via_dyndns').first() - if (ondemand_creation != None) and (strtobool(ondemand_creation.value) == True): - record = Record(name=hostname,type='A',data=myip,status=False,ttl=3600) - result = record.add(domain.name) - if result['status'] == 'ok': - history = History(msg='DynDNS update: created record {0} in zone {1}, it now represents {2}'.format(hostname,domain.name,myip), detail=str(result), created_by=current_user.username) - history.add() - return render_template('dyndns.html', response='good'), 200 + myip_addr = [] + if myip: + for address in myip.split(','): + myip_addr += utils.validate_ipaddress(address) - history = History(msg='DynDNS update: attempted update of {0} but it does not exist for this user'.format(hostname), created_by=current_user.username) - history.add() - return render_template('dyndns.html', response='nohost'), 200 + remote_addr = utils.validate_ipaddress(request.headers.get('X-Forwarded-For', request.remote_addr).split(', ')[:1]) + + response='nochg' + for ip in myip_addr or remote_addr: + if isinstance(ip, ipaddress.IPv4Address): + rtype='A' + else: + rtype='AAAA' + + r = Record(name=hostname,type=rtype) + # check if the user requested record exists within this domain + if r.exists(domain.name) and r.is_allowed_edit(): + if r.data == str(ip): + # record content did not change, return 'nochg' + history = History(msg="DynDNS update: attempted update of {0} but record did not change".format(hostname), created_by=current_user.username) + history.add() + else: + oldip = r.data + result = r.update(domain.name, str(ip)) + if result['status'] == 'ok': + history = History(msg='DynDNS update: updated {0} record {1} in zone {2}, it changed from {3} to {4}'.format(rtype,hostname,domain.name,oldip,str(ip)), detail=str(result), created_by=current_user.username) + history.add() + response='good' + else: + response='911' + break + elif r.is_allowed_edit(): + ondemand_creation = DomainSetting.query.filter(DomainSetting.domain == domain).filter(DomainSetting.setting == 'create_via_dyndns').first() + if (ondemand_creation is not None) and (strtobool(ondemand_creation.value) == True): + record = Record(name=hostname,type=rtype,data=str(ip),status=False,ttl=3600) + result = record.add(domain.name) + if result['status'] == 'ok': + history = History(msg='DynDNS update: created record {0} in zone {1}, it now represents {2}'.format(hostname,domain.name,str(ip)), detail=str(result), created_by=current_user.username) + history.add() + response='good' + else: + history = History(msg='DynDNS update: attempted update of {0} but it does not exist for this user'.format(hostname), created_by=current_user.username) + history.add() + + return render_template('dyndns.html', response=response), 200 @app.route('/', methods=['GET', 'POST'])