]>
git.ipfire.org Git - people/stevee/ddns.git/blob - src/ddns/providers.py
4f27af97b336f7a0fa911f0952e4e136d5cf1d8f
2 ###############################################################################
4 # ddns - A dynamic DNS client for IPFire #
5 # Copyright (C) 2012-2017 IPFire development team #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
29 import xml
.dom
.minidom
33 # Import all possible exception types.
36 logger
= logging
.getLogger("ddns.providers")
43 Returns a dict with all automatically registered providers.
45 return _providers
.copy()
47 class DDNSProvider(object):
48 # A short string that uniquely identifies
52 # The full name of the provider.
55 # A weburl to the homepage of the provider.
56 # (Where to register a new account?)
59 # A list of supported protocols.
60 protocols
= ("ipv6", "ipv4")
64 # holdoff time - Number of days no update is performed unless
65 # the IP address has changed.
68 # holdoff time for update failures - Number of days no update
69 # is tried after the last one has failed.
70 holdoff_failure_days
= 0.5
72 # True if the provider is able to remove records, too.
73 # Required to remove AAAA records if IPv6 is absent again.
74 can_remove_records
= True
76 # True if the provider supports authentication via a random
77 # generated token instead of username and password.
78 supports_token_auth
= True
83 Should be overwritten to check if the system the code is running
84 on has all the required tools to support this provider.
88 def __init__(self
, core
, **settings
):
91 # Copy a set of default settings and
92 # update them by those from the configuration file.
93 self
.settings
= self
.DEFAULT_SETTINGS
.copy()
94 self
.settings
.update(settings
)
96 def __init_subclass__(cls
, **kwargs
):
97 super().__init
_subclass
__(**kwargs
)
99 if not all((cls
.handle
, cls
.name
, cls
.website
)):
100 raise DDNSError(_("Provider is not properly configured"))
102 assert cls
.handle
not in _providers
, \
103 "Provider '%s' has already been registered" % cls
.handle
106 _providers
[cls
.handle
] = cls
109 return "<DDNS Provider %s (%s)>" % (self
.name
, self
.handle
)
111 def __cmp__(self
, other
):
112 return (lambda a
, b
: (a
> b
)-(a
< b
))(self
.hostname
, other
.hostname
)
118 def get(self
, key
, default
=None):
120 Get a setting from the settings dictionary.
122 return self
.settings
.get(key
, default
)
127 Fast access to the hostname.
129 return self
.get("hostname")
134 Fast access to the username.
136 return self
.get("username")
141 Fast access to the password.
143 return self
.get("password")
148 Fast access to the token.
150 return self
.get("token")
152 def __call__(self
, force
=False):
154 logger
.debug(_("Updating %s forced") % self
.hostname
)
156 # Do nothing if the last update has failed or no update is required
157 elif self
.has_failure
or not self
.requires_update
:
160 # Execute the update.
164 # 1) Catch network errors early, because we do not want to log
165 # them to the database. They are usually temporary and caused
166 # by the client side, so that we will retry quickly.
167 # 2) If there is an internet server error (HTTP code 500) on the
168 # provider's site, we will not log a failure and try again
170 except (DDNSNetworkError
, DDNSInternalServerError
):
173 # In case of any errors, log the failed request and
174 # raise the exception.
175 except DDNSError
as e
:
176 self
.core
.db
.log_failure(self
.hostname
, e
)
179 logger
.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") %
180 {"hostname": self
.hostname
, "provider": self
.name
})
181 self
.core
.db
.log_success(self
.hostname
)
184 for protocol
in self
.protocols
:
185 if self
.have_address(protocol
):
186 self
.update_protocol(protocol
)
187 elif self
.can_remove_records
:
188 self
.remove_protocol(protocol
)
190 def update_protocol(self
, proto
):
191 raise NotImplementedError
193 def remove_protocol(self
, proto
):
194 if not self
.can_remove_records
:
195 raise RuntimeError("can_remove_records is enabled, but remove_protocol() not implemented")
197 raise NotImplementedError
200 def requires_update(self
):
201 # If the IP addresses have changed, an update is required
202 if self
.ip_address_changed(self
.protocols
):
203 logger
.debug(_("An update for %(hostname)s (%(provider)s) is performed because of an IP address change") %
204 {"hostname": self
.hostname
, "provider": self
.name
})
208 # If the holdoff time has expired, an update is required, too
209 if self
.holdoff_time_expired():
210 logger
.debug(_("An update for %(hostname)s (%(provider)s) is performed because the holdoff time has expired") %
211 {"hostname": self
.hostname
, "provider": self
.name
})
215 # Otherwise, we don't need to perform an update
216 logger
.debug(_("No update required for %(hostname)s (%(provider)s)") %
217 {"hostname": self
.hostname
, "provider": self
.name
})
222 def has_failure(self
):
224 Returns True when the last update has failed and no retry
225 should be performed, yet.
227 last_status
= self
.db
.last_update_status(self
.hostname
)
229 # Return False if the last update has not failed.
230 if not last_status
== "failure":
233 # If there is no holdoff time, we won't update ever again.
234 if self
.holdoff_failure_days
is None:
235 logger
.warning(_("An update has not been performed because earlier updates failed for %s") % self
.hostname
)
236 logger
.warning(_("There will be no retries"))
240 # Determine when the holdoff time ends
241 last_update
= self
.db
.last_update(self
.hostname
, status
=last_status
)
242 holdoff_end
= last_update
+ datetime
.timedelta(days
=self
.holdoff_failure_days
)
244 now
= datetime
.datetime
.utcnow()
245 if now
< holdoff_end
:
246 failure_message
= self
.db
.last_update_failure_message(self
.hostname
)
248 logger
.warning(_("An update has not been performed because earlier updates failed for %s") % self
.hostname
)
251 logger
.warning(_("Last failure message:"))
253 for line
in failure_message
.splitlines():
254 logger
.warning(" %s" % line
)
256 logger
.warning(_("Further updates will be withheld until %s") % holdoff_end
)
262 def ip_address_changed(self
, protos
):
264 Returns True if this host is already up to date
265 and does not need to change the IP address on the
269 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
270 current_address
= self
.get_address(proto
)
272 # Handle if the system has not got any IP address from a protocol
273 # (i.e. had full dual-stack connectivity which it has not any more)
274 if current_address
is None:
275 # If addresses still exists in the DNS system and if this provider
276 # is able to remove records, we will do that.
277 if addresses
and self
.can_remove_records
:
280 # Otherwise, we cannot go on...
283 if not current_address
in addresses
:
288 def holdoff_time_expired(self
):
290 Returns true if the holdoff time has expired
291 and the host requires an update
293 # If no holdoff days is defined, we cannot go on
294 if not self
.holdoff_days
:
297 # Get the timestamp of the last successfull update
298 last_update
= self
.db
.last_update(self
.hostname
, status
="success")
300 # If no timestamp has been recorded, no update has been
301 # performed. An update should be performed now.
305 # Determine when the holdoff time ends
306 holdoff_end
= last_update
+ datetime
.timedelta(days
=self
.holdoff_days
)
308 now
= datetime
.datetime
.utcnow()
310 if now
>= holdoff_end
:
311 logger
.debug("The holdoff time has expired for %s" % self
.hostname
)
314 logger
.debug("Updates for %s are held off until %s" %
315 (self
.hostname
, holdoff_end
))
318 def send_request(self
, *args
, **kwargs
):
320 Proxy connection to the send request
323 return self
.core
.system
.send_request(*args
, **kwargs
)
325 def get_address(self
, proto
, default
=None):
327 Proxy method to get the current IP address.
329 return self
.core
.system
.get_address(proto
) or default
331 def have_address(self
, proto
):
333 Returns True if an IP address for the given protocol
336 address
= self
.get_address(proto
)
344 class DDNSProtocolDynDNS2(object):
346 This is an abstract class that implements the DynDNS updater
347 protocol version 2. As this is a popular way to update dynamic
348 DNS records, this class is supposed make the provider classes
352 # Information about the format of the request is to be found
353 # http://dyn.com/support/developers/api/perform-update/
354 # http://dyn.com/support/developers/api/return-codes/
356 # The DynDNS protocol version 2 does not allow to remove records
357 can_remove_records
= False
359 # The DynDNS protocol version 2 only supports authentication via
360 # username and password.
361 supports_token_auth
= False
363 def prepare_request_data(self
, proto
):
365 "hostname" : self
.hostname
,
366 "myip" : self
.get_address(proto
),
371 def update_protocol(self
, proto
):
372 data
= self
.prepare_request_data(proto
)
374 return self
.send_request(data
)
376 def send_request(self
, data
):
377 # Send update to the server.
378 response
= DDNSProvider
.send_request(self
, self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
380 # Get the full response message.
381 output
= response
.read().decode()
383 # Handle success messages.
384 if output
.startswith("good") or output
.startswith("nochg"):
387 # Handle error codes.
388 if output
== "badauth":
389 raise DDNSAuthenticationError
390 elif output
== "abuse":
392 elif output
== "notfqdn":
393 raise DDNSRequestError(_("No valid FQDN was given"))
394 elif output
== "nohost":
395 raise DDNSRequestError(_("Specified host does not exist"))
396 elif output
== "911":
397 raise DDNSInternalServerError
398 elif output
== "dnserr":
399 raise DDNSInternalServerError(_("DNS error encountered"))
400 elif output
== "badagent":
401 raise DDNSBlockedError
402 elif output
== "badip":
403 raise DDNSBlockedError
405 # If we got here, some other update error happened.
406 raise DDNSUpdateError(_("Server response: %s") % output
)
409 class DDNSResponseParserXML(object):
411 This class provides a parser for XML responses which
412 will be sent by various providers. This class uses the python
413 shipped XML minidom module to walk through the XML tree and return
417 def get_xml_tag_value(self
, document
, content
):
418 # Send input to the parser.
419 xmldoc
= xml
.dom
.minidom
.parseString(document
)
421 # Get XML elements by the given content.
422 element
= xmldoc
.getElementsByTagName(content
)
424 # If no element has been found, we directly can return None.
428 # Only get the first child from an element, even there are more than one.
429 firstchild
= element
[0].firstChild
431 # Get the value of the child.
432 value
= firstchild
.nodeValue
438 class DDNSProviderAllInkl(DDNSProvider
):
439 handle
= "all-inkl.com"
440 name
= "All-inkl.com"
441 website
= "http://all-inkl.com/"
442 protocols
= ("ipv4",)
444 # There are only information provided by the vendor how to
445 # perform an update on a FRITZ Box. Grab requried informations
447 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
449 url
= "https://dyndns.kasserver.com"
450 can_remove_records
= False
451 supports_token_auth
= False
454 # There is no additional data required so we directly can
456 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
)
458 # Get the full response message.
459 output
= response
.read().decode()
461 # Handle success messages.
462 if output
.startswith("good") or output
.startswith("nochg"):
465 # If we got here, some other update error happened.
466 raise DDNSUpdateError
469 class DDNSProviderBindNsupdate(DDNSProvider
):
471 name
= "BIND nsupdate utility"
472 website
= "http://en.wikipedia.org/wiki/Nsupdate"
476 supports_token_auth
= False
480 # Search if the nsupdate utility is available
481 paths
= os
.environ
.get("PATH")
483 for path
in paths
.split(":"):
484 executable
= os
.path
.join(path
, "nsupdate")
486 if os
.path
.exists(executable
):
492 scriptlet
= self
.__make
_scriptlet
()
494 # -v enables TCP hence we transfer keys and other data that may
495 # exceed the size of one packet.
496 # -t sets the timeout
497 command
= ["nsupdate", "-v", "-t", "60"]
499 p
= subprocess
.Popen(command
, shell
=True, stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
500 stdout
, stderr
= p
.communicate(scriptlet
)
502 if p
.returncode
== 0:
505 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p
.returncode
, stderr
))
507 def __make_scriptlet(self
):
510 # Set a different server the update is sent to.
511 server
= self
.get("server", None)
513 scriptlet
.append("server %s" % server
)
515 # Set the DNS zone the host should be added to.
516 zone
= self
.get("zone", None)
518 scriptlet
.append("zone %s" % zone
)
520 key
= self
.get("key", None)
522 secret
= self
.get("secret")
524 scriptlet
.append("key %s %s" % (key
, secret
))
526 ttl
= self
.get("ttl", self
.DEFAULT_TTL
)
528 # Perform an update for each supported protocol.
529 for rrtype
, proto
in (("AAAA", "ipv6"), ("A", "ipv4")):
530 address
= self
.get_address(proto
)
534 scriptlet
.append("update delete %s. %s" % (self
.hostname
, rrtype
))
535 scriptlet
.append("update add %s. %s %s %s" % \
536 (self
.hostname
, ttl
, rrtype
, address
))
538 # Send the actions to the server.
539 scriptlet
.append("send")
540 scriptlet
.append("quit")
542 logger
.debug(_("Scriptlet:"))
543 for line
in scriptlet
:
544 # Masquerade the line with the secret key.
545 if line
.startswith("key"):
546 line
= "key **** ****"
548 logger
.debug(" %s" % line
)
550 return "\n".join(scriptlet
).encode()
553 class DDNSProviderChangeIP(DDNSProvider
):
554 handle
= "changeip.com"
555 name
= "ChangeIP.com"
556 website
= "https://changeip.com"
557 protocols
= ("ipv4",)
559 # Detailed information about the update api can be found here.
560 # http://www.changeip.com/accounts/knowledgebase.php?action=displayarticle&id=34
562 url
= "https://nic.changeip.com/nic/update"
563 can_remove_records
= False
564 supports_token_auth
= False
566 def update_protocol(self
, proto
):
568 "hostname" : self
.hostname
,
569 "myip" : self
.get_address(proto
),
572 # Send update to the server.
574 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
576 # Handle error codes.
577 except urllib
.error
.HTTPError
as e
:
579 raise DDNSRequestError(_("Domain not found."))
583 # Handle success message.
584 if response
.code
== 200:
587 # If we got here, some other update error happened.
588 raise DDNSUpdateError(_("Server response: %s") % output
)
591 class DDNSProviderDesecIO(DDNSProtocolDynDNS2
, DDNSProvider
):
594 website
= "https://www.desec.io"
595 protocols
= ("ipv6", "ipv4",)
597 # ipv4 / ipv6 records are automatically removed when the update
598 # request originates from the respectively other protocol and no
599 # address is explicitly provided for the unused protocol.
601 url
= "https://update.dedyn.io"
603 # desec.io sends the IPv6 and IPv4 address in one request
606 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
608 # This one supports IPv6
609 myipv6
= self
.get_address("ipv6")
611 # Add update information if we have an IPv6 address.
613 data
["myipv6"] = myipv6
615 self
.send_request(data
)
618 class DDNSProviderDDNSS(DDNSProvider
):
621 website
= "http://www.ddnss.de"
622 protocols
= ("ipv4",)
624 # Detailed information about how to send the update request and possible response
625 # codes can be obtained from here.
626 # http://www.ddnss.de/info.php
627 # http://www.megacomputing.de/2014/08/dyndns-service-response-time/#more-919
629 url
= "https://www.ddnss.de/upd.php"
630 can_remove_records
= False
631 supports_token_auth
= False
633 def update_protocol(self
, proto
):
635 "ip" : self
.get_address(proto
),
636 "host" : self
.hostname
,
639 # Check if a token has been set.
641 data
["key"] = self
.token
643 # Check if username and hostname are given.
644 elif self
.username
and self
.password
:
646 "user" : self
.username
,
647 "pwd" : self
.password
,
650 # Raise an error if no auth details are given.
652 raise DDNSConfigurationError
654 # Send update to the server.
655 response
= self
.send_request(self
.url
, data
=data
)
657 # This provider sends the response code as part of the header.
658 # Get status information from the header.
659 output
= response
.getheader('ddnss-response')
661 # Handle success messages.
662 if output
== "good" or output
== "nochg":
665 # Handle error codes.
666 if output
== "badauth":
667 raise DDNSAuthenticationError
668 elif output
== "notfqdn":
669 raise DDNSRequestError(_("No valid FQDN was given"))
670 elif output
== "nohost":
671 raise DDNSRequestError(_("Specified host does not exist"))
672 elif output
== "911":
673 raise DDNSInternalServerError
674 elif output
== "dnserr":
675 raise DDNSInternalServerError(_("DNS error encountered"))
676 elif output
== "disabled":
677 raise DDNSRequestError(_("Account disabled or locked"))
679 # If we got here, some other update error happened.
680 raise DDNSUpdateError
683 class DDNSProviderDHS(DDNSProvider
):
685 name
= "DHS International"
686 website
= "http://dhs.org/"
687 protocols
= ("ipv4",)
689 # No information about the used update api provided on webpage,
690 # grabed from source code of ez-ipudate.
692 # Provider currently does not support TLS 1.2.
693 url
= "https://members.dhs.org/nic/hosts"
694 can_remove_records
= False
695 supports_token_auth
= False
697 def update_protocol(self
, proto
):
699 "domain" : self
.hostname
,
700 "ip" : self
.get_address(proto
),
702 "hostcmdstage" : "2",
706 # Send update to the server.
707 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
709 # Handle success messages.
710 if response
.code
== 200:
713 # If we got here, some other update error happened.
714 raise DDNSUpdateError
717 class DDNSProviderDNSpark(DDNSProvider
):
718 handle
= "dnspark.com"
720 website
= "http://dnspark.com/"
721 protocols
= ("ipv4",)
723 # Informations to the used api can be found here:
724 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
726 url
= "https://control.dnspark.com/api/dynamic/update.php"
727 can_remove_records
= False
728 supports_token_auth
= False
730 def update_protocol(self
, proto
):
732 "domain" : self
.hostname
,
733 "ip" : self
.get_address(proto
),
736 # Send update to the server.
737 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
739 # Get the full response message.
740 output
= response
.read().decode()
742 # Handle success messages.
743 if output
.startswith("ok") or output
.startswith("nochange"):
746 # Handle error codes.
747 if output
== "unauth":
748 raise DDNSAuthenticationError
749 elif output
== "abuse":
751 elif output
== "blocked":
752 raise DDNSBlockedError
753 elif output
== "nofqdn":
754 raise DDNSRequestError(_("No valid FQDN was given"))
755 elif output
== "nohost":
756 raise DDNSRequestError(_("Invalid hostname specified"))
757 elif output
== "notdyn":
758 raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
759 elif output
== "invalid":
760 raise DDNSRequestError(_("Invalid IP address has been sent"))
762 # If we got here, some other update error happened.
763 raise DDNSUpdateError
766 class DDNSProviderDtDNS(DDNSProvider
):
769 website
= "http://dtdns.com/"
770 protocols
= ("ipv4",)
772 # Information about the format of the HTTPS request is to be found
773 # http://www.dtdns.com/dtsite/updatespec
775 url
= "https://www.dtdns.com/api/autodns.cfm"
776 can_remove_records
= False
777 supports_token_auth
= False
779 def update_protocol(self
, proto
):
781 "ip" : self
.get_address(proto
),
782 "id" : self
.hostname
,
786 # Send update to the server.
787 response
= self
.send_request(self
.url
, data
=data
)
789 # Get the full response message.
790 output
= response
.read().decode()
792 # Remove all leading and trailing whitespace.
793 output
= output
.strip()
795 # Handle success messages.
796 if "now points to" in output
:
799 # Handle error codes.
800 if output
== "No hostname to update was supplied.":
801 raise DDNSRequestError(_("No hostname specified"))
803 elif output
== "The hostname you supplied is not valid.":
804 raise DDNSRequestError(_("Invalid hostname specified"))
806 elif output
== "The password you supplied is not valid.":
807 raise DDNSAuthenticationError
809 elif output
== "Administration has disabled this account.":
810 raise DDNSRequestError(_("Account has been disabled"))
812 elif output
== "Illegal character in IP.":
813 raise DDNSRequestError(_("Invalid IP address has been sent"))
815 elif output
== "Too many failed requests.":
816 raise DDNSRequestError(_("Too many failed requests"))
818 # If we got here, some other update error happened.
819 raise DDNSUpdateError
822 class DDNSProviderDuckDNS(DDNSProvider
):
823 handle
= "duckdns.org"
825 website
= "http://www.duckdns.org/"
826 protocols
= ("ipv6", "ipv4",)
828 # Information about the format of the request is to be found
829 # https://www.duckdns.org/spec.jsp
831 url
= "https://www.duckdns.org/update"
832 can_remove_records
= False
833 supports_token_auth
= True
836 # Raise an error if no auth details are given.
838 raise DDNSConfigurationError
841 "domains" : self
.hostname
,
842 "token" : self
.token
,
845 # Check if we update an IPv4 address.
846 address4
= self
.get_address("ipv4")
848 data
["ip"] = address4
850 # Check if we update an IPv6 address.
851 address6
= self
.get_address("ipv6")
853 data
["ipv6"] = address6
855 # Raise an error if no address is given.
856 if "ip" not in data
and "ipv6" not in data
:
857 raise DDNSConfigurationError
859 # Send update to the server.
860 response
= self
.send_request(self
.url
, data
=data
)
862 # Get the full response message.
863 output
= response
.read().decode()
865 # Remove all leading and trailing whitespace.
866 output
= output
.strip()
868 # Handle success messages.
872 # The provider does not give detailed information
873 # if the update fails. Only a "KO" will be sent back.
875 raise DDNSUpdateError
877 # If we got here, some other update error happened.
878 raise DDNSUpdateError
881 class DDNSProviderDyFi(DDNSProtocolDynDNS2
, DDNSProvider
):
884 website
= "https://www.dy.fi/"
885 protocols
= ("ipv4",)
887 # Information about the format of the request is to be found
888 # https://www.dy.fi/page/clients?lang=en
889 # https://www.dy.fi/page/specification?lang=en
891 url
= "https://www.dy.fi/nic/update"
893 # Please only send automatic updates when your IP address changes,
894 # or once per 5 to 6 days to refresh the address mapping (they will
895 # expire if not refreshed within 7 days).
899 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
900 handle
= "dyndns.org"
902 website
= "http://dyn.com/dns/"
903 protocols
= ("ipv4",)
905 # Information about the format of the request is to be found
906 # http://http://dyn.com/support/developers/api/perform-update/
907 # http://dyn.com/support/developers/api/return-codes/
909 url
= "https://members.dyndns.org/nic/update"
912 class DDNSProviderDomainOffensive(DDNSProtocolDynDNS2
, DDNSProvider
):
914 name
= "Domain-Offensive"
915 website
= "https://www.do.de/"
916 protocols
= ("ipv6", "ipv4")
918 # Detailed information about the request and response codes
919 # are available on the providers webpage.
920 # https://www.do.de/wiki/FlexDNS_-_Entwickler
922 url
= "https://ddns.do.de/"
924 class DDNSProviderDynUp(DDNSProvider
):
927 website
= "http://dynup.de/"
928 protocols
= ("ipv4",)
930 # Information about the format of the HTTPS request is to be found
931 # https://dyndnsfree.de/user/hilfe.php
933 url
= "https://dynup.de/dyn.php"
934 can_remove_records
= False
935 supports_token_auth
= False
937 def update_protocol(self
, proto
):
939 "username" : self
.username
,
940 "password" : self
.password
,
941 "hostname" : self
.hostname
,
945 # Send update to the server.
946 response
= self
.send_request(self
.url
, data
=data
)
948 # Get the full response message.
949 output
= response
.read().decode()
951 # Remove all leading and trailing whitespace.
952 output
= output
.strip()
954 # Handle success messages.
955 if output
.startswith("I:OK"):
958 # If we got here, some other update error happened.
959 raise DDNSUpdateError
962 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
965 website
= "http://dynu.com/"
966 protocols
= ("ipv6", "ipv4",)
968 # Detailed information about the request and response codes
969 # are available on the providers webpage.
970 # http://dynu.com/Default.aspx?page=dnsapi
972 url
= "https://api.dynu.com/nic/update"
974 # DynU sends the IPv6 and IPv4 address in one request
977 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
979 # This one supports IPv6
980 myipv6
= self
.get_address("ipv6")
982 # Add update information if we have an IPv6 address.
984 data
["myipv6"] = myipv6
986 self
.send_request(data
)
989 class DDNSProviderEasyDNS(DDNSProvider
):
990 handle
= "easydns.com"
992 website
= "http://www.easydns.com/"
993 protocols
= ("ipv4",)
995 # Detailed information about the request and response codes
996 # (API 1.3) are available on the providers webpage.
997 # https://fusion.easydns.com/index.php?/Knowledgebase/Article/View/102/7/dynamic-dns
999 url
= "https://api.cp.easydns.com/dyn/tomato.php"
1001 supports_token_auth
= False
1003 def update_protocol(self
, proto
):
1005 "myip" : self
.get_address(proto
, "-"),
1006 "hostname" : self
.hostname
,
1009 # Send update to the server.
1010 response
= self
.send_request(self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
1012 # Get the full response message.
1013 output
= response
.read().decode()
1015 # Remove all leading and trailing whitespace.
1016 output
= output
.strip()
1018 # Handle success messages.
1019 if output
.startswith("NOERROR"):
1022 # Handle error codes.
1023 if output
.startswith("NOACCESS"):
1024 raise DDNSAuthenticationError
1026 elif output
.startswith("NOSERVICE"):
1027 raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain"))
1029 elif output
.startswith("ILLEGAL INPUT"):
1030 raise DDNSRequestError(_("Invalid data has been sent"))
1032 elif output
.startswith("TOOSOON"):
1033 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1035 # If we got here, some other update error happened.
1036 raise DDNSUpdateError
1039 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
1040 handle
= "domopoli.de"
1041 name
= "domopoli.de"
1042 website
= "http://domopoli.de/"
1043 protocols
= ("ipv4",)
1045 # https://www.domopoli.de/?page=howto#DynDns_start
1047 # This provider does not support TLS 1.2.
1048 url
= "https://dyndns.domopoli.de/nic/update"
1051 class DDNSProviderDynsNet(DDNSProvider
):
1054 website
= "http://www.dyns.net/"
1055 protocols
= ("ipv4",)
1056 can_remove_records
= False
1057 supports_token_auth
= False
1059 # There is very detailed informatio about how to send the update request and
1060 # the possible response codes. (Currently we are using the v1.1 proto)
1061 # http://www.dyns.net/documentation/technical/protocol/
1063 url
= "https://www.dyns.net/postscript011.php"
1065 def update_protocol(self
, proto
):
1067 "ip" : self
.get_address(proto
),
1068 "host" : self
.hostname
,
1069 "username" : self
.username
,
1070 "password" : self
.password
,
1073 # Send update to the server.
1074 response
= self
.send_request(self
.url
, data
=data
)
1076 # Get the full response message.
1077 output
= response
.read().decode()
1079 # Handle success messages.
1080 if output
.startswith("200"):
1083 # Handle error codes.
1084 if output
.startswith("400"):
1085 raise DDNSRequestError(_("Malformed request has been sent"))
1086 elif output
.startswith("401"):
1087 raise DDNSAuthenticationError
1088 elif output
.startswith("402"):
1089 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1090 elif output
.startswith("403"):
1091 raise DDNSInternalServerError
1093 # If we got here, some other update error happened.
1094 raise DDNSUpdateError(_("Server response: %s") % output
)
1097 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
1100 website
= "http://www.enom.com/"
1101 protocols
= ("ipv4",)
1103 # There are very detailed information about how to send an update request and
1104 # the respone codes.
1105 # http://www.enom.com/APICommandCatalog/
1107 url
= "https://dynamic.name-services.com/interface.asp"
1108 can_remove_records
= False
1109 supports_token_auth
= False
1111 def update_protocol(self
, proto
):
1113 "command" : "setdnshost",
1114 "responsetype" : "xml",
1115 "address" : self
.get_address(proto
),
1116 "domainpassword" : self
.password
,
1117 "zone" : self
.hostname
1120 # Send update to the server.
1121 response
= self
.send_request(self
.url
, data
=data
)
1123 # Get the full response message.
1124 output
= response
.read().decode()
1126 # Handle success messages.
1127 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
1130 # Handle error codes.
1131 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1133 if errorcode
== "304155":
1134 raise DDNSAuthenticationError
1135 elif errorcode
== "304153":
1136 raise DDNSRequestError(_("Domain not found"))
1138 # If we got here, some other update error happened.
1139 raise DDNSUpdateError
1142 class DDNSProviderEntryDNS(DDNSProvider
):
1143 handle
= "entrydns.net"
1145 website
= "http://entrydns.net/"
1146 protocols
= ("ipv4",)
1148 # Some very tiny details about their so called "Simple API" can be found
1149 # here: https://entrydns.net/help
1150 url
= "https://entrydns.net/records/modify"
1151 can_remove_records
= False
1152 supports_token_auth
= True
1154 def update_protocol(self
, proto
):
1156 "ip" : self
.get_address(proto
),
1159 # Add auth token to the update url.
1160 url
= "%s/%s" % (self
.url
, self
.token
)
1162 # Send update to the server.
1164 response
= self
.send_request(url
, data
=data
)
1166 # Handle error codes
1167 except urllib
.error
.HTTPError
as e
:
1169 raise DDNSAuthenticationError
1172 raise DDNSRequestError(_("An invalid IP address was submitted"))
1176 # Handle success messages.
1177 if response
.code
== 200:
1180 # If we got here, some other update error happened.
1181 raise DDNSUpdateError
1184 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
1185 handle
= "freedns.afraid.org"
1186 name
= "freedns.afraid.org"
1187 website
= "http://freedns.afraid.org/"
1189 # No information about the request or response could be found on the vendor
1190 # page. All used values have been collected by testing.
1191 url
= "https://sync.afraid.org/u/"
1192 can_remove_records
= False
1193 supports_token_auth
= True
1195 def update_protocol(self
, proto
):
1197 # Add auth token to the update url.
1198 url
= "%s%s/" % (self
.url
, self
.token
)
1200 # Send update to the server.
1201 response
= self
.send_request(url
)
1203 # Get the full response message.
1204 output
= response
.read().decode()
1206 # Handle success messages.
1207 if output
.startswith("Updated") or output
.startswith("No IP changed detected"):
1210 # Handle error codes.
1211 if output
== "ERROR: Unable to locate this record":
1212 raise DDNSAuthenticationError
1213 elif "is an invalid IP address" in output
:
1214 raise DDNSRequestError(_("Invalid IP address has been sent"))
1216 # If we got here, some other update error happened.
1217 raise DDNSUpdateError
1220 class DDNSProviderHENet(DDNSProtocolDynDNS2
, DDNSProvider
):
1223 website
= "https://he.net"
1224 protocols
= ("ipv6", "ipv4",)
1226 # Detailed information about the update api can be found here.
1227 # http://dns.he.net/docs.html
1229 url
= "https://dyn.dns.he.net/nic/update"
1232 return self
.get("hostname")
1236 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1239 website
= "https://www.inwx.com"
1240 protocols
= ("ipv6", "ipv4")
1242 # Information about the format of the HTTP request is to be found
1243 # here: https://www.inwx.com/en/nameserver2/dyndns (requires login)
1244 # Notice: The URL is the same for: inwx.com|de|at|ch|es
1246 url
= "https://dyndns.inwx.com/nic/update"
1249 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1250 handle
= "itsdns.de"
1252 website
= "http://www.itsdns.de/"
1253 protocols
= ("ipv6", "ipv4")
1255 # Information about the format of the HTTP request is to be found
1256 # here: https://www.itsdns.de/dynupdatehelp.htm
1258 url
= "https://www.itsdns.de/update.php"
1261 class DDNSProviderJoker(DDNSProtocolDynDNS2
, DDNSProvider
):
1262 handle
= "joker.com"
1263 name
= "Joker.com Dynamic DNS"
1264 website
= "https://joker.com/"
1265 protocols
= ("ipv4",)
1267 # Information about the request can be found here:
1268 # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
1269 # Using DynDNS V2 protocol over HTTPS here
1271 url
= "https://svc.joker.com/nic/update"
1274 class DDNSProviderKEYSYSTEMS(DDNSProvider
):
1275 handle
= "key-systems.net"
1276 name
= "dynamicdns.key-systems.net"
1277 website
= "https://domaindiscount24.com/"
1278 protocols
= ("ipv4",)
1280 # There are only information provided by the domaindiscount24 how to
1281 # perform an update with HTTP APIs
1282 # https://www.domaindiscount24.com/faq/dynamic-dns
1283 # examples: https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=auto
1284 # https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=213.x.x.x&mx=213.x.x.x
1286 url
= "https://dynamicdns.key-systems.net/update.php"
1287 can_remove_records
= False
1288 supports_token_auth
= False
1290 def update_protocol(self
, proto
):
1291 address
= self
.get_address(proto
)
1293 "hostname" : self
.hostname
,
1294 "password" : self
.password
,
1298 # Send update to the server.
1299 response
= self
.send_request(self
.url
, data
=data
)
1301 # Get the full response message.
1302 output
= response
.read().decode()
1304 # Handle success messages.
1305 if "code = 200" in output
:
1308 # Handle error messages.
1309 if "abuse prevention triggered" in output
:
1310 raise DDNSAbuseError
1311 elif "invalid password" in output
:
1312 raise DDNSAuthenticationError
1313 elif "Authorization failed" in output
:
1314 raise DDNSRequestError(_("Invalid hostname specified"))
1316 # If we got here, some other update error happened.
1317 raise DDNSUpdateError
1320 class DDNSProviderGoogle(DDNSProtocolDynDNS2
, DDNSProvider
):
1321 handle
= "domains.google.com"
1322 name
= "Google Domains"
1323 website
= "https://domains.google.com/"
1324 protocols
= ("ipv4",)
1326 # Information about the format of the HTTP request is to be found
1327 # here: https://support.google.com/domains/answer/6147083?hl=en
1329 url
= "https://domains.google.com/nic/update"
1332 class DDNSProviderLightningWireLabs(DDNSProvider
):
1333 handle
= "dns.lightningwirelabs.com"
1334 name
= "Lightning Wire Labs DNS Service"
1335 website
= "https://dns.lightningwirelabs.com/"
1337 # Information about the format of the HTTPS request is to be found
1338 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
1340 supports_token_auth
= True
1342 url
= "https://dns.lightningwirelabs.com/update"
1345 # Raise an error if no auth details are given.
1347 raise DDNSConfigurationError
1350 "hostname" : self
.hostname
,
1351 "token" : self
.token
,
1352 "address6" : self
.get_address("ipv6", "-"),
1353 "address4" : self
.get_address("ipv4", "-"),
1356 # Send update to the server.
1357 response
= self
.send_request(self
.url
, data
=data
)
1359 # Handle success messages.
1360 if response
.code
== 200:
1363 # If we got here, some other update error happened.
1364 raise DDNSUpdateError
1367 class DDNSProviderLoopia(DDNSProtocolDynDNS2
, DDNSProvider
):
1368 handle
= "loopia.se"
1370 website
= "https://www.loopia.com"
1371 protocols
= ("ipv4",)
1373 # Information about the format of the HTTP request is to be found
1374 # here: https://support.loopia.com/wiki/About_the_DynDNS_support
1376 url
= "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
1379 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
1380 handle
= "myonlineportal.net"
1381 name
= "myonlineportal.net"
1382 website
= "https:/myonlineportal.net/"
1384 # Information about the request and response can be obtained here:
1385 # https://myonlineportal.net/howto_dyndns
1387 url
= "https://myonlineportal.net/updateddns"
1389 def prepare_request_data(self
, proto
):
1391 "hostname" : self
.hostname
,
1392 "ip" : self
.get_address(proto
),
1398 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
1399 handle
= "namecheap.com"
1401 website
= "http://namecheap.com"
1402 protocols
= ("ipv4",)
1404 # Information about the format of the HTTP request is to be found
1405 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
1406 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
1408 url
= "https://dynamicdns.park-your-domain.com/update"
1409 can_remove_records
= False
1410 supports_token_auth
= False
1412 def update_protocol(self
, proto
):
1413 # Namecheap requires the hostname splitted into a host and domain part.
1414 host
, domain
= self
.hostname
.split(".", 1)
1416 # Get and store curent IP address.
1417 address
= self
.get_address(proto
)
1421 "password" : self
.password
,
1426 # Send update to the server.
1427 response
= self
.send_request(self
.url
, data
=data
)
1429 # Get the full response message.
1430 output
= response
.read().decode()
1432 # Handle success messages.
1433 if self
.get_xml_tag_value(output
, "IP") == address
:
1436 # Handle error codes.
1437 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1439 if errorcode
== "304156":
1440 raise DDNSAuthenticationError
1441 elif errorcode
== "316153":
1442 raise DDNSRequestError(_("Domain not found"))
1443 elif errorcode
== "316154":
1444 raise DDNSRequestError(_("Domain not active"))
1445 elif errorcode
in ("380098", "380099"):
1446 raise DDNSInternalServerError
1448 # If we got here, some other update error happened.
1449 raise DDNSUpdateError
1452 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
1453 handle
= "no-ip.com"
1455 website
= "http://www.noip.com/"
1456 protocols
= ("ipv4",)
1458 # Information about the format of the HTTP request is to be found
1459 # here: http://www.noip.com/integrate/request and
1460 # here: http://www.noip.com/integrate/response
1462 url
= "https://dynupdate.noip.com/nic/update"
1464 def prepare_request_data(self
, proto
):
1465 assert proto
== "ipv4"
1468 "hostname" : self
.hostname
,
1469 "address" : self
.get_address(proto
),
1475 class DDNSProviderNowDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1476 handle
= "now-dns.com"
1478 website
= "http://now-dns.com/"
1479 protocols
= ("ipv6", "ipv4")
1481 # Information about the format of the request is to be found
1482 # but only can be accessed by register an account and login
1483 # https://now-dns.com/?m=api
1485 url
= "https://now-dns.com/update"
1488 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
1489 handle
= "nsupdate.info"
1490 name
= "nsupdate.info"
1491 website
= "http://nsupdate.info/"
1492 protocols
= ("ipv6", "ipv4",)
1494 # Information about the format of the HTTP request can be found
1495 # after login on the provider user interface and here:
1496 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
1498 url
= "https://nsupdate.info/nic/update"
1500 # TODO nsupdate.info can actually do this, but the functionality
1501 # has not been implemented here, yet.
1502 can_remove_records
= False
1504 supports_token_auth
= True
1506 # After a failed update, there will be no retries
1507 # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
1508 holdoff_failure_days
= None
1510 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
1511 # and for the password a so called secret.
1514 return self
.get("hostname")
1518 return self
.token
or self
.get("secret")
1520 def prepare_request_data(self
, proto
):
1522 "myip" : self
.get_address(proto
),
1528 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1529 handle
= "opendns.com"
1531 website
= "http://www.opendns.com"
1533 # Detailed information about the update request and possible
1534 # response codes can be obtained from here:
1535 # https://support.opendns.com/entries/23891440
1537 url
= "https://updates.opendns.com/nic/update"
1539 def prepare_request_data(self
, proto
):
1541 "hostname" : self
.hostname
,
1542 "myip" : self
.get_address(proto
),
1548 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1551 website
= "http://www.ovh.com/"
1552 protocols
= ("ipv4",)
1554 # OVH only provides very limited information about how to
1555 # update a DynDNS host. They only provide the update url
1556 # on the their german subpage.
1558 # http://hilfe.ovh.de/DomainDynHost
1560 url
= "https://www.ovh.com/nic/update"
1562 def prepare_request_data(self
, proto
):
1563 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1565 "system" : "dyndns",
1571 class DDNSProviderRegfish(DDNSProvider
):
1572 handle
= "regfish.com"
1573 name
= "Regfish GmbH"
1574 website
= "http://www.regfish.com/"
1576 # A full documentation to the providers api can be found here
1577 # but is only available in german.
1578 # https://www.regfish.de/domains/dyndns/dokumentation
1580 url
= "https://dyndns.regfish.de/"
1581 can_remove_records
= False
1582 supports_token_auth
= True
1586 "fqdn" : self
.hostname
,
1589 # Check if we update an IPv6 address.
1590 address6
= self
.get_address("ipv6")
1592 data
["ipv6"] = address6
1594 # Check if we update an IPv4 address.
1595 address4
= self
.get_address("ipv4")
1597 data
["ipv4"] = address4
1599 # Raise an error if none address is given.
1600 if "ipv6" not in data
and "ipv4" not in data
:
1601 raise DDNSConfigurationError
1603 # Check if a token has been set.
1605 data
["token"] = self
.token
1607 # Raise an error if no token and no useranem and password
1609 elif not self
.username
and not self
.password
:
1610 raise DDNSConfigurationError(_("No Auth details specified"))
1612 # HTTP Basic Auth is only allowed if no token is used.
1614 # Send update to the server.
1615 response
= self
.send_request(self
.url
, data
=data
)
1617 # Send update to the server.
1618 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1620 # Get the full response message.
1621 output
= response
.read().decode()
1623 # Handle success messages.
1624 if "100" in output
or "101" in output
:
1627 # Handle error codes.
1628 if "401" or "402" in output
:
1629 raise DDNSAuthenticationError
1630 elif "408" in output
:
1631 raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
1632 elif "409" in output
:
1633 raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
1634 elif "412" in output
:
1635 raise DDNSRequestError(_("No valid FQDN was given"))
1636 elif "414" in output
:
1637 raise DDNSInternalServerError
1639 # If we got here, some other update error happened.
1640 raise DDNSUpdateError
1643 class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1644 handle
= "schokokeks.org"
1646 website
= "http://www.schokokeks.org/"
1647 protocols
= ("ipv4",)
1649 # Information about the format of the request is to be found
1650 # https://wiki.schokokeks.org/DynDNS
1651 url
= "https://dyndns.schokokeks.org/nic/update"
1654 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1655 handle
= "selfhost.de"
1656 name
= "Selfhost.de"
1657 website
= "http://www.selfhost.de/"
1658 protocols
= ("ipv4",)
1660 url
= "https://carol.selfhost.de/nic/update"
1662 def prepare_request_data(self
, proto
):
1663 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1671 class DDNSProviderServercow(DDNSProvider
):
1672 handle
= "servercow.de"
1673 name
= "servercow.de"
1674 website
= "https://servercow.de/"
1675 protocols
= ("ipv4", "ipv6")
1677 url
= "https://www.servercow.de/dnsupdate/update.php"
1678 can_remove_records
= False
1679 supports_token_auth
= False
1681 def update_protocol(self
, proto
):
1683 "ipaddr" : self
.get_address(proto
),
1684 "hostname" : self
.hostname
,
1685 "username" : self
.username
,
1686 "pass" : self
.password
,
1689 # Send request to provider
1690 response
= self
.send_request(self
.url
, data
=data
)
1693 output
= response
.read().decode()
1695 # Server responds with OK if update was successful
1696 if output
.startswith("OK"):
1700 elif output
.startswith("FAILED - Authentication failed"):
1701 raise DDNSAuthenticationError
1703 # If we got here, some other update error happened
1704 raise DDNSUpdateError(output
)
1707 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1708 handle
= "spdns.org"
1710 website
= "https://www.spdyn.de/"
1712 # Detailed information about request and response codes are provided
1713 # by the vendor. They are using almost the same mechanism and status
1714 # codes as dyndns.org so we can inherit all those stuff.
1716 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1717 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1719 url
= "https://update.spdyn.de/nic/update"
1721 supports_token_auth
= True
1725 return self
.get("username") or self
.hostname
1729 return self
.get("password") or self
.token
1732 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1733 handle
= "strato.com"
1735 website
= "http:/www.strato.com/"
1736 protocols
= ("ipv4",)
1738 # Information about the request and response can be obtained here:
1739 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1741 url
= "https://dyndns.strato.com/nic/update"
1743 def prepare_request_data(self
, proto
):
1744 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1747 "backupmx" : "NOCHG"
1753 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1754 handle
= "twodns.de"
1756 website
= "http://www.twodns.de"
1757 protocols
= ("ipv4",)
1759 # Detailed information about the request can be found here
1760 # http://twodns.de/en/faqs
1761 # http://twodns.de/en/api
1763 url
= "https://update.twodns.de/update"
1765 def prepare_request_data(self
, proto
):
1766 assert proto
== "ipv4"
1769 "ip" : self
.get_address(proto
),
1770 "hostname" : self
.hostname
1776 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1777 handle
= "udmedia.de"
1778 name
= "Udmedia GmbH"
1779 website
= "http://www.udmedia.de"
1780 protocols
= ("ipv4",)
1782 # Information about the request can be found here
1783 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1785 url
= "https://www.udmedia.de/nic/update"
1788 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1789 handle
= "variomedia.de"
1791 website
= "http://www.variomedia.de/"
1792 protocols
= ("ipv6", "ipv4",)
1794 # Detailed information about the request can be found here
1795 # https://dyndns.variomedia.de/
1797 url
= "https://dyndns.variomedia.de/nic/update"
1799 def prepare_request_data(self
, proto
):
1801 "hostname" : self
.hostname
,
1802 "myip" : self
.get_address(proto
),
1808 class DDNSProviderXLhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1809 handle
= "xlhost.de"
1811 website
= "http://xlhost.de/"
1812 protocols
= ("ipv4",)
1814 # Information about the format of the HTTP request is to be found
1815 # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
1817 url
= "https://nsupdate.xlhost.de/"
1820 class DDNSProviderZoneedit(DDNSProvider
):
1821 handle
= "zoneedit.com"
1823 website
= "http://www.zoneedit.com"
1824 protocols
= ("ipv4",)
1826 supports_token_auth
= False
1828 # Detailed information about the request and the response codes can be
1830 # http://www.zoneedit.com/doc/api/other.html
1831 # http://www.zoneedit.com/faq.html
1833 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1835 def update_protocol(self
, proto
):
1837 "dnsto" : self
.get_address(proto
),
1838 "host" : self
.hostname
1841 # Send update to the server.
1842 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1844 # Get the full response message.
1845 output
= response
.read().decode()
1847 # Handle success messages.
1848 if output
.startswith("<SUCCESS"):
1851 # Handle error codes.
1852 if output
.startswith("invalid login"):
1853 raise DDNSAuthenticationError
1854 elif output
.startswith("<ERROR CODE=\"704\""):
1855 raise DDNSRequestError(_("No valid FQDN was given"))
1856 elif output
.startswith("<ERROR CODE=\"702\""):
1857 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1859 # If we got here, some other update error happened.
1860 raise DDNSUpdateError
1863 class DDNSProviderDNSmadeEasy(DDNSProvider
):
1864 handle
= "dnsmadeeasy.com"
1865 name
= "DNSmadeEasy.com"
1866 website
= "http://www.dnsmadeeasy.com/"
1867 protocols
= ("ipv4",)
1869 # DNS Made Easy Nameserver Provider also offering Dynamic DNS
1870 # Documentation can be found here:
1871 # http://www.dnsmadeeasy.com/dynamic-dns/
1873 url
= "https://cp.dnsmadeeasy.com/servlet/updateip?"
1874 can_remove_records
= False
1875 supports_token_auth
= False
1877 def update_protocol(self
, proto
):
1879 "ip" : self
.get_address(proto
),
1880 "id" : self
.hostname
,
1881 "username" : self
.username
,
1882 "password" : self
.password
,
1885 # Send update to the server.
1886 response
= self
.send_request(self
.url
, data
=data
)
1888 # Get the full response message.
1889 output
= response
.read().decode()
1891 # Handle success messages.
1892 if output
.startswith("success") or output
.startswith("error-record-ip-same"):
1895 # Handle error codes.
1896 if output
.startswith("error-auth-suspend"):
1897 raise DDNSRequestError(_("Account has been suspended"))
1899 elif output
.startswith("error-auth-voided"):
1900 raise DDNSRequestError(_("Account has been revoked"))
1902 elif output
.startswith("error-record-invalid"):
1903 raise DDNSRequestError(_("Specified host does not exist"))
1905 elif output
.startswith("error-auth"):
1906 raise DDNSAuthenticationError
1908 # If we got here, some other update error happened.
1909 raise DDNSUpdateError(_("Server response: %s") % output
)
1912 class DDNSProviderZZZZ(DDNSProvider
):
1915 website
= "https://zzzz.io"
1916 protocols
= ("ipv6", "ipv4",)
1918 # Detailed information about the update request can be found here:
1919 # https://zzzz.io/faq/
1921 # Details about the possible response codes have been provided in the bugtracker:
1922 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1924 url
= "https://zzzz.io/api/v1/update"
1925 can_remove_records
= False
1926 supports_token_auth
= True
1928 def update_protocol(self
, proto
):
1930 "ip" : self
.get_address(proto
),
1931 "token" : self
.token
,
1935 data
["type"] = "aaaa"
1937 # zzzz uses the host from the full hostname as part
1938 # of the update url.
1939 host
, domain
= self
.hostname
.split(".", 1)
1941 # Add host value to the update url.
1942 url
= "%s/%s" % (self
.url
, host
)
1944 # Send update to the server.
1946 response
= self
.send_request(url
, data
=data
)
1948 # Handle error codes.
1949 except DDNSNotFound
:
1950 raise DDNSRequestError(_("Invalid hostname specified"))
1952 # Handle success messages.
1953 if response
.code
== 200:
1956 # If we got here, some other update error happened.
1957 raise DDNSUpdateError