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