1<?php 2 3declare(strict_types=1); 4/** 5 * SimplePie 6 * 7 * A PHP-Based RSS and Atom Feed Framework. 8 * Takes the hard work out of managing a complete RSS/Atom solution. 9 * 10 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without modification, are 14 * permitted provided that the following conditions are met: 15 * 16 * * Redistributions of source code must retain the above copyright notice, this list of 17 * conditions and the following disclaimer. 18 * 19 * * Redistributions in binary form must reproduce the above copyright notice, this list 20 * of conditions and the following disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * * Neither the name of the SimplePie Team nor the names of its contributors may be used 24 * to endorse or promote products derived from this software without specific prior 25 * written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 28 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 29 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 30 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 * 37 * @package SimplePie 38 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue 39 * @author Ryan Parman 40 * @author Sam Sneddon 41 * @author Ryan McCue 42 * @link http://simplepie.org/ SimplePie 43 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 44 */ 45 46namespace SimplePie\Net; 47 48/** 49 * Class to validate and to work with IPv6 addresses. 50 * 51 * @package SimplePie 52 * @subpackage HTTP 53 * @copyright 2003-2005 The PHP Group 54 * @license http://www.opensource.org/licenses/bsd-license.php 55 * @link http://pear.php.net/package/Net_IPv6 56 * @author Alexander Merz <alexander.merz@web.de> 57 * @author elfrink at introweb dot nl 58 * @author Josh Peck <jmp at joshpeck dot org> 59 * @author Sam Sneddon <geoffers@gmail.com> 60 */ 61class IPv6 62{ 63 /** 64 * Uncompresses an IPv6 address 65 * 66 * RFC 4291 allows you to compress concecutive zero pieces in an address to 67 * '::'. This method expects a valid IPv6 address and expands the '::' to 68 * the required number of zero pieces. 69 * 70 * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 71 * ::1 -> 0:0:0:0:0:0:0:1 72 * 73 * @author Alexander Merz <alexander.merz@web.de> 74 * @author elfrink at introweb dot nl 75 * @author Josh Peck <jmp at joshpeck dot org> 76 * @copyright 2003-2005 The PHP Group 77 * @license http://www.opensource.org/licenses/bsd-license.php 78 * @param string $ip An IPv6 address 79 * @return string The uncompressed IPv6 address 80 */ 81 public static function uncompress($ip) 82 { 83 $c1 = -1; 84 $c2 = -1; 85 if (substr_count($ip, '::') === 1) { 86 [$ip1, $ip2] = explode('::', $ip); 87 if ($ip1 === '') { 88 $c1 = -1; 89 } else { 90 $c1 = substr_count($ip1, ':'); 91 } 92 if ($ip2 === '') { 93 $c2 = -1; 94 } else { 95 $c2 = substr_count($ip2, ':'); 96 } 97 if (strpos($ip2, '.') !== false) { 98 $c2++; 99 } 100 // :: 101 if ($c1 === -1 && $c2 === -1) { 102 $ip = '0:0:0:0:0:0:0:0'; 103 } 104 // ::xxx 105 elseif ($c1 === -1) { 106 $fill = str_repeat('0:', 7 - $c2); 107 $ip = str_replace('::', $fill, $ip); 108 } 109 // xxx:: 110 elseif ($c2 === -1) { 111 $fill = str_repeat(':0', 7 - $c1); 112 $ip = str_replace('::', $fill, $ip); 113 } 114 // xxx::xxx 115 else { 116 $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); 117 $ip = str_replace('::', $fill, $ip); 118 } 119 } 120 return $ip; 121 } 122 123 /** 124 * Compresses an IPv6 address 125 * 126 * RFC 4291 allows you to compress concecutive zero pieces in an address to 127 * '::'. This method expects a valid IPv6 address and compresses consecutive 128 * zero pieces to '::'. 129 * 130 * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 131 * 0:0:0:0:0:0:0:1 -> ::1 132 * 133 * @see uncompress() 134 * @param string $ip An IPv6 address 135 * @return string The compressed IPv6 address 136 */ 137 public static function compress($ip) 138 { 139 // Prepare the IP to be compressed 140 $ip = self::uncompress($ip); 141 $ip_parts = self::split_v6_v4($ip); 142 143 // Replace all leading zeros 144 $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); 145 146 // Find bunches of zeros 147 if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) { 148 $max = 0; 149 $pos = null; 150 foreach ($matches[0] as $match) { 151 if (strlen($match[0]) > $max) { 152 $max = strlen($match[0]); 153 $pos = $match[1]; 154 } 155 } 156 157 $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); 158 } 159 160 if ($ip_parts[1] !== '') { 161 return implode(':', $ip_parts); 162 } 163 164 return $ip_parts[0]; 165 } 166 167 /** 168 * Splits an IPv6 address into the IPv6 and IPv4 representation parts 169 * 170 * RFC 4291 allows you to represent the last two parts of an IPv6 address 171 * using the standard IPv4 representation 172 * 173 * Example: 0:0:0:0:0:0:13.1.68.3 174 * 0:0:0:0:0:FFFF:129.144.52.38 175 * 176 * @param string $ip An IPv6 address 177 * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part 178 */ 179 private static function split_v6_v4($ip) 180 { 181 if (strpos($ip, '.') !== false) { 182 $pos = strrpos($ip, ':'); 183 $ipv6_part = substr($ip, 0, $pos); 184 $ipv4_part = substr($ip, $pos + 1); 185 return [$ipv6_part, $ipv4_part]; 186 } 187 188 return [$ip, '']; 189 } 190 191 /** 192 * Checks an IPv6 address 193 * 194 * Checks if the given IP is a valid IPv6 address 195 * 196 * @param string $ip An IPv6 address 197 * @return bool true if $ip is a valid IPv6 address 198 */ 199 public static function check_ipv6($ip) 200 { 201 $ip = self::uncompress($ip); 202 [$ipv6, $ipv4] = self::split_v6_v4($ip); 203 $ipv6 = explode(':', $ipv6); 204 $ipv4 = explode('.', $ipv4); 205 if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) { 206 foreach ($ipv6 as $ipv6_part) { 207 // The section can't be empty 208 if ($ipv6_part === '') { 209 return false; 210 } 211 212 // Nor can it be over four characters 213 if (strlen($ipv6_part) > 4) { 214 return false; 215 } 216 217 // Remove leading zeros (this is safe because of the above) 218 $ipv6_part = ltrim($ipv6_part, '0'); 219 if ($ipv6_part === '') { 220 $ipv6_part = '0'; 221 } 222 223 // Check the value is valid 224 $value = hexdec($ipv6_part); 225 if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) { 226 return false; 227 } 228 } 229 if (count($ipv4) === 4) { 230 foreach ($ipv4 as $ipv4_part) { 231 $value = (int) $ipv4_part; 232 if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) { 233 return false; 234 } 235 } 236 } 237 return true; 238 } 239 240 return false; 241 } 242 243 /** 244 * Checks if the given IP is a valid IPv6 address 245 * 246 * @codeCoverageIgnore 247 * @deprecated Use {@see IPv6::check_ipv6()} instead 248 * @see check_ipv6 249 * @param string $ip An IPv6 address 250 * @return bool true if $ip is a valid IPv6 address 251 */ 252 public static function checkIPv6($ip) 253 { 254 return self::check_ipv6($ip); 255 } 256} 257 258class_alias('SimplePie\Net\IPv6', 'SimplePie_Net_IPv6'); 259