ddclient/t/is-and-extract-ipv6.pl
2020-09-21 15:16:11 -04:00

441 lines
14 KiB
Perl

use Test::More;
use B qw(perlstring);
use ddclient::t;
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
eval { require 'ddclient'; } or BAIL_OUT($@);
my @valid_ipv6 = (
"::abcd:efAB:CDEF", # case sensitivity
"08:09:0a:0b:0c:0d:0e:0f", # leading zeros
# with thanks to http://home.deds.nl/~aeron/regex/valid_ipv6.txt
"1111:2222:3333:4444:5555:6666:7777:8888",
"1111:2222:3333:4444:5555:6666:7777::",
"1111:2222:3333:4444:5555:6666::",
"1111:2222:3333:4444:5555::",
"1111:2222:3333:4444::",
"1111:2222:3333::",
"1111:2222::",
"1111::",
"::",
"1111:2222:3333:4444:5555:6666::8888",
"1111:2222:3333:4444:5555::8888",
"1111:2222:3333:4444::8888",
"1111:2222:3333::8888",
"1111:2222::8888",
"1111::8888",
"::8888",
"1111:2222:3333:4444:5555::7777:8888",
"1111:2222:3333:4444::7777:8888",
"1111:2222:3333::7777:8888",
"1111:2222::7777:8888",
"1111::7777:8888",
"::7777:8888",
"1111:2222:3333:4444::6666:7777:8888",
"1111:2222:3333::6666:7777:8888",
"1111:2222::6666:7777:8888",
"1111::6666:7777:8888",
"::6666:7777:8888",
"1111:2222:3333::5555:6666:7777:8888",
"1111:2222::5555:6666:7777:8888",
"1111::5555:6666:7777:8888",
"::5555:6666:7777:8888",
"1111:2222::4444:5555:6666:7777:8888",
"1111::4444:5555:6666:7777:8888",
"::4444:5555:6666:7777:8888",
"1111::3333:4444:5555:6666:7777:8888",
"::3333:4444:5555:6666:7777:8888",
"::2222:3333:4444:5555:6666:7777:8888",
# IPv4-mapped IPv6 addresses
"1111:2222:3333:4444:5555:6666:0.0.0.0",
"1111:2222:3333:4444:5555:6666:00.00.00.00",
"1111:2222:3333:4444:5555:6666:000.000.000.000",
"1111:2222:3333:4444:5555:6666:123.123.123.123",
"1111:2222:3333:4444:5555::123.123.123.123",
"1111:2222:3333:4444::123.123.123.123",
"1111:2222:3333::123.123.123.123",
"1111:2222::123.123.123.123",
"1111::123.123.123.123",
"::123.123.123.123",
"1111:2222:3333:4444::6666:123.123.123.123",
"1111:2222:3333::6666:123.123.123.123",
"1111:2222::6666:123.123.123.123",
"1111::6666:123.123.123.123",
"::6666:123.123.123.123",
"1111:2222:3333::5555:6666:123.123.123.123",
"1111:2222::5555:6666:123.123.123.123",
"1111::5555:6666:123.123.123.123",
"::5555:6666:123.123.123.123",
"1111:2222::4444:5555:6666:123.123.123.123",
"1111::4444:5555:6666:123.123.123.123",
"::4444:5555:6666:123.123.123.123",
"1111::3333:4444:5555:6666:123.123.123.123",
"::3333:4444:5555:6666:123.123.123.123",
"::2222:3333:4444:5555:6666:123.123.123.123",
);
my @invalid_ipv6 = (
# Empty string and bogus text
undef,
"",
" ",
"foobar",
# Valid IPv6 with extra text before or after
"foo2001:DB8:4341:0781:1111:2222:3333:4444",
"foo 2001:DB8:4341:0781::4444",
"foo 2001:DB8:4341:0781:1111:: bar",
"foo2001:DB8:4341:0781::100bar",
"2001:DB8:4341:0781::1 bar",
"2001:DB8:4341:0781::0001bar",
"foo bar 3001:DB8:4341:0781:1111:2222:3333:4444 foo bar",
"__3001:DB8:4341:0781::4444",
"__3001:DB8:4341:0781:1111::__",
"--3001:DB8:4341:0781::100--",
"/3001:DB8:4341:0781::1/",
"3001:DB8:4341:0781::0001%",
"fdb6:1d86:d9bd:1::4444%eth0",
"fdb6:1d86:d9bd:1:1111::%ens192",
"fdb6:1d86:d9bd:1::100%en0",
"fdb6:1d86:d9bd:1::1%eth1.100",
# With thanks to http://home.deds.nl/~aeron/regex/invalid_ipv6.txt
# Invalid data
"XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX",
# Too many components
"1111:2222:3333:4444:5555:6666:7777:8888:9999",
"1111:2222:3333:4444:5555:6666:7777:8888::",
"::2222:3333:4444:5555:6666:7777:8888:9999",
# Too few components
"1111:2222:3333:4444:5555:6666:7777",
"1111:2222:3333:4444:5555:6666",
"1111:2222:3333:4444:5555",
"1111:2222:3333:4444",
"1111:2222:3333",
"1111:2222",
"1111",
# Missing :
"11112222:3333:4444:5555:6666:7777:8888",
"1111:22223333:4444:5555:6666:7777:8888",
"1111:2222:33334444:5555:6666:7777:8888",
"1111:2222:3333:44445555:6666:7777:8888",
"1111:2222:3333:4444:55556666:7777:8888",
"1111:2222:3333:4444:5555:66667777:8888",
"1111:2222:3333:4444:5555:6666:77778888",
# Missing : intended for ::
"1111:2222:3333:4444:5555:6666:7777:8888:",
"1111:2222:3333:4444:5555:6666:7777:",
"1111:2222:3333:4444:5555:6666:",
"1111:2222:3333:4444:5555:",
"1111:2222:3333:4444:",
"1111:2222:3333:",
"1111:2222:",
"1111:",
":",
":8888",
":7777:8888",
":6666:7777:8888",
":5555:6666:7777:8888",
":4444:5555:6666:7777:8888",
":3333:4444:5555:6666:7777:8888",
":2222:3333:4444:5555:6666:7777:8888",
":1111:2222:3333:4444:5555:6666:7777:8888",
# :::
":::2222:3333:4444:5555:6666:7777:8888",
"1111:::3333:4444:5555:6666:7777:8888",
"1111:2222:::4444:5555:6666:7777:8888",
"1111:2222:3333:::5555:6666:7777:8888",
"1111:2222:3333:4444:::6666:7777:8888",
"1111:2222:3333:4444:5555:::7777:8888",
"1111:2222:3333:4444:5555:6666:::8888",
"1111:2222:3333:4444:5555:6666:7777:::",
# Double ::
"::2222::4444:5555:6666:7777:8888",
"::2222:3333::5555:6666:7777:8888",
"::2222:3333:4444::6666:7777:8888",
"::2222:3333:4444:5555::7777:8888",
"::2222:3333:4444:5555:7777::8888",
"::2222:3333:4444:5555:7777:8888::",
"1111::3333::5555:6666:7777:8888",
"1111::3333:4444::6666:7777:8888",
"1111::3333:4444:5555::7777:8888",
"1111::3333:4444:5555:6666::8888",
"1111::3333:4444:5555:6666:7777::",
"1111:2222::4444::6666:7777:8888",
"1111:2222::4444:5555::7777:8888",
"1111:2222::4444:5555:6666::8888",
"1111:2222::4444:5555:6666:7777::",
"1111:2222:3333::5555::7777:8888",
"1111:2222:3333::5555:6666::8888",
"1111:2222:3333::5555:6666:7777::",
"1111:2222:3333:4444::6666::8888",
"1111:2222:3333:4444::6666:7777::",
"1111:2222:3333:4444:5555::7777::",
# Invalid data
"XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4",
"1111:2222:3333:4444:5555:6666:256.256.256.256",
# Too many components
"1111:2222:3333:4444:5555:6666:7777:8888:1.2.3",
"1111:2222:3333:4444:5555:6666:7777:1.2.3.4",
"1111:2222:3333:4444:5555:6666::1.2.3.4",
"::2222:3333:4444:5555:6666:7777:1.2.3.4",
"1111:2222:3333:4444:5555:6666:1.2.3.4.5",
# Too few components
"1111:2222:3333:4444:5555:1.2.3.4",
"1111:2222:3333:4444:1.2.3.4",
"1111:2222:3333:1.2.3.4",
"1111:2222:1.2.3.4",
"1111:1.2.3.4",
"1.2.3.4",
# Missing :
"11112222:3333:4444:5555:6666:1.2.3.4",
"1111:22223333:4444:5555:6666:1.2.3.4",
"1111:2222:33334444:5555:6666:1.2.3.4",
"1111:2222:3333:44445555:6666:1.2.3.4",
"1111:2222:3333:4444:55556666:1.2.3.4",
"1111:2222:3333:4444:5555:66661.2.3.4",
# Missing .
"1111:2222:3333:4444:5555:6666:255255.255.255",
"1111:2222:3333:4444:5555:6666:255.255255.255",
"1111:2222:3333:4444:5555:6666:255.255.255255",
# Missing : intended for ::
":1.2.3.4",
":6666:1.2.3.4",
":5555:6666:1.2.3.4",
":4444:5555:6666:1.2.3.4",
":3333:4444:5555:6666:1.2.3.4",
":2222:3333:4444:5555:6666:1.2.3.4",
":1111:2222:3333:4444:5555:6666:1.2.3.4",
# :::
":::2222:3333:4444:5555:6666:1.2.3.4",
"1111:::3333:4444:5555:6666:1.2.3.4",
"1111:2222:::4444:5555:6666:1.2.3.4",
"1111:2222:3333:::5555:6666:1.2.3.4",
"1111:2222:3333:4444:::6666:1.2.3.4",
"1111:2222:3333:4444:5555:::1.2.3.4",
# Double ::
"::2222::4444:5555:6666:1.2.3.4",
"::2222:3333::5555:6666:1.2.3.4",
"::2222:3333:4444::6666:1.2.3.4",
"::2222:3333:4444:5555::1.2.3.4",
"1111::3333::5555:6666:1.2.3.4",
"1111::3333:4444::6666:1.2.3.4",
"1111::3333:4444:5555::1.2.3.4",
"1111:2222::4444::6666:1.2.3.4",
"1111:2222::4444:5555::1.2.3.4",
"1111:2222:3333::5555::1.2.3.4",
# Missing parts
"::.",
"::..",
"::...",
"::1...",
"::1.2..",
"::1.2.3.",
"::.2..",
"::.2.3.",
"::.2.3.4",
"::..3.",
"::..3.4",
"::...4",
# Extra : in front
":1111:2222:3333:4444:5555:6666:7777::",
":1111:2222:3333:4444:5555:6666::",
":1111:2222:3333:4444:5555::",
":1111:2222:3333:4444::",
":1111:2222:3333::",
":1111:2222::",
":1111::",
":::",
":1111:2222:3333:4444:5555:6666::8888",
":1111:2222:3333:4444:5555::8888",
":1111:2222:3333:4444::8888",
":1111:2222:3333::8888",
":1111:2222::8888",
":1111::8888",
":::8888",
":1111:2222:3333:4444:5555::7777:8888",
":1111:2222:3333:4444::7777:8888",
":1111:2222:3333::7777:8888",
":1111:2222::7777:8888",
":1111::7777:8888",
":::7777:8888",
":1111:2222:3333:4444::6666:7777:8888",
":1111:2222:3333::6666:7777:8888",
":1111:2222::6666:7777:8888",
":1111::6666:7777:8888",
":::6666:7777:8888",
":1111:2222:3333::5555:6666:7777:8888",
":1111:2222::5555:6666:7777:8888",
":1111::5555:6666:7777:8888",
":::5555:6666:7777:8888",
":1111:2222::4444:5555:6666:7777:8888",
":1111::4444:5555:6666:7777:8888",
":::4444:5555:6666:7777:8888",
":1111::3333:4444:5555:6666:7777:8888",
":::3333:4444:5555:6666:7777:8888",
":::2222:3333:4444:5555:6666:7777:8888",
":1111:2222:3333:4444:5555:6666:1.2.3.4",
":1111:2222:3333:4444:5555::1.2.3.4",
":1111:2222:3333:4444::1.2.3.4",
":1111:2222:3333::1.2.3.4",
":1111:2222::1.2.3.4",
":1111::1.2.3.4",
":::1.2.3.4",
":1111:2222:3333:4444::6666:1.2.3.4",
":1111:2222:3333::6666:1.2.3.4",
":1111:2222::6666:1.2.3.4",
":1111::6666:1.2.3.4",
":::6666:1.2.3.4",
":1111:2222:3333::5555:6666:1.2.3.4",
":1111:2222::5555:6666:1.2.3.4",
":1111::5555:6666:1.2.3.4",
":::5555:6666:1.2.3.4",
":1111:2222::4444:5555:6666:1.2.3.4",
":1111::4444:5555:6666:1.2.3.4",
":::4444:5555:6666:1.2.3.4",
":1111::3333:4444:5555:6666:1.2.3.4",
":::3333:4444:5555:6666:1.2.3.4",
":::2222:3333:4444:5555:6666:1.2.3.4",
# Extra : at end
"1111:2222:3333:4444:5555:6666:7777:::",
"1111:2222:3333:4444:5555:6666:::",
"1111:2222:3333:4444:5555:::",
"1111:2222:3333:4444:::",
"1111:2222:3333:::",
"1111:2222:::",
"1111:::",
":::",
"1111:2222:3333:4444:5555:6666::8888:",
"1111:2222:3333:4444:5555::8888:",
"1111:2222:3333:4444::8888:",
"1111:2222:3333::8888:",
"1111:2222::8888:",
"1111::8888:",
"::8888:",
"1111:2222:3333:4444:5555::7777:8888:",
"1111:2222:3333:4444::7777:8888:",
"1111:2222:3333::7777:8888:",
"1111:2222::7777:8888:",
"1111::7777:8888:",
"::7777:8888:",
"1111:2222:3333:4444::6666:7777:8888:",
"1111:2222:3333::6666:7777:8888:",
"1111:2222::6666:7777:8888:",
"1111::6666:7777:8888:",
"::6666:7777:8888:",
"1111:2222:3333::5555:6666:7777:8888:",
"1111:2222::5555:6666:7777:8888:",
"1111::5555:6666:7777:8888:",
"::5555:6666:7777:8888:",
"1111:2222::4444:5555:6666:7777:8888:",
"1111::4444:5555:6666:7777:8888:",
"::4444:5555:6666:7777:8888:",
"1111::3333:4444:5555:6666:7777:8888:",
"::3333:4444:5555:6666:7777:8888:",
"::2222:3333:4444:5555:6666:7777:8888:",
);
subtest "is_ipv6() with valid addresses" => sub {
foreach my $ip (@valid_ipv6) {
ok(ddclient::is_ipv6($ip), "is_ipv6('$ip')");
}
};
subtest "is_ipv6() with invalid addresses" => sub {
foreach my $ip (@invalid_ipv6) {
ok(!ddclient::is_ipv6($ip), sprintf("!is_ipv6(%s)", defined($ip) ? "'$ip'" : 'undef'));
}
};
subtest "is_ipv6() with char adjacent to valid address" => sub {
foreach my $ch (split(//, '/.,:z @$#&%!^*()_-+'), "\n") {
subtest perlstring($ch) => sub {
foreach my $ip (@valid_ipv6) {
subtest $ip => sub {
my $test = $ch . $ip; # insert at front
ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
$test = $ip . $ch; # add at end
ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
$test = $ch . $ip . $ch; # wrap front and end
ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
};
}
};
}
};
subtest "extract_ipv6()" => sub {
my @test_cases = (
{name => "undef", text => undef, want => undef},
{name => "empty", text => "", want => undef},
{name => "invalid", text => "::12345", want => undef},
{name => "two addrs", text => "::1\n::2", want => "::1"},
{name => "zone index", text => "fe80::1%0", want => "fe80::1"},
{name => "url host+port", text => "[::1]:123", want => "::1"},
{name => "url host+zi+port", text => "[fe80::1%250]:123", want => "fe80::1"},
{name => "zero pad", text => "::0001", want => "::1"},
);
foreach my $tc (@test_cases) {
is(ddclient::extract_ipv6($tc->{text}), $tc->{want}, $tc->{name});
}
};
subtest "extract_ipv6() of valid addr with adjacent non-word char" => sub {
foreach my $wb (split(//, '/, @$#&%!^*()_-+'), "\n") {
subtest perlstring($wb) => sub {
my $test = "";
foreach my $ip (@valid_ipv6) {
$test = "foo" . $wb . $ip . $wb . "bar"; # wrap front and end
$ip =~ s/\b0+\B//g; ## remove embedded leading zeros for testing
is(ddclient::extract_ipv6($test), $ip, perlstring($test));
}
};
}
};
subtest "interface config samples" => sub {
for my $sample (@ddclient::t::interface_samples) {
if (defined($sample->{want_extract_ipv6_global})) {
subtest $sample->{name} => sub {
my $ip = ddclient::extract_ipv6($sample->{text});
ok(ddclient::is_ipv6($ip), "extract_ipv6() returns an IPv6 address");
};
foreach my $line (split(/\n/, $sample->{text})) {
my $ip = ddclient::extract_ipv6($line);
if ($ip) { ## Test cases may have lines that do not contain IPv6 address.
ok(ddclient::is_ipv6($ip),
sprintf("extract_ipv6(%s) returns an IPv6 address", perlstring($line)));
}
}
}
}
};
done_testing();