]>
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(_("%s is already up to date") % self
.hostname
)
135 # Execute the update.
139 raise NotImplementedError
141 def is_uptodate(self
, protos
):
143 Returns True if this host is already up to date
144 and does not need to change the IP address on the
148 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
150 current_address
= self
.get_address(proto
)
152 # If no addresses for the given protocol exist, we
154 if current_address
is None and not addresses
:
157 if not current_address
in addresses
:
162 def send_request(self
, *args
, **kwargs
):
164 Proxy connection to the send request
167 return self
.core
.system
.send_request(*args
, **kwargs
)
169 def get_address(self
, proto
, default
=None):
171 Proxy method to get the current IP address.
173 return self
.core
.system
.get_address(proto
) or default
176 class DDNSProtocolDynDNS2(object):
178 This is an abstract class that implements the DynDNS updater
179 protocol version 2. As this is a popular way to update dynamic
180 DNS records, this class is supposed make the provider classes
184 # Information about the format of the request is to be found
185 # http://dyn.com/support/developers/api/perform-update/
186 # http://dyn.com/support/developers/api/return-codes/
188 def _prepare_request_data(self
):
190 "hostname" : self
.hostname
,
191 "myip" : self
.get_address("ipv4"),
197 data
= self
._prepare
_request
_data
()
199 # Send update to the server.
200 response
= self
.send_request(self
.url
, data
=data
,
201 username
=self
.username
, password
=self
.password
)
203 # Get the full response message.
204 output
= response
.read()
206 # Handle success messages.
207 if output
.startswith("good") or output
.startswith("nochg"):
210 # Handle error codes.
211 if output
== "badauth":
212 raise DDNSAuthenticationError
213 elif output
== "aduse":
215 elif output
== "notfqdn":
216 raise DDNSRequestError(_("No valid FQDN was given."))
217 elif output
== "nohost":
218 raise DDNSRequestError(_("Specified host does not exist."))
219 elif output
== "911":
220 raise DDNSInternalServerError
221 elif output
== "dnserr":
222 raise DDNSInternalServerError(_("DNS error encountered."))
224 # If we got here, some other update error happened.
225 raise DDNSUpdateError(_("Server response: %s") % output
)
228 class DDNSResponseParserXML(object):
230 This class provides a parser for XML responses which
231 will be sent by various providers. This class uses the python
232 shipped XML minidom module to walk through the XML tree and return
236 def get_xml_tag_value(self
, document
, content
):
237 # Send input to the parser.
238 xmldoc
= xml
.dom
.minidom
.parseString(document
)
240 # Get XML elements by the given content.
241 element
= xmldoc
.getElementsByTagName(content
)
243 # If no element has been found, we directly can return None.
247 # Only get the first child from an element, even there are more than one.
248 firstchild
= element
[0].firstChild
250 # Get the value of the child.
251 value
= firstchild
.nodeValue
257 class DDNSProviderAllInkl(DDNSProvider
):
258 handle
= "all-inkl.com"
259 name
= "All-inkl.com"
260 website
= "http://all-inkl.com/"
261 protocols
= ("ipv4",)
263 # There are only information provided by the vendor how to
264 # perform an update on a FRITZ Box. Grab requried informations
266 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
268 url
= "http://dyndns.kasserver.com"
271 # There is no additional data required so we directly can
273 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
)
275 # Get the full response message.
276 output
= response
.read()
278 # Handle success messages.
279 if output
.startswith("good") or output
.startswith("nochg"):
282 # If we got here, some other update error happened.
283 raise DDNSUpdateError
286 class DDNSProviderBindNsupdate(DDNSProvider
):
288 name
= "BIND nsupdate utility"
289 website
= "http://en.wikipedia.org/wiki/Nsupdate"
294 scriptlet
= self
.__make
_scriptlet
()
296 # -v enables TCP hence we transfer keys and other data that may
297 # exceed the size of one packet.
298 # -t sets the timeout
299 command
= ["nsupdate", "-v", "-t", "60"]
301 p
= subprocess
.Popen(command
, shell
=True,
302 stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
304 stdout
, stderr
= p
.communicate(scriptlet
)
306 if p
.returncode
== 0:
309 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p
.returncode
, stderr
))
311 def __make_scriptlet(self
):
314 # Set a different server the update is sent to.
315 server
= self
.get("server", None)
317 scriptlet
.append("server %s" % server
)
319 key
= self
.get("key", None)
321 secret
= self
.get("secret")
323 scriptlet
.append("key %s %s" % (key
, secret
))
325 ttl
= self
.get("ttl", self
.DEFAULT_TTL
)
327 # Perform an update for each supported protocol.
328 for rrtype
, proto
in (("AAAA", "ipv6"), ("A", "ipv4")):
329 address
= self
.get_address(proto
)
333 scriptlet
.append("update delete %s. %s" % (self
.hostname
, rrtype
))
334 scriptlet
.append("update add %s. %s %s %s" % \
335 (self
.hostname
, ttl
, rrtype
, address
))
337 # Send the actions to the server.
338 scriptlet
.append("send")
339 scriptlet
.append("quit")
341 logger
.debug(_("Scriptlet:"))
342 for line
in scriptlet
:
343 # Masquerade the line with the secret key.
344 if line
.startswith("key"):
345 line
= "key **** ****"
347 logger
.debug(" %s" % line
)
349 return "\n".join(scriptlet
)
352 class DDNSProviderDHS(DDNSProvider
):
354 name
= "DHS International"
355 website
= "http://dhs.org/"
356 protocols
= ("ipv4",)
358 # No information about the used update api provided on webpage,
359 # grabed from source code of ez-ipudate.
361 url
= "http://members.dhs.org/nic/hosts"
365 "domain" : self
.hostname
,
366 "ip" : self
.get_address("ipv4"),
368 "hostcmdstage" : "2",
372 # Send update to the server.
373 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
376 # Handle success messages.
377 if response
.code
== 200:
380 # If we got here, some other update error happened.
381 raise DDNSUpdateError
384 class DDNSProviderDNSpark(DDNSProvider
):
385 handle
= "dnspark.com"
387 website
= "http://dnspark.com/"
388 protocols
= ("ipv4",)
390 # Informations to the used api can be found here:
391 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
393 url
= "https://control.dnspark.com/api/dynamic/update.php"
397 "domain" : self
.hostname
,
398 "ip" : self
.get_address("ipv4"),
401 # Send update to the server.
402 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
405 # Get the full response message.
406 output
= response
.read()
408 # Handle success messages.
409 if output
.startswith("ok") or output
.startswith("nochange"):
412 # Handle error codes.
413 if output
== "unauth":
414 raise DDNSAuthenticationError
415 elif output
== "abuse":
417 elif output
== "blocked":
418 raise DDNSBlockedError
419 elif output
== "nofqdn":
420 raise DDNSRequestError(_("No valid FQDN was given."))
421 elif output
== "nohost":
422 raise DDNSRequestError(_("Invalid hostname specified."))
423 elif output
== "notdyn":
424 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
425 elif output
== "invalid":
426 raise DDNSRequestError(_("Invalid IP address has been sent."))
428 # If we got here, some other update error happened.
429 raise DDNSUpdateError
432 class DDNSProviderDtDNS(DDNSProvider
):
435 website
= "http://dtdns.com/"
436 protocols
= ("ipv4",)
438 # Information about the format of the HTTPS request is to be found
439 # http://www.dtdns.com/dtsite/updatespec
441 url
= "https://www.dtdns.com/api/autodns.cfm"
445 "ip" : self
.get_address("ipv4"),
446 "id" : self
.hostname
,
450 # Send update to the server.
451 response
= self
.send_request(self
.url
, data
=data
)
453 # Get the full response message.
454 output
= response
.read()
456 # Remove all leading and trailing whitespace.
457 output
= output
.strip()
459 # Handle success messages.
460 if "now points to" in output
:
463 # Handle error codes.
464 if output
== "No hostname to update was supplied.":
465 raise DDNSRequestError(_("No hostname specified."))
467 elif output
== "The hostname you supplied is not valid.":
468 raise DDNSRequestError(_("Invalid hostname specified."))
470 elif output
== "The password you supplied is not valid.":
471 raise DDNSAuthenticationError
473 elif output
== "Administration has disabled this account.":
474 raise DDNSRequestError(_("Account has been disabled."))
476 elif output
== "Illegal character in IP.":
477 raise DDNSRequestError(_("Invalid IP address has been sent."))
479 elif output
== "Too many failed requests.":
480 raise DDNSRequestError(_("Too many failed requests."))
482 # If we got here, some other update error happened.
483 raise DDNSUpdateError
486 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
487 handle
= "dyndns.org"
489 website
= "http://dyn.com/dns/"
490 protocols
= ("ipv4",)
492 # Information about the format of the request is to be found
493 # http://http://dyn.com/support/developers/api/perform-update/
494 # http://dyn.com/support/developers/api/return-codes/
496 url
= "https://members.dyndns.org/nic/update"
499 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
502 website
= "http://dynu.com/"
503 protocols
= ("ipv6", "ipv4",)
505 # Detailed information about the request and response codes
506 # are available on the providers webpage.
507 # http://dynu.com/Default.aspx?page=dnsapi
509 url
= "https://api.dynu.com/nic/update"
511 def _prepare_request_data(self
):
512 data
= DDNSProviderDynDNS
._prepare
_request
_data
(self
)
514 # This one supports IPv6
516 "myipv6" : self
.get_address("ipv6"),
522 class DDNSProviderEasyDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
523 handle
= "easydns.com"
525 website
= "http://www.easydns.com/"
526 protocols
= ("ipv4",)
528 # There is only some basic documentation provided by the vendor,
529 # also searching the web gain very poor results.
530 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
532 url
= "http://api.cp.easydns.com/dyn/tomato.php"
535 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
538 website
= "http://www.enom.com/"
540 # There are very detailed information about how to send an update request and
542 # http://www.enom.com/APICommandCatalog/
544 url
= "https://dynamic.name-services.com/interface.asp"
548 "command" : "setdnshost",
549 "responsetype" : "xml",
550 "address" : self
.get_address("ipv4"),
551 "domainpassword" : self
.password
,
552 "zone" : self
.hostname
555 # Send update to the server.
556 response
= self
.send_request(self
.url
, data
=data
)
558 # Get the full response message.
559 output
= response
.read()
561 # Handle success messages.
562 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
565 # Handle error codes.
566 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
568 if errorcode
== "304155":
569 raise DDNSAuthenticationError
570 elif errorcode
== "304153":
571 raise DDNSRequestError(_("Domain not found."))
573 # If we got here, some other update error happened.
574 raise DDNSUpdateError
577 class DDNSProviderEntryDNS(DDNSProvider
):
578 handle
= "entrydns.net"
580 website
= "http://entrydns.net/"
581 protocols
= ("ipv4",)
583 # Some very tiny details about their so called "Simple API" can be found
584 # here: https://entrydns.net/help
585 url
= "https://entrydns.net/records/modify"
589 "ip" : self
.get_address("ipv4")
592 # Add auth token to the update url.
593 url
= "%s/%s" % (self
.url
, self
.token
)
595 # Send update to the server.
597 response
= self
.send_request(url
, method
="PUT", data
=data
)
600 except urllib2
.HTTPError
, e
:
602 raise DDNSAuthenticationError
605 raise DDNSRequestError(_("An invalid IP address was submitted"))
609 # Handle success messages.
610 if response
.code
== 200:
613 # If we got here, some other update error happened.
614 raise DDNSUpdateError
617 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
618 handle
= "freedns.afraid.org"
619 name
= "freedns.afraid.org"
620 website
= "http://freedns.afraid.org/"
622 # No information about the request or response could be found on the vendor
623 # page. All used values have been collected by testing.
624 url
= "https://freedns.afraid.org/dynamic/update.php"
628 return self
.get("proto")
631 address
= self
.get_address(self
.proto
)
637 # Add auth token to the update url.
638 url
= "%s?%s" % (self
.url
, self
.token
)
640 # Send update to the server.
641 response
= self
.send_request(url
, data
=data
)
643 # Get the full response message.
644 output
= response
.read()
646 # Handle success messages.
647 if output
.startswith("Updated") or "has not changed" in output
:
650 # Handle error codes.
651 if output
== "ERROR: Unable to locate this record":
652 raise DDNSAuthenticationError
653 elif "is an invalid IP address" in output
:
654 raise DDNSRequestError(_("Invalid IP address has been sent."))
656 # If we got here, some other update error happened.
657 raise DDNSUpdateError
660 class DDNSProviderLightningWireLabs(DDNSProvider
):
661 handle
= "dns.lightningwirelabs.com"
662 name
= "Lightning Wire Labs DNS Service"
663 website
= "http://dns.lightningwirelabs.com/"
665 # Information about the format of the HTTPS request is to be found
666 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
668 url
= "https://dns.lightningwirelabs.com/update"
672 "hostname" : self
.hostname
,
673 "address6" : self
.get_address("ipv6", "-"),
674 "address4" : self
.get_address("ipv4", "-"),
677 # Check if a token has been set.
679 data
["token"] = self
.token
681 # Check for username and password.
682 elif self
.username
and self
.password
:
684 "username" : self
.username
,
685 "password" : self
.password
,
688 # Raise an error if no auth details are given.
690 raise DDNSConfigurationError
692 # Send update to the server.
693 response
= self
.send_request(self
.url
, data
=data
)
695 # Handle success messages.
696 if response
.code
== 200:
699 # If we got here, some other update error happened.
700 raise DDNSUpdateError
703 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
704 handle
= "namecheap.com"
706 website
= "http://namecheap.com"
707 protocols
= ("ipv4",)
709 # Information about the format of the HTTP request is to be found
710 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
711 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
713 url
= "https://dynamicdns.park-your-domain.com/update"
716 # Namecheap requires the hostname splitted into a host and domain part.
717 host
, domain
= self
.hostname
.split(".", 1)
720 "ip" : self
.get_address("ipv4"),
721 "password" : self
.password
,
726 # Send update to the server.
727 response
= self
.send_request(self
.url
, data
=data
)
729 # Get the full response message.
730 output
= response
.read()
732 # Handle success messages.
733 if self
.get_xml_tag_value(output
, "IP") == self
.get_address("ipv4"):
736 # Handle error codes.
737 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
739 if errorcode
== "304156":
740 raise DDNSAuthenticationError
741 elif errorcode
== "316153":
742 raise DDNSRequestError(_("Domain not found."))
743 elif errorcode
== "316154":
744 raise DDNSRequestError(_("Domain not active."))
745 elif errorcode
in ("380098", "380099"):
746 raise DDNSInternalServerError
748 # If we got here, some other update error happened.
749 raise DDNSUpdateError
752 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
755 website
= "http://www.no-ip.com/"
756 protocols
= ("ipv4",)
758 # Information about the format of the HTTP request is to be found
759 # here: http://www.no-ip.com/integrate/request and
760 # here: http://www.no-ip.com/integrate/response
762 url
= "http://dynupdate.no-ip.com/nic/update"
764 def _prepare_request_data(self
):
766 "hostname" : self
.hostname
,
767 "address" : self
.get_address("ipv4"),
773 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
774 handle
= "nsupdate.info"
775 name
= "nsupdate.info"
776 website
= "http://www.nsupdate.info/"
777 protocols
= ("ipv6", "ipv4",)
779 # Information about the format of the HTTP request can be found
780 # after login on the provider user intrface and here:
781 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
783 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
784 # and for the password a so called secret.
787 return self
.get("hostname")
791 return self
.get("secret")
795 return self
.get("proto")
799 # The update URL is different by the used protocol.
800 if self
.proto
== "ipv4":
801 return "https://ipv4.nsupdate.info/nic/update"
802 elif self
.proto
== "ipv6":
803 return "https://ipv6.nsupdate.info/nic/update"
805 raise DDNSUpdateError(_("Invalid protocol has been given"))
807 def _prepare_request_data(self
):
809 "myip" : self
.get_address(self
.proto
),
815 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
818 website
= "http://www.ovh.com/"
819 protocols
= ("ipv4",)
821 # OVH only provides very limited information about how to
822 # update a DynDNS host. They only provide the update url
823 # on the their german subpage.
825 # http://hilfe.ovh.de/DomainDynHost
827 url
= "https://www.ovh.com/nic/update"
829 def _prepare_request_data(self
):
830 data
= DDNSProtocolDynDNS2
._prepare_request_data(self
)
838 class DDNSProviderRegfish(DDNSProvider
):
839 handle
= "regfish.com"
840 name
= "Regfish GmbH"
841 website
= "http://www.regfish.com/"
843 # A full documentation to the providers api can be found here
844 # but is only available in german.
845 # https://www.regfish.de/domains/dyndns/dokumentation
847 url
= "https://dyndns.regfish.de/"
851 "fqdn" : self
.hostname
,
854 # Check if we update an IPv6 address.
855 address6
= self
.get_address("ipv6")
857 data
["ipv6"] = address6
859 # Check if we update an IPv4 address.
860 address4
= self
.get_address("ipv4")
862 data
["ipv4"] = address4
864 # Raise an error if none address is given.
865 if not data
.has_key("ipv6") and not data
.has_key("ipv4"):
866 raise DDNSConfigurationError
868 # Check if a token has been set.
870 data
["token"] = self
.token
872 # Raise an error if no token and no useranem and password
874 elif not self
.username
and not self
.password
:
875 raise DDNSConfigurationError(_("No Auth details specified."))
877 # HTTP Basic Auth is only allowed if no token is used.
879 # Send update to the server.
880 response
= self
.send_request(self
.url
, data
=data
)
882 # Send update to the server.
883 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
886 # Get the full response message.
887 output
= response
.read()
889 # Handle success messages.
890 if "100" in output
or "101" in output
:
893 # Handle error codes.
894 if "401" or "402" in output
:
895 raise DDNSAuthenticationError
896 elif "408" in output
:
897 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
898 elif "409" in output
:
899 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
900 elif "412" in output
:
901 raise DDNSRequestError(_("No valid FQDN was given."))
902 elif "414" in output
:
903 raise DDNSInternalServerError
905 # If we got here, some other update error happened.
906 raise DDNSUpdateError
909 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
910 handle
= "selfhost.de"
912 website
= "http://www.selfhost.de/"
913 protocols
= ("ipv4",)
915 url
= "https://carol.selfhost.de/nic/update"
917 def _prepare_request_data(self
):
918 data
= DDNSProviderDynDNS
._prepare
_request
_data
(self
)
926 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
929 website
= "http://spdns.org/"
930 protocols
= ("ipv4",)
932 # Detailed information about request and response codes are provided
933 # by the vendor. They are using almost the same mechanism and status
934 # codes as dyndns.org so we can inherit all those stuff.
936 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
937 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
939 url
= "https://update.spdns.de/nic/update"
942 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
943 handle
= "strato.com"
945 website
= "http:/www.strato.com/"
946 protocols
= ("ipv4",)
948 # Information about the request and response can be obtained here:
949 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
951 url
= "https://dyndns.strato.com/nic/update"
954 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
957 website
= "http://www.twodns.de"
958 protocols
= ("ipv4",)
960 # Detailed information about the request can be found here
961 # http://twodns.de/en/faqs
962 # http://twodns.de/en/api
964 url
= "https://update.twodns.de/update"
966 def _prepare_request_data(self
):
968 "ip" : self
.get_address("ipv4"),
969 "hostname" : self
.hostname
975 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
976 handle
= "udmedia.de"
977 name
= "Udmedia GmbH"
978 website
= "http://www.udmedia.de"
979 protocols
= ("ipv4",)
981 # Information about the request can be found here
982 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
984 url
= "https://www.udmedia.de/nic/update"
987 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
988 handle
= "variomedia.de"
990 website
= "http://www.variomedia.de/"
991 protocols
= ("ipv6", "ipv4",)
993 # Detailed information about the request can be found here
994 # https://dyndns.variomedia.de/
996 url
= "https://dyndns.variomedia.de/nic/update"
1000 return self
.get("proto")
1002 def _prepare_request_data(self
):
1004 "hostname" : self
.hostname
,
1005 "myip" : self
.get_address(self
.proto
)
1011 class DDNSProviderZoneedit(DDNSProtocolDynDNS2
, DDNSProvider
):
1012 handle
= "zoneedit.com"
1014 website
= "http://www.zoneedit.com"
1015 protocols
= ("ipv4",)
1017 # Detailed information about the request and the response codes can be
1019 # http://www.zoneedit.com/doc/api/other.html
1020 # http://www.zoneedit.com/faq.html
1022 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1026 return self
.get("proto")
1030 "dnsto" : self
.get_address(self
.proto
),
1031 "host" : self
.hostname
1034 # Send update to the server.
1035 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
1038 # Get the full response message.
1039 output
= response
.read()
1041 # Handle success messages.
1042 if output
.startswith("<SUCCESS"):
1045 # Handle error codes.
1046 if output
.startswith("invalid login"):
1047 raise DDNSAuthenticationError
1048 elif output
.startswith("<ERROR CODE=\"704\""):
1049 raise DDNSRequestError(_("No valid FQDN was given."))
1050 elif output
.startswith("<ERROR CODE=\"702\""):
1051 raise DDNSInternalServerError
1053 # If we got here, some other update error happened.
1054 raise DDNSUpdateError