From: Tim Düsterhus Date: Fri, 23 Jul 2021 10:33:23 +0000 (+0200) Subject: Fix IpAddress::toBulletMasked() for IP addresses with unmasked quadruplets with value... X-Git-Tag: 5.4.2~13 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=cb18d650563164f49e518ad9d699116b8f75dcf4;p=GitHub%2FWoltLab%2FWCF.git Fix IpAddress::toBulletMasked() for IP addresses with unmasked quadruplets with value zero Rebuild the IPv6 masking algorithm to a proper parser handling the quadruplets one-by-one, instead of attempting to process the IP address using regular expressions. This patch fixes masking of IP addresses such as: 2001:db8:1234:0:abcd::1234 Applying a /64 mask will transform the IP address into: 2001:db8:1234:: Note how the explicit 0 block is elided as well. Previously the bullet masking algorithm would transform this into: 2001:db8:1234:••••:••••:••••:•••• Resulting in an IP address with just 7 quadruplets. Now it correctly returns: 2001:db8:1234:0:••••:••••:••••:•••• --- diff --git a/wcfsetup/install/files/lib/util/IpAddress.class.php b/wcfsetup/install/files/lib/util/IpAddress.class.php index d96dc4c557..404ec8cf49 100644 --- a/wcfsetup/install/files/lib/util/IpAddress.class.php +++ b/wcfsetup/install/files/lib/util/IpAddress.class.php @@ -134,18 +134,43 @@ final class IpAddress (string)$masked ); } else { - $maskedDigits = (128 - $mask6) / 4; - - // Partially masked quadruplet. - $replacement = \str_repeat("\u{2022}", ($maskedDigits % 4)); - // Fully masked quadruplets. - $replacement .= \str_repeat(":\u{2022}\u{2022}\u{2022}\u{2022}", ($maskedDigits / 4)); + $quadruplets = []; + // We need to check whether we have an all-zero IP address, because $quadruplets + // will contain an empty entry otherwise. + if ((string)$masked !== '::') { + $quadruplets = \explode( + ':', + \preg_replace('/::$/', '', (string)$masked) + ); + } + while (\count($quadruplets) < 8) { + $quadruplets[] = '0'; + } + + $result = []; + for ($i = 0; $i < 128; $i += 16) { + $quadruplet = \array_shift($quadruplets); + if ($mask6 >= ($i + 16)) { + // This quadruplet is completely unmasked. This case is special, because we don't + // apply the padding for formatting reasons. + $result[] = $quadruplet; + } else { + // This quadruplet is partially or completely masked. + $visibleDigits = \max(($mask6 - $i) / 4, 0); + $paddedQuadruplet = \str_pad( + $quadruplet, + 4, + '0', + \STR_PAD_LEFT + ); + + $maskedQuadruplet = \substr($paddedQuadruplet, 0, $visibleDigits); + $maskedQuadruplet .= \str_repeat("\u{2022}", 4 - $visibleDigits); + $result[] = $maskedQuadruplet; + } + } - return \preg_replace( - '/.{' . ($maskedDigits % 4) . '}::$/', - $replacement, - (string)$masked - ); + return \implode(':', $result); } }