Improve normalizer detecting IPv4 inside of IPv6

The subnet for an IPv4 address inside of IPv6 is now returned in its
IPv4 form.

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
pull/29853/head
Vincent Petry 2021-11-22 15:53:41 +07:00 committed by backportbot[bot]
parent 87af149292
commit d9a5f2867e
2 changed files with 51 additions and 3 deletions

@ -92,6 +92,39 @@ class IpAddress {
return \inet_ntop($binary).'/'.$maskBits;
}
/**
* Returns the IPv4 address embedded in an IPv6 if applicable.
* The detected format is "::ffff:x.x.x.x" using the binary form.
*
* @param string $ipv6 IPv6 string
* @return null|string embedded IPv4 string or null if none was found
*/
private function getEmbeddedIpv4($ipv6) {
$binary = inet_pton($ipv6);
if (!$binary) {
return null;
}
for ($i = 0; $i <= 9; $i++) {
if (unpack('C', $binary[$i])[1] !== 0) {
return null;
}
}
for ($i = 10; $i <= 11; $i++) {
if (unpack('C', $binary[$i])[1] !== 255) {
return null;
}
}
$binary4 = '';
for ($i = 12; $i < 16; $i++) {
$binary4 .= $binary[$i];
}
return inet_ntop($binary4);
}
/**
* Gets either the /32 (IPv4) or the /64 (IPv6) subnet of an IP address
*
@ -103,9 +136,16 @@ class IpAddress {
$this->ip,
32
);
} elseif (substr(strtolower($this->ip), 0, 7) === '::ffff:') {
return '::ffff:' . $this->getIPv4Subnet(substr($this->ip, 7), 32);
}
$ipv4 = $this->getEmbeddedIpv4($this->ip);
if ($ipv4 !== null) {
return $this->getIPv4Subnet(
$ipv4,
32
);
}
return $this->getIPv6Subnet(
$this->ip,
64

@ -37,7 +37,15 @@ class IpAddressTest extends TestCase {
],
[
'::ffff:192.168.0.123',
'::ffff:192.168.0.123/32',
'192.168.0.123/32',
],
[
'0:0:0:0:0:ffff:192.168.0.123',
'192.168.0.123/32',
],
[
'0:0:0:0:0:ffff:c0a8:7b',
'192.168.0.123/32',
],
[
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',