From ae01ba26c12ce7ad7f0a4edc8139c5b5b96f7f96 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 26 Nov 2024 17:35:23 +0000 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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();