]>
git.ipfire.org Git - 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 all possible exception types.
29 logger
= logging
.getLogger("ddns.providers")
32 class DDNSProvider(object):
34 # A short string that uniquely identifies
38 # The full name of the provider.
41 # A weburl to the homepage of the provider.
42 # (Where to register a new account?)
45 # A list of supported protocols.
46 "protocols" : ["ipv6", "ipv4"],
51 def __init__(self
, core
, **settings
):
54 # Copy a set of default settings and
55 # update them by those from the configuration file.
56 self
.settings
= self
.DEFAULT_SETTINGS
.copy()
57 self
.settings
.update(settings
)
60 return "<DDNS Provider %s (%s)>" % (self
.name
, self
.handle
)
62 def __cmp__(self
, other
):
63 return cmp(self
.hostname
, other
.hostname
)
68 Returns the name of the provider.
70 return self
.INFO
.get("name")
75 Returns the website URL of the provider
76 or None if that is not available.
78 return self
.INFO
.get("website", None)
83 Returns the handle of this provider.
85 return self
.INFO
.get("handle")
87 def get(self
, key
, default
=None):
89 Get a setting from the settings dictionary.
91 return self
.settings
.get(key
, default
)
96 Fast access to the hostname.
98 return self
.get("hostname")
103 Fast access to the username.
105 return self
.get("username")
110 Fast access to the password.
112 return self
.get("password")
116 return self
.INFO
.get("protocols")
121 Fast access to the token.
123 return self
.get("token")
125 def __call__(self
, force
=False):
127 logger
.info(_("Updating %s forced") % self
.hostname
)
129 # Check if we actually need to update this host.
130 elif self
.is_uptodate(self
.protocols
):
131 logger
.info(_("%s is already up to date") % self
.hostname
)
134 # Execute the update.
138 raise NotImplementedError
140 def is_uptodate(self
, protos
):
142 Returns True if this host is already up to date
143 and does not need to change the IP address on the
147 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
149 current_address
= self
.get_address(proto
)
151 if not current_address
in addresses
:
156 def send_request(self
, *args
, **kwargs
):
158 Proxy connection to the send request
161 return self
.core
.system
.send_request(*args
, **kwargs
)
163 def get_address(self
, proto
):
165 Proxy method to get the current IP address.
167 return self
.core
.system
.get_address(proto
)
170 class DDNSProviderDHS(DDNSProvider
):
172 "handle" : "dhs.org",
173 "name" : "DHS International",
174 "website" : "http://dhs.org/",
175 "protocols" : ["ipv4",]
178 # No information about the used update api provided on webpage,
179 # grabed from source code of ez-ipudate.
180 url
= "http://members.dhs.org/nic/hosts"
184 "username" : self
.username
,
185 "password" : self
.password
,
189 "domain" : self
.hostname
,
190 "ip" : self
.get_address("ipv4"),
192 "hostcmdstage" : "2",
196 # Send update to the server.
197 response
= self
.send_request(url
, username
=self
.username
, password
=self
.password
,
200 # Handle success messages.
201 if response
.code
== 200:
204 # Handle error codes.
205 elif response
.code
== 401:
206 raise DDNSAuthenticationError
208 # If we got here, some other update error happened.
209 raise DDNSUpdateError
212 class DDNSProviderDNSpark(DDNSProvider
):
214 "handle" : "dnspark.com",
216 "website" : "http://dnspark.com/",
217 "protocols" : ["ipv4",]
220 # Informations to the used api can be found here:
221 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
222 url
= "https://control.dnspark.com/api/dynamic/update.php"
226 "username" : self
.username
,
227 "password" : self
.password
,
231 "domain" : self
.hostname
,
232 "ip" : self
.get_address("ipv4"),
235 # Send update to the server.
236 response
= self
.send_request(url
, username
=self
.username
, password
=self
.password
,
239 # Get the full response message.
240 output
= response
.read()
242 # Handle success messages.
243 if output
.startswith("ok") or output
.startswith("nochange"):
246 # Handle error codes.
247 if output
== "unauth":
248 raise DDNSAuthenticationError
249 elif output
== "abuse":
251 elif output
== "blocked":
252 raise DDNSBlockedError
253 elif output
== "nofqdn":
254 raise DDNSRequestError(_("No valid FQDN was given."))
255 elif output
== "nohost":
256 raise DDNSRequestError(_("Invalid hostname specified."))
257 elif output
== "notdyn":
258 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
259 elif output
== "invalid":
260 raise DDNSRequestError(_("Invalid IP address has been sent."))
262 # If we got here, some other update error happened.
263 raise DDNSUpdateError
266 class DDNSProviderDtDNS(DDNSProvider
):
268 "handle" : "dtdns.com",
270 "website" : "http://dtdns.com/",
271 "protocols" : ["ipv4",]
274 # Information about the format of the HTTPS request is to be found
275 # http://www.dtdns.com/dtsite/updatespec
276 url
= "https://www.dtdns.com/api/autodns.cfm"
281 "ip" : self
.get_address("ipv4"),
282 "id" : self
.hostname
,
286 # Send update to the server.
287 response
= self
.send_request(self
.url
, data
=data
)
289 # Get the full response message.
290 output
= response
.read()
292 # Remove all leading and trailing whitespace.
293 output
= output
.strip()
295 # Handle success messages.
296 if "now points to" in output
:
299 # Handle error codes.
300 if output
== "No hostname to update was supplied.":
301 raise DDNSRequestError(_("No hostname specified."))
303 elif output
== "The hostname you supplied is not valid.":
304 raise DDNSRequestError(_("Invalid hostname specified."))
306 elif output
== "The password you supplied is not valid.":
307 raise DDNSAuthenticationError
309 elif output
== "Administration has disabled this account.":
310 raise DDNSRequestError(_("Account has been disabled."))
312 elif output
== "Illegal character in IP.":
313 raise DDNSRequestError(_("Invalid IP address has been sent."))
315 elif output
== "Too many failed requests.":
316 raise DDNSRequestError(_("Too many failed requests."))
318 # If we got here, some other update error happened.
319 raise DDNSUpdateError
322 class DDNSProviderFreeDNSAfraidOrg(DDNSProvider
):
324 "handle" : "freedns.afraid.org",
325 "name" : "freedns.afraid.org",
326 "website" : "http://freedns.afraid.org/",
327 "protocols" : ["ipv6", "ipv4",]
330 # No information about the request or response could be found on the vendor
331 # page. All used values have been collected by testing.
332 url
= "https://freedns.afraid.org/dynamic/update.php"
336 return self
.get("proto")
339 address
= self
.get_address(self
.proto
)
345 # Add auth token to the update url.
346 url
= "%s?%s" % (self
.url
, self
.token
)
348 # Send update to the server.
349 response
= self
.send_request(url
, data
=data
)
351 # Get the full response message.
352 output
= response
.read()
354 # Handle success messages.
355 if output
.startswith("Updated") or "has not changed" in output
:
358 # Handle error codes.
359 if output
== "ERROR: Unable to locate this record":
360 raise DDNSAuthenticationError
361 elif "is an invalid IP address" in output
:
362 raise DDNSRequestError(_("Invalid IP address has been sent."))
364 # If we got here, some other update error happened.
365 raise DDNSUpdateError
368 class DDNSProviderLightningWireLabs(DDNSProvider
):
370 "handle" : "dns.lightningwirelabs.com",
371 "name" : "Lightning Wire Labs",
372 "website" : "http://dns.lightningwirelabs.com/",
373 "protocols" : ["ipv6", "ipv4",]
376 # Information about the format of the HTTPS request is to be found
377 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
378 url
= "https://dns.lightningwirelabs.com/update"
382 "hostname" : self
.hostname
,
385 # Check if we update an IPv6 address.
386 address6
= self
.get_address("ipv6")
388 data
["address6"] = address6
390 # Check if we update an IPv4 address.
391 address4
= self
.get_address("ipv4")
393 data
["address4"] = address4
395 # Raise an error if none address is given.
396 if not data
.has_key("address6") and not data
.has_key("address4"):
397 raise DDNSConfigurationError
399 # Check if a token has been set.
401 data
["token"] = self
.token
403 # Check for username and password.
404 elif self
.username
and self
.password
:
406 "username" : self
.username
,
407 "password" : self
.password
,
410 # Raise an error if no auth details are given.
412 raise DDNSConfigurationError
414 # Send update to the server.
415 response
= self
.send_request(self
.url
, data
=data
)
417 # Handle success messages.
418 if response
.code
== 200:
421 # Handle error codes.
422 if response
.code
== 403:
423 raise DDNSAuthenticationError
424 elif response
.code
== 400:
425 raise DDNSRequestError
427 # If we got here, some other update error happened.
428 raise DDNSUpdateError
431 class DDNSProviderNOIP(DDNSProvider
):
433 "handle" : "no-ip.com",
435 "website" : "http://www.no-ip.com/",
436 "protocols" : ["ipv4",]
439 # Information about the format of the HTTP request is to be found
440 # here: http://www.no-ip.com/integrate/request and
441 # here: http://www.no-ip.com/integrate/response
443 url
= "http://%(username)s:%(password)s@dynupdate.no-ip.com/nic/update"
447 "username" : self
.username
,
448 "password" : self
.password
,
452 "hostname" : self
.hostname
,
453 "address" : self
.get_address("ipv4"),
456 # Send update to the server.
457 response
= self
.send_request(url
, data
=data
)
459 # Get the full response message.
460 output
= response
.read()
462 # Handle success messages.
463 if output
.startswith("good") or output
.startswith("nochg"):
466 # Handle error codes.
467 if output
== "badauth":
468 raise DDNSAuthenticationError
469 elif output
== "aduse":
471 elif output
== "911":
472 raise DDNSInternalServerError
474 # If we got here, some other update error happened.
475 raise DDNSUpdateError
478 class DDNSProviderSelfhost(DDNSProvider
):
480 "handle" : "selfhost.de",
481 "name" : "Selfhost.de",
482 "website" : "http://www.selfhost.de/",
483 "protocols" : ["ipv4",],
486 url
= "https://carol.selfhost.de/update"
490 "username" : self
.username
,
491 "password" : self
.password
,
495 response
= self
.send_request(self
.url
, data
=data
)
497 match
= re
.search("status=20(0|4)", response
.read())
499 raise DDNSUpdateError