]> git.ipfire.org Git - ddns.git/blobdiff - src/ddns/providers.py
namecheap: Fix undeclared variable.
[ddns.git] / src / ddns / providers.py
index 1e88995962d8343288a582084d3012833f6ee355..6ac556444553fbf0d6e8b23854fe228ad6c81fc5 100644 (file)
@@ -161,6 +161,15 @@ class DDNSProvider(object):
                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:
@@ -539,6 +548,110 @@ class DDNSProviderBindNsupdate(DDNSProvider):
                return "\n".join(scriptlet)
 
 
+class DDNSProviderChangeIP(DDNSProvider):
+       handle    = "changeip.com"
+       name      = "ChangeIP.com"
+       website   = "https://changeip.com"
+       protocols = ("ipv4",)
+
+       # Detailed information about the update api can be found here.
+       # http://www.changeip.com/accounts/knowledgebase.php?action=displayarticle&id=34
+
+       url = "https://nic.changeip.com/nic/update"
+       can_remove_records = False
+
+       def update_protocol(self, proto):
+               data = {
+                       "hostname" : self.hostname,
+                       "myip"     : self.get_address(proto),
+               }
+
+               # Send update to the server.
+               try:
+                       response = self.send_request(self.url, username=self.username, password=self.password,
+                               data=data)
+
+               # Handle error codes.
+               except urllib2.HTTPError, e:
+                       if e.code == 422:
+                               raise DDNSRequestError(_("Domain not found."))
+
+                       raise
+
+               # Handle success message.
+               if response.code == 200:
+                       return
+
+               # If we got here, some other update error happened.
+               raise DDNSUpdateError(_("Server response: %s") % output)
+
+
+class DDNSProviderDDNSS(DDNSProvider):
+       handle    = "ddnss.de"
+       name      = "DDNSS"
+       website   = "http://www.ddnss.de"
+       protocols = ("ipv4",)
+
+       # Detailed information about how to send the update request and possible response
+       # codes can be obtained from here.
+       # http://www.ddnss.de/info.php
+       # http://www.megacomputing.de/2014/08/dyndns-service-response-time/#more-919
+
+       url = "http://www.ddnss.de/upd.php"
+       can_remove_records = False
+
+       def update_protocol(self, proto):
+               data = {
+                       "ip"   : self.get_address(proto),
+                       "host" : self.hostname,
+               }
+
+               # Check if a token has been set.
+               if self.token:
+                       data["key"] = self.token
+
+               # Check if username and hostname are given.
+               elif self.username and self.password:
+                       data.update({
+                               "user" : self.username,
+                               "pwd"  : self.password,
+                       })
+
+               # Raise an error if no auth details are given.
+               else:
+                       raise DDNSConfigurationError
+
+               # Send update to the server.
+               response = self.send_request(self.url, data=data)
+
+               # This provider sends the response code as part of the header.
+               header = response.info()
+
+               # Get status information from the header.
+               output = header.getheader('ddnss-response')
+
+               # Handle success messages.
+               if output == "good" or output == "nochg":
+                       return
+
+               # Handle error codes.
+               if output == "badauth":
+                       raise DDNSAuthenticationError
+               elif output == "notfqdn":
+                       raise DDNSRequestError(_("No valid FQDN was given."))
+               elif output == "nohost":
+                       raise DDNSRequestError(_("Specified host does not exist."))
+               elif output == "911":
+                       raise DDNSInternalServerError
+               elif output == "dnserr":
+                       raise DDNSInternalServerError(_("DNS error encountered."))
+               elif output == "disabled":
+                       raise DDNSRequestError(_("Account disabled or locked."))
+
+               # If we got here, some other update error happened.
+               raise DDNSUpdateError
+
+
 class DDNSProviderDHS(DDNSProvider):
        handle    = "dhs.org"
        name      = "DHS International"
@@ -716,18 +829,54 @@ class DDNSProviderDynU(DDNSProtocolDynDNS2, DDNSProvider):
                self.send_request(data)
 
 
-class DDNSProviderEasyDNS(DDNSProtocolDynDNS2, DDNSProvider):
+class DDNSProviderEasyDNS(DDNSProvider):
        handle    = "easydns.com"
        name      = "EasyDNS"
        website   = "http://www.easydns.com/"
        protocols = ("ipv4",)
 
-       # There is only some basic documentation provided by the vendor,
-       # also searching the web gain very poor results.
-       # http://mediawiki.easydns.com/index.php/Dynamic_DNS
+       # Detailed information about the request and response codes
+       # (API 1.3) are available on the providers webpage.
+       # https://fusion.easydns.com/index.php?/Knowledgebase/Article/View/102/7/dynamic-dns
 
        url = "http://api.cp.easydns.com/dyn/tomato.php"
 
+       def update_protocol(self, proto):
+               data = {
+                       "myip"     : self.get_address(proto, "-"),
+                       "hostname" : self.hostname,
+               }
+
+               # Send update to the server.
+               response = self.send_request(self.url, data=data,
+                       username=self.username, password=self.password)
+
+               # Get the full response message.
+               output = response.read()
+
+               # Remove all leading and trailing whitespace.
+               output = output.strip()
+
+               # Handle success messages.
+               if output.startswith("NOERROR"):
+                       return
+
+               # Handle error codes.
+               if output.startswith("NOACCESS"):
+                       raise DDNSAuthenticationError
+
+               elif output.startswith("NOSERVICE"):
+                       raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain."))
+
+               elif output.startswith("ILLEGAL INPUT"):
+                       raise DDNSRequestError(_("Invalid data has been sent."))
+
+               elif output.startswith("TOOSOON"):
+                       raise DDNSRequestError(_("Too frequent update requests have been sent."))
+
+               # If we got here, some other update error happened.
+               raise DDNSUpdateError
+
 
 class DDNSProviderDomopoli(DDNSProtocolDynDNS2, DDNSProvider):
        handle    = "domopoli.de"
@@ -908,6 +1057,31 @@ class DDNSProviderFreeDNSAfraidOrg(DDNSProvider):
                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"
@@ -951,6 +1125,18 @@ class DDNSProviderLightningWireLabs(DDNSProvider):
                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"
@@ -987,8 +1173,11 @@ class DDNSProviderNamecheap(DDNSResponseParserXML, DDNSProvider):
                # 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
@@ -1233,7 +1422,7 @@ class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
 
        @property
        def password(self):
-               return self.get("username") or self.token
+               return self.get("password") or self.token
 
 
 class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
@@ -1247,6 +1436,15 @@ class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
 
        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"
@@ -1303,7 +1501,19 @@ class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
                return data
 
 
-class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
+class DDNSProviderXLhost(DDNSProtocolDynDNS2, DDNSProvider):
+        handle    = "xlhost.de"
+        name     = "XLhost"
+        website   = "http://xlhost.de/"
+        protocols = ("ipv4",)
+
+        # Information about the format of the HTTP request is to be found
+        # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
+
+        url = "https://nsupdate.xlhost.de/"
+
+
+class DDNSProviderZoneedit(DDNSProvider):
        handle    = "zoneedit.com"
        name      = "Zoneedit"
        website   = "http://www.zoneedit.com"
@@ -1345,6 +1555,54 @@ class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
                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"