Add is_ipv6_global and extract_ipv6_global functions
This commit is contained in:
parent
147ee33754
commit
1ad4d6737a
5 changed files with 182 additions and 61 deletions
|
@ -74,6 +74,7 @@ handwritten_tests = \
|
|||
t/geturl_ssl.pl \
|
||||
t/is-and-extract-ipv4.pl \
|
||||
t/is-and-extract-ipv6.pl \
|
||||
t/is-and-extract-ipv6-global.pl \
|
||||
t/parse_assignments.pl \
|
||||
t/write_cache.pl
|
||||
generated_tests = \
|
||||
|
@ -150,4 +151,5 @@ 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/t.pm \
|
||||
t/lib/ok.pm
|
||||
|
|
32
ddclient.in
32
ddclient.in
|
@ -2406,6 +2406,38 @@ sub extract_ipv6 {
|
|||
return $ip;
|
||||
}
|
||||
|
||||
my $regex_ipv6_global = qr{
|
||||
(?! # Is not one of the following addresses:
|
||||
0{0,4}: # ::/16 is assumed to never contain globaly routable addresses
|
||||
| f[cd][0-9a-f]{2}: # fc00::/7 RFC4193 ULA
|
||||
| fe[89ab][0-9a-f]: # fe80::/10 link local
|
||||
| ff[0-9a-f]{2}: # ff00::/8 multicast
|
||||
)
|
||||
$regex_ipv6 # And is a valid IPv6 address
|
||||
}xi;
|
||||
|
||||
######################################################################
|
||||
## is_ipv6_global() returns true if the string is a valid IPv6 address
|
||||
## that is probably globally routable, with no preceding or trailing
|
||||
## characters. All addresses in the ::/16 block are assumed to not be
|
||||
## globally routable.
|
||||
######################################################################
|
||||
sub is_ipv6_global {
|
||||
return (shift // '') =~ /\A$regex_ipv6_global\z/
|
||||
}
|
||||
|
||||
######################################################################
|
||||
## extract_ipv6_global() finds the first IPv6 address in the given
|
||||
## string that satisfies is_ipv6_global(), removes embedded leading
|
||||
## zeros, and returns the result. Returns undef if no such address is
|
||||
## found.
|
||||
######################################################################
|
||||
sub extract_ipv6_global {
|
||||
(shift // '') =~ /($regex_ipv6_global)/ or return undef;
|
||||
(my $ip = $1) =~ s/\b0+\B//g; ## remove embedded leading zeros
|
||||
return $ip;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
## group_hosts_by
|
||||
######################################################################
|
||||
|
|
64
t/is-and-extract-ipv6-global.pl
Normal file
64
t/is-and-extract-ipv6-global.pl
Normal file
|
@ -0,0 +1,64 @@
|
|||
use Test::More;
|
||||
use ddclient::t;
|
||||
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||
|
||||
subtest "is_ipv6_global() with valid but non-globally-routable addresses" => sub {
|
||||
foreach my $ip (
|
||||
# The entirety of ::/16 is assumed to never contain globally routable addresses
|
||||
"::",
|
||||
"::1",
|
||||
"0:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
# fc00::/7 unique local addresses (ULA)
|
||||
"fc00::",
|
||||
"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
# fe80::/10 link-local unicast addresses
|
||||
"fe80::",
|
||||
"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
# ff00::/8 multicast addresses
|
||||
"ff00::",
|
||||
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
# Case insensitivity of the negative lookahead
|
||||
"FF00::",
|
||||
) {
|
||||
ok(!ddclient::is_ipv6_global($ip), "!is_ipv6_global('$ip')");
|
||||
}
|
||||
};
|
||||
|
||||
subtest "is_ipv6_global() with valid, globally routable addresses" => sub {
|
||||
foreach my $ip (
|
||||
"1::", # just after ::/16 assumed non-global block
|
||||
"fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", # just before fc00::/7 ULA block
|
||||
"fe00::", # just after fc00::/7 ULA block
|
||||
"fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", # just before fe80::/10 link-local block
|
||||
"fec0::", # just after fe80::/10 link-local block
|
||||
"feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", # just before ff00::/8 multicast block
|
||||
) {
|
||||
ok(ddclient::is_ipv6_global($ip), "is_ipv6_global('$ip')");
|
||||
}
|
||||
};
|
||||
|
||||
subtest "extract_ipv6_global()" => sub {
|
||||
my @test_cases = (
|
||||
{name => "undef", text => undef, want => undef},
|
||||
{name => "empty", text => "", want => undef},
|
||||
{name => "only non-global", text => "foo fe80:: bar", want => undef},
|
||||
{name => "single global", text => "foo 2000:: bar", want => "2000::"},
|
||||
{name => "multiple globals", text => "2000:: 3000::", want => "2000::"},
|
||||
{name => "global before non-global", text => "2000:: fe80::", want => "2000::"},
|
||||
{name => "non-global before global", text => "fe80:: 2000::", want => "2000::"},
|
||||
{name => "zero pad", text => "2001::0001", want => "2001::1"},
|
||||
);
|
||||
foreach my $tc (@test_cases) {
|
||||
is(ddclient::extract_ipv6_global($tc->{text}), $tc->{want}, $tc->{name});
|
||||
}
|
||||
};
|
||||
|
||||
subtest "interface config samples" => sub {
|
||||
for my $sample (@ddclient::t::interface_samples) {
|
||||
my $got = ddclient::extract_ipv6_global($sample->{text});
|
||||
is($got, $sample->{want_extract_ipv6_global}, $sample->{name});
|
||||
}
|
||||
};
|
||||
|
||||
done_testing();
|
|
@ -1,6 +1,6 @@
|
|||
use Test::More;
|
||||
use B qw(perlstring);
|
||||
|
||||
use ddclient::t;
|
||||
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||
|
||||
|
@ -361,60 +361,6 @@ my @invalid_ipv6 = (
|
|||
"::2222:3333:4444:5555:6666:7777:8888:",
|
||||
);
|
||||
|
||||
my @if_samples = (
|
||||
# Sample output from:
|
||||
# ip -6 -o addr show dev <interface> scope global
|
||||
# This seems to be consistent accross platforms. The last line is from Ubuntu of a static
|
||||
# assigned IPv6.
|
||||
["ip -6 -o addr show dev <interface> scope global", <<'EOF'],
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1::8214/128 scope global dynamic noprefixroute \ valid_lft 63197sec preferred_lft 63197sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781::8214/128 scope global dynamic noprefixroute \ valid_lft 63197sec preferred_lft 63197sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:89b9:4b1c:186c:a0c7/64 scope global temporary dynamic \ valid_lft 85954sec preferred_lft 21767sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:89b9:4b1c:186c:a0c7/64 scope global temporary dynamic \ valid_lft 85954sec preferred_lft 21767sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:34a6:c329:c52e:8ba6/64 scope global temporary deprecated dynamic \ valid_lft 85954sec preferred_lft 0sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:b417:fe35:166b:4816/64 scope global dynamic mngtmpaddr noprefixroute \ valid_lft 85954sec preferred_lft 85954sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:34a6:c329:c52e:8ba6/64 scope global temporary deprecated dynamic \ valid_lft 85954sec preferred_lft 0sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:f911:a224:7e69:d22/64 scope global dynamic mngtmpaddr noprefixroute \ valid_lft 85954sec preferred_lft 85954sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781::100/128 scope global noprefixroute \ valid_lft forever preferred_lft forever
|
||||
EOF
|
||||
# Sample output from MacOS:
|
||||
# ifconfig <interface> | grep -w "inet6"
|
||||
# (Yes, there is a tab at start of each line.) The last two lines are with a manually
|
||||
# configured static GUA.
|
||||
["MacOS: ifconfig <interface> | grep -w \"inet6\"", <<'EOF'],
|
||||
inet6 fe80::1419:abd0:5943:8bbb%en0 prefixlen 64 secured scopeid 0xa
|
||||
inet6 fdb6:1d86:d9bd:1:142c:8e9e:de48:843e prefixlen 64 autoconf secured
|
||||
inet6 fdb6:1d86:d9bd:1:7447:cf67:edbd:cea4 prefixlen 64 autoconf temporary
|
||||
inet6 fdb6:1d86:d9bd:1::c5b3 prefixlen 64 dynamic
|
||||
inet6 2001:DB8:4341:0781:141d:66b9:2ba1:b67d prefixlen 64 autoconf secured
|
||||
inet6 2001:DB8:4341:0781:64e1:b68f:e8af:5d6e prefixlen 64 autoconf temporary
|
||||
inet6 fe80::1419:abd0:5943:8bbb%en0 prefixlen 64 secured scopeid 0xa
|
||||
inet6 2001:DB8:4341:0781::101 prefixlen 64
|
||||
EOF
|
||||
["RHEL: ifconfig <interface> | grep -w \"inet6\"", <<'EOF'],
|
||||
inet6 2001:DB8:4341:0781::dc14 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 fe80::cd48:4a58:3b0f:4d30 prefixlen 64 scopeid 0x20<link>
|
||||
inet6 2001:DB8:4341:0781:e720:3aec:a936:36d4 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:9c16:8cbf:ae33:f1cc prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1::dc14 prefixlen 128 scopeid 0x0<global>
|
||||
EOF
|
||||
["Ubuntu: ifconfig <interface> | grep -w \"inet6\"", <<'EOF'],
|
||||
inet6 fdb6:1d86:d9bd:1:34a6:c329:c52e:8ba6 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:89b9:4b1c:186c:a0c7 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1::8214 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:b417:fe35:166b:4816 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fe80::5b31:fc63:d353:da68 prefixlen 64 scopeid 0x20<link>
|
||||
inet6 2001:DB8:4341:0781::8214 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:34a6:c329:c52e:8ba6 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:89b9:4b1c:186c:a0c7 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:f911:a224:7e69:d22 prefixlen 64 scopeid 0x0<global>
|
||||
EOF
|
||||
["Busybox: ifconfig <interface> | grep -w \"inet6\"", <<'EOF'],
|
||||
inet6 addr: fe80::4362:31ff:fe08:61b4/64 Scope:Link
|
||||
inet6 addr: 2001:DB8:4341:0781:ed44:eb63:b070:212f/128 Scope:Global
|
||||
EOF
|
||||
);
|
||||
|
||||
|
||||
subtest "is_ipv6() with valid addresses" => sub {
|
||||
foreach my $ip (@valid_ipv6) {
|
||||
|
@ -475,12 +421,11 @@ subtest "extract_ipv6() of valid addr with adjacent non-word char" => sub {
|
|||
};
|
||||
|
||||
subtest "interface config samples" => sub {
|
||||
for my $sample (@if_samples) {
|
||||
my ($name, $text) = @$sample;
|
||||
subtest $name => sub {
|
||||
my $ip = ddclient::extract_ipv6($text);
|
||||
ok(ddclient::is_ipv6($ip), "extract_ipv6(\$text) returns an IPv6 address");
|
||||
foreach my $line (split(/\n/, $text)) {
|
||||
for my $sample (@ddclient::t::interface_samples) {
|
||||
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);
|
||||
ok(ddclient::is_ipv6($ip),
|
||||
sprintf("extract_ipv6(%s) returns an IPv6 address", perlstring($line)));
|
||||
|
|
78
t/lib/ddclient/t.pm
Normal file
78
t/lib/ddclient/t.pm
Normal file
|
@ -0,0 +1,78 @@
|
|||
package ddclient::t;
|
||||
require v5.10.1;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our @interface_samples = (
|
||||
# Sample output from:
|
||||
# ip -6 -o addr show dev <interface> scope global
|
||||
# This seems to be consistent accross platforms. The last line is from Ubuntu of a static
|
||||
# assigned IPv6.
|
||||
{
|
||||
name => 'ip -6 -o addr show dev <interface> scope global',
|
||||
text => <<'EOF',
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1::8214/128 scope global dynamic noprefixroute \ valid_lft 63197sec preferred_lft 63197sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781::8214/128 scope global dynamic noprefixroute \ valid_lft 63197sec preferred_lft 63197sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:89b9:4b1c:186c:a0c7/64 scope global temporary dynamic \ valid_lft 85954sec preferred_lft 21767sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:89b9:4b1c:186c:a0c7/64 scope global temporary dynamic \ valid_lft 85954sec preferred_lft 21767sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:34a6:c329:c52e:8ba6/64 scope global temporary deprecated dynamic \ valid_lft 85954sec preferred_lft 0sec
|
||||
2: ens160 inet6 fdb6:1d86:d9bd:1:b417:fe35:166b:4816/64 scope global dynamic mngtmpaddr noprefixroute \ valid_lft 85954sec preferred_lft 85954sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:34a6:c329:c52e:8ba6/64 scope global temporary deprecated dynamic \ valid_lft 85954sec preferred_lft 0sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781:f911:a224:7e69:d22/64 scope global dynamic mngtmpaddr noprefixroute \ valid_lft 85954sec preferred_lft 85954sec
|
||||
2: ens160 inet6 2001:DB8:4341:0781::100/128 scope global noprefixroute \ valid_lft forever preferred_lft forever
|
||||
EOF
|
||||
want_extract_ipv6_global => '2001:DB8:4341:781::8214',
|
||||
},
|
||||
# Sample output from MacOS:
|
||||
# ifconfig <interface> | grep -w "inet6"
|
||||
# (Yes, there is a tab at start of each line.) The last two lines are with a manually
|
||||
# configured static GUA.
|
||||
{
|
||||
name => 'MacOS: ifconfig <interface> | grep -w inet6',
|
||||
text => <<'EOF',
|
||||
inet6 fe80::1419:abd0:5943:8bbb%en0 prefixlen 64 secured scopeid 0xa
|
||||
inet6 fdb6:1d86:d9bd:1:142c:8e9e:de48:843e prefixlen 64 autoconf secured
|
||||
inet6 fdb6:1d86:d9bd:1:7447:cf67:edbd:cea4 prefixlen 64 autoconf temporary
|
||||
inet6 fdb6:1d86:d9bd:1::c5b3 prefixlen 64 dynamic
|
||||
inet6 2001:DB8:4341:0781:141d:66b9:2ba1:b67d prefixlen 64 autoconf secured
|
||||
inet6 2001:DB8:4341:0781:64e1:b68f:e8af:5d6e prefixlen 64 autoconf temporary
|
||||
inet6 fe80::1419:abd0:5943:8bbb%en0 prefixlen 64 secured scopeid 0xa
|
||||
inet6 2001:DB8:4341:0781::101 prefixlen 64
|
||||
EOF
|
||||
want_extract_ipv6_global => '2001:DB8:4341:781:141d:66b9:2ba1:b67d',
|
||||
},
|
||||
{
|
||||
name => 'RHEL: ifconfig <interface> | grep -w inet6',
|
||||
text => <<'EOF',
|
||||
inet6 2001:DB8:4341:0781::dc14 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 fe80::cd48:4a58:3b0f:4d30 prefixlen 64 scopeid 0x20<link>
|
||||
inet6 2001:DB8:4341:0781:e720:3aec:a936:36d4 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:9c16:8cbf:ae33:f1cc prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1::dc14 prefixlen 128 scopeid 0x0<global>
|
||||
EOF
|
||||
want_extract_ipv6_global => '2001:DB8:4341:781::dc14',
|
||||
},
|
||||
{
|
||||
name => 'Ubuntu: ifconfig <interface> | grep -w inet6',
|
||||
text => <<'EOF',
|
||||
inet6 fdb6:1d86:d9bd:1:34a6:c329:c52e:8ba6 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:89b9:4b1c:186c:a0c7 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1::8214 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 fdb6:1d86:d9bd:1:b417:fe35:166b:4816 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 fe80::5b31:fc63:d353:da68 prefixlen 64 scopeid 0x20<link>
|
||||
inet6 2001:DB8:4341:0781::8214 prefixlen 128 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:34a6:c329:c52e:8ba6 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:89b9:4b1c:186c:a0c7 prefixlen 64 scopeid 0x0<global>
|
||||
inet6 2001:DB8:4341:0781:f911:a224:7e69:d22 prefixlen 64 scopeid 0x0<global>
|
||||
EOF
|
||||
want_extract_ipv6_global => '2001:DB8:4341:781::8214',
|
||||
},
|
||||
{
|
||||
name => 'Busybox: ifconfig <interface> | grep -w inet6',
|
||||
text => <<'EOF',
|
||||
inet6 addr: fe80::4362:31ff:fe08:61b4/64 Scope:Link
|
||||
inet6 addr: 2001:DB8:4341:0781:ed44:eb63:b070:212f/128 Scope:Global
|
||||
EOF
|
||||
want_extract_ipv6_global => '2001:DB8:4341:781:ed44:eb63:b070:212f',
|
||||
},
|
||||
);
|
Loading…
Reference in a new issue