From d8a9d9d089787773855070841389bc3215322091 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 20 May 2024 00:55:11 -0400 Subject: [PATCH 1/2] Add support for infinite duration --- Makefile.am | 1 + ddclient.in | 8 +++++-- t/interval_expired.pl | 51 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 t/interval_expired.pl diff --git a/Makefile.am b/Makefile.am index 9503c04..459931e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,7 @@ handwritten_tests = \ t/builtinfw_query.pl \ t/get_ip_from_if.pl \ t/geturl_connectivity.pl \ + t/interval_expired.pl \ t/is-and-extract-ipv4.pl \ t/is-and-extract-ipv6.pl \ t/is-and-extract-ipv6-global.pl \ diff --git a/ddclient.in b/ddclient.in index f6bf5ae..c0fa54e 100755 --- a/ddclient.in +++ b/ddclient.in @@ -104,7 +104,7 @@ my $programd = $0; $programd =~ s%^.*/%%; my $program = $programd; $program =~ s/d$//; -my $now = time; +our $now = time; my $hostname = hostname(); # subst_var(subst, default) returns subst unless it looks like @foo@ in which case it returns @@ -140,7 +140,8 @@ $ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin our %globals; our %config; -my ($result, %cache); +our %cache; +my $result; my $saved_cache; my %saved_opt; my $daemon; @@ -2465,6 +2466,8 @@ sub interval { $value = $1 * 60*60; } elsif ($value =~ /^(\d+)(days|d)/i) { $value = $1 * 60*60*24; + } elsif ($value =~ qr/^(?:inf(?:init[ye])?|indefinite(?:ly)?|never|forever|always)$/i) { + $value = 'inf'; } elsif ($value !~ /^\d+$/) { $value = undef; } @@ -2473,6 +2476,7 @@ sub interval { sub interval_expired { my ($host, $time, $interval) = @_; + return 0 if ($config{$host}{$interval} // 0) == 'inf'; return 1 if !exists $cache{$host}; return 1 if !exists $cache{$host}{$time} || !$cache{$host}{$time}; return 1 if !exists $config{$host}{$interval} || !$config{$host}{$interval}; diff --git a/t/interval_expired.pl b/t/interval_expired.pl new file mode 100644 index 0000000..1043dea --- /dev/null +++ b/t/interval_expired.pl @@ -0,0 +1,51 @@ +use Test::More; +SKIP: { eval { require Test::Warnings; } or skip($@, 1); } +eval { require 'ddclient'; } or BAIL_OUT($@); + +my $h = 't/interval_expired.pl'; + +my $default_now = 1000000000; + +my @test_cases = ( + { + interval => 'inf', + want => 0, + }, + { + now => 'inf', + interval => 'inf', + want => 0, + }, + { + cache => '-inf', + interval => 'inf', + want => 0, + }, + { + cache => undef, # Falsy cache value. + interval => 'inf', + want => 0, + }, + { + now => 0, + cache => 0, # Different kind of falsy cache value. + interval => 'inf', + want => 0, + }, +); + +for my $tc (@test_cases) { + $tc->{now} //= $default_now; + # For convenience, $tc->{cache} is an offset from $tc->{now}, not an absolute time.. + my $cachetime = $tc->{now} + $tc->{cache} if defined($tc->{cache}); + $ddclient::config{$h} = {'interval' => $tc->{interval}}; + %ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" + $ddclient::cache{$h} = {'cached-time' => $cachetime} if defined($cachetime); + %ddclient::cache if 0; # suppress spurious warning "Name used only once: possible typo" + $ddclient::now = $tc->{now}; + $ddclient::now if 0; # suppress spurious warning "Name used only once: possible typo" + my $desc = "now=$tc->{now}, cache=${\($cachetime // 'undef')}, interval=$tc->{interval}"; + is(ddclient::interval_expired($h, 'cached-time', 'interval'), $tc->{want}, $desc); +} + +done_testing(); From 61b979c49eed96b5f69bd0ffbbbf92555c6923ee Mon Sep 17 00:00:00 2001 From: Joel Croteau Date: Sat, 20 Apr 2024 20:13:14 -0700 Subject: [PATCH 2/2] New 'emailonly' protocol that simply sends an email on IP change This adds a protocol to email IP address changes without needing a dynamic DNS service. This is useful if you don't use a DDNS service but want to be notified when the IP of a machine changes. --- ChangeLog.md | 2 ++ ddclient.conf.in | 6 +++++ ddclient.in | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index d0ca95c..eea6429 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -41,6 +41,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master). * The second and subsequent lines in a multi-line log message are now prefixed with a `|` character. [#676](https://github.com/ddclient/ddclient/pull/676) + * `emailonly`: New `protocol` option that simply emails you when your IP + address changes. [#654](https://github.com/ddclient/ddclient/pull/654) ### Bug fixes diff --git a/ddclient.conf.in b/ddclient.conf.in index 8652635..e916958 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -391,3 +391,9 @@ ssl=yes # use ssl-support. Works with # password=ddns_password # redirect=2 # example.com + +## +## Email Only +## +# protocol=emailonly +# host.example.com diff --git a/ddclient.in b/ddclient.in index c0fa54e..9ce0d1d 100755 --- a/ddclient.in +++ b/ddclient.in @@ -1067,6 +1067,18 @@ my %services = ( %{$variables{'service-common-defaults'}}, }, }, + 'emailonly' => { + 'updateable' => undef, + 'update' => \&nic_emailonly_update, + 'examples' => \&nic_emailonly_examples, + 'variables' => { + %{$variables{'service-common-defaults'}}, + 'login' => undef, + 'password' => undef, + # Change default to never re-notify if IP address has not changed. + 'max-interval' => setv(T_DELAY, 0, 0, 'inf', 0), + }, + }, ); # Delete undefined variables to make it easier to cancel previously defined variables. for my $svc (values(%services)) { @@ -8080,6 +8092,55 @@ sub nic_infomaniak_update { } } +###################################################################### +## nic_emailonly_update +## +## Written by Joel Croteau +## +## Do not update Dynamic DNS, only send status emails. Use if you do +## not have a DDNS host, but still want to get emails when your IP +## address changes. Note that you must set the "mail" config option +## and configure sendmail for this to have an effect. At least one +## host must be specified; the host names are mentioned in the email. +###################################################################### +sub nic_emailonly_update { + debug("\nnic_emailonly_update -------------------"); + # Note: This is logged after $config{$_}{'max-interval'] even if the IP address hasn't changed, + # so it is best to avoid phrasing like, "IP address has changed." + logmsg(email => 1, join("\n", 'Host IP addresses:', map({ + my $ipv4 = delete($config{$_}{'wantipv4'}); + my $ipv6 = delete($config{$_}{'wantipv6'}); + $config{$_}{'status-ipv4'} = 'good' if $ipv4; + $config{$_}{'status-ipv6'} = 'good' if $ipv6; + $config{$_}{'ipv4'} = $ipv4 if $ipv4; + $config{$_}{'ipv6'} = $ipv6 if $ipv6; + $config{$_}{'mtime'} = $now; + sprintf('%30s %s', $_, join(' ', grep(defined($_), $ipv4, $ipv6))); + } @_))); +} + +###################################################################### +## nic_emailonly_examples +###################################################################### +sub nic_emailonly_examples { + return <<"EoEXAMPLE"; +o 'emailonly' + +The 'emailonly' protocol is a dummy protocol that will send status emails but +not actually issue any dynamic DNS updates. You can use this if you don\'t +have a DDNS host, but still want to get emails when your IP address changes. +For this to have an effect, you must set the 'mail' config option, have +sendmail properly configured on your machine, and specify at least one dummy +hostname. + +Example ${program}.conf file entries: + ## single host update + mail=me\@example.com + protocol=emailonly + host.example.com +EoEXAMPLE +} + # Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR), # otherwise do nothing. This "modulino" pattern makes it possible to import this file as a module # and test its functions directly; there's no need for test-only command-line arguments or stdout