]>
git.ipfire.org Git - oddments/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
= "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 url
= "http://dyndns.domopoli.de/nic/update"
1050 class DDNSProviderDynsNet(DDNSProvider
):
1053 website
= "http://www.dyns.net/"
1054 protocols
= ("ipv4",)
1055 can_remove_records
= False
1056 supports_token_auth
= False
1058 # There is very detailed informatio about how to send the update request and
1059 # the possible response codes. (Currently we are using the v1.1 proto)
1060 # http://www.dyns.net/documentation/technical/protocol/
1062 url
= "http://www.dyns.net/postscript011.php"
1064 def update_protocol(self
, proto
):
1066 "ip" : self
.get_address(proto
),
1067 "host" : self
.hostname
,
1068 "username" : self
.username
,
1069 "password" : self
.password
,
1072 # Send update to the server.
1073 response
= self
.send_request(self
.url
, data
=data
)
1075 # Get the full response message.
1076 output
= response
.read().decode()
1078 # Handle success messages.
1079 if output
.startswith("200"):
1082 # Handle error codes.
1083 if output
.startswith("400"):
1084 raise DDNSRequestError(_("Malformed request has been sent"))
1085 elif output
.startswith("401"):
1086 raise DDNSAuthenticationError
1087 elif output
.startswith("402"):
1088 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1089 elif output
.startswith("403"):
1090 raise DDNSInternalServerError
1092 # If we got here, some other update error happened.
1093 raise DDNSUpdateError(_("Server response: %s") % output
)
1096 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
1099 website
= "http://www.enom.com/"
1100 protocols
= ("ipv4",)
1102 # There are very detailed information about how to send an update request and
1103 # the respone codes.
1104 # http://www.enom.com/APICommandCatalog/
1106 url
= "https://dynamic.name-services.com/interface.asp"
1107 can_remove_records
= False
1108 supports_token_auth
= False
1110 def update_protocol(self
, proto
):
1112 "command" : "setdnshost",
1113 "responsetype" : "xml",
1114 "address" : self
.get_address(proto
),
1115 "domainpassword" : self
.password
,
1116 "zone" : self
.hostname
1119 # Send update to the server.
1120 response
= self
.send_request(self
.url
, data
=data
)
1122 # Get the full response message.
1123 output
= response
.read().decode()
1125 # Handle success messages.
1126 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
1129 # Handle error codes.
1130 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1132 if errorcode
== "304155":
1133 raise DDNSAuthenticationError
1134 elif errorcode
== "304153":
1135 raise DDNSRequestError(_("Domain not found"))
1137 # If we got here, some other update error happened.
1138 raise DDNSUpdateError
1141 class DDNSProviderEntryDNS(DDNSProvider
):
1142 handle
= "entrydns.net"
1144 website
= "http://entrydns.net/"
1145 protocols
= ("ipv4",)
1147 # Some very tiny details about their so called "Simple API" can be found
1148 # here: https://entrydns.net/help
1149 url
= "https://entrydns.net/records/modify"
1150 can_remove_records
= False
1151 supports_token_auth
= True
1153 def update_protocol(self
, proto
):
1155 "ip" : self
.get_address(proto
),
1158 # Add auth token to the update url.
1159 url
= "%s/%s" % (self
.url
, self
.token
)
1161 # Send update to the server.
1163 response
= self
.send_request(url
, data
=data
)
1165 # Handle error codes
1166 except urllib
.error
.HTTPError
as e
:
1168 raise DDNSAuthenticationError
1171 raise DDNSRequestError(_("An invalid IP address was submitted"))
1175 # Handle success messages.
1176 if response
.code
== 200:
1179 # If we got here, some other update error happened.
1180 raise DDNSUpdateError
1183 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
1184 handle
= "freedns.afraid.org"
1185 name
= "freedns.afraid.org"
1186 website
= "http://freedns.afraid.org/"
1188 # No information about the request or response could be found on the vendor
1189 # page. All used values have been collected by testing.
1190 url
= "https://freedns.afraid.org/dynamic/update.php"
1191 can_remove_records
= False
1192 supports_token_auth
= True
1194 def update_protocol(self
, proto
):
1196 "address" : self
.get_address(proto
),
1199 # Add auth token to the update url.
1200 url
= "%s?%s" % (self
.url
, self
.token
)
1202 # Send update to the server.
1203 response
= self
.send_request(url
, data
=data
)
1205 # Get the full response message.
1206 output
= response
.read().decode()
1208 # Handle success messages.
1209 if output
.startswith("Updated") or "has not changed" in output
:
1212 # Handle error codes.
1213 if output
== "ERROR: Unable to locate this record":
1214 raise DDNSAuthenticationError
1215 elif "is an invalid IP address" in output
:
1216 raise DDNSRequestError(_("Invalid IP address has been sent"))
1218 # If we got here, some other update error happened.
1219 raise DDNSUpdateError
1222 class DDNSProviderHENet(DDNSProtocolDynDNS2
, DDNSProvider
):
1225 website
= "https://he.net"
1226 protocols
= ("ipv6", "ipv4",)
1228 # Detailed information about the update api can be found here.
1229 # http://dns.he.net/docs.html
1231 url
= "https://dyn.dns.he.net/nic/update"
1234 return self
.get("hostname")
1238 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1241 website
= "https://www.inwx.com"
1242 protocols
= ("ipv6", "ipv4")
1244 # Information about the format of the HTTP request is to be found
1245 # here: https://www.inwx.com/en/nameserver2/dyndns (requires login)
1246 # Notice: The URL is the same for: inwx.com|de|at|ch|es
1248 url
= "https://dyndns.inwx.com/nic/update"
1251 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1252 handle
= "itsdns.de"
1254 website
= "http://www.itsdns.de/"
1255 protocols
= ("ipv6", "ipv4")
1257 # Information about the format of the HTTP request is to be found
1258 # here: https://www.itsdns.de/dynupdatehelp.htm
1260 url
= "https://www.itsdns.de/update.php"
1263 class DDNSProviderJoker(DDNSProtocolDynDNS2
, DDNSProvider
):
1264 handle
= "joker.com"
1265 name
= "Joker.com Dynamic DNS"
1266 website
= "https://joker.com/"
1267 protocols
= ("ipv4",)
1269 # Information about the request can be found here:
1270 # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
1271 # Using DynDNS V2 protocol over HTTPS here
1273 url
= "https://svc.joker.com/nic/update"
1276 class DDNSProviderKEYSYSTEMS(DDNSProvider
):
1277 handle
= "key-systems.net"
1278 name
= "dynamicdns.key-systems.net"
1279 website
= "https://domaindiscount24.com/"
1280 protocols
= ("ipv4",)
1282 # There are only information provided by the domaindiscount24 how to
1283 # perform an update with HTTP APIs
1284 # https://www.domaindiscount24.com/faq/dynamic-dns
1285 # examples: https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=auto
1286 # https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=213.x.x.x&mx=213.x.x.x
1288 url
= "https://dynamicdns.key-systems.net/update.php"
1289 can_remove_records
= False
1290 supports_token_auth
= False
1292 def update_protocol(self
, proto
):
1293 address
= self
.get_address(proto
)
1295 "hostname" : self
.hostname
,
1296 "password" : self
.password
,
1300 # Send update to the server.
1301 response
= self
.send_request(self
.url
, data
=data
)
1303 # Get the full response message.
1304 output
= response
.read().decode()
1306 # Handle success messages.
1307 if "code = 200" in output
:
1310 # Handle error messages.
1311 if "abuse prevention triggered" in output
:
1312 raise DDNSAbuseError
1313 elif "invalid password" in output
:
1314 raise DDNSAuthenticationError
1315 elif "Authorization failed" in output
:
1316 raise DDNSRequestError(_("Invalid hostname specified"))
1318 # If we got here, some other update error happened.
1319 raise DDNSUpdateError
1322 class DDNSProviderGoogle(DDNSProtocolDynDNS2
, DDNSProvider
):
1323 handle
= "domains.google.com"
1324 name
= "Google Domains"
1325 website
= "https://domains.google.com/"
1326 protocols
= ("ipv4",)
1328 # Information about the format of the HTTP request is to be found
1329 # here: https://support.google.com/domains/answer/6147083?hl=en
1331 url
= "https://domains.google.com/nic/update"
1334 class DDNSProviderLightningWireLabs(DDNSProvider
):
1335 handle
= "dns.lightningwirelabs.com"
1336 name
= "Lightning Wire Labs DNS Service"
1337 website
= "https://dns.lightningwirelabs.com/"
1339 # Information about the format of the HTTPS request is to be found
1340 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
1342 supports_token_auth
= True
1344 url
= "https://dns.lightningwirelabs.com/update"
1347 # Raise an error if no auth details are given.
1349 raise DDNSConfigurationError
1352 "hostname" : self
.hostname
,
1353 "token" : self
.token
,
1354 "address6" : self
.get_address("ipv6", "-"),
1355 "address4" : self
.get_address("ipv4", "-"),
1358 # Send update to the server.
1359 response
= self
.send_request(self
.url
, data
=data
)
1361 # Handle success messages.
1362 if response
.code
== 200:
1365 # If we got here, some other update error happened.
1366 raise DDNSUpdateError
1369 class DDNSProviderLoopia(DDNSProtocolDynDNS2
, DDNSProvider
):
1370 handle
= "loopia.se"
1372 website
= "https://www.loopia.com"
1373 protocols
= ("ipv4",)
1375 # Information about the format of the HTTP request is to be found
1376 # here: https://support.loopia.com/wiki/About_the_DynDNS_support
1378 url
= "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
1381 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
1382 handle
= "myonlineportal.net"
1383 name
= "myonlineportal.net"
1384 website
= "https:/myonlineportal.net/"
1386 # Information about the request and response can be obtained here:
1387 # https://myonlineportal.net/howto_dyndns
1389 url
= "https://myonlineportal.net/updateddns"
1391 def prepare_request_data(self
, proto
):
1393 "hostname" : self
.hostname
,
1394 "ip" : self
.get_address(proto
),
1400 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
1401 handle
= "namecheap.com"
1403 website
= "http://namecheap.com"
1404 protocols
= ("ipv4",)
1406 # Information about the format of the HTTP request is to be found
1407 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
1408 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
1410 url
= "https://dynamicdns.park-your-domain.com/update"
1411 can_remove_records
= False
1412 supports_token_auth
= False
1414 def update_protocol(self
, proto
):
1415 # Namecheap requires the hostname splitted into a host and domain part.
1416 host
, domain
= self
.hostname
.split(".", 1)
1418 # Get and store curent IP address.
1419 address
= self
.get_address(proto
)
1423 "password" : self
.password
,
1428 # Send update to the server.
1429 response
= self
.send_request(self
.url
, data
=data
)
1431 # Get the full response message.
1432 output
= response
.read().decode()
1434 # Handle success messages.
1435 if self
.get_xml_tag_value(output
, "IP") == address
:
1438 # Handle error codes.
1439 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1441 if errorcode
== "304156":
1442 raise DDNSAuthenticationError
1443 elif errorcode
== "316153":
1444 raise DDNSRequestError(_("Domain not found"))
1445 elif errorcode
== "316154":
1446 raise DDNSRequestError(_("Domain not active"))
1447 elif errorcode
in ("380098", "380099"):
1448 raise DDNSInternalServerError
1450 # If we got here, some other update error happened.
1451 raise DDNSUpdateError
1454 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
1455 handle
= "no-ip.com"
1457 website
= "http://www.noip.com/"
1458 protocols
= ("ipv4",)
1460 # Information about the format of the HTTP request is to be found
1461 # here: http://www.noip.com/integrate/request and
1462 # here: http://www.noip.com/integrate/response
1464 url
= "https://dynupdate.noip.com/nic/update"
1466 def prepare_request_data(self
, proto
):
1467 assert proto
== "ipv4"
1470 "hostname" : self
.hostname
,
1471 "address" : self
.get_address(proto
),
1477 class DDNSProviderNowDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1478 handle
= "now-dns.com"
1480 website
= "http://now-dns.com/"
1481 protocols
= ("ipv6", "ipv4")
1483 # Information about the format of the request is to be found
1484 # but only can be accessed by register an account and login
1485 # https://now-dns.com/?m=api
1487 url
= "https://now-dns.com/update"
1490 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
1491 handle
= "nsupdate.info"
1492 name
= "nsupdate.info"
1493 website
= "http://nsupdate.info/"
1494 protocols
= ("ipv6", "ipv4",)
1496 # Information about the format of the HTTP request can be found
1497 # after login on the provider user interface and here:
1498 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
1500 url
= "https://nsupdate.info/nic/update"
1502 # TODO nsupdate.info can actually do this, but the functionality
1503 # has not been implemented here, yet.
1504 can_remove_records
= False
1506 supports_token_auth
= True
1508 # After a failed update, there will be no retries
1509 # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
1510 holdoff_failure_days
= None
1512 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
1513 # and for the password a so called secret.
1516 return self
.get("hostname")
1520 return self
.token
or self
.get("secret")
1522 def prepare_request_data(self
, proto
):
1524 "myip" : self
.get_address(proto
),
1530 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1531 handle
= "opendns.com"
1533 website
= "http://www.opendns.com"
1535 # Detailed information about the update request and possible
1536 # response codes can be obtained from here:
1537 # https://support.opendns.com/entries/23891440
1539 url
= "https://updates.opendns.com/nic/update"
1541 def prepare_request_data(self
, proto
):
1543 "hostname" : self
.hostname
,
1544 "myip" : self
.get_address(proto
),
1550 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1553 website
= "http://www.ovh.com/"
1554 protocols
= ("ipv4",)
1556 # OVH only provides very limited information about how to
1557 # update a DynDNS host. They only provide the update url
1558 # on the their german subpage.
1560 # http://hilfe.ovh.de/DomainDynHost
1562 url
= "https://www.ovh.com/nic/update"
1564 def prepare_request_data(self
, proto
):
1565 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1567 "system" : "dyndns",
1573 class DDNSProviderRegfish(DDNSProvider
):
1574 handle
= "regfish.com"
1575 name
= "Regfish GmbH"
1576 website
= "http://www.regfish.com/"
1578 # A full documentation to the providers api can be found here
1579 # but is only available in german.
1580 # https://www.regfish.de/domains/dyndns/dokumentation
1582 url
= "https://dyndns.regfish.de/"
1583 can_remove_records
= False
1584 supports_token_auth
= True
1588 "fqdn" : self
.hostname
,
1591 # Check if we update an IPv6 address.
1592 address6
= self
.get_address("ipv6")
1594 data
["ipv6"] = address6
1596 # Check if we update an IPv4 address.
1597 address4
= self
.get_address("ipv4")
1599 data
["ipv4"] = address4
1601 # Raise an error if none address is given.
1602 if "ipv6" not in data
and "ipv4" not in data
:
1603 raise DDNSConfigurationError
1605 # Check if a token has been set.
1607 data
["token"] = self
.token
1609 # Raise an error if no token and no useranem and password
1611 elif not self
.username
and not self
.password
:
1612 raise DDNSConfigurationError(_("No Auth details specified"))
1614 # HTTP Basic Auth is only allowed if no token is used.
1616 # Send update to the server.
1617 response
= self
.send_request(self
.url
, data
=data
)
1619 # Send update to the server.
1620 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1622 # Get the full response message.
1623 output
= response
.read().decode()
1625 # Handle success messages.
1626 if "100" in output
or "101" in output
:
1629 # Handle error codes.
1630 if "401" or "402" in output
:
1631 raise DDNSAuthenticationError
1632 elif "408" in output
:
1633 raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
1634 elif "409" in output
:
1635 raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
1636 elif "412" in output
:
1637 raise DDNSRequestError(_("No valid FQDN was given"))
1638 elif "414" in output
:
1639 raise DDNSInternalServerError
1641 # If we got here, some other update error happened.
1642 raise DDNSUpdateError
1645 class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1646 handle
= "schokokeks.org"
1648 website
= "http://www.schokokeks.org/"
1649 protocols
= ("ipv4",)
1651 # Information about the format of the request is to be found
1652 # https://wiki.schokokeks.org/DynDNS
1653 url
= "https://dyndns.schokokeks.org/nic/update"
1656 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1657 handle
= "selfhost.de"
1658 name
= "Selfhost.de"
1659 website
= "http://www.selfhost.de/"
1660 protocols
= ("ipv4",)
1662 url
= "https://carol.selfhost.de/nic/update"
1664 def prepare_request_data(self
, proto
):
1665 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1673 class DDNSProviderServercow(DDNSProvider
):
1674 handle
= "servercow.de"
1675 name
= "servercow.de"
1676 website
= "https://servercow.de/"
1677 protocols
= ("ipv4", "ipv6")
1679 url
= "https://www.servercow.de/dnsupdate/update.php"
1680 can_remove_records
= False
1681 supports_token_auth
= False
1683 def update_protocol(self
, proto
):
1685 "ipaddr" : self
.get_address(proto
),
1686 "hostname" : self
.hostname
,
1687 "username" : self
.username
,
1688 "pass" : self
.password
,
1691 # Send request to provider
1692 response
= self
.send_request(self
.url
, data
=data
)
1695 output
= response
.read().decode()
1697 # Server responds with OK if update was successful
1698 if output
.startswith("OK"):
1702 elif output
.startswith("FAILED - Authentication failed"):
1703 raise DDNSAuthenticationError
1705 # If we got here, some other update error happened
1706 raise DDNSUpdateError(output
)
1709 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1710 handle
= "spdns.org"
1712 website
= "https://www.spdyn.de/"
1714 # Detailed information about request and response codes are provided
1715 # by the vendor. They are using almost the same mechanism and status
1716 # codes as dyndns.org so we can inherit all those stuff.
1718 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1719 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1721 url
= "https://update.spdyn.de/nic/update"
1723 supports_token_auth
= True
1727 return self
.get("username") or self
.hostname
1731 return self
.get("password") or self
.token
1734 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1735 handle
= "strato.com"
1737 website
= "http:/www.strato.com/"
1738 protocols
= ("ipv4",)
1740 # Information about the request and response can be obtained here:
1741 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1743 url
= "https://dyndns.strato.com/nic/update"
1745 def prepare_request_data(self
, proto
):
1746 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1749 "backupmx" : "NOCHG"
1755 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1756 handle
= "twodns.de"
1758 website
= "http://www.twodns.de"
1759 protocols
= ("ipv4",)
1761 # Detailed information about the request can be found here
1762 # http://twodns.de/en/faqs
1763 # http://twodns.de/en/api
1765 url
= "https://update.twodns.de/update"
1767 def prepare_request_data(self
, proto
):
1768 assert proto
== "ipv4"
1771 "ip" : self
.get_address(proto
),
1772 "hostname" : self
.hostname
1778 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1779 handle
= "udmedia.de"
1780 name
= "Udmedia GmbH"
1781 website
= "http://www.udmedia.de"
1782 protocols
= ("ipv4",)
1784 # Information about the request can be found here
1785 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1787 url
= "https://www.udmedia.de/nic/update"
1790 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1791 handle
= "variomedia.de"
1793 website
= "http://www.variomedia.de/"
1794 protocols
= ("ipv6", "ipv4",)
1796 # Detailed information about the request can be found here
1797 # https://dyndns.variomedia.de/
1799 url
= "https://dyndns.variomedia.de/nic/update"
1801 def prepare_request_data(self
, proto
):
1803 "hostname" : self
.hostname
,
1804 "myip" : self
.get_address(proto
),
1810 class DDNSProviderXLhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1811 handle
= "xlhost.de"
1813 website
= "http://xlhost.de/"
1814 protocols
= ("ipv4",)
1816 # Information about the format of the HTTP request is to be found
1817 # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
1819 url
= "https://nsupdate.xlhost.de/"
1822 class DDNSProviderZoneedit(DDNSProvider
):
1823 handle
= "zoneedit.com"
1825 website
= "http://www.zoneedit.com"
1826 protocols
= ("ipv4",)
1828 supports_token_auth
= False
1830 # Detailed information about the request and the response codes can be
1832 # http://www.zoneedit.com/doc/api/other.html
1833 # http://www.zoneedit.com/faq.html
1835 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1837 def update_protocol(self
, proto
):
1839 "dnsto" : self
.get_address(proto
),
1840 "host" : self
.hostname
1843 # Send update to the server.
1844 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1846 # Get the full response message.
1847 output
= response
.read().decode()
1849 # Handle success messages.
1850 if output
.startswith("<SUCCESS"):
1853 # Handle error codes.
1854 if output
.startswith("invalid login"):
1855 raise DDNSAuthenticationError
1856 elif output
.startswith("<ERROR CODE=\"704\""):
1857 raise DDNSRequestError(_("No valid FQDN was given"))
1858 elif output
.startswith("<ERROR CODE=\"702\""):
1859 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1861 # If we got here, some other update error happened.
1862 raise DDNSUpdateError
1865 class DDNSProviderDNSmadeEasy(DDNSProvider
):
1866 handle
= "dnsmadeeasy.com"
1867 name
= "DNSmadeEasy.com"
1868 website
= "http://www.dnsmadeeasy.com/"
1869 protocols
= ("ipv4",)
1871 # DNS Made Easy Nameserver Provider also offering Dynamic DNS
1872 # Documentation can be found here:
1873 # http://www.dnsmadeeasy.com/dynamic-dns/
1875 url
= "https://cp.dnsmadeeasy.com/servlet/updateip?"
1876 can_remove_records
= False
1877 supports_token_auth
= False
1879 def update_protocol(self
, proto
):
1881 "ip" : self
.get_address(proto
),
1882 "id" : self
.hostname
,
1883 "username" : self
.username
,
1884 "password" : self
.password
,
1887 # Send update to the server.
1888 response
= self
.send_request(self
.url
, data
=data
)
1890 # Get the full response message.
1891 output
= response
.read().decode()
1893 # Handle success messages.
1894 if output
.startswith("success") or output
.startswith("error-record-ip-same"):
1897 # Handle error codes.
1898 if output
.startswith("error-auth-suspend"):
1899 raise DDNSRequestError(_("Account has been suspended"))
1901 elif output
.startswith("error-auth-voided"):
1902 raise DDNSRequestError(_("Account has been revoked"))
1904 elif output
.startswith("error-record-invalid"):
1905 raise DDNSRequestError(_("Specified host does not exist"))
1907 elif output
.startswith("error-auth"):
1908 raise DDNSAuthenticationError
1910 # If we got here, some other update error happened.
1911 raise DDNSUpdateError(_("Server response: %s") % output
)
1914 class DDNSProviderZZZZ(DDNSProvider
):
1917 website
= "https://zzzz.io"
1918 protocols
= ("ipv6", "ipv4",)
1920 # Detailed information about the update request can be found here:
1921 # https://zzzz.io/faq/
1923 # Details about the possible response codes have been provided in the bugtracker:
1924 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1926 url
= "https://zzzz.io/api/v1/update"
1927 can_remove_records
= False
1928 supports_token_auth
= True
1930 def update_protocol(self
, proto
):
1932 "ip" : self
.get_address(proto
),
1933 "token" : self
.token
,
1937 data
["type"] = "aaaa"
1939 # zzzz uses the host from the full hostname as part
1940 # of the update url.
1941 host
, domain
= self
.hostname
.split(".", 1)
1943 # Add host value to the update url.
1944 url
= "%s/%s" % (self
.url
, host
)
1946 # Send update to the server.
1948 response
= self
.send_request(url
, data
=data
)
1950 # Handle error codes.
1951 except DDNSNotFound
:
1952 raise DDNSRequestError(_("Invalid hostname specified"))
1954 # Handle success messages.
1955 if response
.code
== 200:
1958 # If we got here, some other update error happened.
1959 raise DDNSUpdateError