]>
git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
85cb6afb4692366cf2559a38f9987cdd3cebdf5c
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 header
= response
.info()
647 # Get status information from the header.
648 output
= header
.getheader('ddnss-response')
650 # Handle success messages.
651 if output
== "good" or output
== "nochg":
654 # Handle error codes.
655 if output
== "badauth":
656 raise DDNSAuthenticationError
657 elif output
== "notfqdn":
658 raise DDNSRequestError(_("No valid FQDN was given"))
659 elif output
== "nohost":
660 raise DDNSRequestError(_("Specified host does not exist"))
661 elif output
== "911":
662 raise DDNSInternalServerError
663 elif output
== "dnserr":
664 raise DDNSInternalServerError(_("DNS error encountered"))
665 elif output
== "disabled":
666 raise DDNSRequestError(_("Account disabled or locked"))
668 # If we got here, some other update error happened.
669 raise DDNSUpdateError
672 class DDNSProviderDHS(DDNSProvider
):
674 name
= "DHS International"
675 website
= "http://dhs.org/"
676 protocols
= ("ipv4",)
678 # No information about the used update api provided on webpage,
679 # grabed from source code of ez-ipudate.
681 url
= "http://members.dhs.org/nic/hosts"
682 can_remove_records
= False
684 def update_protocol(self
, proto
):
686 "domain" : self
.hostname
,
687 "ip" : self
.get_address(proto
),
689 "hostcmdstage" : "2",
693 # Send update to the server.
694 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
696 # Handle success messages.
697 if response
.code
== 200:
700 # If we got here, some other update error happened.
701 raise DDNSUpdateError
704 class DDNSProviderDNSpark(DDNSProvider
):
705 handle
= "dnspark.com"
707 website
= "http://dnspark.com/"
708 protocols
= ("ipv4",)
710 # Informations to the used api can be found here:
711 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
713 url
= "https://control.dnspark.com/api/dynamic/update.php"
714 can_remove_records
= False
716 def update_protocol(self
, proto
):
718 "domain" : self
.hostname
,
719 "ip" : self
.get_address(proto
),
722 # Send update to the server.
723 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
725 # Get the full response message.
726 output
= response
.read().decode()
728 # Handle success messages.
729 if output
.startswith("ok") or output
.startswith("nochange"):
732 # Handle error codes.
733 if output
== "unauth":
734 raise DDNSAuthenticationError
735 elif output
== "abuse":
737 elif output
== "blocked":
738 raise DDNSBlockedError
739 elif output
== "nofqdn":
740 raise DDNSRequestError(_("No valid FQDN was given"))
741 elif output
== "nohost":
742 raise DDNSRequestError(_("Invalid hostname specified"))
743 elif output
== "notdyn":
744 raise DDNSRequestError(_("Hostname not marked as a dynamic host"))
745 elif output
== "invalid":
746 raise DDNSRequestError(_("Invalid IP address has been sent"))
748 # If we got here, some other update error happened.
749 raise DDNSUpdateError
752 class DDNSProviderDtDNS(DDNSProvider
):
755 website
= "http://dtdns.com/"
756 protocols
= ("ipv4",)
758 # Information about the format of the HTTPS request is to be found
759 # http://www.dtdns.com/dtsite/updatespec
761 url
= "https://www.dtdns.com/api/autodns.cfm"
762 can_remove_records
= False
764 def update_protocol(self
, proto
):
766 "ip" : self
.get_address(proto
),
767 "id" : self
.hostname
,
771 # Send update to the server.
772 response
= self
.send_request(self
.url
, data
=data
)
774 # Get the full response message.
775 output
= response
.read().decode()
777 # Remove all leading and trailing whitespace.
778 output
= output
.strip()
780 # Handle success messages.
781 if "now points to" in output
:
784 # Handle error codes.
785 if output
== "No hostname to update was supplied.":
786 raise DDNSRequestError(_("No hostname specified"))
788 elif output
== "The hostname you supplied is not valid.":
789 raise DDNSRequestError(_("Invalid hostname specified"))
791 elif output
== "The password you supplied is not valid.":
792 raise DDNSAuthenticationError
794 elif output
== "Administration has disabled this account.":
795 raise DDNSRequestError(_("Account has been disabled"))
797 elif output
== "Illegal character in IP.":
798 raise DDNSRequestError(_("Invalid IP address has been sent"))
800 elif output
== "Too many failed requests.":
801 raise DDNSRequestError(_("Too many failed requests"))
803 # If we got here, some other update error happened.
804 raise DDNSUpdateError
807 class DDNSProviderDuckDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
808 handle
= "duckdns.org"
810 website
= "http://www.duckdns.org/"
811 protocols
= ("ipv4",)
813 # Information about the format of the request is to be found
814 # https://www.duckdns.org/install.jsp
816 url
= "https://www.duckdns.org/nic/update"
819 class DDNSProviderDyFi(DDNSProtocolDynDNS2
, DDNSProvider
):
822 website
= "https://www.dy.fi/"
823 protocols
= ("ipv4",)
825 # Information about the format of the request is to be found
826 # https://www.dy.fi/page/clients?lang=en
827 # https://www.dy.fi/page/specification?lang=en
829 url
= "http://www.dy.fi/nic/update"
831 # Please only send automatic updates when your IP address changes,
832 # or once per 5 to 6 days to refresh the address mapping (they will
833 # expire if not refreshed within 7 days).
837 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
838 handle
= "dyndns.org"
840 website
= "http://dyn.com/dns/"
841 protocols
= ("ipv4",)
843 # Information about the format of the request is to be found
844 # http://http://dyn.com/support/developers/api/perform-update/
845 # http://dyn.com/support/developers/api/return-codes/
847 url
= "https://members.dyndns.org/nic/update"
850 class DDNSProviderDomainOffensive(DDNSProtocolDynDNS2
, DDNSProvider
):
852 name
= "Domain-Offensive"
853 website
= "https://www.do.de/"
854 protocols
= ("ipv6", "ipv4")
856 # Detailed information about the request and response codes
857 # are available on the providers webpage.
858 # https://www.do.de/wiki/FlexDNS_-_Entwickler
860 url
= "https://ddns.do.de/"
862 class DDNSProviderDynUp(DDNSProvider
):
865 website
= "http://dynup.de/"
866 protocols
= ("ipv4",)
868 # Information about the format of the HTTPS request is to be found
869 # https://dyndnsfree.de/user/hilfe.php
871 url
= "https://dynup.de/dyn.php"
872 can_remove_records
= False
874 def update_protocol(self
, proto
):
876 "username" : self
.username
,
877 "password" : self
.password
,
878 "hostname" : self
.hostname
,
882 # Send update to the server.
883 response
= self
.send_request(self
.url
, data
=data
)
885 # Get the full response message.
886 output
= response
.read().decode()
888 # Remove all leading and trailing whitespace.
889 output
= output
.strip()
891 # Handle success messages.
892 if output
.startswith("I:OK"):
895 # If we got here, some other update error happened.
896 raise DDNSUpdateError
899 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
902 website
= "http://dynu.com/"
903 protocols
= ("ipv6", "ipv4",)
905 # Detailed information about the request and response codes
906 # are available on the providers webpage.
907 # http://dynu.com/Default.aspx?page=dnsapi
909 url
= "https://api.dynu.com/nic/update"
911 # DynU sends the IPv6 and IPv4 address in one request
914 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
916 # This one supports IPv6
917 myipv6
= self
.get_address("ipv6")
919 # Add update information if we have an IPv6 address.
921 data
["myipv6"] = myipv6
923 self
.send_request(data
)
926 class DDNSProviderEasyDNS(DDNSProvider
):
927 handle
= "easydns.com"
929 website
= "http://www.easydns.com/"
930 protocols
= ("ipv4",)
932 # Detailed information about the request and response codes
933 # (API 1.3) are available on the providers webpage.
934 # https://fusion.easydns.com/index.php?/Knowledgebase/Article/View/102/7/dynamic-dns
936 url
= "http://api.cp.easydns.com/dyn/tomato.php"
938 def update_protocol(self
, proto
):
940 "myip" : self
.get_address(proto
, "-"),
941 "hostname" : self
.hostname
,
944 # Send update to the server.
945 response
= self
.send_request(self
.url
, data
=data
, username
=self
.username
, password
=self
.password
)
947 # Get the full response message.
948 output
= response
.read().decode()
950 # Remove all leading and trailing whitespace.
951 output
= output
.strip()
953 # Handle success messages.
954 if output
.startswith("NOERROR"):
957 # Handle error codes.
958 if output
.startswith("NOACCESS"):
959 raise DDNSAuthenticationError
961 elif output
.startswith("NOSERVICE"):
962 raise DDNSRequestError(_("Dynamic DNS is not turned on for this domain"))
964 elif output
.startswith("ILLEGAL INPUT"):
965 raise DDNSRequestError(_("Invalid data has been sent"))
967 elif output
.startswith("TOOSOON"):
968 raise DDNSRequestError(_("Too frequent update requests have been sent"))
970 # If we got here, some other update error happened.
971 raise DDNSUpdateError
974 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
975 handle
= "domopoli.de"
977 website
= "http://domopoli.de/"
978 protocols
= ("ipv4",)
980 # https://www.domopoli.de/?page=howto#DynDns_start
982 url
= "http://dyndns.domopoli.de/nic/update"
985 class DDNSProviderDynsNet(DDNSProvider
):
988 website
= "http://www.dyns.net/"
989 protocols
= ("ipv4",)
990 can_remove_records
= False
992 # There is very detailed informatio about how to send the update request and
993 # the possible response codes. (Currently we are using the v1.1 proto)
994 # http://www.dyns.net/documentation/technical/protocol/
996 url
= "http://www.dyns.net/postscript011.php"
998 def update_protocol(self
, proto
):
1000 "ip" : self
.get_address(proto
),
1001 "host" : self
.hostname
,
1002 "username" : self
.username
,
1003 "password" : self
.password
,
1006 # Send update to the server.
1007 response
= self
.send_request(self
.url
, data
=data
)
1009 # Get the full response message.
1010 output
= response
.read().decode()
1012 # Handle success messages.
1013 if output
.startswith("200"):
1016 # Handle error codes.
1017 if output
.startswith("400"):
1018 raise DDNSRequestError(_("Malformed request has been sent"))
1019 elif output
.startswith("401"):
1020 raise DDNSAuthenticationError
1021 elif output
.startswith("402"):
1022 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1023 elif output
.startswith("403"):
1024 raise DDNSInternalServerError
1026 # If we got here, some other update error happened.
1027 raise DDNSUpdateError(_("Server response: %s") % output
)
1030 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
1033 website
= "http://www.enom.com/"
1034 protocols
= ("ipv4",)
1036 # There are very detailed information about how to send an update request and
1037 # the respone codes.
1038 # http://www.enom.com/APICommandCatalog/
1040 url
= "https://dynamic.name-services.com/interface.asp"
1041 can_remove_records
= False
1043 def update_protocol(self
, proto
):
1045 "command" : "setdnshost",
1046 "responsetype" : "xml",
1047 "address" : self
.get_address(proto
),
1048 "domainpassword" : self
.password
,
1049 "zone" : self
.hostname
1052 # Send update to the server.
1053 response
= self
.send_request(self
.url
, data
=data
)
1055 # Get the full response message.
1056 output
= response
.read().decode()
1058 # Handle success messages.
1059 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
1062 # Handle error codes.
1063 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1065 if errorcode
== "304155":
1066 raise DDNSAuthenticationError
1067 elif errorcode
== "304153":
1068 raise DDNSRequestError(_("Domain not found"))
1070 # If we got here, some other update error happened.
1071 raise DDNSUpdateError
1074 class DDNSProviderEntryDNS(DDNSProvider
):
1075 handle
= "entrydns.net"
1077 website
= "http://entrydns.net/"
1078 protocols
= ("ipv4",)
1080 # Some very tiny details about their so called "Simple API" can be found
1081 # here: https://entrydns.net/help
1082 url
= "https://entrydns.net/records/modify"
1083 can_remove_records
= False
1085 def update_protocol(self
, proto
):
1087 "ip" : self
.get_address(proto
),
1090 # Add auth token to the update url.
1091 url
= "%s/%s" % (self
.url
, self
.token
)
1093 # Send update to the server.
1095 response
= self
.send_request(url
, data
=data
)
1097 # Handle error codes
1098 except urllib
.error
.HTTPError
as e
:
1100 raise DDNSAuthenticationError
1103 raise DDNSRequestError(_("An invalid IP address was submitted"))
1107 # Handle success messages.
1108 if response
.code
== 200:
1111 # If we got here, some other update error happened.
1112 raise DDNSUpdateError
1115 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
1116 handle
= "freedns.afraid.org"
1117 name
= "freedns.afraid.org"
1118 website
= "http://freedns.afraid.org/"
1120 # No information about the request or response could be found on the vendor
1121 # page. All used values have been collected by testing.
1122 url
= "https://freedns.afraid.org/dynamic/update.php"
1123 can_remove_records
= False
1125 def update_protocol(self
, proto
):
1127 "address" : self
.get_address(proto
),
1130 # Add auth token to the update url.
1131 url
= "%s?%s" % (self
.url
, self
.token
)
1133 # Send update to the server.
1134 response
= self
.send_request(url
, data
=data
)
1136 # Get the full response message.
1137 output
= response
.read().decode()
1139 # Handle success messages.
1140 if output
.startswith("Updated") or "has not changed" in output
:
1143 # Handle error codes.
1144 if output
== "ERROR: Unable to locate this record":
1145 raise DDNSAuthenticationError
1146 elif "is an invalid IP address" in output
:
1147 raise DDNSRequestError(_("Invalid IP address has been sent"))
1149 # If we got here, some other update error happened.
1150 raise DDNSUpdateError
1153 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1156 website
= "https://www.inwx.com"
1157 protocols
= ("ipv6", "ipv4")
1159 # Information about the format of the HTTP request is to be found
1160 # here: https://www.inwx.com/en/nameserver2/dyndns (requires login)
1161 # Notice: The URL is the same for: inwx.com|de|at|ch|es
1163 url
= "https://dyndns.inwx.com/nic/update"
1166 class DDNSProviderItsdns(DDNSProtocolDynDNS2
, DDNSProvider
):
1167 handle
= "itsdns.de"
1169 website
= "http://www.itsdns.de/"
1170 protocols
= ("ipv6", "ipv4")
1172 # Information about the format of the HTTP request is to be found
1173 # here: https://www.itsdns.de/dynupdatehelp.htm
1175 url
= "https://www.itsdns.de/update.php"
1178 class DDNSProviderJoker(DDNSProtocolDynDNS2
, DDNSProvider
):
1179 handle
= "joker.com"
1180 name
= "Joker.com Dynamic DNS"
1181 website
= "https://joker.com/"
1182 protocols
= ("ipv4",)
1184 # Information about the request can be found here:
1185 # https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
1186 # Using DynDNS V2 protocol over HTTPS here
1188 url
= "https://svc.joker.com/nic/update"
1191 class DDNSProviderKEYSYSTEMS(DDNSProvider
):
1192 handle
= "key-systems.net"
1193 name
= "dynamicdns.key-systems.net"
1194 website
= "https://domaindiscount24.com/"
1195 protocols
= ("ipv4",)
1197 # There are only information provided by the domaindiscount24 how to
1198 # perform an update with HTTP APIs
1199 # https://www.domaindiscount24.com/faq/dynamic-dns
1200 # examples: https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=auto
1201 # https://dynamicdns.key-systems.net/update.php?hostname=hostname&password=password&ip=213.x.x.x&mx=213.x.x.x
1203 url
= "https://dynamicdns.key-systems.net/update.php"
1204 can_remove_records
= False
1206 def update_protocol(self
, proto
):
1207 address
= self
.get_address(proto
)
1209 "hostname" : self
.hostname
,
1210 "password" : self
.password
,
1214 # Send update to the server.
1215 response
= self
.send_request(self
.url
, data
=data
)
1217 # Get the full response message.
1218 output
= response
.read().decode()
1220 # Handle success messages.
1221 if "code = 200" in output
:
1224 # Handle error messages.
1225 if "abuse prevention triggered" in output
:
1226 raise DDNSAbuseError
1227 elif "invalid password" in output
:
1228 raise DDNSAuthenticationError
1229 elif "Authorization failed" in output
:
1230 raise DDNSRequestError(_("Invalid hostname specified"))
1232 # If we got here, some other update error happened.
1233 raise DDNSUpdateError
1236 class DDNSProviderGoogle(DDNSProtocolDynDNS2
, DDNSProvider
):
1237 handle
= "domains.google.com"
1238 name
= "Google Domains"
1239 website
= "https://domains.google.com/"
1240 protocols
= ("ipv4",)
1242 # Information about the format of the HTTP request is to be found
1243 # here: https://support.google.com/domains/answer/6147083?hl=en
1245 url
= "https://domains.google.com/nic/update"
1248 class DDNSProviderLightningWireLabs(DDNSProvider
):
1249 handle
= "dns.lightningwirelabs.com"
1250 name
= "Lightning Wire Labs DNS Service"
1251 website
= "https://dns.lightningwirelabs.com/"
1253 # Information about the format of the HTTPS request is to be found
1254 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
1256 url
= "https://dns.lightningwirelabs.com/update"
1260 "hostname" : self
.hostname
,
1261 "address6" : self
.get_address("ipv6", "-"),
1262 "address4" : self
.get_address("ipv4", "-"),
1265 # Check if a token has been set.
1267 data
["token"] = self
.token
1269 # Check for username and password.
1270 elif self
.username
and self
.password
:
1272 "username" : self
.username
,
1273 "password" : self
.password
,
1276 # Raise an error if no auth details are given.
1278 raise DDNSConfigurationError
1280 # Send update to the server.
1281 response
= self
.send_request(self
.url
, data
=data
)
1283 # Handle success messages.
1284 if response
.code
== 200:
1287 # If we got here, some other update error happened.
1288 raise DDNSUpdateError
1291 class DDNSProviderLoopia(DDNSProtocolDynDNS2
, DDNSProvider
):
1292 handle
= "loopia.se"
1294 website
= "https://www.loopia.com"
1295 protocols
= ("ipv4",)
1297 # Information about the format of the HTTP request is to be found
1298 # here: https://support.loopia.com/wiki/About_the_DynDNS_support
1300 url
= "https://dns.loopia.se/XDynDNSServer/XDynDNS.php"
1303 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
1304 handle
= "myonlineportal.net"
1305 name
= "myonlineportal.net"
1306 website
= "https:/myonlineportal.net/"
1308 # Information about the request and response can be obtained here:
1309 # https://myonlineportal.net/howto_dyndns
1311 url
= "https://myonlineportal.net/updateddns"
1313 def prepare_request_data(self
, proto
):
1315 "hostname" : self
.hostname
,
1316 "ip" : self
.get_address(proto
),
1322 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
1323 handle
= "namecheap.com"
1325 website
= "http://namecheap.com"
1326 protocols
= ("ipv4",)
1328 # Information about the format of the HTTP request is to be found
1329 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
1330 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
1332 url
= "https://dynamicdns.park-your-domain.com/update"
1333 can_remove_records
= False
1335 def update_protocol(self
, proto
):
1336 # Namecheap requires the hostname splitted into a host and domain part.
1337 host
, domain
= self
.hostname
.split(".", 1)
1339 # Get and store curent IP address.
1340 address
= self
.get_address(proto
)
1344 "password" : self
.password
,
1349 # Send update to the server.
1350 response
= self
.send_request(self
.url
, data
=data
)
1352 # Get the full response message.
1353 output
= response
.read().decode()
1355 # Handle success messages.
1356 if self
.get_xml_tag_value(output
, "IP") == address
:
1359 # Handle error codes.
1360 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
1362 if errorcode
== "304156":
1363 raise DDNSAuthenticationError
1364 elif errorcode
== "316153":
1365 raise DDNSRequestError(_("Domain not found"))
1366 elif errorcode
== "316154":
1367 raise DDNSRequestError(_("Domain not active"))
1368 elif errorcode
in ("380098", "380099"):
1369 raise DDNSInternalServerError
1371 # If we got here, some other update error happened.
1372 raise DDNSUpdateError
1375 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
1376 handle
= "no-ip.com"
1378 website
= "http://www.noip.com/"
1379 protocols
= ("ipv4",)
1381 # Information about the format of the HTTP request is to be found
1382 # here: http://www.noip.com/integrate/request and
1383 # here: http://www.noip.com/integrate/response
1385 url
= "http://dynupdate.noip.com/nic/update"
1387 def prepare_request_data(self
, proto
):
1388 assert proto
== "ipv4"
1391 "hostname" : self
.hostname
,
1392 "address" : self
.get_address(proto
),
1398 class DDNSProviderNowDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1399 handle
= "now-dns.com"
1401 website
= "http://now-dns.com/"
1402 protocols
= ("ipv6", "ipv4")
1404 # Information about the format of the request is to be found
1405 # but only can be accessed by register an account and login
1406 # https://now-dns.com/?m=api
1408 url
= "https://now-dns.com/update"
1411 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
1412 handle
= "nsupdate.info"
1413 name
= "nsupdate.info"
1414 website
= "http://nsupdate.info/"
1415 protocols
= ("ipv6", "ipv4",)
1417 # Information about the format of the HTTP request can be found
1418 # after login on the provider user interface and here:
1419 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
1421 url
= "https://nsupdate.info/nic/update"
1423 # TODO nsupdate.info can actually do this, but the functionality
1424 # has not been implemented here, yet.
1425 can_remove_records
= False
1427 # After a failed update, there will be no retries
1428 # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
1429 holdoff_failure_days
= None
1431 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
1432 # and for the password a so called secret.
1435 return self
.get("hostname")
1439 return self
.token
or self
.get("secret")
1441 def prepare_request_data(self
, proto
):
1443 "myip" : self
.get_address(proto
),
1449 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1450 handle
= "opendns.com"
1452 website
= "http://www.opendns.com"
1454 # Detailed information about the update request and possible
1455 # response codes can be obtained from here:
1456 # https://support.opendns.com/entries/23891440
1458 url
= "https://updates.opendns.com/nic/update"
1460 def prepare_request_data(self
, proto
):
1462 "hostname" : self
.hostname
,
1463 "myip" : self
.get_address(proto
),
1469 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1472 website
= "http://www.ovh.com/"
1473 protocols
= ("ipv4",)
1475 # OVH only provides very limited information about how to
1476 # update a DynDNS host. They only provide the update url
1477 # on the their german subpage.
1479 # http://hilfe.ovh.de/DomainDynHost
1481 url
= "https://www.ovh.com/nic/update"
1483 def prepare_request_data(self
, proto
):
1484 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1486 "system" : "dyndns",
1492 class DDNSProviderRegfish(DDNSProvider
):
1493 handle
= "regfish.com"
1494 name
= "Regfish GmbH"
1495 website
= "http://www.regfish.com/"
1497 # A full documentation to the providers api can be found here
1498 # but is only available in german.
1499 # https://www.regfish.de/domains/dyndns/dokumentation
1501 url
= "https://dyndns.regfish.de/"
1502 can_remove_records
= False
1506 "fqdn" : self
.hostname
,
1509 # Check if we update an IPv6 address.
1510 address6
= self
.get_address("ipv6")
1512 data
["ipv6"] = address6
1514 # Check if we update an IPv4 address.
1515 address4
= self
.get_address("ipv4")
1517 data
["ipv4"] = address4
1519 # Raise an error if none address is given.
1520 if "ipv6" not in data
and "ipv4" not in data
:
1521 raise DDNSConfigurationError
1523 # Check if a token has been set.
1525 data
["token"] = self
.token
1527 # Raise an error if no token and no useranem and password
1529 elif not self
.username
and not self
.password
:
1530 raise DDNSConfigurationError(_("No Auth details specified"))
1532 # HTTP Basic Auth is only allowed if no token is used.
1534 # Send update to the server.
1535 response
= self
.send_request(self
.url
, data
=data
)
1537 # Send update to the server.
1538 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1540 # Get the full response message.
1541 output
= response
.read().decode()
1543 # Handle success messages.
1544 if "100" in output
or "101" in output
:
1547 # Handle error codes.
1548 if "401" or "402" in output
:
1549 raise DDNSAuthenticationError
1550 elif "408" in output
:
1551 raise DDNSRequestError(_("Invalid IPv4 address has been sent"))
1552 elif "409" in output
:
1553 raise DDNSRequestError(_("Invalid IPv6 address has been sent"))
1554 elif "412" in output
:
1555 raise DDNSRequestError(_("No valid FQDN was given"))
1556 elif "414" in output
:
1557 raise DDNSInternalServerError
1559 # If we got here, some other update error happened.
1560 raise DDNSUpdateError
1563 class DDNSProviderSchokokeksDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1564 handle
= "schokokeks.org"
1566 website
= "http://www.schokokeks.org/"
1567 protocols
= ("ipv4",)
1569 # Information about the format of the request is to be found
1570 # https://wiki.schokokeks.org/DynDNS
1571 url
= "https://dyndns.schokokeks.org/nic/update"
1574 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1575 handle
= "selfhost.de"
1576 name
= "Selfhost.de"
1577 website
= "http://www.selfhost.de/"
1578 protocols
= ("ipv4",)
1580 url
= "https://carol.selfhost.de/nic/update"
1582 def prepare_request_data(self
, proto
):
1583 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1591 class DDNSProviderServercow(DDNSProvider
):
1592 handle
= "servercow.de"
1593 name
= "servercow.de"
1594 website
= "https://servercow.de/"
1595 protocols
= ("ipv4", "ipv6")
1597 url
= "https://www.servercow.de/dnsupdate/update.php"
1598 can_remove_records
= False
1600 def update_protocol(self
, proto
):
1602 "ipaddr" : self
.get_address(proto
),
1603 "hostname" : self
.hostname
,
1604 "username" : self
.username
,
1605 "pass" : self
.password
,
1608 # Send request to provider
1609 response
= self
.send_request(self
.url
, data
=data
)
1612 output
= response
.read().decode()
1614 # Server responds with OK if update was successful
1615 if output
.startswith("OK"):
1619 elif output
.startswith("FAILED - Authentication failed"):
1620 raise DDNSAuthenticationError
1622 # If we got here, some other update error happened
1623 raise DDNSUpdateError(output
)
1626 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1627 handle
= "spdns.org"
1629 website
= "https://www.spdyn.de/"
1631 # Detailed information about request and response codes are provided
1632 # by the vendor. They are using almost the same mechanism and status
1633 # codes as dyndns.org so we can inherit all those stuff.
1635 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1636 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1638 url
= "https://update.spdyn.de/nic/update"
1642 return self
.get("username") or self
.hostname
1646 return self
.get("password") or self
.token
1649 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1650 handle
= "strato.com"
1652 website
= "http:/www.strato.com/"
1653 protocols
= ("ipv4",)
1655 # Information about the request and response can be obtained here:
1656 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1658 url
= "https://dyndns.strato.com/nic/update"
1660 def prepare_request_data(self
, proto
):
1661 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1664 "backupmx" : "NOCHG"
1670 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1671 handle
= "twodns.de"
1673 website
= "http://www.twodns.de"
1674 protocols
= ("ipv4",)
1676 # Detailed information about the request can be found here
1677 # http://twodns.de/en/faqs
1678 # http://twodns.de/en/api
1680 url
= "https://update.twodns.de/update"
1682 def prepare_request_data(self
, proto
):
1683 assert proto
== "ipv4"
1686 "ip" : self
.get_address(proto
),
1687 "hostname" : self
.hostname
1693 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1694 handle
= "udmedia.de"
1695 name
= "Udmedia GmbH"
1696 website
= "http://www.udmedia.de"
1697 protocols
= ("ipv4",)
1699 # Information about the request can be found here
1700 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1702 url
= "https://www.udmedia.de/nic/update"
1705 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1706 handle
= "variomedia.de"
1708 website
= "http://www.variomedia.de/"
1709 protocols
= ("ipv6", "ipv4",)
1711 # Detailed information about the request can be found here
1712 # https://dyndns.variomedia.de/
1714 url
= "https://dyndns.variomedia.de/nic/update"
1716 def prepare_request_data(self
, proto
):
1718 "hostname" : self
.hostname
,
1719 "myip" : self
.get_address(proto
),
1725 class DDNSProviderXLhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1726 handle
= "xlhost.de"
1728 website
= "http://xlhost.de/"
1729 protocols
= ("ipv4",)
1731 # Information about the format of the HTTP request is to be found
1732 # here: https://xlhost.de/faq/index_html?topicId=CQA2ELIPO4SQ
1734 url
= "https://nsupdate.xlhost.de/"
1737 class DDNSProviderZoneedit(DDNSProvider
):
1738 handle
= "zoneedit.com"
1740 website
= "http://www.zoneedit.com"
1741 protocols
= ("ipv4",)
1743 # Detailed information about the request and the response codes can be
1745 # http://www.zoneedit.com/doc/api/other.html
1746 # http://www.zoneedit.com/faq.html
1748 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1750 def update_protocol(self
, proto
):
1752 "dnsto" : self
.get_address(proto
),
1753 "host" : self
.hostname
1756 # Send update to the server.
1757 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
, data
=data
)
1759 # Get the full response message.
1760 output
= response
.read().decode()
1762 # Handle success messages.
1763 if output
.startswith("<SUCCESS"):
1766 # Handle error codes.
1767 if output
.startswith("invalid login"):
1768 raise DDNSAuthenticationError
1769 elif output
.startswith("<ERROR CODE=\"704\""):
1770 raise DDNSRequestError(_("No valid FQDN was given"))
1771 elif output
.startswith("<ERROR CODE=\"702\""):
1772 raise DDNSRequestError(_("Too frequent update requests have been sent"))
1774 # If we got here, some other update error happened.
1775 raise DDNSUpdateError
1778 class DDNSProviderDNSmadeEasy(DDNSProvider
):
1779 handle
= "dnsmadeeasy.com"
1780 name
= "DNSmadeEasy.com"
1781 website
= "http://www.dnsmadeeasy.com/"
1782 protocols
= ("ipv4",)
1784 # DNS Made Easy Nameserver Provider also offering Dynamic DNS
1785 # Documentation can be found here:
1786 # http://www.dnsmadeeasy.com/dynamic-dns/
1788 url
= "https://cp.dnsmadeeasy.com/servlet/updateip?"
1789 can_remove_records
= False
1791 def update_protocol(self
, proto
):
1793 "ip" : self
.get_address(proto
),
1794 "id" : self
.hostname
,
1795 "username" : self
.username
,
1796 "password" : self
.password
,
1799 # Send update to the server.
1800 response
= self
.send_request(self
.url
, data
=data
)
1802 # Get the full response message.
1803 output
= response
.read().decode()
1805 # Handle success messages.
1806 if output
.startswith("success") or output
.startswith("error-record-ip-same"):
1809 # Handle error codes.
1810 if output
.startswith("error-auth-suspend"):
1811 raise DDNSRequestError(_("Account has been suspended"))
1813 elif output
.startswith("error-auth-voided"):
1814 raise DDNSRequestError(_("Account has been revoked"))
1816 elif output
.startswith("error-record-invalid"):
1817 raise DDNSRequestError(_("Specified host does not exist"))
1819 elif output
.startswith("error-auth"):
1820 raise DDNSAuthenticationError
1822 # If we got here, some other update error happened.
1823 raise DDNSUpdateError(_("Server response: %s") % output
)
1826 class DDNSProviderZZZZ(DDNSProvider
):
1829 website
= "https://zzzz.io"
1830 protocols
= ("ipv6", "ipv4",)
1832 # Detailed information about the update request can be found here:
1833 # https://zzzz.io/faq/
1835 # Details about the possible response codes have been provided in the bugtracker:
1836 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1838 url
= "https://zzzz.io/api/v1/update"
1839 can_remove_records
= False
1841 def update_protocol(self
, proto
):
1843 "ip" : self
.get_address(proto
),
1844 "token" : self
.token
,
1848 data
["type"] = "aaaa"
1850 # zzzz uses the host from the full hostname as part
1851 # of the update url.
1852 host
, domain
= self
.hostname
.split(".", 1)
1854 # Add host value to the update url.
1855 url
= "%s/%s" % (self
.url
, host
)
1857 # Send update to the server.
1859 response
= self
.send_request(url
, data
=data
)
1861 # Handle error codes.
1862 except DDNSNotFound
:
1863 raise DDNSRequestError(_("Invalid hostname specified"))
1865 # Handle success messages.
1866 if response
.code
== 200:
1869 # If we got here, some other update error happened.
1870 raise DDNSUpdateError