]>
git.ipfire.org Git - oddments/ddns.git/blob - src/ddns/providers.py
2 ###############################################################################
4 # ddns - A dynamic DNS client for IPFire #
5 # Copyright (C) 2012 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 ###############################################################################
25 import xml
.dom
.minidom
29 # Import all possible exception types.
32 logger
= logging
.getLogger("ddns.providers")
39 Returns a dict with all automatically registered providers.
41 return _providers
.copy()
43 class DDNSProvider(object):
44 # A short string that uniquely identifies
48 # The full name of the provider.
51 # A weburl to the homepage of the provider.
52 # (Where to register a new account?)
55 # A list of supported protocols.
56 protocols
= ("ipv6", "ipv4")
60 # Automatically register all providers.
61 class __metaclass__(type):
62 def __init__(provider
, name
, bases
, dict):
63 type.__init
__(provider
, name
, bases
, dict)
65 # The main class from which is inherited is not registered
67 if name
== "DDNSProvider":
70 if not all((provider
.handle
, provider
.name
, provider
.website
)):
71 raise DDNSError(_("Provider is not properly configured"))
73 assert not _providers
.has_key(provider
.handle
), \
74 "Provider '%s' has already been registered" % provider
.handle
76 _providers
[provider
.handle
] = provider
78 def __init__(self
, core
, **settings
):
81 # Copy a set of default settings and
82 # update them by those from the configuration file.
83 self
.settings
= self
.DEFAULT_SETTINGS
.copy()
84 self
.settings
.update(settings
)
87 return "<DDNS Provider %s (%s)>" % (self
.name
, self
.handle
)
89 def __cmp__(self
, other
):
90 return cmp(self
.hostname
, other
.hostname
)
92 def get(self
, key
, default
=None):
94 Get a setting from the settings dictionary.
96 return self
.settings
.get(key
, default
)
101 Fast access to the hostname.
103 return self
.get("hostname")
108 Fast access to the username.
110 return self
.get("username")
115 Fast access to the password.
117 return self
.get("password")
122 Fast access to the token.
124 return self
.get("token")
126 def __call__(self
, force
=False):
128 logger
.debug(_("Updating %s forced") % self
.hostname
)
130 # Check if we actually need to update this host.
131 elif self
.is_uptodate(self
.protocols
):
132 logger
.debug(_("The dynamic host %(hostname)s (%(provider)s) is already up to date") % \
133 { "hostname" : self
.hostname
, "provider" : self
.name
})
136 # Execute the update.
139 logger
.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") % \
140 { "hostname" : self
.hostname
, "provider" : self
.name
})
143 for protocol
in self
.protocols
:
144 if self
.have_address(protocol
):
145 self
.update_protocol(protocol
)
147 self
.remove_protocol(protocol
)
149 def update_protocol(self
, proto
):
150 raise NotImplementedError
152 def remove_protocol(self
, proto
):
153 logger
.warning(_("%(hostname)s current resolves to an IP address"
154 " of the %(proto)s protocol which could not be removed by ddns") % \
155 { "hostname" : self
.hostname
, "proto" : proto
})
157 # Maybe this will raise NotImplementedError at some time
158 #raise NotImplementedError
160 def is_uptodate(self
, protos
):
162 Returns True if this host is already up to date
163 and does not need to change the IP address on the
167 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
169 current_address
= self
.get_address(proto
)
171 # If no addresses for the given protocol exist, we
173 if current_address
is None and not addresses
:
176 if not current_address
in addresses
:
181 def send_request(self
, *args
, **kwargs
):
183 Proxy connection to the send request
186 return self
.core
.system
.send_request(*args
, **kwargs
)
188 def get_address(self
, proto
, default
=None):
190 Proxy method to get the current IP address.
192 return self
.core
.system
.get_address(proto
) or default
194 def have_address(self
, proto
):
196 Returns True if an IP address for the given protocol
199 address
= self
.get_address(proto
)
207 class DDNSProtocolDynDNS2(object):
209 This is an abstract class that implements the DynDNS updater
210 protocol version 2. As this is a popular way to update dynamic
211 DNS records, this class is supposed make the provider classes
215 # Information about the format of the request is to be found
216 # http://dyn.com/support/developers/api/perform-update/
217 # http://dyn.com/support/developers/api/return-codes/
219 def prepare_request_data(self
, proto
):
221 "hostname" : self
.hostname
,
222 "myip" : self
.get_address(proto
),
227 def update_protocol(self
, proto
):
228 data
= self
.prepare_request_data(proto
)
230 return self
.send_request(data
)
232 def send_request(self
, data
):
233 # Send update to the server.
234 response
= DDNSProvider
.send_request(self
, self
.url
, data
=data
,
235 username
=self
.username
, password
=self
.password
)
237 # Get the full response message.
238 output
= response
.read()
240 # Handle success messages.
241 if output
.startswith("good") or output
.startswith("nochg"):
244 # Handle error codes.
245 if output
== "badauth":
246 raise DDNSAuthenticationError
247 elif output
== "abuse":
249 elif output
== "notfqdn":
250 raise DDNSRequestError(_("No valid FQDN was given."))
251 elif output
== "nohost":
252 raise DDNSRequestError(_("Specified host does not exist."))
253 elif output
== "911":
254 raise DDNSInternalServerError
255 elif output
== "dnserr":
256 raise DDNSInternalServerError(_("DNS error encountered."))
257 elif output
== "badagent":
258 raise DDNSBlockedError
260 # If we got here, some other update error happened.
261 raise DDNSUpdateError(_("Server response: %s") % output
)
264 class DDNSResponseParserXML(object):
266 This class provides a parser for XML responses which
267 will be sent by various providers. This class uses the python
268 shipped XML minidom module to walk through the XML tree and return
272 def get_xml_tag_value(self
, document
, content
):
273 # Send input to the parser.
274 xmldoc
= xml
.dom
.minidom
.parseString(document
)
276 # Get XML elements by the given content.
277 element
= xmldoc
.getElementsByTagName(content
)
279 # If no element has been found, we directly can return None.
283 # Only get the first child from an element, even there are more than one.
284 firstchild
= element
[0].firstChild
286 # Get the value of the child.
287 value
= firstchild
.nodeValue
293 class DDNSProviderAllInkl(DDNSProvider
):
294 handle
= "all-inkl.com"
295 name
= "All-inkl.com"
296 website
= "http://all-inkl.com/"
297 protocols
= ("ipv4",)
299 # There are only information provided by the vendor how to
300 # perform an update on a FRITZ Box. Grab requried informations
302 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
304 url
= "http://dyndns.kasserver.com"
307 # There is no additional data required so we directly can
309 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
)
311 # Get the full response message.
312 output
= response
.read()
314 # Handle success messages.
315 if output
.startswith("good") or output
.startswith("nochg"):
318 # If we got here, some other update error happened.
319 raise DDNSUpdateError
322 class DDNSProviderBindNsupdate(DDNSProvider
):
324 name
= "BIND nsupdate utility"
325 website
= "http://en.wikipedia.org/wiki/Nsupdate"
330 scriptlet
= self
.__make
_scriptlet
()
332 # -v enables TCP hence we transfer keys and other data that may
333 # exceed the size of one packet.
334 # -t sets the timeout
335 command
= ["nsupdate", "-v", "-t", "60"]
337 p
= subprocess
.Popen(command
, shell
=True,
338 stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
340 stdout
, stderr
= p
.communicate(scriptlet
)
342 if p
.returncode
== 0:
345 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p
.returncode
, stderr
))
347 def __make_scriptlet(self
):
350 # Set a different server the update is sent to.
351 server
= self
.get("server", None)
353 scriptlet
.append("server %s" % server
)
355 # Set the DNS zone the host should be added to.
356 zone
= self
.get("zone", None)
358 scriptlet
.append("zone %s" % zone
)
360 key
= self
.get("key", None)
362 secret
= self
.get("secret")
364 scriptlet
.append("key %s %s" % (key
, secret
))
366 ttl
= self
.get("ttl", self
.DEFAULT_TTL
)
368 # Perform an update for each supported protocol.
369 for rrtype
, proto
in (("AAAA", "ipv6"), ("A", "ipv4")):
370 address
= self
.get_address(proto
)
374 scriptlet
.append("update delete %s. %s" % (self
.hostname
, rrtype
))
375 scriptlet
.append("update add %s. %s %s %s" % \
376 (self
.hostname
, ttl
, rrtype
, address
))
378 # Send the actions to the server.
379 scriptlet
.append("send")
380 scriptlet
.append("quit")
382 logger
.debug(_("Scriptlet:"))
383 for line
in scriptlet
:
384 # Masquerade the line with the secret key.
385 if line
.startswith("key"):
386 line
= "key **** ****"
388 logger
.debug(" %s" % line
)
390 return "\n".join(scriptlet
)
393 class DDNSProviderDHS(DDNSProvider
):
395 name
= "DHS International"
396 website
= "http://dhs.org/"
397 protocols
= ("ipv4",)
399 # No information about the used update api provided on webpage,
400 # grabed from source code of ez-ipudate.
402 url
= "http://members.dhs.org/nic/hosts"
404 def update_protocol(self
, proto
):
406 "domain" : self
.hostname
,
407 "ip" : self
.get_address(proto
),
409 "hostcmdstage" : "2",
413 # Send update to the server.
414 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
417 # Handle success messages.
418 if response
.code
== 200:
421 # If we got here, some other update error happened.
422 raise DDNSUpdateError
425 class DDNSProviderDNSpark(DDNSProvider
):
426 handle
= "dnspark.com"
428 website
= "http://dnspark.com/"
429 protocols
= ("ipv4",)
431 # Informations to the used api can be found here:
432 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
434 url
= "https://control.dnspark.com/api/dynamic/update.php"
436 def update_protocol(self
, proto
):
438 "domain" : self
.hostname
,
439 "ip" : self
.get_address(proto
),
442 # Send update to the server.
443 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
446 # Get the full response message.
447 output
= response
.read()
449 # Handle success messages.
450 if output
.startswith("ok") or output
.startswith("nochange"):
453 # Handle error codes.
454 if output
== "unauth":
455 raise DDNSAuthenticationError
456 elif output
== "abuse":
458 elif output
== "blocked":
459 raise DDNSBlockedError
460 elif output
== "nofqdn":
461 raise DDNSRequestError(_("No valid FQDN was given."))
462 elif output
== "nohost":
463 raise DDNSRequestError(_("Invalid hostname specified."))
464 elif output
== "notdyn":
465 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
466 elif output
== "invalid":
467 raise DDNSRequestError(_("Invalid IP address has been sent."))
469 # If we got here, some other update error happened.
470 raise DDNSUpdateError
473 class DDNSProviderDtDNS(DDNSProvider
):
476 website
= "http://dtdns.com/"
477 protocols
= ("ipv4",)
479 # Information about the format of the HTTPS request is to be found
480 # http://www.dtdns.com/dtsite/updatespec
482 url
= "https://www.dtdns.com/api/autodns.cfm"
484 def update_protocol(self
, proto
):
486 "ip" : self
.get_address(proto
),
487 "id" : self
.hostname
,
491 # Send update to the server.
492 response
= self
.send_request(self
.url
, data
=data
)
494 # Get the full response message.
495 output
= response
.read()
497 # Remove all leading and trailing whitespace.
498 output
= output
.strip()
500 # Handle success messages.
501 if "now points to" in output
:
504 # Handle error codes.
505 if output
== "No hostname to update was supplied.":
506 raise DDNSRequestError(_("No hostname specified."))
508 elif output
== "The hostname you supplied is not valid.":
509 raise DDNSRequestError(_("Invalid hostname specified."))
511 elif output
== "The password you supplied is not valid.":
512 raise DDNSAuthenticationError
514 elif output
== "Administration has disabled this account.":
515 raise DDNSRequestError(_("Account has been disabled."))
517 elif output
== "Illegal character in IP.":
518 raise DDNSRequestError(_("Invalid IP address has been sent."))
520 elif output
== "Too many failed requests.":
521 raise DDNSRequestError(_("Too many failed requests."))
523 # If we got here, some other update error happened.
524 raise DDNSUpdateError
527 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
528 handle
= "dyndns.org"
530 website
= "http://dyn.com/dns/"
531 protocols
= ("ipv4",)
533 # Information about the format of the request is to be found
534 # http://http://dyn.com/support/developers/api/perform-update/
535 # http://dyn.com/support/developers/api/return-codes/
537 url
= "https://members.dyndns.org/nic/update"
540 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
543 website
= "http://dynu.com/"
544 protocols
= ("ipv6", "ipv4",)
546 # Detailed information about the request and response codes
547 # are available on the providers webpage.
548 # http://dynu.com/Default.aspx?page=dnsapi
550 url
= "https://api.dynu.com/nic/update"
552 # DynU sends the IPv6 and IPv4 address in one request
555 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
557 # This one supports IPv6
558 myipv6
= self
.get_address("ipv6")
560 # Add update information if we have an IPv6 address.
562 data
["myipv6"] = myipv6
564 self
._send
_request
(data
)
567 class DDNSProviderEasyDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
568 handle
= "easydns.com"
570 website
= "http://www.easydns.com/"
571 protocols
= ("ipv4",)
573 # There is only some basic documentation provided by the vendor,
574 # also searching the web gain very poor results.
575 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
577 url
= "http://api.cp.easydns.com/dyn/tomato.php"
580 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
581 handle
= "domopoli.de"
583 website
= "http://domopoli.de/"
584 protocols
= ("ipv4",)
586 # https://www.domopoli.de/?page=howto#DynDns_start
588 url
= "http://dyndns.domopoli.de/nic/update"
591 class DDNSProviderDynsNet(DDNSProvider
):
594 website
= "http://www.dyns.net/"
595 protocols
= ("ipv4",)
597 # There is very detailed informatio about how to send the update request and
598 # the possible response codes. (Currently we are using the v1.1 proto)
599 # http://www.dyns.net/documentation/technical/protocol/
601 url
= "http://www.dyns.net/postscript011.php"
603 def update_protocol(self
, proto
):
605 "ip" : self
.get_address(proto
),
606 "host" : self
.hostname
,
607 "username" : self
.username
,
608 "password" : self
.password
,
611 # Send update to the server.
612 response
= self
.send_request(self
.url
, data
=data
)
614 # Get the full response message.
615 output
= response
.read()
617 # Handle success messages.
618 if output
.startswith("200"):
621 # Handle error codes.
622 if output
.startswith("400"):
623 raise DDNSRequestError(_("Malformed request has been sent."))
624 elif output
.startswith("401"):
625 raise DDNSAuthenticationError
626 elif output
.startswith("402"):
627 raise DDNSRequestError(_("Too frequent update requests have been sent."))
628 elif output
.startswith("403"):
629 raise DDNSInternalServerError
631 # If we got here, some other update error happened.
632 raise DDNSUpdateError(_("Server response: %s") % output
)
635 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
638 website
= "http://www.enom.com/"
639 protocols
= ("ipv4",)
641 # There are very detailed information about how to send an update request and
643 # http://www.enom.com/APICommandCatalog/
645 url
= "https://dynamic.name-services.com/interface.asp"
647 def update_protocol(self
, proto
):
649 "command" : "setdnshost",
650 "responsetype" : "xml",
651 "address" : self
.get_address(proto
),
652 "domainpassword" : self
.password
,
653 "zone" : self
.hostname
656 # Send update to the server.
657 response
= self
.send_request(self
.url
, data
=data
)
659 # Get the full response message.
660 output
= response
.read()
662 # Handle success messages.
663 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
666 # Handle error codes.
667 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
669 if errorcode
== "304155":
670 raise DDNSAuthenticationError
671 elif errorcode
== "304153":
672 raise DDNSRequestError(_("Domain not found."))
674 # If we got here, some other update error happened.
675 raise DDNSUpdateError
678 class DDNSProviderEntryDNS(DDNSProvider
):
679 handle
= "entrydns.net"
681 website
= "http://entrydns.net/"
682 protocols
= ("ipv4",)
684 # Some very tiny details about their so called "Simple API" can be found
685 # here: https://entrydns.net/help
686 url
= "https://entrydns.net/records/modify"
688 def update_protocol(self
, proto
):
690 "ip" : self
.get_address(proto
),
693 # Add auth token to the update url.
694 url
= "%s/%s" % (self
.url
, self
.token
)
696 # Send update to the server.
698 response
= self
.send_request(url
, data
=data
)
701 except urllib2
.HTTPError
, e
:
703 raise DDNSAuthenticationError
706 raise DDNSRequestError(_("An invalid IP address was submitted"))
710 # Handle success messages.
711 if response
.code
== 200:
714 # If we got here, some other update error happened.
715 raise DDNSUpdateError
718 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
719 handle
= "freedns.afraid.org"
720 name
= "freedns.afraid.org"
721 website
= "http://freedns.afraid.org/"
723 # No information about the request or response could be found on the vendor
724 # page. All used values have been collected by testing.
725 url
= "https://freedns.afraid.org/dynamic/update.php"
727 def update_protocol(self
, proto
):
729 "address" : self
.get_address(proto
),
732 # Add auth token to the update url.
733 url
= "%s?%s" % (self
.url
, self
.token
)
735 # Send update to the server.
736 response
= self
.send_request(url
, data
=data
)
738 # Get the full response message.
739 output
= response
.read()
741 # Handle success messages.
742 if output
.startswith("Updated") or "has not changed" in output
:
745 # Handle error codes.
746 if output
== "ERROR: Unable to locate this record":
747 raise DDNSAuthenticationError
748 elif "is an invalid IP address" in output
:
749 raise DDNSRequestError(_("Invalid IP address has been sent."))
751 # If we got here, some other update error happened.
752 raise DDNSUpdateError
755 class DDNSProviderLightningWireLabs(DDNSProvider
):
756 handle
= "dns.lightningwirelabs.com"
757 name
= "Lightning Wire Labs DNS Service"
758 website
= "http://dns.lightningwirelabs.com/"
760 # Information about the format of the HTTPS request is to be found
761 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
763 url
= "https://dns.lightningwirelabs.com/update"
767 "hostname" : self
.hostname
,
768 "address6" : self
.get_address("ipv6", "-"),
769 "address4" : self
.get_address("ipv4", "-"),
772 # Check if a token has been set.
774 data
["token"] = self
.token
776 # Check for username and password.
777 elif self
.username
and self
.password
:
779 "username" : self
.username
,
780 "password" : self
.password
,
783 # Raise an error if no auth details are given.
785 raise DDNSConfigurationError
787 # Send update to the server.
788 response
= self
.send_request(self
.url
, data
=data
)
790 # Handle success messages.
791 if response
.code
== 200:
794 # If we got here, some other update error happened.
795 raise DDNSUpdateError
798 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
799 handle
= "myonlineportal.net"
800 name
= "myonlineportal.net"
801 website
= "https:/myonlineportal.net/"
803 # Information about the request and response can be obtained here:
804 # https://myonlineportal.net/howto_dyndns
806 url
= "https://myonlineportal.net/updateddns"
808 def prepare_request_data(self
, proto
):
810 "hostname" : self
.hostname
,
811 "ip" : self
.get_address(proto
),
817 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
818 handle
= "namecheap.com"
820 website
= "http://namecheap.com"
821 protocols
= ("ipv4",)
823 # Information about the format of the HTTP request is to be found
824 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
825 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
827 url
= "https://dynamicdns.park-your-domain.com/update"
829 def update_protocol(self
, proto
):
830 # Namecheap requires the hostname splitted into a host and domain part.
831 host
, domain
= self
.hostname
.split(".", 1)
834 "ip" : self
.get_address(proto
),
835 "password" : self
.password
,
840 # Send update to the server.
841 response
= self
.send_request(self
.url
, data
=data
)
843 # Get the full response message.
844 output
= response
.read()
846 # Handle success messages.
847 if self
.get_xml_tag_value(output
, "IP") == address
:
850 # Handle error codes.
851 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
853 if errorcode
== "304156":
854 raise DDNSAuthenticationError
855 elif errorcode
== "316153":
856 raise DDNSRequestError(_("Domain not found."))
857 elif errorcode
== "316154":
858 raise DDNSRequestError(_("Domain not active."))
859 elif errorcode
in ("380098", "380099"):
860 raise DDNSInternalServerError
862 # If we got here, some other update error happened.
863 raise DDNSUpdateError
866 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
869 website
= "http://www.no-ip.com/"
870 protocols
= ("ipv4",)
872 # Information about the format of the HTTP request is to be found
873 # here: http://www.no-ip.com/integrate/request and
874 # here: http://www.no-ip.com/integrate/response
876 url
= "http://dynupdate.no-ip.com/nic/update"
878 def prepare_request_data(self
, proto
):
879 assert proto
== "ipv4"
882 "hostname" : self
.hostname
,
883 "address" : self
.get_address(proto
),
889 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
890 handle
= "nsupdate.info"
891 name
= "nsupdate.info"
892 website
= "http://nsupdate.info/"
893 protocols
= ("ipv6", "ipv4",)
895 # Information about the format of the HTTP request can be found
896 # after login on the provider user interface and here:
897 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
899 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
900 # and for the password a so called secret.
903 return self
.get("hostname")
907 return self
.token
or self
.get("secret")
911 # The update URL is different by the used protocol.
912 if self
.proto
== "ipv4":
913 return "https://ipv4.nsupdate.info/nic/update"
914 elif self
.proto
== "ipv6":
915 return "https://ipv6.nsupdate.info/nic/update"
917 raise DDNSUpdateError(_("Invalid protocol has been given"))
919 def prepare_request_data(self
, proto
):
921 "myip" : self
.get_address(proto
),
927 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
928 handle
= "opendns.com"
930 website
= "http://www.opendns.com"
932 # Detailed information about the update request and possible
933 # response codes can be obtained from here:
934 # https://support.opendns.com/entries/23891440
936 url
= "https://updates.opendns.com/nic/update"
938 def prepare_request_data(self
, proto
):
940 "hostname" : self
.hostname
,
941 "myip" : self
.get_address(proto
),
947 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
950 website
= "http://www.ovh.com/"
951 protocols
= ("ipv4",)
953 # OVH only provides very limited information about how to
954 # update a DynDNS host. They only provide the update url
955 # on the their german subpage.
957 # http://hilfe.ovh.de/DomainDynHost
959 url
= "https://www.ovh.com/nic/update"
961 def prepare_request_data(self
, proto
):
962 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
970 class DDNSProviderRegfish(DDNSProvider
):
971 handle
= "regfish.com"
972 name
= "Regfish GmbH"
973 website
= "http://www.regfish.com/"
975 # A full documentation to the providers api can be found here
976 # but is only available in german.
977 # https://www.regfish.de/domains/dyndns/dokumentation
979 url
= "https://dyndns.regfish.de/"
983 "fqdn" : self
.hostname
,
986 # Check if we update an IPv6 address.
987 address6
= self
.get_address("ipv6")
989 data
["ipv6"] = address6
991 # Check if we update an IPv4 address.
992 address4
= self
.get_address("ipv4")
994 data
["ipv4"] = address4
996 # Raise an error if none address is given.
997 if not data
.has_key("ipv6") and not data
.has_key("ipv4"):
998 raise DDNSConfigurationError
1000 # Check if a token has been set.
1002 data
["token"] = self
.token
1004 # Raise an error if no token and no useranem and password
1006 elif not self
.username
and not self
.password
:
1007 raise DDNSConfigurationError(_("No Auth details specified."))
1009 # HTTP Basic Auth is only allowed if no token is used.
1011 # Send update to the server.
1012 response
= self
.send_request(self
.url
, data
=data
)
1014 # Send update to the server.
1015 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
1018 # Get the full response message.
1019 output
= response
.read()
1021 # Handle success messages.
1022 if "100" in output
or "101" in output
:
1025 # Handle error codes.
1026 if "401" or "402" in output
:
1027 raise DDNSAuthenticationError
1028 elif "408" in output
:
1029 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
1030 elif "409" in output
:
1031 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
1032 elif "412" in output
:
1033 raise DDNSRequestError(_("No valid FQDN was given."))
1034 elif "414" in output
:
1035 raise DDNSInternalServerError
1037 # If we got here, some other update error happened.
1038 raise DDNSUpdateError
1041 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1042 handle
= "selfhost.de"
1043 name
= "Selfhost.de"
1044 website
= "http://www.selfhost.de/"
1045 protocols
= ("ipv4",)
1047 url
= "https://carol.selfhost.de/nic/update"
1049 def prepare_request_data(self
, proto
):
1050 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1058 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1059 handle
= "spdns.org"
1061 website
= "http://spdns.org/"
1063 # Detailed information about request and response codes are provided
1064 # by the vendor. They are using almost the same mechanism and status
1065 # codes as dyndns.org so we can inherit all those stuff.
1067 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1068 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1070 url
= "https://update.spdns.de/nic/update"
1074 return self
.get("username") or self
.hostname
1078 return self
.get("username") or self
.token
1081 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1082 handle
= "strato.com"
1084 website
= "http:/www.strato.com/"
1085 protocols
= ("ipv4",)
1087 # Information about the request and response can be obtained here:
1088 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1090 url
= "https://dyndns.strato.com/nic/update"
1093 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1094 handle
= "twodns.de"
1096 website
= "http://www.twodns.de"
1097 protocols
= ("ipv4",)
1099 # Detailed information about the request can be found here
1100 # http://twodns.de/en/faqs
1101 # http://twodns.de/en/api
1103 url
= "https://update.twodns.de/update"
1105 def prepare_request_data(self
, proto
):
1106 assert proto
== "ipv4"
1109 "ip" : self
.get_address(proto
),
1110 "hostname" : self
.hostname
1116 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1117 handle
= "udmedia.de"
1118 name
= "Udmedia GmbH"
1119 website
= "http://www.udmedia.de"
1120 protocols
= ("ipv4",)
1122 # Information about the request can be found here
1123 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1125 url
= "https://www.udmedia.de/nic/update"
1128 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1129 handle
= "variomedia.de"
1131 website
= "http://www.variomedia.de/"
1132 protocols
= ("ipv6", "ipv4",)
1134 # Detailed information about the request can be found here
1135 # https://dyndns.variomedia.de/
1137 url
= "https://dyndns.variomedia.de/nic/update"
1139 def prepare_request_data(self
, proto
):
1141 "hostname" : self
.hostname
,
1142 "myip" : self
.get_address(proto
),
1148 class DDNSProviderZoneedit(DDNSProtocolDynDNS2
, DDNSProvider
):
1149 handle
= "zoneedit.com"
1151 website
= "http://www.zoneedit.com"
1152 protocols
= ("ipv4",)
1154 # Detailed information about the request and the response codes can be
1156 # http://www.zoneedit.com/doc/api/other.html
1157 # http://www.zoneedit.com/faq.html
1159 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1161 def update_protocol(self
, proto
):
1163 "dnsto" : self
.get_address(proto
),
1164 "host" : self
.hostname
1167 # Send update to the server.
1168 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
1171 # Get the full response message.
1172 output
= response
.read()
1174 # Handle success messages.
1175 if output
.startswith("<SUCCESS"):
1178 # Handle error codes.
1179 if output
.startswith("invalid login"):
1180 raise DDNSAuthenticationError
1181 elif output
.startswith("<ERROR CODE=\"704\""):
1182 raise DDNSRequestError(_("No valid FQDN was given."))
1183 elif output
.startswith("<ERROR CODE=\"702\""):
1184 raise DDNSInternalServerError
1186 # If we got here, some other update error happened.
1187 raise DDNSUpdateError
1190 class DDNSProviderZZZZ(DDNSProvider
):
1193 website
= "https://zzzz.io"
1194 protocols
= ("ipv6", "ipv4",)
1196 # Detailed information about the update request can be found here:
1197 # https://zzzz.io/faq/
1199 # Details about the possible response codes have been provided in the bugtracker:
1200 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1202 url
= "https://zzzz.io/api/v1/update"
1204 def update_protocol(self
, proto
):
1206 "ip" : self
.get_address(proto
),
1207 "token" : self
.token
,
1211 data
["type"] = "aaaa"
1213 # zzzz uses the host from the full hostname as part
1214 # of the update url.
1215 host
, domain
= self
.hostname
.split(".", 1)
1217 # Add host value to the update url.
1218 url
= "%s/%s" % (self
.url
, host
)
1220 # Send update to the server.
1222 response
= self
.send_request(url
, data
=data
)
1224 # Handle error codes.
1225 except DDNSNotFound
:
1226 raise DDNSRequestError(_("Invalid hostname specified"))
1228 # Handle success messages.
1229 if response
.code
== 200:
1232 # If we got here, some other update error happened.
1233 raise DDNSUpdateError