+ url = "https://control.dnspark.com/api/dynamic/update.php"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "domain" : self.hostname,
+ "ip" : self.get_address(proto),
+ }
+
+ # Send update to the server.
+ response = self.send_request(self.url, username=self.username, password=self.password,
+ data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Handle success messages.
+ if output.startswith("ok") or output.startswith("nochange"):
+ return
+
+ # Handle error codes.
+ if output == "unauth":
+ raise DDNSAuthenticationError
+ elif output == "abuse":
+ raise DDNSAbuseError
+ elif output == "blocked":
+ raise DDNSBlockedError
+ elif output == "nofqdn":
+ raise DDNSRequestError(_("No valid FQDN was given"))
+ elif output == "nohost":
+ raise DDNSRequestError(_("Invalid hostname specified"))
+ elif output == "notdyn":
+ raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
+ elif output == "invalid":
+ raise DDNSRequestError(_("Invalid IP address has been sent"))
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError
+
+
+class DDNSProviderDtDNS(DDNSProvider):
+ handle = "dtdns.com"
+ name = "DtDNS"
+ website = "http://dtdns.com/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the HTTPS request is to be found
+ # http://www.dtdns.com/dtsite/updatespec
+
+ url = "https://www.dtdns.com/api/autodns.cfm"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "ip" : self.get_address(proto),
+ "id" : self.hostname,
+ "pw" : self.password
+ }
+
+ # Send update to the server.
+ response = self.send_request(self.url, data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Remove all leading and trailing whitespace.
+ output = output.strip()
+
+ # Handle success messages.
+ if "now points to" in output:
+ return
+
+ # Handle error codes.
+ if output == "No hostname to update was supplied.":
+ raise DDNSRequestError(_("No hostname specified"))
+
+ elif output == "The hostname you supplied is not valid.":
+ 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"))
+
+ elif output == "Illegal character in IP.":
+ raise DDNSRequestError(_("Invalid IP address has been sent"))
+
+ elif output == "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 DDNSProviderDyFi(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "dy.fi"
+ name = "dy.fi"
+ website = "https://www.dy.fi/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the request is to be found
+ # https://www.dy.fi/page/clients?lang=en
+ # https://www.dy.fi/page/specification?lang=en
+
+ url = "http://www.dy.fi/nic/update"
+
+ # Please only send automatic updates when your IP address changes,
+ # or once per 5 to 6 days to refresh the address mapping (they will
+ # expire if not refreshed within 7 days).
+ holdoff_days = 6
+
+
+class DDNSProviderDynDNS(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "dyndns.org"
+ name = "Dyn"
+ website = "http://dyn.com/dns/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the request is to be found
+ # http://http://dyn.com/support/developers/api/perform-update/
+ # http://dyn.com/support/developers/api/return-codes/
+
+ url = "https://members.dyndns.org/nic/update"
+
+
+class DDNSProviderDomainOffensive(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "do.de"
+ name = "Domain-Offensive"
+ website = "https://www.do.de/"
+ protocols = ("ipv6", "ipv4")
+
+ # Detailed information about the request and response codes
+ # are available on the providers webpage.
+ # https://www.do.de/wiki/FlexDNS_-_Entwickler
+
+ url = "https://ddns.do.de/"
+
+
+class DDNSProviderDynU(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "dynu.com"
+ name = "Dynu"
+ website = "http://dynu.com/"
+ protocols = ("ipv6", "ipv4",)
+
+ # Detailed information about the request and response codes
+ # are available on the providers webpage.
+ # http://dynu.com/Default.aspx?page=dnsapi
+
+ url = "https://api.dynu.com/nic/update"
+
+ # DynU 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 DDNSProviderEasyDNS(DDNSProvider):
+ handle = "easydns.com"
+ name = "EasyDNS"
+ website = "http://www.easydns.com/"
+ protocols = ("ipv4",)
+
+ # 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"
+ name = "domopoli.de"
+ website = "http://domopoli.de/"
+ protocols = ("ipv4",)
+
+ # https://www.domopoli.de/?page=howto#DynDns_start
+
+ url = "http://dyndns.domopoli.de/nic/update"
+
+
+class DDNSProviderDynsNet(DDNSProvider):
+ handle = "dyns.net"
+ name = "DyNS"
+ website = "http://www.dyns.net/"
+ protocols = ("ipv4",)
+ can_remove_records = False
+
+ # There is very detailed informatio about how to send the update request and
+ # the possible response codes. (Currently we are using the v1.1 proto)
+ # http://www.dyns.net/documentation/technical/protocol/
+
+ url = "http://www.dyns.net/postscript011.php"
+
+ def update_protocol(self, proto):
+ data = {
+ "ip" : self.get_address(proto),
+ "host" : 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("200"):
+ return
+
+ # Handle error codes.
+ if output.startswith("400"):
+ 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"))
+ elif output.startswith("403"):
+ raise DDNSInternalServerError
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError(_("Server response: %s") % output)
+
+
+class DDNSProviderEnomCom(DDNSResponseParserXML, DDNSProvider):
+ handle = "enom.com"
+ name = "eNom Inc."
+ website = "http://www.enom.com/"
+ protocols = ("ipv4",)
+
+ # There are very detailed information about how to send an update request and
+ # the respone codes.
+ # http://www.enom.com/APICommandCatalog/
+
+ url = "https://dynamic.name-services.com/interface.asp"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "command" : "setdnshost",
+ "responsetype" : "xml",
+ "address" : self.get_address(proto),
+ "domainpassword" : self.password,
+ "zone" : self.hostname
+ }
+
+ # 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 self.get_xml_tag_value(output, "ErrCount") == "0":
+ return
+
+ # Handle error codes.
+ errorcode = self.get_xml_tag_value(output, "ResponseNumber")
+
+ if errorcode == "304155":
+ raise DDNSAuthenticationError
+ elif errorcode == "304153":
+ raise DDNSRequestError(_("Domain not found"))
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError
+
+
+class DDNSProviderEntryDNS(DDNSProvider):
+ handle = "entrydns.net"
+ name = "EntryDNS"
+ website = "http://entrydns.net/"
+ protocols = ("ipv4",)
+
+ # Some very tiny details about their so called "Simple API" can be found
+ # here: https://entrydns.net/help
+ url = "https://entrydns.net/records/modify"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "ip" : self.get_address(proto),
+ }
+
+ # Add auth token to the update url.
+ url = "%s/%s" % (self.url, self.token)
+
+ # Send update to the server.
+ try:
+ response = self.send_request(url, data=data)
+
+ # Handle error codes
+ except urllib2.HTTPError, e:
+ if e.code == 404:
+ raise DDNSAuthenticationError
+
+ elif e.code == 422:
+ raise DDNSRequestError(_("An invalid IP address was submitted"))
+
+ raise
+
+ # Handle success messages.
+ if response.code == 200:
+ return
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError
+
+
+class DDNSProviderFreeDNSAfraidOrg(DDNSProvider):
+ handle = "freedns.afraid.org"
+ name = "freedns.afraid.org"
+ website = "http://freedns.afraid.org/"
+
+ # No information about the request or response could be found on the vendor
+ # page. All used values have been collected by testing.
+ url = "https://freedns.afraid.org/dynamic/update.php"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "address" : self.get_address(proto),
+ }
+
+ # Add auth token to the update url.
+ url = "%s?%s" % (self.url, self.token)
+
+ # Send update to the server.
+ response = self.send_request(url, data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Handle success messages.
+ if output.startswith("Updated") or "has not changed" in output:
+ return
+
+ # Handle error codes.
+ 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"))
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError
+
+
+class DDNSProviderItsdns(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "itsdns.de"
+ name = "it's DNS"
+ website = "http://www.itsdns.de/"
+ protocols = ("ipv6", "ipv4")
+
+ # Information about the format of the HTTP request is to be found
+ # here: https://www.itsdns.de/dynupdatehelp.htm
+
+ url = "https://www.itsdns.de/update.php"
+
+
+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"
+ website = "http://dns.lightningwirelabs.com/"
+
+ # Information about the format of the HTTPS request is to be found
+ # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
+
+ url = "https://dns.lightningwirelabs.com/update"
+
+ def update(self):
+ data = {
+ "hostname" : self.hostname,
+ "address6" : self.get_address("ipv6", "-"),
+ "address4" : self.get_address("ipv4", "-"),
+ }
+
+ # Check if a token has been set.
+ if self.token:
+ data["token"] = self.token
+
+ # Check for username and password.
+ elif self.username and self.password:
+ data.update({
+ "username" : self.username,
+ "password" : 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)
+
+ # Handle success messages.
+ if response.code == 200:
+ return
+
+ # If we got here, some other update error happened.
+ 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"
+ website = "https:/myonlineportal.net/"
+
+ # Information about the request and response can be obtained here:
+ # https://myonlineportal.net/howto_dyndns
+
+ url = "https://myonlineportal.net/updateddns"
+
+ def prepare_request_data(self, proto):
+ data = {
+ "hostname" : self.hostname,
+ "ip" : self.get_address(proto),
+ }
+
+ return data
+
+
+class DDNSProviderNamecheap(DDNSResponseParserXML, DDNSProvider):
+ handle = "namecheap.com"
+ name = "Namecheap"
+ website = "http://namecheap.com"
+ protocols = ("ipv4",)
+
+ # Information about the format of the HTTP request is to be found
+ # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
+ # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
+
+ url = "https://dynamicdns.park-your-domain.com/update"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ # 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" : address,
+ "password" : self.password,
+ "host" : host,
+ "domain" : domain
+ }
+
+ # 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 self.get_xml_tag_value(output, "IP") == address:
+ return
+
+ # Handle error codes.
+ errorcode = self.get_xml_tag_value(output, "ResponseNumber")
+
+ if errorcode == "304156":
+ raise DDNSAuthenticationError
+ elif errorcode == "316153":
+ raise DDNSRequestError(_("Domain not found"))
+ elif errorcode == "316154":
+ raise DDNSRequestError(_("Domain not active"))
+ elif errorcode in ("380098", "380099"):
+ raise DDNSInternalServerError
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError
+
+
+class DDNSProviderNOIP(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "no-ip.com"
+ name = "No-IP"
+ website = "http://www.no-ip.com/"
+ protocols = ("ipv4",)
+
+ # Information about the format of the HTTP request is to be found
+ # here: http://www.no-ip.com/integrate/request and
+ # here: http://www.no-ip.com/integrate/response
+
+ url = "http://dynupdate.no-ip.com/nic/update"
+
+ def prepare_request_data(self, proto):
+ assert proto == "ipv4"
+
+ data = {
+ "hostname" : self.hostname,
+ "address" : self.get_address(proto),
+ }
+
+ 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"
+ website = "http://nsupdate.info/"
+ protocols = ("ipv6", "ipv4",)
+
+ # Information about the format of the HTTP request can be found
+ # after login on the provider user interface and here:
+ # http://nsupdateinfo.readthedocs.org/en/latest/user.html
+
+ url = "https://nsupdate.info/nic/update"
+
+ # TODO nsupdate.info can actually do this, but the functionality
+ # has not been implemented here, yet.
+ can_remove_records = False
+
+ # After a failed update, there will be no retries
+ # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
+ holdoff_failure_days = None
+
+ # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
+ # and for the password a so called secret.
+ @property
+ def username(self):
+ return self.get("hostname")
+
+ @property
+ def password(self):
+ return self.token or self.get("secret")
+
+ def prepare_request_data(self, proto):
+ data = {
+ "myip" : self.get_address(proto),
+ }
+
+ return data
+
+
+class DDNSProviderOpenDNS(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "opendns.com"
+ name = "OpenDNS"
+ website = "http://www.opendns.com"
+
+ # Detailed information about the update request and possible
+ # response codes can be obtained from here:
+ # https://support.opendns.com/entries/23891440
+
+ url = "https://updates.opendns.com/nic/update"
+
+ def prepare_request_data(self, proto):
+ data = {
+ "hostname" : self.hostname,
+ "myip" : self.get_address(proto),
+ }
+
+ return data
+
+
+class DDNSProviderOVH(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "ovh.com"
+ name = "OVH"
+ website = "http://www.ovh.com/"
+ protocols = ("ipv4",)
+
+ # OVH only provides very limited information about how to
+ # update a DynDNS host. They only provide the update url
+ # on the their german subpage.
+ #
+ # http://hilfe.ovh.de/DomainDynHost
+
+ url = "https://www.ovh.com/nic/update"
+
+ def prepare_request_data(self, proto):
+ data = DDNSProtocolDynDNS2.prepare_request_data(self, proto)
+ data.update({
+ "system" : "dyndns",
+ })
+
+ return data
+
+
+class DDNSProviderRegfish(DDNSProvider):
+ handle = "regfish.com"
+ name = "Regfish GmbH"
+ website = "http://www.regfish.com/"
+
+ # A full documentation to the providers api can be found here
+ # but is only available in german.
+ # https://www.regfish.de/domains/dyndns/dokumentation
+
+ url = "https://dyndns.regfish.de/"
+ can_remove_records = False
+
+ def update(self):
+ data = {
+ "fqdn" : self.hostname,
+ }
+
+ # Check if we update an IPv6 address.
+ address6 = self.get_address("ipv6")
+ if address6:
+ data["ipv6"] = address6
+
+ # Check if we update an IPv4 address.
+ address4 = self.get_address("ipv4")
+ if address4:
+ data["ipv4"] = address4
+
+ # Raise an error if none address is given.
+ if not data.has_key("ipv6") and not data.has_key("ipv4"):
+ raise DDNSConfigurationError
+
+ # Check if a token has been set.
+ if self.token:
+ data["token"] = self.token
+
+ # 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"))
+
+ # HTTP Basic Auth is only allowed if no token is used.
+ if self.token:
+ # Send update to the server.
+ response = self.send_request(self.url, data=data)
+ else:
+ # Send update to the server.
+ response = self.send_request(self.url, username=self.username, password=self.password,
+ data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Handle success messages.
+ if "100" in output or "101" in output:
+ return
+
+ # Handle error codes.
+ if "401" or "402" in output:
+ raise DDNSAuthenticationError
+ elif "408" in output:
+ raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
+ elif "409" in output:
+ raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
+ elif "412" in output:
+ raise DDNSRequestError(_("No valid FQDN was given"))
+ elif "414" in output:
+ raise DDNSInternalServerError
+
+ # If we got here, some other update error happened.
+ 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"
+ website = "http://www.selfhost.de/"
+ protocols = ("ipv4",)
+
+ url = "https://carol.selfhost.de/nic/update"
+
+ def prepare_request_data(self, proto):
+ data = DDNSProtocolDynDNS2.prepare_request_data(self, proto)
+ data.update({
+ "hostname" : "1",
+ })
+
+ 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 = "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
+ # codes as dyndns.org so we can inherit all those stuff.
+ #
+ # http://wiki.securepoint.de/index.php/SPDNS_FAQ
+ # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
+
+ url = "https://update.spdyn.de/nic/update"
+
+ @property
+ def username(self):
+ return self.get("username") or self.hostname
+
+ @property
+ def password(self):
+ return self.get("password") or self.token
+
+
+class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "strato.com"
+ name = "Strato AG"
+ website = "http:/www.strato.com/"
+ protocols = ("ipv4",)
+
+ # Information about the request and response can be obtained here:
+ # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
+
+ 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"
+ name = "TwoDNS"
+ website = "http://www.twodns.de"
+ protocols = ("ipv4",)
+
+ # Detailed information about the request can be found here
+ # http://twodns.de/en/faqs
+ # http://twodns.de/en/api
+
+ url = "https://update.twodns.de/update"
+
+ def prepare_request_data(self, proto):
+ assert proto == "ipv4"
+
+ data = {
+ "ip" : self.get_address(proto),
+ "hostname" : self.hostname
+ }
+
+ return data
+
+
+class DDNSProviderUdmedia(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "udmedia.de"
+ name = "Udmedia GmbH"
+ website = "http://www.udmedia.de"
+ protocols = ("ipv4",)
+
+ # Information about the request can be found here
+ # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
+
+ url = "https://www.udmedia.de/nic/update"
+
+
+class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
+ handle = "variomedia.de"
+ name = "Variomedia"
+ website = "http://www.variomedia.de/"
+ protocols = ("ipv6", "ipv4",)
+
+ # Detailed information about the request can be found here
+ # https://dyndns.variomedia.de/
+
+ url = "https://dyndns.variomedia.de/nic/update"
+
+ def prepare_request_data(self, proto):
+ data = {
+ "hostname" : self.hostname,
+ "myip" : self.get_address(proto),
+ }
+
+ return data
+
+
+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"
+ protocols = ("ipv4",)
+
+ # Detailed information about the request and the response codes can be
+ # obtained here:
+ # http://www.zoneedit.com/doc/api/other.html
+ # http://www.zoneedit.com/faq.html
+
+ url = "https://dynamic.zoneedit.com/auth/dynamic.html"
+
+ def update_protocol(self, proto):
+ data = {
+ "dnsto" : self.get_address(proto),
+ "host" : self.hostname
+ }
+
+ # Send update to the server.
+ response = self.send_request(self.url, username=self.username, password=self.password,
+ data=data)
+
+ # Get the full response message.
+ output = response.read()
+
+ # Handle success messages.
+ if output.startswith("<SUCCESS"):
+ return
+
+ # Handle error codes.
+ if output.startswith("invalid login"):
+ raise DDNSAuthenticationError
+ elif output.startswith("<ERROR CODE=\"704\""):
+ raise DDNSRequestError(_("No valid FQDN was given"))
+ elif output.startswith("<ERROR CODE=\"702\""):
+ 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):