1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * This file contains the implementation of the Net_IPv6 class 7 * 8 * PHP versions 4 and 5 9 * 10 * LICENSE: This source file is subject to the New BSD license, that is 11 * available through the world-wide-web at 12 * http://www.opensource.org/licenses/bsd-license.php 13 * If you did not receive a copy of the new BSDlicense and are unable 14 * to obtain it through the world-wide-web, please send a note to 15 * license@php.net so we can mail you a copy immediately 16 * 17 * @category Net 18 * @package Net_IPv6 19 * @author Alexander Merz <alexander.merz@web.de> 20 * @copyright 2003-2005 The PHP Group 21 * @license BSD License http://www.opensource.org/licenses/bsd-license.php 22 * @version CVS: $Id: IPv6.php 339983 2016-09-04 21:13:31Z alexmerz $ 23 * @link http://pear.php.net/package/Net_IPv6 24 */ 25 26// {{{ constants 27 28/** 29 * Error message if netmask bits was not found 30 * @see isInNetmask 31 */ 32define("NET_IPV6_NO_NETMASK_MSG", "Netmask length not found"); 33 34/** 35 * Error code if netmask bits was not found 36 * @see isInNetmask 37 */ 38define("NET_IPV6_NO_NETMASK", 10); 39 40/** 41 * Address Type: Unassigned (RFC 1884, Section 2.3) 42 * @see getAddressType() 43 */ 44define("NET_IPV6_UNASSIGNED", 1); 45 46/** 47 * Address Type: Reserved (RFC 1884, Section 2.3) 48 * @see getAddressType() 49 */ 50define("NET_IPV6_RESERVED", 11); 51 52/** 53 * Address Type: Reserved for NSAP Allocation (RFC 1884, Section 2.3) 54 * @see getAddressType() 55 */ 56define("NET_IPV6_RESERVED_NSAP", 12); 57 58/** 59 * Address Type: Reserved for IPX Allocation (RFC 1884, Section 2.3) 60 * @see getAddressType() 61 */ 62define("NET_IPV6_RESERVED_IPX", 13); 63 64/** 65 * Address Type: Reserved for Geographic-Based Unicast Addresses 66 * (RFC 1884, Section 2.3) 67 * @see getAddressType() 68 */ 69define("NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC", 14); 70 71/** 72 * Address Type: Provider-Based Unicast Address (RFC 1884, Section 2.3) 73 * @see getAddressType() 74 */ 75define("NET_IPV6_UNICAST_PROVIDER", 22); 76 77/** 78 * Address Type: Multicast Addresses (RFC 1884, Section 2.3) 79 * @see getAddressType() 80 */ 81define("NET_IPV6_MULTICAST", 31); 82 83/** 84 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) 85 * @see getAddressType() 86 */ 87define("NET_IPV6_LOCAL_LINK", 42); 88 89/** 90 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) 91 * @see getAddressType() 92 */ 93define("NET_IPV6_LOCAL_SITE", 43); 94 95/** 96 * Address Type: Address range to embedded IPv4 ip in an IPv6 address (RFC 4291, Section 2.5.5) 97 * @see getAddressType() 98 */ 99define("NET_IPV6_IPV4MAPPING", 51); 100 101/** 102 * Address Type: Unspecified (RFC 4291, Section 2.5.2) 103 * @see getAddressType() 104 */ 105define("NET_IPV6_UNSPECIFIED", 52); 106 107/** 108 * Address Type: Unspecified (RFC 4291, Section 2.5.3) 109 * @see getAddressType() 110 */ 111define("NET_IPV6_LOOPBACK", 53); 112 113/** 114 * Address Type: address can not assigned to a specific type 115 * @see getAddressType() 116 */ 117define("NET_IPV6_UNKNOWN_TYPE", 1001); 118 119// }}} 120// {{{ Net_IPv6 121 122/** 123 * Class to validate and to work with IPv6 addresses. 124 * 125 * @category Net 126 * @package Net_IPv6 127 * @author Alexander Merz <alexander.merz@web.de> 128 * @author <elfrink at introweb dot nl> 129 * @author Josh Peck <jmp at joshpeck dot org> 130 * @copyright 2003-2010 The PHP Group 131 * @license BSD License http://www.opensource.org/licenses/bsd-license.php 132 * @version Release: 1.1.0RC5 133 * @link http://pear.php.net/package/Net_IPv6 134 */ 135class Net_IPv6 136{ 137 138 // {{{ separate() 139 /** 140 * Separates an IPv6 address into the address and a prefix length part 141 * 142 * @param String $ip the (compressed) IP as Hex representation 143 * 144 * @return Array the first element is the IP, the second the prefix length 145 * @since 1.2.0 146 * @access public 147 * @static 148 */ 149 public static function separate($ip) 150 { 151 152 $addr = $ip; 153 $spec = ''; 154 155 if(false === strrpos($ip, '/')) { 156 157 return array($addr, $spec); 158 159 } 160 161 $elements = explode('/', $ip); 162 163 if(2 == count($elements)) { 164 165 $addr = $elements[0]; 166 $spec = $elements[1]; 167 168 } 169 170 return array($addr, $spec); 171 172 } 173 // }}} 174 175 // {{{ removeNetmaskSpec() 176 177 /** 178 * Removes a possible existing prefix length/ netmask specification at an IP address. 179 * 180 * @param String $ip the (compressed) IP as Hex representation 181 * 182 * @return String the IP without netmask length 183 * @since 1.1.0 184 * @access public 185 * @static 186 */ 187 public static function removeNetmaskSpec($ip) 188 { 189 190 $elements = Net_IPv6::separate($ip); 191 192 return $elements[0]; 193 194 } 195 // }}} 196 // {{{ removePrefixLength() 197 198 /** 199 * Tests for a prefix length specification in the address 200 * and removes the prefix length, if exists 201 * 202 * The method is technically identical to removeNetmaskSpec() and 203 * will be dropped in a future release. 204 * 205 * @param String $ip a valid ipv6 address 206 * 207 * @return String the address without a prefix length 208 * @access public 209 * @static 210 * @see removeNetmaskSpec() 211 * @deprecated 212 */ 213 public static function removePrefixLength($ip) 214 { 215 $pos = strrpos($ip, '/'); 216 217 if (false !== $pos) { 218 219 return substr($ip, 0, $pos); 220 221 } 222 223 return $ip; 224 } 225 226 // }}} 227 // {{{ getNetmaskSpec() 228 229 /** 230 * Returns a possible existing prefix length/netmask specification on an IP address. 231 * 232 * @param String $ip the (compressed) IP as Hex representation 233 * 234 * @return String the netmask spec 235 * @since 1.1.0 236 * @access public 237 * @static 238 */ 239 public static function getNetmaskSpec($ip) 240 { 241 242 $elements = Net_IPv6::separate($ip); 243 244 return $elements[1]; 245 246 } 247 248 // }}} 249 // {{{ getPrefixLength() 250 251 /** 252 * Tests for a prefix length specification in the address 253 * and returns the prefix length, if exists 254 * 255 * The method is technically identical to getNetmaskSpec() and 256 * will be dropped in a future release. 257 * 258 * @param String $ip a valid ipv6 address 259 * 260 * @return Mixed the prefix as String or false, if no prefix was found 261 * @access public 262 * @static 263 * @deprecated 264 */ 265 public static function getPrefixLength($ip) 266 { 267 if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", 268 $ip, $matches)) { 269 270 return $matches[2]; 271 272 } else { 273 274 return false; 275 276 } 277 278 } 279 280 // }}} 281 // {{{ getNetmask() 282 283 /** 284 * Calculates the network prefix based on the netmask bits. 285 * 286 * @param String $ip the (compressed) IP in Hex format 287 * @param int $bits if the number of netmask bits is not part of the IP 288 * you must provide the number of bits 289 * 290 * @return String the network prefix 291 * @since 1.1.0 292 * @access public 293 * @static 294 */ 295 public static function getNetmask($ip, $bits = null) 296 { 297 if (null==$bits) { 298 299 $elements = explode('/', $ip); 300 301 if (2 == count($elements)) { 302 303 $addr = $elements[0]; 304 $bits = $elements[1]; 305 306 } else { 307 308 include_once 'PEAR.php'; 309 310 return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, 311 NET_IPV6_NO_NETMASK); 312 } 313 314 } else { 315 316 $addr = $ip; 317 318 } 319 320 $addr = Net_IPv6::uncompress($addr); 321 $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits); 322 323 return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask); 324 } 325 326 // }}} 327 // {{{ isInNetmask() 328 329 /** 330 * Checks if an (compressed) IP is in a specific address space. 331 * 332 * If the IP does not contain the number of netmask bits (F8000::FFFF/16) 333 * then you have to use the $bits parameter. 334 * 335 * @param String $ip the IP to check (eg. F800::FFFF) 336 * @param String $netmask the netmask (eg F800::) 337 * @param int $bits the number of netmask bits to compare, 338 * if not given in $ip 339 * 340 * @return boolean true if $ip is in the netmask 341 * @since 1.1.0 342 * @access public 343 * @static 344 */ 345 public static function isInNetmask($ip, $netmask, $bits=null) 346 { 347 // try to get the bit count 348 349 if (null == $bits) { 350 351 $elements = explode('/', $ip); 352 353 if (2 == count($elements)) { 354 355 $ip = $elements[0]; 356 $bits = $elements[1]; 357 358 } else if (null == $bits) { 359 360 $elements = explode('/', $netmask); 361 362 if (2 == count($elements)) { 363 364 $netmask = $elements[0]; 365 $bits = $elements[1]; 366 367 // Correctly uncompress netmasks with prefixes, ie ::FFFF/96 == 0:0:0:0:0:FFFF:0:0/96 368 // Need only for xxx::yyy/zz and ::yyy/zz 369 // Added by Observium developers 370 if (preg_match('/\:\:[a-f\d]{1,4}$/', $netmask) || // ::yyy/zz 371 preg_match('/^[a-f\d]{1,4}.*\:\:.*[a-f\d]{1,4}$/', $netmask)) { // xxx::yyy/zz 372 $c_bits = intval((128 - $bits) / 16); 373 $netmask .= str_repeat(':0', $c_bits); 374 } 375 376 } 377 378 if (null == $bits) { 379 380 include_once 'PEAR.php'; 381 return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, 382 NET_IPV6_NO_NETMASK); 383 384 } 385 386 } 387 388 } 389 390 $binIp = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip)); 391 $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask)); 392 //echo("IP: $ip, Net: $netmask, CIDR: /$bits\n"); 393 //echo("IP: ".substr($binIp, 0, $bits)." ".substr($binIp, $bits)." (".strlen($binIp). 394 // ")\nNet: ".substr($binNetmask, 0, $bits)." ".substr($binNetmask, $bits)." (".strlen($binNetmask).")\n"); 395 396 if (null != $bits 397 && "" != $bits 398 && 0 == strncmp($binNetmask, $binIp, $bits)) { 399 400 return true; 401 402 } 403 404 return false; 405 } 406 407 // }}} 408 // {{{ getAddressType() 409 410 /** 411 * Returns the type of an IPv6 address. 412 * 413 * RFC 2373, Section 2.3 describes several types of addresses in 414 * the IPv6 address space. 415 * Several address types are markers for reserved spaces and as 416 * a consequence are subject to change. 417 * 418 * @param String $ip the IP address in Hex format, 419 * compressed IPs are allowed 420 * 421 * @return int one of the address type constants 422 * @access public 423 * @since 1.1.0 424 * @static 425 * 426 * @see NET_IPV6_UNASSIGNED 427 * @see NET_IPV6_RESERVED 428 * @see NET_IPV6_RESERVED_NSAP 429 * @see NET_IPV6_RESERVED_IPX 430 * @see NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC 431 * @see NET_IPV6_UNICAST_PROVIDER 432 * @see NET_IPV6_MULTICAST 433 * @see NET_IPV6_LOCAL_LINK 434 * @see NET_IPV6_LOCAL_SITE 435 * @see NET_IPV6_IPV4MAPPING 436 * @see NET_IPV6_UNSPECIFIED 437 * @see NET_IPV6_LOOPBACK 438 * @see NET_IPV6_UNKNOWN_TYPE 439 */ 440 public static function getAddressType($ip) 441 { 442 $ip = Net_IPv6::removeNetmaskSpec($ip); 443 $binip = Net_IPv6::_ip2Bin($ip); 444 445 if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128 446 447 return NET_IPV6_UNSPECIFIED; 448 449 } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128 450 451 return NET_IPV6_LOOPBACK; 452 453 } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96 454 455 return NET_IPV6_IPV4MAPPING; 456 457 } else if (0 == strncmp('1111111010', $binip, 10)) { 458 459 return NET_IPV6_LOCAL_LINK; 460 461 } else if (0 == strncmp('1111111011', $binip, 10)) { 462 463 return NET_IPV6_LOCAL_SITE; 464 465 } else if (0 == strncmp('111111100', $binip, 9)) { 466 467 return NET_IPV6_UNASSIGNED; 468 469 } else if (0 == strncmp('11111111', $binip, 8)) { 470 471 return NET_IPV6_MULTICAST; 472 473 } else if (0 == strncmp('00000000', $binip, 8)) { 474 475 return NET_IPV6_RESERVED; 476 477 } else if (0 == strncmp('00000001', $binip, 8) 478 || 0 == strncmp('1111110', $binip, 7)) { 479 480 return NET_IPV6_UNASSIGNED; 481 482 } else if (0 == strncmp('0000001', $binip, 7)) { 483 484 return NET_IPV6_RESERVED_NSAP; 485 486 } else if (0 == strncmp('0000010', $binip, 7)) { 487 488 return NET_IPV6_RESERVED_IPX; 489 490 } else if (0 == strncmp('0000011', $binip, 7) || 491 0 == strncmp('111110', $binip, 6) || 492 0 == strncmp('11110', $binip, 5) || 493 0 == strncmp('00001', $binip, 5) || 494 0 == strncmp('1110', $binip, 4) || 495 0 == strncmp('0001', $binip, 4) || 496 0 == strncmp('001', $binip, 3) || 497 0 == strncmp('011', $binip, 3) || 498 0 == strncmp('101', $binip, 3) || 499 0 == strncmp('110', $binip, 3)) { 500 501 return NET_IPV6_UNASSIGNED; 502 503 } else if (0 == strncmp('010', $binip, 3)) { 504 505 return NET_IPV6_UNICAST_PROVIDER; 506 507 } else if (0 == strncmp('100', $binip, 3)) { 508 509 return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC; 510 511 } 512 513 return NET_IPV6_UNKNOWN_TYPE; 514 } 515 516 // }}} 517 // {{{ Uncompress() 518 519 /** 520 * Uncompresses an IPv6 address 521 * 522 * RFC 2373 allows you to compress zeros in an address to '::'. This 523 * function expects a valid IPv6 address and expands the '::' to 524 * the required zeros. 525 * 526 * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 527 * ::1 -> 0:0:0:0:0:0:0:1 528 * 529 * @param String $ip a valid IPv6-address (hex format) 530 * @param Boolean $leadingZeros if true, leading zeros are added to each 531 * block of the address 532 * (FF01::101 -> 533 * FF01:0000:0000:0000:0000:0000:0000:0101) 534 * 535 * @return String the uncompressed IPv6-address (hex format) 536 * @access public 537 * @see Compress() 538 * @static 539 * @author Pascal Uhlmann 540 */ 541 public static function uncompress($ip, $leadingZeros = false) 542 { 543 544 $prefix = Net_IPv6::getPrefixLength($ip); 545 546 if (false === $prefix) { 547 548 $prefix = ''; 549 550 } else { 551 552 $ip = Net_IPv6::removeNetmaskSpec($ip); 553 $prefix = '/'.$prefix; 554 555 } 556 557 $netmask = Net_IPv6::getNetmaskSpec($ip); 558 $uip = Net_IPv6::removeNetmaskSpec($ip); 559 560 $c1 = -1; 561 $c2 = -1; 562 563 if (false !== strpos($uip, '::') ) { 564 565 list($ip1, $ip2) = explode('::', $uip, 2); 566 567 if ("" == $ip1) { 568 569 $c1 = -1; 570 571 } else { 572 573 $pos = 0; 574 575 if (0 < ($pos = substr_count($ip1, ':'))) { 576 577 $c1 = $pos; 578 579 } else { 580 581 $c1 = 0; 582 583 } 584 } 585 if ("" == $ip2) { 586 587 $c2 = -1; 588 589 } else { 590 591 $pos = 0; 592 593 if (0 < ($pos = substr_count($ip2, ':'))) { 594 595 $c2 = $pos; 596 597 } else { 598 599 $c2 = 0; 600 601 } 602 603 } 604 605 if (strstr($ip2, '.')) { 606 607 $c2++; 608 609 } 610 if (-1 == $c1 && -1 == $c2) { // :: 611 612 $uip = "0:0:0:0:0:0:0:0"; 613 614 } else if (-1 == $c1) { // ::xxx 615 616 $fill = str_repeat('0:', max(1, 7-$c2)); 617 $uip = str_replace('::', $fill, $uip); 618 619 } else if (-1 == $c2) { // xxx:: 620 621 $fill = str_repeat(':0', max(1, 7-$c1)); 622 $uip = str_replace('::', $fill, $uip); 623 624 } else { // xxx::xxx 625 626 $fill = str_repeat(':0:', 6-$c2-$c1); 627 $uip = str_replace('::', $fill, $uip); 628 $uip = str_replace('::', ':', $uip); 629 630 } 631 } 632 633 if(true == $leadingZeros) { 634 635 $uipT = array(); 636 $uiparts = explode(':', $uip); 637 638 foreach($uiparts as $p) { 639 640 $uipT[] = sprintf('%04s', $p); 641 642 } 643 644 $uip = implode(':', $uipT); 645 } 646 647 if ('' != $netmask) { 648 649 $uip = $uip.'/'.$netmask; 650 651 } 652 653 return $uip.$prefix; 654 } 655 656 // }}} 657 // {{{ Compress() 658 659 /** 660 * Compresses an IPv6 address 661 * 662 * RFC 2373 allows you to compress zeros in an address to '::'. This 663 * function expects a valid IPv6 address and compresses successive zeros 664 * to '::' 665 * 666 * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 667 * 0:0:0:0:0:0:0:1 -> ::1 668 * 669 * When $ip is an already compressed address and $force is false, the method returns 670 * the value as is, even if the address can be compressed further. 671 * 672 * Example: FF01::0:1 -> FF01::0:1 673 * 674 * To enforce maximum compression, you can set the second argument $force to true. 675 * 676 * Example: FF01::0:1 -> FF01::1 677 * 678 * @param String $ip a valid IPv6-address (hex format) 679 * @param boolean $force if true the address will be compressed as best as possible (since 1.2.0) 680 * 681 * @return String the compressed IPv6-address (hex format) 682 * @access public 683 * @see Uncompress() 684 * @static 685 * @author elfrink at introweb dot nl 686 */ 687 public static function compress($ip, $force = false) 688 { 689 690 if(false !== strpos($ip, '::')) { // its already compressed 691 692 if(true == $force) { 693 694 $ip = Net_IPv6::uncompress($ip); 695 696 } else { 697 698 return $ip; 699 700 } 701 702 } 703 704 $prefix = Net_IPv6::getPrefixLength($ip); 705 706 if (false === $prefix) { 707 708 $prefix = ''; 709 710 } else { 711 712 $ip = Net_IPv6::removeNetmaskSpec($ip); 713 $prefix = '/'.$prefix; 714 715 } 716 717 $netmask = Net_IPv6::getNetmaskSpec($ip); 718 $ip = Net_IPv6::removeNetmaskSpec($ip); 719 720 $ipp = explode(':', $ip); 721 722 for ($i = 0; $i < count($ipp); $i++) { 723 724 $ipp[$i] = dechex(hexdec($ipp[$i])); 725 726 } 727 728 $cip = ':' . join(':', $ipp) . ':'; 729 730 preg_match_all("/(:0)(:0)+/", $cip, $zeros); 731 732 if (count($zeros[0]) > 0) { 733 734 $match = ''; 735 736 foreach ($zeros[0] as $zero) { 737 738 if (strlen($zero) > strlen($match)) { 739 740 $match = $zero; 741 742 } 743 } 744 745 $cip = preg_replace('/' . $match . '/', ':', $cip, 1); 746 747 } 748 749 $cip = preg_replace('/((^:)|(:$))/', '', $cip); 750 $cip = preg_replace('/((^:)|(:$))/', '::', $cip); 751 752 if ('' != $netmask) { 753 754 $cip = $cip.'/'.$netmask; 755 756 } 757 758 return $cip.$prefix; 759 760 } 761 762 // }}} 763 // {{{ recommendedFormat() 764 /** 765 * Represent IPv6 address in RFC5952 format. 766 * 767 * @param String $ip a valid IPv6-address (hex format) 768 * 769 * @return String the recommended representation of IPv6-address (hex format) 770 * @access public 771 * @see compress() 772 * @static 773 * @author koyama at hoge dot org 774 * @todo This method may become a part of compress() in a further releases 775 */ 776 public static function recommendedFormat($ip) 777 { 778 $compressed = self::compress($ip, true); 779 // RFC5952 4.2.2 780 // The symbol "::" MUST NOT be used to shorten just one 781 // 16-bit 0 field. 782 if ((substr_count($compressed, ':') == 7) && 783 (strpos($compressed, '::') !== false)) { 784 $compressed = str_replace('::', ':0:', $compressed); 785 } 786 return $compressed; 787 } 788 // }}} 789 790 // {{{ isCompressible() 791 792 /** 793 * Checks, if an IPv6 address can be compressed 794 * 795 * @param String $ip a valid IPv6 address 796 * 797 * @return Boolean true, if address can be compressed 798 * @access public 799 * @since 1.2.0b 800 * @static 801 * @author Manuel Schmitt 802 */ 803 public static function isCompressible($ip) 804 { 805 806 return (bool)($ip != Net_IPv6::compress($address)); 807 808 } 809 810 // }}} 811 // {{{ SplitV64() 812 813 /** 814 * Splits an IPv6 address into the IPv6 and a possible IPv4 part 815 * 816 * RFC 2373 allows you to note the last two parts of an IPv6 address as 817 * an IPv4 compatible address 818 * 819 * Example: 0:0:0:0:0:0:13.1.68.3 820 * 0:0:0:0:0:FFFF:129.144.52.38 821 * 822 * @param String $ip a valid IPv6-address (hex format) 823 * @param Boolean $uncompress if true, the address will be uncompressed 824 * before processing 825 * 826 * @return Array [0] contains the IPv6 part, 827 * [1] the IPv4 part (hex format) 828 * @access public 829 * @static 830 */ 831 public static function SplitV64($ip, $uncompress = true) 832 { 833 $ip = Net_IPv6::removeNetmaskSpec($ip); 834 835 if ($uncompress) { 836 837 $ip = Net_IPv6::uncompress($ip); 838 839 } 840 841 if (strstr($ip, '.')) { 842 843 $pos = strrpos($ip, ':'); 844 845 if(false === $pos) { 846 return array("", $ip); 847 } 848 849 $ip{$pos} = '_'; 850 $ipPart = explode('_', $ip); 851 852 return $ipPart; 853 854 } else { 855 856 return array($ip, ""); 857 858 } 859 } 860 861 // }}} 862 // {{{ checkIPv6() 863 864 /** 865 * Checks an IPv6 address 866 * 867 * Checks if the given IP is IPv6-compatible 868 * 869 * @param String $ip a valid IPv6-address 870 * 871 * @return Boolean true if $ip is an IPv6 address 872 * @access public 873 * @static 874 */ 875 public static function checkIPv6($ip) 876 { 877 878 $elements = Net_IPv6::separate($ip); 879 880 $ip = $elements[0]; 881 882 if('' != $elements[1] && ( !is_numeric($elements[1]) || 0 > $elements || 128 < $elements[1])) { 883 884 return false; 885 886 } 887 888 $ipPart = Net_IPv6::SplitV64($ip); 889 $count = 0; 890 891 if (!empty($ipPart[0])) { 892 $ipv6 = explode(':', $ipPart[0]); 893 894 if(8 < count($ipv6)) { 895 return false; 896 } 897 898 foreach($ipv6 as $element) { // made a validate precheck 899 if(!preg_match('/[0-9a-fA-F]*/', $element)) { 900 return false; 901 } 902 } 903 904 for ($i = 0; $i < count($ipv6); $i++) { 905 906 if(4 < strlen($ipv6[$i])) { 907 908 return false; 909 910 } 911 912 $dec = hexdec($ipv6[$i]); 913 $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/", 914 "\\1", 915 $ipv6[$i])); 916 917 if ($ipv6[$i] >= 0 && $dec <= 65535 918 && $hex == strtoupper(dechex($dec))) { 919 920 $count++; 921 922 } 923 924 } 925 926 if (8 == $count and empty($ipPart[1])) { 927 928 return true; 929 930 } else if (6 == $count and !empty($ipPart[1])) { 931 932 $ipv4 = explode('.', $ipPart[1]); 933 $count = 0; 934 935 for ($i = 0; $i < count($ipv4); $i++) { 936 937 if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255 938 && preg_match("/^\d{1,3}$/", $ipv4[$i])) { 939 940 $count++; 941 942 } 943 944 } 945 946 if (4 == $count) { 947 948 return true; 949 950 } 951 952 } else { 953 954 return false; 955 956 } 957 958 } else { 959 960 return false; 961 962 } 963 964 } 965 966 // }}} 967 968 // {{{ _parseAddress() 969 970 /** 971 * Returns the lowest and highest IPv6 address 972 * for a given IP and netmask specification 973 * 974 * The netmask may be a part of the $ip or 975 * the number of netmask bits is provided via $bits 976 * 977 * The result is an indexed array. The key 'start' 978 * contains the lowest possible IP address. The key 979 * 'end' the highest address. 980 * 981 * @param String $ipToParse the IPv6 address 982 * @param String $bits the optional count of netmask bits 983 * 984 * @return Array ['start', 'end'] the lowest and highest IPv6 address 985 * @access public 986 * @static 987 * @author Nicholas Williams 988 */ 989 990 public static function parseAddress($ipToParse, $bits = null) 991 { 992 993 $ip = null; 994 $bitmask = null; 995 996 if ( null == $bits ) { 997 998 $elements = explode('/', $ipToParse); 999 1000 if ( 2 == count($elements) ) { 1001 1002 $ip = Net_IPv6::uncompress($elements[0]); 1003 $bitmask = $elements[1]; 1004 1005 } else { 1006 1007 include_once 'PEAR.php'; 1008 1009 return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, 1010 NET_IPV6_NO_NETMASK); 1011 } 1012 } else { 1013 1014 $ip = Net_IPv6::uncompress($ipToParse); 1015 $bitmask = $bits; 1016 1017 } 1018 1019 $binNetmask = str_repeat('1', $bitmask). 1020 str_repeat('0', 128 - $bitmask); 1021 $maxNetmask = str_repeat('1', 128); 1022 $netmask = Net_IPv6::_bin2Ip($binNetmask); 1023 1024 $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 1025 & $binNetmask); 1026 $endAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 1027 | ($binNetmask ^ $maxNetmask)); 1028 1029 return array('start' => $startAddress, 'end' => $endAddress); 1030 } 1031 1032 // }}} 1033 1034 // {{{ _ip2Bin() 1035 1036 /** 1037 * Converts an IPv6 address from Hex into Binary representation. 1038 * 1039 * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 1040 * compressed IPs are allowed 1041 * 1042 * @return String the binary representation 1043 * @access private 1044 * @since 1.1.0 1045 */ 1046 protected static function _ip2Bin($ip) 1047 { 1048 $binstr = ''; 1049 1050 $ip = Net_IPv6::removeNetmaskSpec($ip); 1051 1052 // Correctly convert IPv4 mapped addresses 1053 // Added by Observium developers 1054 list(, $ipv4) = Net_IPv6::SplitV64($ip, FALSE); 1055 if (strlen($ipv4)) 1056 { 1057 $ipv4map = explode('.', $ipv4, 4); 1058 $ipv4replace = dechex($ipv4map[0] * 256 + $ipv4map[1]) . ':' . dechex($ipv4map[2] * 256 + $ipv4map[3]); 1059 $ip = str_replace($ipv4, $ipv4replace, $ip); 1060 } 1061 1062 $ip = Net_IPv6::Uncompress($ip); 1063 1064 $parts = explode(':', $ip); 1065 1066 foreach ( $parts as $v ) { 1067 1068 $str = base_convert($v, 16, 2); 1069 $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT); 1070 1071 } 1072 1073 return $binstr; 1074 } 1075 1076 // }}} 1077 // {{{ _bin2Ip() 1078 1079 /** 1080 * Converts an IPv6 address from Binary into Hex representation. 1081 * 1082 * @param String $bin the IP address as binary 1083 * 1084 * @return String the uncompressed Hex representation 1085 * @access private 1086 * @since 1.1.0 1087 */ 1088 protected static function _bin2Ip($bin) 1089 { 1090 $ip = ""; 1091 1092 if (strlen($bin) < 128) { 1093 1094 $bin = str_pad($bin, 128, '0', STR_PAD_LEFT); 1095 1096 } 1097 1098 $parts = str_split($bin, "16"); 1099 1100 foreach ( $parts as $v ) { 1101 1102 $str = base_convert($v, 2, 16); 1103 $ip .= $str.":"; 1104 1105 } 1106 1107 $ip = substr($ip, 0, -1); 1108 1109 return $ip; 1110 } 1111 1112 // }}} 1113} 1114// }}} 1115 1116/* 1117 * Local variables: 1118 * tab-width: 4 1119 * c-basic-offset: 4 1120 * c-hanging-comment-ender-p: nil 1121 * End: 1122 */ 1123 1124?> 1125