Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
6fa3c98496
18 changed files with 290 additions and 69 deletions
18
.github/workflows/pr.yml
vendored
18
.github/workflows/pr.yml
vendored
|
@ -29,3 +29,21 @@ jobs:
|
||||||
git show "${out}" >&2
|
git show "${out}" >&2
|
||||||
exit 1
|
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)!' <<EOF || fatal "--autosquash commits not allowed without the 'pr-permit-autosquash' label"
|
||||||
|
${out}
|
||||||
|
EOF
|
||||||
|
|
45
ChangeLog.md
45
ChangeLog.md
|
@ -3,6 +3,51 @@
|
||||||
This document describes notable changes. For details, see the [source code
|
This document describes notable changes. For details, see the [source code
|
||||||
repository history](https://github.com/ddclient/ddclient/commits/main).
|
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
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
[#565](https://github.com/ddclient/ddclient/pull/565)
|
||||||
|
|
||||||
## 2024-12-25 v4.0.0-rc.1
|
## 2024-12-25 v4.0.0-rc.1
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
|
@ -28,8 +28,8 @@ $(subst_files): Makefile
|
||||||
-e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \
|
-e 's|@PACKAGE_VERSION[@]|$(PACKAGE_VERSION)|g' \
|
||||||
-e '1 s|^#\!.*perl$$|#\!$(PERL)|g' \
|
-e '1 s|^#\!.*perl$$|#\!$(PERL)|g' \
|
||||||
-e 's|@localstatedir[@]|$(localstatedir)|g' \
|
-e 's|@localstatedir[@]|$(localstatedir)|g' \
|
||||||
|
-e 's|@confdir[@]|$(confdir)|g' \
|
||||||
-e 's|@runstatedir[@]|$(runstatedir)|g' \
|
-e 's|@runstatedir[@]|$(runstatedir)|g' \
|
||||||
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
|
|
||||||
-e 's|@CURL[@]|$(CURL)|g' \
|
-e 's|@CURL[@]|$(CURL)|g' \
|
||||||
"$${in}" >'$@'.tmp && \
|
"$${in}" >'$@'.tmp && \
|
||||||
{ ! test -x "$${in}" || chmod +x '$@'.tmp; }
|
{ ! test -x "$${in}" || chmod +x '$@'.tmp; }
|
||||||
|
@ -40,7 +40,7 @@ ddclient.conf: $(srcdir)/ddclient.conf.in
|
||||||
|
|
||||||
bin_SCRIPTS = ddclient
|
bin_SCRIPTS = ddclient
|
||||||
|
|
||||||
sysconf_DATA = ddclient.conf
|
conf_DATA = ddclient.conf
|
||||||
|
|
||||||
install-data-local:
|
install-data-local:
|
||||||
$(MKDIR_P) '$(DESTDIR)$(localstatedir)'/cache/ddclient
|
$(MKDIR_P) '$(DESTDIR)$(localstatedir)'/cache/ddclient
|
||||||
|
@ -77,6 +77,7 @@ handwritten_tests = \
|
||||||
t/skip.pl \
|
t/skip.pl \
|
||||||
t/ssl-validate.pl \
|
t/ssl-validate.pl \
|
||||||
t/update_nics.pl \
|
t/update_nics.pl \
|
||||||
|
t/use_cmd.pl \
|
||||||
t/use_web.pl \
|
t/use_web.pl \
|
||||||
t/variable_defaults.pl \
|
t/variable_defaults.pl \
|
||||||
t/write_recap.pl
|
t/write_recap.pl
|
||||||
|
@ -156,6 +157,7 @@ EXTRA_DIST += $(handwritten_tests) \
|
||||||
t/lib/ddclient/Test/Fake/HTTPD/dummy-ca-cert.pem \
|
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-cert.pem \
|
||||||
t/lib/ddclient/Test/Fake/HTTPD/dummy-server-key.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.pm \
|
||||||
t/lib/ddclient/t/HTTPD.pm \
|
t/lib/ddclient/t/HTTPD.pm \
|
||||||
t/lib/ddclient/t/ip.pm \
|
t/lib/ddclient/t/ip.pm \
|
||||||
|
|
|
@ -106,7 +106,7 @@ operating system. See the image to the right for a list of distributions with a
|
||||||
```shell
|
```shell
|
||||||
./configure \
|
./configure \
|
||||||
--prefix=/usr \
|
--prefix=/usr \
|
||||||
--sysconfdir=/etc/ddclient \
|
--sysconfdir=/etc \
|
||||||
--localstatedir=/var
|
--localstatedir=/var
|
||||||
make
|
make
|
||||||
make VERBOSE=1 check
|
make VERBOSE=1 check
|
||||||
|
|
15
configure.ac
15
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_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects parallel-tests])
|
||||||
AM_SILENT_RULES
|
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
|
AC_PROG_MKDIR_P
|
||||||
|
|
||||||
# The Fedora Docker image doesn't come with the 'findutils' package.
|
# The Fedora Docker image doesn't come with the 'findutils' package.
|
||||||
|
@ -75,6 +87,7 @@ m4_foreach_w([_m], [
|
||||||
# then some tests will fail. Only prints a warning if not installed.
|
# then some tests will fail. Only prints a warning if not installed.
|
||||||
m4_foreach_w([_m], [
|
m4_foreach_w([_m], [
|
||||||
B
|
B
|
||||||
|
Exporter
|
||||||
File::Spec::Functions
|
File::Spec::Functions
|
||||||
File::Temp
|
File::Temp
|
||||||
List::Util
|
List::Util
|
||||||
|
@ -88,7 +101,6 @@ m4_foreach_w([_m], [
|
||||||
# prints a warning if not installed.
|
# prints a warning if not installed.
|
||||||
m4_foreach_w([_m], [
|
m4_foreach_w([_m], [
|
||||||
Carp
|
Carp
|
||||||
Exporter
|
|
||||||
HTTP::Daemon=6.12
|
HTTP::Daemon=6.12
|
||||||
HTTP::Daemon::SSL
|
HTTP::Daemon::SSL
|
||||||
HTTP::Message::PSGI
|
HTTP::Message::PSGI
|
||||||
|
@ -100,6 +112,7 @@ m4_foreach_w([_m], [
|
||||||
Test::Warnings
|
Test::Warnings
|
||||||
Time::HiRes
|
Time::HiRes
|
||||||
URI
|
URI
|
||||||
|
parent
|
||||||
], [AX_PROG_PERL_MODULES([_m], [],
|
], [AX_PROG_PERL_MODULES([_m], [],
|
||||||
[AC_MSG_WARN([some tests may be skipped due to missing module _m])])])
|
[AC_MSG_WARN([some tests may be skipped due to missing module _m])])])
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ daemon=300 # check every 300 seconds
|
||||||
syslog=yes # log update msgs to syslog
|
syslog=yes # log update msgs to syslog
|
||||||
mail=root # mail all msgs to root
|
mail=root # mail all msgs to root
|
||||||
mail-failure=root # mail failed update msgs to root
|
mail-failure=root # mail failed update msgs to root
|
||||||
|
# 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.
|
pid=@runstatedir@/ddclient.pid # record PID in file.
|
||||||
# postscript=script # run script after updating. The new IP is
|
# postscript=script # run script after updating. The new IP is
|
||||||
# added as argument.
|
# added as argument.
|
||||||
|
@ -130,10 +133,10 @@ pid=@runstatedir@/ddclient.pid # record PID in file.
|
||||||
##
|
##
|
||||||
## NearlyFreeSpeech.NET (nearlyfreespeech.net)
|
## NearlyFreeSpeech.NET (nearlyfreespeech.net)
|
||||||
##
|
##
|
||||||
# protocol = nfsn, \
|
# protocol=nfsn, \
|
||||||
|
# zone=example.com, \
|
||||||
# login=member-login, \
|
# login=member-login, \
|
||||||
# password=api-key, \
|
# password=api-key \
|
||||||
# zone=example.com \
|
|
||||||
# example.com,subdomain.example.com
|
# example.com,subdomain.example.com
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
19
ddclient.in
19
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
|
# For consistency and to match user expectations, the release part of the version is always three
|
||||||
# components: MAJOR.MINOR.PATCH.
|
# 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_903');
|
||||||
|
|
||||||
sub parse_version {
|
sub parse_version {
|
||||||
my ($v) = @_;
|
my ($v) = @_;
|
||||||
|
@ -132,7 +132,7 @@ sub subst_var {
|
||||||
return $subst;
|
return $subst;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $etc = subst_var('@sysconfdir@', '/etc/ddclient');
|
my $etc = subst_var('@confdir@', '/etc/ddclient');
|
||||||
my $cachedir = subst_var('@localstatedir@', '/var') . '/cache/ddclient';
|
my $cachedir = subst_var('@localstatedir@', '/var') . '/cache/ddclient';
|
||||||
our @curl = (subst_var('@CURL@', 'curl'));
|
our @curl = (subst_var('@CURL@', 'curl'));
|
||||||
|
|
||||||
|
@ -704,6 +704,7 @@ our %cfgvars = (
|
||||||
'priority' => setv(T_STRING,0, 'notice', undef),
|
'priority' => setv(T_STRING,0, 'notice', undef),
|
||||||
'mail' => setv(T_EMAIL, 0, undef, undef),
|
'mail' => setv(T_EMAIL, 0, undef, undef),
|
||||||
'mail-failure' => setv(T_EMAIL, 0, undef, undef),
|
'mail-failure' => setv(T_EMAIL, 0, undef, undef),
|
||||||
|
'mail-from' => setv(T_EMAIL, 0, undef, undef),
|
||||||
'max-warn' => setv(T_NUMBER,0, 1, undef),
|
'max-warn' => setv(T_NUMBER,0, 1, undef),
|
||||||
|
|
||||||
'exec' => setv(T_BOOL, 0, 1, undef),
|
'exec' => setv(T_BOOL, 0, 1, undef),
|
||||||
|
@ -1436,6 +1437,7 @@ my @opt = (
|
||||||
["max-warn", "=i", "--max-warn=<max> : log at most <max> warning messages for undefined IP address"],
|
["max-warn", "=i", "--max-warn=<max> : log at most <max> warning messages for undefined IP address"],
|
||||||
["mail", "=s", "--mail=<address> : e-mail messages to <address>"],
|
["mail", "=s", "--mail=<address> : e-mail messages to <address>"],
|
||||||
["mail-failure", "=s", "--mail-failure=<addr> : e-mail messages for failed updates to <addr>"],
|
["mail-failure", "=s", "--mail-failure=<addr> : e-mail messages for failed updates to <addr>"],
|
||||||
|
["mail-from", "=s", '--mail-from=<addr> : set the "From:" header in e-mail messages to <addr> if non-empty'],
|
||||||
["exec", "!", "--{no}exec : do {not} execute; just show what would be done"],
|
["exec", "!", "--{no}exec : do {not} execute; just show what would be done"],
|
||||||
["debug", "!", "--{no}debug : print {no} debugging information"],
|
["debug", "!", "--{no}debug : print {no} debugging information"],
|
||||||
["verbose", "!", "--{no}verbose : print {no} verbose information"],
|
["verbose", "!", "--{no}verbose : print {no} verbose information"],
|
||||||
|
@ -2413,8 +2415,10 @@ sub sendmail {
|
||||||
$recipients = opt('mail-failure');
|
$recipients = opt('mail-failure');
|
||||||
}
|
}
|
||||||
if ($emailbody && $recipients && $emailbody ne $last_emailbody) {
|
if ($emailbody && $recipients && $emailbody ne $last_emailbody) {
|
||||||
|
my $sender = opt('mail-from') // '';
|
||||||
pipecmd("sendmail -oi $recipients",
|
pipecmd("sendmail -oi $recipients",
|
||||||
"To: $recipients",
|
"To: $recipients",
|
||||||
|
$sender ne '' ? ("From: $sender") : (),
|
||||||
"Subject: status report from $program\@$hostname",
|
"Subject: status report from $program\@$hostname",
|
||||||
"\r\n",
|
"\r\n",
|
||||||
$emailbody,
|
$emailbody,
|
||||||
|
@ -3346,10 +3350,9 @@ sub get_ipv4 {
|
||||||
} elsif ($p{'usev4'} eq 'cmdv4') {
|
} elsif ($p{'usev4'} eq 'cmdv4') {
|
||||||
## Obtain IPv4 address by executing the command in "cmdv4=<command>"
|
## Obtain IPv4 address by executing the command in "cmdv4=<command>"
|
||||||
warning("'--cmd-skip' ignored for '--usev4=$p{'usev4'}'")
|
warning("'--cmd-skip' ignored for '--usev4=$p{'usev4'}'")
|
||||||
if (opt('verbose') && $p{'cmd-skip'});
|
if opt('verbose') && defined($p{'cmd-skip'});
|
||||||
if ($arg) {
|
if ($arg) {
|
||||||
my $sys_cmd = quotemeta($arg);
|
$reply = qx{$arg};
|
||||||
$reply = qx{$sys_cmd};
|
|
||||||
$reply = '' if $?;
|
$reply = '' if $?;
|
||||||
}
|
}
|
||||||
} elsif ($p{'usev4'} eq 'webv4') {
|
} elsif ($p{'usev4'} eq 'webv4') {
|
||||||
|
@ -3459,10 +3462,10 @@ sub get_ipv6 {
|
||||||
$ipv6 = get_ip_from_interface($arg, 6);
|
$ipv6 = get_ip_from_interface($arg, 6);
|
||||||
} elsif ($p{'usev6'} eq 'cmdv6' || $p{'usev6'} eq 'cmd') {
|
} elsif ($p{'usev6'} eq 'cmdv6' || $p{'usev6'} eq 'cmd') {
|
||||||
## Obtain IPv6 address by executing the command in "cmdv6=<command>"
|
## Obtain IPv6 address by executing the command in "cmdv6=<command>"
|
||||||
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) {
|
if ($arg) {
|
||||||
my $sys_cmd = quotemeta($arg);
|
$reply = qx{$arg};
|
||||||
$reply = qx{$sys_cmd};
|
|
||||||
$reply = '' if $?;
|
$reply = '' if $?;
|
||||||
}
|
}
|
||||||
} elsif ($p{'usev6'} eq 'webv6' || $p{'usev6'} eq 'web') {
|
} elsif ($p{'usev6'} eq 'webv6' || $p{'usev6'} eq 'web') {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
|
||||||
}
|
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
|
httpd_required();
|
||||||
|
|
||||||
$ddclient::globals{'ssl_ca_file'} = $ca_file;
|
$ddclient::globals{'ssl_ca_file'} = $ca_file;
|
||||||
|
|
||||||
for my $ipv ('4', '6') {
|
for my $ipv ('4', '6') {
|
||||||
|
|
80
t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem
Normal file
80
t/lib/ddclient/Test/Fake/HTTPD/other-ca-cert.pem
Normal file
|
@ -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-----
|
|
@ -7,21 +7,42 @@ use warnings;
|
||||||
use parent qw(ddclient::Test::Fake::HTTPD);
|
use parent qw(ddclient::Test::Fake::HTTPD);
|
||||||
|
|
||||||
use Exporter qw(import);
|
use Exporter qw(import);
|
||||||
use JSON::PP;
|
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { require 'ddclient'; }
|
BEGIN { require 'ddclient'; }
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
our @EXPORT = qw(
|
our @EXPORT = qw(
|
||||||
httpd
|
httpd
|
||||||
|
httpd_ok httpd_required $httpd_supported $httpd_support_error
|
||||||
httpd_ipv6_ok httpd_ipv6_required $httpd_ipv6_supported $httpd_ipv6_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
|
httpd_ssl_ok httpd_ssl_required $httpd_ssl_supported $httpd_ssl_support_error
|
||||||
$ca_file $certdir
|
$ca_file $certdir $other_ca_file
|
||||||
$textplain
|
$textplain
|
||||||
);
|
);
|
||||||
|
|
||||||
our $httpd_ssl_support_error;
|
our $httpd_supported;
|
||||||
our $httpd_ssl_supported = eval { require HTTP::Daemon::SSL; 1; } or $httpd_ssl_support_error = $@;
|
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 {
|
sub httpd_ssl_ok {
|
||||||
ok($httpd_ssl_supported, "SSL is supported") or diag($httpd_ssl_support_error);
|
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;
|
plan(skip_all => $httpd_ssl_support_error) if !$httpd_ssl_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
our $httpd_ipv6_support_error;
|
our $httpd_ipv6_supported = $httpd_supported;
|
||||||
our $httpd_ipv6_supported = $ipv6_supported or $httpd_ipv6_support_error = $ipv6_support_error;
|
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); }
|
$httpd_ipv6_supported = eval { require HTTP::Daemon; HTTP::Daemon->VERSION(6.12); }
|
||||||
or $httpd_ipv6_support_error = $@
|
or $httpd_ipv6_support_error = $@
|
||||||
if $httpd_ipv6_supported;
|
if $httpd_ipv6_supported;
|
||||||
|
@ -104,6 +128,7 @@ sub reset {
|
||||||
|
|
||||||
our $certdir = "$ENV{abs_top_srcdir}/t/lib/ddclient/Test/Fake/HTTPD";
|
our $certdir = "$ENV{abs_top_srcdir}/t/lib/ddclient/Test/Fake/HTTPD";
|
||||||
our $ca_file = "$certdir/dummy-ca-cert.pem";
|
our $ca_file = "$certdir/dummy-ca-cert.pem";
|
||||||
|
our $other_ca_file = "$certdir/other-ca-cert.pem";
|
||||||
|
|
||||||
my %daemons;
|
my %daemons;
|
||||||
|
|
||||||
|
@ -111,6 +136,7 @@ sub httpd {
|
||||||
my ($ipv, $ssl) = @_;
|
my ($ipv, $ssl) = @_;
|
||||||
$ipv //= '';
|
$ipv //= '';
|
||||||
$ssl = !!$ssl;
|
$ssl = !!$ssl;
|
||||||
|
return undef if !$httpd_supported;
|
||||||
return undef if $ipv eq '6' && !$httpd_ipv6_supported;
|
return undef if $ipv eq '6' && !$httpd_ipv6_supported;
|
||||||
return undef if $ssl && !$httpd_ssl_supported;
|
return undef if $ssl && !$httpd_ssl_supported;
|
||||||
if (!defined($daemons{$ipv}{$ssl})) {
|
if (!defined($daemons{$ipv}{$ssl})) {
|
||||||
|
|
|
@ -2,10 +2,9 @@ use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
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 JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import(); }
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
httpd_required();
|
||||||
}
|
|
||||||
|
|
||||||
ddclient::load_json_support('directnic');
|
ddclient::load_json_support('directnic');
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,9 @@ use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
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 JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import(); }
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
httpd_required();
|
||||||
}
|
|
||||||
|
|
||||||
ddclient::load_json_support('dnsexit2');
|
ddclient::load_json_support('dnsexit2');
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
||||||
use MIME::Base64;
|
use MIME::Base64;
|
||||||
use Scalar::Util qw(blessed);
|
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
httpd_required();
|
||||||
}
|
|
||||||
|
|
||||||
httpd()->run(sub {
|
httpd()->run(sub {
|
||||||
my ($req) = @_;
|
my ($req) = @_;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
|
||||||
}
|
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
|
httpd_required();
|
||||||
|
|
||||||
httpd('4')->run(
|
httpd('4')->run(
|
||||||
sub { return [200, ['Content-Type' => 'text/plain'], ['127.0.0.1 skip 127.0.0.2']]; });
|
sub { return [200, ['Content-Type' => 'text/plain'], ['127.0.0.1 skip 127.0.0.2']]; });
|
||||||
httpd('6')->run(
|
httpd('6')->run(
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
|
||||||
}
|
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
httpd_ssl_required();
|
local $ddclient::globals{debug} = 1;
|
||||||
|
local $ddclient::globals{verbose} = 1;
|
||||||
|
|
||||||
# Note: $ddclient::globals{'ssl_ca_file'} is intentionally NOT set to "$certdir/dummy-ca-cert.pem"
|
httpd_required();
|
||||||
# so that we can test what happens when certificate validation fails.
|
httpd_ssl_required();
|
||||||
|
|
||||||
httpd('4', 1)->run(sub { return [200, $textplain, ['127.0.0.1']]; });
|
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);
|
httpd('6', 1)->run(sub { return [200, $textplain, ['::1']]; }) if httpd('6', 1);
|
||||||
|
@ -70,10 +68,21 @@ my @test_cases = (
|
||||||
);
|
);
|
||||||
|
|
||||||
for my $tc (@test_cases) {
|
for my $tc (@test_cases) {
|
||||||
|
local $ddclient::_l = ddclient::pushlogctx($tc->{desc});
|
||||||
SKIP: {
|
SKIP: {
|
||||||
skip("IPv6 not supported on this system", 1) if $tc->{ipv6} && !$ipv6_supported;
|
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;
|
skip("HTTP::Daemon too old for IPv6 support", 1) if $tc->{ipv6} && !$httpd_ipv6_supported;
|
||||||
$ddclient::config{$h} = $tc->{cfg};
|
# $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"
|
%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})
|
is(ddclient::get_ipv4(ddclient::strategy_inputs('usev4', $h)), $tc->{want}, $tc->{desc})
|
||||||
if ($tc->{cfg}{usev4});
|
if ($tc->{cfg}{usev4});
|
||||||
|
|
|
@ -6,12 +6,11 @@ BEGIN { eval { require JSON::PP; 1; } or plan(skip_all => $@); JSON::PP->import(
|
||||||
use List::Util qw(max);
|
use List::Util qw(max);
|
||||||
use Scalar::Util qw(refaddr);
|
use Scalar::Util qw(refaddr);
|
||||||
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
|
||||||
}
|
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
|
httpd_required();
|
||||||
|
|
||||||
httpd('4')->run();
|
httpd('4')->run();
|
||||||
httpd('6')->run() if httpd('6');
|
httpd('6')->run() if httpd('6');
|
||||||
local %ddclient::builtinweb = (
|
local %ddclient::builtinweb = (
|
||||||
|
|
41
t/use_cmd.pl
Normal file
41
t/use_cmd.pl
Normal file
|
@ -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();
|
18
t/use_web.pl
18
t/use_web.pl
|
@ -1,13 +1,11 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
BEGIN { SKIP: { eval { require Test::Warnings; 1; } or skip($@, 1); } }
|
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'; } or BAIL_OUT($@); }
|
||||||
BEGIN {
|
use ddclient::t::HTTPD;
|
||||||
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
|
||||||
ddclient::t::HTTPD->import();
|
|
||||||
}
|
|
||||||
use ddclient::t::ip;
|
use ddclient::t::ip;
|
||||||
|
|
||||||
|
httpd_required();
|
||||||
|
|
||||||
my $builtinweb = 't/use_web.pl builtinweb';
|
my $builtinweb = 't/use_web.pl builtinweb';
|
||||||
my $h = 't/use_web.pl hostname';
|
my $h = 't/use_web.pl hostname';
|
||||||
|
|
||||||
|
@ -70,16 +68,6 @@ for my $ipv ('4', '6') {
|
||||||
}
|
}
|
||||||
|
|
||||||
for my $tc (@test_cases) {
|
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};
|
local $ddclient::builtinweb{$builtinweb} = $tc->{biw};
|
||||||
$ddclient::builtinweb if 0;
|
$ddclient::builtinweb if 0;
|
||||||
local $ddclient::config{$h} = $tc->{cfg};
|
local $ddclient::config{$h} = $tc->{cfg};
|
||||||
|
|
Loading…
Reference in a new issue