From 7054a09ec6999b053e51c54443363eea4fe5a7c8 Mon Sep 17 00:00:00 2001 From: Franco Fichtner Date: Thu, 11 Jul 2024 07:45:15 +0200 Subject: [PATCH] dns/ddclient: sync with master --- dns/ddclient/Makefile | 3 +- dns/ddclient/pkg-descr | 5 ++ .../mvc/app/models/OPNsense/DynDNS/DynDNS.php | 12 +-- .../DynDNS/FieldTypes/ServiceField.php | 5 +- .../scripts/ddclient/lib/account/__init__.py | 2 +- .../scripts/ddclient/lib/account/azure.py | 2 +- .../ddclient/lib/account/cloudflare.py | 10 +-- .../scripts/ddclient/lib/account/gandi.py | 81 +++++++++++++++++++ .../opnsense/scripts/ddclient/lib/poller.py | 9 ++- 9 files changed, 106 insertions(+), 23 deletions(-) create mode 100755 dns/ddclient/src/opnsense/scripts/ddclient/lib/account/gandi.py diff --git a/dns/ddclient/Makefile b/dns/ddclient/Makefile index fcf1873f7..b16c15dc8 100644 --- a/dns/ddclient/Makefile +++ b/dns/ddclient/Makefile @@ -1,6 +1,5 @@ PLUGIN_NAME= ddclient -PLUGIN_VERSION= 1.21 -PLUGIN_REVISION= 2 +PLUGIN_VERSION= 1.22 PLUGIN_DEPENDS= ddclient py${PLUGIN_PYTHON}-boto3 PLUGIN_COMMENT= Dynamic DNS client PLUGIN_MAINTAINER= ad@opnsense.org diff --git a/dns/ddclient/pkg-descr b/dns/ddclient/pkg-descr index a54d3b570..8f61187af 100644 --- a/dns/ddclient/pkg-descr +++ b/dns/ddclient/pkg-descr @@ -6,6 +6,11 @@ WWW: https://github.com/ddclient/ddclient Plugin Changelog ================ +1.22 + +* Add gandi support +* Optionally support descriptive values for account selection when using native backend + 1.21 * Add Netcup support (contributed by Ingo Lafrenz) diff --git a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.php b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.php index 9432b5062..31682aae0 100644 --- a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.php +++ b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.php @@ -28,13 +28,13 @@ namespace OPNsense\DynDNS; - use Phalcon\Messages\Message; - use OPNsense\Base\BaseModel; +use OPNsense\Base\BaseModel; +use OPNsense\Base\Messages\Message; - /** - * Class DynDNS - * @package OPNsense\DynDNS - */ +/** + * Class DynDNS + * @package OPNsense\DynDNS + */ class DynDNS extends BaseModel { public function performValidation($validateFullModel = false) diff --git a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/FieldTypes/ServiceField.php b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/FieldTypes/ServiceField.php index 891423c1b..46798b5fa 100644 --- a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/FieldTypes/ServiceField.php +++ b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/FieldTypes/ServiceField.php @@ -42,9 +42,8 @@ class ServiceField extends BaseListField if ((string)$this->getParentModel()->general->backend == 'opnsense') { $supported = json_decode((new Backend())->configdRun("ddclient opnbackend supported"), true); if (!empty($supported)) { - foreach ($supported as $srv) { - self::$internalCacheOptionList[$srv] = $srv; - } + self::$internalCacheOptionList = $supported; + asort(self::$internalCacheOptionList, SORT_NATURAL | SORT_FLAG_CASE); } } } diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/__init__.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/__init__.py index a5cb502ea..4c3716b95 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/__init__.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/__init__.py @@ -61,7 +61,7 @@ class BaseAccount: @staticmethod def known_services(): - return [] + return {} @property def id(self): diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py index 64a635ab0..e368b0f61 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py @@ -82,7 +82,7 @@ class Azure(BaseAccount): @staticmethod def known_services(): - return Azure._services + return {'azure': 'Microsoft Azure'} @staticmethod def match(account): diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py index f5bd716ba..6620e2431 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py @@ -42,7 +42,7 @@ class Cloudflare(BaseAccount): @staticmethod def known_services(): - return Cloudflare._services.keys() + return {'cloudflare': 'Cloudflare'} @staticmethod def match(account): @@ -51,13 +51,7 @@ class Cloudflare(BaseAccount): def execute(self): if super().execute(): # IPv4/IPv6 - recordType = None - if str(self.current_address).find(':') > 1: - #IPv6 - recordType = "AAAA" - else: - #IPv4 - recordType = "A" + recordType = "AAAA" if str(self.current_address).find(':') > 1 else "A" # get ZoneID url = "https://%s/client/v4/zones" % self._services[self.settings.get('service')] diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/gandi.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/gandi.py new file mode 100755 index 000000000..c92f1e254 --- /dev/null +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/gandi.py @@ -0,0 +1,81 @@ +""" + Copyright (c) 2024 Thomas Cekal + Copyright (c) 2024 Ad Schellevis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +""" +import json +import syslog +import requests +from . import BaseAccount + + +class Gandi(BaseAccount): + _services = { + 'gandi': 'api.gandi.net' + } + + def __init__(self, account: dict): + super().__init__(account) + + @staticmethod + def known_services(): + return Gandi._services.keys() + + @staticmethod + def match(account): + return account.get('service') in Gandi._services + + def execute(self): + if super().execute(): + # IPv4/IPv6 + recordType = "AAAA" if str(self.current_address).find(':') > 1 else "A" + + # Use bearer (api key) authentication + url = "https://api.gandi.net/v5/livedns/domains/" + self.settings.get('zone') + "/records/" + self.settings.get('hostnames') + "/" + recordType + payload = "{\"rrset_values\":[\"" + self.current_address + "\"],\"rrset_ttl\":300}" + headers = { + 'authorization': "Bearer " + self.settings.get('password'), + 'content-type': "application/json", + 'User-Agent': 'OPNsense-dyndns' + } + # Send IP address update + req = requests.request("PUT", url, data=payload, headers=headers) + if 200 <= req.status_code < 300: + if self.is_verbose: + syslog.syslog( + syslog.LOG_NOTICE, + "Account %s set new ip %s [%s]" % (self.description, self.current_address, req.text.strip()) + ) + + self.update_state(address=self.current_address, status=req.text.split()[0] if req.text else '') + return True + else: + syslog.syslog( + syslog.LOG_ERR, + "Account %s failed to set new ip %s [%d - %s]" % ( + self.description, self.current_address, req.status_code, req.text.replace('\n', '') + ) + ) + + return False diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/poller.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/poller.py index d338ce569..bb1d4900b 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/poller.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/poller.py @@ -63,9 +63,14 @@ class AccountFactory: return handler(account) def known_services(self): - all_services = [] + all_services = {} for handler in self._account_classes: - all_services += handler.known_services() + data = handler.known_services() + if type(data) is dict: + all_services.update(data) + else: + for item in data: + all_services[item] = item return all_services