2 Index: release/readme.txt
3 ===================================================================
5 Property changes on: release
6 ___________________________________________________________________
11 Index: sample-etc_ddclient.conf
12 ===================================================================
13 --- sample-etc_ddclient.conf (.../tags/release-3.8.0)
14 +++ sample-etc_ddclient.conf (.../trunk)
16 #use=fw, fw=192.168.1.254/status.htm, fw-skip='IP Address' # found after IP Address
18 ## To obtain an IP address from Web status page (using the proxy if defined)
19 +## by default, checkip.dyndns.org is used if you use the dyndns protocol.
20 +## Using use=web is enough to get it working.
21 +## WARNING: set deamon at least to 600 seconds if you use checkip or you could
22 +## get banned from their service.
23 #use=web, web=checkip.dyndns.org/, web-skip='IP Address' # found after IP Address
25 #use=ip, ip=127.0.0.1 # via static IP's
27 # login=my-namecheap.com-login, \
28 # password=my-namecheap.com-password \
29 # myhost.namecheap.com
33 +## Loopia (loopia.se)
38 +# server=dns.loopia.se
39 +# script=/XDynDNSServer/XDynDNS.php
40 +# login=my-loopia.se-login
41 +# password=my-loopia.se-password
42 +# my.domain.tld,other.domain.tld
45 ===================================================================
46 --- ddclient (.../tags/release-3.8.0)
47 +++ ddclient (.../trunk)
52 -my $version = "3.8.0";
53 +my ($VERSION) = q$Revision$ =~ /(\d+)/;
55 +my $version = "3.8.0-r". $VERSION;
57 $programd =~ s%^.*/%%;
58 my $program = $programd;
60 'dyndns' => { 'url' => 'http://checkip.dyndns.org/', 'skip' =>
61 'Current IP Address:', },
62 'dnspark' => { 'url' => 'http://ipdetect.dnspark.com/', 'skip' => 'Current Address:', },
63 + 'loopia' => { 'url' => 'http://dns.loopia.se/checkip/checkip.php', 'skip' => 'Current Address:', },
66 'watchguard-soho' => {
68 'if' => ": obtain IP from the -if {interface}",
69 'cmd' => ": obtain IP from the -cmd {external-command}",
70 'cisco' => ": obtain IP from Cisco FW at the -fw {address}",
71 + 'cisco-asa' => ": obtain IP from Cisco ASA at the -fw {address}",
72 map { $_ => sprintf ": obtain IP from %s at the -fw {address}", $builtinfw{$_}->{'name'} } keys %builtinfw,
74 sub ip_strategies_usage {
76 my %web_strategies = (
85 'global-defaults' => {
86 'daemon' => setv(T_DELAY, 0, 0, 1, 0, interval('60s')),
87 + 'foreground' => setv(T_BOOL, 0, 0, 1, 0, undef),
88 'file' => setv(T_FILE, 0, 0, 1, "$etc$program.conf", undef),
89 'cache' => setv(T_FILE, 0, 0, 1, "$cachedir$program.cache", undef),
90 'pid' => setv(T_FILE, 0, 0, 1, "", undef),
92 'examples' => \&nic_dyndns2_examples,
94 { 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), },
95 + { 'script' => setv(T_STRING, 1, 1, 1, '/nic/update', undef), },
96 # { 'offline' => setv(T_BOOL, 0, 1, 1, 0, undef), },
97 $variables{'dyndns-common-defaults'},
98 $variables{'service-common-defaults'},
100 $variables{'service-common-defaults'},
104 + 'updateable' => undef,
105 + 'update' => \&nic_freedns_update,
106 + 'examples' => \&nic_freedns_examples,
107 + 'variables' => merge(
108 + { 'server' => setv(T_FQDNP, 1, 0, 1, 'freedns.afraid.org', undef) },
109 + { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
110 + $variables{'service-common-defaults'},
114 $variables{'merged'} = merge($variables{'global-defaults'},
115 $variables{'service-common-defaults'},
117 "usage: ${program} [options]",
119 [ "daemon", "=s", "-daemon delay : run as a daemon, specify delay as an interval." ],
120 ++ [ "foreground", "!", "-foreground : do not fork" ],
121 [ "proxy", "=s", "-proxy host : use 'host' as the HTTP proxy" ],
122 [ "server", "=s", "-server host : update DNS information on 'host'" ],
123 [ "protocol", "=s", "-protocol type : update protocol used" ],
125 $SIG{'HUP'} = sub { $caught_hup = 1; };
126 $SIG{'TERM'} = sub { $caught_term = 1; };
127 $SIG{'KILL'} = sub { $caught_kill = 1; };
128 -if (opt('daemon') && !opt('force')) {
129 +# don't fork() if foreground or force is on
130 +if (opt('foreground') || opt('force')) {
132 +} elsif (opt('daemon')) {
133 $SIG{'CHLD'} = 'IGNORE';
136 @@ -633,12 +654,15 @@
139 $SIG{'CHLD'} = 'DEFAULT';
140 - $opt{'syslog'} = 1;
141 open(STDOUT, ">/dev/null");
142 open(STDERR, ">/dev/null");
143 open(STDIN, "</dev/null");
146 +# write out the pid file if we're daemon'ized
149 + $opt{'syslog'} = 1;
153 @@ -716,47 +740,58 @@
155 ######################################################################
161 - foreach my $s (sort keys %services) {
162 - my (@hosts, %ips) = ();
163 - my $updateable = $services{$s}{'updateable'};
164 - my $update = $services{$s}{'update'};
165 + foreach my $s (sort keys %services) {
166 + my (@hosts, %ips) = ();
167 + my $updateable = $services{$s}{'updateable'};
168 + my $update = $services{$s}{'update'};
170 + foreach my $h (sort keys %config) {
171 + next if $config{$h}{'protocol'} ne lc($s);
173 + my $use = $config{$h}{'use'} || opt('use');
174 + local $opt{$use} = $config{$h}{$use} if $config{$h}{$use};
175 + # bug #13: we should only do this once
176 + # use isn't enough, we have to save the origin to.
177 + # this will break the multiple ip stuff if use has
178 + # been used twice for the same device.
180 + if (defined $iplist{$use}) {
181 + $ip = $iplist{$use};
183 + $ip = get_ip($use, $h);
184 + if (!defined $ip || !$ip) {
185 + warning("unable to determine IP address")
186 + if !$daemon || opt('verbose');
189 + if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
190 + warning("malformed IP address (%s)", $ip);
193 + $iplist{$use} = $ip;
195 + $config{$h}{'wantip'} = $ip;
196 + next if !nic_updateable($h, $updateable);
201 + $0 = sprintf("%s - updating %s", $program, join(',', @hosts));
203 + runpostscript(join ' ', keys %ips);
206 foreach my $h (sort keys %config) {
207 - next if $config{$h}{'protocol'} ne lc($s);
209 - my $use = $config{$h}{'use'} || opt('use');
210 - local $opt{$use} = $config{$h}{$use} if $config{$h}{$use};
211 - my $ip = get_ip($use);
212 - if (!defined $ip || !$ip) {
213 - warning("unable to determine IP address")
214 - if !$daemon || opt('verbose');
217 - if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
218 - warning("malformed IP address (%s)", $ip);
221 - $config{$h}{'wantip'} = $ip;
222 - next if !nic_updateable($h, $updateable);
225 + if (!exists $examined{$h}) {
226 + failed("%s was not updated because protocol %s is not supported.",
227 + $h, define($config{$h}{'protocol'}, '<undefined>')
232 - $0 = sprintf("%s - updating %s", $program, join(',', @hosts));
234 - runpostscript(join ' ', keys %ips);
237 - foreach my $h (sort keys %config) {
238 - if (!exists $examined{$h}) {
239 - failed("%s was not updated because protocol %s is not supported.",
240 - $h, define($config{$h}{'protocol'}, '<undefined>')
244 - write_cache(opt('cache'));
245 + write_cache(opt('cache'));
247 ######################################################################
252 foreach my $h (keys %cache) {
253 - if (exists $config{$h}) {
254 + if (exists $config->{$h}) {
255 foreach (qw(atime mtime wtime ip status)) {
256 - $config{$h}{$_} = $cache{$h}{$_} if exists $cache{$h}{$_};
257 + $config->{$h}{$_} = $cache{$h}{$_} if exists $cache{$h}{$_};
261 @@ -1055,7 +1090,7 @@
262 $opt{'use'} = 'web' if !define($opt{'use'}) && defined($opt{'web'});
265 - $opt{'max-interval'} = max(interval(opt('max-interval')), interval(default('max-interval')));
266 + $opt{'max-interval'} = min(interval(opt('max-interval')), interval(default('max-interval')));
267 $opt{'min-interval'} = max(interval(opt('min-interval')), interval(default('min-interval')));
268 $opt{'min-error-interval'} = max(interval(opt('min-error-interval')), interval(default('min-error-interval')));
270 @@ -1093,7 +1128,7 @@
271 ## merge options into host definitions or globals
273 foreach my $h (@hosts) {
274 - $config{$h} = %{ merge(\%options, $config{$h}) };
275 + $config{$h} = merge(\%options, $config{$h});
277 $opt{'host'} = join(',', @hosts);
279 @@ -1157,6 +1192,8 @@
280 $proto = $config{$h}{'protocol'};
281 $proto = opt('protocol') if !defined($proto);
283 + load_sha1_support() if ($proto eq "freedns");
285 if (!exists($services{$proto})) {
286 warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
288 @@ -1481,6 +1518,8 @@
293 + return $config{$h}{$v} if defined($h && $config{$h}{$v});
294 return $opt{$v} if defined $opt{$v};
295 return $globals{$v} if defined $globals{$v};
296 return default($v) if defined default($v);
297 @@ -1696,11 +1735,6 @@
298 # fix padding at the end
299 my $padding = (3 - length($_[0]) % 3) % 3;
300 $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
302 - # break encoded string into lines of no more than 76 characters each
304 - $res =~ s/(.{1,76})/$1$eol/g;
308 ######################################################################
309 @@ -1712,12 +1746,26 @@
311 Error loading the Perl module IO::Socket::SSL needed for SSL connect.
312 On Debian, the package libio-socket-ssl-perl must be installed.
313 +On Red Hat, the package perl-IO-Socket-SSL must be installed.
316 import IO::Socket::SSL;
317 { no warnings; $IO::Socket::SSL::DEBUG = 0; }
319 ######################################################################
320 +## load_sha1_support
321 +######################################################################
322 +sub load_sha1_support {
323 + my $sha1_loaded = eval {require Digest::SHA1};
324 + unless ($sha1_loaded) {
326 +Error loading the Perl module Digest::SHA1 needed for freedns update.
327 +On Debian, the package libdigest-sha1-perl must be installed.
330 + import Digest::SHA1 (qw/sha1_hex/);
332 +######################################################################
334 ######################################################################
336 @@ -1729,8 +1777,10 @@
337 my ($sd, $rq, $request, $reply);
339 debug("proxy = $proxy");
340 - debug("url = $url");
341 + debug("url = %s", $url);
342 ## canonify proxy and url
344 + $force_ssl = 1 if ($url =~ /^https:/);
345 $proxy =~ s%^https?://%%i;
346 $url =~ s%^https?://%%i;
348 @@ -1743,7 +1793,7 @@
349 $globals{'fw'} && debug("glo fw = $globals{'fw'}");
350 #if ( $globals{'ssl'} and $server ne $globals{'fw'} ) {
351 ## always omit SSL for connections to local router
352 - if ( $globals{'ssl'} and (caller(1))[3] ne 'main::get_ip' ) {
353 + if ( $force_ssl || ($globals{'ssl'} and (caller(1))[3] ne 'main::get_ip') ) {
357 @@ -1769,7 +1819,7 @@
358 $request .= "Host: $server\n";
360 my $auth = encode_base64("${login}:${password}");
361 - $request .= "Authorization: Basic $auth" if $login || $password;
362 + $request .= "Authorization: Basic $auth\n" if $login || $password;
363 $request .= "User-Agent: ${program}/${version}\n";
364 $request .= "Connection: close\n";
366 @@ -1781,7 +1831,7 @@
367 $0 = sprintf("%s - connecting to %s port %s", $program, $peer, $port);
369 debug("skipped network connection");
370 - verbose("SENDING:", $request);
371 + verbose("SENDING:", "%s", $request);
373 $sd = IO::Socket::SSL->new(
375 @@ -1802,39 +1852,39 @@
376 defined $sd or warning("cannot connect to $peer:$port socket: $@");
380 - ## send the request to the http server
381 - verbose("CONNECTED: ", $use_ssl ? 'using SSL' : 'using HTTP');
382 - verbose("SENDING:", $request);
384 + ## send the request to the http server
385 + verbose("CONNECTED: ", $use_ssl ? 'using SSL' : 'using HTTP');
386 + verbose("SENDING:", "%s", $request);
388 - $0 = sprintf("%s - sending to %s port %s", $program, $peer, $port);
389 - my $result = syswrite $sd, $rq;
390 - if ($result != length($rq)) {
391 - warning("cannot send to $peer:$port ($!).");
395 - local $SIG{'ALRM'} = sub { $timeout = 1; };
396 + $0 = sprintf("%s - sending to %s port %s", $program, $peer, $port);
397 + my $result = syswrite $sd, $rq;
398 + if ($result != length($rq)) {
399 + warning("cannot send to $peer:$port ($!).");
401 + $0 = sprintf("%s - reading from %s port %s", $program, $peer, $port);
403 + local $SIG{'ALRM'} = sub { die "timeout";};
404 + alarm(opt('timeout')) if opt('timeout') > 0;
405 + while ($_ = <$sd>) {
406 + $0 = sprintf("%s - read from %s port %s", $program, $peer, $port);
407 + verbose("RECEIVE:", "%s", define($_, "<undefined>"));
408 + $reply .= $_ if defined $_;
410 + if (opt('timeout') > 0) {
416 - $0 = sprintf("%s - reading from %s port %s", $program, $peer, $port);
417 - alarm(opt('timeout')) if opt('timeout') > 0;
418 - while (!$timeout && ($_ = <$sd>)) {
419 - $0 = sprintf("%s - read from %s port %s", $program, $peer, $port);
420 - verbose("RECEIVE:", "%s", define($_, "<undefined>"));
421 - $reply .= $_ if defined $_;
423 - if (opt('timeout') > 0) {
428 - warning("TIMEOUT: %s after %s seconds", $to, opt('timeout'));
431 - $reply = '' if !defined $reply;
432 + if ($@ and $@ =~ /timeout/) {
433 + warning("TIMEOUT: %s after %s seconds", $to, opt('timeout'));
436 + $reply = '' if !defined $reply;
440 - $0 = sprintf("%s - closed %s port %s", $program, $peer, $port);
441 + $0 = sprintf("%s - closed %s port %s", $program, $peer, $port);
443 ## during testing simulate reading the URL
445 @@ -1855,28 +1905,29 @@
446 ######################################################################
450 my ($ip, $arg, $reply, $url, $skip) = (undef, opt($use), '');
451 $arg = '' unless $arg;
455 + $ip = opt('ip', $h);
458 } elsif ($use eq 'if') {
459 - $skip = opt('if-skip') || '';
460 + $skip = opt('if-skip', $h) || '';
461 $reply = `ifconfig $arg 2> /dev/null`;
464 } elsif ($use eq 'cmd') {
466 - $skip = opt('cmd-skip') || '';
467 + $skip = opt('cmd-skip', $h) || '';
472 } elsif ($use eq 'web') {
473 - $url = opt('web') || '';
474 - $skip = opt('web-skip') || '';
475 + $url = opt('web', $h) || '';
476 + $skip = opt('web-skip', $h) || '';
478 if (exists $builtinweb{$url}) {
479 $skip = $builtinweb{$url}->{'skip'} unless $skip;
480 @@ -1885,15 +1936,15 @@
484 - $reply = geturl(opt('proxy'), $url) || '';
485 + $reply = geturl(opt('proxy', $h), $url) || '';
488 } elsif (($use eq 'cisco')) {
489 # Stuff added to support Cisco router ip http daemon
490 # User fw-login should only have level 1 access to prevent
491 # password theft. This is pretty harmless.
492 - my $queryif = opt('if');
493 - $skip = opt('fw-skip') || '';
494 + my $queryif = opt('if', $h);
495 + $skip = opt('fw-skip', $h) || '';
497 # Convert slashes to protected value "\/"
498 $queryif =~ s%\/%\\\/%g;
499 @@ -1901,13 +1952,30 @@
500 # Protect special HTML characters (like '?')
501 $queryif =~ s/([\?&= ])/sprintf("%%%02x",ord($1))/ge;
503 - $url = "http://".opt('fw')."/level/1/exec/show/ip/interface/brief/${queryif}/CR";
504 - $reply = geturl('', $url, opt('fw-login'), opt('fw-password')) || '';
505 + $url = "http://".opt('fw', $h)."/level/1/exec/show/ip/interface/brief/${queryif}/CR";
506 + $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
509 + } elsif (($use eq 'cisco-asa')) {
510 + # Stuff added to support Cisco ASA ip https daemon
511 + # User fw-login should only have level 1 access to prevent
512 + # password theft. This is pretty harmless.
513 + my $queryif = opt('if', $h);
514 + $skip = opt('fw-skip', $h) || '';
516 + # Convert slashes to protected value "\/"
517 + $queryif =~ s%\/%\\\/%g;
519 + # Protect special HTML characters (like '?')
520 + $queryif =~ s/([\?&= ])/sprintf("%%%02x",ord($1))/ge;
522 + $url = "https://".opt('fw', $h)."/exec/show%20interface%20${queryif}";
523 + $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
527 - $url = opt('fw') || '';
528 - $skip = opt('fw-skip') || '';
529 + $url = opt('fw', $h) || '';
530 + $skip = opt('fw-skip', $h) || '';
532 if (exists $builtinfw{$use}) {
533 $skip = $builtinfw{$use}->{'skip'} unless $skip;
534 @@ -1916,7 +1984,7 @@
538 - $reply = geturl('', $url, opt('fw-login'), opt('fw-password')) || '';
539 + $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
542 if (!defined $reply) {
543 @@ -2270,6 +2338,7 @@
544 Configuration variables applicable to the 'dyndns2' protocol are:
546 server=fqdn.of.service ## defaults to members.dyndns.org
547 + script=/path/to/script ## defaults to /nic/update
548 backupmx=no|yes ## indicates that this host is the primary MX for the domain.
549 static=no|yes ## indicates that this host has a static IP address.
550 custom=no|yes ## indicates that this host is a 'custom' top-level domain name.
551 @@ -2318,7 +2387,9 @@
552 '!yours' => 'The hostname specified exists, but not under the username currently being used',
553 '!donator' => 'The offline setting was set, when the user is not a donator',
554 '!active' => 'The hostname specified is in a Custom DNS domain which has not yet been activated.',
555 - 'abuse', => 'The hostname specified is blocked for abuse; fill in the form at http://support.dyndns.org/abuse.php to be unblocked',
556 + 'abuse', => 'The hostname specified is blocked for abuse; you should receive an email notification ' .
557 + 'which provides an unblock request link. More info can be found on ' .
558 + 'https://www.dyndns.com/support/abuse.html',
560 'numhost' => 'System error: Too many or too few hosts found. Contact support@dyndns.org',
561 'dnserr' => 'System error: DNS error encountered. Contact support@dyndns.org',
562 @@ -2338,7 +2409,7 @@
563 verbose("UPDATE:","updating %s", $hosts);
565 ## Select the DynDNS system to update
566 - my $url = "http://$config{$h}{'server'}/nic/update?system=";
567 + my $url = "http://$config{$h}{'server'}$config{$h}{'script'}?system=";
568 if ($config{$h}{'custom'}) {
569 warning("updating %s: 'custom' and 'static' may not be used together. ('static' ignored)", $hosts)
570 if $config{$h}{'static'};
571 @@ -2375,6 +2446,8 @@
573 my @reply = split /\n/, $reply;
574 my $state = 'header';
575 + my $returnedip = $ip;
577 foreach my $line (@reply) {
578 if ($state eq 'header') {
580 @@ -2385,7 +2458,10 @@
581 } elsif ($state =~ /^results/) {
584 - my ($status, $ip) = split / /, lc $line;
585 + # bug #10: some dyndns providers does not return the IP so
586 + # we can't use the returned IP
587 + my ($status, $returnedip) = split / /, lc $line;
588 + $ip = $returnedip if (not $ip);
589 my $h = shift @hosts;
591 $config{$h}{'status'} = $status;
592 @@ -3378,7 +3454,98 @@
596 +######################################################################
598 +######################################################################
599 +## nic_freedns_examples
600 +######################################################################
601 +sub nic_freedns_examples {
606 +The 'freedns' protocol is used by DNS services offered by freedns.afraid.org.
608 +Configuration variables applicable to the 'freedns' protocol are:
609 + protocol=freedns ##
610 + server=fqdn.of.service ## defaults to freedns.afraid.org
611 + login=service-login ## login name and password registered with the service
612 + password=service-password ##
613 + fully.qualified.host ## the host registered with the service.
615 +Example ${program}.conf file entries:
616 + ## single host update
617 + protocol=freedns, \\
618 + login=my-freedns.afraid.org-login, \\
619 + password=my-freedns.afraid.org-password \\
624 ######################################################################
625 +## nic_freedns_update
627 +## written by John Haney
629 +## based on http://freedns.afraid.org/api/
630 +## needs this url to update:
631 +## http://freedns.afraid.org/api/?action=getdyndns&sha=<sha1sum of login|password>
632 +## This returns a list of host|currentIP|updateURL lines.
633 +## Pick the line that matches myhost, and fetch the URL.
634 +## word 'Updated' for success, 'fail' for failure.
636 +######################################################################
637 +sub nic_freedns_update {
640 + debug("\nnic_freedns_update -------------------");
642 + ## First get the list of updatable hosts
644 + $url = "http://$config{$_[0]}{'server'}/api/?action=getdyndns&sha=".&sha1_hex("$config{$_[0]}{'login'}|$config{$_[0]}{'password'}");
645 + my $reply = geturl(opt('proxy'), $url);
646 + if (!defined($reply) || !$reply || !header_ok($_[0], $reply)) {
647 + failed("updating %s: Could not connect to %s for site list.", $_[0], $url);
650 + my @lines = split("\n", $reply);
653 + my @rec = split(/\|/, $_);
654 + $freedns_hosts{$rec[0]} = \@rec if ($#rec > 0);
656 + if (!keys %freedns_hosts) {
657 + failed("Could not get freedns update URLs from %s", $config{$_[0]}{'server'});
660 + ## update each configured host
661 + foreach my $h (@_) {
663 + my $ip = delete $config{$h}{'wantip'};
664 + info("setting IP address to %s for %s", $ip, $h);
665 + verbose("UPDATE:","updating %s", $h);
667 + if($ip ne $freedns_hosts{$h}->[1]) {
668 + my $reply = geturl(opt('proxy'), $freedns_hosts{$h}->[2]);
669 + if (!defined($reply) || !$reply) {
670 + failed("updating %s: Could not connect to %s.", $h, $freedns_hosts{$h}->[2]);
673 + last if !header_ok($h, $reply);
675 + if($reply =~ /Updated.*host/) {
676 + success("updating %s: good: IP address set to %s", $h, $ip);
678 + $config{$h}{'status'} = 'failed';
679 + warning("SENT: %s", $freedns_hosts{$h}->[2]) unless opt('verbose');
680 + warning("REPLIED: %s", $reply);
681 + failed("updating %s: Invalid reply.", $h);
687 +######################################################################
688 # vim: ai ts=4 sw=4 tw=78 :
691 Index: patches/loopia.patch
692 ===================================================================
693 Index: patches/freedns-patch
694 ===================================================================
695 Index: patches/prevent-hang.patch
696 ===================================================================
697 Index: patches/cisco-asa.patch
698 ===================================================================
699 Index: patches/foreground.patch
700 ===================================================================
702 ===================================================================
703 --- README (.../tags/release-3.8.0)
704 +++ README (.../trunk)
706 DnsPark - See http://www.dnspark.com for details
707 DslReports - See http://www.dslreports.com for details
708 Sitelutions - see http://www.sitelutions.com for details
709 +Loopia - See http://www.loopia.se for details
711 DDclient now supports many of cable/dsl broadband routers.
714 ===================================================================
715 --- RELEASENOTE (.../tags/release-3.8.0)
716 +++ RELEASENOTE (.../trunk)
718 +It's been a while but here is new release of ddclient. There are some important
719 +changes and some documentation is modified. A detailed overview can be found
720 +in ChangeLog but here's a quick overview:
722 + * Added ddclient-noip.patch send by Kurt Bussche.
723 + * Added and applied default timeout patch from
724 + https://bugs.launchpad.net/ubuntu/+source/ddclient/+bug/116066
725 + * Sending mail when killed, not after TERM-signal
726 + * Added support for multiple IP adresses.
730 ===================================================================