dns/ddclient - add "post" protocol in custom service type. for https://github.com/opnsense/plugins/pull/3511

Explain __MYIP__ and __HOSTNAME__ usage in server help text, while here add some missing @staticmethod decorators in various accounts. Make sure the server fields either validates a domain or an uri, depending on the protocol selected.
This commit is contained in:
Ad Schellevis
2023-07-31 15:42:47 +02:00
parent ce6803cd5c
commit c9c2a75460
8 changed files with 85 additions and 26 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
PLUGIN_NAME= ddclient
PLUGIN_VERSION= 1.13
PLUGIN_REVISION= 2
PLUGIN_VERSION= 1.14
#PLUGIN_REVISION= 1
PLUGIN_DEPENDS= ddclient-devel
PLUGIN_COMMENT= Dynamic DNS client
PLUGIN_MAINTAINER= ad@opnsense.org
@@ -27,7 +27,10 @@
<id>account.server</id>
<label>Server</label>
<type>text</type>
<help>DynDNS Server</help>
<help>DynDNS Server hostname or uri to use (depending on the protocol).
When a URI is provided, the tag __MYIP__ will be replaced with the current detected address for this service
and __HOSTNAME__ will contain the (comma separated) list of hostnames provided.
</help>
<style>optional_setting service_custom</style>
</field>
<field>
@@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2021 Deciso B.V.
* Copyright (C) 2021-2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
namespace OPNsense\DynDNS;
use Phalcon\Messages\Message;
use OPNsense\Base\BaseModel;
/**
@@ -36,4 +37,45 @@
*/
class DynDNS extends BaseModel
{
public function performValidation($validateFullModel = false)
{
$messages = parent::performValidation($validateFullModel);
$validate_servers = [];
foreach ($this->getFlatNodes() as $key => $node) {
$tagName = $node->getInternalXMLTagName();
$parentNode = $node->getParentNode();
if ($validateFullModel || $node->isFieldChanged()) {
if ($parentNode->getInternalXMLTagName() === 'account' && in_array($tagName, ['protocol', 'server'])) {
$parentKey = $parentNode->__reference;
$validate_servers[$parentKey] = $parentNode;
}
}
}
foreach ($validate_servers as $key => $node) {
if ((string)$node->service != 'custom') {
continue;
}
$srv = (string)$node->server;
if ((string)$node->protocol == 'post') {
if (empty($srv) || filter_var($srv, FILTER_VALIDATE_URL) === false) {
$messages->appendMessage(
new Message(
gettext("A valid URI is required."),
$key . ".server"
)
);
}
} else {
if (empty($srv) || filter_var($srv, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) {
$messages->appendMessage(
new Message(
gettext("A valid domain is required."),
$key . ".server"
)
);
}
}
}
return $messages;
}
}
@@ -93,11 +93,11 @@
<OptionValues>
<dyndns1>DynDns1</dyndns1>
<dyndns2>DynDns2</dyndns2>
<post>custom POST</post>
</OptionValues>
</protocol>
<server type="HostnameField">
<server type="TextField">
<Required>N</Required>
<IpAllowed>N</IpAllowed>
</server>
<username type="TextField">
<Required>N</Required>
@@ -105,7 +105,7 @@
<ValidationMessage>The username contains invalid characters.</ValidationMessage>
</username>
<password type="UpdateOnlyTextField">
<Required>Y</Required>
<Required>N</Required>
<mask>/^[^\n]*$/</mask>
</password>
<resourceId type="TextField">
@@ -120,6 +120,7 @@ class BaseAccount:
interface = self.settings['interface'] if self.settings.get('interface' ,'').strip() != '' else None
)
if self._current_address != '' and (
self._state.get('ip') is None or
self._current_address != self._state.get('ip') or
@@ -84,6 +84,7 @@ class Azure(BaseAccount):
def known_services():
return Azure._services
@staticmethod
def match(account):
return account.get('service') in Azure._services
@@ -44,6 +44,7 @@ class Cloudflare(BaseAccount):
def known_services():
return Cloudflare._services.keys()
@staticmethod
def match(account):
return account.get('service') in Cloudflare._services
@@ -55,35 +55,46 @@ class DynDNS2(BaseAccount):
def known_services():
return list(DynDNS2._services.keys()) + ['custom']
@staticmethod
def match(account):
if account.get('service') in DynDNS2._services or (
account.get('server') is not None and account.get('protocol') in ['dyndns2', 'dyndns1']
):
if account.get('service') in DynDNS2.known_services():
return True
else:
return False
def execute(self):
if super().execute():
proto = 'https' if self.settings.get('force_ssl', False) else 'http'
if self.settings.get('service') in self._services:
url = "%s://%s/nic/update" % (proto, self._services[self.settings.get('service')])
protocol = self.settings.get('protocol', None)
if protocol == 'post':
url = self.settings.get('server')
url = url.replace('__MYIP__', self.current_address)
url = url.replace('__HOSTNAME__', self.settings.get('hostnames'))
req = requests.post(
url=url,
headers={'User-Agent': 'OPNsense-dyndns'},
auth=HTTPBasicAuth(self.settings.get('username'), self.settings.get('password'))
)
else:
url = "%s://%s/nic/update" % (proto, self.settings.get('server'))
uri_proto = 'https' if self.settings.get('force_ssl', False) else 'http'
if self.settings.get('service') in self._services:
url = "%s://%s/nic/update" % (uri_proto, self._services[self.settings.get('service')])
else:
url = "%s://%s/nic/update" % (uri_proto, self.settings.get('server'))
req_opts = {
'url': url,
'params': {
'hostname': self.settings.get('hostnames'),
'myip': self.current_address,
'wildcard': 'ON' if self.settings.get('wildcard', False) else 'NOCHG'
},
'auth': HTTPBasicAuth(self.settings.get('username'), self.settings.get('password')),
'headers': {
'User-Agent': 'OPNsense-dyndns'
req_opts = {
'url': url,
'params': {
'hostname': self.settings.get('hostnames'),
'myip': self.current_address,
'wildcard': 'ON' if self.settings.get('wildcard', False) else 'NOCHG'
},
'auth': HTTPBasicAuth(self.settings.get('username'), self.settings.get('password')),
'headers': {
'User-Agent': 'OPNsense-dyndns'
}
}
}
req = requests.get(**req_opts)
req = requests.get(**req_opts)
if req.status_code == 200:
if self.is_verbose:
syslog.syslog(