]> git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
bc16c4c849913dfb75b4f709fe6c1b2540ba3d0e
[ddns.git] / src / ddns / providers.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # ddns - A dynamic DNS client for IPFire #
5 # Copyright (C) 2012 IPFire development team #
6 # #
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. #
11 # #
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. #
16 # #
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/>. #
19 # #
20 ###############################################################################
21
22 import logging
23 import subprocess
24 import urllib2
25 import xml.dom.minidom
26
27 from i18n import _
28
29 # Import all possible exception types.
30 from .errors import *
31
32 logger = logging.getLogger("ddns.providers")
33 logger.propagate = 1
34
35 _providers = {}
36
37 def get():
38 """
39 Returns a dict with all automatically registered providers.
40 """
41 return _providers.copy()
42
43 class DDNSProvider(object):
44 # A short string that uniquely identifies
45 # this provider.
46 handle = None
47
48 # The full name of the provider.
49 name = None
50
51 # A weburl to the homepage of the provider.
52 # (Where to register a new account?)
53 website = None
54
55 # A list of supported protocols.
56 protocols = ("ipv6", "ipv4")
57
58 DEFAULT_SETTINGS = {}
59
60 # Automatically register all providers.
61 class __metaclass__(type):
62 def __init__(provider, name, bases, dict):
63 type.__init__(provider, name, bases, dict)
64
65 # The main class from which is inherited is not registered
66 # as a provider.
67 if name == "DDNSProvider":
68 return
69
70 if not all((provider.handle, provider.name, provider.website)):
71 raise DDNSError(_("Provider is not properly configured"))
72
73 assert not _providers.has_key(provider.handle), \
74 "Provider '%s' has already been registered" % provider.handle
75
76 _providers[provider.handle] = provider
77
78 def __init__(self, core, **settings):
79 self.core = core
80
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)
85
86 def __repr__(self):
87 return "<DDNS Provider %s (%s)>" % (self.name, self.handle)
88
89 def __cmp__(self, other):
90 return cmp(self.hostname, other.hostname)
91
92 def get(self, key, default=None):
93 """
94 Get a setting from the settings dictionary.
95 """
96 return self.settings.get(key, default)
97
98 @property
99 def hostname(self):
100 """
101 Fast access to the hostname.
102 """
103 return self.get("hostname")
104
105 @property
106 def username(self):
107 """
108 Fast access to the username.
109 """
110 return self.get("username")
111
112 @property
113 def password(self):
114 """
115 Fast access to the password.
116 """
117 return self.get("password")
118
119 @property
120 def token(self):
121 """
122 Fast access to the token.
123 """
124 return self.get("token")
125
126 def __call__(self, force=False):
127 if force:
128 logger.debug(_("Updating %s forced") % self.hostname)
129
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)
133 return
134
135 # Execute the update.
136 self.update()
137
138 def update(self):
139 raise NotImplementedError
140
141 def is_uptodate(self, protos):
142 """
143 Returns True if this host is already up to date
144 and does not need to change the IP address on the
145 name server.
146 """
147 for proto in protos:
148 addresses = self.core.system.resolve(self.hostname, proto)
149
150 current_address = self.get_address(proto)
151
152 # If no addresses for the given protocol exist, we
153 # are fine...
154 if current_address is None and not addresses:
155 continue
156
157 if not current_address in addresses:
158 return False
159
160 return True
161
162 def send_request(self, *args, **kwargs):
163 """
164 Proxy connection to the send request
165 method.
166 """
167 return self.core.system.send_request(*args, **kwargs)
168
169 def get_address(self, proto, default=None):
170 """
171 Proxy method to get the current IP address.
172 """
173 return self.core.system.get_address(proto) or default
174
175
176 class DDNSProtocolDynDNS2(object):
177 """
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
181 shorter and simpler.
182 """
183
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/
187
188 def _prepare_request_data(self):
189 data = {
190 "hostname" : self.hostname,
191 "myip" : self.get_address("ipv4"),
192 }
193
194 return data
195
196 def update(self):
197 data = self._prepare_request_data()
198
199 # Send update to the server.
200 response = self.send_request(self.url, data=data,
201 username=self.username, password=self.password)
202
203 # Get the full response message.
204 output = response.read()
205
206 # Handle success messages.
207 if output.startswith("good") or output.startswith("nochg"):
208 return
209
210 # Handle error codes.
211 if output == "badauth":
212 raise DDNSAuthenticationError
213 elif output == "aduse":
214 raise DDNSAbuseError
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."))
223
224 # If we got here, some other update error happened.
225 raise DDNSUpdateError(_("Server response: %s") % output)
226
227
228 class DDNSProviderAllInkl(DDNSProvider):
229 handle = "all-inkl.com"
230 name = "All-inkl.com"
231 website = "http://all-inkl.com/"
232 protocols = ("ipv4",)
233
234 # There are only information provided by the vendor how to
235 # perform an update on a FRITZ Box. Grab requried informations
236 # from the net.
237 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
238
239 url = "http://dyndns.kasserver.com"
240
241 def update(self):
242 # There is no additional data required so we directly can
243 # send our request.
244 response = self.send_request(self.url, username=self.username, password=self.password)
245
246 # Get the full response message.
247 output = response.read()
248
249 # Handle success messages.
250 if output.startswith("good") or output.startswith("nochg"):
251 return
252
253 # If we got here, some other update error happened.
254 raise DDNSUpdateError
255
256
257 class DDNSProviderBindNsupdate(DDNSProvider):
258 handle = "nsupdate"
259 name = "BIND nsupdate utility"
260 website = "http://en.wikipedia.org/wiki/Nsupdate"
261
262 DEFAULT_TTL = 60
263
264 def update(self):
265 scriptlet = self.__make_scriptlet()
266
267 # -v enables TCP hence we transfer keys and other data that may
268 # exceed the size of one packet.
269 # -t sets the timeout
270 command = ["nsupdate", "-v", "-t", "60"]
271
272 p = subprocess.Popen(command, shell=True,
273 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
274 )
275 stdout, stderr = p.communicate(scriptlet)
276
277 if p.returncode == 0:
278 return
279
280 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p.returncode, stderr))
281
282 def __make_scriptlet(self):
283 scriptlet = []
284
285 # Set a different server the update is sent to.
286 server = self.get("server", None)
287 if server:
288 scriptlet.append("server %s" % server)
289
290 key = self.get("key", None)
291 if key:
292 secret = self.get("secret")
293
294 scriptlet.append("key %s %s" % (key, secret))
295
296 ttl = self.get("ttl", self.DEFAULT_TTL)
297
298 # Perform an update for each supported protocol.
299 for rrtype, proto in (("AAAA", "ipv6"), ("A", "ipv4")):
300 address = self.get_address(proto)
301 if not address:
302 continue
303
304 scriptlet.append("update delete %s. %s" % (self.hostname, rrtype))
305 scriptlet.append("update add %s. %s %s %s" % \
306 (self.hostname, ttl, rrtype, address))
307
308 # Send the actions to the server.
309 scriptlet.append("send")
310 scriptlet.append("quit")
311
312 logger.debug(_("Scriptlet:"))
313 for line in scriptlet:
314 # Masquerade the line with the secret key.
315 if line.startswith("key"):
316 line = "key **** ****"
317
318 logger.debug(" %s" % line)
319
320 return "\n".join(scriptlet)
321
322
323 class DDNSProviderDHS(DDNSProvider):
324 handle = "dhs.org"
325 name = "DHS International"
326 website = "http://dhs.org/"
327 protocols = ("ipv4",)
328
329 # No information about the used update api provided on webpage,
330 # grabed from source code of ez-ipudate.
331
332 url = "http://members.dhs.org/nic/hosts"
333
334 def update(self):
335 data = {
336 "domain" : self.hostname,
337 "ip" : self.get_address("ipv4"),
338 "hostcmd" : "edit",
339 "hostcmdstage" : "2",
340 "type" : "4",
341 }
342
343 # Send update to the server.
344 response = self.send_request(self.url, username=self.username, password=self.password,
345 data=data)
346
347 # Handle success messages.
348 if response.code == 200:
349 return
350
351 # If we got here, some other update error happened.
352 raise DDNSUpdateError
353
354
355 class DDNSProviderDNSpark(DDNSProvider):
356 handle = "dnspark.com"
357 name = "DNS Park"
358 website = "http://dnspark.com/"
359 protocols = ("ipv4",)
360
361 # Informations to the used api can be found here:
362 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
363
364 url = "https://control.dnspark.com/api/dynamic/update.php"
365
366 def update(self):
367 data = {
368 "domain" : self.hostname,
369 "ip" : self.get_address("ipv4"),
370 }
371
372 # Send update to the server.
373 response = self.send_request(self.url, username=self.username, password=self.password,
374 data=data)
375
376 # Get the full response message.
377 output = response.read()
378
379 # Handle success messages.
380 if output.startswith("ok") or output.startswith("nochange"):
381 return
382
383 # Handle error codes.
384 if output == "unauth":
385 raise DDNSAuthenticationError
386 elif output == "abuse":
387 raise DDNSAbuseError
388 elif output == "blocked":
389 raise DDNSBlockedError
390 elif output == "nofqdn":
391 raise DDNSRequestError(_("No valid FQDN was given."))
392 elif output == "nohost":
393 raise DDNSRequestError(_("Invalid hostname specified."))
394 elif output == "notdyn":
395 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
396 elif output == "invalid":
397 raise DDNSRequestError(_("Invalid IP address has been sent."))
398
399 # If we got here, some other update error happened.
400 raise DDNSUpdateError
401
402
403 class DDNSProviderDtDNS(DDNSProvider):
404 handle = "dtdns.com"
405 name = "DtDNS"
406 website = "http://dtdns.com/"
407 protocols = ("ipv4",)
408
409 # Information about the format of the HTTPS request is to be found
410 # http://www.dtdns.com/dtsite/updatespec
411
412 url = "https://www.dtdns.com/api/autodns.cfm"
413
414 def update(self):
415 data = {
416 "ip" : self.get_address("ipv4"),
417 "id" : self.hostname,
418 "pw" : self.password
419 }
420
421 # Send update to the server.
422 response = self.send_request(self.url, data=data)
423
424 # Get the full response message.
425 output = response.read()
426
427 # Remove all leading and trailing whitespace.
428 output = output.strip()
429
430 # Handle success messages.
431 if "now points to" in output:
432 return
433
434 # Handle error codes.
435 if output == "No hostname to update was supplied.":
436 raise DDNSRequestError(_("No hostname specified."))
437
438 elif output == "The hostname you supplied is not valid.":
439 raise DDNSRequestError(_("Invalid hostname specified."))
440
441 elif output == "The password you supplied is not valid.":
442 raise DDNSAuthenticationError
443
444 elif output == "Administration has disabled this account.":
445 raise DDNSRequestError(_("Account has been disabled."))
446
447 elif output == "Illegal character in IP.":
448 raise DDNSRequestError(_("Invalid IP address has been sent."))
449
450 elif output == "Too many failed requests.":
451 raise DDNSRequestError(_("Too many failed requests."))
452
453 # If we got here, some other update error happened.
454 raise DDNSUpdateError
455
456
457 class DDNSProviderDynDNS(DDNSProtocolDynDNS2, DDNSProvider):
458 handle = "dyndns.org"
459 name = "Dyn"
460 website = "http://dyn.com/dns/"
461 protocols = ("ipv4",)
462
463 # Information about the format of the request is to be found
464 # http://http://dyn.com/support/developers/api/perform-update/
465 # http://dyn.com/support/developers/api/return-codes/
466
467 url = "https://members.dyndns.org/nic/update"
468
469
470 class DDNSProviderDynU(DDNSProtocolDynDNS2, DDNSProvider):
471 handle = "dynu.com"
472 name = "Dynu"
473 website = "http://dynu.com/"
474 protocols = ("ipv6", "ipv4",)
475
476 # Detailed information about the request and response codes
477 # are available on the providers webpage.
478 # http://dynu.com/Default.aspx?page=dnsapi
479
480 url = "https://api.dynu.com/nic/update"
481
482 def _prepare_request_data(self):
483 data = DDNSProviderDynDNS._prepare_request_data(self)
484
485 # This one supports IPv6
486 data.update({
487 "myipv6" : self.get_address("ipv6"),
488 })
489
490 return data
491
492
493 class DDNSProviderEasyDNS(DDNSProtocolDynDNS2, DDNSProvider):
494 handle = "easydns.com"
495 name = "EasyDNS"
496 website = "http://www.easydns.com/"
497 protocols = ("ipv4",)
498
499 # There is only some basic documentation provided by the vendor,
500 # also searching the web gain very poor results.
501 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
502
503 url = "http://api.cp.easydns.com/dyn/tomato.php"
504
505
506 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider):
507 handle = "freedns.afraid.org"
508 name = "freedns.afraid.org"
509 website = "http://freedns.afraid.org/"
510
511 # No information about the request or response could be found on the vendor
512 # page. All used values have been collected by testing.
513 url = "https://freedns.afraid.org/dynamic/update.php"
514
515 @property
516 def proto(self):
517 return self.get("proto")
518
519 def update(self):
520 address = self.get_address(self.proto)
521
522 data = {
523 "address" : address,
524 }
525
526 # Add auth token to the update url.
527 url = "%s?%s" % (self.url, self.token)
528
529 # Send update to the server.
530 response = self.send_request(url, data=data)
531
532 if output.startswith("Updated") or "has not changed" in output:
533 return
534
535 # Handle error codes.
536 if output == "ERROR: Unable to locate this record":
537 raise DDNSAuthenticationError
538 elif "is an invalid IP address" in output:
539 raise DDNSRequestError(_("Invalid IP address has been sent."))
540
541 # If we got here, some other update error happened.
542 raise DDNSUpdateError
543
544
545 class DDNSProviderLightningWireLabs(DDNSProvider):
546 handle = "dns.lightningwirelabs.com"
547 name = "Lightning Wire Labs DNS Service"
548 website = "http://dns.lightningwirelabs.com/"
549
550 # Information about the format of the HTTPS request is to be found
551 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
552
553 url = "https://dns.lightningwirelabs.com/update"
554
555 def update(self):
556 data = {
557 "hostname" : self.hostname,
558 "address6" : self.get_address("ipv6", "-"),
559 "address4" : self.get_address("ipv4", "-"),
560 }
561
562 # Check if a token has been set.
563 if self.token:
564 data["token"] = self.token
565
566 # Check for username and password.
567 elif self.username and self.password:
568 data.update({
569 "username" : self.username,
570 "password" : self.password,
571 })
572
573 # Raise an error if no auth details are given.
574 else:
575 raise DDNSConfigurationError
576
577 # Send update to the server.
578 response = self.send_request(self.url, data=data)
579
580 # Handle success messages.
581 if response.code == 200:
582 return
583
584 # If we got here, some other update error happened.
585 raise DDNSUpdateError
586
587
588 class DDNSProviderNamecheap(DDNSProvider):
589 handle = "namecheap.com"
590 name = "Namecheap"
591 website = "http://namecheap.com"
592 protocols = ("ipv4",)
593
594 # Information about the format of the HTTP request is to be found
595 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
596 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
597
598 url = "https://dynamicdns.park-your-domain.com/update"
599
600 def parse_xml(self, document, content):
601 # Send input to the parser.
602 xmldoc = xml.dom.minidom.parseString(document)
603
604 # Get XML elements by the given content.
605 element = xmldoc.getElementsByTagName(content)
606
607 # If no element has been found, we directly can return None.
608 if not element:
609 return None
610
611 # Only get the first child from an element, even there are more than one.
612 firstchild = element[0].firstChild
613
614 # Get the value of the child.
615 value = firstchild.nodeValue
616
617 # Return the value.
618 return value
619
620 def update(self):
621 # Namecheap requires the hostname splitted into a host and domain part.
622 host, domain = self.hostname.split(".", 1)
623
624 data = {
625 "ip" : self.get_address("ipv4"),
626 "password" : self.password,
627 "host" : host,
628 "domain" : domain
629 }
630
631 # Send update to the server.
632 response = self.send_request(self.url, data=data)
633
634 # Get the full response message.
635 output = response.read()
636
637 # Handle success messages.
638 if self.parse_xml(output, "IP") == self.get_address("ipv4"):
639 return
640
641 # Handle error codes.
642 errorcode = self.parse_xml(output, "ResponseNumber")
643
644 if errorcode == "304156":
645 raise DDNSAuthenticationError
646 elif errorcode == "316153":
647 raise DDNSRequestError(_("Domain not found."))
648 elif errorcode == "316154":
649 raise DDNSRequestError(_("Domain not active."))
650 elif errorcode in ("380098", "380099"):
651 raise DDNSInternalServerError
652
653 # If we got here, some other update error happened.
654 raise DDNSUpdateError
655
656
657 class DDNSProviderNOIP(DDNSProtocolDynDNS2, DDNSProvider):
658 handle = "no-ip.com"
659 name = "No-IP"
660 website = "http://www.no-ip.com/"
661 protocols = ("ipv4",)
662
663 # Information about the format of the HTTP request is to be found
664 # here: http://www.no-ip.com/integrate/request and
665 # here: http://www.no-ip.com/integrate/response
666
667 url = "http://dynupdate.no-ip.com/nic/update"
668
669 def _prepare_request_data(self):
670 data = {
671 "hostname" : self.hostname,
672 "address" : self.get_address("ipv4"),
673 }
674
675 return data
676
677
678 class DDNSProviderOVH(DDNSProtocolDynDNS2, DDNSProvider):
679 handle = "ovh.com"
680 name = "OVH"
681 website = "http://www.ovh.com/"
682 protocols = ("ipv4",)
683
684 # OVH only provides very limited information about how to
685 # update a DynDNS host. They only provide the update url
686 # on the their german subpage.
687 #
688 # http://hilfe.ovh.de/DomainDynHost
689
690 url = "https://www.ovh.com/nic/update"
691
692 def _prepare_request_data(self):
693 data = DDNSProtocolDynDNS2._prepare_request_data(self)
694 data.update({
695 "system" : "dyndns",
696 })
697
698 return data
699
700
701 class DDNSProviderRegfish(DDNSProvider):
702 handle = "regfish.com"
703 name = "Regfish GmbH"
704 website = "http://www.regfish.com/"
705
706 # A full documentation to the providers api can be found here
707 # but is only available in german.
708 # https://www.regfish.de/domains/dyndns/dokumentation
709
710 url = "https://dyndns.regfish.de/"
711
712 def update(self):
713 data = {
714 "fqdn" : self.hostname,
715 }
716
717 # Check if we update an IPv6 address.
718 address6 = self.get_address("ipv6")
719 if address6:
720 data["ipv6"] = address6
721
722 # Check if we update an IPv4 address.
723 address4 = self.get_address("ipv4")
724 if address4:
725 data["ipv4"] = address4
726
727 # Raise an error if none address is given.
728 if not data.has_key("ipv6") and not data.has_key("ipv4"):
729 raise DDNSConfigurationError
730
731 # Check if a token has been set.
732 if self.token:
733 data["token"] = self.token
734
735 # Raise an error if no token and no useranem and password
736 # are given.
737 elif not self.username and not self.password:
738 raise DDNSConfigurationError(_("No Auth details specified."))
739
740 # HTTP Basic Auth is only allowed if no token is used.
741 if self.token:
742 # Send update to the server.
743 response = self.send_request(self.url, data=data)
744 else:
745 # Send update to the server.
746 response = self.send_request(self.url, username=self.username, password=self.password,
747 data=data)
748
749 # Get the full response message.
750 output = response.read()
751
752 # Handle success messages.
753 if "100" in output or "101" in output:
754 return
755
756 # Handle error codes.
757 if "401" or "402" in output:
758 raise DDNSAuthenticationError
759 elif "408" in output:
760 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
761 elif "409" in output:
762 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
763 elif "412" in output:
764 raise DDNSRequestError(_("No valid FQDN was given."))
765 elif "414" in output:
766 raise DDNSInternalServerError
767
768 # If we got here, some other update error happened.
769 raise DDNSUpdateError
770
771
772 class DDNSProviderSelfhost(DDNSProtocolDynDNS2, DDNSProvider):
773 handle = "selfhost.de"
774 name = "Selfhost.de"
775 website = "http://www.selfhost.de/"
776 protocols = ("ipv4",)
777
778 url = "https://carol.selfhost.de/nic/update"
779
780 def _prepare_request_data(self):
781 data = DDNSProviderDynDNS._prepare_request_data(self)
782 data.update({
783 "hostname" : "1",
784 })
785
786 return data
787
788
789 class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
790 handle = "spdns.org"
791 name = "SPDNS"
792 website = "http://spdns.org/"
793 protocols = ("ipv4",)
794
795 # Detailed information about request and response codes are provided
796 # by the vendor. They are using almost the same mechanism and status
797 # codes as dyndns.org so we can inherit all those stuff.
798 #
799 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
800 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
801
802 url = "https://update.spdns.de/nic/update"
803
804
805 class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
806 handle = "strato.com"
807 name = "Strato AG"
808 website = "http:/www.strato.com/"
809 protocols = ("ipv4",)
810
811 # Information about the request and response can be obtained here:
812 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
813
814 url = "https://dyndns.strato.com/nic/update"
815
816
817 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2, DDNSProvider):
818 handle = "twodns.de"
819 name = "TwoDNS"
820 website = "http://www.twodns.de"
821 protocols = ("ipv4",)
822
823 # Detailed information about the request can be found here
824 # http://twodns.de/en/faqs
825 # http://twodns.de/en/api
826
827 url = "https://update.twodns.de/update"
828
829 def _prepare_request_data(self):
830 data = {
831 "ip" : self.get_address("ipv4"),
832 "hostname" : self.hostname
833 }
834
835 return data
836
837
838 class DDNSProviderUdmedia(DDNSProtocolDynDNS2, DDNSProvider):
839 handle = "udmedia.de"
840 name = "Udmedia GmbH"
841 website = "http://www.udmedia.de"
842 protocols = ("ipv4",)
843
844 # Information about the request can be found here
845 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
846
847 url = "https://www.udmedia.de/nic/update"
848
849
850 class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
851 handle = "variomedia.de"
852 name = "Variomedia"
853 website = "http://www.variomedia.de/"
854 protocols = ("ipv6", "ipv4",)
855
856 # Detailed information about the request can be found here
857 # https://dyndns.variomedia.de/
858
859 url = "https://dyndns.variomedia.de/nic/update"
860
861 @property
862 def proto(self):
863 return self.get("proto")
864
865 def _prepare_request_data(self):
866 data = {
867 "hostname" : self.hostname,
868 "myip" : self.get_address(self.proto)
869 }
870
871 return data
872
873
874 class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
875 handle = "zoneedit.com"
876 name = "Zoneedit"
877 website = "http://www.zoneedit.com"
878 protocols = ("ipv4",)
879
880 # Detailed information about the request and the response codes can be
881 # obtained here:
882 # http://www.zoneedit.com/doc/api/other.html
883 # http://www.zoneedit.com/faq.html
884
885 url = "https://dynamic.zoneedit.com/auth/dynamic.html"
886
887 @property
888 def proto(self):
889 return self.get("proto")
890
891 def update(self):
892 data = {
893 "dnsto" : self.get_address(self.proto),
894 "host" : self.hostname
895 }
896
897 # Send update to the server.
898 response = self.send_request(self.url, username=self.username, password=self.password,
899 data=data)
900
901 # Get the full response message.
902 output = response.read()
903
904 # Handle success messages.
905 if output.startswith("<SUCCESS"):
906 return
907
908 # Handle error codes.
909 if output.startswith("invalid login"):
910 raise DDNSAuthenticationError
911 elif output.startswith("<ERROR CODE=\"704\""):
912 raise DDNSRequestError(_("No valid FQDN was given."))
913 elif output.startswith("<ERROR CODE=\"702\""):
914 raise DDNSInternalServerError
915
916 # If we got here, some other update error happened.
917 raise DDNSUpdateError