@@ -17,94 +17,94 @@ |
||
| 17 | 17 | * @package OC\Security\Normalizer |
| 18 | 18 | */ |
| 19 | 19 | class IpAddress { |
| 20 | - /** |
|
| 21 | - * @param string $ip IP to normalize |
|
| 22 | - */ |
|
| 23 | - public function __construct( |
|
| 24 | - private string $ip, |
|
| 25 | - ) { |
|
| 26 | - } |
|
| 27 | - |
|
| 28 | - /** |
|
| 29 | - * Return the given subnet for an IPv6 address |
|
| 30 | - * Rely on security.ipv6_normalized_subnet_size, defaults to 56 |
|
| 31 | - */ |
|
| 32 | - private function getIPv6Subnet(string $ip): string { |
|
| 33 | - if (str_starts_with($ip, '[') && str_ends_with($ip, ']')) { |
|
| 34 | - $ip = substr($ip, 1, -1); |
|
| 35 | - } |
|
| 36 | - |
|
| 37 | - // Remove explicit interface if present (e.g., %enp2s0) |
|
| 38 | - $pos = strpos($ip, '%'); |
|
| 39 | - if ($pos !== false) { |
|
| 40 | - $ip = substr($ip, 0, $pos); |
|
| 41 | - } |
|
| 42 | - |
|
| 43 | - $config = \OCP\Server::get(IConfig::class); |
|
| 44 | - $maskSize = min(64, max(32, $config->getSystemValueInt('security.ipv6_normalized_subnet_size', 56))); |
|
| 45 | - |
|
| 46 | - $binary = inet_pton($ip); |
|
| 47 | - if ($binary === false) { |
|
| 48 | - return $ip . '/' . $maskSize; |
|
| 49 | - } |
|
| 50 | - |
|
| 51 | - if (PHP_INT_SIZE === 4) { |
|
| 52 | - // 32-bit PHP |
|
| 53 | - $value = match($maskSize) { |
|
| 54 | - 64 => -1, |
|
| 55 | - 63 => PHP_INT_MAX, |
|
| 56 | - default => (1 << ($maskSize - 32)) - 1, |
|
| 57 | - }; |
|
| 58 | - // as long as we support 32bit PHP we cannot use the `P` pack formatter (and not overflow 32bit integer) |
|
| 59 | - $mask = pack('VVVV', -1, $value, 0, 0); |
|
| 60 | - } else { |
|
| 61 | - // 64-bit PHP |
|
| 62 | - $mask = pack('VVP', (1 << 32) - 1, (1 << ($maskSize - 32)) - 1, 0); |
|
| 63 | - } |
|
| 64 | - |
|
| 65 | - return inet_ntop($binary & $mask) . '/' . $maskSize; |
|
| 66 | - } |
|
| 67 | - |
|
| 68 | - /** |
|
| 69 | - * Returns the IPv4 address embedded in an IPv6 if applicable. |
|
| 70 | - * The detected format is "::ffff:x.x.x.x" using the binary form. |
|
| 71 | - * |
|
| 72 | - * @return string|null embedded IPv4 string or null if none was found |
|
| 73 | - */ |
|
| 74 | - private function getEmbeddedIpv4(string $ipv6): ?string { |
|
| 75 | - $binary = inet_pton($ipv6); |
|
| 76 | - if ($binary === false) { |
|
| 77 | - return null; |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - $mask = inet_pton('::FFFF:FFFF'); |
|
| 81 | - if (($binary & ~$mask) !== inet_pton('::FFFF:0.0.0.0')) { |
|
| 82 | - return null; |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - return inet_ntop(substr($binary, -4)); |
|
| 86 | - } |
|
| 87 | - |
|
| 88 | - /** |
|
| 89 | - * Gets either the /32 (IPv4) or the /56 (default for IPv6) subnet of an IP address |
|
| 90 | - */ |
|
| 91 | - public function getSubnet(): string { |
|
| 92 | - if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { |
|
| 93 | - return $this->ip . '/32'; |
|
| 94 | - } |
|
| 95 | - |
|
| 96 | - $ipv4 = $this->getEmbeddedIpv4($this->ip); |
|
| 97 | - if ($ipv4 !== null) { |
|
| 98 | - return $ipv4 . '/32'; |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - return $this->getIPv6Subnet($this->ip); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - /** |
|
| 105 | - * Returns the specified IP address |
|
| 106 | - */ |
|
| 107 | - public function __toString(): string { |
|
| 108 | - return $this->ip; |
|
| 109 | - } |
|
| 20 | + /** |
|
| 21 | + * @param string $ip IP to normalize |
|
| 22 | + */ |
|
| 23 | + public function __construct( |
|
| 24 | + private string $ip, |
|
| 25 | + ) { |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + /** |
|
| 29 | + * Return the given subnet for an IPv6 address |
|
| 30 | + * Rely on security.ipv6_normalized_subnet_size, defaults to 56 |
|
| 31 | + */ |
|
| 32 | + private function getIPv6Subnet(string $ip): string { |
|
| 33 | + if (str_starts_with($ip, '[') && str_ends_with($ip, ']')) { |
|
| 34 | + $ip = substr($ip, 1, -1); |
|
| 35 | + } |
|
| 36 | + |
|
| 37 | + // Remove explicit interface if present (e.g., %enp2s0) |
|
| 38 | + $pos = strpos($ip, '%'); |
|
| 39 | + if ($pos !== false) { |
|
| 40 | + $ip = substr($ip, 0, $pos); |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + $config = \OCP\Server::get(IConfig::class); |
|
| 44 | + $maskSize = min(64, max(32, $config->getSystemValueInt('security.ipv6_normalized_subnet_size', 56))); |
|
| 45 | + |
|
| 46 | + $binary = inet_pton($ip); |
|
| 47 | + if ($binary === false) { |
|
| 48 | + return $ip . '/' . $maskSize; |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + if (PHP_INT_SIZE === 4) { |
|
| 52 | + // 32-bit PHP |
|
| 53 | + $value = match($maskSize) { |
|
| 54 | + 64 => -1, |
|
| 55 | + 63 => PHP_INT_MAX, |
|
| 56 | + default => (1 << ($maskSize - 32)) - 1, |
|
| 57 | + }; |
|
| 58 | + // as long as we support 32bit PHP we cannot use the `P` pack formatter (and not overflow 32bit integer) |
|
| 59 | + $mask = pack('VVVV', -1, $value, 0, 0); |
|
| 60 | + } else { |
|
| 61 | + // 64-bit PHP |
|
| 62 | + $mask = pack('VVP', (1 << 32) - 1, (1 << ($maskSize - 32)) - 1, 0); |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + return inet_ntop($binary & $mask) . '/' . $maskSize; |
|
| 66 | + } |
|
| 67 | + |
|
| 68 | + /** |
|
| 69 | + * Returns the IPv4 address embedded in an IPv6 if applicable. |
|
| 70 | + * The detected format is "::ffff:x.x.x.x" using the binary form. |
|
| 71 | + * |
|
| 72 | + * @return string|null embedded IPv4 string or null if none was found |
|
| 73 | + */ |
|
| 74 | + private function getEmbeddedIpv4(string $ipv6): ?string { |
|
| 75 | + $binary = inet_pton($ipv6); |
|
| 76 | + if ($binary === false) { |
|
| 77 | + return null; |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + $mask = inet_pton('::FFFF:FFFF'); |
|
| 81 | + if (($binary & ~$mask) !== inet_pton('::FFFF:0.0.0.0')) { |
|
| 82 | + return null; |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + return inet_ntop(substr($binary, -4)); |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + /** |
|
| 89 | + * Gets either the /32 (IPv4) or the /56 (default for IPv6) subnet of an IP address |
|
| 90 | + */ |
|
| 91 | + public function getSubnet(): string { |
|
| 92 | + if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { |
|
| 93 | + return $this->ip . '/32'; |
|
| 94 | + } |
|
| 95 | + |
|
| 96 | + $ipv4 = $this->getEmbeddedIpv4($this->ip); |
|
| 97 | + if ($ipv4 !== null) { |
|
| 98 | + return $ipv4 . '/32'; |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + return $this->getIPv6Subnet($this->ip); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + /** |
|
| 105 | + * Returns the specified IP address |
|
| 106 | + */ |
|
| 107 | + public function __toString(): string { |
|
| 108 | + return $this->ip; |
|
| 109 | + } |
|
| 110 | 110 | } |
@@ -45,7 +45,7 @@ discard block |
||
| 45 | 45 | |
| 46 | 46 | $binary = inet_pton($ip); |
| 47 | 47 | if ($binary === false) { |
| 48 | - return $ip . '/' . $maskSize; |
|
| 48 | + return $ip.'/'.$maskSize; |
|
| 49 | 49 | } |
| 50 | 50 | |
| 51 | 51 | if (PHP_INT_SIZE === 4) { |
@@ -62,7 +62,7 @@ discard block |
||
| 62 | 62 | $mask = pack('VVP', (1 << 32) - 1, (1 << ($maskSize - 32)) - 1, 0); |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | - return inet_ntop($binary & $mask) . '/' . $maskSize; |
|
| 65 | + return inet_ntop($binary & $mask).'/'.$maskSize; |
|
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | /** |
@@ -90,12 +90,12 @@ discard block |
||
| 90 | 90 | */ |
| 91 | 91 | public function getSubnet(): string { |
| 92 | 92 | if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { |
| 93 | - return $this->ip . '/32'; |
|
| 93 | + return $this->ip.'/32'; |
|
| 94 | 94 | } |
| 95 | 95 | |
| 96 | 96 | $ipv4 = $this->getEmbeddedIpv4($this->ip); |
| 97 | 97 | if ($ipv4 !== null) { |
| 98 | - return $ipv4 . '/32'; |
|
| 98 | + return $ipv4.'/32'; |
|
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 101 | return $this->getIPv6Subnet($this->ip); |