[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework Utilities Package 4 * 5 * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE.txt 7 * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. 8 */ 9 10 namespace Joomla\Utilities; 11 12 /** 13 * IpHelper is a utility class for processing IP addresses 14 * 15 * @since 1.6.0 16 */ 17 final class IpHelper 18 { 19 /** 20 * The IP address of the current visitor 21 * 22 * @var string 23 * @since 1.6.0 24 */ 25 private static $ip = null; 26 27 /** 28 * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers? 29 * 30 * @var boolean 31 * @since 1.6.0 32 * @note The default value is false in version 2.0+ 33 */ 34 private static $allowIpOverrides = false; 35 36 /** 37 * Private constructor to prevent instantiation of this class 38 * 39 * @since 1.6.0 40 */ 41 private function __construct() 42 { 43 } 44 45 /** 46 * Get the current visitor's IP address 47 * 48 * @return string 49 * 50 * @since 1.6.0 51 */ 52 public static function getIp() 53 { 54 if (self::$ip === null) 55 { 56 $ip = static::detectAndCleanIP(); 57 58 if (!empty($ip) && ($ip != '0.0.0.0') && \function_exists('inet_pton') && \function_exists('inet_ntop')) 59 { 60 $myIP = @inet_pton($ip); 61 62 if ($myIP !== false) 63 { 64 $ip = inet_ntop($myIP); 65 } 66 } 67 68 static::setIp($ip); 69 } 70 71 return self::$ip; 72 } 73 74 /** 75 * Set the IP address of the current visitor 76 * 77 * @param string $ip The visitor's IP address 78 * 79 * @return void 80 * 81 * @since 1.6.0 82 */ 83 public static function setIp($ip) 84 { 85 self::$ip = $ip; 86 } 87 88 /** 89 * Is it an IPv6 IP address? 90 * 91 * @param string $ip An IPv4 or IPv6 address 92 * 93 * @return boolean 94 * 95 * @since 1.6.0 96 */ 97 public static function isIPv6($ip) 98 { 99 return strpos($ip, ':') !== false; 100 } 101 102 /** 103 * Checks if an IP is contained in a list of IPs or IP expressions 104 * 105 * @param string $ip The IPv4/IPv6 address to check 106 * @param array|string $ipTable An IP expression (or a comma-separated or array list of IP expressions) to check against 107 * 108 * @return boolean 109 * 110 * @since 1.6.0 111 */ 112 public static function IPinList($ip, $ipTable = '') 113 { 114 // No point proceeding with an empty IP list 115 if (empty($ipTable)) 116 { 117 return false; 118 } 119 120 // If the IP list is not an array, convert it to an array 121 if (!\is_array($ipTable)) 122 { 123 if (strpos($ipTable, ',') !== false) 124 { 125 $ipTable = explode(',', $ipTable); 126 $ipTable = array_map('trim', $ipTable); 127 } 128 else 129 { 130 $ipTable = trim($ipTable); 131 $ipTable = array($ipTable); 132 } 133 } 134 135 // If no IP address is found, return false 136 if ($ip === '0.0.0.0') 137 { 138 return false; 139 } 140 141 // If no IP is given, return false 142 if (empty($ip)) 143 { 144 return false; 145 } 146 147 // Sanity check 148 if (!\function_exists('inet_pton')) 149 { 150 return false; 151 } 152 153 // Get the IP's in_adds representation 154 $myIP = @inet_pton($ip); 155 156 // If the IP is in an unrecognisable format, quite 157 if ($myIP === false) 158 { 159 return false; 160 } 161 162 $ipv6 = static::isIPv6($ip); 163 164 foreach ($ipTable as $ipExpression) 165 { 166 $ipExpression = trim($ipExpression); 167 168 // Inclusive IP range, i.e. 123.123.123.123-124.125.126.127 169 if (strstr($ipExpression, '-')) 170 { 171 list($from, $to) = explode('-', $ipExpression, 2); 172 173 if ($ipv6 && (!static::isIPv6($from) || !static::isIPv6($to))) 174 { 175 // Do not apply IPv4 filtering on an IPv6 address 176 continue; 177 } 178 179 if (!$ipv6 && (static::isIPv6($from) || static::isIPv6($to))) 180 { 181 // Do not apply IPv6 filtering on an IPv4 address 182 continue; 183 } 184 185 $from = @inet_pton(trim($from)); 186 $to = @inet_pton(trim($to)); 187 188 // Sanity check 189 if (($from === false) || ($to === false)) 190 { 191 continue; 192 } 193 194 // Swap from/to if they're in the wrong order 195 if ($from > $to) 196 { 197 list($from, $to) = array($to, $from); 198 } 199 200 if (($myIP >= $from) && ($myIP <= $to)) 201 { 202 return true; 203 } 204 } 205 // Netmask or CIDR provided 206 elseif (strstr($ipExpression, '/')) 207 { 208 $binaryip = static::inetToBits($myIP); 209 210 list($net, $maskbits) = explode('/', $ipExpression, 2); 211 212 if ($ipv6 && !static::isIPv6($net)) 213 { 214 // Do not apply IPv4 filtering on an IPv6 address 215 continue; 216 } 217 218 if (!$ipv6 && static::isIPv6($net)) 219 { 220 // Do not apply IPv6 filtering on an IPv4 address 221 continue; 222 } 223 224 if ($ipv6 && strstr($maskbits, ':')) 225 { 226 // Perform an IPv6 CIDR check 227 if (static::checkIPv6CIDR($myIP, $ipExpression)) 228 { 229 return true; 230 } 231 232 // If we didn't match it proceed to the next expression 233 continue; 234 } 235 236 if (!$ipv6 && strstr($maskbits, '.')) 237 { 238 // Convert IPv4 netmask to CIDR 239 $long = ip2long($maskbits); 240 $base = ip2long('255.255.255.255'); 241 $maskbits = 32 - log(($long ^ $base) + 1, 2); 242 } 243 244 // Convert network IP to in_addr representation 245 $net = @inet_pton($net); 246 247 // Sanity check 248 if ($net === false) 249 { 250 continue; 251 } 252 253 // Get the network's binary representation 254 $expectedNumberOfBits = $ipv6 ? 128 : 24; 255 $binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT); 256 257 // Check the corresponding bits of the IP and the network 258 $ipNetBits = substr($binaryip, 0, $maskbits); 259 $netBits = substr($binarynet, 0, $maskbits); 260 261 if ($ipNetBits === $netBits) 262 { 263 return true; 264 } 265 } 266 else 267 { 268 // IPv6: Only single IPs are supported 269 if ($ipv6) 270 { 271 $ipExpression = trim($ipExpression); 272 273 if (!static::isIPv6($ipExpression)) 274 { 275 continue; 276 } 277 278 $ipCheck = @inet_pton($ipExpression); 279 280 if ($ipCheck === false) 281 { 282 continue; 283 } 284 285 if ($ipCheck == $myIP) 286 { 287 return true; 288 } 289 } 290 else 291 { 292 // Standard IPv4 address, i.e. 123.123.123.123 or partial IP address, i.e. 123.[123.][123.][123] 293 $dots = 0; 294 295 if (substr($ipExpression, -1) == '.') 296 { 297 // Partial IP address. Convert to CIDR and re-match 298 foreach (count_chars($ipExpression, 1) as $i => $val) 299 { 300 if ($i == 46) 301 { 302 $dots = $val; 303 } 304 } 305 306 switch ($dots) 307 { 308 case 1: 309 $netmask = '255.0.0.0'; 310 $ipExpression .= '0.0.0'; 311 312 break; 313 314 case 2: 315 $netmask = '255.255.0.0'; 316 $ipExpression .= '0.0'; 317 318 break; 319 320 case 3: 321 $netmask = '255.255.255.0'; 322 $ipExpression .= '0'; 323 324 break; 325 326 default: 327 $dots = 0; 328 } 329 330 if ($dots) 331 { 332 $binaryip = static::inetToBits($myIP); 333 334 // Convert netmask to CIDR 335 $long = ip2long($netmask); 336 $base = ip2long('255.255.255.255'); 337 $maskbits = 32 - log(($long ^ $base) + 1, 2); 338 339 $net = @inet_pton($ipExpression); 340 341 // Sanity check 342 if ($net === false) 343 { 344 continue; 345 } 346 347 // Get the network's binary representation 348 $expectedNumberOfBits = $ipv6 ? 128 : 24; 349 $binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT); 350 351 // Check the corresponding bits of the IP and the network 352 $ipNetBits = substr($binaryip, 0, $maskbits); 353 $netBits = substr($binarynet, 0, $maskbits); 354 355 if ($ipNetBits === $netBits) 356 { 357 return true; 358 } 359 } 360 } 361 362 if (!$dots) 363 { 364 $ip = @inet_pton(trim($ipExpression)); 365 366 if ($ip == $myIP) 367 { 368 return true; 369 } 370 } 371 } 372 } 373 } 374 375 return false; 376 } 377 378 /** 379 * Works around the REMOTE_ADDR not containing the user's IP 380 * 381 * @return void 382 * 383 * @since 1.6.0 384 */ 385 public static function workaroundIPIssues() 386 { 387 $ip = static::getIp(); 388 389 if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === $ip) 390 { 391 return; 392 } 393 394 if (isset($_SERVER['REMOTE_ADDR'])) 395 { 396 $_SERVER['JOOMLA_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR']; 397 } 398 elseif (\function_exists('getenv')) 399 { 400 if (getenv('REMOTE_ADDR')) 401 { 402 $_SERVER['JOOMLA_REMOTE_ADDR'] = getenv('REMOTE_ADDR'); 403 } 404 } 405 406 $_SERVER['REMOTE_ADDR'] = $ip; 407 } 408 409 /** 410 * Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header? 411 * 412 * @param boolean $newState True to allow the override 413 * 414 * @return void 415 * 416 * @since 1.6.0 417 */ 418 public static function setAllowIpOverrides($newState) 419 { 420 self::$allowIpOverrides = $newState ? true : false; 421 } 422 423 /** 424 * Gets the visitor's IP address. 425 * 426 * Automatically handles reverse proxies reporting the IPs of intermediate devices, like load balancers. Examples: 427 * 428 * - https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html 429 * - https://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips 430 * 431 * The solution used is assuming that the last IP address is the external one. 432 * 433 * @return string 434 * 435 * @since 1.6.0 436 */ 437 protected static function detectAndCleanIP() 438 { 439 $ip = static::detectIP(); 440 441 if (strstr($ip, ',') !== false || strstr($ip, ' ') !== false) 442 { 443 $ip = str_replace(' ', ',', $ip); 444 $ip = str_replace(',,', ',', $ip); 445 $ips = explode(',', $ip); 446 $ip = ''; 447 448 while (empty($ip) && !empty($ips)) 449 { 450 $ip = array_shift($ips); 451 $ip = trim($ip); 452 } 453 } 454 else 455 { 456 $ip = trim($ip); 457 } 458 459 return $ip; 460 } 461 462 /** 463 * Gets the visitor's IP address 464 * 465 * @return string 466 * 467 * @since 1.6.0 468 */ 469 protected static function detectIP() 470 { 471 // Normally the $_SERVER superglobal is set 472 if (isset($_SERVER)) 473 { 474 // Do we have an x-forwarded-for HTTP header (e.g. NginX)? 475 if (self::$allowIpOverrides && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) 476 { 477 return $_SERVER['HTTP_X_FORWARDED_FOR']; 478 } 479 480 // Do we have a client-ip header (e.g. non-transparent proxy)? 481 if (self::$allowIpOverrides && isset($_SERVER['HTTP_CLIENT_IP'])) 482 { 483 return $_SERVER['HTTP_CLIENT_IP']; 484 } 485 486 // Normal, non-proxied server or server behind a transparent proxy 487 if (isset($_SERVER['REMOTE_ADDR'])) 488 { 489 return $_SERVER['REMOTE_ADDR']; 490 } 491 } 492 493 /* 494 * This part is executed on PHP running as CGI, or on SAPIs which do not set the $_SERVER superglobal 495 * If getenv() is disabled, you're screwed 496 */ 497 if (!\function_exists('getenv')) 498 { 499 return ''; 500 } 501 502 // Do we have an x-forwarded-for HTTP header? 503 if (self::$allowIpOverrides && getenv('HTTP_X_FORWARDED_FOR')) 504 { 505 return getenv('HTTP_X_FORWARDED_FOR'); 506 } 507 508 // Do we have a client-ip header? 509 if (self::$allowIpOverrides && getenv('HTTP_CLIENT_IP')) 510 { 511 return getenv('HTTP_CLIENT_IP'); 512 } 513 514 // Normal, non-proxied server or server behind a transparent proxy 515 if (getenv('REMOTE_ADDR')) 516 { 517 return getenv('REMOTE_ADDR'); 518 } 519 520 // Catch-all case for broken servers, apparently 521 return ''; 522 } 523 524 /** 525 * Converts inet_pton output to bits string 526 * 527 * @param string $inet The in_addr representation of an IPv4 or IPv6 address 528 * 529 * @return string 530 * 531 * @since 1.6.0 532 */ 533 protected static function inetToBits($inet) 534 { 535 if (\strlen($inet) == 4) 536 { 537 $unpacked = unpack('A4', $inet); 538 } 539 else 540 { 541 $unpacked = unpack('A16', $inet); 542 } 543 544 $unpacked = str_split($unpacked[1]); 545 $binaryip = ''; 546 547 foreach ($unpacked as $char) 548 { 549 $binaryip .= str_pad(decbin(\ord($char)), 8, '0', STR_PAD_LEFT); 550 } 551 552 return $binaryip; 553 } 554 555 /** 556 * Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet 557 * 558 * @param string $ip The IPv6 address to check, e.g. 21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A 559 * @param string $cidrnet The IPv6 CIDR block, e.g. 21DA:00D3:0000:2F3B::/64 560 * 561 * @return boolean 562 * 563 * @since 1.6.0 564 */ 565 protected static function checkIPv6CIDR($ip, $cidrnet) 566 { 567 $ip = inet_pton($ip); 568 $binaryip = static::inetToBits($ip); 569 570 list($net, $maskbits) = explode('/', $cidrnet); 571 $net = inet_pton($net); 572 $binarynet = static::inetToBits($net); 573 574 $ipNetBits = substr($binaryip, 0, $maskbits); 575 $netBits = substr($binarynet, 0, $maskbits); 576 577 return $ipNetBits === $netBits; 578 } 579 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |