]> git.ipfire.org Git - ddns.git/blob - src/ddns/providers.py
6ab9073ecee6a7a283dffcfcbcb0f2a2342717c2
[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.info(_("The dynamic host %(hostname)s (%(provider)s) is already up to date") % \
133 { "hostname" : self.hostname, "provider" : self.name })
134 return
135
136 # Execute the update.
137 self.update()
138
139 logger.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") % \
140 { "hostname" : self.hostname, "provider" : self.name })
141
142 def update(self):
143 raise NotImplementedError
144
145 def is_uptodate(self, protos):
146 """
147 Returns True if this host is already up to date
148 and does not need to change the IP address on the
149 name server.
150 """
151 for proto in protos:
152 addresses = self.core.system.resolve(self.hostname, proto)
153
154 current_address = self.get_address(proto)
155
156 # If no addresses for the given protocol exist, we
157 # are fine...
158 if current_address is None and not addresses:
159 continue
160
161 if not current_address in addresses:
162 return False
163
164 return True
165
166 def send_request(self, *args, **kwargs):
167 """
168 Proxy connection to the send request
169 method.
170 """
171 return self.core.system.send_request(*args, **kwargs)
172
173 def get_address(self, proto, default=None):
174 """
175 Proxy method to get the current IP address.
176 """
177 return self.core.system.get_address(proto) or default
178
179
180 class DDNSProtocolDynDNS2(object):
181 """
182 This is an abstract class that implements the DynDNS updater
183 protocol version 2. As this is a popular way to update dynamic
184 DNS records, this class is supposed make the provider classes
185 shorter and simpler.
186 """
187
188 # Information about the format of the request is to be found
189 # http://dyn.com/support/developers/api/perform-update/
190 # http://dyn.com/support/developers/api/return-codes/
191
192 def _prepare_request_data(self):
193 data = {
194 "hostname" : self.hostname,
195 "myip" : self.get_address("ipv4"),
196 }
197
198 return data
199
200 def update(self):
201 data = self._prepare_request_data()
202
203 # Send update to the server.
204 response = self.send_request(self.url, data=data,
205 username=self.username, password=self.password)
206
207 # Get the full response message.
208 output = response.read()
209
210 # Handle success messages.
211 if output.startswith("good") or output.startswith("nochg"):
212 return
213
214 # Handle error codes.
215 if output == "badauth":
216 raise DDNSAuthenticationError
217 elif output == "aduse":
218 raise DDNSAbuseError
219 elif output == "notfqdn":
220 raise DDNSRequestError(_("No valid FQDN was given."))
221 elif output == "nohost":
222 raise DDNSRequestError(_("Specified host does not exist."))
223 elif output == "911":
224 raise DDNSInternalServerError
225 elif output == "dnserr":
226 raise DDNSInternalServerError(_("DNS error encountered."))
227
228 # If we got here, some other update error happened.
229 raise DDNSUpdateError(_("Server response: %s") % output)
230
231
232 class DDNSResponseParserXML(object):
233 """
234 This class provides a parser for XML responses which
235 will be sent by various providers. This class uses the python
236 shipped XML minidom module to walk through the XML tree and return
237 a requested element.
238 """
239
240 def get_xml_tag_value(self, document, content):
241 # Send input to the parser.
242 xmldoc = xml.dom.minidom.parseString(document)
243
244 # Get XML elements by the given content.
245 element = xmldoc.getElementsByTagName(content)
246
247 # If no element has been found, we directly can return None.
248 if not element:
249 return None
250
251 # Only get the first child from an element, even there are more than one.
252 firstchild = element[0].firstChild
253
254 # Get the value of the child.
255 value = firstchild.nodeValue
256
257 # Return the value.
258 return value
259
260
261 class DDNSProviderAllInkl(DDNSProvider):
262 handle = "all-inkl.com"
263 name = "All-inkl.com"
264 website = "http://all-inkl.com/"
265 protocols = ("ipv4",)
266
267 # There are only information provided by the vendor how to
268 # perform an update on a FRITZ Box. Grab requried informations
269 # from the net.
270 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
271
272 url = "http://dyndns.kasserver.com"
273
274 def update(self):
275 # There is no additional data required so we directly can
276 # send our request.
277 response = self.send_request(self.url, username=self.username, password=self.password)
278
279 # Get the full response message.
280 output = response.read()
281
282 # Handle success messages.
283 if output.startswith("good") or output.startswith("nochg"):
284 return
285
286 # If we got here, some other update error happened.
287 raise DDNSUpdateError
288
289
290 class DDNSProviderBindNsupdate(DDNSProvider):
291 handle = "nsupdate"
292 name = "BIND nsupdate utility"
293 website = "http://en.wikipedia.org/wiki/Nsupdate"
294
295 DEFAULT_TTL = 60
296
297 def update(self):
298 scriptlet = self.__make_scriptlet()
299
300 # -v enables TCP hence we transfer keys and other data that may
301 # exceed the size of one packet.
302 # -t sets the timeout
303 command = ["nsupdate", "-v", "-t", "60"]
304
305 p = subprocess.Popen(command, shell=True,
306 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
307 )
308 stdout, stderr = p.communicate(scriptlet)
309
310 if p.returncode == 0:
311 return
312
313 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p.returncode, stderr))
314
315 def __make_scriptlet(self):
316 scriptlet = []
317
318 # Set a different server the update is sent to.
319 server = self.get("server", None)
320 if server:
321 scriptlet.append("server %s" % server)
322
323 key = self.get("key", None)
324 if key:
325 secret = self.get("secret")
326
327 scriptlet.append("key %s %s" % (key, secret))
328
329 ttl = self.get("ttl", self.DEFAULT_TTL)
330
331 # Perform an update for each supported protocol.
332 for rrtype, proto in (("AAAA", "ipv6"), ("A", "ipv4")):
333 address = self.get_address(proto)
334 if not address:
335 continue
336
337 scriptlet.append("update delete %s. %s" % (self.hostname, rrtype))
338 scriptlet.append("update add %s. %s %s %s" % \
339 (self.hostname, ttl, rrtype, address))
340
341 # Send the actions to the server.
342 scriptlet.append("send")
343 scriptlet.append("quit")
344
345 logger.debug(_("Scriptlet:"))
346 for line in scriptlet:
347 # Masquerade the line with the secret key.
348 if line.startswith("key"):
349 line = "key **** ****"
350
351 logger.debug(" %s" % line)
352
353 return "\n".join(scriptlet)
354
355
356 class DDNSProviderDHS(DDNSProvider):
357 handle = "dhs.org"
358 name = "DHS International"
359 website = "http://dhs.org/"
360 protocols = ("ipv4",)
361
362 # No information about the used update api provided on webpage,
363 # grabed from source code of ez-ipudate.
364
365 url = "http://members.dhs.org/nic/hosts"
366
367 def update(self):
368 data = {
369 "domain" : self.hostname,
370 "ip" : self.get_address("ipv4"),
371 "hostcmd" : "edit",
372 "hostcmdstage" : "2",
373 "type" : "4",
374 }
375
376 # Send update to the server.
377 response = self.send_request(self.url, username=self.username, password=self.password,
378 data=data)
379
380 # Handle success messages.
381 if response.code == 200:
382 return
383
384 # If we got here, some other update error happened.
385 raise DDNSUpdateError
386
387
388 class DDNSProviderDNSpark(DDNSProvider):
389 handle = "dnspark.com"
390 name = "DNS Park"
391 website = "http://dnspark.com/"
392 protocols = ("ipv4",)
393
394 # Informations to the used api can be found here:
395 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
396
397 url = "https://control.dnspark.com/api/dynamic/update.php"
398
399 def update(self):
400 data = {
401 "domain" : self.hostname,
402 "ip" : self.get_address("ipv4"),
403 }
404
405 # Send update to the server.
406 response = self.send_request(self.url, username=self.username, password=self.password,
407 data=data)
408
409 # Get the full response message.
410 output = response.read()
411
412 # Handle success messages.
413 if output.startswith("ok") or output.startswith("nochange"):
414 return
415
416 # Handle error codes.
417 if output == "unauth":
418 raise DDNSAuthenticationError
419 elif output == "abuse":
420 raise DDNSAbuseError
421 elif output == "blocked":
422 raise DDNSBlockedError
423 elif output == "nofqdn":
424 raise DDNSRequestError(_("No valid FQDN was given."))
425 elif output == "nohost":
426 raise DDNSRequestError(_("Invalid hostname specified."))
427 elif output == "notdyn":
428 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
429 elif output == "invalid":
430 raise DDNSRequestError(_("Invalid IP address has been sent."))
431
432 # If we got here, some other update error happened.
433 raise DDNSUpdateError
434
435
436 class DDNSProviderDtDNS(DDNSProvider):
437 handle = "dtdns.com"
438 name = "DtDNS"
439 website = "http://dtdns.com/"
440 protocols = ("ipv4",)
441
442 # Information about the format of the HTTPS request is to be found
443 # http://www.dtdns.com/dtsite/updatespec
444
445 url = "https://www.dtdns.com/api/autodns.cfm"
446
447 def update(self):
448 data = {
449 "ip" : self.get_address("ipv4"),
450 "id" : self.hostname,
451 "pw" : self.password
452 }
453
454 # Send update to the server.
455 response = self.send_request(self.url, data=data)
456
457 # Get the full response message.
458 output = response.read()
459
460 # Remove all leading and trailing whitespace.
461 output = output.strip()
462
463 # Handle success messages.
464 if "now points to" in output:
465 return
466
467 # Handle error codes.
468 if output == "No hostname to update was supplied.":
469 raise DDNSRequestError(_("No hostname specified."))
470
471 elif output == "The hostname you supplied is not valid.":
472 raise DDNSRequestError(_("Invalid hostname specified."))
473
474 elif output == "The password you supplied is not valid.":
475 raise DDNSAuthenticationError
476
477 elif output == "Administration has disabled this account.":
478 raise DDNSRequestError(_("Account has been disabled."))
479
480 elif output == "Illegal character in IP.":
481 raise DDNSRequestError(_("Invalid IP address has been sent."))
482
483 elif output == "Too many failed requests.":
484 raise DDNSRequestError(_("Too many failed requests."))
485
486 # If we got here, some other update error happened.
487 raise DDNSUpdateError
488
489
490 class DDNSProviderDynDNS(DDNSProtocolDynDNS2, DDNSProvider):
491 handle = "dyndns.org"
492 name = "Dyn"
493 website = "http://dyn.com/dns/"
494 protocols = ("ipv4",)
495
496 # Information about the format of the request is to be found
497 # http://http://dyn.com/support/developers/api/perform-update/
498 # http://dyn.com/support/developers/api/return-codes/
499
500 url = "https://members.dyndns.org/nic/update"
501
502
503 class DDNSProviderDynU(DDNSProtocolDynDNS2, DDNSProvider):
504 handle = "dynu.com"
505 name = "Dynu"
506 website = "http://dynu.com/"
507 protocols = ("ipv6", "ipv4",)
508
509 # Detailed information about the request and response codes
510 # are available on the providers webpage.
511 # http://dynu.com/Default.aspx?page=dnsapi
512
513 url = "https://api.dynu.com/nic/update"
514
515 def _prepare_request_data(self):
516 data = DDNSProtocolDynDNS2._prepare_request_data(self)
517
518 # This one supports IPv6
519 data.update({
520 "myipv6" : self.get_address("ipv6"),
521 })
522
523 return data
524
525
526 class DDNSProviderEasyDNS(DDNSProtocolDynDNS2, DDNSProvider):
527 handle = "easydns.com"
528 name = "EasyDNS"
529 website = "http://www.easydns.com/"
530 protocols = ("ipv4",)
531
532 # There is only some basic documentation provided by the vendor,
533 # also searching the web gain very poor results.
534 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
535
536 url = "http://api.cp.easydns.com/dyn/tomato.php"
537
538
539 class DDNSProviderEnomCom(DDNSResponseParserXML, DDNSProvider):
540 handle = "enom.com"
541 name = "eNom Inc."
542 website = "http://www.enom.com/"
543
544 # There are very detailed information about how to send an update request and
545 # the respone codes.
546 # http://www.enom.com/APICommandCatalog/
547
548 url = "https://dynamic.name-services.com/interface.asp"
549
550 def update(self):
551 data = {
552 "command" : "setdnshost",
553 "responsetype" : "xml",
554 "address" : self.get_address("ipv4"),
555 "domainpassword" : self.password,
556 "zone" : self.hostname
557 }
558
559 # Send update to the server.
560 response = self.send_request(self.url, data=data)
561
562 # Get the full response message.
563 output = response.read()
564
565 # Handle success messages.
566 if self.get_xml_tag_value(output, "ErrCount") == "0":
567 return
568
569 # Handle error codes.
570 errorcode = self.get_xml_tag_value(output, "ResponseNumber")
571
572 if errorcode == "304155":
573 raise DDNSAuthenticationError
574 elif errorcode == "304153":
575 raise DDNSRequestError(_("Domain not found."))
576
577 # If we got here, some other update error happened.
578 raise DDNSUpdateError
579
580
581 class DDNSProviderEntryDNS(DDNSProvider):
582 handle = "entrydns.net"
583 name = "EntryDNS"
584 website = "http://entrydns.net/"
585 protocols = ("ipv4",)
586
587 # Some very tiny details about their so called "Simple API" can be found
588 # here: https://entrydns.net/help
589 url = "https://entrydns.net/records/modify"
590
591 def update(self):
592 data = {
593 "ip" : self.get_address("ipv4")
594 }
595
596 # Add auth token to the update url.
597 url = "%s/%s" % (self.url, self.token)
598
599 # Send update to the server.
600 try:
601 response = self.send_request(url, method="PUT", data=data)
602
603 # Handle error codes
604 except urllib2.HTTPError, e:
605 if e.code == 404:
606 raise DDNSAuthenticationError
607
608 elif e.code == 422:
609 raise DDNSRequestError(_("An invalid IP address was submitted"))
610
611 raise
612
613 # Handle success messages.
614 if response.code == 200:
615 return
616
617 # If we got here, some other update error happened.
618 raise DDNSUpdateError
619
620
621 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider):
622 handle = "freedns.afraid.org"
623 name = "freedns.afraid.org"
624 website = "http://freedns.afraid.org/"
625
626 # No information about the request or response could be found on the vendor
627 # page. All used values have been collected by testing.
628 url = "https://freedns.afraid.org/dynamic/update.php"
629
630 @property
631 def proto(self):
632 return self.get("proto")
633
634 def update(self):
635 address = self.get_address(self.proto)
636
637 data = {
638 "address" : address,
639 }
640
641 # Add auth token to the update url.
642 url = "%s?%s" % (self.url, self.token)
643
644 # Send update to the server.
645 response = self.send_request(url, data=data)
646
647 # Get the full response message.
648 output = response.read()
649
650 # Handle success messages.
651 if output.startswith("Updated") or "has not changed" in output:
652 return
653
654 # Handle error codes.
655 if output == "ERROR: Unable to locate this record":
656 raise DDNSAuthenticationError
657 elif "is an invalid IP address" in output:
658 raise DDNSRequestError(_("Invalid IP address has been sent."))
659
660 # If we got here, some other update error happened.
661 raise DDNSUpdateError
662
663
664 class DDNSProviderLightningWireLabs(DDNSProvider):
665 handle = "dns.lightningwirelabs.com"
666 name = "Lightning Wire Labs DNS Service"
667 website = "http://dns.lightningwirelabs.com/"
668
669 # Information about the format of the HTTPS request is to be found
670 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
671
672 url = "https://dns.lightningwirelabs.com/update"
673
674 def update(self):
675 data = {
676 "hostname" : self.hostname,
677 "address6" : self.get_address("ipv6", "-"),
678 "address4" : self.get_address("ipv4", "-"),
679 }
680
681 # Check if a token has been set.
682 if self.token:
683 data["token"] = self.token
684
685 # Check for username and password.
686 elif self.username and self.password:
687 data.update({
688 "username" : self.username,
689 "password" : self.password,
690 })
691
692 # Raise an error if no auth details are given.
693 else:
694 raise DDNSConfigurationError
695
696 # Send update to the server.
697 response = self.send_request(self.url, data=data)
698
699 # Handle success messages.
700 if response.code == 200:
701 return
702
703 # If we got here, some other update error happened.
704 raise DDNSUpdateError
705
706
707 class DDNSProviderNamecheap(DDNSResponseParserXML, DDNSProvider):
708 handle = "namecheap.com"
709 name = "Namecheap"
710 website = "http://namecheap.com"
711 protocols = ("ipv4",)
712
713 # Information about the format of the HTTP request is to be found
714 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
715 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
716
717 url = "https://dynamicdns.park-your-domain.com/update"
718
719 def update(self):
720 # Namecheap requires the hostname splitted into a host and domain part.
721 host, domain = self.hostname.split(".", 1)
722
723 data = {
724 "ip" : self.get_address("ipv4"),
725 "password" : self.password,
726 "host" : host,
727 "domain" : domain
728 }
729
730 # Send update to the server.
731 response = self.send_request(self.url, data=data)
732
733 # Get the full response message.
734 output = response.read()
735
736 # Handle success messages.
737 if self.get_xml_tag_value(output, "IP") == self.get_address("ipv4"):
738 return
739
740 # Handle error codes.
741 errorcode = self.get_xml_tag_value(output, "ResponseNumber")
742
743 if errorcode == "304156":
744 raise DDNSAuthenticationError
745 elif errorcode == "316153":
746 raise DDNSRequestError(_("Domain not found."))
747 elif errorcode == "316154":
748 raise DDNSRequestError(_("Domain not active."))
749 elif errorcode in ("380098", "380099"):
750 raise DDNSInternalServerError
751
752 # If we got here, some other update error happened.
753 raise DDNSUpdateError
754
755
756 class DDNSProviderNOIP(DDNSProtocolDynDNS2, DDNSProvider):
757 handle = "no-ip.com"
758 name = "No-IP"
759 website = "http://www.no-ip.com/"
760 protocols = ("ipv4",)
761
762 # Information about the format of the HTTP request is to be found
763 # here: http://www.no-ip.com/integrate/request and
764 # here: http://www.no-ip.com/integrate/response
765
766 url = "http://dynupdate.no-ip.com/nic/update"
767
768 def _prepare_request_data(self):
769 data = {
770 "hostname" : self.hostname,
771 "address" : self.get_address("ipv4"),
772 }
773
774 return data
775
776
777 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2, DDNSProvider):
778 handle = "nsupdate.info"
779 name = "nsupdate.info"
780 website = "http://www.nsupdate.info/"
781 protocols = ("ipv6", "ipv4",)
782
783 # Information about the format of the HTTP request can be found
784 # after login on the provider user intrface and here:
785 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
786
787 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
788 # and for the password a so called secret.
789 @property
790 def username(self):
791 return self.get("hostname")
792
793 @property
794 def password(self):
795 return self.get("secret")
796
797 @property
798 def proto(self):
799 return self.get("proto")
800
801 @property
802 def url(self):
803 # The update URL is different by the used protocol.
804 if self.proto == "ipv4":
805 return "https://ipv4.nsupdate.info/nic/update"
806 elif self.proto == "ipv6":
807 return "https://ipv6.nsupdate.info/nic/update"
808 else:
809 raise DDNSUpdateError(_("Invalid protocol has been given"))
810
811 def _prepare_request_data(self):
812 data = {
813 "myip" : self.get_address(self.proto),
814 }
815
816 return data
817
818
819 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2, DDNSProvider):
820 handle = "opendns.com"
821 name = "OpenDNS"
822 website = "http://www.opendns.com"
823
824 # Detailed information about the update request and possible
825 # response codes can be obtained from here:
826 # https://support.opendns.com/entries/23891440
827
828 url = "https://updates.opendns.com/nic/update"
829
830 @property
831 def proto(self):
832 return self.get("proto")
833
834 def _prepare_request_data(self):
835 data = {
836 "hostname" : self.hostname,
837 "myip" : self.get_address(self.proto)
838 }
839
840 return data
841
842
843 class DDNSProviderOVH(DDNSProtocolDynDNS2, DDNSProvider):
844 handle = "ovh.com"
845 name = "OVH"
846 website = "http://www.ovh.com/"
847 protocols = ("ipv4",)
848
849 # OVH only provides very limited information about how to
850 # update a DynDNS host. They only provide the update url
851 # on the their german subpage.
852 #
853 # http://hilfe.ovh.de/DomainDynHost
854
855 url = "https://www.ovh.com/nic/update"
856
857 def _prepare_request_data(self):
858 data = DDNSProtocolDynDNS2._prepare_request_data(self)
859 data.update({
860 "system" : "dyndns",
861 })
862
863 return data
864
865
866 class DDNSProviderRegfish(DDNSProvider):
867 handle = "regfish.com"
868 name = "Regfish GmbH"
869 website = "http://www.regfish.com/"
870
871 # A full documentation to the providers api can be found here
872 # but is only available in german.
873 # https://www.regfish.de/domains/dyndns/dokumentation
874
875 url = "https://dyndns.regfish.de/"
876
877 def update(self):
878 data = {
879 "fqdn" : self.hostname,
880 }
881
882 # Check if we update an IPv6 address.
883 address6 = self.get_address("ipv6")
884 if address6:
885 data["ipv6"] = address6
886
887 # Check if we update an IPv4 address.
888 address4 = self.get_address("ipv4")
889 if address4:
890 data["ipv4"] = address4
891
892 # Raise an error if none address is given.
893 if not data.has_key("ipv6") and not data.has_key("ipv4"):
894 raise DDNSConfigurationError
895
896 # Check if a token has been set.
897 if self.token:
898 data["token"] = self.token
899
900 # Raise an error if no token and no useranem and password
901 # are given.
902 elif not self.username and not self.password:
903 raise DDNSConfigurationError(_("No Auth details specified."))
904
905 # HTTP Basic Auth is only allowed if no token is used.
906 if self.token:
907 # Send update to the server.
908 response = self.send_request(self.url, data=data)
909 else:
910 # Send update to the server.
911 response = self.send_request(self.url, username=self.username, password=self.password,
912 data=data)
913
914 # Get the full response message.
915 output = response.read()
916
917 # Handle success messages.
918 if "100" in output or "101" in output:
919 return
920
921 # Handle error codes.
922 if "401" or "402" in output:
923 raise DDNSAuthenticationError
924 elif "408" in output:
925 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
926 elif "409" in output:
927 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
928 elif "412" in output:
929 raise DDNSRequestError(_("No valid FQDN was given."))
930 elif "414" in output:
931 raise DDNSInternalServerError
932
933 # If we got here, some other update error happened.
934 raise DDNSUpdateError
935
936
937 class DDNSProviderSelfhost(DDNSProtocolDynDNS2, DDNSProvider):
938 handle = "selfhost.de"
939 name = "Selfhost.de"
940 website = "http://www.selfhost.de/"
941 protocols = ("ipv4",)
942
943 url = "https://carol.selfhost.de/nic/update"
944
945 def _prepare_request_data(self):
946 data = DDNSProtocolDynDNS2._prepare_request_data(self)
947 data.update({
948 "hostname" : "1",
949 })
950
951 return data
952
953
954 class DDNSProviderSPDNS(DDNSProtocolDynDNS2, DDNSProvider):
955 handle = "spdns.org"
956 name = "SPDNS"
957 website = "http://spdns.org/"
958 protocols = ("ipv4",)
959
960 # Detailed information about request and response codes are provided
961 # by the vendor. They are using almost the same mechanism and status
962 # codes as dyndns.org so we can inherit all those stuff.
963 #
964 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
965 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
966
967 url = "https://update.spdns.de/nic/update"
968
969
970 class DDNSProviderStrato(DDNSProtocolDynDNS2, DDNSProvider):
971 handle = "strato.com"
972 name = "Strato AG"
973 website = "http:/www.strato.com/"
974 protocols = ("ipv4",)
975
976 # Information about the request and response can be obtained here:
977 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
978
979 url = "https://dyndns.strato.com/nic/update"
980
981
982 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2, DDNSProvider):
983 handle = "twodns.de"
984 name = "TwoDNS"
985 website = "http://www.twodns.de"
986 protocols = ("ipv4",)
987
988 # Detailed information about the request can be found here
989 # http://twodns.de/en/faqs
990 # http://twodns.de/en/api
991
992 url = "https://update.twodns.de/update"
993
994 def _prepare_request_data(self):
995 data = {
996 "ip" : self.get_address("ipv4"),
997 "hostname" : self.hostname
998 }
999
1000 return data
1001
1002
1003 class DDNSProviderUdmedia(DDNSProtocolDynDNS2, DDNSProvider):
1004 handle = "udmedia.de"
1005 name = "Udmedia GmbH"
1006 website = "http://www.udmedia.de"
1007 protocols = ("ipv4",)
1008
1009 # Information about the request can be found here
1010 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1011
1012 url = "https://www.udmedia.de/nic/update"
1013
1014
1015 class DDNSProviderVariomedia(DDNSProtocolDynDNS2, DDNSProvider):
1016 handle = "variomedia.de"
1017 name = "Variomedia"
1018 website = "http://www.variomedia.de/"
1019 protocols = ("ipv6", "ipv4",)
1020
1021 # Detailed information about the request can be found here
1022 # https://dyndns.variomedia.de/
1023
1024 url = "https://dyndns.variomedia.de/nic/update"
1025
1026 @property
1027 def proto(self):
1028 return self.get("proto")
1029
1030 def _prepare_request_data(self):
1031 data = {
1032 "hostname" : self.hostname,
1033 "myip" : self.get_address(self.proto)
1034 }
1035
1036 return data
1037
1038
1039 class DDNSProviderZoneedit(DDNSProtocolDynDNS2, DDNSProvider):
1040 handle = "zoneedit.com"
1041 name = "Zoneedit"
1042 website = "http://www.zoneedit.com"
1043 protocols = ("ipv4",)
1044
1045 # Detailed information about the request and the response codes can be
1046 # obtained here:
1047 # http://www.zoneedit.com/doc/api/other.html
1048 # http://www.zoneedit.com/faq.html
1049
1050 url = "https://dynamic.zoneedit.com/auth/dynamic.html"
1051
1052 @property
1053 def proto(self):
1054 return self.get("proto")
1055
1056 def update(self):
1057 data = {
1058 "dnsto" : self.get_address(self.proto),
1059 "host" : self.hostname
1060 }
1061
1062 # Send update to the server.
1063 response = self.send_request(self.url, username=self.username, password=self.password,
1064 data=data)
1065
1066 # Get the full response message.
1067 output = response.read()
1068
1069 # Handle success messages.
1070 if output.startswith("<SUCCESS"):
1071 return
1072
1073 # Handle error codes.
1074 if output.startswith("invalid login"):
1075 raise DDNSAuthenticationError
1076 elif output.startswith("<ERROR CODE=\"704\""):
1077 raise DDNSRequestError(_("No valid FQDN was given."))
1078 elif output.startswith("<ERROR CODE=\"702\""):
1079 raise DDNSInternalServerError
1080
1081 # If we got here, some other update error happened.
1082 raise DDNSUpdateError