mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
dns/ddclient: Add support for altering IPv6 addresses in ddclient plugin (#4497)
* Add support for altering IPv6 addresses in ddclient plugin * Refactoring of checkip * dns/ddclient - minor cleanups for https://github.com/opnsense/plugins/pull/4491 * simplify network / host concat a bit * add try...except for the curl fetch in case the other end doesn't return a valid address * extend form help text for "Dynamic ipv6 host" a bit --------- Co-authored-by: SaarLAN-Pissbeutel <183202051+SaarLAN-Pissbeutel@users.noreply.github.com> Co-authored-by: Marc Philippi <mphilippi@t-online.de>
This commit is contained in:
co-authored by
SaarLAN-Pissbeutel
Marc Philippi
parent
2b17488006
commit
8606b3572d
@@ -91,6 +91,13 @@
|
||||
<label>Interface to monitor</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.dynipv6host</id>
|
||||
<label>Dynamic ipv6 host</label>
|
||||
<type>text</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Swap the interface identifier of the ipv6 address with the given partial ipv6 address (the least significant 64 bits of the address)</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>account.checkip_timeout</id>
|
||||
<label>Check ip timeout</label>
|
||||
|
||||
@@ -165,6 +165,11 @@
|
||||
</check001>
|
||||
</Constraints>
|
||||
</checkip>
|
||||
<dynipv6host type="TextField">
|
||||
<Required>N</Required>
|
||||
<mask>/^::(([0-9a-fA-F]{1,4}:){0,3}[0-9a-fA-F]{1,4})?$/u</mask>
|
||||
<ValidationMessage>Entry is not a valid partial ipv6 address definition (e.g. ::1000).</ValidationMessage>
|
||||
</dynipv6host>
|
||||
<checkip_timeout type="IntegerField">
|
||||
<Default>10</Default>
|
||||
<Required>Y</Required>
|
||||
|
||||
@@ -118,7 +118,8 @@ class BaseAccount:
|
||||
service = self.settings.get('checkip'),
|
||||
proto = 'https' if self.settings.get('force_ssl', False) else 'http',
|
||||
timeout = str(self.settings.get('checkip_timeout', '10')),
|
||||
interface = self.settings['interface'] if self.settings.get('interface' ,'').strip() != '' else None
|
||||
interface = self.settings['interface'] if self.settings.get('interface' ,'').strip() != '' else None,
|
||||
dynipv6host = self.settings['dynipv6host'] if self.settings.get('dynipv6host' ,'').strip() != '' else None
|
||||
)
|
||||
|
||||
if self._current_address == None:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Copyright (c) 2022-2023 Ad Schellevis <ad@opnsense.org>
|
||||
Copyright (c) 2022-2025 Ad Schellevis <ad@opnsense.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -67,11 +67,29 @@ def extract_address(host, txt):
|
||||
return ""
|
||||
|
||||
|
||||
def checkip(service, proto='https', timeout='10', interface=None):
|
||||
def transform_ip(ip, ipv6host=None):
|
||||
""" Changes ipv6 addresses if interface identifier is given
|
||||
:param ip: ip address
|
||||
:param ipv6host: 64 bit interface identifier
|
||||
:return ipaddress.IPv4Address|ipaddress.IPv6Address
|
||||
:raises ValueError: If the input can not be converted to an IPaddress
|
||||
"""
|
||||
if ipv6host and ip.find(':') > 0:
|
||||
# extract 64 bit long prefix and add ipv6host [64]bits
|
||||
return ipaddress.ip_address(
|
||||
ipaddress.ip_network("%s/64" % ip, strict=False).network_address.exploded[0:19] +
|
||||
ipaddress.ip_address(ipv6host).exploded[19:]
|
||||
)
|
||||
else:
|
||||
return ipaddress.ip_address(ip)
|
||||
|
||||
|
||||
def checkip(service, proto='https', timeout='10', interface=None, dynipv6host=None):
|
||||
""" find ip address using external services defined in checkip_service_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_'):
|
||||
@@ -84,8 +102,13 @@ def checkip(service, proto='https', timeout='10', interface=None):
|
||||
params.append(interface)
|
||||
url = checkip_service_list[service] % proto
|
||||
params.append(url)
|
||||
return extract_address(urlparse(url).hostname,
|
||||
extracted_address = extract_address(urlparse(url).hostname,
|
||||
subprocess.run(params, capture_output=True, text=True).stdout)
|
||||
try:
|
||||
return str(transform_ip(extracted_address, dynipv6host))
|
||||
except ValueError:
|
||||
# invalid address
|
||||
return ""
|
||||
elif service in ['if', 'if6'] and interface is not None:
|
||||
# return first non private IPv[4|6] interface address
|
||||
ifcfg = subprocess.run(['/sbin/ifconfig', interface], capture_output=True, text=True).stdout
|
||||
@@ -94,7 +117,7 @@ def checkip(service, proto='https', timeout='10', interface=None):
|
||||
parts = line.split()
|
||||
if (parts[0] == 'inet' and service == 'if') or (parts[0] == 'inet6' and service == 'if6'):
|
||||
try:
|
||||
address = ipaddress.ip_address(parts[1])
|
||||
address = transform_ip(parts[1], dynipv6host)
|
||||
if address.is_global:
|
||||
return str(address)
|
||||
except ValueError:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"zone": "{{ account.zone }}",
|
||||
"checkip": "{{ account.checkip }}",
|
||||
"interface": "{% if account.interface %}{{physical_interface(account.interface)}}{% endif %}",
|
||||
"dynipv6host": "{{ account.dynipv6host }}",
|
||||
"checkip_timeout": {{ account.checkip_timeout }},
|
||||
"force_ssl": {{ "true" if account.force_ssl == '1' else "false"}},
|
||||
"ttl": "{{ account.ttl }}",
|
||||
|
||||
Reference in New Issue
Block a user