New is_ipv6 and extract_ipv6 algorithms
Also add unit tests.
This commit is contained in:
parent
92c1294af9
commit
29202f5bc1
3 changed files with 549 additions and 29 deletions
|
@ -71,6 +71,7 @@ AM_PL_LOG_FLAGS = -Mstrict -w \
|
||||||
-MDevel::Autoflush
|
-MDevel::Autoflush
|
||||||
handwritten_tests = \
|
handwritten_tests = \
|
||||||
t/is-and-extract-ipv4.pl \
|
t/is-and-extract-ipv4.pl \
|
||||||
|
t/is-and-extract-ipv6.pl \
|
||||||
t/geturl_connectivity.pl \
|
t/geturl_connectivity.pl \
|
||||||
t/geturl_ssl.pl \
|
t/geturl_ssl.pl \
|
||||||
t/parse_assignments.pl \
|
t/parse_assignments.pl \
|
||||||
|
|
85
ddclient.in
85
ddclient.in
|
@ -2297,44 +2297,71 @@ sub extract_ipv4 {
|
||||||
return $ip;
|
return $ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## Regex that matches an IPv6 address. Accepts embedded leading zeros.
|
||||||
|
## Accepts IPv4-mapped IPv6 addresses such as 64:ff9b::192.0.2.13.
|
||||||
|
######################################################################
|
||||||
|
my $regex_ipv6 = qr/
|
||||||
|
# Define some named groups so we can use Perl's recursive subpattern feature for shorthand:
|
||||||
|
(?<g>[0-9A-F]{1,4}){0} # "g" matches a group of 1 to 4 hex chars
|
||||||
|
(?<g_>(?&g):){0} # "g_" matches a group of 1 to 4 hex chars followed by a colon
|
||||||
|
(?<_g>:(?&g)){0} # "_g" matches a colon followed by a group of 1 to 4 hex chars
|
||||||
|
(?<g0>(?&g)?){0} # "g0" is an optional "g" (matches a group of 0 to 4 hex chars)
|
||||||
|
(?<g0_>(?&g0):){0} # "g0_" is an optional "g" followed by a colon
|
||||||
|
(?<x>[:.0-9A-Z]){0} # "x" matches chars that should never come before or after the address
|
||||||
|
(?<ip4>$regex_ipv4){0} # "ip4" matches an IPv4 address x.x.x.x
|
||||||
|
|
||||||
|
# Now for the regex itself:
|
||||||
|
(?<!(?&x)) # Not preceded by a character that is not allowed to come before the address
|
||||||
|
(?:
|
||||||
|
(?&g_){7}(?&g) # Exactly 8 groups of 1-4 hex chars
|
||||||
|
| (?&g_){1,7}: # OR compressed form with the double colon at the end
|
||||||
|
| :(?&_g){1,7} # OR compressed form with the double colon at the beginning
|
||||||
|
| :: # OR compressed to just a double colon
|
||||||
|
| # OR compressed form with the double colon in the middle:
|
||||||
|
(?= # Only consider this case if the string...
|
||||||
|
(?&g0_){2,7}(?&g0) # ...has 3 to 8 possibly empty groups (at least 3 because there
|
||||||
|
# will be at least one group before the ::, at least one group
|
||||||
|
# after the ::, and the :: itself is an empty group)...
|
||||||
|
(?!(?&x)) # ...then ends.
|
||||||
|
) # If the condition is true, then:
|
||||||
|
(?&g_){1,6} # 1 to 6 non-empty groups before the double colon
|
||||||
|
(?&_g){1,6} # 1 to 6 non-empty groups after the double colon
|
||||||
|
| # OR an IPv4-mapped IPv6 address:
|
||||||
|
(?: # It starts with a 96-bit prefix represented by:
|
||||||
|
(?&g_){6} # Exactly 6 groups of 1-4 hex chars with their colons
|
||||||
|
| (?&g_){1,5}: # OR compressed form with the double colon at the end
|
||||||
|
| ::(?&g_){0,5} # OR compressed form with the double colon at the beginning
|
||||||
|
| # OR compressed form with the double colon in the middle:
|
||||||
|
(?= # Only consider this case if the prefix...
|
||||||
|
(?&g0_){3,6} # ...has 3 to 6 possibly empty groups...
|
||||||
|
(?&ip4)(?!(?&x)) # ...then ends.
|
||||||
|
) # If the condition is true, then:
|
||||||
|
(?&g_){1,4} # 1 to 4 non-empty groups before the double colon
|
||||||
|
(?&_g){1,4} # 1 to 4 non-empty groups after the double colon
|
||||||
|
: # colon separating the IPv6 part from the IPv4 part
|
||||||
|
)
|
||||||
|
(?&ip4) # Prefix is followed by an IPv4 address
|
||||||
|
)
|
||||||
|
(?!(?&x)) # Not followed by a character that is not allowed to come after the address
|
||||||
|
/xi;
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
## is_ipv6() validates if string is valid IPv6 address with no preceding
|
## is_ipv6() validates if string is valid IPv6 address with no preceding
|
||||||
## or trailing spaces/characters.
|
## or trailing spaces/characters, not even line breaks.
|
||||||
######################################################################
|
######################################################################
|
||||||
sub is_ipv6 {
|
sub is_ipv6 {
|
||||||
my ($value) = @_;
|
return (shift // '') =~ /\A$regex_ipv6\z/;
|
||||||
return (length($value // '') != 0) &&
|
|
||||||
((extract_ipv6($value) // '') eq (($value =~ s/\b0+\B//g) ? $value : $value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
## extract_ipv6() extracts the first valid IPv6 address from the given string.
|
## extract_ipv6() finds the first valid IPv6 address in the given string,
|
||||||
## Accepts leading zeros in the address but removes them in returned value.
|
## removes embedded leading zeros, and returns the result.
|
||||||
######################################################################
|
######################################################################
|
||||||
sub extract_ipv6 {
|
sub extract_ipv6 {
|
||||||
my $content = shift;
|
(shift // '') =~ /($regex_ipv6)/ or return undef;
|
||||||
my $omits;
|
(my $ip = $1) =~ s/\b0+\B//g; ## remove embedded leading zeros
|
||||||
my $ip = "";
|
return $ip;
|
||||||
my $linenumbers = 0;
|
|
||||||
|
|
||||||
my @values = split('\n', $content);
|
|
||||||
foreach my $val (@values) {
|
|
||||||
next unless $val =~ /((:{0,2}[A-F0-9]{1,4}){0,7}:{1,2}[A-F0-9]{1,4})/i; # invalid char
|
|
||||||
my $parsed = $1;
|
|
||||||
|
|
||||||
# check for at least 7 colons
|
|
||||||
my $count_colon = () = $parsed =~ /:/g;
|
|
||||||
if ($count_colon != 7) {
|
|
||||||
# or one double colon
|
|
||||||
my $count_double_colon = () = $parsed =~ /::/g;
|
|
||||||
if ($count_double_colon != 1) {
|
|
||||||
next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$parsed =~ s/\b0+\B//g; ## remove embedded leading zeros
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
492
t/is-and-extract-ipv6.pl
Normal file
492
t/is-and-extract-ipv6.pl
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
use Test::More;
|
||||||
|
use B qw(perlstring);
|
||||||
|
|
||||||
|
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:",
|
||||||
|
);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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 (@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)) {
|
||||||
|
my $ip = ddclient::extract_ipv6($line);
|
||||||
|
ok(ddclient::is_ipv6($ip),
|
||||||
|
sprintf("extract_ipv6(%s) returns an IPv6 address", perlstring($line)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
done_testing();
|
Loading…
Reference in a new issue