2015-08-17 17:00:26 -07:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\HttpFoundation ;
/**
* Http utility functions .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class IpUtils
{
2018-11-23 12:29:20 +00:00
private static $checkedIps = array ();
2015-08-17 17:00:26 -07:00
/**
* This class should not be instantiated .
*/
private function __construct ()
{
}
/**
* Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets .
*
* @ param string $requestIp IP to check
* @ param string | array $ips List of IPs or subnets ( can be a string if only a single one )
*
* @ return bool Whether the IP is valid
*/
public static function checkIp ( $requestIp , $ips )
{
2018-11-23 12:29:20 +00:00
if ( ! \is_array ( $ips )) {
2015-08-17 17:00:26 -07:00
$ips = array ( $ips );
}
$method = substr_count ( $requestIp , ':' ) > 1 ? 'checkIp6' : 'checkIp4' ;
foreach ( $ips as $ip ) {
if ( self :: $method ( $requestIp , $ip )) {
return true ;
}
}
return false ;
}
/**
* Compares two IPv4 addresses .
* In case a subnet is given , it checks if it contains the request IP .
*
* @ param string $requestIp IPv4 address to check
* @ param string $ip IPv4 address or subnet in CIDR notation
*
2017-02-02 16:28:38 -08:00
* @ return bool Whether the request IP matches the IP , or whether the request IP is within the CIDR subnet
2015-08-17 17:00:26 -07:00
*/
public static function checkIp4 ( $requestIp , $ip )
{
2018-11-23 12:29:20 +00:00
$cacheKey = $requestIp . '-' . $ip ;
if ( isset ( self :: $checkedIps [ $cacheKey ])) {
return self :: $checkedIps [ $cacheKey ];
}
2017-02-02 16:28:38 -08:00
if ( ! filter_var ( $requestIp , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 )) {
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = false ;
2017-02-02 16:28:38 -08:00
}
2015-08-17 17:00:26 -07:00
if ( false !== strpos ( $ip , '/' )) {
list ( $address , $netmask ) = explode ( '/' , $ip , 2 );
2018-11-23 12:29:20 +00:00
if ( '0' === $netmask ) {
return self :: $checkedIps [ $cacheKey ] = filter_var ( $address , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 );
2015-11-17 13:42:33 -08:00
}
if ( $netmask < 0 || $netmask > 32 ) {
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = false ;
2015-08-17 17:00:26 -07:00
}
} else {
$address = $ip ;
$netmask = 32 ;
}
2018-11-23 12:29:20 +00:00
if ( false === ip2long ( $address )) {
return self :: $checkedIps [ $cacheKey ] = false ;
}
return self :: $checkedIps [ $cacheKey ] = 0 === substr_compare ( sprintf ( '%032b' , ip2long ( $requestIp )), sprintf ( '%032b' , ip2long ( $address )), 0 , $netmask );
2015-08-17 17:00:26 -07:00
}
/**
* Compares two IPv6 addresses .
* In case a subnet is given , it checks if it contains the request IP .
*
* @ author David Soria Parra < dsp at php dot net >
*
* @ see https :// github . com / dsp / v6tools
*
* @ param string $requestIp IPv6 address to check
* @ param string $ip IPv6 address or subnet in CIDR notation
*
* @ return bool Whether the IP is valid
*
* @ throws \RuntimeException When IPV6 support is not enabled
*/
public static function checkIp6 ( $requestIp , $ip )
{
2018-11-23 12:29:20 +00:00
$cacheKey = $requestIp . '-' . $ip ;
if ( isset ( self :: $checkedIps [ $cacheKey ])) {
return self :: $checkedIps [ $cacheKey ];
}
if ( ! (( \extension_loaded ( 'sockets' ) && \defined ( 'AF_INET6' )) || @ inet_pton ( '::1' ))) {
2015-08-17 17:00:26 -07:00
throw new \RuntimeException ( 'Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".' );
}
if ( false !== strpos ( $ip , '/' )) {
list ( $address , $netmask ) = explode ( '/' , $ip , 2 );
2018-11-23 12:29:20 +00:00
if ( '0' === $netmask ) {
return ( bool ) unpack ( 'n*' , @ inet_pton ( $address ));
}
2015-08-17 17:00:26 -07:00
if ( $netmask < 1 || $netmask > 128 ) {
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = false ;
2015-08-17 17:00:26 -07:00
}
} else {
$address = $ip ;
$netmask = 128 ;
}
2016-04-20 09:56:34 -07:00
$bytesAddr = unpack ( 'n*' , @ inet_pton ( $address ));
$bytesTest = unpack ( 'n*' , @ inet_pton ( $requestIp ));
if ( ! $bytesAddr || ! $bytesTest ) {
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = false ;
2016-04-20 09:56:34 -07:00
}
2015-08-17 17:00:26 -07:00
for ( $i = 1 , $ceil = ceil ( $netmask / 16 ); $i <= $ceil ; ++ $i ) {
$left = $netmask - 16 * ( $i - 1 );
$left = ( $left <= 16 ) ? $left : 16 ;
$mask = ~ ( 0xffff >> $left ) & 0xffff ;
if (( $bytesAddr [ $i ] & $mask ) != ( $bytesTest [ $i ] & $mask )) {
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = false ;
2015-08-17 17:00:26 -07:00
}
}
2018-11-23 12:29:20 +00:00
return self :: $checkedIps [ $cacheKey ] = true ;
2015-08-17 17:00:26 -07:00
}
}