]>
git.ipfire.org Git - people/stevee/ddns.git/blob - src/ddns/providers.py
a1ca3f3d58a1a50480316cd1240b581fbe01d824
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
79 Should be overwritten to check if the system the code is running
80 on has all the required tools to support this provider.
84 def __init__(self
, core
, **settings
):
87 # Copy a set of default settings and
88 # update them by those from the configuration file.
89 self
.settings
= self
.DEFAULT_SETTINGS
.copy()
90 self
.settings
.update(settings
)
92 def __init_subclass__(cls
, **kwargs
):
93 super().__init
_subclass
__(**kwargs
)
95 if not all((cls
.handle
, cls
.name
, cls
.website
)):
96 raise DDNSError(_("Provider is not properly configured"))
98 assert cls
.handle
not in _providers
, \
99 "Provider '%s' has already been registered" % cls
.handle
102 _providers
[cls
.handle
] = cls
105 return "<DDNS Provider %s (%s)>" % (self
.name
, self
.handle
)
107 def __cmp__(self
, other
):
108 return (lambda a
, b
: (a
> b
)-(a
< b
))(self
.hostname
, other
.hostname
)
114 def get(self
, key
, default
=None):
116 Get a setting from the settings dictionary.
118 return self
.settings
.get(key
, default
)
123 Fast access to the hostname.
125 return self
.get("hostname")
130 Fast access to the username.
132 return self
.get("username")
137 Fast access to the password.
139 return self
.get("password")
144 Fast access to the token.
146 return self
.get("token")
148 def __call__(self
, force
=False):
150 logger
.debug(_("Updating %s forced") % self
.hostname
)
152 # Do nothing if the last update has failed or no update is required
153 elif self
.has_failure
or not self
.requires_update
:
156 # Execute the update.
160 # 1) Catch network errors early, because we do not want to log
161 # them to the database. They are usually temporary and caused
162 # by the client side, so that we will retry quickly.
163 # 2) If there is an internet server error (HTTP code 500) on the
164 # provider's site, we will not log a failure and try again
166 except (DDNSNetworkError
, DDNSInternalServerError
):
169 # In case of any errors, log the failed request and
170 # raise the exception.
171 except DDNSError
as e
:
172 self
.core
.db
.log_failure(self
.hostname
, e
)
175 logger
.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") %
176 {"hostname": self
.hostname
, "provider": self
.name
})
177 self
.core
.db
.log_success(self
.hostname
)
180 for protocol
in self
.protocols
:
181 if self
.have_address(protocol
):
182 self
.update_protocol(protocol
)
183 elif self
.can_remove_records
:
184 self
.remove_protocol(protocol
)
186 def update_protocol(self
, proto
):
187 raise NotImplementedError
189 def remove_protocol(self
, proto
):
190 if not self
.can_remove_records
:
191 raise RuntimeError("can_remove_records is enabled, but remove_protocol() not implemented")
193 raise NotImplementedError
196 def requires_update(self
):
197 # If the IP addresses have changed, an update is required
198 if self
.ip_address_changed(self
.protocols
):
199 logger
.debug(_("An update for %(hostname)s (%(provider)s) is performed because of an IP address change") %
200 {"hostname": self
.hostname
, "provider": self
.name
})
204 # If the holdoff time has expired, an update is required, too
205 if self
.holdoff_time_expired():
206 logger
.debug(_("An update for %(hostname)s (%(provider)s) is performed because the holdoff time has expired") %
207 {"hostname": self
.hostname
, "provider": self
.name
})
211 # Otherwise, we don't need to perform an update
212 logger
.debug(_("No update required for %(hostname)s (%(provider)s)") %
213 {"hostname": self
.hostname
, "provider": self
.name
})
218 def has_failure(self
):
220 Returns True when the last update has failed and no retry
221 should be performed, yet.
223 last_status
= self
.db
.last_update_status(self
.hostname
)
225 # Return False if the last update has not failed.
226 if not last_status
== "failure":
229 # If there is no holdoff time, we won't update ever again.
230 if self
.holdoff_failure_days
is None:
231 logger
.warning(_("An update has not been performed because earlier updates failed for %s") % self
.hostname
)
232 logger
.warning(_("There will be no retries"))
236 # Determine when the holdoff time ends
237 last_update
= self
.db
.last_update(self
.hostname
, status
=last_status
)
238 holdoff_end
= last_update
+ datetime
.timedelta(days
=self
.holdoff_failure_days
)
240 now
= datetime
.datetime
.utcnow()
241 if now
< holdoff_end
:
242 failure_message
= self
.db
.last_update_failure_message(self
.hostname
)
244 logger
.warning(_("An update has not been performed because earlier updates failed for %s") % self
.hostname
)
247 logger
.warning(_("Last failure message:"))
249 for line
in failure_message
.splitlines():
250 logger
.warning(" %s" % line
)
252 logger
.warning(_("Further updates will be withheld until %s") % holdoff_end
)
258 def ip_address_changed(self
, protos
):
260 Returns True if this host is already up to date
261 and does not need to change the IP address on the
265 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
266 current_address
= self
.get_address(proto
)
268 # Handle if the system has not got any IP address from a protocol
269 # (i.e. had full dual-stack connectivity which it has not any more)
270 if current_address
is None:
271 # If addresses still exists in the DNS system and if this provider
272 # is able to remove records, we will do that.
273 if addresses
and self
.can_remove_records
:
276 # Otherwise, we cannot go on...
279 if not current_address
in addresses
:
284 def holdoff_time_expired(self
):
286 Returns true if the holdoff time has expired
287 and the host requires an update
289 # If no holdoff days is defined, we cannot go on
290 if not self
.holdoff_days
:
293 # Get the timestamp of the last successfull update
294 last_update
= self
.db
.last_update(self
.hostname
, status
="success")
296 # If no timestamp has been recorded, no update has been
297 # performed. An update should be performed now.
301 # Determine when the holdoff time ends
302 holdoff_end
= last_update
+ datetime
.timedelta(days
=self
.holdoff_days
)
304 now
= datetime
.datetime
.utcnow()
306 if now
>= holdoff_end
:
307 logger
.debug("The holdoff time has expired for %s" % self
.hostname
)
310 logger
.debug("Updates for %s are held off until %s" %
311 (self
.hostname
, holdoff_end
))
314 def send_request(self
, *args
, **kwargs
):
316 Proxy connection to the send request
319 return self
.core
.system
.send_request(*args
, **kwargs
)
321 def get_address(self
, proto
, default
=None):
323 Proxy method to get the current IP address.
325 return self
.core
.system
.get_address(proto
) or default
327 def have_address(self
, proto
):
329 Returns True if an IP address for the given protocol
332 address
= self
.get_address(proto
)
340 class DDNSProtocolDynDNS2(object):
342 This is an abstract class that implements the DynDNS updater
343 protocol version 2. As this is a popular way to update dynamic
344 DNS records, this class is supposed make the provider classes
348 # Information about the format of the request is to be found
349 # http://dyn.com/support/developers/api/perform-update/
350 # http://dyn.com/support/developers/api/return-codes/
352 # The DynDNS protocol version 2 does not allow to remove records
353 can_remove_records
= False
355 def prepare_request_data(self
, proto
):
357 "hostname" : self
.hostname
,
358 "myip" : self
.get_address(proto
),
363 def update_protocol(self
, proto
):
364 data
= self
.prepare_request_data(proto
)
366 return self
.send_request(data
)
368 def send_request(self
, data
):
369 # Send update to the server.
370 response
= DDNSProvider
.send_request(self
, self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
372 # Get the full response message.
373 output
= response
.read().decode()
375 # Handle success messages.
376 if output
.startswith("good") or output
.startswith("nochg"):
379 # Handle error codes.
380 if output
== "badauth":
381 raise DDNSAuthenticationError
382 elif output
== "abuse":
384 elif output
== "notfqdn":
385 raise DDNSRequestError(_("No valid FQDN was given"))
386 elif output
== "nohost":
387 raise DDNSRequestError(_("Specified host does not exist"))
388 elif output
== "911":
389 raise DDNSInternalServerError
390 elif output
== "dnserr":
391 raise DDNSInternalServerError(_("DNS error encountered"))
392 elif output
== "badagent":
393 raise DDNSBlockedError
394 elif output
== "badip":
395 raise DDNSBlockedError
397 # If we got here, some other update error happened.
398 raise DDNSUpdateError(_("Server response: %s") % output
)
401 class DDNSResponseParserXML(object):
403 This class provides a parser for XML responses which
404 will be sent by various providers. This class uses the python
405 shipped XML minidom module to walk through the XML tree and return
409 def get_xml_tag_value(self
, document
, content
):
410 # Send input to the parser.
411 xmldoc
= xml
.dom
.minidom
.parseString(document
)
413 # Get XML elements by the given content.
414 element
= xmldoc
.getElementsByTagName(content
)
416 # If no element has been found, we directly can return None.
420 # Only get the first child from an element, even there are more than one.
421 firstchild
= element
[0].firstChild
423 # Get the value of the child.
424 value
= firstchild
.nodeValue
430 class DDNSProviderAllInkl(DDNSProvider
):
431 handle
= "all-inkl.com"
432 name
= "All-inkl.com"
433 website
= "http://all-inkl.com/"
434 protocols
= ("ipv4",)
436 # There are only information provided by the vendor how to
437 # perform an update on a FRITZ Box. Grab requried informations
439 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
441 url
= "http://dyndns.kasserver.com"
442 can_remove_records
= False
445 # There is no additional data required so we directly can
447 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
)
449 # Get the full response message.
450 output
= response
.read().decode()
452 # Handle success messages.
453 if output
.startswith("good") or output
.startswith("nochg"):
456 # If we got here, some other update error happened.
457 raise DDNSUpdateError
460 class DDNSProviderBindNsupdate(DDNSProvider
):
462 name
= "BIND nsupdate utility"
463 website
= "http://en.wikipedia.org/wiki/Nsupdate"
469 # Search if the nsupdate utility is available
470 paths
= os
.environ
.get("PATH")
472 for path
in paths
.split(":"):
473 executable
= os
.path
.join(path
, "nsupdate")
475 if os
.path
.exists(executable
):
481 scriptlet
= self
.__make
_scriptlet
()
483 # -v enables TCP hence we transfer keys and other data that may
484 # exceed the size of one packet.
485 # -t sets the timeout
486 command
= ["nsupdate", "-v", "-t", "60"]
488 p
= subprocess
.Popen(command
, shell
=True, stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
489 stdout
, stderr
= p
.communicate(scriptlet
)
491 if p
.returncode
== 0:
494 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p
.returncode
, stderr
))
496 def __make_scriptlet(self
):
499 # Set a different server the update is sent to.
500 server
= self
.get("server", None)
502 scriptlet
.append("server %s" % server
)
504 # Set the DNS zone the host should be added to.
505 zone
= self
.get("zone", None)
507 scriptlet
.append("zone %s" % zone
)
509 key
= self
.get("key", None)
511 secret
= self
.get("secret")
513 scriptlet
.append("key %s %s" % (key
, secret
))
515 ttl
= self
.get("ttl", self
.DEFAULT_TTL
)
517 # Perform an update for each supported protocol.
518 for rrtype
, proto
in (("AAAA", "ipv6"), ("A", "ipv4")):
519 address
= self
.get_address(proto
)
523 scriptlet
.append("update delete %s. %s" % (self
.hostname
, rrtype
))
524 scriptlet
.append("update add %s. %s %s %s" % \
525 (self
.hostname
, ttl
, rrtype
, address
))
527 # Send the actions to the server.
528 scriptlet
.append("send")
529 scriptlet
.append("quit")
531 logger
.debug(_("Scriptlet:"))
532 for line
in scriptlet
:
533 # Masquerade the line with the secret key.
534 if line
.startswith("key"):
535 line
= "key **** ****"
537 logger
.debug(" %s" % line
)
539 return "\n".join(scriptlet
)
542 class DDNSProviderChangeIP(DDNSProvider
):
543 handle
= "changeip.com"
544 name
= "ChangeIP.com"
545 website
= "https://changeip.com"
546 protocols
= ("ipv4",)
548 # Detailed information about the update api can be found here.
549 # http://www.changeip.com/accounts/knowledgebase.php?action=displayarticle&id=34
551 url
= "https://nic.changeip.com/nic/update"
552 can_remove_records
= False
554 def update_protocol(self
, proto
):
556 "hostname" : self
.hostname
,
557 "myip" : self
.get_address(proto
),
560 # Send update to the server.
562 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
564 # Handle error codes.
565 except urllib
.error
.HTTPError
as e
:
567 raise DDNSRequestError(_("Domain not found."))
571 # Handle success message.
572 if response
.code
== 200:
575 # If we got here, some other update error happened.
576 raise DDNSUpdateError(_("Server response: %s") % output
)
579 class DDNSProviderDesecIO(DDNSProtocolDynDNS2
, DDNSProvider
):
582 website
= "https://www.desec.io"
583 protocols
= ("ipv6", "ipv4",)
585 # ipv4 / ipv6 records are automatically removed when the update
586 # request originates from the respectively other protocol and no
587 # address is explicitly provided for the unused protocol.
589 url
= "https://update.dedyn.io"
591 # desec.io sends the IPv6 and IPv4 address in one request
594 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
596 # This one supports IPv6
597 myipv6
= self
.get_address("ipv6")
599 # Add update information if we have an IPv6 address.
601 data
["myipv6"] = myipv6
603 self
.send_request(data
)
606 class DDNSProviderDDNSS(DDNSProvider
):
609 website
= "http://www.ddnss.de"
610 protocols
= ("ipv4",)
612 # Detailed information about how to send the update request and possible response
613 # codes can be obtained from here.
614 # http://www.ddnss.de/info.php
615 # http://www.megacomputing.de/2014/08/dyndns-service-response-time/#more-919
617 url
= "http://www.ddnss.de/upd.php"
618 can_remove_records
= False
620 def update_protocol(self
, proto
):
622 "ip" : self
.get_address(proto
),
623 "host" : self
.hostname
,
626 # Check if a token has been set.
628 data
["key"] = self
.token
630 # Check if username and hostname are given.
631 elif self
.username
and self
.password
:
633 "user" : self
.username
,
634 "pwd" : self
.password
,
637 # Raise an error if no auth details are given.
639 raise DDNSConfigurationError
641 # Send update to the server.
642 response
= self
.send_request(self
.url
, data
=data
)
644 # This provider sends the response code as part of the header.
645 # Get status information from the header.
646 output
= response
.getheader('ddnss-response')
648 # Handle success messages.
649 if output
== "good" or output
== "nochg":
652 # Handle error codes.
653 if output
== "badauth":
654 raise DDNSAuthenticationError
655 elif output
== "notfqdn":
656 raise DDNSRequestError(_("No valid FQDN was given"))
657 elif output
== "nohost":
658 raise DDNSRequestError(_("Specified host does not exist"))
659 elif output
== "911":
660 raise DDNSInternalServerError
661 elif output
== "dnserr":
662 raise DDNSInternalServerError(_("DNS error encountered"))
663 elif output
== "disabled":
664 raise DDNSRequestError(_("Account disabled or locked"))
666 # If we got here, some other update error happened.
667 raise DDNSUpdateError
670 class DDNSProviderDHS(DDNSProvider
):
672 name
= "DHS International"
673 website
= "http://dhs.org/"
674 protocols
= ("ipv4",)
676 # No information about the used update api provided on webpage,
677 # grabed from source code of ez-ipudate.
679 url
= "http://members.dhs.org/nic/hosts"
680 can_remove_records
= False
682 def update_protocol(self
, proto
):
684 "domain" : self
.hostname
,
685 "ip" : self
.get_address(proto
),
687 "hostcmdstage" : "2",
691 # Send update to the server.
692 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
694 # Handle success messages.
695 if response
.code
== 200:
698 # If we got here, some other update error happened.
699 raise DDNSUpdateError
702 class DDNSProviderDNSpark(DDNSProvider
):
703 handle
= "dnspark.com"
705 website
= "http://dnspark.com/"
706 protocols
= ("ipv4",)
708 # Informations to the used api can be found here:
709 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
711 url
= "https://control.dnspark.com/api/dynamic/update.php"
712 can_remove_records
= False
714 def update_protocol(self
, proto
):
716 "domain" : self
.hostname
,
717 "ip" : self
.get_address(proto
),
720 # Send update to the server.
721 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
723 # Get the full response message.
724 output
= response
.read().decode()
726 # Handle success messages.
727 if output
.startswith("ok") or output
.startswith("nochange"):
730 # Handle error codes.
731 if output
== "unauth":
732 raise DDNSAuthenticationError
733 elif output
== "abuse":
735 elif output
== "blocked":
736 raise DDNSBlockedError
737 elif output
== "nofqdn":
738 raise DDNSRequestError(_("No valid FQDN was given"))
739 elif output
== "nohost":
740 raise DDNSRequestError(_("Invalid hostname specified"))
741 elif output
== "notdyn":
742 raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
743 elif output
== "invalid":
744 raise DDNSRequestError(_("Invalid IP address has been sent"))
746 # If we got here, some other update error happened.
747 raise DDNSUpdateError
750 class DDNSProviderDtDNS(DDNSProvider
):
753 website
= "http://dtdns.com/"
754 protocols
= ("ipv4",)
756 # Information about the format of the HTTPS request is to be found
757 # http://www.dtdns.com/dtsite/updatespec
759 url
= "https://www.dtdns.com/api/autodns.cfm"
760 can_remove_records
= False
762 def update_protocol(self
, proto
):
764 "ip" : self
.get_address(proto
),
765 "id" : self
.hostname
,
769 # Send update to the server.
770 response
= self
.send_request(self
.url
, data
=data
)
772 # Get the full response message.
773 output
= response
.read().decode()
775 # Remove all leading and trailing whitespace.
776 output
= output
.strip()
778 # Handle success messages.
779 if "now points to" in output
:
782 # Handle error codes.
783 if output
== "No hostname to update was supplied.":
784 raise DDNSRequestError(_("No hostname specified"))
786 elif output
== "The hostname you supplied is not valid.":
787 raise DDNSRequestError(_("Invalid hostname specified"))
789 elif output
== "The password you supplied is not valid.":
790 raise DDNSAuthenticationError
792 elif output
== "Administration has disabled this account.":
793 raise DDNSRequestError(_("Account has been disabled"))
795 elif output
== "Illegal character in IP.":
796 raise DDNSRequestError(_("Invalid IP address has been sent"))
798 elif output
== "Too many failed requests.":
799 raise DDNSRequestError(_("Too many failed requests"))
801 # If we got here, some other update error happened.
802 raise DDNSUpdateError
805 class DDNSProviderDuckDNS(DDNSProvider
):
806 handle
= "duckdns.org"
808 website
= "http://www.duckdns.org/"
809 protocols
= ("ipv6", "ipv4",)
811 # Information about the format of the request is to be found
812 # https://www.duckdns.org/spec.jsp
814 url
= "https://www.duckdns.org/update"
815 can_remove_records
= False
818 # Raise an error if no auth details are given.
820 raise DDNSConfigurationError
823 "domains" : self
.hostname
,
824 "token" : self
.token
,
827 # Check if we update an IPv4 address.
828 address4
= self
.get_address("ipv4")
830 data
["ip"] = address4
832 # Check if we update an IPv6 address.
833 address6
= self
.get_address("ipv6")
835 data
["ipv6"] = address6
837 # Raise an error if no address is given.
838 if "ip" not in data
and "ipv6" not in data
:
839 raise DDNSConfigurationError
841 # Send update to the server.
842 response
= self
.send_request(self
.url
, data
=data
)
844 # Get the full response message.
845 output
= response
.read().decode()
847 # Remove all leading and trailing whitespace.
848 output
= output
.strip()
850 # Handle success messages.
854 # The provider does not give detailed information
855 # if the update fails. Only a "KO" will be sent back.
857 raise DDNSUpdateError
859 # If we got here, some other update error happened.
860 raise DDNSUpdateError
863 class DDNSProviderDyFi(DDNSProtocolDynDNS2
, DDNSProvider
):
866 website
= "https://www.dy.fi/"
867 protocols
= ("ipv4",)
869 # Information about the format of the request is to be found
870 # https://www.dy.fi/page/clients?lang=en
871 # https://www.dy.fi/page/specification?lang=en
873 url
= "https://www.dy.fi/nic/update"
875 # Please only send automatic updates when your IP address changes,
876 # or once per 5 to 6 days to refresh the address mapping (they will
877 # expire if not refreshed within 7 days).
881 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
882 handle
= "dyndns.org"
884 website
= "http://dyn.com/dns/"
885 protocols
= ("ipv4",)
887 # Information about the format of the request is to be found
888 # http://http://dyn.com/support/developers/api/perform-update/
889 # http://dyn.com/support/developers/api/return-codes/
891 url
= "https://members.dyndns.org/nic/update"
894 class DDNSProviderDomainOffensive(DDNSProtocolDynDNS2
, DDNSProvider
):
896 name
= "Domain-Offensive"
897 website
= "https://www.do.de/"
898 protocols
= ("ipv6", "ipv4")
900 # Detailed information about the request and response codes
901 # are available on the providers webpage.
902 # https://www.do.de/wiki/FlexDNS_-_Entwickler
904 url
= "https://ddns.do.de/"
906 class DDNSProviderDynUp(DDNSProvider
):
909 website
= "http://dynup.de/"
910 protocols
= ("ipv4",)
912 # Information about the format of the HTTPS request is to be found
913 # https://dyndnsfree.de/user/hilfe.php
915 url
= "https://dynup.de/dyn.php"
916 can_remove_records
= False
918 def update_protocol(self
, proto
):
920 "username" : self
.username
,
921 "password" : self
.password
,
922 "hostname" : self
.hostname
,
926 # Send update to the server.
927 response
= self
.send_request(self
.url
, data
=data
)
929 # Get the full response message.
930 output
= response
.read().decode()
932 # Remove all leading and trailing whitespace.
933 output
= output
.strip()
935 # Handle success messages.
936 if output
.startswith("I:OK"):
939 # If we got here, some other update error happened.
940 raise DDNSUpdateError
943 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
946 website
= "http://dynu.com/"
947 protocols
= ("ipv6", "ipv4",)
949 # Detailed information about the request and response codes
950 # are available on the providers webpage.
951 # http://dynu.com/Default.aspx?page=dnsapi
953 url
= "https://api.dynu.com/nic/update"
955 # DynU sends the IPv6 and IPv4 address in one request
958 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
960 # This one supports IPv6
961 myipv6
= self
.get_address("ipv6")
963 # Add update information if we have an IPv6 address.
965 data
["myipv6"] = myipv6
967 self
.send_request(data
)
970 class DDNSProviderEasyDNS(DDNSProvider
):
971 handle
= "easydns.com"
973 website
= "http://www.easydns.com/"
974 protocols
= ("ipv4",)
976 # Detailed information about the request and response codes
977 # (API 1.3) are available on the providers webpage.
978 # https://fusion.easydns.com/index.php?/Knowledgebase/Article/View/102/7/dynamic-dns
980 url
= "http://api.cp.easydns.com/dyn/tomato.php"
982 def update_protocol(self
, proto
):
984 "myip" : self
.get_address(proto
, "-"),
985 "hostname" : self
.hostname
,
988 # Send update to the server.
989 response
= self
.send_request(self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
991 # Get the full response message.
992 output
= response
.read().decode()
994 # Remove all leading and trailing whitespace.
995 output
= output
.strip()
997 # Handle success messages.
998 if output
.startswith("NOERROR"):
1001 # Handle error codes.
1002 if output
.startswith("NOACCESS"):
1003 raise DDNSAuthenticationError
1005 elif output
.startswith("NOSERVICE"):
1006 raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain"))
1008 elif output
.startswith("ILLEGAL INPUT"):
1009 raise DDNSRequestError(_("Invalid data has been sent"))
1011 elif output
.startswith("TOOSOON"):
1012 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1014 # If we got here, some other update error happened.
1015 raise DDNSUpdateError
1018 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
1019 handle
= "domopoli.de"
1020 name
= "domopoli.de"
1021 website
= "http://domopoli.de/"
1022 protocols
= ("ipv4",)
1024 # https://www.domopoli.de/?page=howto#DynDns_start
1026 url
= "http://dyndns.domopoli.de/nic/update"
1029 class DDNSProviderDynsNet(DDNSProvider
):
1032 website
= "http://www.dyns.net/"
1033 protocols
= ("ipv4",)
1034 can_remove_records
= False
1036 # There is very detailed informatio about how to send the update request and
1037 # the possible response codes. (Currently we are using the v1.1 proto)
1038 # http://www.dyns.net/documentation/technical/protocol/
1040 url
= "http://www.dyns.net/postscript011.php"
1042 def update_protocol(self
, proto
):
1044 "ip" : self
.get_address(proto
),
1045 "host" : self
.hostname
,
1046 "username" : self
.username
,
1047 "password" : self
.password
,
1050 # Send update to the server.
1051 response
= self
.send_request(self
.url
, data
=data
)
1053 # Get the full response message.
1054 output
= response
.read().decode()
1056 # Handle success messages.
1057 if output
.startswith("200"):
1060 # Handle error codes.
1061 if output
.startswith("400"):
1062 raise DDNSRequestError(_("Malformed request has been sent"))
1063 elif output
.startswith("401"):
1064 raise DDNSAuthenticationError
1065 elif output
.startswith("402"):
1066 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1067 elif output
.startswith("403"):
1068 raise DDNSInternalServerError
1070 # If we got here, some other update error happened.
1071 raise DDNSUpdateError(_("Server response: %s") % output
)
1074 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
1077 website
= "http://www.enom.com/"
1078 protocols
= ("ipv4",)
1080 # There are very detailed information about how to send an update request and
1081 # the respone codes.
1082 # http://www.enom.com/APICommandCatalog/
1084 url
= "https://dynamic.name-services.com/interface.asp"
1085 can_remove_records
= False
1087 def update_protocol(self
, proto
):
1089 "command" : "setdnshost",
1090 "responsetype" : "xml",
1091 "address" : self
.get_address(proto
),
1092 "domainpassword" : self
.password
,
1093 "zone" : self
.hostname
1096 # Send update to the server.
1097 response
= self
.send_request(self
.url
, data
=data
)
1099 # Get the full response message.
1100 output
= response
.read().decode()
1102 # Handle success messages.
1103 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
1106 # Handle error codes.
1107 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1109 if errorcode
== "304155":
1110 raise DDNSAuthenticationError
1111 elif errorcode
== "304153":
1112 raise DDNSRequestError(_("Domain not found"))
1114 # If we got here, some other update error happened.
1115 raise DDNSUpdateError
1118 class DDNSProviderEntryDNS(DDNSProvider
):
1119 handle
= "entrydns.net"
1121 website
= "http://entrydns.net/"
1122 protocols
= ("ipv4",)
1124 # Some very tiny details about their so called "Simple API" can be found
1125 # here: https://entrydns.net/help
1126 url
= "https://entrydns.net/records/modify"
1127 can_remove_records
= False
1129 def update_protocol(self
, proto
):
1131 "ip" : self
.get_address(proto
),
1134 # Add auth token to the update url.
1135 url
= "%s/%s" % (self
.url
, self
.token
)
1137 # Send update to the server.
1139 response
= self
.send_request(url
, data
=data
)
1141 # Handle error codes
1142 except urllib
.error
.HTTPError
as e
:
1144 raise DDNSAuthenticationError
1147 raise DDNSRequestError(_("An invalid IP address was submitted"))
1151 # Handle success messages.
1152 if response
.code
== 200:
1155 # If we got here, some other update error happened.
1156 raise DDNSUpdateError
1159 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
1160 handle
= "freedns.afraid.org"
1161 name
= "freedns.afraid.org"
1162 website
= "http://freedns.afraid.org/"
1164 # No information about the request or response could be found on the vendor
1165 # page. All used values have been collected by testing.
1166 url
= "https://freedns.afraid.org/dynamic/update.php"
1167 can_remove_records
= False
1169 def update_protocol(self
, proto
):
1171 "address" : self
.get_address(proto
),
1174 # Add auth token to the update url.
1175 url
= "%s?%s" % (self
.url
, self
.token
)
1177 # Send update to the server.
1178 response
= self
.send_request(url
, data
=data
)
1180 # Get the full response message.
1181 output
= response
.read().decode()
1183 # Handle success messages.
1184 if output
.startswith("Updated") or "has not changed" in output
:
1187 # Handle error codes.
1188 if output
== "ERROR: Unable to locate this record":
1189 raise DDNSAuthenticationError
1190 elif "is an invalid IP address" in output
:
1191 raise DDNSRequestError(_("Invalid IP address has been sent"))
1193 # If we got here, some other update error happened.
1194 raise DDNSUpdateError
1197 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1200 website
= "https://www.inwx.com"
1201 protocols
= ("ipv6", "ipv4")
1203 # Information about the format of the HTTP request is to be found
1204 # here: https://www.inwx.com/en/nameserver2/dyndns (requires login)
1205 # Notice: The URL is the same for: inwx.com|de|at|ch|es
1207 url
= "https://dyndns.inwx.com/nic/update"
1210 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1211 handle
= "itsdns.de"
1213 website
= "http://www.itsdns.de/"
1214 protocols
= ("ipv6", "ipv4")
1216 # Information about the format of the HTTP request is to be found
1217 # here: https://www.itsdns.de/dynupdatehelp.htm
1219 url
= "https://www.itsdns.de/update.php"
1222 class DDNSProviderJoker(DDNSProtocolDynDNS2
, DDNSProvider
):
1223 handle
= "joker.com"
1224 name
= "Joker.com Dynamic DNS"
1225 website
= "https://joker.com/"
1226 protocols
= ("ipv4",)
1228 # Information about the request can be found here:
1229 # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
1230 # Using DynDNS V2 protocol over HTTPS here
1232 url
= "https://svc.joker.com/nic/update"
1235 class DDNSProviderKEYSYSTEMS(DDNSProvider
):
1236 handle
= "key-systems.net"
1237 name
= "dynamicdns.key-systems.net"
1238 website
= "https://domaindiscount24.com/"
1239 protocols
= ("ipv4",)
1241 # There are only information provided by the domaindiscount24 how to
1242 # perform an update with HTTP APIs
1243 # https://www.domaindiscount24.com/faq/dynamic-dns
1244 # examples: https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=auto
1245 # https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=213.x.x.x&mx=213.x.x.x
1247 url
= "https://dynamicdns.key-systems.net/update.php"
1248 can_remove_records
= False
1250 def update_protocol(self
, proto
):
1251 address
= self
.get_address(proto
)
1253 "hostname" : self
.hostname
,
1254 "password" : self
.password
,
1258 # Send update to the server.
1259 response
= self
.send_request(self
.url
, data
=data
)
1261 # Get the full response message.
1262 output
= response
.read().decode()
1264 # Handle success messages.
1265 if "code = 200" in output
:
1268 # Handle error messages.
1269 if "abuse prevention triggered" in output
:
1270 raise DDNSAbuseError
1271 elif "invalid password" in output
:
1272 raise DDNSAuthenticationError
1273 elif "Authorization failed" in output
:
1274 raise DDNSRequestError(_("Invalid hostname specified"))
1276 # If we got here, some other update error happened.
1277 raise DDNSUpdateError
1280 class DDNSProviderGoogle(DDNSProtocolDynDNS2
, DDNSProvider
):
1281 handle
= "domains.google.com"
1282 name
= "Google Domains"
1283 website
= "https://domains.google.com/"
1284 protocols
= ("ipv4",)
1286 # Information about the format of the HTTP request is to be found
1287 # here: https://support.google.com/domains/answer/6147083?hl=en
1289 url
= "https://domains.google.com/nic/update"
1292 class DDNSProviderLightningWireLabs(DDNSProvider
):
1293 handle
= "dns.lightningwirelabs.com"
1294 name
= "Lightning Wire Labs DNS Service"
1295 website
= "https://dns.lightningwirelabs.com/"
1297 # Information about the format of the HTTPS request is to be found
1298 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
1300 url
= "https://dns.lightningwirelabs.com/update"
1303 # Raise an error if no auth details are given.
1305 raise DDNSConfigurationError
1308 "hostname" : self
.hostname
,
1309 "token" : self
.token
,
1310 "address6" : self
.get_address("ipv6", "-"),
1311 "address4" : self
.get_address("ipv4", "-"),
1314 # Send update to the server.
1315 response
= self
.send_request(self
.url
, data
=data
)
1317 # Handle success messages.
1318 if response
.code
== 200:
1321 # If we got here, some other update error happened.
1322 raise DDNSUpdateError
1325 class DDNSProviderLoopia(DDNSProtocolDynDNS2
, DDNSProvider
):
1326 handle
= "loopia.se"
1328 website
= "https://www.loopia.com"
1329 protocols
= ("ipv4",)
1331 # Information about the format of the HTTP request is to be found
1332 # here: https://support.loopia.com/wiki/About_the_DynDNS_support
1334 url
= "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
1337 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
1338 handle
= "myonlineportal.net"
1339 name
= "myonlineportal.net"
1340 website
= "https:/myonlineportal.net/"
1342 # Information about the request and response can be obtained here:
1343 # https://myonlineportal.net/howto_dyndns
1345 url
= "https://myonlineportal.net/updateddns"
1347 def prepare_request_data(self
, proto
):
1349 "hostname" : self
.hostname
,
1350 "ip" : self
.get_address(proto
),
1356 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
1357 handle
= "namecheap.com"
1359 website
= "http://namecheap.com"
1360 protocols
= ("ipv4",)
1362 # Information about the format of the HTTP request is to be found
1363 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
1364 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
1366 url
= "https://dynamicdns.park-your-domain.com/update"
1367 can_remove_records
= False
1369 def update_protocol(self
, proto
):
1370 # Namecheap requires the hostname splitted into a host and domain part.
1371 host
, domain
= self
.hostname
.split(".", 1)
1373 # Get and store curent IP address.
1374 address
= self
.get_address(proto
)
1378 "password" : self
.password
,
1383 # Send update to the server.
1384 response
= self
.send_request(self
.url
, data
=data
)
1386 # Get the full response message.
1387 output
= response
.read().decode()
1389 # Handle success messages.
1390 if self
.get_xml_tag_value(output
, "IP") == address
:
1393 # Handle error codes.
1394 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1396 if errorcode
== "304156":
1397 raise DDNSAuthenticationError
1398 elif errorcode
== "316153":
1399 raise DDNSRequestError(_("Domain not found"))
1400 elif errorcode
== "316154":
1401 raise DDNSRequestError(_("Domain not active"))
1402 elif errorcode
in ("380098", "380099"):
1403 raise DDNSInternalServerError
1405 # If we got here, some other update error happened.
1406 raise DDNSUpdateError
1409 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
1410 handle
= "no-ip.com"
1412 website
= "http://www.noip.com/"
1413 protocols
= ("ipv4",)
1415 # Information about the format of the HTTP request is to be found
1416 # here: http://www.noip.com/integrate/request and
1417 # here: http://www.noip.com/integrate/response
1419 url
= "http://dynupdate.noip.com/nic/update"
1421 def prepare_request_data(self
, proto
):
1422 assert proto
== "ipv4"
1425 "hostname" : self
.hostname
,
1426 "address" : self
.get_address(proto
),
1432 class DDNSProviderNowDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1433 handle
= "now-dns.com"
1435 website
= "http://now-dns.com/"
1436 protocols
= ("ipv6", "ipv4")
1438 # Information about the format of the request is to be found
1439 # but only can be accessed by register an account and login
1440 # https://now-dns.com/?m=api
1442 url
= "https://now-dns.com/update"
1445 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
1446 handle
= "nsupdate.info"
1447 name
= "nsupdate.info"
1448 website
= "http://nsupdate.info/"
1449 protocols
= ("ipv6", "ipv4",)
1451 # Information about the format of the HTTP request can be found
1452 # after login on the provider user interface and here:
1453 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
1455 url
= "https://nsupdate.info/nic/update"
1457 # TODO nsupdate.info can actually do this, but the functionality
1458 # has not been implemented here, yet.
1459 can_remove_records
= False
1461 # After a failed update, there will be no retries
1462 # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
1463 holdoff_failure_days
= None
1465 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
1466 # and for the password a so called secret.
1469 return self
.get("hostname")
1473 return self
.token
or self
.get("secret")
1475 def prepare_request_data(self
, proto
):
1477 "myip" : self
.get_address(proto
),
1483 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1484 handle
= "opendns.com"
1486 website
= "http://www.opendns.com"
1488 # Detailed information about the update request and possible
1489 # response codes can be obtained from here:
1490 # https://support.opendns.com/entries/23891440
1492 url
= "https://updates.opendns.com/nic/update"
1494 def prepare_request_data(self
, proto
):
1496 "hostname" : self
.hostname
,
1497 "myip" : self
.get_address(proto
),
1503 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1506 website
= "http://www.ovh.com/"
1507 protocols
= ("ipv4",)
1509 # OVH only provides very limited information about how to
1510 # update a DynDNS host. They only provide the update url
1511 # on the their german subpage.
1513 # http://hilfe.ovh.de/DomainDynHost
1515 url
= "https://www.ovh.com/nic/update"
1517 def prepare_request_data(self
, proto
):
1518 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1520 "system" : "dyndns",
1526 class DDNSProviderRegfish(DDNSProvider
):
1527 handle
= "regfish.com"
1528 name
= "Regfish GmbH"
1529 website
= "http://www.regfish.com/"
1531 # A full documentation to the providers api can be found here
1532 # but is only available in german.
1533 # https://www.regfish.de/domains/dyndns/dokumentation
1535 url
= "https://dyndns.regfish.de/"
1536 can_remove_records
= False
1540 "fqdn" : self
.hostname
,
1543 # Check if we update an IPv6 address.
1544 address6
= self
.get_address("ipv6")
1546 data
["ipv6"] = address6
1548 # Check if we update an IPv4 address.
1549 address4
= self
.get_address("ipv4")
1551 data
["ipv4"] = address4
1553 # Raise an error if none address is given.
1554 if "ipv6" not in data
and "ipv4" not in data
:
1555 raise DDNSConfigurationError
1557 # Check if a token has been set.
1559 data
["token"] = self
.token
1561 # Raise an error if no token and no useranem and password
1563 elif not self
.username
and not self
.password
:
1564 raise DDNSConfigurationError(_("No Auth details specified"))
1566 # HTTP Basic Auth is only allowed if no token is used.
1568 # Send update to the server.
1569 response
= self
.send_request(self
.url
, data
=data
)
1571 # Send update to the server.
1572 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1574 # Get the full response message.
1575 output
= response
.read().decode()
1577 # Handle success messages.
1578 if "100" in output
or "101" in output
:
1581 # Handle error codes.
1582 if "401" or "402" in output
:
1583 raise DDNSAuthenticationError
1584 elif "408" in output
:
1585 raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
1586 elif "409" in output
:
1587 raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
1588 elif "412" in output
:
1589 raise DDNSRequestError(_("No valid FQDN was given"))
1590 elif "414" in output
:
1591 raise DDNSInternalServerError
1593 # If we got here, some other update error happened.
1594 raise DDNSUpdateError
1597 class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1598 handle
= "schokokeks.org"
1600 website
= "http://www.schokokeks.org/"
1601 protocols
= ("ipv4",)
1603 # Information about the format of the request is to be found
1604 # https://wiki.schokokeks.org/DynDNS
1605 url
= "https://dyndns.schokokeks.org/nic/update"
1608 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1609 handle
= "selfhost.de"
1610 name
= "Selfhost.de"
1611 website
= "http://www.selfhost.de/"
1612 protocols
= ("ipv4",)
1614 url
= "https://carol.selfhost.de/nic/update"
1616 def prepare_request_data(self
, proto
):
1617 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1625 class DDNSProviderServercow(DDNSProvider
):
1626 handle
= "servercow.de"
1627 name
= "servercow.de"
1628 website
= "https://servercow.de/"
1629 protocols
= ("ipv4", "ipv6")
1631 url
= "https://www.servercow.de/dnsupdate/update.php"
1632 can_remove_records
= False
1634 def update_protocol(self
, proto
):
1636 "ipaddr" : self
.get_address(proto
),
1637 "hostname" : self
.hostname
,
1638 "username" : self
.username
,
1639 "pass" : self
.password
,
1642 # Send request to provider
1643 response
= self
.send_request(self
.url
, data
=data
)
1646 output
= response
.read().decode()
1648 # Server responds with OK if update was successful
1649 if output
.startswith("OK"):
1653 elif output
.startswith("FAILED - Authentication failed"):
1654 raise DDNSAuthenticationError
1656 # If we got here, some other update error happened
1657 raise DDNSUpdateError(output
)
1660 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1661 handle
= "spdns.org"
1663 website
= "https://www.spdyn.de/"
1665 # Detailed information about request and response codes are provided
1666 # by the vendor. They are using almost the same mechanism and status
1667 # codes as dyndns.org so we can inherit all those stuff.
1669 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1670 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1672 url
= "https://update.spdyn.de/nic/update"
1676 return self
.get("username") or self
.hostname
1680 return self
.get("password") or self
.token
1683 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1684 handle
= "strato.com"
1686 website
= "http:/www.strato.com/"
1687 protocols
= ("ipv4",)
1689 # Information about the request and response can be obtained here:
1690 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1692 url
= "https://dyndns.strato.com/nic/update"
1694 def prepare_request_data(self
, proto
):
1695 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1698 "backupmx" : "NOCHG"
1704 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1705 handle
= "twodns.de"
1707 website
= "http://www.twodns.de"
1708 protocols
= ("ipv4",)
1710 # Detailed information about the request can be found here
1711 # http://twodns.de/en/faqs
1712 # http://twodns.de/en/api
1714 url
= "https://update.twodns.de/update"
1716 def prepare_request_data(self
, proto
):
1717 assert proto
== "ipv4"
1720 "ip" : self
.get_address(proto
),
1721 "hostname" : self
.hostname
1727 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1728 handle
= "udmedia.de"
1729 name
= "Udmedia GmbH"
1730 website
= "http://www.udmedia.de"
1731 protocols
= ("ipv4",)
1733 # Information about the request can be found here
1734 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1736 url
= "https://www.udmedia.de/nic/update"
1739 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1740 handle
= "variomedia.de"
1742 website
= "http://www.variomedia.de/"
1743 protocols
= ("ipv6", "ipv4",)
1745 # Detailed information about the request can be found here
1746 # https://dyndns.variomedia.de/
1748 url
= "https://dyndns.variomedia.de/nic/update"
1750 def prepare_request_data(self
, proto
):
1752 "hostname" : self
.hostname
,
1753 "myip" : self
.get_address(proto
),
1759 class DDNSProviderXLhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1760 handle
= "xlhost.de"
1762 website
= "http://xlhost.de/"
1763 protocols
= ("ipv4",)
1765 # Information about the format of the HTTP request is to be found
1766 # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
1768 url
= "https://nsupdate.xlhost.de/"
1771 class DDNSProviderZoneedit(DDNSProvider
):
1772 handle
= "zoneedit.com"
1774 website
= "http://www.zoneedit.com"
1775 protocols
= ("ipv4",)
1777 # Detailed information about the request and the response codes can be
1779 # http://www.zoneedit.com/doc/api/other.html
1780 # http://www.zoneedit.com/faq.html
1782 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1784 def update_protocol(self
, proto
):
1786 "dnsto" : self
.get_address(proto
),
1787 "host" : self
.hostname
1790 # Send update to the server.
1791 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1793 # Get the full response message.
1794 output
= response
.read().decode()
1796 # Handle success messages.
1797 if output
.startswith("<SUCCESS"):
1800 # Handle error codes.
1801 if output
.startswith("invalid login"):
1802 raise DDNSAuthenticationError
1803 elif output
.startswith("<ERROR CODE=\"704\""):
1804 raise DDNSRequestError(_("No valid FQDN was given"))
1805 elif output
.startswith("<ERROR CODE=\"702\""):
1806 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1808 # If we got here, some other update error happened.
1809 raise DDNSUpdateError
1812 class DDNSProviderDNSmadeEasy(DDNSProvider
):
1813 handle
= "dnsmadeeasy.com"
1814 name
= "DNSmadeEasy.com"
1815 website
= "http://www.dnsmadeeasy.com/"
1816 protocols
= ("ipv4",)
1818 # DNS Made Easy Nameserver Provider also offering Dynamic DNS
1819 # Documentation can be found here:
1820 # http://www.dnsmadeeasy.com/dynamic-dns/
1822 url
= "https://cp.dnsmadeeasy.com/servlet/updateip?"
1823 can_remove_records
= False
1825 def update_protocol(self
, proto
):
1827 "ip" : self
.get_address(proto
),
1828 "id" : self
.hostname
,
1829 "username" : self
.username
,
1830 "password" : self
.password
,
1833 # Send update to the server.
1834 response
= self
.send_request(self
.url
, data
=data
)
1836 # Get the full response message.
1837 output
= response
.read().decode()
1839 # Handle success messages.
1840 if output
.startswith("success") or output
.startswith("error-record-ip-same"):
1843 # Handle error codes.
1844 if output
.startswith("error-auth-suspend"):
1845 raise DDNSRequestError(_("Account has been suspended"))
1847 elif output
.startswith("error-auth-voided"):
1848 raise DDNSRequestError(_("Account has been revoked"))
1850 elif output
.startswith("error-record-invalid"):
1851 raise DDNSRequestError(_("Specified host does not exist"))
1853 elif output
.startswith("error-auth"):
1854 raise DDNSAuthenticationError
1856 # If we got here, some other update error happened.
1857 raise DDNSUpdateError(_("Server response: %s") % output
)
1860 class DDNSProviderZZZZ(DDNSProvider
):
1863 website
= "https://zzzz.io"
1864 protocols
= ("ipv6", "ipv4",)
1866 # Detailed information about the update request can be found here:
1867 # https://zzzz.io/faq/
1869 # Details about the possible response codes have been provided in the bugtracker:
1870 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1872 url
= "https://zzzz.io/api/v1/update"
1873 can_remove_records
= False
1875 def update_protocol(self
, proto
):
1877 "ip" : self
.get_address(proto
),
1878 "token" : self
.token
,
1882 data
["type"] = "aaaa"
1884 # zzzz uses the host from the full hostname as part
1885 # of the update url.
1886 host
, domain
= self
.hostname
.split(".", 1)
1888 # Add host value to the update url.
1889 url
= "%s/%s" % (self
.url
, host
)
1891 # Send update to the server.
1893 response
= self
.send_request(url
, data
=data
)
1895 # Handle error codes.
1896 except DDNSNotFound
:
1897 raise DDNSRequestError(_("Invalid hostname specified"))
1899 # Handle success messages.
1900 if response
.code
== 200:
1903 # If we got here, some other update error happened.
1904 raise DDNSUpdateError