]>
git.ipfire.org Git - people/stevee/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")
118 def __call__(self
, force
=False):
120 logger
.info(_("Updating %s forced") % self
.hostname
)
122 # Check if we actually need to update this host.
123 elif self
.is_uptodate(self
.protocols
):
124 logger
.info(_("%s is already up to date") % self
.hostname
)
127 # Execute the update.
131 raise NotImplementedError
133 def is_uptodate(self
, protos
):
135 Returns True if this host is already up to date
136 and does not need to change the IP address on the
140 addresses
= self
.core
.system
.resolve(self
.hostname
, proto
)
142 current_address
= self
.get_address(proto
)
144 if not current_address
in addresses
:
149 def send_request(self
, *args
, **kwargs
):
151 Proxy connection to the send request
154 return self
.core
.system
.send_request(*args
, **kwargs
)
156 def get_address(self
, proto
):
158 Proxy method to get the current IP address.
160 return self
.core
.system
.get_address(proto
)
163 class DDNSProviderDHS(DDNSProvider
):
165 "handle" : "dhs.org",
166 "name" : "DHS International",
167 "website" : "http://dhs.org/",
168 "protocols" : ["ipv4",]
171 # No information about the used update api provided on webpage,
172 # grabed from source code of ez-ipudate.
173 url
= "http://members.dhs.org/nic/hosts"
177 "domain" : self
.hostname
,
178 "ip" : self
.get_address("ipv4"),
180 "hostcmdstage" : "2",
184 # Send update to the server.
185 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
188 # Handle success messages.
189 if response
.code
== 200:
192 # Handle error codes.
193 elif response
.code
== 401:
194 raise DDNSAuthenticationError
196 # If we got here, some other update error happened.
197 raise DDNSUpdateError
200 class DDNSProviderDNSpark(DDNSProvider
):
202 "handle" : "dnspark.com",
204 "website" : "http://dnspark.com/",
205 "protocols" : ["ipv4",]
208 # Informations to the used api can be found here:
209 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
210 url
= "https://control.dnspark.com/api/dynamic/update.php"
214 "domain" : self
.hostname
,
215 "ip" : self
.get_address("ipv4"),
218 # Send update to the server.
219 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
222 # Get the full response message.
223 output
= response
.read()
225 # Handle success messages.
226 if output
.startswith("ok") or output
.startswith("nochange"):
229 # Handle error codes.
230 if output
== "unauth":
231 raise DDNSAuthenticationError
232 elif output
== "abuse":
234 elif output
== "blocked":
235 raise DDNSBlockedError
236 elif output
== "nofqdn":
237 raise DDNSRequestError(_("No valid FQDN was given."))
238 elif output
== "nohost":
239 raise DDNSRequestError(_("Invalid hostname specified."))
240 elif output
== "notdyn":
241 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
242 elif output
== "invalid":
243 raise DDNSRequestError(_("Invalid IP address has been sent."))
245 # If we got here, some other update error happened.
246 raise DDNSUpdateError
249 class DDNSProviderDtDNS(DDNSProvider
):
251 "handle" : "dtdns.com",
253 "website" : "http://dtdns.com/",
254 "protocols" : ["ipv4",]
257 # Information about the format of the HTTPS request is to be found
258 # http://www.dtdns.com/dtsite/updatespec
259 url
= "https://www.dtdns.com/api/autodns.cfm"
264 "ip" : self
.get_address("ipv4"),
265 "id" : self
.hostname
,
269 # Send update to the server.
270 response
= self
.send_request(self
.url
, data
=data
)
272 # Get the full response message.
273 output
= response
.read()
275 # Remove all leading and trailing whitespace.
276 output
= output
.strip()
278 # Handle success messages.
279 if "now points to" in output
:
282 # Handle error codes.
283 if output
== "No hostname to update was supplied.":
284 raise DDNSRequestError(_("No hostname specified."))
286 elif output
== "The hostname you supplied is not valid.":
287 raise DDNSRequestError(_("Invalid hostname specified."))
289 elif output
== "The password you supplied is not valid.":
290 raise DDNSAuthenticationError
292 elif output
== "Administration has disabled this account.":
293 raise DDNSRequestError(_("Account has been disabled."))
295 elif output
== "Illegal character in IP.":
296 raise DDNSRequestError(_("Invalid IP address has been sent."))
298 elif output
== "Too many failed requests.":
299 raise DDNSRequestError(_("Too many failed requests."))
301 # If we got here, some other update error happened.
302 raise DDNSUpdateError
305 class DDNSProviderDynDNS(DDNSProvider
):
307 "handle" : "dyndns.org",
309 "website" : "http://dyn.com/dns/",
310 "protocols" : ["ipv4",]
313 # Information about the format of the request is to be found
314 # http://http://dyn.com/support/developers/api/perform-update/
315 # http://dyn.com/support/developers/api/return-codes/
316 url
= "https://members.dyndns.org/nic/update"
320 "hostname" : self
.hostname
,
321 "myip" : self
.get_address("ipv4"),
324 # Send update to the server.
325 response
= self
.send_request(self
.url
, username
=self
.username
, password
=self
.password
,
328 # Get the full response message.
329 output
= response
.read()
331 # Handle success messages.
332 if output
.startswith("good") or output
.startswith("nochg"):
335 # Handle error codes.
336 if output
== "badauth":
337 raise DDNSAuthenticationError
338 elif output
== "aduse":
340 elif output
== "notfqdn":
341 raise DDNSRequestError(_("No valid FQDN was given."))
342 elif output
== "nohost":
343 raise DDNSRequestError(_("Specified host does not exist."))
344 elif output
== "911":
345 raise DDNSInternalServerError
346 elif output
== "dnserr":
347 raise DDNSInternalServerError(_("DNS error encountered."))
349 # If we got here, some other update error happened.
350 raise DDNSUpdateError
353 class DDNSProviderLightningWireLabs(DDNSProvider
):
355 "handle" : "dns.lightningwirelabs.com",
356 "name" : "Lightning Wire Labs",
357 "website" : "http://dns.lightningwirelabs.com/",
358 "protocols" : ["ipv6", "ipv4",]
361 # Information about the format of the HTTPS request is to be found
362 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
363 url
= "https://dns.lightningwirelabs.com/update"
368 Fast access to the token.
370 return self
.get("token")
374 "hostname" : self
.hostname
,
377 # Check if we update an IPv6 address.
378 address6
= self
.get_address("ipv6")
380 data
["address6"] = address6
382 # Check if we update an IPv4 address.
383 address4
= self
.get_address("ipv4")
385 data
["address4"] = address4
387 # Raise an error if none address is given.
388 if not data
.has_key("address6") and not data
.has_key("address4"):
389 raise DDNSConfigurationError
391 # Check if a token has been set.
393 data
["token"] = self
.token
395 # Check for username and password.
396 elif self
.username
and self
.password
:
398 "username" : self
.username
,
399 "password" : self
.password
,
402 # Raise an error if no auth details are given.
404 raise DDNSConfigurationError
406 # Send update to the server.
407 response
= self
.send_request(self
.url
, data
=data
)
409 # Handle success messages.
410 if response
.code
== 200:
413 # Handle error codes.
414 if response
.code
== 403:
415 raise DDNSAuthenticationError
416 elif response
.code
== 400:
417 raise DDNSRequestError
419 # If we got here, some other update error happened.
420 raise DDNSUpdateError
423 class DDNSProviderNOIP(DDNSProvider
):
425 "handle" : "no-ip.com",
427 "website" : "http://www.no-ip.com/",
428 "protocols" : ["ipv4",]
431 # Information about the format of the HTTP request is to be found
432 # here: http://www.no-ip.com/integrate/request and
433 # here: http://www.no-ip.com/integrate/response
435 url
= "http://%(username)s:%(password)s@dynupdate.no-ip.com/nic/update"
439 "username" : self
.username
,
440 "password" : self
.password
,
444 "hostname" : self
.hostname
,
445 "address" : self
.get_address("ipv4"),
448 # Send update to the server.
449 response
= self
.send_request(url
, data
=data
)
451 # Get the full response message.
452 output
= response
.read()
454 # Handle success messages.
455 if output
.startswith("good") or output
.startswith("nochg"):
458 # Handle error codes.
459 if output
== "badauth":
460 raise DDNSAuthenticationError
461 elif output
== "aduse":
463 elif output
== "911":
464 raise DDNSInternalServerError
466 # If we got here, some other update error happened.
467 raise DDNSUpdateError
470 class DDNSProviderSelfhost(DDNSProvider
):
472 "handle" : "selfhost.de",
473 "name" : "Selfhost.de",
474 "website" : "http://www.selfhost.de/",
475 "protocols" : ["ipv4",],
478 url
= "https://carol.selfhost.de/update"
482 "username" : self
.username
,
483 "password" : self
.password
,
487 response
= self
.send_request(self
.url
, data
=data
)
489 match
= re
.search("status=20(0|4)", response
.read())
491 raise DDNSUpdateError
493 class DDNSProviderVariomedia(DDNSProviderDynDNS
):
495 "handle" : "variomedia.de",
496 "name" : "Variomedia",
497 "website" : "http://www.variomedia.de/",
498 "protocols" : ["ipv6", "ipv4",]
501 # Detailed information about the request can be found here
502 # https://dyndns.variomedia.de/
504 url
= "https://dyndns.variomedia.de/nic/update"
508 return self
.get("proto")
510 def _prepare_request_data(self
):
512 "hostname" : self
.hostname
,
513 "myip" : self
.get_address(self
.proto
)