1<?php 2 3/** 4 * DokuWiki 32bit shims for IP address functions. 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 */ 8 9namespace dokuwiki; 10 11class Ip32 12{ 13 public static $b32 = '4294967296'; 14 /** 15 * slow and ugly bitwise_and for 32bit arch 16 * @param $u64 unsigned 64bit integer as string 17 * likely from ipv6UpperLowerOn32 18 * @param $pow 0-64 power of 2 for bitmask 19 */ 20 public static function bitmask64On32(string $u64, int $pow): string 21 { 22 $upper = bcdiv($u64, Ip32::$b32, 0); 23 $lower = bcmod($u64, Ip32::$b32); 24 // str of len 32 all 0s and 1s 25 $upper_bin = Ip32::decimalToBinary32($upper); 26 $lower_bin = Ip32::decimalToBinary32($lower); 27 $bin = $upper_bin . $lower_bin; 28 29 $mask = Ip32::makeBitmaskOn32(64 - $pow); 30 31 // most right is lowest bit 32 $res = '0'; 33 for ($i = 0; $i < 64; $i++) { 34 if (bcmul($bin[$i], $mask[$i]) == 1) { 35 $res = bcadd($res, bcpow(2, 63 - $i)); 36 } 37 } 38 return $res; 39 } 40 41 42 /** 43 * modeling bitshift like ~0 << $pow for 32-bit arch 44 * @param pow power of 2 for mask 45 * @return 64-char string of 1 and 0s 46 * pow=1 47 * 1111111111111111111111111111111111111111111111111111111111111110 48 * pow=63 49 * 1000000000000000000000000000000000000000000000000000000000000000 50 * pow=64 51 * 0000000000000000000000000000000000000000000000000000000000000000 52 */ 53 private static function makeBitmaskOn32(int $pow): string 54 { 55 $pow = $pow < 0 ? 64 - $pow : $pow; 56 $mask = sprintf("%064d", 0); 57 for ($i = 0; $i < 64; $i++) { 58 if ($i >= $pow) { 59 $mask[63 - $i] = '1'; 60 } 61 } 62 return $mask; 63 } 64 65 /** 66 * conversion of inet_pton ipv6 into 64-bit upper and lower 67 * bcmath version for 32-bit architecture 68 * w/o no unpack('J') - unsigned long long (always 64 bit, big endian byte order) 69 * 70 * results match unpack('Jupper/Jlower', $binary) 71 * 72 * @param string $binary inet_pton's ipv6 16 element binary 73 * 74 * @return string[] upper 64 and lower 64 for ipToNumber 75 */ 76 public static function ipv6UpperLowerOn32(string $binary) 77 { 78 // unpack into four 32-bit unsigned ints to recombine as 2 64-bit 79 $parts = unpack('N4', $binary); 80 $upper = Ip32::partsTo64($parts[1], $parts[2]); 81 $lower = Ip32::partsTo64($parts[3], $parts[4]); 82 return ['upper' => $upper, 'lower' => $lower]; 83 } 84 85 private static function partsTo64(string $high, string $low): string 86 { 87 // signed to unsigned 88 $high = $high < 0 ? bcadd($high, Ip32::$b32) : $high; 89 $low = $low < 0 ? bcadd($low, Ip32::$b32) : $low; 90 91 return bcadd(bcmul($high, Ip32::$b32), $low); 92 } 93 94 /** 95 * Convert a decimal number to 32-bit binary string using bcmath 96 * Handles large numbers that exceed PHP_INT_MAX on 32-bit systems 97 * 98 * @param string $decimal The decimal number as string 99 * @return string 32-bit binary representation 100 */ 101 private static function decimalToBinary32(string $decimal): string 102 { 103 if (bccomp($decimal, '0') == 0) { 104 return str_repeat('0', 32); 105 } 106 $binary = ''; 107 $num = $decimal; 108 for ($i = 31; $i >= 0; $i--) { 109 $power = bcpow('2', (string)$i); 110 if (bccomp($num, $power) >= 0) { 111 $binary .= '1'; 112 $num = bcsub($num, $power); 113 } else { 114 $binary .= '0'; 115 } 116 } 117 return $binary; 118 } 119} 120