]> git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
Add nsupdate.info as new provider.
[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 DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2, DDNSProvider):
679 handle = "nsupdate.info"
680 name = "nsupdate.info"
681 website = "http://www.nsupdate.info/"
682 protocols = ("ipv6", "ipv4",)
683
684 # Information about the format of the HTTP request can be found
685 # after login on the provider user intrface and here:
686 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
687
688 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
689 # and for the password a so called secret.
690 @property
691 def username(self):
692 return self.get("hostname")
693
694 @property
695 def password(self):
696 return self.get("secret")
697
698 @property
699 def proto(self):
700 return self.get("proto")
701
702 @property
703 def url(self):
704 # The update URL is different by the used protocol.
705 if self.proto == "ipv4":
706 return "https://ipv4.nsupdate.info/nic/update"
707 elif self.proto == "ipv6":
708 return "https://ipv6.nsupdate.info/nic/update"
709 else:
710 raise DDNSUpdateError(_("Invalid protocol has been given"))
711
712 def _prepare_request_data(self):
713 data = {
714 "myip" : self.get_address(self.proto),
715 }
716
717 return data
718
719
720 class DDNSProviderOVH(DDNSProtocolDynDNS2, DDNSProvider):
721 handle = "ovh.com"
722 name = "OVH"
723 website = "http://www.ovh.com/"
724 protocols = ("ipv4",)
725
726 # OVH only provides very limited information about how to
727 # update a DynDNS host. They only provide the update url
728 # on the their german subpage.
729 #
730 # http://hilfe.ovh.de/DomainDynHost
731
732 url = "https://www.ovh.com/nic/update"
733
734 def _prepare_request_data(self):
735 data = DDNSProtocolDynDNS2._prepare_request_data(self)
736 data.update({
737 "system" : "dyndns",
738 })
739
740 return data
741
742
743 class DDNSProviderRegfish(DDNSProvider):
744 handle = "regfish.com"
745 name = "Regfish GmbH"
746 website = "http://www.regfish.com/"
747
748 # A full documentation to the providers api can be found here
749 # but is only available in german.
750 # https://www.regfish.de/domains/dyndns/dokumentation
751
752 url = "https://dyndns.regfish.de/"
753
754 def update(self):
755 data = {
756 "fqdn" : self.hostname,
757 }
758
759 # Check if we update an IPv6 address.
760 address6 = self.get_address("ipv6")
761 if address6:
762 data["ipv6"] = address6
763
764 # Check if we update an IPv4 address.
765 address4 = self.get_address("ipv4")
766 if address4:
767 data["ipv4"] = address4
768
769 # Raise an error if none address is given.
770 if not data.has_key("ipv6") and not data.has_key("ipv4"):
771 raise DDNSConfigurationError
772
773 # Check if a token has been set.
774 if self.token:
775 data["token"] = self.token
776
777 # Raise an error if no token and no useranem and password
778 # are given.
779 elif not self.username and not self.password:
780 raise DDNSConfigurationError(_("No Auth details specified."))
781
782 # HTTP Basic Auth is only allowed if no token is used.
783 if self.token:
784 # Send update to the server.
785 response = self.send_request(self.url, data=data)
786 else:
787 # Send update to the server.
788 response = self.send_request(self.url, username=self.username, password=self.password,
789 data=data)
790
791 # Get the full response message.
792 output = response.read()
793
794 # Handle success messages.
795 if "100" in output or "101" in output:
796 return
797
798 # Handle error codes.
799 if "401" or "402" in output:
800 raise DDNSAuthenticationError
801 elif "408" in output:
802 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
803 elif "409" in output:
804 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
805 elif "412" in output:
806 raise DDNSRequestError(_("No valid FQDN was given."))
807 elif "414" in output:
808 raise DDNSInternalServerError
809
810 # If we got here, some other update error happened.
811 raise DDNSUpdateError
812
813
814 class DDNSProviderSelfhost(DDNSProtocolDynDNS2, DDNSProvider):
815 handle = "selfhost.de"
816 name = "Selfhost.de"
817 website = "http://www.selfhost.de/"
818 protocols = ("ipv4",)
819
820 url = "https://carol.selfhost.de/nic/update"
821
822 def _prepare_request_data(self):
823 data = DDNSProviderDynDNS._prepare_request_data(self)
824 data.update({
825 "hostname" : "1",
826 })
827
828 return data
829
830
831 class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
832 handle = "spdns.org"
833 name = "SPDNS"
834 website = "http://spdns.org/"
835 protocols = ("ipv4",)
836
837 # Detailed information about request and response codes are provided
838 # by the vendor. They are using almost the same mechanism and status
839 # codes as dyndns.org so we can inherit all those stuff.
840 #
841 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
842 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
843
844 url = "https://update.spdns.de/nic/update"
845
846
847 class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
848 handle = "strato.com"
849 name = "Strato AG"
850 website = "http:/www.strato.com/"
851 protocols = ("ipv4",)
852
853 # Information about the request and response can be obtained here:
854 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
855
856 url = "https://dyndns.strato.com/nic/update"
857
858
859 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2, DDNSProvider):
860 handle = "twodns.de"
861 name = "TwoDNS"
862 website = "http://www.twodns.de"
863 protocols = ("ipv4",)
864
865 # Detailed information about the request can be found here
866 # http://twodns.de/en/faqs
867 # http://twodns.de/en/api
868
869 url = "https://update.twodns.de/update"
870
871 def _prepare_request_data(self):
872 data = {
873 "ip" : self.get_address("ipv4"),
874 "hostname" : self.hostname
875 }
876
877 return data
878
879
880 class DDNSProviderUdmedia(DDNSProtocolDynDNS2, DDNSProvider):
881 handle = "udmedia.de"
882 name = "Udmedia GmbH"
883 website = "http://www.udmedia.de"
884 protocols = ("ipv4",)
885
886 # Information about the request can be found here
887 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
888
889 url = "https://www.udmedia.de/nic/update"
890
891
892 class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
893 handle = "variomedia.de"
894 name = "Variomedia"
895 website = "http://www.variomedia.de/"
896 protocols = ("ipv6", "ipv4",)
897
898 # Detailed information about the request can be found here
899 # https://dyndns.variomedia.de/
900
901 url = "https://dyndns.variomedia.de/nic/update"
902
903 @property
904 def proto(self):
905 return self.get("proto")
906
907 def _prepare_request_data(self):
908 data = {
909 "hostname" : self.hostname,
910 "myip" : self.get_address(self.proto)
911 }
912
913 return data
914
915
916 class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
917 handle = "zoneedit.com"
918 name = "Zoneedit"
919 website = "http://www.zoneedit.com"
920 protocols = ("ipv4",)
921
922 # Detailed information about the request and the response codes can be
923 # obtained here:
924 # http://www.zoneedit.com/doc/api/other.html
925 # http://www.zoneedit.com/faq.html
926
927 url = "https://dynamic.zoneedit.com/auth/dynamic.html"
928
929 @property
930 def proto(self):
931 return self.get("proto")
932
933 def update(self):
934 data = {
935 "dnsto" : self.get_address(self.proto),
936 "host" : self.hostname
937 }
938
939 # Send update to the server.
940 response = self.send_request(self.url, username=self.username, password=self.password,
941 data=data)
942
943 # Get the full response message.
944 output = response.read()
945
946 # Handle success messages.
947 if output.startswith("<SUCCESS"):
948 return
949
950 # Handle error codes.
951 if output.startswith("invalid login"):
952 raise DDNSAuthenticationError
953 elif output.startswith("<ERROR CODE=\"704\""):
954 raise DDNSRequestError(_("No valid FQDN was given."))
955 elif output.startswith("<ERROR CODE=\"702\""):
956 raise DDNSInternalServerError
957
958 # If we got here, some other update error happened.
959 raise DDNSUpdateError