From: Aaron Schulz Date: Fri, 12 Nov 2010 19:37:37 +0000 (+0000) Subject: * Removed redundant check in toUnsigned6(). X-Git-Tag: 1.31.0-rc.0~33911 X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/exercices/modifier.php?a=commitdiff_plain;h=426f30bcfe198dbc3e2bbb7e647dd0e3a30d3bba;p=lhc%2Fweb%2Fwiklou.git * Removed redundant check in toUnsigned6(). * MW requires PHP 5.1+, so the -1/false ip2long annoyance is gone. Also, ip2long("255.255.255.255") is -1 so no special case code is needed anymore. * Removed toSigned() (not used outside IP.php). Due to the above points, ip2long() is totally equilivant. * Moved some functions and consts around. * Comment tweaks. --- diff --git a/includes/IP.php b/includes/IP.php index b2f4874eba..ee34edb157 100644 --- a/includes/IP.php +++ b/includes/IP.php @@ -29,13 +29,11 @@ define( 'RE_IP_ADD' , RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' // An IPv4 block is an IP address and a prefix (d1 to d32) define( 'RE_IP_PREFIX', '(3[0-2]|[12]?\d)' ); define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX ); -// For IPv6 canonicalization (NOT for strict validation; these are quite lax!) -define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' ); -define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' ); -define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' ); + // An IPv6 block is an IP address and a prefix (d1 to d128) +define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' ); define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)'); -// An IPv6 address is made up of 8 octets. However, the "::" abbreviations can be used. +// An IPv6 address is made up of 8 words. However, the "::" abbreviations can be used. define( 'RE_IPV6_ADD', '(' . // starts with "::" (includes the address "::") '(::|:(:' . RE_IPV6_WORD . '){1,7})' . @@ -48,6 +46,10 @@ define( 'RE_IPV6_ADD', ')' ); define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX ); +// For IPv6 canonicalization (NOT for strict validation; these are quite lax!) +define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' ); +define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' ); + // This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network define( 'IP_ADDRESS_STRING', '(?:' . @@ -63,8 +65,9 @@ define( 'IP_ADDRESS_STRING', */ class IP { /** - * Given a string, determine if it as valid IP. - * Note: Unlike isValid(), this looks for networks too. + * Determine if a string is as valid IP address or network (CIDR prefix). + * SIIT IPv4-translated addresses are rejected. + * Note: canonicalize() tries to convert translated addresses to IPv4. * @param string $ip possible IP address * @return bool */ @@ -92,6 +95,30 @@ class IP { return (bool)preg_match( '/^' . RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)$/', $ip ); } + /** + * Validate an IP address. Ranges are NOT considered valid. + * SIIT IPv4-translated addresses are rejected. + * Note: canonicalize() tries to convert translated addresses to IPv4. + * @param string $ip + * @return boolean True if it is valid. + */ + public static function isValid( $ip ) { + return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip ) + || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) ); + } + + /** + * Validate an IP Block (valid address WITH a valid prefix). + * SIIT IPv4-translated addresses are rejected. + * Note: canonicalize() tries to convert translated addresses to IPv4. + * @param string $ipblock + * @return boolean True if it is valid. + */ + public static function isValidBlock( $ipblock ) { + return ( preg_match( '/^' . RE_IPV6_BLOCK . '$/', $ipblock ) + || preg_match( '/^' . RE_IPV4_BLOCK . '$/', $ipblock ) ); + } + /** * Given an IP address in dotted-quad notation, returns an IPv6 octet. * See http://www.answers.com/topic/ipv4-compatible-address @@ -125,16 +152,9 @@ class IP { return self::toOctet( self::toUnsigned( $ip ) ); } - private static function toUnsigned6( $ip ) { - if ( self::isIPv6( $ip ) ) { - return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 ); - } - return false; - } - /** * Convert an IP into a nice standard form. - * IPv6 addresses in octet notation are expanded to 8 octets. + * IPv6 addresses in octet notation are expanded to 8 words. * IPv4 addresses are just trimmed. * @param string $ip IP address in quad or octet form (CIDR or not). * @return string @@ -190,8 +210,7 @@ class IP { * @return string */ public static function toOctet( $ip_int ) { - $ip_hex = wfBaseConvert( $ip_int, 10, 16, 32, false ); // uppercase hex - return self::hexToOctet( $ip_hex ); + return self::hexToOctet( wfBaseConvert( $ip_int, 10, 16, 32, false ) ); } /** @@ -213,9 +232,9 @@ class IP { * @return string (of format a:b:c:d:e:f:g:h) */ public static function hexToOctet( $ip_hex ) { - // Convert to padded uppercase hex + // Pad hex to 32 chars (128 bits) $ip_hex = str_pad( strtoupper( $ip_hex ), 32, '0', STR_PAD_LEFT ); - // Separate into 8 octets + // Separate into 8 words $ip_oct = substr( $ip_hex, 0, 4 ); for ( $n = 1; $n < 8; $n++ ) { $ip_oct .= ':' . substr( $ip_hex, 4 * $n, 4 ); @@ -231,7 +250,7 @@ class IP { * @return string (of format a.b.c.d) */ public static function hexToQuad( $ip_hex ) { - // Convert to padded uppercase hex + // Pad hex to 8 chars (32 bits) $ip_hex = str_pad( strtoupper( $ip_hex ), 8, '0', STR_PAD_LEFT ); // Separate into four quads $s = ''; @@ -244,113 +263,6 @@ class IP { return $s; } - /** - * Convert a network specification in IPv6 CIDR notation to an - * integer network and a number of bits - * @return array(string, int) - */ - private static function parseCIDR6( $range ) { - # Explode into - $parts = explode( '/', IP::sanitizeIP( $range ), 2 ); - if ( count( $parts ) != 2 ) { - return array( false, false ); - } - list( $network, $bits ) = $parts; - $network = self::IPv6ToRawHex( $network ); - if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) { - if ( $bits == 0 ) { - $network = "0"; - } else { - # Native 32 bit functions WONT work here!!! - # Convert to a padded binary number - $network = wfBaseConvert( $network, 16, 2, 128 ); - # Truncate the last (128-$bits) bits and replace them with zeros - $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT ); - # Convert back to an integer - $network = wfBaseConvert( $network, 2, 10 ); - } - } else { - $network = false; - $bits = false; - } - return array( $network, (int)$bits ); - } - - /** - * Given a string range in a number of formats, return the - * start and end of the range in hexadecimal. For IPv6. - * - * Formats are: - * 2001:0db8:85a3::7344/96 CIDR - * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range - * 2001:0db8:85a3::7344/96 Single IP - * @return array(string, int) - */ - private static function parseRange6( $range ) { - # Expand any IPv6 IP - $range = IP::sanitizeIP( $range ); - // CIDR notation... - if ( strpos( $range, '/' ) !== false ) { - list( $network, $bits ) = self::parseCIDR6( $range ); - if ( $network === false ) { - $start = $end = false; - } else { - $start = wfBaseConvert( $network, 10, 16, 32, false ); - # Turn network to binary (again) - $end = wfBaseConvert( $network, 10, 2, 128 ); - # Truncate the last (128-$bits) bits and replace them with ones - $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT ); - # Convert to hex - $end = wfBaseConvert( $end, 2, 16, 32, false ); - # see toHex() comment - $start = "v6-$start"; - $end = "v6-$end"; - } - // Explicit range notation... - } elseif ( strpos( $range, '-' ) !== false ) { - list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) ); - $start = self::toUnsigned6( $start ); - $end = self::toUnsigned6( $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 ); - } - if ( $start === false || $end === false ) { - return array( false, false ); - } else { - return array( $start, $end ); - } - } - - /** - * Validate an IP address. Ranges are NOT considered valid. - * @param string $ip - * @return boolean True if it is valid. - */ - public static function isValid( $ip ) { - return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip ) - || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) ); - } - - /** - * Validate an IP Block (valid address WITH a valid prefix). - * @param string $ipblock - * @return boolean True if it is valid. - */ - public static function isValidBlock( $ipblock ) { - return ( preg_match( '/^' . RE_IPV6_BLOCK . '$/', $ipblock ) - || preg_match( '/^' . RE_IPV4_BLOCK . '$/', $ipblock ) ); - } - /** * Determine if an IP address really is an IP address, and if it is public, * i.e. not RFC 1918 or similar @@ -405,7 +317,7 @@ class IP { if ( !$privateRanges ) { $privateRanges = array( array( 'fc::', '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 + array( '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1' ), # loopback ); } $n = self::toHex( $ip ); @@ -442,36 +354,10 @@ class IP { return $n; } - /** - * Given an IP address in dotted-quad/octet notation, returns an unsigned integer. - * Like ip2long() except that it actually works and has a consistent error return value. - * Comes from ProxyTools.php - * @param string $ip Quad dotted IP address. - * @return mixed (string/int/false) - */ - public static function toUnsigned( $ip ) { - if ( self::isIPv6( $ip ) ) { - $n = wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 ); - } else { - if ( $ip == '255.255.255.255' ) { - $n = -1; - } else { - $n = ip2long( $ip ); - if ( $n == -1 || $n === false ) { # Return value on error depends on PHP version - $n = false; - } - } - if ( $n < 0 ) { - $n += pow( 2, 32 ); - } - } - return $n; - } - /** * Given an IPv6 address in octet notation, returns a pure hex string. * @param string $ip octet ipv6 IP address. - * @return string hex (uppercase) + * @return string pure hex (uppercase) */ private static function IPv6ToRawHex( $ip ) { $ip = self::sanitizeIP( $ip ); @@ -486,24 +372,31 @@ class IP { } /** - * Convert a dotted-quad IP to a signed integer - * @param string $ip - * @return mixed (string/false) + * Given an IP address in dotted-quad/octet notation, returns an unsigned integer. + * Like ip2long() except that it actually works and has a consistent error return value. + * Comes from ProxyTools.php + * @param string $ip Quad dotted IP address. + * @return mixed (string/int/false) */ - public static function toSigned( $ip ) { - if ( $ip == '255.255.255.255' ) { - $n = -1; + public static function toUnsigned( $ip ) { + if ( self::isIPv6( $ip ) ) { + $n = wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 ); } else { $n = ip2long( $ip ); - if ( $n == -1 ) { - $n = false; + if ( $n < 0 ) { + $n += pow( 2, 32 ); } } return $n; } + 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 + * Convert a network specification in CIDR notation + * to an integer network and a number of bits * @param string $range (CIDR IP) * @return array(int, int) */ @@ -513,7 +406,7 @@ class IP { return array( false, false ); } list( $network, $bits ) = $parts; - $network = self::toSigned( $network ); + $network = ip2long( $network ); if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 32 ) { if ( $bits == 0 ) { $network = 0; @@ -586,6 +479,93 @@ class IP { } } + /** + * Convert a network specification in IPv6 CIDR notation to an + * integer network and a number of bits + * @return array(string, int) + */ + private static function parseCIDR6( $range ) { + # Explode into + $parts = explode( '/', IP::sanitizeIP( $range ), 2 ); + if ( count( $parts ) != 2 ) { + return array( false, false ); + } + list( $network, $bits ) = $parts; + $network = self::IPv6ToRawHex( $network ); + if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) { + if ( $bits == 0 ) { + $network = "0"; + } else { + # Native 32 bit functions WONT work here!!! + # Convert to a padded binary number + $network = wfBaseConvert( $network, 16, 2, 128 ); + # Truncate the last (128-$bits) bits and replace them with zeros + $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT ); + # Convert back to an integer + $network = wfBaseConvert( $network, 2, 10 ); + } + } else { + $network = false; + $bits = false; + } + return array( $network, (int)$bits ); + } + + /** + * Given a string range in a number of formats, return the + * start and end of the range in hexadecimal. For IPv6. + * + * Formats are: + * 2001:0db8:85a3::7344/96 CIDR + * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range + * 2001:0db8:85a3::7344/96 Single IP + * @return array(string, int) + */ + private static function parseRange6( $range ) { + # Expand any IPv6 IP + $range = IP::sanitizeIP( $range ); + // CIDR notation... + if ( strpos( $range, '/' ) !== false ) { + list( $network, $bits ) = self::parseCIDR6( $range ); + if ( $network === false ) { + $start = $end = false; + } else { + $start = wfBaseConvert( $network, 10, 16, 32, false ); + # Turn network to binary (again) + $end = wfBaseConvert( $network, 10, 2, 128 ); + # Truncate the last (128-$bits) bits and replace them with ones + $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT ); + # Convert to hex + $end = wfBaseConvert( $end, 2, 16, 32, false ); + # see toHex() comment + $start = "v6-$start"; + $end = "v6-$end"; + } + // Explicit range notation... + } elseif ( strpos( $range, '-' ) !== false ) { + list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) ); + $start = self::toUnsigned6( $start ); + $end = self::toUnsigned6( $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 ); + } + if ( $start === false || $end === false ) { + return array( false, false ); + } else { + return array( $start, $end ); + } + } + /** * Determine if a given IPv4/IPv6 address is in a given CIDR network * @param $addr The address to check against the given range. diff --git a/maintenance/tests/phpunit/includes/IPTest.php b/maintenance/tests/phpunit/includes/IPTest.php index e74a543f1b..9f3f58af90 100644 --- a/maintenance/tests/phpunit/includes/IPTest.php +++ b/maintenance/tests/phpunit/includes/IPTest.php @@ -135,10 +135,8 @@ class IPTest extends PHPUnit_Framework_TestCase { public function testip2longWrapper() { // fixme : add more tests ? $this->assertEquals( pow(2,32) - 1, IP::toUnsigned( '255.255.255.255' )); - $this->assertEquals( -1 , IP::toSigned( '255.255.255.255' )) ; $i = 'IN.VA.LI.D'; $this->assertFalse( IP::toUnSigned( $i ) ); - $this->assertFalse( IP::toSigned( $i ) ); } public function testPrivateIPs() { @@ -199,8 +197,6 @@ class IPTest extends PHPUnit_Framework_TestCase { function testCIDRParsing() { $this->assertFalseCIDR( '192.0.2.0' , "missing mask" ); $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" ); - - // code calls IP::toSigned() // Verify if statement $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );