]>
git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
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
= "http://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
= "http://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 url
= "http://members.dhs.org/nic/hosts"
693 can_remove_records
= False
694 supports_token_auth
= False
696 def update_protocol(self
, proto
):
698 "domain" : self
.hostname
,
699 "ip" : self
.get_address(proto
),
701 "hostcmdstage" : "2",
705 # Send update to the server.
706 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
708 # Handle success messages.
709 if response
.code
== 200:
712 # If we got here, some other update error happened.
713 raise DDNSUpdateError
716 class DDNSProviderDNSpark(DDNSProvider
):
717 handle
= "dnspark.com"
719 website
= "http://dnspark.com/"
720 protocols
= ("ipv4",)
722 # Informations to the used api can be found here:
723 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
725 url
= "https://control.dnspark.com/api/dynamic/update.php"
726 can_remove_records
= False
727 supports_token_auth
= False
729 def update_protocol(self
, proto
):
731 "domain" : self
.hostname
,
732 "ip" : self
.get_address(proto
),
735 # Send update to the server.
736 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
738 # Get the full response message.
739 output
= response
.read().decode()
741 # Handle success messages.
742 if output
.startswith("ok") or output
.startswith("nochange"):
745 # Handle error codes.
746 if output
== "unauth":
747 raise DDNSAuthenticationError
748 elif output
== "abuse":
750 elif output
== "blocked":
751 raise DDNSBlockedError
752 elif output
== "nofqdn":
753 raise DDNSRequestError(_("No valid FQDN was given"))
754 elif output
== "nohost":
755 raise DDNSRequestError(_("Invalid hostname specified"))
756 elif output
== "notdyn":
757 raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
758 elif output
== "invalid":
759 raise DDNSRequestError(_("Invalid IP address has been sent"))
761 # If we got here, some other update error happened.
762 raise DDNSUpdateError
765 class DDNSProviderDtDNS(DDNSProvider
):
768 website
= "http://dtdns.com/"
769 protocols
= ("ipv4",)
771 # Information about the format of the HTTPS request is to be found
772 # http://www.dtdns.com/dtsite/updatespec
774 url
= "https://www.dtdns.com/api/autodns.cfm"
775 can_remove_records
= False
776 supports_token_auth
= False
778 def update_protocol(self
, proto
):
780 "ip" : self
.get_address(proto
),
781 "id" : self
.hostname
,
785 # Send update to the server.
786 response
= self
.send_request(self
.url
, data
=data
)
788 # Get the full response message.
789 output
= response
.read().decode()
791 # Remove all leading and trailing whitespace.
792 output
= output
.strip()
794 # Handle success messages.
795 if "now points to" in output
:
798 # Handle error codes.
799 if output
== "No hostname to update was supplied.":
800 raise DDNSRequestError(_("No hostname specified"))
802 elif output
== "The hostname you supplied is not valid.":
803 raise DDNSRequestError(_("Invalid hostname specified"))
805 elif output
== "The password you supplied is not valid.":
806 raise DDNSAuthenticationError
808 elif output
== "Administration has disabled this account.":
809 raise DDNSRequestError(_("Account has been disabled"))
811 elif output
== "Illegal character in IP.":
812 raise DDNSRequestError(_("Invalid IP address has been sent"))
814 elif output
== "Too many failed requests.":
815 raise DDNSRequestError(_("Too many failed requests"))
817 # If we got here, some other update error happened.
818 raise DDNSUpdateError
821 class DDNSProviderDuckDNS(DDNSProvider
):
822 handle
= "duckdns.org"
824 website
= "http://www.duckdns.org/"
825 protocols
= ("ipv6", "ipv4",)
827 # Information about the format of the request is to be found
828 # https://www.duckdns.org/spec.jsp
830 url
= "https://www.duckdns.org/update"
831 can_remove_records
= False
832 supports_token_auth
= True
835 # Raise an error if no auth details are given.
837 raise DDNSConfigurationError
840 "domains" : self
.hostname
,
841 "token" : self
.token
,
844 # Check if we update an IPv4 address.
845 address4
= self
.get_address("ipv4")
847 data
["ip"] = address4
849 # Check if we update an IPv6 address.
850 address6
= self
.get_address("ipv6")
852 data
["ipv6"] = address6
854 # Raise an error if no address is given.
855 if "ip" not in data
and "ipv6" not in data
:
856 raise DDNSConfigurationError
858 # Send update to the server.
859 response
= self
.send_request(self
.url
, data
=data
)
861 # Get the full response message.
862 output
= response
.read().decode()
864 # Remove all leading and trailing whitespace.
865 output
= output
.strip()
867 # Handle success messages.
871 # The provider does not give detailed information
872 # if the update fails. Only a "KO" will be sent back.
874 raise DDNSUpdateError
876 # If we got here, some other update error happened.
877 raise DDNSUpdateError
880 class DDNSProviderDyFi(DDNSProtocolDynDNS2
, DDNSProvider
):
883 website
= "https://www.dy.fi/"
884 protocols
= ("ipv4",)
886 # Information about the format of the request is to be found
887 # https://www.dy.fi/page/clients?lang=en
888 # https://www.dy.fi/page/specification?lang=en
890 url
= "https://www.dy.fi/nic/update"
892 # Please only send automatic updates when your IP address changes,
893 # or once per 5 to 6 days to refresh the address mapping (they will
894 # expire if not refreshed within 7 days).
898 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
899 handle
= "dyndns.org"
901 website
= "http://dyn.com/dns/"
902 protocols
= ("ipv4",)
904 # Information about the format of the request is to be found
905 # http://http://dyn.com/support/developers/api/perform-update/
906 # http://dyn.com/support/developers/api/return-codes/
908 url
= "https://members.dyndns.org/nic/update"
911 class DDNSProviderDomainOffensive(DDNSProtocolDynDNS2
, DDNSProvider
):
913 name
= "Domain-Offensive"
914 website
= "https://www.do.de/"
915 protocols
= ("ipv6", "ipv4")
917 # Detailed information about the request and response codes
918 # are available on the providers webpage.
919 # https://www.do.de/wiki/FlexDNS_-_Entwickler
921 url
= "https://ddns.do.de/"
923 class DDNSProviderDynUp(DDNSProvider
):
926 website
= "http://dynup.de/"
927 protocols
= ("ipv4",)
929 # Information about the format of the HTTPS request is to be found
930 # https://dyndnsfree.de/user/hilfe.php
932 url
= "https://dynup.de/dyn.php"
933 can_remove_records
= False
934 supports_token_auth
= False
936 def update_protocol(self
, proto
):
938 "username" : self
.username
,
939 "password" : self
.password
,
940 "hostname" : self
.hostname
,
944 # Send update to the server.
945 response
= self
.send_request(self
.url
, data
=data
)
947 # Get the full response message.
948 output
= response
.read().decode()
950 # Remove all leading and trailing whitespace.
951 output
= output
.strip()
953 # Handle success messages.
954 if output
.startswith("I:OK"):
957 # If we got here, some other update error happened.
958 raise DDNSUpdateError
961 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
964 website
= "http://dynu.com/"
965 protocols
= ("ipv6", "ipv4",)
967 # Detailed information about the request and response codes
968 # are available on the providers webpage.
969 # http://dynu.com/Default.aspx?page=dnsapi
971 url
= "https://api.dynu.com/nic/update"
973 # DynU sends the IPv6 and IPv4 address in one request
976 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
978 # This one supports IPv6
979 myipv6
= self
.get_address("ipv6")
981 # Add update information if we have an IPv6 address.
983 data
["myipv6"] = myipv6
985 self
.send_request(data
)
988 class DDNSProviderEasyDNS(DDNSProvider
):
989 handle
= "easydns.com"
991 website
= "http://www.easydns.com/"
992 protocols
= ("ipv4",)
994 # Detailed information about the request and response codes
995 # (API 1.3) are available on the providers webpage.
996 # https://fusion.easydns.com/index.php?/Knowledgebase/Article/View/102/7/dynamic-dns
998 url
= "http://api.cp.easydns.com/dyn/tomato.php"
1000 supports_token_auth
= False
1002 def update_protocol(self
, proto
):
1004 "myip" : self
.get_address(proto
, "-"),
1005 "hostname" : self
.hostname
,
1008 # Send update to the server.
1009 response
= self
.send_request(self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
1011 # Get the full response message.
1012 output
= response
.read().decode()
1014 # Remove all leading and trailing whitespace.
1015 output
= output
.strip()
1017 # Handle success messages.
1018 if output
.startswith("NOERROR"):
1021 # Handle error codes.
1022 if output
.startswith("NOACCESS"):
1023 raise DDNSAuthenticationError
1025 elif output
.startswith("NOSERVICE"):
1026 raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain"))
1028 elif output
.startswith("ILLEGAL INPUT"):
1029 raise DDNSRequestError(_("Invalid data has been sent"))
1031 elif output
.startswith("TOOSOON"):
1032 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1034 # If we got here, some other update error happened.
1035 raise DDNSUpdateError
1038 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
1039 handle
= "domopoli.de"
1040 name
= "domopoli.de"
1041 website
= "http://domopoli.de/"
1042 protocols
= ("ipv4",)
1044 # https://www.domopoli.de/?page=howto#DynDns_start
1046 url
= "http://dyndns.domopoli.de/nic/update"
1049 class DDNSProviderDynsNet(DDNSProvider
):
1052 website
= "http://www.dyns.net/"
1053 protocols
= ("ipv4",)
1054 can_remove_records
= False
1055 supports_token_auth
= False
1057 # There is very detailed informatio about how to send the update request and
1058 # the possible response codes. (Currently we are using the v1.1 proto)
1059 # http://www.dyns.net/documentation/technical/protocol/
1061 url
= "http://www.dyns.net/postscript011.php"
1063 def update_protocol(self
, proto
):
1065 "ip" : self
.get_address(proto
),
1066 "host" : self
.hostname
,
1067 "username" : self
.username
,
1068 "password" : self
.password
,
1071 # Send update to the server.
1072 response
= self
.send_request(self
.url
, data
=data
)
1074 # Get the full response message.
1075 output
= response
.read().decode()
1077 # Handle success messages.
1078 if output
.startswith("200"):
1081 # Handle error codes.
1082 if output
.startswith("400"):
1083 raise DDNSRequestError(_("Malformed request has been sent"))
1084 elif output
.startswith("401"):
1085 raise DDNSAuthenticationError
1086 elif output
.startswith("402"):
1087 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1088 elif output
.startswith("403"):
1089 raise DDNSInternalServerError
1091 # If we got here, some other update error happened.
1092 raise DDNSUpdateError(_("Server response: %s") % output
)
1095 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
1098 website
= "http://www.enom.com/"
1099 protocols
= ("ipv4",)
1101 # There are very detailed information about how to send an update request and
1102 # the respone codes.
1103 # http://www.enom.com/APICommandCatalog/
1105 url
= "https://dynamic.name-services.com/interface.asp"
1106 can_remove_records
= False
1107 supports_token_auth
= False
1109 def update_protocol(self
, proto
):
1111 "command" : "setdnshost",
1112 "responsetype" : "xml",
1113 "address" : self
.get_address(proto
),
1114 "domainpassword" : self
.password
,
1115 "zone" : self
.hostname
1118 # Send update to the server.
1119 response
= self
.send_request(self
.url
, data
=data
)
1121 # Get the full response message.
1122 output
= response
.read().decode()
1124 # Handle success messages.
1125 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
1128 # Handle error codes.
1129 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1131 if errorcode
== "304155":
1132 raise DDNSAuthenticationError
1133 elif errorcode
== "304153":
1134 raise DDNSRequestError(_("Domain not found"))
1136 # If we got here, some other update error happened.
1137 raise DDNSUpdateError
1140 class DDNSProviderEntryDNS(DDNSProvider
):
1141 handle
= "entrydns.net"
1143 website
= "http://entrydns.net/"
1144 protocols
= ("ipv4",)
1146 # Some very tiny details about their so called "Simple API" can be found
1147 # here: https://entrydns.net/help
1148 url
= "https://entrydns.net/records/modify"
1149 can_remove_records
= False
1150 supports_token_auth
= True
1152 def update_protocol(self
, proto
):
1154 "ip" : self
.get_address(proto
),
1157 # Add auth token to the update url.
1158 url
= "%s/%s" % (self
.url
, self
.token
)
1160 # Send update to the server.
1162 response
= self
.send_request(url
, data
=data
)
1164 # Handle error codes
1165 except urllib
.error
.HTTPError
as e
:
1167 raise DDNSAuthenticationError
1170 raise DDNSRequestError(_("An invalid IP address was submitted"))
1174 # Handle success messages.
1175 if response
.code
== 200:
1178 # If we got here, some other update error happened.
1179 raise DDNSUpdateError
1182 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
1183 handle
= "freedns.afraid.org"
1184 name
= "freedns.afraid.org"
1185 website
= "http://freedns.afraid.org/"
1187 # No information about the request or response could be found on the vendor
1188 # page. All used values have been collected by testing.
1189 url
= "https://freedns.afraid.org/dynamic/update.php"
1190 can_remove_records
= False
1191 supports_token_auth
= True
1193 def update_protocol(self
, proto
):
1195 "address" : self
.get_address(proto
),
1198 # Add auth token to the update url.
1199 url
= "%s?%s" % (self
.url
, self
.token
)
1201 # Send update to the server.
1202 response
= self
.send_request(url
, data
=data
)
1204 # Get the full response message.
1205 output
= response
.read().decode()
1207 # Handle success messages.
1208 if output
.startswith("Updated") or "has not changed" in output
:
1211 # Handle error codes.
1212 if output
== "ERROR: Unable to locate this record":
1213 raise DDNSAuthenticationError
1214 elif "is an invalid IP address" in output
:
1215 raise DDNSRequestError(_("Invalid IP address has been sent"))
1217 # If we got here, some other update error happened.
1218 raise DDNSUpdateError
1221 class DDNSProviderHENet(DDNSProtocolDynDNS2
, DDNSProvider
):
1224 website
= "https://he.net"
1225 protocols
= ("ipv6", "ipv4",)
1227 # Detailed information about the update api can be found here.
1228 # http://dns.he.net/docs.html
1230 url
= "https://dyn.dns.he.net/nic/update"
1233 return self
.get("hostname")
1237 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1240 website
= "https://www.inwx.com"
1241 protocols
= ("ipv6", "ipv4")
1243 # Information about the format of the HTTP request is to be found
1244 # here: https://www.inwx.com/en/nameserver2/dyndns (requires login)
1245 # Notice: The URL is the same for: inwx.com|de|at|ch|es
1247 url
= "https://dyndns.inwx.com/nic/update"
1250 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1251 handle
= "itsdns.de"
1253 website
= "http://www.itsdns.de/"
1254 protocols
= ("ipv6", "ipv4")
1256 # Information about the format of the HTTP request is to be found
1257 # here: https://www.itsdns.de/dynupdatehelp.htm
1259 url
= "https://www.itsdns.de/update.php"
1262 class DDNSProviderJoker(DDNSProtocolDynDNS2
, DDNSProvider
):
1263 handle
= "joker.com"
1264 name
= "Joker.com Dynamic DNS"
1265 website
= "https://joker.com/"
1266 protocols
= ("ipv4",)
1268 # Information about the request can be found here:
1269 # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
1270 # Using DynDNS V2 protocol over HTTPS here
1272 url
= "https://svc.joker.com/nic/update"
1275 class DDNSProviderKEYSYSTEMS(DDNSProvider
):
1276 handle
= "key-systems.net"
1277 name
= "dynamicdns.key-systems.net"
1278 website
= "https://domaindiscount24.com/"
1279 protocols
= ("ipv4",)
1281 # There are only information provided by the domaindiscount24 how to
1282 # perform an update with HTTP APIs
1283 # https://www.domaindiscount24.com/faq/dynamic-dns
1284 # examples: https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=auto
1285 # https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=213.x.x.x&mx=213.x.x.x
1287 url
= "https://dynamicdns.key-systems.net/update.php"
1288 can_remove_records
= False
1289 supports_token_auth
= False
1291 def update_protocol(self
, proto
):
1292 address
= self
.get_address(proto
)
1294 "hostname" : self
.hostname
,
1295 "password" : self
.password
,
1299 # Send update to the server.
1300 response
= self
.send_request(self
.url
, data
=data
)
1302 # Get the full response message.
1303 output
= response
.read().decode()
1305 # Handle success messages.
1306 if "code = 200" in output
:
1309 # Handle error messages.
1310 if "abuse prevention triggered" in output
:
1311 raise DDNSAbuseError
1312 elif "invalid password" in output
:
1313 raise DDNSAuthenticationError
1314 elif "Authorization failed" in output
:
1315 raise DDNSRequestError(_("Invalid hostname specified"))
1317 # If we got here, some other update error happened.
1318 raise DDNSUpdateError
1321 class DDNSProviderGoogle(DDNSProtocolDynDNS2
, DDNSProvider
):
1322 handle
= "domains.google.com"
1323 name
= "Google Domains"
1324 website
= "https://domains.google.com/"
1325 protocols
= ("ipv4",)
1327 # Information about the format of the HTTP request is to be found
1328 # here: https://support.google.com/domains/answer/6147083?hl=en
1330 url
= "https://domains.google.com/nic/update"
1333 class DDNSProviderLightningWireLabs(DDNSProvider
):
1334 handle
= "dns.lightningwirelabs.com"
1335 name
= "Lightning Wire Labs DNS Service"
1336 website
= "https://dns.lightningwirelabs.com/"
1338 # Information about the format of the HTTPS request is to be found
1339 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
1341 supports_token_auth
= True
1343 url
= "https://dns.lightningwirelabs.com/update"
1346 # Raise an error if no auth details are given.
1348 raise DDNSConfigurationError
1351 "hostname" : self
.hostname
,
1352 "token" : self
.token
,
1353 "address6" : self
.get_address("ipv6", "-"),
1354 "address4" : self
.get_address("ipv4", "-"),
1357 # Send update to the server.
1358 response
= self
.send_request(self
.url
, data
=data
)
1360 # Handle success messages.
1361 if response
.code
== 200:
1364 # If we got here, some other update error happened.
1365 raise DDNSUpdateError
1368 class DDNSProviderLoopia(DDNSProtocolDynDNS2
, DDNSProvider
):
1369 handle
= "loopia.se"
1371 website
= "https://www.loopia.com"
1372 protocols
= ("ipv4",)
1374 # Information about the format of the HTTP request is to be found
1375 # here: https://support.loopia.com/wiki/About_the_DynDNS_support
1377 url
= "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
1380 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
1381 handle
= "myonlineportal.net"
1382 name
= "myonlineportal.net"
1383 website
= "https:/myonlineportal.net/"
1385 # Information about the request and response can be obtained here:
1386 # https://myonlineportal.net/howto_dyndns
1388 url
= "https://myonlineportal.net/updateddns"
1390 def prepare_request_data(self
, proto
):
1392 "hostname" : self
.hostname
,
1393 "ip" : self
.get_address(proto
),
1399 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
1400 handle
= "namecheap.com"
1402 website
= "http://namecheap.com"
1403 protocols
= ("ipv4",)
1405 # Information about the format of the HTTP request is to be found
1406 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
1407 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
1409 url
= "https://dynamicdns.park-your-domain.com/update"
1410 can_remove_records
= False
1411 supports_token_auth
= False
1413 def update_protocol(self
, proto
):
1414 # Namecheap requires the hostname splitted into a host and domain part.
1415 host
, domain
= self
.hostname
.split(".", 1)
1417 # Get and store curent IP address.
1418 address
= self
.get_address(proto
)
1422 "password" : self
.password
,
1427 # Send update to the server.
1428 response
= self
.send_request(self
.url
, data
=data
)
1430 # Get the full response message.
1431 output
= response
.read().decode()
1433 # Handle success messages.
1434 if self
.get_xml_tag_value(output
, "IP") == address
:
1437 # Handle error codes.
1438 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1440 if errorcode
== "304156":
1441 raise DDNSAuthenticationError
1442 elif errorcode
== "316153":
1443 raise DDNSRequestError(_("Domain not found"))
1444 elif errorcode
== "316154":
1445 raise DDNSRequestError(_("Domain not active"))
1446 elif errorcode
in ("380098", "380099"):
1447 raise DDNSInternalServerError
1449 # If we got here, some other update error happened.
1450 raise DDNSUpdateError
1453 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
1454 handle
= "no-ip.com"
1456 website
= "http://www.noip.com/"
1457 protocols
= ("ipv4",)
1459 # Information about the format of the HTTP request is to be found
1460 # here: http://www.noip.com/integrate/request and
1461 # here: http://www.noip.com/integrate/response
1463 url
= "http://dynupdate.noip.com/nic/update"
1465 def prepare_request_data(self
, proto
):
1466 assert proto
== "ipv4"
1469 "hostname" : self
.hostname
,
1470 "address" : self
.get_address(proto
),
1476 class DDNSProviderNowDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1477 handle
= "now-dns.com"
1479 website
= "http://now-dns.com/"
1480 protocols
= ("ipv6", "ipv4")
1482 # Information about the format of the request is to be found
1483 # but only can be accessed by register an account and login
1484 # https://now-dns.com/?m=api
1486 url
= "https://now-dns.com/update"
1489 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
1490 handle
= "nsupdate.info"
1491 name
= "nsupdate.info"
1492 website
= "http://nsupdate.info/"
1493 protocols
= ("ipv6", "ipv4",)
1495 # Information about the format of the HTTP request can be found
1496 # after login on the provider user interface and here:
1497 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
1499 url
= "https://nsupdate.info/nic/update"
1501 # TODO nsupdate.info can actually do this, but the functionality
1502 # has not been implemented here, yet.
1503 can_remove_records
= False
1505 supports_token_auth
= True
1507 # After a failed update, there will be no retries
1508 # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
1509 holdoff_failure_days
= None
1511 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
1512 # and for the password a so called secret.
1515 return self
.get("hostname")
1519 return self
.token
or self
.get("secret")
1521 def prepare_request_data(self
, proto
):
1523 "myip" : self
.get_address(proto
),
1529 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1530 handle
= "opendns.com"
1532 website
= "http://www.opendns.com"
1534 # Detailed information about the update request and possible
1535 # response codes can be obtained from here:
1536 # https://support.opendns.com/entries/23891440
1538 url
= "https://updates.opendns.com/nic/update"
1540 def prepare_request_data(self
, proto
):
1542 "hostname" : self
.hostname
,
1543 "myip" : self
.get_address(proto
),
1549 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1552 website
= "http://www.ovh.com/"
1553 protocols
= ("ipv4",)
1555 # OVH only provides very limited information about how to
1556 # update a DynDNS host. They only provide the update url
1557 # on the their german subpage.
1559 # http://hilfe.ovh.de/DomainDynHost
1561 url
= "https://www.ovh.com/nic/update"
1563 def prepare_request_data(self
, proto
):
1564 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1566 "system" : "dyndns",
1572 class DDNSProviderRegfish(DDNSProvider
):
1573 handle
= "regfish.com"
1574 name
= "Regfish GmbH"
1575 website
= "http://www.regfish.com/"
1577 # A full documentation to the providers api can be found here
1578 # but is only available in german.
1579 # https://www.regfish.de/domains/dyndns/dokumentation
1581 url
= "https://dyndns.regfish.de/"
1582 can_remove_records
= False
1583 supports_token_auth
= True
1587 "fqdn" : self
.hostname
,
1590 # Check if we update an IPv6 address.
1591 address6
= self
.get_address("ipv6")
1593 data
["ipv6"] = address6
1595 # Check if we update an IPv4 address.
1596 address4
= self
.get_address("ipv4")
1598 data
["ipv4"] = address4
1600 # Raise an error if none address is given.
1601 if "ipv6" not in data
and "ipv4" not in data
:
1602 raise DDNSConfigurationError
1604 # Check if a token has been set.
1606 data
["token"] = self
.token
1608 # Raise an error if no token and no useranem and password
1610 elif not self
.username
and not self
.password
:
1611 raise DDNSConfigurationError(_("No Auth details specified"))
1613 # HTTP Basic Auth is only allowed if no token is used.
1615 # Send update to the server.
1616 response
= self
.send_request(self
.url
, data
=data
)
1618 # Send update to the server.
1619 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1621 # Get the full response message.
1622 output
= response
.read().decode()
1624 # Handle success messages.
1625 if "100" in output
or "101" in output
:
1628 # Handle error codes.
1629 if "401" or "402" in output
:
1630 raise DDNSAuthenticationError
1631 elif "408" in output
:
1632 raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
1633 elif "409" in output
:
1634 raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
1635 elif "412" in output
:
1636 raise DDNSRequestError(_("No valid FQDN was given"))
1637 elif "414" in output
:
1638 raise DDNSInternalServerError
1640 # If we got here, some other update error happened.
1641 raise DDNSUpdateError
1644 class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1645 handle
= "schokokeks.org"
1647 website
= "http://www.schokokeks.org/"
1648 protocols
= ("ipv4",)
1650 # Information about the format of the request is to be found
1651 # https://wiki.schokokeks.org/DynDNS
1652 url
= "https://dyndns.schokokeks.org/nic/update"
1655 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1656 handle
= "selfhost.de"
1657 name
= "Selfhost.de"
1658 website
= "http://www.selfhost.de/"
1659 protocols
= ("ipv4",)
1661 url
= "https://carol.selfhost.de/nic/update"
1663 def prepare_request_data(self
, proto
):
1664 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1672 class DDNSProviderServercow(DDNSProvider
):
1673 handle
= "servercow.de"
1674 name
= "servercow.de"
1675 website
= "https://servercow.de/"
1676 protocols
= ("ipv4", "ipv6")
1678 url
= "https://www.servercow.de/dnsupdate/update.php"
1679 can_remove_records
= False
1680 supports_token_auth
= False
1682 def update_protocol(self
, proto
):
1684 "ipaddr" : self
.get_address(proto
),
1685 "hostname" : self
.hostname
,
1686 "username" : self
.username
,
1687 "pass" : self
.password
,
1690 # Send request to provider
1691 response
= self
.send_request(self
.url
, data
=data
)
1694 output
= response
.read().decode()
1696 # Server responds with OK if update was successful
1697 if output
.startswith("OK"):
1701 elif output
.startswith("FAILED - Authentication failed"):
1702 raise DDNSAuthenticationError
1704 # If we got here, some other update error happened
1705 raise DDNSUpdateError(output
)
1708 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1709 handle
= "spdns.org"
1711 website
= "https://www.spdyn.de/"
1713 # Detailed information about request and response codes are provided
1714 # by the vendor. They are using almost the same mechanism and status
1715 # codes as dyndns.org so we can inherit all those stuff.
1717 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1718 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1720 url
= "https://update.spdyn.de/nic/update"
1722 supports_token_auth
= True
1726 return self
.get("username") or self
.hostname
1730 return self
.get("password") or self
.token
1733 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1734 handle
= "strato.com"
1736 website
= "http:/www.strato.com/"
1737 protocols
= ("ipv4",)
1739 # Information about the request and response can be obtained here:
1740 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1742 url
= "https://dyndns.strato.com/nic/update"
1744 def prepare_request_data(self
, proto
):
1745 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1748 "backupmx" : "NOCHG"
1754 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1755 handle
= "twodns.de"
1757 website
= "http://www.twodns.de"
1758 protocols
= ("ipv4",)
1760 # Detailed information about the request can be found here
1761 # http://twodns.de/en/faqs
1762 # http://twodns.de/en/api
1764 url
= "https://update.twodns.de/update"
1766 def prepare_request_data(self
, proto
):
1767 assert proto
== "ipv4"
1770 "ip" : self
.get_address(proto
),
1771 "hostname" : self
.hostname
1777 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1778 handle
= "udmedia.de"
1779 name
= "Udmedia GmbH"
1780 website
= "http://www.udmedia.de"
1781 protocols
= ("ipv4",)
1783 # Information about the request can be found here
1784 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1786 url
= "https://www.udmedia.de/nic/update"
1789 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1790 handle
= "variomedia.de"
1792 website
= "http://www.variomedia.de/"
1793 protocols
= ("ipv6", "ipv4",)
1795 # Detailed information about the request can be found here
1796 # https://dyndns.variomedia.de/
1798 url
= "https://dyndns.variomedia.de/nic/update"
1800 def prepare_request_data(self
, proto
):
1802 "hostname" : self
.hostname
,
1803 "myip" : self
.get_address(proto
),
1809 class DDNSProviderXLhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1810 handle
= "xlhost.de"
1812 website
= "http://xlhost.de/"
1813 protocols
= ("ipv4",)
1815 # Information about the format of the HTTP request is to be found
1816 # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
1818 url
= "https://nsupdate.xlhost.de/"
1821 class DDNSProviderZoneedit(DDNSProvider
):
1822 handle
= "zoneedit.com"
1824 website
= "http://www.zoneedit.com"
1825 protocols
= ("ipv4",)
1827 supports_token_auth
= False
1829 # Detailed information about the request and the response codes can be
1831 # http://www.zoneedit.com/doc/api/other.html
1832 # http://www.zoneedit.com/faq.html
1834 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1836 def update_protocol(self
, proto
):
1838 "dnsto" : self
.get_address(proto
),
1839 "host" : self
.hostname
1842 # Send update to the server.
1843 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1845 # Get the full response message.
1846 output
= response
.read().decode()
1848 # Handle success messages.
1849 if output
.startswith("<SUCCESS"):
1852 # Handle error codes.
1853 if output
.startswith("invalid login"):
1854 raise DDNSAuthenticationError
1855 elif output
.startswith("<ERROR CODE=\"704\""):
1856 raise DDNSRequestError(_("No valid FQDN was given"))
1857 elif output
.startswith("<ERROR CODE=\"702\""):
1858 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1860 # If we got here, some other update error happened.
1861 raise DDNSUpdateError
1864 class DDNSProviderDNSmadeEasy(DDNSProvider
):
1865 handle
= "dnsmadeeasy.com"
1866 name
= "DNSmadeEasy.com"
1867 website
= "http://www.dnsmadeeasy.com/"
1868 protocols
= ("ipv4",)
1870 # DNS Made Easy Nameserver Provider also offering Dynamic DNS
1871 # Documentation can be found here:
1872 # http://www.dnsmadeeasy.com/dynamic-dns/
1874 url
= "https://cp.dnsmadeeasy.com/servlet/updateip?"
1875 can_remove_records
= False
1876 supports_token_auth
= False
1878 def update_protocol(self
, proto
):
1880 "ip" : self
.get_address(proto
),
1881 "id" : self
.hostname
,
1882 "username" : self
.username
,
1883 "password" : self
.password
,
1886 # Send update to the server.
1887 response
= self
.send_request(self
.url
, data
=data
)
1889 # Get the full response message.
1890 output
= response
.read().decode()
1892 # Handle success messages.
1893 if output
.startswith("success") or output
.startswith("error-record-ip-same"):
1896 # Handle error codes.
1897 if output
.startswith("error-auth-suspend"):
1898 raise DDNSRequestError(_("Account has been suspended"))
1900 elif output
.startswith("error-auth-voided"):
1901 raise DDNSRequestError(_("Account has been revoked"))
1903 elif output
.startswith("error-record-invalid"):
1904 raise DDNSRequestError(_("Specified host does not exist"))
1906 elif output
.startswith("error-auth"):
1907 raise DDNSAuthenticationError
1909 # If we got here, some other update error happened.
1910 raise DDNSUpdateError(_("Server response: %s") % output
)
1913 class DDNSProviderZZZZ(DDNSProvider
):
1916 website
= "https://zzzz.io"
1917 protocols
= ("ipv6", "ipv4",)
1919 # Detailed information about the update request can be found here:
1920 # https://zzzz.io/faq/
1922 # Details about the possible response codes have been provided in the bugtracker:
1923 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1925 url
= "https://zzzz.io/api/v1/update"
1926 can_remove_records
= False
1927 supports_token_auth
= True
1929 def update_protocol(self
, proto
):
1931 "ip" : self
.get_address(proto
),
1932 "token" : self
.token
,
1936 data
["type"] = "aaaa"
1938 # zzzz uses the host from the full hostname as part
1939 # of the update url.
1940 host
, domain
= self
.hostname
.split(".", 1)
1942 # Add host value to the update url.
1943 url
= "%s/%s" % (self
.url
, host
)
1945 # Send update to the server.
1947 response
= self
.send_request(url
, data
=data
)
1949 # Handle error codes.
1950 except DDNSNotFound
:
1951 raise DDNSRequestError(_("Invalid hostname specified"))
1953 # Handle success messages.
1954 if response
.code
== 200:
1957 # If we got here, some other update error happened.
1958 raise DDNSUpdateError