duckdns.org: Support new update API

Message ID 20200709151009.9217-1-stefan.schantl@ipfire.org
State Accepted
Headers
Series duckdns.org: Support new update API |

Commit Message

Stefan Schantl July 9, 2020, 3:10 p.m. UTC
  The provider entirely changed the update API, which now is
not longer DynDNS2 compatible, but supports IPv6 and cleaning records.

Sadly they do not provide any detailed return codes in case an update fails.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
---
 src/ddns/providers.py | 46 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 4 deletions(-)
  

Comments

Michael Tremer July 10, 2020, 1:24 p.m. UTC | #1
Hello,

Great to see some activity on here again :)

> On 9 Jul 2020, at 16:10, Stefan Schantl <stefan.schantl@ipfire.org> wrote:
> 
> The provider entirely changed the update API, which now is
> not longer DynDNS2 compatible, but supports IPv6 and cleaning records.
> 
> Sadly they do not provide any detailed return codes in case an update fails.
> 
> Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
> ---
> src/ddns/providers.py | 46 +++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 42 insertions(+), 4 deletions(-)
> 
> diff --git a/src/ddns/providers.py b/src/ddns/providers.py
> index 46d8a67..690516f 100644
> --- a/src/ddns/providers.py
> +++ b/src/ddns/providers.py
> @@ -802,16 +802,54 @@ class DDNSProviderDtDNS(DDNSProvider):
> 		raise DDNSUpdateError
> 
> 
> -class DDNSProviderDuckDNS(DDNSProtocolDynDNS2, DDNSProvider):
> +class DDNSProviderDuckDNS(DDNSProvider):
> 	handle    = "duckdns.org"
> 	name      = "Duck DNS"
> 	website   = "http://www.duckdns.org/"
> -	protocols = ("ipv4",)
> +	protocols = ("ipv6", "ipv4",)

Yay, IPv6!

> 	# Information about the format of the request is to be found
> -	# https://www.duckdns.org/install.jsp
> +	# https://www.duckdns.org/spec.jsp
> +
> +	# ipv4 / ipv6 records are automatically removed during the update
> +	# process ( clear=true ) and the ipv4 / ipv6 addresses appended.
> +	url = "https://www.duckdns.org/update"
> +
> +	def update(self):
> +		# Raise an error if no auth details are given.
> +		if not self.token:
> +			raise DDNSConfigurationError

In general, it would be nice to add a reason for this error like this:

  raise DDNSConfigurationError(“Missing token”)

> +
> +		data =  {
> +			"domains" : self.hostname,
> +			"token"    : self.token,
> +			"ipv6" : self.get_address("ipv6", "-"),
> +			"ip" : self.get_address("ipv4", "-"),
> +                        "clear" : "true",
> +		}
> +
> +		# Send update to the server.
> +		response = self.send_request(self.url, data=data)
> +
> +		# Get the full response message.
> +		output = response.read().decode()
> +
> +		# Remove all leading and trailing whitespace.
> +		output = output.strip()

I think this is a rather pretty solution.

Could you give this to another person for testing?

Acked-by: Michael Tremer <michael.tremer@ipfire.org>

Best,
-Michael

> -	url = "https://www.duckdns.org/nic/update"
> +		print(output)
> +
> +		# Handle success messages.
> +		if output == "OK":
> +			return
> +
> +		# The provider does not give detailed information
> +		# if the update fails. Only a "KO" will be sent back.
> +		if output == "KO":
> +			raise DDNSUpdateError
> +
> +		# If we got here, some other update error happened.
> +		raise DDNSUpdateError
> 
> 
> class DDNSProviderDyFi(DDNSProtocolDynDNS2, DDNSProvider):
> -- 
> 2.20.1
> 
> _______________________________________________
> ddns mailing list
> ddns@lists.ipfire.org
> https://lists.ipfire.org/mailman/listinfo/ddns
  

Patch

diff --git a/src/ddns/providers.py b/src/ddns/providers.py
index 46d8a67..690516f 100644
--- a/src/ddns/providers.py
+++ b/src/ddns/providers.py
@@ -802,16 +802,54 @@  class DDNSProviderDtDNS(DDNSProvider):
 		raise DDNSUpdateError
 
 
-class DDNSProviderDuckDNS(DDNSProtocolDynDNS2, DDNSProvider):
+class DDNSProviderDuckDNS(DDNSProvider):
 	handle    = "duckdns.org"
 	name      = "Duck DNS"
 	website   = "http://www.duckdns.org/"
-	protocols = ("ipv4",)
+	protocols = ("ipv6", "ipv4",)
 
 	# Information about the format of the request is to be found
-	# https://www.duckdns.org/install.jsp
+	# https://www.duckdns.org/spec.jsp
+
+	# ipv4 / ipv6 records are automatically removed during the update
+	# process ( clear=true ) and the ipv4 / ipv6 addresses appended.
+	url = "https://www.duckdns.org/update"
+
+	def update(self):
+		# Raise an error if no auth details are given.
+		if not self.token:
+			raise DDNSConfigurationError
+
+		data =  {
+			"domains" : self.hostname,
+			"token"    : self.token,
+			"ipv6" : self.get_address("ipv6", "-"),
+			"ip" : self.get_address("ipv4", "-"),
+                        "clear" : "true",
+		}
+
+		# Send update to the server.
+		response = self.send_request(self.url, data=data)
+
+		# Get the full response message.
+		output = response.read().decode()
+
+		# Remove all leading and trailing whitespace.
+		output = output.strip()
 
-	url = "https://www.duckdns.org/nic/update"
+		print(output)
+
+		# Handle success messages.
+		if output == "OK":
+			return
+
+		# The provider does not give detailed information
+		# if the update fails. Only a "KO" will be sent back.
+		if output == "KO":
+			raise DDNSUpdateError
+
+		# If we got here, some other update error happened.
+		raise DDNSUpdateError
 
 
 class DDNSProviderDyFi(DDNSProtocolDynDNS2, DDNSProvider):