From 716e75eec405ecaff92b12fc26121498c0045e49 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Wed, 17 Dec 2025 14:48:52 +0100 Subject: [PATCH] ddclient: Cloudflare - add Cloudflare dns ip check option, merge https://github.com/opnsense/plugins/pull/4184 with minor modifications. --- .../mvc/app/models/OPNsense/DynDNS/DynDNS.xml | 1 + .../src/opnsense/scripts/ddclient/checkip | 4 +-- .../opnsense/scripts/ddclient/lib/__init__.py | 2 +- .../opnsense/scripts/ddclient/lib/address.py | 33 ++++++++++++++++--- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml index 6eac4cd24..75c390ce1 100644 --- a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml +++ b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml @@ -146,6 +146,7 @@ cloudflare cloudflare-ipv4 cloudflare-ipv6 + cloudflare-dns dynu-ipv4 dynu-ipv6 freedns diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/checkip b/dns/ddclient/src/opnsense/scripts/ddclient/checkip index 0f5126552..08d9edc6f 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/checkip +++ b/dns/ddclient/src/opnsense/scripts/ddclient/checkip @@ -26,13 +26,13 @@ POSSIBILITY OF SUCH DAMAGE. """ import argparse -from lib import checkip_service_list, checkip +from lib import registered_services, checkip if __name__ == '__main__': # handle parameters parser = argparse.ArgumentParser() - parser.add_argument('-s', '--service', help='service name', choices=checkip_service_list.keys(), required=True) + parser.add_argument('-s', '--service', help='service name', choices=registered_services(), required=True) parser.add_argument('-i', '--interface', help='interface', type=str, default='') parser.add_argument('-t', '--tls', help='enforce tls', choices=['0', '1'], default='1') parser.add_argument('--timeout', help='timeout', type=str, default='10') diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/__init__.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/__init__.py index 025fbe05a..762f022d1 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/__init__.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/__init__.py @@ -57,5 +57,5 @@ changed (return status of `execute()`) the state is flushed to disk. """ -from .address import checkip_service_list, checkip +from .address import registered_services, checkip from .poller import AccountFactory, Poller diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py index 44ad79299..396b4720b 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py @@ -26,6 +26,8 @@ import subprocess import re import ipaddress +import dns.resolver +import dns.rdataclass from urllib.parse import urlparse checkip_service_list = { @@ -51,6 +53,19 @@ checkip_service_list = { 'zoneedit': '%s://dynamic.zoneedit.com/checkip.html' } +checkip_dns_list = { + 'cloudflare-dns': { + 'nameservers': ['1.1.1.1','1.0.0.1'], + 'resolve_params': { + 'qname': 'whoami.cloudflare', + 'rdtype': 'TXT', + 'rdclass': dns.rdataclass.from_text('CH') + } + } +} + +def registered_services(): + return list(checkip_service_list.keys()) + list(checkip_dns_list.keys()) def extract_address(host, txt): """ Extract first IPv4 or IPv6 address from provided string @@ -87,17 +102,17 @@ def transform_ip(ip, ipv6host=None): def checkip(service, proto='https', timeout='10', interface=None, dynipv6host=None): - """ find ip address using external services defined in checkip_service_list + """ find ip address using external web services defined in checkip_service_list + or dns services defined in checkip_dns_list :param proto: protocol :param timeout: timeout in seconds :param interface: bind to interface :param dynipv6host: optional partial ipv6 address :return: str """ - if service.startswith('web_'): + if service.lstrip('web_') in checkip_service_list: # configuration name, strip web_ part - service = service[4:] - if service in checkip_service_list: + service = service.lstrip('web_') params = ['/usr/local/bin/curl', '-m', timeout] if interface is not None: params.append("--interface") @@ -124,5 +139,15 @@ def checkip(service, proto='https', timeout='10', interface=None, dynipv6host=No return str(address) except ValueError: continue + elif service.lstrip('dns_') in checkip_dns_list: + svc_info = checkip_dns_list[service.lstrip('dns_')] + resolve_params = svc_info['resolve_params'] + dns_resolver = dns.resolver.Resolver() + dns_resolver.nameservers = svc_info['nameservers'] + try: + dns_response = dns_resolver.resolve(**resolve_params) + return dns_response[0].to_text().strip('"') + except: + return "" else: return ""