try:
self.update()
+ # 1) Catch network errors early, because we do not want to log
+ # them to the database. They are usually temporary and caused
+ # by the client side, so that we will retry quickly.
+ # 2) If there is an internet server error (HTTP code 500) on the
+ # provider's site, we will not log a failure and try again
+ # shortly.
+ except (DDNSNetworkError, DDNSInternalServerError):
+ raise
+
# In case of any errors, log the failed request and
# raise the exception.
except DDNSError as e:
elif output == "abuse":
raise DDNSAbuseError
elif output == "notfqdn":
- raise DDNSRequestError(_("No valid FQDN was given."))
+ raise DDNSRequestError(_("No valid FQDN was given"))
elif output == "nohost":
- raise DDNSRequestError(_("Specified host does not exist."))
+ raise DDNSRequestError(_("Specified host does not exist"))
elif output == "911":
raise DDNSInternalServerError
elif output == "dnserr":
- raise DDNSInternalServerError(_("DNS error encountered."))
+ raise DDNSInternalServerError(_("DNS error encountered"))
elif output == "badagent":
raise DDNSBlockedError
raise DDNSUpdateError(_("Server response: %s") % output)
+class DDNSProviderDesecIO(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "desec.io"
+ name = "desec.io"
+ website = "https://www.desec.io"
+ protocols = ("ipv6", "ipv4",)
+
+ # ipv4 / ipv6 records are automatically removed when the update
+ # request originates from the respectively other protocol and no
+ # address is explicitly provided for the unused protocol.
+
+ url = "https://update.dedyn.io"
+
+ # desec.io sends the IPv6 and IPv4 address in one request
+
+ def update(self):
+ data = DDNSProtocolDynDNS2.prepare_request_data(self, "ipv4")
+
+ # This one supports IPv6
+ myipv6 = self.get_address("ipv6")
+
+ # Add update information if we have an IPv6 address.
+ if myipv6:
+ data["myipv6"] = myipv6
+
+ self.send_request(data)
+
+
class DDNSProviderDDNSS(DDNSProvider):
handle = "ddnss.de"
name = "DDNSS"
if output == "badauth":
raise DDNSAuthenticationError
elif output == "notfqdn":
- raise DDNSRequestError(_("No valid FQDN was given."))
+ raise DDNSRequestError(_("No valid FQDN was given"))
elif output == "nohost":
- raise DDNSRequestError(_("Specified host does not exist."))
+ raise DDNSRequestError(_("Specified host does not exist"))
elif output == "911":
raise DDNSInternalServerError
elif output == "dnserr":
- raise DDNSInternalServerError(_("DNS error encountered."))
+ raise DDNSInternalServerError(_("DNS error encountered"))
elif output == "disabled":
- raise DDNSRequestError(_("Account disabled or locked."))
+ raise DDNSRequestError(_("Account disabled or locked"))
# If we got here, some other update error happened.
raise DDNSUpdateError
elif output == "blocked":
raise DDNSBlockedError
elif output == "nofqdn":
- raise DDNSRequestError(_("No valid FQDN was given."))
+ raise DDNSRequestError(_("No valid FQDN was given"))
elif output == "nohost":
- raise DDNSRequestError(_("Invalid hostname specified."))
+ raise DDNSRequestError(_("Invalid hostname specified"))
elif output == "notdyn":
- raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
+ raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
elif output == "invalid":
- raise DDNSRequestError(_("Invalid IP address has been sent."))
+ raise DDNSRequestError(_("Invalid IP address has been sent"))
# If we got here, some other update error happened.
raise DDNSUpdateError
# Handle error codes.
if output == "No hostname to update was supplied.":
- raise DDNSRequestError(_("No hostname specified."))
+ raise DDNSRequestError(_("No hostname specified"))
elif output == "The hostname you supplied is not valid.":
- raise DDNSRequestError(_("Invalid hostname specified."))
+ raise DDNSRequestError(_("Invalid hostname specified"))
elif output == "The password you supplied is not valid.":
raise DDNSAuthenticationError
elif output == "Administration has disabled this account.":
- raise DDNSRequestError(_("Account has been disabled."))
+ raise DDNSRequestError(_("Account has been disabled"))
elif output == "Illegal character in IP.":
- raise DDNSRequestError(_("Invalid IP address has been sent."))
+ raise DDNSRequestError(_("Invalid IP address has been sent"))
elif output == "Too many failed requests.":
- raise DDNSRequestError(_("Too many failed requests."))
+ raise DDNSRequestError(_("Too many failed requests"))
# If we got here, some other update error happened.
raise DDNSUpdateError
+class DDNSProviderDuckDNS(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "duckdns.org"
+ name = "Duck DNS"
+ website = "http://www.duckdns.org/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the request is to be found
+ # https://www.duckdns.org/install.jsp
+
+ url = "https://www.duckdns.org/nic/update"
+
+
class DDNSProviderDynDNS(DDNSProtocolDynDNS2, DDNSProvider):
handle = "dyndns.org"
name = "Dyn"
raise DDNSAuthenticationError
elif output.startswith("NOSERVICE"):
- raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain."))
+ raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain"))
elif output.startswith("ILLEGAL INPUT"):
- raise DDNSRequestError(_("Invalid data has been sent."))
+ raise DDNSRequestError(_("Invalid data has been sent"))
elif output.startswith("TOOSOON"):
- raise DDNSRequestError(_("Too frequent update requests have been sent."))
+ raise DDNSRequestError(_("Too frequent update requests have been sent"))
# If we got here, some other update error happened.
raise DDNSUpdateError
# Handle error codes.
if output.startswith("400"):
- raise DDNSRequestError(_("Malformed request has been sent."))
+ raise DDNSRequestError(_("Malformed request has been sent"))
elif output.startswith("401"):
raise DDNSAuthenticationError
elif output.startswith("402"):
- raise DDNSRequestError(_("Too frequent update requests have been sent."))
+ raise DDNSRequestError(_("Too frequent update requests have been sent"))
elif output.startswith("403"):
raise DDNSInternalServerError
if errorcode == "304155":
raise DDNSAuthenticationError
elif errorcode == "304153":
- raise DDNSRequestError(_("Domain not found."))
+ raise DDNSRequestError(_("Domain not found"))
# If we got here, some other update error happened.
raise DDNSUpdateError
if output == "ERROR: Unable to locate this record":
raise DDNSAuthenticationError
elif "is an invalid IP address" in output:
- raise DDNSRequestError(_("Invalid IP address has been sent."))
+ raise DDNSRequestError(_("Invalid IP address has been sent"))
# If we got here, some other update error happened.
raise DDNSUpdateError
+class DDNSProviderJoker(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "joker.com"
+ name = "Joker.com Dynamic DNS"
+ website = "https://joker.com/"
+ protocols = ("ipv4",)
+
+ # Information about the request can be found here:
+ # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
+ # Using DynDNS V2 protocol over HTTPS here
+
+ url = "https://svc.joker.com/nic/update"
+
+
+class DDNSProviderGoogle(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "domains.google.com"
+ name = "Google Domains"
+ website = "https://domains.google.com/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the HTTP request is to be found
+ # here: https://support.google.com/domains/answer/6147083?hl=en
+
+ url = "https://domains.google.com/nic/update"
+
+
class DDNSProviderLightningWireLabs(DDNSProvider):
handle = "dns.lightningwirelabs.com"
name = "Lightning Wire Labs DNS Service"
raise DDNSUpdateError
+class DDNSProviderLoopia(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "loopia.se"
+ name = "Loopia AB"
+ website = "https://www.loopia.com"
+ protocols = ("ipv4",)
+
+ # Information about the format of the HTTP request is to be found
+ # here: https://support.loopia.com/wiki/About_the_DynDNS_support
+
+ url = "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
+
+
class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2, DDNSProvider):
handle = "myonlineportal.net"
name = "myonlineportal.net"
# Namecheap requires the hostname splitted into a host and domain part.
host, domain = self.hostname.split(".", 1)
+ # Get and store curent IP address.
+ address = self.get_address(proto)
+
data = {
- "ip" : self.get_address(proto),
+ "ip" : address,
"password" : self.password,
"host" : host,
"domain" : domain
if errorcode == "304156":
raise DDNSAuthenticationError
elif errorcode == "316153":
- raise DDNSRequestError(_("Domain not found."))
+ raise DDNSRequestError(_("Domain not found"))
elif errorcode == "316154":
- raise DDNSRequestError(_("Domain not active."))
+ raise DDNSRequestError(_("Domain not active"))
elif errorcode in ("380098", "380099"):
raise DDNSInternalServerError
return data
+class DDNSProviderNowDNS(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "now-dns.com"
+ name = "NOW-DNS"
+ website = "http://now-dns.com/"
+ protocols = ("ipv6", "ipv4")
+
+ # Information about the format of the request is to be found
+ # but only can be accessed by register an account and login
+ # https://now-dns.com/?m=api
+
+ url = "https://now-dns.com/update"
+
+
class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2, DDNSProvider):
handle = "nsupdate.info"
name = "nsupdate.info"
# Raise an error if no token and no useranem and password
# are given.
elif not self.username and not self.password:
- raise DDNSConfigurationError(_("No Auth details specified."))
+ raise DDNSConfigurationError(_("No Auth details specified"))
# HTTP Basic Auth is only allowed if no token is used.
if self.token:
if "401" or "402" in output:
raise DDNSAuthenticationError
elif "408" in output:
- raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
+ raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
elif "409" in output:
- raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
+ raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
elif "412" in output:
- raise DDNSRequestError(_("No valid FQDN was given."))
+ raise DDNSRequestError(_("No valid FQDN was given"))
elif "414" in output:
raise DDNSInternalServerError
raise DDNSUpdateError
+class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "schokokeks.org"
+ name = "Schokokeks"
+ website = "http://www.schokokeks.org/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the request is to be found
+ # https://wiki.schokokeks.org/DynDNS
+ url = "https://dyndns.schokokeks.org/nic/update"
+
+
class DDNSProviderSelfhost(DDNSProtocolDynDNS2, DDNSProvider):
handle = "selfhost.de"
name = "Selfhost.de"
return data
+class DDNSProviderServercow(DDNSProvider):
+ handle = "servercow.de"
+ name = "servercow.de"
+ website = "https://servercow.de/"
+ protocols = ("ipv4", "ipv6")
+
+ url = "https://www.servercow.de/dnsupdate/update.php"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "ipaddr" : self.get_address(proto),
+ "hostname" : self.hostname,
+ "username" : self.username,
+ "pass" : self.password,
+ }
+
+ # Send request to provider
+ response = self.send_request(self.url, data=data)
+
+ # Read response
+ output = response.read()
+
+ # Server responds with OK if update was successful
+ if output.startswith("OK"):
+ return
+
+ # Catch any errors
+ elif output.startswith("FAILED - Authentication failed"):
+ raise DDNSAuthenticationError
+
+ # If we got here, some other update error happened
+ raise DDNSUpdateError(output)
+
+
class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
handle = "spdns.org"
- name = "SPDNS"
- website = "http://spdns.org/"
+ name = "SPDYN"
+ website = "https://www.spdyn.de/"
# Detailed information about request and response codes are provided
# by the vendor. They are using almost the same mechanism and status
# http://wiki.securepoint.de/index.php/SPDNS_FAQ
# http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
- url = "https://update.spdns.de/nic/update"
+ url = "https://update.spdyn.de/nic/update"
@property
def username(self):
url = "https://dyndns.strato.com/nic/update"
+ def prepare_request_data(self, proto):
+ data = DDNSProtocolDynDNS2.prepare_request_data(self, proto)
+ data.update({
+ "mx" : "NOCHG",
+ "backupmx" : "NOCHG"
+ })
+
+ return data
+
class DDNSProviderTwoDNS(DDNSProtocolDynDNS2, DDNSProvider):
handle = "twodns.de"
if output.startswith("invalid login"):
raise DDNSAuthenticationError
elif output.startswith("<ERROR CODE=\"704\""):
- raise DDNSRequestError(_("No valid FQDN was given."))
+ raise DDNSRequestError(_("No valid FQDN was given"))
elif output.startswith("<ERROR CODE=\"702\""):
- raise DDNSInternalServerError
+ raise DDNSRequestError(_("Too frequent update requests have been sent"))
# If we got here, some other update error happened.
raise DDNSUpdateError
+class DDNSProviderDNSmadeEasy(DDNSProvider):
+ handle = "dnsmadeeasy.com"
+ name = "DNSmadeEasy.com"
+ website = "http://www.dnsmadeeasy.com/"
+ protocols = ("ipv4",)
+
+ # DNS Made Easy Nameserver Provider also offering Dynamic DNS
+ # Documentation can be found here:
+ # http://www.dnsmadeeasy.com/dynamic-dns/
+
+ url = "https://cp.dnsmadeeasy.com/servlet/updateip?"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "ip" : self.get_address(proto),
+ "id" : self.hostname,
+ "username" : self.username,
+ "password" : self.password,
+ }
+
+ # Send update to the server.
+ response = self.send_request(self.url, data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Handle success messages.
+ if output.startswith("success") or output.startswith("error-record-ip-same"):
+ return
+
+ # Handle error codes.
+ if output.startswith("error-auth-suspend"):
+ raise DDNSRequestError(_("Account has been suspended"))
+
+ elif output.startswith("error-auth-voided"):
+ raise DDNSRequestError(_("Account has been revoked"))
+
+ elif output.startswith("error-record-invalid"):
+ raise DDNSRequestError(_("Specified host does not exist"))
+
+ elif output.startswith("error-auth"):
+ raise DDNSAuthenticationError
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError(_("Server response: %s") % output)
+
+
class DDNSProviderZZZZ(DDNSProvider):
handle = "zzzz.io"
name = "zzzz"