]>
git.ipfire.org Git - people/ms/ddns.git/blob - src/ddns/providers.py
2 ###############################################################################
4 # ddns - A dynamic DNS client for IPFire #
5 # Copyright (C) 2012 IPFire development team #
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. #
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. #
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/>. #
20 ###############################################################################
26 import xml
.dom
.minidom
30 # Import all possible exception types.
33 logger
= logging
.getLogger("ddns.providers")
40 Returns a dict with all automatically registered providers.
42 return _providers
.copy()
44 class DDNSProvider(object):
45 # A short string that uniquely identifies
49 # The full name of the provider.
52 # A weburl to the homepage of the provider.
53 # (Where to register a new account?)
56 # A list of supported protocols.
57 protocols
= ("ipv6", "ipv4")
61 # holdoff time - Number of days no update is performed unless
62 # the IP address has changed.
65 # Automatically register all providers.
66 class __metaclass__(type):
67 def __init__(provider
, name
, bases
, dict):
68 type.__init
__(provider
, name
, bases
, dict)
70 # The main class from which is inherited is not registered
72 if name
== "DDNSProvider":
75 if not all((provider
.handle
, provider
.name
, provider
.website
)):
76 raise DDNSError(_("Provider is not properly configured"))
78 assert not _providers
.has_key(provider
.handle
), \
79 "Provider '%s' has already been registered" % provider
.handle
81 _providers
[provider
.handle
] = provider
83 def __init__(self
, core
, **settings
):
86 # Copy a set of default settings and
87 # update them by those from the configuration file.
88 self
.settings
= self
.DEFAULT_SETTINGS
.copy()
89 self
.settings
.update(settings
)
92 return "<DDNS Provider %s (%s)>" % (self
.name
, self
.handle
)
94 def __cmp__(self
, other
):
95 return cmp(self
.hostname
, other
.hostname
)
101 def get(self
, key
, default
=None):
103 Get a setting from the settings dictionary.
105 return self
.settings
.get(key
, default
)
110 Fast access to the hostname.
112 return self
.get("hostname")
117 Fast access to the username.
119 return self
.get("username")
124 Fast access to the password.
126 return self
.get("password")
131 Fast access to the token.
133 return self
.get("token")
135 def __call__(self
, force
=False):
137 logger
.debug(_("Updating %s forced") % self
.hostname
)
139 # Do nothing if no update is required
140 elif not self
.requires_update
:
143 # Execute the update.
147 # In case of any errors, log the failed request and
148 # raise the exception.
149 except DDNSError
as e
:
150 self
.core
.db
.log_failure(self
.hostname
, e
)
153 logger
.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") % \
154 { "hostname" : self
.hostname
, "provider" : self
.name
})
155 self
.core
.db
.log_success(self
.hostname
)
158 for protocol
in self
.protocols
:
159 if self
.have_address(protocol
):
160 self
.update_protocol(protocol
)
162 self
.remove_protocol(protocol
)
164 def update_protocol(self
, proto
):
165 raise NotImplementedError
167 def remove_protocol(self
, proto
):
168 logger
.warning(_("%(hostname)s current resolves to an IP address"
169 " of the %(proto)s protocol which could not be removed by ddns") % \
170 { "hostname" : self
.hostname
, "proto" : proto
})
172 # Maybe this will raise NotImplementedError at some time
173 #raise NotImplementedError
176 def requires_update(self
):
177 # If the IP addresses have changed, an update is required
178 if self
.ip_address_changed(self
.protocols
):
179 logger
.debug(_("An update for %(hostname)s (%(provider)s)"
180 " is performed because of an IP address change") % \
181 { "hostname" : self
.hostname
, "provider" : self
.name
})
185 # If the holdoff time has expired, an update is required, too
186 if self
.holdoff_time_expired():
187 logger
.debug(_("An update for %(hostname)s (%(provider)s)"
188 " is performed because the holdoff time has expired") % \
189 { "hostname" : self
.hostname
, "provider" : self
.name
})
193 # Otherwise, we don't need to perform an update
194 logger
.debug(_("No update required for %(hostname)s (%(provider)s)") % \
195 { "hostname" : self
.hostname
, "provider" : self
.name
})
199 def ip_address_changed(self
, protos
):
201 Returns True if this host is already up to date
202 and does not need to change the IP address on the
206 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
208 current_address
= self
.get_address(proto
)
210 # If no addresses for the given protocol exist, we
212 if current_address
is None and not addresses
:
215 if not current_address
in addresses
:
220 def holdoff_time_expired(self
):
222 Returns true if the holdoff time has expired
223 and the host requires an update
225 # If no holdoff days is defined, we cannot go on
226 if not self
.holdoff_days
:
229 # Get the timestamp of the last successfull update
230 last_update
= self
.db
.last_update(self
.hostname
)
232 # If no timestamp has been recorded, no update has been
233 # performed. An update should be performed now.
237 # Determine when the holdoff time ends
238 holdoff_end
= last_update
+ datetime
.timedelta(days
=self
.holdoff_days
)
240 now
= datetime
.datetime
.utcnow()
242 if now
>= holdoff_end
:
243 logger
.debug("The holdoff time has expired for %s" % self
.hostname
)
246 logger
.debug("Updates for %s are held off until %s" % \
247 (self
.hostname
, holdoff_end
))
250 def send_request(self
, *args
, **kwargs
):
252 Proxy connection to the send request
255 return self
.core
.system
.send_request(*args
, **kwargs
)
257 def get_address(self
, proto
, default
=None):
259 Proxy method to get the current IP address.
261 return self
.core
.system
.get_address(proto
) or default
263 def have_address(self
, proto
):
265 Returns True if an IP address for the given protocol
268 address
= self
.get_address(proto
)
276 class DDNSProtocolDynDNS2(object):
278 This is an abstract class that implements the DynDNS updater
279 protocol version 2. As this is a popular way to update dynamic
280 DNS records, this class is supposed make the provider classes
284 # Information about the format of the request is to be found
285 # http://dyn.com/support/developers/api/perform-update/
286 # http://dyn.com/support/developers/api/return-codes/
288 def prepare_request_data(self
, proto
):
290 "hostname" : self
.hostname
,
291 "myip" : self
.get_address(proto
),
296 def update_protocol(self
, proto
):
297 data
= self
.prepare_request_data(proto
)
299 return self
.send_request(data
)
301 def send_request(self
, data
):
302 # Send update to the server.
303 response
= DDNSProvider
.send_request(self
, self
.url
, data
=data
,
304 username
=self
.username
, password
=self
.password
)
306 # Get the full response message.
307 output
= response
.read()
309 # Handle success messages.
310 if output
.startswith("good") or output
.startswith("nochg"):
313 # Handle error codes.
314 if output
== "badauth":
315 raise DDNSAuthenticationError
316 elif output
== "abuse":
318 elif output
== "notfqdn":
319 raise DDNSRequestError(_("No valid FQDN was given."))
320 elif output
== "nohost":
321 raise DDNSRequestError(_("Specified host does not exist."))
322 elif output
== "911":
323 raise DDNSInternalServerError
324 elif output
== "dnserr":
325 raise DDNSInternalServerError(_("DNS error encountered."))
326 elif output
== "badagent":
327 raise DDNSBlockedError
329 # If we got here, some other update error happened.
330 raise DDNSUpdateError(_("Server response: %s") % output
)
333 class DDNSResponseParserXML(object):
335 This class provides a parser for XML responses which
336 will be sent by various providers. This class uses the python
337 shipped XML minidom module to walk through the XML tree and return
341 def get_xml_tag_value(self
, document
, content
):
342 # Send input to the parser.
343 xmldoc
= xml
.dom
.minidom
.parseString(document
)
345 # Get XML elements by the given content.
346 element
= xmldoc
.getElementsByTagName(content
)
348 # If no element has been found, we directly can return None.
352 # Only get the first child from an element, even there are more than one.
353 firstchild
= element
[0].firstChild
355 # Get the value of the child.
356 value
= firstchild
.nodeValue
362 class DDNSProviderAllInkl(DDNSProvider
):
363 handle
= "all-inkl.com"
364 name
= "All-inkl.com"
365 website
= "http://all-inkl.com/"
366 protocols
= ("ipv4",)
368 # There are only information provided by the vendor how to
369 # perform an update on a FRITZ Box. Grab requried informations
371 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
373 url
= "http://dyndns.kasserver.com"
376 # There is no additional data required so we directly can
378 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
)
380 # Get the full response message.
381 output
= response
.read()
383 # Handle success messages.
384 if output
.startswith("good") or output
.startswith("nochg"):
387 # If we got here, some other update error happened.
388 raise DDNSUpdateError
391 class DDNSProviderBindNsupdate(DDNSProvider
):
393 name
= "BIND nsupdate utility"
394 website
= "http://en.wikipedia.org/wiki/Nsupdate"
399 scriptlet
= self
.__make
_scriptlet
()
401 # -v enables TCP hence we transfer keys and other data that may
402 # exceed the size of one packet.
403 # -t sets the timeout
404 command
= ["nsupdate", "-v", "-t", "60"]
406 p
= subprocess
.Popen(command
, shell
=True,
407 stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
409 stdout
, stderr
= p
.communicate(scriptlet
)
411 if p
.returncode
== 0:
414 raise DDNSError("nsupdate terminated with error code: %s\n %s" % (p
.returncode
, stderr
))
416 def __make_scriptlet(self
):
419 # Set a different server the update is sent to.
420 server
= self
.get("server", None)
422 scriptlet
.append("server %s" % server
)
424 # Set the DNS zone the host should be added to.
425 zone
= self
.get("zone", None)
427 scriptlet
.append("zone %s" % zone
)
429 key
= self
.get("key", None)
431 secret
= self
.get("secret")
433 scriptlet
.append("key %s %s" % (key
, secret
))
435 ttl
= self
.get("ttl", self
.DEFAULT_TTL
)
437 # Perform an update for each supported protocol.
438 for rrtype
, proto
in (("AAAA", "ipv6"), ("A", "ipv4")):
439 address
= self
.get_address(proto
)
443 scriptlet
.append("update delete %s. %s" % (self
.hostname
, rrtype
))
444 scriptlet
.append("update add %s. %s %s %s" % \
445 (self
.hostname
, ttl
, rrtype
, address
))
447 # Send the actions to the server.
448 scriptlet
.append("send")
449 scriptlet
.append("quit")
451 logger
.debug(_("Scriptlet:"))
452 for line
in scriptlet
:
453 # Masquerade the line with the secret key.
454 if line
.startswith("key"):
455 line
= "key **** ****"
457 logger
.debug(" %s" % line
)
459 return "\n".join(scriptlet
)
462 class DDNSProviderDHS(DDNSProvider
):
464 name
= "DHS International"
465 website
= "http://dhs.org/"
466 protocols
= ("ipv4",)
468 # No information about the used update api provided on webpage,
469 # grabed from source code of ez-ipudate.
471 url
= "http://members.dhs.org/nic/hosts"
473 def update_protocol(self
, proto
):
475 "domain" : self
.hostname
,
476 "ip" : self
.get_address(proto
),
478 "hostcmdstage" : "2",
482 # Send update to the server.
483 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
486 # Handle success messages.
487 if response
.code
== 200:
490 # If we got here, some other update error happened.
491 raise DDNSUpdateError
494 class DDNSProviderDNSpark(DDNSProvider
):
495 handle
= "dnspark.com"
497 website
= "http://dnspark.com/"
498 protocols
= ("ipv4",)
500 # Informations to the used api can be found here:
501 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
503 url
= "https://control.dnspark.com/api/dynamic/update.php"
505 def update_protocol(self
, proto
):
507 "domain" : self
.hostname
,
508 "ip" : self
.get_address(proto
),
511 # Send update to the server.
512 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
515 # Get the full response message.
516 output
= response
.read()
518 # Handle success messages.
519 if output
.startswith("ok") or output
.startswith("nochange"):
522 # Handle error codes.
523 if output
== "unauth":
524 raise DDNSAuthenticationError
525 elif output
== "abuse":
527 elif output
== "blocked":
528 raise DDNSBlockedError
529 elif output
== "nofqdn":
530 raise DDNSRequestError(_("No valid FQDN was given."))
531 elif output
== "nohost":
532 raise DDNSRequestError(_("Invalid hostname specified."))
533 elif output
== "notdyn":
534 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
535 elif output
== "invalid":
536 raise DDNSRequestError(_("Invalid IP address has been sent."))
538 # If we got here, some other update error happened.
539 raise DDNSUpdateError
542 class DDNSProviderDtDNS(DDNSProvider
):
545 website
= "http://dtdns.com/"
546 protocols
= ("ipv4",)
548 # Information about the format of the HTTPS request is to be found
549 # http://www.dtdns.com/dtsite/updatespec
551 url
= "https://www.dtdns.com/api/autodns.cfm"
553 def update_protocol(self
, proto
):
555 "ip" : self
.get_address(proto
),
556 "id" : self
.hostname
,
560 # Send update to the server.
561 response
= self
.send_request(self
.url
, data
=data
)
563 # Get the full response message.
564 output
= response
.read()
566 # Remove all leading and trailing whitespace.
567 output
= output
.strip()
569 # Handle success messages.
570 if "now points to" in output
:
573 # Handle error codes.
574 if output
== "No hostname to update was supplied.":
575 raise DDNSRequestError(_("No hostname specified."))
577 elif output
== "The hostname you supplied is not valid.":
578 raise DDNSRequestError(_("Invalid hostname specified."))
580 elif output
== "The password you supplied is not valid.":
581 raise DDNSAuthenticationError
583 elif output
== "Administration has disabled this account.":
584 raise DDNSRequestError(_("Account has been disabled."))
586 elif output
== "Illegal character in IP.":
587 raise DDNSRequestError(_("Invalid IP address has been sent."))
589 elif output
== "Too many failed requests.":
590 raise DDNSRequestError(_("Too many failed requests."))
592 # If we got here, some other update error happened.
593 raise DDNSUpdateError
596 class DDNSProviderDynDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
597 handle
= "dyndns.org"
599 website
= "http://dyn.com/dns/"
600 protocols
= ("ipv4",)
602 # Information about the format of the request is to be found
603 # http://http://dyn.com/support/developers/api/perform-update/
604 # http://dyn.com/support/developers/api/return-codes/
606 url
= "https://members.dyndns.org/nic/update"
609 class DDNSProviderDynU(DDNSProtocolDynDNS2
, DDNSProvider
):
612 website
= "http://dynu.com/"
613 protocols
= ("ipv6", "ipv4",)
615 # Detailed information about the request and response codes
616 # are available on the providers webpage.
617 # http://dynu.com/Default.aspx?page=dnsapi
619 url
= "https://api.dynu.com/nic/update"
621 # DynU sends the IPv6 and IPv4 address in one request
624 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, "ipv4")
626 # This one supports IPv6
627 myipv6
= self
.get_address("ipv6")
629 # Add update information if we have an IPv6 address.
631 data
["myipv6"] = myipv6
633 self
._send
_request
(data
)
636 class DDNSProviderEasyDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
637 handle
= "easydns.com"
639 website
= "http://www.easydns.com/"
640 protocols
= ("ipv4",)
642 # There is only some basic documentation provided by the vendor,
643 # also searching the web gain very poor results.
644 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
646 url
= "http://api.cp.easydns.com/dyn/tomato.php"
649 class DDNSProviderDomopoli(DDNSProtocolDynDNS2
, DDNSProvider
):
650 handle
= "domopoli.de"
652 website
= "http://domopoli.de/"
653 protocols
= ("ipv4",)
655 # https://www.domopoli.de/?page=howto#DynDns_start
657 url
= "http://dyndns.domopoli.de/nic/update"
660 class DDNSProviderDynsNet(DDNSProvider
):
663 website
= "http://www.dyns.net/"
664 protocols
= ("ipv4",)
666 # There is very detailed informatio about how to send the update request and
667 # the possible response codes. (Currently we are using the v1.1 proto)
668 # http://www.dyns.net/documentation/technical/protocol/
670 url
= "http://www.dyns.net/postscript011.php"
672 def update_protocol(self
, proto
):
674 "ip" : self
.get_address(proto
),
675 "host" : self
.hostname
,
676 "username" : self
.username
,
677 "password" : self
.password
,
680 # Send update to the server.
681 response
= self
.send_request(self
.url
, data
=data
)
683 # Get the full response message.
684 output
= response
.read()
686 # Handle success messages.
687 if output
.startswith("200"):
690 # Handle error codes.
691 if output
.startswith("400"):
692 raise DDNSRequestError(_("Malformed request has been sent."))
693 elif output
.startswith("401"):
694 raise DDNSAuthenticationError
695 elif output
.startswith("402"):
696 raise DDNSRequestError(_("Too frequent update requests have been sent."))
697 elif output
.startswith("403"):
698 raise DDNSInternalServerError
700 # If we got here, some other update error happened.
701 raise DDNSUpdateError(_("Server response: %s") % output
)
704 class DDNSProviderEnomCom(DDNSResponseParserXML
, DDNSProvider
):
707 website
= "http://www.enom.com/"
708 protocols
= ("ipv4",)
710 # There are very detailed information about how to send an update request and
712 # http://www.enom.com/APICommandCatalog/
714 url
= "https://dynamic.name-services.com/interface.asp"
716 def update_protocol(self
, proto
):
718 "command" : "setdnshost",
719 "responsetype" : "xml",
720 "address" : self
.get_address(proto
),
721 "domainpassword" : self
.password
,
722 "zone" : self
.hostname
725 # Send update to the server.
726 response
= self
.send_request(self
.url
, data
=data
)
728 # Get the full response message.
729 output
= response
.read()
731 # Handle success messages.
732 if self
.get_xml_tag_value(output
, "ErrCount") == "0":
735 # Handle error codes.
736 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
738 if errorcode
== "304155":
739 raise DDNSAuthenticationError
740 elif errorcode
== "304153":
741 raise DDNSRequestError(_("Domain not found."))
743 # If we got here, some other update error happened.
744 raise DDNSUpdateError
747 class DDNSProviderEntryDNS(DDNSProvider
):
748 handle
= "entrydns.net"
750 website
= "http://entrydns.net/"
751 protocols
= ("ipv4",)
753 # Some very tiny details about their so called "Simple API" can be found
754 # here: https://entrydns.net/help
755 url
= "https://entrydns.net/records/modify"
757 def update_protocol(self
, proto
):
759 "ip" : self
.get_address(proto
),
762 # Add auth token to the update url.
763 url
= "%s/%s" % (self
.url
, self
.token
)
765 # Send update to the server.
767 response
= self
.send_request(url
, data
=data
)
770 except urllib2
.HTTPError
, e
:
772 raise DDNSAuthenticationError
775 raise DDNSRequestError(_("An invalid IP address was submitted"))
779 # Handle success messages.
780 if response
.code
== 200:
783 # If we got here, some other update error happened.
784 raise DDNSUpdateError
787 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
788 handle
= "freedns.afraid.org"
789 name
= "freedns.afraid.org"
790 website
= "http://freedns.afraid.org/"
792 # No information about the request or response could be found on the vendor
793 # page. All used values have been collected by testing.
794 url
= "https://freedns.afraid.org/dynamic/update.php"
796 def update_protocol(self
, proto
):
798 "address" : self
.get_address(proto
),
801 # Add auth token to the update url.
802 url
= "%s?%s" % (self
.url
, self
.token
)
804 # Send update to the server.
805 response
= self
.send_request(url
, data
=data
)
807 # Get the full response message.
808 output
= response
.read()
810 # Handle success messages.
811 if output
.startswith("Updated") or "has not changed" in output
:
814 # Handle error codes.
815 if output
== "ERROR: Unable to locate this record":
816 raise DDNSAuthenticationError
817 elif "is an invalid IP address" in output
:
818 raise DDNSRequestError(_("Invalid IP address has been sent."))
820 # If we got here, some other update error happened.
821 raise DDNSUpdateError
824 class DDNSProviderLightningWireLabs(DDNSProvider
):
825 handle
= "dns.lightningwirelabs.com"
826 name
= "Lightning Wire Labs DNS Service"
827 website
= "http://dns.lightningwirelabs.com/"
829 # Information about the format of the HTTPS request is to be found
830 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
832 url
= "https://dns.lightningwirelabs.com/update"
836 "hostname" : self
.hostname
,
837 "address6" : self
.get_address("ipv6", "-"),
838 "address4" : self
.get_address("ipv4", "-"),
841 # Check if a token has been set.
843 data
["token"] = self
.token
845 # Check for username and password.
846 elif self
.username
and self
.password
:
848 "username" : self
.username
,
849 "password" : self
.password
,
852 # Raise an error if no auth details are given.
854 raise DDNSConfigurationError
856 # Send update to the server.
857 response
= self
.send_request(self
.url
, data
=data
)
859 # Handle success messages.
860 if response
.code
== 200:
863 # If we got here, some other update error happened.
864 raise DDNSUpdateError
867 class DDNSProviderMyOnlinePortal(DDNSProtocolDynDNS2
, DDNSProvider
):
868 handle
= "myonlineportal.net"
869 name
= "myonlineportal.net"
870 website
= "https:/myonlineportal.net/"
872 # Information about the request and response can be obtained here:
873 # https://myonlineportal.net/howto_dyndns
875 url
= "https://myonlineportal.net/updateddns"
877 def prepare_request_data(self
, proto
):
879 "hostname" : self
.hostname
,
880 "ip" : self
.get_address(proto
),
886 class DDNSProviderNamecheap(DDNSResponseParserXML
, DDNSProvider
):
887 handle
= "namecheap.com"
889 website
= "http://namecheap.com"
890 protocols
= ("ipv4",)
892 # Information about the format of the HTTP request is to be found
893 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
894 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
896 url
= "https://dynamicdns.park-your-domain.com/update"
898 def update_protocol(self
, proto
):
899 # Namecheap requires the hostname splitted into a host and domain part.
900 host
, domain
= self
.hostname
.split(".", 1)
903 "ip" : self
.get_address(proto
),
904 "password" : self
.password
,
909 # Send update to the server.
910 response
= self
.send_request(self
.url
, data
=data
)
912 # Get the full response message.
913 output
= response
.read()
915 # Handle success messages.
916 if self
.get_xml_tag_value(output
, "IP") == address
:
919 # Handle error codes.
920 errorcode
= self
.get_xml_tag_value(output
, "ResponseNumber")
922 if errorcode
== "304156":
923 raise DDNSAuthenticationError
924 elif errorcode
== "316153":
925 raise DDNSRequestError(_("Domain not found."))
926 elif errorcode
== "316154":
927 raise DDNSRequestError(_("Domain not active."))
928 elif errorcode
in ("380098", "380099"):
929 raise DDNSInternalServerError
931 # If we got here, some other update error happened.
932 raise DDNSUpdateError
935 class DDNSProviderNOIP(DDNSProtocolDynDNS2
, DDNSProvider
):
938 website
= "http://www.no-ip.com/"
939 protocols
= ("ipv4",)
941 # Information about the format of the HTTP request is to be found
942 # here: http://www.no-ip.com/integrate/request and
943 # here: http://www.no-ip.com/integrate/response
945 url
= "http://dynupdate.no-ip.com/nic/update"
947 def prepare_request_data(self
, proto
):
948 assert proto
== "ipv4"
951 "hostname" : self
.hostname
,
952 "address" : self
.get_address(proto
),
958 class DDNSProviderNsupdateINFO(DDNSProtocolDynDNS2
, DDNSProvider
):
959 handle
= "nsupdate.info"
960 name
= "nsupdate.info"
961 website
= "http://nsupdate.info/"
962 protocols
= ("ipv6", "ipv4",)
964 # Information about the format of the HTTP request can be found
965 # after login on the provider user interface and here:
966 # http://nsupdateinfo.readthedocs.org/en/latest/user.html
968 # Nsupdate.info uses the hostname as user part for the HTTP basic auth,
969 # and for the password a so called secret.
972 return self
.get("hostname")
976 return self
.token
or self
.get("secret")
980 # The update URL is different by the used protocol.
981 if self
.proto
== "ipv4":
982 return "https://ipv4.nsupdate.info/nic/update"
983 elif self
.proto
== "ipv6":
984 return "https://ipv6.nsupdate.info/nic/update"
986 raise DDNSUpdateError(_("Invalid protocol has been given"))
988 def prepare_request_data(self
, proto
):
990 "myip" : self
.get_address(proto
),
996 class DDNSProviderOpenDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
997 handle
= "opendns.com"
999 website
= "http://www.opendns.com"
1001 # Detailed information about the update request and possible
1002 # response codes can be obtained from here:
1003 # https://support.opendns.com/entries/23891440
1005 url
= "https://updates.opendns.com/nic/update"
1007 def prepare_request_data(self
, proto
):
1009 "hostname" : self
.hostname
,
1010 "myip" : self
.get_address(proto
),
1016 class DDNSProviderOVH(DDNSProtocolDynDNS2
, DDNSProvider
):
1019 website
= "http://www.ovh.com/"
1020 protocols
= ("ipv4",)
1022 # OVH only provides very limited information about how to
1023 # update a DynDNS host. They only provide the update url
1024 # on the their german subpage.
1026 # http://hilfe.ovh.de/DomainDynHost
1028 url
= "https://www.ovh.com/nic/update"
1030 def prepare_request_data(self
, proto
):
1031 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1033 "system" : "dyndns",
1039 class DDNSProviderRegfish(DDNSProvider
):
1040 handle
= "regfish.com"
1041 name
= "Regfish GmbH"
1042 website
= "http://www.regfish.com/"
1044 # A full documentation to the providers api can be found here
1045 # but is only available in german.
1046 # https://www.regfish.de/domains/dyndns/dokumentation
1048 url
= "https://dyndns.regfish.de/"
1052 "fqdn" : self
.hostname
,
1055 # Check if we update an IPv6 address.
1056 address6
= self
.get_address("ipv6")
1058 data
["ipv6"] = address6
1060 # Check if we update an IPv4 address.
1061 address4
= self
.get_address("ipv4")
1063 data
["ipv4"] = address4
1065 # Raise an error if none address is given.
1066 if not data
.has_key("ipv6") and not data
.has_key("ipv4"):
1067 raise DDNSConfigurationError
1069 # Check if a token has been set.
1071 data
["token"] = self
.token
1073 # Raise an error if no token and no useranem and password
1075 elif not self
.username
and not self
.password
:
1076 raise DDNSConfigurationError(_("No Auth details specified."))
1078 # HTTP Basic Auth is only allowed if no token is used.
1080 # Send update to the server.
1081 response
= self
.send_request(self
.url
, data
=data
)
1083 # Send update to the server.
1084 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
1087 # Get the full response message.
1088 output
= response
.read()
1090 # Handle success messages.
1091 if "100" in output
or "101" in output
:
1094 # Handle error codes.
1095 if "401" or "402" in output
:
1096 raise DDNSAuthenticationError
1097 elif "408" in output
:
1098 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
1099 elif "409" in output
:
1100 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
1101 elif "412" in output
:
1102 raise DDNSRequestError(_("No valid FQDN was given."))
1103 elif "414" in output
:
1104 raise DDNSInternalServerError
1106 # If we got here, some other update error happened.
1107 raise DDNSUpdateError
1110 class DDNSProviderSelfhost(DDNSProtocolDynDNS2
, DDNSProvider
):
1111 handle
= "selfhost.de"
1112 name
= "Selfhost.de"
1113 website
= "http://www.selfhost.de/"
1114 protocols
= ("ipv4",)
1116 url
= "https://carol.selfhost.de/nic/update"
1118 def prepare_request_data(self
, proto
):
1119 data
= DDNSProtocolDynDNS2
.prepare_request_data(self
, proto
)
1127 class DDNSProviderSPDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1128 handle
= "spdns.org"
1130 website
= "http://spdns.org/"
1132 # Detailed information about request and response codes are provided
1133 # by the vendor. They are using almost the same mechanism and status
1134 # codes as dyndns.org so we can inherit all those stuff.
1136 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
1137 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
1139 url
= "https://update.spdns.de/nic/update"
1143 return self
.get("username") or self
.hostname
1147 return self
.get("username") or self
.token
1150 class DDNSProviderStrato(DDNSProtocolDynDNS2
, DDNSProvider
):
1151 handle
= "strato.com"
1153 website
= "http:/www.strato.com/"
1154 protocols
= ("ipv4",)
1156 # Information about the request and response can be obtained here:
1157 # http://www.strato-faq.de/article/671/So-einfach-richten-Sie-DynDNS-f%C3%BCr-Ihre-Domains-ein.html
1159 url
= "https://dyndns.strato.com/nic/update"
1162 class DDNSProviderTwoDNS(DDNSProtocolDynDNS2
, DDNSProvider
):
1163 handle
= "twodns.de"
1165 website
= "http://www.twodns.de"
1166 protocols
= ("ipv4",)
1168 # Detailed information about the request can be found here
1169 # http://twodns.de/en/faqs
1170 # http://twodns.de/en/api
1172 url
= "https://update.twodns.de/update"
1174 def prepare_request_data(self
, proto
):
1175 assert proto
== "ipv4"
1178 "ip" : self
.get_address(proto
),
1179 "hostname" : self
.hostname
1185 class DDNSProviderUdmedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1186 handle
= "udmedia.de"
1187 name
= "Udmedia GmbH"
1188 website
= "http://www.udmedia.de"
1189 protocols
= ("ipv4",)
1191 # Information about the request can be found here
1192 # http://www.udmedia.de/faq/content/47/288/de/wie-lege-ich-einen-dyndns_eintrag-an.html
1194 url
= "https://www.udmedia.de/nic/update"
1197 class DDNSProviderVariomedia(DDNSProtocolDynDNS2
, DDNSProvider
):
1198 handle
= "variomedia.de"
1200 website
= "http://www.variomedia.de/"
1201 protocols
= ("ipv6", "ipv4",)
1203 # Detailed information about the request can be found here
1204 # https://dyndns.variomedia.de/
1206 url
= "https://dyndns.variomedia.de/nic/update"
1208 def prepare_request_data(self
, proto
):
1210 "hostname" : self
.hostname
,
1211 "myip" : self
.get_address(proto
),
1217 class DDNSProviderZoneedit(DDNSProtocolDynDNS2
, DDNSProvider
):
1218 handle
= "zoneedit.com"
1220 website
= "http://www.zoneedit.com"
1221 protocols
= ("ipv4",)
1223 # Detailed information about the request and the response codes can be
1225 # http://www.zoneedit.com/doc/api/other.html
1226 # http://www.zoneedit.com/faq.html
1228 url
= "https://dynamic.zoneedit.com/auth/dynamic.html"
1230 def update_protocol(self
, proto
):
1232 "dnsto" : self
.get_address(proto
),
1233 "host" : self
.hostname
1236 # Send update to the server.
1237 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
1240 # Get the full response message.
1241 output
= response
.read()
1243 # Handle success messages.
1244 if output
.startswith("<SUCCESS"):
1247 # Handle error codes.
1248 if output
.startswith("invalid login"):
1249 raise DDNSAuthenticationError
1250 elif output
.startswith("<ERROR CODE=\"704\""):
1251 raise DDNSRequestError(_("No valid FQDN was given."))
1252 elif output
.startswith("<ERROR CODE=\"702\""):
1253 raise DDNSInternalServerError
1255 # If we got here, some other update error happened.
1256 raise DDNSUpdateError
1259 class DDNSProviderZZZZ(DDNSProvider
):
1262 website
= "https://zzzz.io"
1263 protocols
= ("ipv6", "ipv4",)
1265 # Detailed information about the update request can be found here:
1266 # https://zzzz.io/faq/
1268 # Details about the possible response codes have been provided in the bugtracker:
1269 # https://bugzilla.ipfire.org/show_bug.cgi?id=10584#c2
1271 url
= "https://zzzz.io/api/v1/update"
1273 def update_protocol(self
, proto
):
1275 "ip" : self
.get_address(proto
),
1276 "token" : self
.token
,
1280 data
["type"] = "aaaa"
1282 # zzzz uses the host from the full hostname as part
1283 # of the update url.
1284 host
, domain
= self
.hostname
.split(".", 1)
1286 # Add host value to the update url.
1287 url
= "%s/%s" % (self
.url
, host
)
1289 # Send update to the server.
1291 response
= self
.send_request(url
, data
=data
)
1293 # Handle error codes.
1294 except DDNSNotFound
:
1295 raise DDNSRequestError(_("Invalid hostname specified"))
1297 # Handle success messages.
1298 if response
.code
== 200:
1301 # If we got here, some other update error happened.
1302 raise DDNSUpdateError