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 338818 2016-03-25 12:15:02Z 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 addresse. 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 addresse. 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 contains 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 } 368 369 if (null == $bits) { 370 371 include_once 'PEAR.php'; 372 return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, 373 NET_IPV6_NO_NETMASK); 374 375 } 376 377 } 378 379 } 380 381 $binIp = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip)); 382 $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask)); 383 384 if (null != $bits 385 && "" != $bits 386 && 0 == strncmp($binNetmask, $binIp, $bits)) { 387 388 return true; 389 390 } 391 392 return false; 393 } 394 395 // }}} 396 // {{{ getAddressType() 397 398 /** 399 * Returns the type of an IPv6 address. 400 * 401 * RFC 2373, Section 2.3 describes several types of addresses in 402 * the IPv6 addresse space. 403 * Several addresse types are markers for reserved spaces and as 404 * consequence a subject to change. 405 * 406 * @param String $ip the IP address in Hex format, 407 * compressed IPs are allowed 408 * 409 * @return int one of the addresse type constants 410 * @access public 411 * @since 1.1.0 412 * @static 413 * 414 * @see NET_IPV6_UNASSIGNED 415 * @see NET_IPV6_RESERVED 416 * @see NET_IPV6_RESERVED_NSAP 417 * @see NET_IPV6_RESERVED_IPX 418 * @see NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC 419 * @see NET_IPV6_UNICAST_PROVIDER 420 * @see NET_IPV6_MULTICAST 421 * @see NET_IPV6_LOCAL_LINK 422 * @see NET_IPV6_LOCAL_SITE 423 * @see NET_IPV6_IPV4MAPPING 424 * @see NET_IPV6_UNSPECIFIED 425 * @see NET_IPV6_LOOPBACK 426 * @see NET_IPV6_UNKNOWN_TYPE 427 */ 428 public static function getAddressType($ip) 429 { 430 $ip = Net_IPv6::removeNetmaskSpec($ip); 431 $binip = Net_IPv6::_ip2Bin($ip); 432 433 if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128 434 435 return NET_IPV6_UNSPECIFIED; 436 437 } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128 438 439 return NET_IPV6_LOOPBACK; 440 441 } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96 442 443 return NET_IPV6_IPV4MAPPING; 444 445 } else if (0 == strncmp('1111111010', $binip, 10)) { 446 447 return NET_IPV6_LOCAL_LINK; 448 449 } else if (0 == strncmp('1111111011', $binip, 10)) { 450 451 return NET_IPV6_LOCAL_SITE; 452 453 } else if (0 == strncmp('111111100', $binip, 9)) { 454 455 return NET_IPV6_UNASSIGNED; 456 457 } else if (0 == strncmp('11111111', $binip, 8)) { 458 459 return NET_IPV6_MULTICAST; 460 461 } else if (0 == strncmp('00000000', $binip, 8)) { 462 463 return NET_IPV6_RESERVED; 464 465 } else if (0 == strncmp('00000001', $binip, 8) 466 || 0 == strncmp('1111110', $binip, 7)) { 467 468 return NET_IPV6_UNASSIGNED; 469 470 } else if (0 == strncmp('0000001', $binip, 7)) { 471 472 return NET_IPV6_RESERVED_NSAP; 473 474 } else if (0 == strncmp('0000010', $binip, 7)) { 475 476 return NET_IPV6_RESERVED_IPX;; 477 478 } else if (0 == strncmp('0000011', $binip, 7) || 479 0 == strncmp('111110', $binip, 6) || 480 0 == strncmp('11110', $binip, 5) || 481 0 == strncmp('00001', $binip, 5) || 482 0 == strncmp('1110', $binip, 4) || 483 0 == strncmp('0001', $binip, 4) || 484 0 == strncmp('001', $binip, 3) || 485 0 == strncmp('011', $binip, 3) || 486 0 == strncmp('101', $binip, 3) || 487 0 == strncmp('110', $binip, 3)) { 488 489 return NET_IPV6_UNASSIGNED; 490 491 } else if (0 == strncmp('010', $binip, 3)) { 492 493 return NET_IPV6_UNICAST_PROVIDER; 494 495 } else if (0 == strncmp('100', $binip, 3)) { 496 497 return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC; 498 499 } 500 501 return NET_IPV6_UNKNOWN_TYPE; 502 } 503 504 // }}} 505 // {{{ Uncompress() 506 507 /** 508 * Uncompresses an IPv6 adress 509 * 510 * RFC 2373 allows you to compress zeros in an adress to '::'. This 511 * function expects an valid IPv6 adress and expands the '::' to 512 * the required zeros. 513 * 514 * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 515 * ::1 -> 0:0:0:0:0:0:0:1 516 * 517 * @param String $ip a valid IPv6-adress (hex format) 518 * @param Boolean $leadingZeros if true, leading zeros are added to each 519 * block of the address 520 * (FF01::101 -> 521 * FF01:0000:0000:0000:0000:0000:0000:0101) 522 * 523 * @return String the uncompressed IPv6-adress (hex format) 524 * @access public 525 * @see Compress() 526 * @static 527 * @author Pascal Uhlmann 528 */ 529 public static function uncompress($ip, $leadingZeros = false) 530 { 531 532 $prefix = Net_IPv6::getPrefixLength($ip); 533 534 if (false === $prefix) { 535 536 $prefix = ''; 537 538 } else { 539 540 $ip = Net_IPv6::removePrefixLength($ip); 541 $prefix = '/'.$prefix; 542 543 } 544 545 $netmask = Net_IPv6::getNetmaskSpec($ip); 546 $uip = Net_IPv6::removeNetmaskSpec($ip); 547 548 $c1 = -1; 549 $c2 = -1; 550 551 if (false !== strpos($uip, '::') ) { 552 553 list($ip1, $ip2) = explode('::', $uip, 2); 554 555 if ("" == $ip1) { 556 557 $c1 = -1; 558 559 } else { 560 561 $pos = 0; 562 563 if (0 < ($pos = substr_count($ip1, ':'))) { 564 565 $c1 = $pos; 566 567 } else { 568 569 $c1 = 0; 570 571 } 572 } 573 if ("" == $ip2) { 574 575 $c2 = -1; 576 577 } else { 578 579 $pos = 0; 580 581 if (0 < ($pos = substr_count($ip2, ':'))) { 582 583 $c2 = $pos; 584 585 } else { 586 587 $c2 = 0; 588 589 } 590 591 } 592 593 if (strstr($ip2, '.')) { 594 595 $c2++; 596 597 } 598 if (-1 == $c1 && -1 == $c2) { // :: 599 600 $uip = "0:0:0:0:0:0:0:0"; 601 602 } else if (-1 == $c1) { // ::xxx 603 604 $fill = str_repeat('0:', max(1, 7-$c2)); 605 $uip = str_replace('::', $fill, $uip); 606 607 } else if (-1 == $c2) { // xxx:: 608 609 $fill = str_repeat(':0', max(1, 7-$c1)); 610 $uip = str_replace('::', $fill, $uip); 611 612 } else { // xxx::xxx 613 614 $fill = str_repeat(':0:', 6-$c2-$c1); 615 $uip = str_replace('::', $fill, $uip); 616 $uip = str_replace('::', ':', $uip); 617 618 } 619 } 620 621 if(true == $leadingZeros) { 622 623 $uipT = array(); 624 $uiparts = explode(':', $uip); 625 626 foreach($uiparts as $p) { 627 628 $uipT[] = sprintf('%04s', $p); 629 630 } 631 632 $uip = implode(':', $uipT); 633 } 634 635 if ('' != $netmask) { 636 637 $uip = $uip.'/'.$netmask; 638 639 } 640 641 return $uip.$prefix; 642 } 643 644 // }}} 645 // {{{ Compress() 646 647 /** 648 * Compresses an IPv6 adress 649 * 650 * RFC 2373 allows you to compress zeros in an adress to '::'. This 651 * function expects an valid IPv6 adress and compresses successive zeros 652 * to '::' 653 * 654 * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 655 * 0:0:0:0:0:0:0:1 -> ::1 656 * 657 * Whe $ip is an already compressed adress the methode returns the value as is, 658 * also if the adress can be compressed further. 659 * 660 * Example: FF01::0:1 -> FF01::0:1 661 * 662 * To enforce maximum compression, you can set the second argument $force to true. 663 * 664 * Example: FF01::0:1 -> FF01::1 665 * 666 * @param String $ip a valid IPv6-adress (hex format) 667 * @param boolean $force if true the adress will be compresses as best as possible (since 1.2.0) 668 * 669 * @return tring the compressed IPv6-adress (hex format) 670 * @access public 671 * @see Uncompress() 672 * @static 673 * @author elfrink at introweb dot nl 674 */ 675 public static function compress($ip, $force = false) 676 { 677 678 if(false !== strpos($ip, '::')) { // its already compressed 679 680 if(true == $force) { 681 682 $ip = Net_IPv6::uncompress($ip); 683 684 } else { 685 686 return $ip; 687 688 } 689 690 } 691 692 $prefix = Net_IPv6::getPrefixLength($ip); 693 694 if (false === $prefix) { 695 696 $prefix = ''; 697 698 } else { 699 700 $ip = Net_IPv6::removePrefixLength($ip); 701 $prefix = '/'.$prefix; 702 703 } 704 705 $netmask = Net_IPv6::getNetmaskSpec($ip); 706 $ip = Net_IPv6::removeNetmaskSpec($ip); 707 708 $ipp = explode(':', $ip); 709 710 for ($i = 0; $i < count($ipp); $i++) { 711 712 $ipp[$i] = dechex(hexdec($ipp[$i])); 713 714 } 715 716 $cip = ':' . join(':', $ipp) . ':'; 717 718 preg_match_all("/(:0)(:0)+/", $cip, $zeros); 719 720 if (count($zeros[0]) > 0) { 721 722 $match = ''; 723 724 foreach ($zeros[0] as $zero) { 725 726 if (strlen($zero) > strlen($match)) { 727 728 $match = $zero; 729 730 } 731 } 732 733 $cip = preg_replace('/' . $match . '/', ':', $cip, 1); 734 735 } 736 737 if ($cip != "::") { 738 $cip = preg_replace('/((^:)|(:$))/', '', $cip); 739 $cip = preg_replace('/((^:)|(:$))/', '::', $cip); 740 } 741 742 if ('' != $netmask) { 743 744 $cip = $cip.'/'.$netmask; 745 746 } 747 748 return $cip.$prefix; 749 750 } 751 752 // }}} 753 // {{{ recommendedFormat() 754 /** 755 * Represent IPv6 address in RFC5952 format. 756 * 757 * @param String $ip a valid IPv6-adress (hex format) 758 * 759 * @return String the recommended representation of IPv6-adress (hex format) 760 * @access public 761 * @see compress() 762 * @static 763 * @author koyama at hoge dot org 764 * @todo This method may become a part of compress() in a further releases 765 */ 766 public static function recommendedFormat($ip) 767 { 768 $compressed = self::compress($ip, true); 769 // RFC5952 4.2.2 770 // The symbol "::" MUST NOT be used to shorten just one 771 // 16-bit 0 field. 772 if ((substr_count($compressed, ':') == 7) && 773 (strpos($compressed, '::') !== false)) { 774 $compressed = str_replace('::', ':0:', $compressed); 775 } 776 return $compressed; 777 } 778 // }}} 779 780 // {{{ isCompressible() 781 782 /** 783 * Checks, if an IPv6 adress can be compressed 784 * 785 * @param String $ip a valid IPv6 adress 786 * 787 * @return Boolean true, if adress can be compressed 788 * 789 * @access public 790 * @since 1.2.0b 791 * @static 792 * @author Manuel Schmitt 793 */ 794 public static function isCompressible($ip) 795 { 796 797 return (bool)($ip != Net_IPv6::compress($address)); 798 799 } 800 801 // }}} 802 // {{{ SplitV64() 803 804 /** 805 * Splits an IPv6 adress into the IPv6 and a possible IPv4 part 806 * 807 * RFC 2373 allows you to note the last two parts of an IPv6 adress as 808 * an IPv4 compatible adress 809 * 810 * Example: 0:0:0:0:0:0:13.1.68.3 811 * 0:0:0:0:0:FFFF:129.144.52.38 812 * 813 * @param String $ip a valid IPv6-adress (hex format) 814 * @param Boolean $uncompress if true, the address will be uncompressed 815 * before processing 816 * 817 * @return Array [0] contains the IPv6 part, 818 * [1] the IPv4 part (hex format) 819 * @access public 820 * @static 821 */ 822 public static function SplitV64($ip, $uncompress = true) 823 { 824 $ip = Net_IPv6::removeNetmaskSpec($ip); 825 826 if ($uncompress) { 827 828 $ip = Net_IPv6::Uncompress($ip); 829 830 } 831 832 if (strstr($ip, '.')) { 833 834 $pos = strrpos($ip, ':'); 835 836 if(false === $pos) { 837 return array("", $ip); 838 } 839 840 $ip{$pos} = '_'; 841 $ipPart = explode('_', $ip); 842 843 return $ipPart; 844 845 } else { 846 847 return array($ip, ""); 848 849 } 850 } 851 852 // }}} 853 // {{{ checkIPv6() 854 855 /** 856 * Checks an IPv6 adress 857 * 858 * Checks if the given IP is IPv6-compatible 859 * 860 * @param String $ip a valid IPv6-adress 861 * 862 * @return Boolean true if $ip is an IPv6 adress 863 * @access public 864 * @static 865 */ 866 public static function checkIPv6($ip) 867 { 868 869 $elements = Net_IPv6::separate($ip); 870 871 $ip = $elements[0]; 872 873 if('' != $elements[1] && ( !is_numeric($elements[1]) || 0 > $elements || 128 < $elements[1])) { 874 875 return false; 876 877 } 878 879 $ipPart = Net_IPv6::SplitV64($ip); 880 $count = 0; 881 882 if (!empty($ipPart[0])) { 883 $ipv6 = explode(':', $ipPart[0]); 884 885 if(8 < count($ipv6)) { 886 return false; 887 } 888 889 foreach($ipv6 as $element) { // made a validate precheck 890 if(!preg_match('/[0-9a-fA-F]*/', $element)) { 891 return false; 892 } 893 } 894 895 for ($i = 0; $i < count($ipv6); $i++) { 896 897 if(4 < strlen($ipv6[$i])) { 898 899 return false; 900 901 } 902 903 $dec = hexdec($ipv6[$i]); 904 $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/", 905 "\\1", 906 $ipv6[$i])); 907 908 if ($ipv6[$i] >= 0 && $dec <= 65535 909 && $hex == strtoupper(dechex($dec))) { 910 911 $count++; 912 913 } 914 915 } 916 917 if (8 == $count and empty($ipPart[1])) { 918 919 return true; 920 921 } else if (6 == $count and !empty($ipPart[1])) { 922 923 $ipv4 = explode('.', $ipPart[1]); 924 $count = 0; 925 926 for ($i = 0; $i < count($ipv4); $i++) { 927 928 if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255 929 && preg_match("/^\d{1,3}$/", $ipv4[$i])) { 930 931 $count++; 932 933 } 934 935 } 936 937 if (4 == $count) { 938 939 return true; 940 941 } 942 943 } else { 944 945 return false; 946 947 } 948 949 } else { 950 951 return false; 952 953 } 954 955 } 956 957 // }}} 958 959 // {{{ _parseAddress() 960 961 /** 962 * Returns the lowest and highest IPv6 address 963 * for a given IP and netmask specification 964 * 965 * The netmask may be a part of the $ip or 966 * the number of netwask bits is provided via $bits 967 * 968 * The result is an indexed array. The key 'start' 969 * contains the lowest possible IP adress. The key 970 * 'end' the highest address. 971 * 972 * @param String $ipToParse the IPv6 address 973 * @param String $bits the optional count of netmask bits 974 * 975 * @return Array ['start', 'end'] the lowest and highest IPv6 address 976 * @access public 977 * @static 978 * @author Nicholas Williams 979 */ 980 981 public static function parseAddress($ipToParse, $bits = null) 982 { 983 984 $ip = null; 985 $bitmask = null; 986 987 if ( null == $bits ) { 988 989 $elements = explode('/', $ipToParse); 990 991 if ( 2 == count($elements) ) { 992 993 $ip = Net_IPv6::uncompress($elements[0]); 994 $bitmask = $elements[1]; 995 996 } else { 997 998 include_once 'PEAR.php'; 999 1000 return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, 1001 NET_IPV6_NO_NETMASK); 1002 } 1003 } else { 1004 1005 $ip = Net_IPv6::uncompress($ipToParse); 1006 $bitmask = $bits; 1007 1008 } 1009 1010 $binNetmask = str_repeat('1', $bitmask). 1011 str_repeat('0', 128 - $bitmask); 1012 $maxNetmask = str_repeat('1', 128); 1013 $netmask = Net_IPv6::_bin2Ip($binNetmask); 1014 1015 $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 1016 & $binNetmask); 1017 $endAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 1018 | ($binNetmask ^ $maxNetmask)); 1019 1020 return array('start' => $startAddress, 'end' => $endAddress); 1021 } 1022 1023 // }}} 1024 1025 // {{{ _ip2Bin() 1026 1027 /** 1028 * Converts an IPv6 address from Hex into Binary representation. 1029 * 1030 * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 1031 * compressed IPs are allowed 1032 * 1033 * @return String the binary representation 1034 * @access private 1035 @ @since 1.1.0 1036 */ 1037 protected static function _ip2Bin($ip) 1038 { 1039 $binstr = ''; 1040 1041 $ip = Net_IPv6::removeNetmaskSpec($ip); 1042 $ip = Net_IPv6::Uncompress($ip); 1043 1044 $parts = explode(':', $ip); 1045 1046 foreach ( $parts as $v ) { 1047 1048 $str = base_convert($v, 16, 2); 1049 $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT); 1050 1051 } 1052 1053 return $binstr; 1054 } 1055 1056 // }}} 1057 // {{{ _bin2Ip() 1058 1059 /** 1060 * Converts an IPv6 address from Binary into Hex representation. 1061 * 1062 * @param String $bin the IP address as binary 1063 * 1064 * @return String the uncompressed Hex representation 1065 * @access private 1066 @ @since 1.1.0 1067 */ 1068 protected static function _bin2Ip($bin) 1069 { 1070 $ip = ""; 1071 1072 if (strlen($bin) < 128) { 1073 1074 $bin = str_pad($bin, 128, '0', STR_PAD_LEFT); 1075 1076 } 1077 1078 $parts = str_split($bin, "16"); 1079 1080 foreach ( $parts as $v ) { 1081 1082 $str = base_convert($v, 2, 16); 1083 $ip .= $str.":"; 1084 1085 } 1086 1087 $ip = substr($ip, 0, -1); 1088 1089 return $ip; 1090 } 1091 1092 // }}} 1093} 1094// }}} 1095 1096/* 1097 * Local variables: 1098 * tab-width: 4 1099 * c-basic-offset: 4 1100 * c-hanging-comment-ender-p: nil 1101 * End: 1102 */ 1103 1104?> 1105