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