From 59f6c2959ab4f3cf54b5332b805991e3b6281b7a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 6 Jan 2025 21:02:54 -0500 Subject: [PATCH 01/25] Prepare for v4.0.0-rc.2 --- ChangeLog.md | 2 ++ ddclient.in | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 946db3d..4c5cc80 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,8 @@ This document describes notable changes. For details, see the [source code repository history](https://github.com/ddclient/ddclient/commits/main). +## v4.0.0-rc.2 (unreleased work-in-progress) + ## 2024-12-25 v4.0.0-rc.1 ### Breaking changes diff --git a/ddclient.in b/ddclient.in index a834b16..b37eed3 100755 --- a/ddclient.in +++ b/ddclient.in @@ -78,7 +78,7 @@ use Sys::Hostname; # # For consistency and to match user expectations, the release part of the version is always three # components: MAJOR.MINOR.PATCH. -use version 0.77; our $VERSION = version->declare('v4.0.0.0_901'); +use version 0.77; our $VERSION = version->declare('v4.0.0.0_902'); sub parse_version { my ($v) = @_; From 8030a46ca396ccc6d9eac3739987287907040849 Mon Sep 17 00:00:00 2001 From: Joel Beckmeyer Date: Fri, 18 Aug 2023 10:00:20 -0400 Subject: [PATCH 02/25] add mail-from option --- ddclient.conf.in | 1 + ddclient.in | 34 +++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index dd4189e..fc89163 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -25,6 +25,7 @@ daemon=300 # check every 300 seconds syslog=yes # log update msgs to syslog mail=root # mail all msgs to root mail-failure=root # mail failed update msgs to root +# mail-from=root # send mail from root, by default unset and handled by system sendmail pid=@runstatedir@/ddclient.pid # record PID in file. # postscript=script # run script after updating. The new IP is # added as argument. diff --git a/ddclient.in b/ddclient.in index b37eed3..3def280 100755 --- a/ddclient.in +++ b/ddclient.in @@ -704,6 +704,7 @@ our %cfgvars = ( 'priority' => setv(T_STRING,0, 'notice', undef), 'mail' => setv(T_EMAIL, 0, undef, undef), 'mail-failure' => setv(T_EMAIL, 0, undef, undef), + 'mail-from' => setv(T_EMAIL, 0, '', undef), 'max-warn' => setv(T_NUMBER,0, 1, undef), 'exec' => setv(T_BOOL, 0, 1, undef), @@ -1427,6 +1428,7 @@ my @opt = ( ["max-warn", "=i", "--max-warn= : log at most warning messages for undefined IP address"], ["mail", "=s", "--mail=
: e-mail messages to
"], ["mail-failure", "=s", "--mail-failure= : e-mail messages for failed updates to "], + ["mail-from", "=s", "--mail-from= : send status e-mail messages from "], ["exec", "!", "--{no}exec : do {not} execute; just show what would be done"], ["debug", "!", "--{no}debug : print {no} debugging information"], ["verbose", "!", "--{no}verbose : print {no} verbose information"], @@ -2399,20 +2401,34 @@ sub logger { } sub sendmail { my $recipients = opt('mail'); + my $sender = opt('mail-from'); if (opt('mail-failure') && ($result ne 'OK' && $result ne '0')) { $recipients = opt('mail-failure'); } if ($emailbody && $recipients && $emailbody ne $last_emailbody) { - pipecmd("sendmail -oi $recipients", - "To: $recipients", - "Subject: status report from $program\@$hostname", - "\r\n", - $emailbody, - "", - "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter - " $program\@$hostname (version $version)" - ); + if ($sender) { + pipecmd("sendmail -oi $recipients", + "To: $recipients", + "From: $sender", + "Subject: status report from $program\@$hostname", + "\r\n", + $emailbody, + "", + "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter + " $program\@$hostname (version $version)" + ); + } else { + pipecmd("sendmail -oi $recipients", + "To: $recipients", + "Subject: status report from $program\@$hostname", + "\r\n", + $emailbody, + "", + "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter + " $program\@$hostname (version $version)" + ); + } } $last_emailbody = $emailbody; $emailbody = ''; From a2e818d6d311ce671bbfbfe230341cb8274e0e6b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 19 Dec 2024 19:30:05 -0500 Subject: [PATCH 03/25] fixup! add mail-from option refine usage wording --- ddclient.conf.in | 4 +++- ddclient.in | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index fc89163..62a7ee9 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -25,7 +25,9 @@ daemon=300 # check every 300 seconds syslog=yes # log update msgs to syslog mail=root # mail all msgs to root mail-failure=root # mail failed update msgs to root -# mail-from=root # send mail from root, by default unset and handled by system sendmail +# mail-from=root # set the email "From:" header to "root". If + # unset (the default) or empty, the from address + # depends on your system's default behavior. pid=@runstatedir@/ddclient.pid # record PID in file. # postscript=script # run script after updating. The new IP is # added as argument. diff --git a/ddclient.in b/ddclient.in index 3def280..07524c3 100755 --- a/ddclient.in +++ b/ddclient.in @@ -1428,7 +1428,7 @@ my @opt = ( ["max-warn", "=i", "--max-warn= : log at most warning messages for undefined IP address"], ["mail", "=s", "--mail=
: e-mail messages to
"], ["mail-failure", "=s", "--mail-failure= : e-mail messages for failed updates to "], - ["mail-from", "=s", "--mail-from= : send status e-mail messages from "], + ["mail-from", "=s", '--mail-from= : set the "From:" header in e-mail messages to if non-empty'], ["exec", "!", "--{no}exec : do {not} execute; just show what would be done"], ["debug", "!", "--{no}debug : print {no} debugging information"], ["verbose", "!", "--{no}verbose : print {no} verbose information"], From 2de77f17f706f43cdc2e5e57833488c5653f1ce4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 19 Dec 2024 19:30:43 -0500 Subject: [PATCH 04/25] fixup! add mail-from option default to undef --- ddclient.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 07524c3..fd4af81 100755 --- a/ddclient.in +++ b/ddclient.in @@ -704,7 +704,7 @@ our %cfgvars = ( 'priority' => setv(T_STRING,0, 'notice', undef), 'mail' => setv(T_EMAIL, 0, undef, undef), 'mail-failure' => setv(T_EMAIL, 0, undef, undef), - 'mail-from' => setv(T_EMAIL, 0, '', undef), + 'mail-from' => setv(T_EMAIL, 0, undef, undef), 'max-warn' => setv(T_NUMBER,0, 1, undef), 'exec' => setv(T_BOOL, 0, 1, undef), @@ -2401,7 +2401,7 @@ sub logger { } sub sendmail { my $recipients = opt('mail'); - my $sender = opt('mail-from'); + my $sender = opt('mail-from') // ''; if (opt('mail-failure') && ($result ne 'OK' && $result ne '0')) { $recipients = opt('mail-failure'); From d1f81dc9e4a6d4d397706e23278dacce4aedbfeb Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 19 Dec 2024 19:31:53 -0500 Subject: [PATCH 05/25] fixup! add mail-from option factor out duplicate code --- ddclient.in | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/ddclient.in b/ddclient.in index fd4af81..1043dce 100755 --- a/ddclient.in +++ b/ddclient.in @@ -2407,28 +2407,16 @@ sub sendmail { $recipients = opt('mail-failure'); } if ($emailbody && $recipients && $emailbody ne $last_emailbody) { - if ($sender) { - pipecmd("sendmail -oi $recipients", - "To: $recipients", - "From: $sender", - "Subject: status report from $program\@$hostname", - "\r\n", - $emailbody, - "", - "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter - " $program\@$hostname (version $version)" - ); - } else { - pipecmd("sendmail -oi $recipients", - "To: $recipients", - "Subject: status report from $program\@$hostname", - "\r\n", - $emailbody, - "", - "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter - " $program\@$hostname (version $version)" - ); - } + pipecmd("sendmail -oi $recipients", + "To: $recipients", + $sender ne '' ? ("From: $sender") : (), + "Subject: status report from $program\@$hostname", + "\r\n", + $emailbody, + "", + "-- ", # https://en.wikipedia.org/wiki/Signature_block#Standard_delimiter + " $program\@$hostname (version $version)" + ); } $last_emailbody = $emailbody; $emailbody = ''; From d2b1a4dfa6c9394634338bb73474b7ea4c49ce48 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 19 Dec 2024 19:33:27 -0500 Subject: [PATCH 06/25] fixup! add mail-from option move variable declaration closer to usage --- ddclient.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 1043dce..ebd0e71 100755 --- a/ddclient.in +++ b/ddclient.in @@ -2401,12 +2401,12 @@ sub logger { } sub sendmail { my $recipients = opt('mail'); - my $sender = opt('mail-from') // ''; if (opt('mail-failure') && ($result ne 'OK' && $result ne '0')) { $recipients = opt('mail-failure'); } if ($emailbody && $recipients && $emailbody ne $last_emailbody) { + my $sender = opt('mail-from') // ''; pipecmd("sendmail -oi $recipients", "To: $recipients", $sender ne '' ? ("From: $sender") : (), From 76fccba1510a8ebe15c5bcdbf4b7626c663fa4a4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 19 Dec 2024 19:42:03 -0500 Subject: [PATCH 07/25] fixup! add mail-from option add changelog entry --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 4c5cc80..81ba17c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -55,6 +55,8 @@ repository history](https://github.com/ddclient/ddclient/commits/main). ### New features + * New `--mail-from` option to control the "From:" header of email messages. + [#565](https://github.com/ddclient/ddclient/pull/565) * Simultaneous/separate updating of IPv4 (A) records and IPv6 (AAAA) records is now supported in the following services: `gandi` ([#558](https://github.com/ddclient/ddclient/pull/558)), `nsupdate` From cf4bad127dd9dd03d9ec3d194530e2e33f940622 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 6 Jan 2025 20:21:53 -0600 Subject: [PATCH 08/25] fixup! add main-from option move changelog entry to v4.0.0-rc.2 --- ChangeLog.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 81ba17c..a8135ad 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,11 @@ repository history](https://github.com/ddclient/ddclient/commits/main). ## v4.0.0-rc.2 (unreleased work-in-progress) +### New features + + * New `--mail-from` option to control the "From:" header of email messages. + [#565](https://github.com/ddclient/ddclient/pull/565) + ## 2024-12-25 v4.0.0-rc.1 ### Breaking changes @@ -55,8 +60,6 @@ repository history](https://github.com/ddclient/ddclient/commits/main). ### New features - * New `--mail-from` option to control the "From:" header of email messages. - [#565](https://github.com/ddclient/ddclient/pull/565) * Simultaneous/separate updating of IPv4 (A) records and IPv6 (AAAA) records is now supported in the following services: `gandi` ([#558](https://github.com/ddclient/ddclient/pull/558)), `nsupdate` From e4920373ee425b1d58bb5f58975c0907831a6314 Mon Sep 17 00:00:00 2001 From: Zorks <6314048+Zorks@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:14:06 -0700 Subject: [PATCH 09/25] Correct NearlyFreeSpeech.NET example extra spaces in the protocol and a trailing comma caused connection issues if not corrected. --- ddclient.conf.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index 62a7ee9..cca3c3d 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -133,9 +133,9 @@ pid=@runstatedir@/ddclient.pid # record PID in file. ## ## NearlyFreeSpeech.NET (nearlyfreespeech.net) ## -# protocol = nfsn, \ +# protocol=nfsn, \ # login=member-login, \ -# password=api-key, \ +# password=api-key \ # zone=example.com \ # example.com,subdomain.example.com From 678b76f7e855d311aebb7d0257d011ebea3e9eb2 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Sat, 28 Dec 2024 13:07:08 -0600 Subject: [PATCH 10/25] nfsn Rearrange zone property in example Usually, the zone property is not the last property in the configuration convention that is followed in ddclient config examples. --- ddclient.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index cca3c3d..878cab3 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -134,9 +134,9 @@ pid=@runstatedir@/ddclient.pid # record PID in file. ## NearlyFreeSpeech.NET (nearlyfreespeech.net) ## # protocol=nfsn, \ +# zone=example.com, \ # login=member-login, \ # password=api-key \ -# zone=example.com \ # example.com,subdomain.example.com ## From 56f88e3babadeb16b290bac5d87bb4cc50ec0d16 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 7 Jan 2025 02:34:05 -0500 Subject: [PATCH 11/25] New GitHub workflow to prohibit autosquash commits This prohibits commits whose commit message starts with "squash!", "fixup!", or "amend!" unless the PR has the 'pr-permit-autosquash' label. Such commits are created via the `--fixup` or `--squash` options to `git commit`, and cause `git rebase --autosquash` to automatically adjust the todo list appropriately before performing the rebase. Their existence implies that the PR should be rebased with `--autosquash` before merging. --- .github/workflows/pr.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b36bfa6..b4a7729 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -29,3 +29,21 @@ jobs: git show "${out}" >&2 exit 1 } + no-autosquash: + if: ${{ !contains(github.event.pull_request.labels.*.name, 'pr-permit-autosquash') }} + name: No --autosquash commits + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: 'No commits with messages starting with "fixup!", "squash!", or "amend!"' + run: | + log() { printf %s\\n "$*" >&2; } + error() { log "ERROR: $@"; } + fatal() { error "$@"; exit 1; } + try() { log "Running command $@"; "$@" || fatal "'$@' failed"; } + out=$(try git log --oneline '${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}') || exit 1 + ! grep -E '^[^ ]* (fixup|squash|amend)!' < Date: Sat, 4 Jan 2025 21:08:45 -0500 Subject: [PATCH 12/25] Change default location of `ddclient.conf` to `${sysconfdir}/ddclient` --- ChangeLog.md | 25 +++++++++++++++++++++++++ Makefile.am | 4 ++-- README.md | 2 +- configure.ac | 12 ++++++++++++ ddclient.in | 2 +- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a8135ad..41ca7e8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,31 @@ repository history](https://github.com/ddclient/ddclient/commits/main). ## v4.0.0-rc.2 (unreleased work-in-progress) +### Breaking changes + + * ddclient now looks for `ddclient.conf` in `${sysconfdir}/ddclient` by + default instead of `${sysconfdir}`. + [#789](https://github.com/ddclient/ddclient/pull/789) + + To retain the previous behavior, pass `'--with-confdir=${sysconfdir}'` to + `configure`. For example: + + ```shell + # Before v4.0.0: + ./configure --sysconfdir=/etc + # Equivalent with v4.0.0 and later (the single quotes are intentional): + ./configure --sysconfdir=/etc --with-confdir='${sysconfdir}' + ``` + + or: + + ```shell + # Before v4.0.0: + ./configure --sysconfdir=/etc/ddclient + # Equivalent with v4.0.0 and later: + ./configure --sysconfdir=/etc + ``` + ### New features * New `--mail-from` option to control the "From:" header of email messages. diff --git a/Makefile.am b/Makefile.am index d9baa98..3be50d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,8 +28,8 @@ $(subst_files): Makefile -e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \ -e '1 s|^#\!.*perl$$|#\!$(PERL)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@confdir[@]|$(confdir)|g' \ -e 's|@runstatedir[@]|$(runstatedir)|g' \ - -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@CURL[@]|$(CURL)|g' \ "$${in}" >'$@'.tmp && \ { ! test -x "$${in}" || chmod +x '$@'.tmp; } @@ -40,7 +40,7 @@ ddclient.conf: $(srcdir)/ddclient.conf.in bin_SCRIPTS = ddclient -sysconf_DATA = ddclient.conf +conf_DATA = ddclient.conf install-data-local: $(MKDIR_P) '$(DESTDIR)$(localstatedir)'/cache/ddclient diff --git a/README.md b/README.md index 22db499..7eef76e 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ operating system. See the image to the right for a list of distributions with a ```shell ./configure \ --prefix=/usr \ - --sysconfdir=/etc/ddclient \ + --sysconfdir=/etc \ --localstatedir=/var make make VERBOSE=1 check diff --git a/configure.ac b/configure.ac index 827f8b7..49daf65 100644 --- a/configure.ac +++ b/configure.ac @@ -23,6 +23,18 @@ AC_REQUIRE_AUX_FILE([tap-driver.sh]) AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects parallel-tests]) AM_SILENT_RULES +m4_define([CONFDIR_DEFAULT], [${sysconfdir}/AC_PACKAGE_NAME]) +AC_ARG_WITH( + [confdir], + [AS_HELP_STRING( + [--with-confdir=DIR], + m4_expand([[look for ddclient.conf in DIR @<:@default: ]CONFDIR_DEFAULT[@:>@]]))], + [], + # The single quotes are intentional; see: + # https://www.gnu.org/software/automake/manual/html_node/Uniform.html + [with_confdir='CONFDIR_DEFAULT']) +AC_SUBST([confdir], [${with_confdir}]) + AC_PROG_MKDIR_P # The Fedora Docker image doesn't come with the 'findutils' package. diff --git a/ddclient.in b/ddclient.in index ebd0e71..12c3094 100755 --- a/ddclient.in +++ b/ddclient.in @@ -132,7 +132,7 @@ sub subst_var { return $subst; } -my $etc = subst_var('@sysconfdir@', '/etc/ddclient'); +my $etc = subst_var('@confdir@', '/etc/ddclient'); my $cachedir = subst_var('@localstatedir@', '/var') . '/cache/ddclient'; our @curl = (subst_var('@CURL@', 'curl')); From 660bb11c02bb756ecfef8facef8fa1f21e28d2ff Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 7 Jan 2025 04:39:01 -0500 Subject: [PATCH 13/25] Release v4.0.0-rc.2 --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 41ca7e8..a0b45e8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,7 +3,7 @@ This document describes notable changes. For details, see the [source code repository history](https://github.com/ddclient/ddclient/commits/main). -## v4.0.0-rc.2 (unreleased work-in-progress) +## 2025-01-07 v4.0.0-rc.2 ### Breaking changes From 8decfc4b7765ed42d8e0598060d7a30ddcdbef42 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 7 Jan 2025 14:34:18 -0500 Subject: [PATCH 14/25] Post-release version bump We'll probably just release `v4.0.0` without an `-rc.3`, but I'm setting it to `-rc.3` just in case (decreasing the version number can break automatically built daily Git snapshots). --- ChangeLog.md | 2 ++ ddclient.in | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index a0b45e8..817b5cf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,8 @@ This document describes notable changes. For details, see the [source code repository history](https://github.com/ddclient/ddclient/commits/main). +## v4.0.0-rc.3 (unreleased work-in-progress) + ## 2025-01-07 v4.0.0-rc.2 ### Breaking changes diff --git a/ddclient.in b/ddclient.in index 12c3094..861abe5 100755 --- a/ddclient.in +++ b/ddclient.in @@ -78,7 +78,7 @@ use Sys::Hostname; # # For consistency and to match user expectations, the release part of the version is always three # components: MAJOR.MINOR.PATCH. -use version 0.77; our $VERSION = version->declare('v4.0.0.0_902'); +use version 0.77; our $VERSION = version->declare('v4.0.0.0_903'); sub parse_version { my ($v) = @_; From 3f3b8cf8257823a1a647927f5c7ef91b4bebecec Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 03:00:41 -0500 Subject: [PATCH 15/25] tests: Localize config setting This isn't strictly necessary, but is good practice because it guarantees that the config is cleaned up after each test case. --- t/ssl-validate.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/ssl-validate.pl b/t/ssl-validate.pl index 5939dd9..bc7553a 100644 --- a/t/ssl-validate.pl +++ b/t/ssl-validate.pl @@ -73,7 +73,7 @@ for my $tc (@test_cases) { SKIP: { skip("IPv6 not supported on this system", 1) if $tc->{ipv6} && !$ipv6_supported; skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported; - $ddclient::config{$h} = $tc->{cfg}; + local $ddclient::config{$h} = $tc->{cfg}; %ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc}) if ($tc->{cfg}{usev4}); From c89a2d61860e44c99e2e7086c1870270ad58d62c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 03:14:16 -0500 Subject: [PATCH 16/25] tests: Enable debug logging in `t/ssl-validate.pl` --- t/ssl-validate.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/t/ssl-validate.pl b/t/ssl-validate.pl index bc7553a..1414c02 100644 --- a/t/ssl-validate.pl +++ b/t/ssl-validate.pl @@ -7,6 +7,9 @@ BEGIN { } use ddclient::t::ip; +local $ddclient::globals{debug} = 1; +local $ddclient::globals{verbose} = 1; + httpd_ssl_required(); # Note: $ddclient::globals{'ssl_ca_file'} is intentionally NOT set to "$certdir/dummy-ca-cert.pem" @@ -70,6 +73,7 @@ my @test_cases = ( ); for my $tc (@test_cases) { + local $ddclient::_l = ddclient::pushlogctx($tc->{desc}); SKIP: { skip("IPv6 not supported on this system", 1) if $tc->{ipv6} && !$ipv6_supported; skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported; From 06c47695fcd1873c5e7b874cf4fee3da126126f0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 03:03:44 -0500 Subject: [PATCH 17/25] tests: Fix t/ssl-validate.pl in minimal test environments --- Makefile.am | 1 + .../Test/Fake/HTTPD/other-ca-cert.pem | 80 +++++++++++++++++++ t/lib/ddclient/t/HTTPD.pm | 3 +- t/ssl-validate.pl | 13 ++- 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem diff --git a/Makefile.am b/Makefile.am index 3be50d6..2124315 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,6 +156,7 @@ EXTRA_DIST += $(handwritten_tests) \ t/lib/ddclient/Test/Fake/HTTPD/dummy-ca-cert.pem \ t/lib/ddclient/Test/Fake/HTTPD/dummy-server-cert.pem \ t/lib/ddclient/Test/Fake/HTTPD/dummy-server-key.pem \ + t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem \ t/lib/ddclient/t.pm \ t/lib/ddclient/t/HTTPD.pm \ t/lib/ddclient/t/ip.pm \ diff --git a/t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem b/t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem new file mode 100644 index 0000000..c15b26c --- /dev/null +++ b/t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem @@ -0,0 +1,80 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 6c:bf:34:52:19:4d:c9:29:2b:a6:8b:41:59:aa:c6:c5:1f:a2:bb:10 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root Certification Authority + Validity + Not Before: Jan 8 08:24:32 2025 GMT + Not After : Jan 9 08:24:32 2125 GMT + Subject: CN=Root Certification Authority + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c3:3d:19:6b:72:0a:9e:87:c0:28:a1:ff:d0:08: + 21:55:52:71:92:f2:98:36:75:fc:95:b4:0c:5e:c9: + 98:b3:3c:a1:ee:cf:91:6f:07:bf:82:c9:d5:51:c0: + eb:f8:46:17:41:52:1d:c6:89:ec:63:dd:5c:30:87: + a7:b5:0d:dd:ae:bf:46:fd:de:1a:be:1d:69:83:0d: + fb:d9:5a:33:0b:8d:5f:63:76:fc:a8:b1:54:37:1e: + 0b:12:44:93:90:39:1c:48:ee:f0:f2:12:fe:dc:fb: + 58:a5:76:3b:e8:e8:94:44:1e:9d:03:22:5f:21:6a: + 17:66:d1:4a:bf:12:d7:3c:15:76:11:76:09:ab:bf: + 21:ef:0c:a5:a9:e0:08:99:63:19:26:e4:d8:5d:c2: + 40:8b:98:e6:5d:df:b3:8c:63:e2:01:7c:5e:fb:55: + 39:a8:67:78:80:d2:6b:61:b2:e2:2e:93:c0:9d:91: + 0e:a1:79:4f:fc:38:94:ff:6f:65:18:8f:3e:0b:8c: + 1f:cd:48:d7:46:5a:a2:76:d6:e0:bd:3c:aa:3d:44: + 9e:50:e6:fd:e1:12:1a:ee:a1:9a:69:48:60:63:da: + 41:ae:a7:3d:36:1b:95:fb:b7:f1:0d:60:cd:2f:e3: + b1:1f:b1:db:b4:98:a6:62:87:de:54:80:d1:45:43: + 5b:25 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + E1:7C:D3:C3:9E:C7:F5:2C:DA:7C:D7:85:78:91:BA:26:88:61:F9:D4 + X509v3 Authority Key Identifier: + E1:7C:D3:C3:9E:C7:F5:2C:DA:7C:D7:85:78:91:BA:26:88:61:F9:D4 + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 9d:dc:49:c6:14:13:19:38:d9:14:b5:70:f0:3b:01:8e:d7:32: + a7:69:f0:21:68:ec:ad:8c:ee:53:7d:16:64:7d:3e:c2:d2:ac: + 5a:54:17:55:84:43:1e:46:1d:42:01:fb:89:e0:db:ec:e8:f0: + 3c:22:82:54:1d:38:12:21:45:3c:37:44:3b:2e:c9:4d:ed:8d: + 6e:46:f5:a5:cc:ba:39:61:ab:df:cf:1f:d2:c9:40:e2:db:3f: + 05:ea:83:14:93:5f:0e:3d:33:be:98:04:80:87:25:3a:6c:ff: + 8e:87:6a:32:ed:1e:ec:54:90:9b:2a:6e:12:05:6a:9d:15:48: + 3c:ea:c6:9e:ab:71:58:1e:34:95:3f:9b:9e:e3:e5:4b:fb:9e: + 32:f2:d6:59:bf:8d:09:d6:e4:9e:9e:47:b9:d6:78:5f:f3:0c: + 98:ab:56:f0:18:5d:63:8e:83:ee:c1:f2:84:da:0e:64:af:1c: + 18:ff:b3:f9:15:0b:02:50:77:d1:0b:6e:ba:61:bc:9e:c3:37: + 63:91:26:e8:ce:77:9a:47:8f:ef:38:8f:9c:7f:f1:ab:7b:65: + a5:96:b6:92:2e:c7:d3:c3:7a:54:0d:d6:76:f5:d6:88:13:3b: + 17:e2:02:4e:3b:4d:10:95:0a:bb:47:e9:48:25:76:1d:7b:19: + 5c:6f:b8:a1 +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgIUbL80UhlNySkrpotBWarGxR+iuxAwDQYJKoZIhvcNAQEL +BQAwJzElMCMGA1UEAwwcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAgFw0y +NTAxMDgwODI0MzJaGA8yMTI1MDEwOTA4MjQzMlowJzElMCMGA1UEAwwcUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMM9GWtyCp6HwCih/9AIIVVScZLymDZ1/JW0DF7JmLM8oe7PkW8Hv4LJ +1VHA6/hGF0FSHcaJ7GPdXDCHp7UN3a6/Rv3eGr4daYMN+9laMwuNX2N2/KixVDce +CxJEk5A5HEju8PIS/tz7WKV2O+jolEQenQMiXyFqF2bRSr8S1zwVdhF2Cau/Ie8M +pangCJljGSbk2F3CQIuY5l3fs4xj4gF8XvtVOahneIDSa2Gy4i6TwJ2RDqF5T/w4 +lP9vZRiPPguMH81I10ZaonbW4L08qj1EnlDm/eESGu6hmmlIYGPaQa6nPTYblfu3 +8Q1gzS/jsR+x27SYpmKH3lSA0UVDWyUCAwEAAaNjMGEwHQYDVR0OBBYEFOF808Oe +x/Us2nzXhXiRuiaIYfnUMB8GA1UdIwQYMBaAFOF808Oex/Us2nzXhXiRuiaIYfnU +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUA +A4IBAQCd3EnGFBMZONkUtXDwOwGO1zKnafAhaOytjO5TfRZkfT7C0qxaVBdVhEMe +Rh1CAfuJ4Nvs6PA8IoJUHTgSIUU8N0Q7LslN7Y1uRvWlzLo5Yavfzx/SyUDi2z8F +6oMUk18OPTO+mASAhyU6bP+Oh2oy7R7sVJCbKm4SBWqdFUg86saeq3FYHjSVP5ue +4+VL+54y8tZZv40J1uSenke51nhf8wyYq1bwGF1jjoPuwfKE2g5krxwY/7P5FQsC +UHfRC266YbyewzdjkSbozneaR4/vOI+cf/Gre2WllraSLsfTw3pUDdZ29daIEzsX +4gJOO00QlQq7R+lIJXYdexlcb7ih +-----END CERTIFICATE----- diff --git a/t/lib/ddclient/t/HTTPD.pm b/t/lib/ddclient/t/HTTPD.pm index f9a5f1a..94ab271 100644 --- a/t/lib/ddclient/t/HTTPD.pm +++ b/t/lib/ddclient/t/HTTPD.pm @@ -16,7 +16,7 @@ our @EXPORT = qw( httpd httpd_ipv6_ok httpd_ipv6_required $httpd_ipv6_supported $httpd_ipv6_support_error httpd_ssl_ok httpd_ssl_required $httpd_ssl_supported $httpd_ssl_support_error - $ca_file $certdir + $ca_file $certdir $other_ca_file $textplain ); @@ -104,6 +104,7 @@ sub reset { our $certdir = "$ENV{abs_top_srcdir}/t/lib/ddclient/Test/Fake/HTTPD"; our $ca_file = "$certdir/dummy-ca-cert.pem"; +our $other_ca_file = "$certdir/other-ca-cert.pem" my %daemons; diff --git a/t/ssl-validate.pl b/t/ssl-validate.pl index 1414c02..ac5f58f 100644 --- a/t/ssl-validate.pl +++ b/t/ssl-validate.pl @@ -12,9 +12,6 @@ local $ddclient::globals{verbose} = 1; httpd_ssl_required(); -# Note: $ddclient::globals{'ssl_ca_file'} is intentionally NOT set to "$certdir/dummy-ca-cert.pem" -# so that we can test what happens when certificate validation fails. - httpd('4', 1)->run(sub { return [200, $textplain, ['127.0.0.1']]; }); httpd('6', 1)->run(sub { return [200, $textplain, ['::1']]; }) if httpd('6', 1); my $h = 't/ssl-validate.pl'; @@ -77,6 +74,16 @@ for my $tc (@test_cases) { SKIP: { skip("IPv6 not supported on this system", 1) if $tc->{ipv6} && !$ipv6_supported; skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported; + # $ddclient::globals{'ssl_ca_file'} is intentionally NOT set to $ca_file so that we can + # test what happens when certificate validation fails. However, if curl can't find any CA + # certificates (which may be the case in some minimal test environments, such as Docker + # images and Debian package builder chroots), it will immediately close the connection + # after it sends the TLS client hello and before it receives the server hello (in Debian + # sid as of 2025-01-08, anyway). This confuses IO::Socket::SSL (used by + # Test::Fake::HTTPD), causing it to hang in the middle of the TLS handshake waiting for + # input that will never arrive. To work around this, the CA certificate file is explicitly + # set to an unrelated certificate so that curl has something to read. + local $ddclient::globals{'ssl_ca_file'} = $other_ca_file; local $ddclient::config{$h} = $tc->{cfg}; %ddclient::config if 0; # suppress spurious warning "Name used only once: possible typo" is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc}) From ecaa05abd3ad9d649ba4a267fc136d006f325b3b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 18:13:36 -0500 Subject: [PATCH 18/25] tests: Delete vestigial code This deleted code came from a previous unpublished prior draft of the tests and was accidentally not deleted when the approach changed. --- t/protocol_dyndns2.pl | 1 - t/use_web.pl | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/t/protocol_dyndns2.pl b/t/protocol_dyndns2.pl index 682be57..8d0e985 100644 --- a/t/protocol_dyndns2.pl +++ b/t/protocol_dyndns2.pl @@ -1,7 +1,6 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } use MIME::Base64; -use Scalar::Util qw(blessed); BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } BEGIN { eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); diff --git a/t/use_web.pl b/t/use_web.pl index b129ecf..9411ae8 100644 --- a/t/use_web.pl +++ b/t/use_web.pl @@ -1,6 +1,5 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } -use Scalar::Util qw(blessed); BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } BEGIN { eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); @@ -70,16 +69,6 @@ for my $ipv ('4', '6') { } for my $tc (@test_cases) { - my $subst = sub { - return map({ - my $class = blessed($_); - (defined($class) && $class->isa('EndpointPlaceholder')) ? do { - my $uri = ${$_}->clone(); - $uri->query_param(tc => $tc->{desc}); - $uri; - } : $_; - } @_); - }; local $ddclient::builtinweb{$builtinweb} = $tc->{biw}; $ddclient::builtinweb if 0; local $ddclient::config{$h} = $tc->{cfg}; From ddeaedc136e9bf75dba2b73e21a15a5735f95a61 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 16:46:48 -0500 Subject: [PATCH 19/25] tests: Add missing semicolon This should have been in commit 06c47695fcd1873c5e7b874cf4fee3da126126f0. The tests that use this module did not fail because an import failure is assumed to be caused by a missing dependency, not a genuine bug. --- t/lib/ddclient/t/HTTPD.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/lib/ddclient/t/HTTPD.pm b/t/lib/ddclient/t/HTTPD.pm index 94ab271..6879372 100644 --- a/t/lib/ddclient/t/HTTPD.pm +++ b/t/lib/ddclient/t/HTTPD.pm @@ -104,7 +104,7 @@ sub reset { our $certdir = "$ENV{abs_top_srcdir}/t/lib/ddclient/Test/Fake/HTTPD"; our $ca_file = "$certdir/dummy-ca-cert.pem"; -our $other_ca_file = "$certdir/other-ca-cert.pem" +our $other_ca_file = "$certdir/other-ca-cert.pem"; my %daemons; From 8cf322e1628723c4ad15c378d68dfe52d44729c4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 18:03:17 -0500 Subject: [PATCH 20/25] tests: Only skip HTTPD tests if dependencies are unavailable This prevents the tests from passing due to syntax errors in the ddclient::t::HTTPD module. --- configure.ac | 3 ++- t/geturl_connectivity.pl | 7 +++---- t/lib/ddclient/t/HTTPD.pm | 35 ++++++++++++++++++++++++++++++----- t/protocol_directnic.pl | 7 +++---- t/protocol_dnsexit2.pl | 7 +++---- t/protocol_dyndns2.pl | 7 +++---- t/skip.pl | 7 +++---- t/ssl-validate.pl | 6 ++---- t/update_nics.pl | 7 +++---- t/use_web.pl | 7 +++---- 10 files changed, 55 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index 49daf65..4ccecc8 100644 --- a/configure.ac +++ b/configure.ac @@ -87,6 +87,7 @@ m4_foreach_w([_m], [ # then some tests will fail. Only prints a warning if not installed. m4_foreach_w([_m], [ B + Exporter File::Spec::Functions File::Temp List::Util @@ -100,7 +101,6 @@ m4_foreach_w([_m], [ # prints a warning if not installed. m4_foreach_w([_m], [ Carp - Exporter HTTP::Daemon=6.12 HTTP::Daemon::SSL HTTP::Message::PSGI @@ -112,6 +112,7 @@ m4_foreach_w([_m], [ Test::Warnings Time::HiRes URI + parent ], [AX_PROG_PERL_MODULES([_m], [], [AC_MSG_WARN([some tests may be skipped due to missing module _m])])]) diff --git a/t/geturl_connectivity.pl b/t/geturl_connectivity.pl index b0dd94d..d3f2033 100644 --- a/t/geturl_connectivity.pl +++ b/t/geturl_connectivity.pl @@ -1,12 +1,11 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; use ddclient::t::ip; +httpd_required(); + $ddclient::globals{'ssl_ca_file'} = $ca_file; for my $ipv ('4', '6') { diff --git a/t/lib/ddclient/t/HTTPD.pm b/t/lib/ddclient/t/HTTPD.pm index 6879372..997e451 100644 --- a/t/lib/ddclient/t/HTTPD.pm +++ b/t/lib/ddclient/t/HTTPD.pm @@ -7,21 +7,42 @@ use warnings; use parent qw(ddclient::Test::Fake::HTTPD); use Exporter qw(import); -use JSON::PP; use Test::More; BEGIN { require 'ddclient'; } use ddclient::t::ip; our @EXPORT = qw( httpd + httpd_ok httpd_required $httpd_supported $httpd_support_error httpd_ipv6_ok httpd_ipv6_required $httpd_ipv6_supported $httpd_ipv6_support_error httpd_ssl_ok httpd_ssl_required $httpd_ssl_supported $httpd_ssl_support_error $ca_file $certdir $other_ca_file $textplain ); -our $httpd_ssl_support_error; -our $httpd_ssl_supported = eval { require HTTP::Daemon::SSL; 1; } or $httpd_ssl_support_error = $@; +our $httpd_supported; +our $httpd_support_error; +BEGIN { + $httpd_supported = eval { + require parent; parent->import(qw(ddclient::Test::Fake::HTTPD)); + require JSON::PP; JSON::PP->import(); + 1; + } or $httpd_support_error = $@; +} + +sub httpd_ok { + ok($httpd_supported, "HTTPD is supported") or diag($httpd_support_error); +} + +sub httpd_required { + plan(skip_all => $httpd_support_error) if !$httpd_supported; +} + +our $httpd_ssl_supported = $httpd_supported; +our $httpd_ssl_support_error = $httpd_support_error; +$httpd_ssl_supported = eval { require HTTP::Daemon::SSL; 1; } + or $httpd_ssl_support_error = $@ + if $httpd_ssl_supported; sub httpd_ssl_ok { ok($httpd_ssl_supported, "SSL is supported") or diag($httpd_ssl_support_error); @@ -31,8 +52,11 @@ sub httpd_ssl_required { plan(skip_all => $httpd_ssl_support_error) if !$httpd_ssl_supported; } -our $httpd_ipv6_support_error; -our $httpd_ipv6_supported = $ipv6_supported or $httpd_ipv6_support_error = $ipv6_support_error; +our $httpd_ipv6_supported = $httpd_supported; +our $httpd_ipv6_support_error = $httpd_support_error; +$httpd_ipv6_supported = $ipv6_supported + or $httpd_ipv6_support_error = $ipv6_support_error + if $httpd_ipv6_supported; $httpd_ipv6_supported = eval { require HTTP::Daemon; HTTP::Daemon->VERSION(6.12); } or $httpd_ipv6_support_error = $@ if $httpd_ipv6_supported; @@ -112,6 +136,7 @@ sub httpd { my ($ipv, $ssl) = @_; $ipv //= ''; $ssl = !!$ssl; + return undef if !$httpd_supported; return undef if $ipv eq '6' && !$httpd_ipv6_supported; return undef if $ssl && !$httpd_ssl_supported; if (!defined($daemons{$ipv}{$ssl})) { diff --git a/t/protocol_directnic.pl b/t/protocol_directnic.pl index 30be5d5..92b245a 100644 --- a/t/protocol_directnic.pl +++ b/t/protocol_directnic.pl @@ -2,10 +2,9 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import(); } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; + +httpd_required(); ddclient::load_json_support('directnic'); diff --git a/t/protocol_dnsexit2.pl b/t/protocol_dnsexit2.pl index 0586276..7b78fac 100644 --- a/t/protocol_dnsexit2.pl +++ b/t/protocol_dnsexit2.pl @@ -2,10 +2,9 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import(); } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; + +httpd_required(); ddclient::load_json_support('dnsexit2'); diff --git a/t/protocol_dyndns2.pl b/t/protocol_dyndns2.pl index 8d0e985..b3130e0 100644 --- a/t/protocol_dyndns2.pl +++ b/t/protocol_dyndns2.pl @@ -2,10 +2,9 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } use MIME::Base64; BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; + +httpd_required(); httpd()->run(sub { my ($req) = @_; diff --git a/t/skip.pl b/t/skip.pl index 1ee68a3..3f0ba3a 100644 --- a/t/skip.pl +++ b/t/skip.pl @@ -1,12 +1,11 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; use ddclient::t::ip; +httpd_required(); + httpd('4')->run( sub { return [200, ['Content-Type' => 'text/plain'], ['127.0.0.1 skip 127.0.0.2']]; }); httpd('6')->run( diff --git a/t/ssl-validate.pl b/t/ssl-validate.pl index ac5f58f..6bea9a3 100644 --- a/t/ssl-validate.pl +++ b/t/ssl-validate.pl @@ -1,15 +1,13 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; use ddclient::t::ip; local $ddclient::globals{debug} = 1; local $ddclient::globals{verbose} = 1; +httpd_required(); httpd_ssl_required(); httpd('4', 1)->run(sub { return [200, $textplain, ['127.0.0.1']]; }); diff --git a/t/update_nics.pl b/t/update_nics.pl index d656c0f..e0fe679 100644 --- a/t/update_nics.pl +++ b/t/update_nics.pl @@ -6,12 +6,11 @@ BEGIN { eval { require JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import( use List::Util qw(max); use Scalar::Util qw(refaddr); BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; use ddclient::t::ip; +httpd_required(); + httpd('4')->run(); httpd('6')->run() if httpd('6'); local %ddclient::builtinweb = ( diff --git a/t/use_web.pl b/t/use_web.pl index 9411ae8..130034a 100644 --- a/t/use_web.pl +++ b/t/use_web.pl @@ -1,12 +1,11 @@ use Test::More; BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } -BEGIN { - eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@); - ddclient::t::HTTPD->import(); -} +use ddclient::t::HTTPD; use ddclient::t::ip; +httpd_required(); + my $builtinweb = 't/use_web.pl builtinweb'; my $h = 't/use_web.pl hostname'; From 8883641d97a2b0b3424022cd06c04ada05eb3495 Mon Sep 17 00:00:00 2001 From: Bodo Eggert <7eggert@gmx.de> Date: Sun, 17 Nov 2024 00:14:25 +0100 Subject: [PATCH 21/25] fix ddclient --verbose calling 'p' instead of using $p{foo} --- ddclient.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 861abe5..6cc5e71 100755 --- a/ddclient.in +++ b/ddclient.in @@ -3454,7 +3454,7 @@ sub get_ipv6 { $ipv6 = get_ip_from_interface($arg, 6); } elsif ($p{'usev6'} eq 'cmdv6' || $p{'usev6'} eq 'cmd') { ## Obtain IPv6 address by executing the command in "cmdv6=" - warning("'--cmd-skip' ignored") if opt('verbose') && p{'cmd-skip'}; + warning("'--cmd-skip' ignored") if opt('verbose') && $p{'cmd-skip'}; if ($arg) { my $sys_cmd = quotemeta($arg); $reply = qx{$sys_cmd}; From 8dcea0d7799470a69881064026d2785ee5e68e4d Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Fri, 27 Dec 2024 21:50:47 -0600 Subject: [PATCH 22/25] Make 'cmd-skip' warning message consistent for IPv4 and IPv6 --- ddclient.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 6cc5e71..5047eb9 100755 --- a/ddclient.in +++ b/ddclient.in @@ -3341,7 +3341,7 @@ sub get_ipv4 { } elsif ($p{'usev4'} eq 'cmdv4') { ## Obtain IPv4 address by executing the command in "cmdv4=" warning("'--cmd-skip' ignored for '--usev4=$p{'usev4'}'") - if (opt('verbose') && $p{'cmd-skip'}); + if opt('verbose') && defined($p{'cmd-skip'}); if ($arg) { my $sys_cmd = quotemeta($arg); $reply = qx{$sys_cmd}; @@ -3454,7 +3454,8 @@ sub get_ipv6 { $ipv6 = get_ip_from_interface($arg, 6); } elsif ($p{'usev6'} eq 'cmdv6' || $p{'usev6'} eq 'cmd') { ## Obtain IPv6 address by executing the command in "cmdv6=" - warning("'--cmd-skip' ignored") if opt('verbose') && $p{'cmd-skip'}; + warning("'--cmd-skip' ignored for '--usev6=$p{'usev6'}'") + if opt('verbose') && defined($p{'cmd-skip'}); if ($arg) { my $sys_cmd = quotemeta($arg); $reply = qx{$sys_cmd}; From ae01ba26c12ce7ad7f0a4edc8139c5b5b96f7f96 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 26 Nov 2024 17:35:23 +0000 Subject: [PATCH 23/25] Do not use `quotemeta` on `cmdv4` and `cmdv6` arguments `quotemeta` prevents executing commands with arguments. With this change, it is now possible to get an IP address with: usev4=cmdv4 cmdv4="dig +short myip.opendns.com @resolver1.opendns.com" --- ddclient.in | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ddclient.in b/ddclient.in index 5047eb9..c91d100 100755 --- a/ddclient.in +++ b/ddclient.in @@ -3343,8 +3343,7 @@ sub get_ipv4 { warning("'--cmd-skip' ignored for '--usev4=$p{'usev4'}'") if opt('verbose') && defined($p{'cmd-skip'}); if ($arg) { - my $sys_cmd = quotemeta($arg); - $reply = qx{$sys_cmd}; + $reply = qx{$arg}; $reply = '' if $?; } } elsif ($p{'usev4'} eq 'webv4') { @@ -3457,8 +3456,7 @@ sub get_ipv6 { warning("'--cmd-skip' ignored for '--usev6=$p{'usev6'}'") if opt('verbose') && defined($p{'cmd-skip'}); if ($arg) { - my $sys_cmd = quotemeta($arg); - $reply = qx{$sys_cmd}; + $reply = qx{$arg}; $reply = '' if $?; } } elsif ($p{'usev6'} eq 'webv6' || $p{'usev6'} eq 'web') { From 1c178d4c09b0b053502432ff4dc7ee587da6e1f4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 18:18:09 -0500 Subject: [PATCH 24/25] Add ChangeLog entry for `--cmdv4`, `--cmdv6` change --- ChangeLog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 817b5cf..579f686 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,17 @@ repository history](https://github.com/ddclient/ddclient/commits/main). ## v4.0.0-rc.3 (unreleased work-in-progress) +### Breaking changes + + * The string argument to `--cmdv4` or `--cmdv6` is now executed as-is by the + system's shell, matching the behavior of the deprecated `--cmd` option. + This makes it possible to pass command-line arguments, which reduces the + need for a custom wrapper script. Beware that the string is also subject to + the shell's command substitution, quote handling, variable expansion, field + splitting, etc., so you may need to add extra escaping to ensure that any + special characters are preserved literally. + [#766](https://github.com/ddclient/ddclient/pull/766) + ## 2025-01-07 v4.0.0-rc.2 ### Breaking changes From 6fd9a6f106ac88386ed02942c58ad8a87aaf7401 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 8 Jan 2025 19:04:59 -0500 Subject: [PATCH 25/25] tests: Add some tests for `use=cmd`, `usev4=cmdv4`, `usev6=cmdv6` --- Makefile.am | 1 + t/use_cmd.pl | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 t/use_cmd.pl diff --git a/Makefile.am b/Makefile.am index 2124315..6a0f32a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -77,6 +77,7 @@ handwritten_tests = \ t/skip.pl \ t/ssl-validate.pl \ t/update_nics.pl \ + t/use_cmd.pl \ t/use_web.pl \ t/variable_defaults.pl \ t/write_recap.pl diff --git a/t/use_cmd.pl b/t/use_cmd.pl new file mode 100644 index 0000000..1391da9 --- /dev/null +++ b/t/use_cmd.pl @@ -0,0 +1,41 @@ +use Test::More; +BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } } +BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); } + +local $ddclient::globals{debug} = 1; +local $ddclient::globals{verbose} = 1; + +my @test_cases; +for my $ipv ('4', '6') { + my $ip = $ipv eq '4' ? '192.0.2.1' : '2001:db8::1'; + for my $use ('use', "usev$ipv") { + my @cmds = (); + push(@cmds, 'cmd') if $use eq 'use' || $ipv eq '6'; + push(@cmds, "cmdv$ipv") if $use ne 'use'; + for my $cmd (@cmds) { + my $cmdarg = "echo '$ip'"; + push( + @test_cases, + { + desc => "$use=$cmd $cmd=\"$cmdarg\"", + cfg => {$use => $cmd, $cmd => $cmdarg}, + want => $ip, + }, + ); + } + } +} + +for my $tc (@test_cases) { + local $ddclient::_l = ddclient::pushlogctx($tc->{desc}); + my $h = 'test-host'; + local $ddclient::config{$h} = $tc->{cfg}; + is(ddclient::get_ip(ddclient::strategy_inputs('use', $h)), $tc->{want}, $tc->{desc}) + if $tc->{cfg}{use}; + is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc}) + if $tc->{cfg}{usev4}; + is(ddclient::get_ipv6(ddclient::strategy_inputs('usev6', $h)), $tc->{want}, $tc->{desc}) + if $tc->{cfg}{usev6}; +} + +done_testing();