]> git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
FreeDNSAfraid: Readd missing lines.
[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 # Get the full response message.
533 output = response.read()
534
535 # Handle success messages.
536 if output.startswith("Updated") or "has not changed" in output:
537 return
538
539 # Handle error codes.
540 if output == "ERROR: Unable to locate this record":
541 raise DDNSAuthenticationError
542 elif "is an invalid IP address" in output:
543 raise DDNSRequestError(_("Invalid IP address has been sent."))
544
545 # If we got here, some other update error happened.
546 raise DDNSUpdateError
547
548
549 class DDNSProviderLightningWireLabs(DDNSProvider):
550 handle = "dns.lightningwirelabs.com"
551 name = "Lightning Wire Labs DNS Service"
552 website = "http://dns.lightningwirelabs.com/"
553
554 # Information about the format of the HTTPS request is to be found
555 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
556
557 url = "https://dns.lightningwirelabs.com/update"
558
559 def update(self):
560 data = {
561 "hostname" : self.hostname,
562 "address6" : self.get_address("ipv6", "-"),
563 "address4" : self.get_address("ipv4", "-"),
564 }
565
566 # Check if a token has been set.
567 if self.token:
568 data["token"] = self.token
569
570 # Check for username and password.
571 elif self.username and self.password:
572 data.update({
573 "username" : self.username,
574 "password" : self.password,
575 })
576
577 # Raise an error if no auth details are given.
578 else:
579 raise DDNSConfigurationError
580
581 # Send update to the server.
582 response = self.send_request(self.url, data=data)
583
584 # Handle success messages.
585 if response.code == 200:
586 return
587
588 # If we got here, some other update error happened.
589 raise DDNSUpdateError
590
591
592 class DDNSProviderNamecheap(DDNSProvider):
593 handle = "namecheap.com"
594 name = "Namecheap"
595 website = "http://namecheap.com"
596 protocols = ("ipv4",)
597
598 # Information about the format of the HTTP request is to be found
599 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
600 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
601
602 url = "https://dynamicdns.park-your-domain.com/update"
603
604 def parse_xml(self, document, content):
605 # Send input to the parser.
606 xmldoc = xml.dom.minidom.parseString(document)
607
608 # Get XML elements by the given content.
609 element = xmldoc.getElementsByTagName(content)
610
611 # If no element has been found, we directly can return None.
612 if not element:
613 return None
614
615 # Only get the first child from an element, even there are more than one.
616 firstchild = element[0].firstChild
617
618 # Get the value of the child.
619 value = firstchild.nodeValue
620
621 # Return the value.
622 return value
623
624 def update(self):
625 # Namecheap requires the hostname splitted into a host and domain part.
626 host, domain = self.hostname.split(".", 1)
627
628 data = {
629 "ip" : self.get_address("ipv4"),
630 "password" : self.password,
631 "host" : host,
632 "domain" : domain
633 }
634
635 # Send update to the server.
636 response = self.send_request(self.url, data=data)
637
638 # Get the full response message.
639 output = response.read()
640
641 # Handle success messages.
642 if self.parse_xml(output, "IP") == self.get_address("ipv4"):
643 return
644
645 # Handle error codes.
646 errorcode = self.parse_xml(output, "ResponseNumber")
647
648 if errorcode == "304156":
649 raise DDNSAuthenticationError
650 elif errorcode == "316153":
651 raise DDNSRequestError(_("Domain not found."))
652 elif errorcode == "316154":
653 raise DDNSRequestError(_("Domain not active."))
654 elif errorcode in ("380098", "380099"):
655 raise DDNSInternalServerError
656
657 # If we got here, some other update error happened.
658 raise DDNSUpdateError
659
660
661 class DDNSProviderNOIP(DDNSProtocolDynDNS2, DDNSProvider):
662 handle = "no-ip.com"
663 name = "No-IP"
664 website = "http://www.no-ip.com/"
665 protocols = ("ipv4",)
666
667 # Information about the format of the HTTP request is to be found
668 # here: http://www.no-ip.com/integrate/request and
669 # here: http://www.no-ip.com/integrate/response
670
671 url = "http://dynupdate.no-ip.com/nic/update"
672
673 def _prepare_request_data(self):
674 data = {
675 "hostname" : self.hostname,
676 "address" : self.get_address("ipv4"),
677 }
678
679 return data
680
681
682 class DDNSProviderOVH(DDNSProtocolDynDNS2, DDNSProvider):
683 handle = "ovh.com"
684 name = "OVH"
685 website = "http://www.ovh.com/"
686 protocols = ("ipv4",)
687
688 # OVH only provides very limited information about how to
689 # update a DynDNS host. They only provide the update url
690 # on the their german subpage.
691 #
692 # http://hilfe.ovh.de/DomainDynHost
693
694 url = "https://www.ovh.com/nic/update"
695
696 def _prepare_request_data(self):
697 data = DDNSProtocolDynDNS2._prepare_request_data(self)
698 data.update({
699 "system" : "dyndns",
700 })
701
702 return data
703
704
705 class DDNSProviderRegfish(DDNSProvider):
706 handle = "regfish.com"
707 name = "Regfish GmbH"
708 website = "http://www.regfish.com/"
709
710 # A full documentation to the providers api can be found here
711 # but is only available in german.
712 # https://www.regfish.de/domains/dyndns/dokumentation
713
714 url = "https://dyndns.regfish.de/"
715
716 def update(self):
717 data = {
718 "fqdn" : self.hostname,
719 }
720
721 # Check if we update an IPv6 address.
722 address6 = self.get_address("ipv6")
723 if address6:
724 data["ipv6"] = address6
725
726 # Check if we update an IPv4 address.
727 address4 = self.get_address("ipv4")
728 if address4:
729 data["ipv4"] = address4
730
731 # Raise an error if none address is given.
732 if not data.has_key("ipv6") and not data.has_key("ipv4"):
733 raise DDNSConfigurationError
734
735 # Check if a token has been set.
736 if self.token:
737 data["token"] = self.token
738
739 # Raise an error if no token and no useranem and password
740 # are given.
741 elif not self.username and not self.password:
742 raise DDNSConfigurationError(_("No Auth details specified."))
743
744 # HTTP Basic Auth is only allowed if no token is used.
745 if self.token:
746 # Send update to the server.
747 response = self.send_request(self.url, data=data)
748 else:
749 # Send update to the server.
750 response = self.send_request(self.url, username=self.username, password=self.password,
751 data=data)
752
753 # Get the full response message.
754 output = response.read()
755
756 # Handle success messages.
757 if "100" in output or "101" in output:
758 return
759
760 # Handle error codes.
761 if "401" or "402" in output:
762 raise DDNSAuthenticationError
763 elif "408" in output:
764 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
765 elif "409" in output:
766 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
767 elif "412" in output:
768 raise DDNSRequestError(_("No valid FQDN was given."))
769 elif "414" in output:
770 raise DDNSInternalServerError
771
772 # If we got here, some other update error happened.
773 raise DDNSUpdateError
774
775
776 class DDNSProviderSelfhost(DDNSProtocolDynDNS2, DDNSProvider):
777 handle = "selfhost.de"
778 name = "Selfhost.de"
779 website = "http://www.selfhost.de/"
780 protocols = ("ipv4",)
781
782 url = "https://carol.selfhost.de/nic/update"
783
784 def _prepare_request_data(self):
785 data = DDNSProviderDynDNS._prepare_request_data(self)
786 data.update({
787 "hostname" : "1",
788 })
789
790 return data
791
792
793 class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
794 handle = "spdns.org"
795 name = "SPDNS"
796 website = "http://spdns.org/"
797 protocols = ("ipv4",)
798
799 # Detailed information about request and response codes are provided
800 # by the vendor. They are using almost the same mechanism and status
801 # codes as dyndns.org so we can inherit all those stuff.
802 #
803 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
804 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
805
806 url = "https://update.spdns.de/nic/update"
807
808
809 class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
810 handle = "strato.com"
811 name = "Strato AG"
812 website = "http:/www.strato.com/"
813 protocols = ("ipv4",)
814
815 # Information about the request and response can be obtained here:
816 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
817
818 url = "https://dyndns.strato.com/nic/update"
819
820
821 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2, DDNSProvider):
822 handle = "twodns.de"
823 name = "TwoDNS"
824 website = "http://www.twodns.de"
825 protocols = ("ipv4",)
826
827 # Detailed information about the request can be found here
828 # http://twodns.de/en/faqs
829 # http://twodns.de/en/api
830
831 url = "https://update.twodns.de/update"
832
833 def _prepare_request_data(self):
834 data = {
835 "ip" : self.get_address("ipv4"),
836 "hostname" : self.hostname
837 }
838
839 return data
840
841
842 class DDNSProviderUdmedia(DDNSProtocolDynDNS2, DDNSProvider):
843 handle = "udmedia.de"
844 name = "Udmedia GmbH"
845 website = "http://www.udmedia.de"
846 protocols = ("ipv4",)
847
848 # Information about the request can be found here
849 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
850
851 url = "https://www.udmedia.de/nic/update"
852
853
854 class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
855 handle = "variomedia.de"
856 name = "Variomedia"
857 website = "http://www.variomedia.de/"
858 protocols = ("ipv6", "ipv4",)
859
860 # Detailed information about the request can be found here
861 # https://dyndns.variomedia.de/
862
863 url = "https://dyndns.variomedia.de/nic/update"
864
865 @property
866 def proto(self):
867 return self.get("proto")
868
869 def _prepare_request_data(self):
870 data = {
871 "hostname" : self.hostname,
872 "myip" : self.get_address(self.proto)
873 }
874
875 return data
876
877
878 class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
879 handle = "zoneedit.com"
880 name = "Zoneedit"
881 website = "http://www.zoneedit.com"
882 protocols = ("ipv4",)
883
884 # Detailed information about the request and the response codes can be
885 # obtained here:
886 # http://www.zoneedit.com/doc/api/other.html
887 # http://www.zoneedit.com/faq.html
888
889 url = "https://dynamic.zoneedit.com/auth/dynamic.html"
890
891 @property
892 def proto(self):
893 return self.get("proto")
894
895 def update(self):
896 data = {
897 "dnsto" : self.get_address(self.proto),
898 "host" : self.hostname
899 }
900
901 # Send update to the server.
902 response = self.send_request(self.url, username=self.username, password=self.password,
903 data=data)
904
905 # Get the full response message.
906 output = response.read()
907
908 # Handle success messages.
909 if output.startswith("<SUCCESS"):
910 return
911
912 # Handle error codes.
913 if output.startswith("invalid login"):
914 raise DDNSAuthenticationError
915 elif output.startswith("<ERROR CODE=\"704\""):
916 raise DDNSRequestError(_("No valid FQDN was given."))
917 elif output.startswith("<ERROR CODE=\"702\""):
918 raise DDNSInternalServerError
919
920 # If we got here, some other update error happened.
921 raise DDNSUpdateError