IPv6.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. namespace MGModule\DNSManager2\mgLibs\custom\vendor;
  3. use MGModule\DNSManager2\mgLibs\custom\vendor\MathBigInteger;
  4. /* Copyright (c) 2011, Sam Clarke <http://www.samclarke.com/>
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of the <organization> nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL SAM CLARKE BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. class IPv6
  30. {
  31. public static function ipv6_range($prefix)
  32. {
  33. // Split in address and prefix length
  34. list($firstaddrstr, $prefixlen) = explode('/', $prefix);
  35. // Parse the address into a binary string
  36. $firstaddrbin = inet_pton($firstaddrstr);
  37. // Convert the binary string to a string with hexadecimal characters
  38. # unpack() can be replaced with bin2hex()
  39. # unpack() is used for symmetry with pack() below
  40. $unpacked = unpack('H*', $firstaddrbin);
  41. $firstaddrhex = reset($unpacked);
  42. // Overwriting first address string to make sure notation is optimal
  43. $firstaddrstr = inet_ntop($firstaddrbin);
  44. // Calculate the number of 'flexible' bits
  45. $flexbits = 128 - $prefixlen;
  46. // Build the hexadecimal string of the last address
  47. $lastaddrhex = $firstaddrhex;
  48. // We start at the end of the string (which is always 32 characters long)
  49. $pos = 31;
  50. while ($flexbits > 0)
  51. {
  52. // Get the character at this position
  53. $orig = substr($lastaddrhex, $pos, 1);
  54. // Convert it to an integer
  55. $origval = hexdec($orig);
  56. // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
  57. $newval = $origval | (pow(2, min(4, $flexbits)) - 1);
  58. // Convert it back to a hexadecimal character
  59. $new = dechex($newval);
  60. // And put that character back in the string
  61. $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1);
  62. // We processed one nibble, move to previous position
  63. $flexbits -= 4;
  64. $pos -= 1;
  65. }
  66. // Convert the hexadecimal string to a binary string
  67. # Using pack() here
  68. # Newer PHP version can use hex2bin()
  69. $lastaddrbin = pack('H*', $lastaddrhex);
  70. // And create an IPv6 address from the binary string
  71. $lastaddrstr = inet_ntop($lastaddrbin);
  72. return array($prefix, $firstaddrstr, $lastaddrstr);
  73. }
  74. /**
  75. * Converts human readable representation to a 128bit int
  76. * which can be stored in MySQL using DECIMAL(39,0).
  77. *
  78. * Requires PHP to be compiled with IPv6 support.
  79. * This could be made to work without IPv6 support but
  80. * I don't think there would be much use for it if PHP
  81. * doesn't support IPv6.
  82. *
  83. * @param string $ip IPv4 or IPv6 address to convert
  84. * @return string 128bit string that can be used with DECIMNAL(39,0) or false
  85. */
  86. public static function inet_ptoi($ip)
  87. {
  88. // make sure it is an ip
  89. if (filter_var($ip, FILTER_VALIDATE_IP) === false)
  90. return false;
  91. $parts = unpack('N*', inet_pton($ip));
  92. // fix IPv4
  93. if (strpos($ip, '.') !== false)
  94. $parts = array(1=>0, 2=>0, 3=>0, 4=>$parts[1]);
  95. foreach ($parts as &$part)
  96. {
  97. // convert any unsigned ints to signed from unpack.
  98. // this should be OK as it will be a PHP float not an int
  99. if ($part < 0)
  100. $part += 4294967296;
  101. }
  102. // use BCMath if the extension exists
  103. if (function_exists('bcadd'))
  104. {
  105. $decimal = $parts[4];
  106. $decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
  107. $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
  108. $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  109. }
  110. // otherwise use the pure PHP BigInteger
  111. else
  112. {
  113. $decimal = new MathBigInteger($parts[4]);
  114. $part3 = new MathBigInteger($parts[3]);
  115. $part2 = new MathBigInteger($parts[2]);
  116. $part1 = new MathBigInteger($parts[1]);
  117. $decimal = $decimal->add($part3->multiply(new MathBigInteger('4294967296')));
  118. $decimal = $decimal->add($part2->multiply(new MathBigInteger('18446744073709551616')));
  119. $decimal = $decimal->add($part1->multiply(new MathBigInteger('79228162514264337593543950336')));
  120. $decimal = $decimal->toString();
  121. }
  122. return $decimal;
  123. }
  124. /**
  125. * Converts a 128bit int to a human readable representation.
  126. *
  127. * Requires PHP to be compiled with IPv6 support.
  128. * This could be made to work without IPv6 support but
  129. * I don't think there would be much use for it if PHP
  130. * doesn't support IPv6.
  131. *
  132. * @param string $decimal 128bit int
  133. * @return string IPv4 or IPv6
  134. */
  135. public static function inet_itop($decimal)
  136. {
  137. $parts = array();
  138. // use BCMath if the extension exists
  139. if (function_exists('bcadd'))
  140. {
  141. $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
  142. $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  143. $parts[2] = bcdiv($decimal, '18446744073709551616', 0);
  144. $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
  145. $parts[3] = bcdiv($decimal, '4294967296', 0);
  146. $decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
  147. $parts[4] = $decimal;
  148. }
  149. // otherwise use the pure PHP BigInteger
  150. else
  151. {
  152. $decimal = new MathBigInteger($decimal);
  153. list($parts[1],) = $decimal->divide(new MathBigInteger('79228162514264337593543950336'));
  154. $decimal = $decimal->subtract($parts[1]->multiply(new MathBigInteger('79228162514264337593543950336')));
  155. list($parts[2],) = $decimal->divide(new MathBigInteger('18446744073709551616'));
  156. $decimal = $decimal->subtract($parts[2]->multiply(new MathBigInteger('18446744073709551616')));
  157. list($parts[3],) = $decimal->divide(new MathBigInteger('4294967296'));
  158. $decimal = $decimal->subtract($parts[3]->multiply(new MathBigInteger('4294967296')));
  159. $parts[4] = $decimal;
  160. $parts[1] = $parts[1]->toString();
  161. $parts[2] = $parts[2]->toString();
  162. $parts[3] = $parts[3]->toString();
  163. $parts[4] = $parts[4]->toString();
  164. }
  165. foreach ($parts as &$part)
  166. {
  167. // convert any signed ints to unsigned for pack
  168. // this should be fine as it will be treated as a float
  169. if ($part > 2147483647)
  170. $part -= 4294967296;
  171. }
  172. $ip = inet_ntop(pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]));
  173. // fix IPv4 by removing :: from the beginning
  174. if (strpos($ip, '.') !== false)
  175. return substr($ip, 2);
  176. return $ip;
  177. }
  178. }
  179. //example
  180. //echo inet_ptoi('::FFFF:FFFF:FFFF:FFFF') . "\n";
  181. //echo inet_ptoi('::FFFF:FFFF') . "\n";
  182. //echo inet_ptoi('255.255.255.255') . "\n";
  183. //echo inet_itop(inet_ptoi('::FFFF:FFFF:FFFF:FFFF')) . "\n";
  184. //echo inet_itop(inet_ptoi('::FFFF:FFFF')) . "\n";
  185. /* Output:
  186. 18446744073709551615
  187. 4294967295
  188. 4294967295
  189. ::ffff:ffff:ffff:ffff
  190. 255.255.255.255
  191. */