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