IP class cleanups

* Removed clumsy and unused toOctet() method
* Avoid internal use of ugly toUnsigned() method
* Use proper return value for toHex() on failure
* Converted isPublic() to use IPSet

Change-Id: I36d0e08fe96f15380aea77ad4a39e93104023ce3
This commit is contained in:
Aaron Schulz 2014-05-16 09:04:34 -07:00
parent e10b688d5f
commit 8eb6ac3e84

View file

@ -67,6 +67,7 @@ define( 'IP_ADDRESS_STRING',
class IP {
/** @var IPSet */
private static $ipSet = null;
/**
* Determine if a string is as valid IP address or network (CIDR prefix).
* SIIT IPv4-translated addresses are rejected.
@ -301,16 +302,6 @@ class IP {
}
}
/**
* Given an unsigned integer, returns an IPv6 address in octet notation
*
* @param $ip_int String: IP address.
* @return String
*/
public static function toOctet( $ip_int ) {
return self::hexToOctet( wfBaseConvert( $ip_int, 10, 16, 32, false ) );
}
/**
* Convert an IPv4 or IPv6 hexadecimal representation back to readable format
*
@ -374,67 +365,19 @@ class IP {
* @return Boolean
*/
public static function isPublic( $ip ) {
if ( self::isIPv6( $ip ) ) {
return self::isPublic6( $ip );
static $privateSet = null;
if ( !$privateSet ) {
$privateSet = new IPSet( array(
'10.0.0.0/8', # RFC 1918 (private)
'172.16.0.0/12', # RFC 1918 (private)
'192.168.0.0/16', # RFC 1918 (private)
'0.0.0.0/8', # this network
'127.0.0.0/8', # loopback
'fc00::/7', # RFC 4193 (local)
'0:0:0:0:0:0:0:1', # loopback
) );
}
$n = self::toUnsigned( $ip );
if ( !$n ) {
return false;
}
// ip2long accepts incomplete addresses, as well as some addresses
// followed by garbage characters. Check that it's really valid.
if ( $ip != long2ip( $n ) ) {
return false;
}
static $privateRanges = false;
if ( !$privateRanges ) {
$privateRanges = array(
array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
array( '172.16.0.0', '172.31.255.255' ), # RFC 1918 (private)
array( '192.168.0.0', '192.168.255.255' ), # RFC 1918 (private)
array( '0.0.0.0', '0.255.255.255' ), # this network
array( '127.0.0.0', '127.255.255.255' ), # loopback
);
}
foreach ( $privateRanges as $r ) {
$start = self::toUnsigned( $r[0] );
$end = self::toUnsigned( $r[1] );
if ( $n >= $start && $n <= $end ) {
return false;
}
}
return true;
}
/**
* Determine if an IPv6 address really is an IP address, and if it is public,
* i.e. not RFC 4193 or similar
*
* @param $ip String
* @return Boolean
*/
private static function isPublic6( $ip ) {
static $privateRanges = false;
if ( !$privateRanges ) {
$privateRanges = array(
array( 'fc00::', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ), # RFC 4193 (local)
array( '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1' ), # loopback
);
}
$n = self::toHex( $ip );
foreach ( $privateRanges as $r ) {
$start = self::toHex( $r[0] );
$end = self::toHex( $r[1] );
if ( $n >= $start && $n <= $end ) {
return false;
}
}
return true;
return !$privateSet->match( $ip );
}
/**
@ -446,7 +389,7 @@ class IP {
* hexadecimal string which sorts after the IPv4 addresses.
*
* @param string $ip quad dotted/octet IP address.
* @return String
* @return String|bool false on failure
*/
public static function toHex( $ip ) {
if ( self::isIPv6( $ip ) ) {
@ -465,12 +408,12 @@ class IP {
* Given an IPv6 address in octet notation, returns a pure hex string.
*
* @param string $ip octet ipv6 IP address.
* @return String: pure hex (uppercase)
* @return String|bool pure hex (uppercase); false on failure
*/
private static function IPv6ToRawHex( $ip ) {
$ip = self::sanitizeIP( $ip );
if ( !$ip ) {
return null;
return false;
}
$r_ip = '';
foreach ( explode( ':', $ip ) as $v ) {
@ -489,7 +432,7 @@ class IP {
*/
public static function toUnsigned( $ip ) {
if ( self::isIPv6( $ip ) ) {
$n = self::toUnsigned6( $ip );
$n = wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
} else {
// Bug 60035: an IP with leading 0's fails in ip2long sometimes (e.g. *.08)
$ip = preg_replace( '/(?<=\.)0+(?=[1-9])/', '', $ip );
@ -507,14 +450,6 @@ class IP {
return $n;
}
/**
* @param $ip
* @return String
*/
private static function toUnsigned6( $ip ) {
return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
}
/**
* Convert a network specification in CIDR notation
* to an integer network and a number of bits
@ -585,13 +520,10 @@ class IP {
return self::parseRange6( $range );
}
if ( self::isIPv4( $start ) && self::isIPv4( $end ) ) {
$start = self::toUnsigned( $start );
$end = self::toUnsigned( $end );
$start = self::toHex( $start );
$end = self::toHex( $end );
if ( $start > $end ) {
$start = $end = false;
} else {
$start = sprintf( '%08X', $start );
$end = sprintf( '%08X', $end );
}
} else {
$start = $end = false;
@ -679,17 +611,11 @@ class IP {
// Explicit range notation...
} elseif ( strpos( $range, '-' ) !== false ) {
list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
$start = self::toUnsigned6( $start );
$end = self::toUnsigned6( $end );
$start = self::toHex( $start );
$end = self::toHex( $end );
if ( $start > $end ) {
$start = $end = false;
} else {
$start = wfBaseConvert( $start, 10, 16, 32, false );
$end = wfBaseConvert( $end, 10, 16, 32, false );
}
# see toHex() comment
$start = "v6-$start";
$end = "v6-$end";
} else {
# Single IP
$start = $end = self::toHex( $range );
@ -812,6 +738,7 @@ class IP {
$trusted = self::$ipSet->match( $ip );
}
wfProfileOut( __METHOD__ );
return $trusted;
}