r76560 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r76559‎ | r76560 | r76561 >
Date:01:33, 12 November 2010
Author:aaron
Status:resolved (Comments)
Tags:
Comment:
* Changes in IP.php:
** Fixed hexToOctet()/toOctet() padding (pad left not right)
** Made hexToQuad() left-pad input (e.g. C -> 0000000C)
** Added isPublic6() function (checked as needed by isPublic())
** Rewrote isValidBlock() to not do flaky and roundabout isArray() check. Works for v6 now.
** Removed toArray(), unused outside IP.php and broken for v6
** Removed toOctet() duplication
** Added new private IPv6ToRawHex() function. Used to make toHex() faster.
** Made some functions private
** Reverted r20435, pointless
** Updated credits
* Changes in IPTests:
** Added a bunch of tests (mostly v6)
** Removed weird array test (especially with toArray() gone) after r76514
** Padding *no* longer needed for hexToX functions - assertion flipped
* CheckUser: removed parseRange6() reference (now private)
Modified paths:
  • /trunk/extensions/CheckUser/CheckUser_body.php (modified) (history)
  • /trunk/phase3/includes/IP.php (modified) (history)
  • /trunk/phase3/maintenance/tests/phpunit/includes/IPTest.php (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/tests/phpunit/includes/IPTest.php
@@ -7,23 +7,71 @@
88 // not sure it should be tested with boolean false. hashar 20100924
99 public function testisIPAddress() {
1010 $this->assertFalse( IP::isIPAddress( false ) );
 11+ $this->assertFalse( IP::isIPAddress( "" ) );
 12+ $this->assertFalse( IP::isIPAddress( 'abc' ) );
 13+ $this->assertFalse( IP::isIPAddress( ':' ) );
1114 $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurence' );
1215 $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurence, last at end' );
1316 $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurence, firt at beginning' );
 17+ $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
 18+ $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
 19+ $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
 20+
 21+ $this->assertTrue( IP::isIPAddress( 'fc:100::' ) );
 22+ $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac::' ) );
 23+ $this->assertTrue( IP::isIPAddress( '::' ), 'IPv6 zero address' );
 24+ $this->assertTrue( IP::isIPAddress( '::fc' ) );
 25+ $this->assertTrue( IP::isIPAddress( '::fc:100:a:d:1:e:ac' ) );
 26+ $this->assertTrue( IP::isIPAddress( 'fc::100' ) );
 27+ $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac' ) );
 28+ $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96', 'IPv6 range with "::"' ) );
 29+ $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0' ) );
 30+ $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24', 'IPv6 range' ) );
 31+ $this->assertTrue( IP::isIPAddress( '124.24.52.13' ) );
 32+ $this->assertTrue( IP::isIPAddress( '1.24.52.13' ) );
 33+ $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
1434 }
1535
16 - /**
17 - * @expectedException MWException
18 - */
19 - public function testArrayIsNotIPAddress() {
20 - IP::isIPAddress( array('') );
 36+ public function testisIPv6() {
 37+ $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
 38+ $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
 39+ $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
 40+ $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
 41+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
 42+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
 43+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
 44+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
 45+ $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 ending it "::" with 8 words' );
 46+ $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words' );
 47+
 48+ $this->assertFalse( IP::isIPv6( ':::' ) );
 49+ $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
 50+ $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
 51+ $this->assertTrue( IP::isIPv6( '::0' ) );
 52+ $this->assertTrue( IP::isIPv6( '::fc' ) );
 53+ $this->assertTrue( IP::isIPv6( '::fc:100' ) );
 54+ $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
 55+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
 56+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
 57+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
 58+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
 59+ $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 octets' );
 60+ $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 octets' );
 61+
 62+ $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
 63+ $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending in lone ":"' );
 64+ $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
 65+ $this->assertTrue( IP::isIPv6( 'fc::100' ) );
 66+ $this->assertTrue( IP::isIPv6( 'fc::100:a' ) );
 67+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d' ) );
 68+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ) );
 69+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ) );
 70+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ) );
 71+ $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 octets' );
 72+ $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 octets' );
 73+
 74+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
2175 }
22 - /**
23 - * @expectedException MWException
24 - */
25 - public function testArrayIsNotIPv6() {
26 - IP::isIPv6( array('') );
27 - }
2876
2977 public function testValidIPs() {
3078 foreach ( range( 0, 255 ) as $i ) {
@@ -35,6 +83,15 @@
3684 $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv4 address" );
3785 }
3886 }
 87+ foreach ( range( 0, 65535 ) as $i ) {
 88+ $a = sprintf( "%04x", $i );
 89+ $b = sprintf( "%03x", $i );
 90+ $c = sprintf( "%02x", $i );
 91+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
 92+ $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
 93+ $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv6 address" );
 94+ }
 95+ }
3996 }
4097
4198 public function testInvalidIPs() {
@@ -47,6 +104,15 @@
48105 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
49106 }
50107 }
 108+ foreach ( range( 'g', 'z' ) as $i ) {
 109+ $a = sprintf( "%04", $i );
 110+ $b = sprintf( "%03", $i );
 111+ $c = sprintf( "%02", $i );
 112+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
 113+ $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
 114+ $this->assertFalse( IP::isValid( $ip ) , "$ip is not a valid IPv6 address" );
 115+ }
 116+ }
51117 }
52118
53119 public function testBogusIPs() {
@@ -92,19 +158,38 @@
93159 $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
94160 }
95161
96 -
97162 public function testHexToQuad() {
98 - $this->assertEquals( '0.0.0.0' , IP::hexToQuad( '0' ) );
99163 $this->assertEquals( '0.0.0.1' , IP::hexToQuad( '00000001' ) );
100164 $this->assertEquals( '255.0.0.0' , IP::hexToQuad( 'FF000000' ) );
101165 $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
102166 $this->assertEquals( '10.188.222.255' , IP::hexToQuad( '0ABCDEFF' ) );
103 -
104 - $this->assertNotEquals( '0.0.0.1' , IP::hexToQuad( '1' ) );
105 - $this->assertNotEquals( '0.0.0.255' , IP::hexToQuad( 'FF' ) );
106 - $this->assertNotEquals( '0.0.255.0' , IP::hexToQuad( 'FF00' ) );
 167+ // hex not left-padded...
 168+ $this->assertEquals( '0.0.0.0' , IP::hexToQuad( '0' ) );
 169+ $this->assertEquals( '0.0.0.1' , IP::hexToQuad( '1' ) );
 170+ $this->assertEquals( '0.0.0.255' , IP::hexToQuad( 'FF' ) );
 171+ $this->assertEquals( '0.0.255.0' , IP::hexToQuad( 'FF00' ) );
107172 }
108173
 174+ public function testHexToOctet() {
 175+ $this->assertEquals( '0:0:0:0:0:0:0:1',
 176+ IP::hexToOctet( '00000000000000000000000000000001' ) );
 177+ $this->assertEquals( '0:0:0:0:0:0:FF:3',
 178+ IP::hexToOctet( '00000000000000000000000000FF0003' ) );
 179+ $this->assertEquals( '0:0:0:0:0:0:FF00:6',
 180+ IP::hexToOctet( '000000000000000000000000FF000006' ) );
 181+ $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
 182+ IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
 183+ $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
 184+ IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
 185+ // hex not left-padded...
 186+ $this->assertEquals( '0:0:0:0:0:0:0:0' , IP::hexToOctet( '0' ) );
 187+ $this->assertEquals( '0:0:0:0:0:0:0:1' , IP::hexToOctet( '1' ) );
 188+ $this->assertEquals( '0:0:0:0:0:0:0:FF' , IP::hexToOctet( 'FF' ) );
 189+ $this->assertEquals( '0:0:0:0:0:0:0:FFD0' , IP::hexToOctet( 'FFD0' ) );
 190+ $this->assertEquals( '0:0:0:0:0:0:FA00:0' , IP::hexToOctet( 'FA000000' ) );
 191+ $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
 192+ }
 193+
109194 /*
110195 * IP::parseCIDR() returns an array containing a signed IP address
111196 * representing the network mask and the bit mask.
Index: trunk/phase3/includes/IP.php
@@ -18,7 +18,7 @@
1919 * http://www.gnu.org/copyleft/gpl.html
2020 *
2121 * @file
22 - * @author Ashar Voultoiz <hashar at free dot fr>
 22+ * @author Ashar Voultoiz <hashar at free dot fr>, Aaron Schulz
2323 */
2424
2525 // Some regex definition to "play" with IP address and IP address blocks
@@ -69,9 +69,6 @@
7070 * @return bool
7171 */
7272 public static function isIPAddress( $ip ) {
73 - if ( !$ip ) {
74 - return false;
75 - }
7673 return (bool)preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip );
7774 }
7875
@@ -82,9 +79,6 @@
8380 * @return bool
8481 */
8582 public static function isIPv6( $ip ) {
86 - if ( !$ip ) {
87 - return false;
88 - }
8983 return (bool)preg_match( '/^' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)$/', $ip );
9084 }
9185
@@ -95,9 +89,6 @@
9690 * @return bool
9791 */
9892 public static function isIPv4( $ip ) {
99 - if ( !$ip ) {
100 - return false;
101 - }
10293 return (bool)preg_match( '/^' . RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)$/', $ip );
10394 }
10495
@@ -116,7 +107,7 @@
117108 if ( self::isIPv6( $ip ) ) {
118109 return $ip;
119110 }
120 - // IPv4 CIDRs
 111+ // IPv4 address with CIDR
121112 if ( strpos( $ip, '/' ) !== false ) {
122113 $parts = explode( '/', $ip, 2 );
123114 if ( count( $parts ) != 2 ) {
@@ -134,27 +125,17 @@
135126 return self::toOctet( self::toUnsigned( $ip ) );
136127 }
137128
138 - /**
139 - * Given an IPv6 address in octet notation, returns an unsigned integer.
140 - * @param string $ip octet ipv6 IP address.
141 - * @return string
142 - */
143 - public static function toUnsigned6( $ip ) {
144 - if ( !$ip ) {
145 - return null;
 129+ private static function toUnsigned6( $ip ) {
 130+ if ( self::isIPv6( $ip ) ) {
 131+ return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
146132 }
147 - $ip = explode( ':', self::sanitizeIP( $ip ) );
148 - $r_ip = '';
149 - foreach ( $ip as $v ) {
150 - $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
151 - }
152 - $r_ip = wfBaseConvert( $r_ip, 16, 10 );
153 - return $r_ip;
 133+ return false;
154134 }
155135
156136 /**
157 - * Given an IPv6 address in octet notation, returns the expanded octet.
158 - * IPv4 IPs will be trimmed, thats it...
 137+ * Convert an IP into a nice standard form.
 138+ * IPv6 addresses in octet notation are expanded to 8 octets.
 139+ * IPv4 addresses are just trimmed.
159140 * @param string $ip IP address in quad or octet form (CIDR or not).
160141 * @return string
161142 */
@@ -205,20 +186,12 @@
206187
207188 /**
208189 * Given an unsigned integer, returns an IPv6 address in octet notation
209 - * @param int $ip_int IP address.
 190+ * @param string $ip_int IP address.
210191 * @return string
211192 */
212193 public static function toOctet( $ip_int ) {
213 - // Convert to padded uppercase hex
214 - $ip_hex = wfBaseConvert( $ip_int, 10, 16, 32, false );
215 - // Separate into 8 octets
216 - $ip_oct = substr( $ip_hex, 0, 4 );
217 - for ( $n = 1; $n < 8; $n++ ) {
218 - $ip_oct .= ':' . substr( $ip_hex, 4 * $n, 4 );
219 - }
220 - // NO leading zeroes
221 - $ip_oct = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct );
222 - return $ip_oct;
 194+ $ip_hex = wfBaseConvert( $ip_int, 10, 16, 32, false ); // uppercase hex
 195+ return self::hexToOctet( $ip_hex );
223196 }
224197
225198 /**
@@ -236,12 +209,12 @@
237210
238211 /**
239212 * Converts a hexadecimal number to an IPv6 address in octet notation
240 - * @param string $ip_hex hex IP
 213+ * @param string $ip_hex pure hex (no v6- prefix)
241214 * @return string (of format a:b:c:d:e:f:g:h)
242215 */
243216 public static function hexToOctet( $ip_hex ) {
244217 // Convert to padded uppercase hex
245 - $ip_hex = str_pad( strtoupper( $ip_hex ), 32, '0' );
 218+ $ip_hex = str_pad( strtoupper( $ip_hex ), 32, '0', STR_PAD_LEFT );
246219 // Separate into 8 octets
247220 $ip_oct = substr( $ip_hex, 0, 4 );
248221 for ( $n = 1; $n < 8; $n++ ) {
@@ -254,16 +227,19 @@
255228
256229 /**
257230 * Converts a hexadecimal number to an IPv4 address in quad-dotted notation
258 - * @param string $ip Hex IP
 231+ * @param string $ip_hex pure hex
259232 * @return string (of format a.b.c.d)
260233 */
261 - public static function hexToQuad( $ip ) {
 234+ public static function hexToQuad( $ip_hex ) {
 235+ // Convert to padded uppercase hex
 236+ $ip_hex = str_pad( strtoupper( $ip_hex ), 8, '0', STR_PAD_LEFT );
 237+ // Separate into four quads
262238 $s = '';
263239 for ( $i = 0; $i < 4; $i++ ) {
264240 if ( $s !== '' ) {
265241 $s .= '.';
266242 }
267 - $s .= base_convert( substr( $ip, $i * 2, 2 ), 16, 10 );
 243+ $s .= base_convert( substr( $ip_hex, $i * 2, 2 ), 16, 10 );
268244 }
269245 return $s;
270246 }
@@ -273,21 +249,21 @@
274250 * integer network and a number of bits
275251 * @return array(string, int)
276252 */
277 - public static function parseCIDR6( $range ) {
 253+ private static function parseCIDR6( $range ) {
278254 # Explode into <expanded IP,range>
279255 $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
280256 if ( count( $parts ) != 2 ) {
281257 return array( false, false );
282258 }
283259 list( $network, $bits ) = $parts;
284 - $network = self::toUnsigned6( $network );
 260+ $network = self::IPv6ToRawHex( $network );
285261 if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) {
286262 if ( $bits == 0 ) {
287263 $network = "0";
288264 } else {
289265 # Native 32 bit functions WONT work here!!!
290266 # Convert to a padded binary number
291 - $network = wfBaseConvert( $network, 10, 2, 128 );
 267+ $network = wfBaseConvert( $network, 16, 2, 128 );
292268 # Truncate the last (128-$bits) bits and replace them with zeros
293269 $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
294270 # Convert back to an integer
@@ -301,8 +277,8 @@
302278 }
303279
304280 /**
305 - * Given a string range in a number of formats, return the start and end of
306 - * the range in hexadecimal. For IPv6.
 281+ * Given a string range in a number of formats, return the
 282+ * start and end of the range in hexadecimal. For IPv6.
307283 *
308284 * Formats are:
309285 * 2001:0db8:85a3::7344/96 CIDR
@@ -310,7 +286,7 @@
311287 * 2001:0db8:85a3::7344/96 Single IP
312288 * @return array(string, int)
313289 */
314 - public static function parseRange6( $range ) {
 290+ private static function parseRange6( $range ) {
315291 # Expand any IPv6 IP
316292 $range = IP::sanitizeIP( $range );
317293 // CIDR notation...
@@ -356,21 +332,23 @@
357333 }
358334
359335 /**
360 - * Validate an IP address.
 336+ * Validate an IP address. Ranges are NOT considered valid.
361337 * @param string $ip
362338 * @return boolean True if it is valid.
363339 */
364340 public static function isValid( $ip ) {
365 - return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip ) || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) );
 341+ return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip )
 342+ || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) );
366343 }
367344
368345 /**
369 - * Validate an IP Block.
 346+ * Validate an IP Block (valid address WITH a valid prefix).
370347 * @param string $ipblock
371348 * @return boolean True if it is valid.
372349 */
373350 public static function isValidBlock( $ipblock ) {
374 - return ( count( self::toArray( $ipblock ) ) == 1 + 5 );
 351+ return ( preg_match( '/^' . RE_IPV6_BLOCK . '$/', $ipblock )
 352+ || preg_match( '/^' . RE_IPV4_BLOCK . '$/', $ipblock ) );
375353 }
376354
377355 /**
@@ -381,6 +359,9 @@
382360 * @return bool
383361 */
384362 public static function isPublic( $ip ) {
 363+ if ( self::isIPv6( $ip ) ) {
 364+ return self::isPublic6( $ip );
 365+ }
385366 $n = self::toUnsigned( $ip );
386367 if ( !$n ) {
387368 return false;
@@ -414,25 +395,32 @@
415396 }
416397
417398 /**
418 - * Split out an IP block as an array of 4 bytes and a mask,
419 - * return false if it can't be determined
420 - *
421 - * @param string $ipblock A quad dotted/octet IP address
422 - * @return array
 399+ * Determine if an IPv6 address really is an IP address, and if it is public,
 400+ * i.e. not RFC 4193 or similar
 401+ * @param string $ip
 402+ * @return bool
423403 */
424 - public static function toArray( $ipblock ) {
425 - $matches = array();
426 - if( preg_match( '/^' . RE_IP_ADD . '(?:\/(?:' . RE_IP_PREFIX . '))?' . '$/', $ipblock, $matches ) ) {
427 - return $matches;
428 - } elseif ( preg_match( '/^' . RE_IPV6_ADD . '(?:\/(?:' . RE_IPV6_PREFIX . '))?' . '$/', $ipblock, $matches ) ) {
429 - return $matches;
430 - } else {
431 - return false;
 404+ private static function isPublic6( $ip ) {
 405+ static $privateRanges = false;
 406+ if ( !$privateRanges ) {
 407+ $privateRanges = array(
 408+ array( 'fc::', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ), # RFC 4193 (local)
 409+ array( '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1' ), # loopback
 410+ );
432411 }
 412+ $n = self::toHex( $ip );
 413+ foreach ( $privateRanges as $r ) {
 414+ $start = self::toHex( $r[0] );
 415+ $end = self::toHex( $r[1] );
 416+ if ( $n >= $start && $n <= $end ) {
 417+ return false;
 418+ }
 419+ }
 420+ return true;
433421 }
434422
435423 /**
436 - * Return a zero-padded hexadecimal representation of an IP address.
 424+ * Return a zero-padded upper case hexadecimal representation of an IP address.
437425 *
438426 * Hexadecimal addresses are used because they can easily be extended to
439427 * IPv6 support. To separate the ranges, the return value from this
@@ -443,11 +431,13 @@
444432 * @return string
445433 */
446434 public static function toHex( $ip ) {
447 - $n = self::toUnsigned( $ip );
448 - if ( $n !== false ) {
449 - $n = self::isIPv6( $ip )
450 - ? 'v6-' . wfBaseConvert( $n, 10, 16, 32, false )
451 - : wfBaseConvert( $n, 10, 16, 8, false );
 435+ if ( self::isIPv6( $ip ) ) {
 436+ $n = 'v6-' . self::IPv6ToRawHex( $ip );
 437+ } else {
 438+ $n = self::toUnsigned( $ip );
 439+ if ( $n !== false ) {
 440+ $n = wfBaseConvert( $n, 10, 16, 8, false );
 441+ }
452442 }
453443 return $n;
454444 }
@@ -457,28 +447,45 @@
458448 * Like ip2long() except that it actually works and has a consistent error return value.
459449 * Comes from ProxyTools.php
460450 * @param string $ip Quad dotted IP address.
461 - * @return integer
 451+ * @return mixed (string/int/false)
462452 */
463453 public static function toUnsigned( $ip ) {
464 - // Use IPv6 functions if needed
465454 if ( self::isIPv6( $ip ) ) {
466 - return self::toUnsigned6( $ip );
467 - }
468 - if ( $ip == '255.255.255.255' ) {
469 - $n = -1;
 455+ $n = wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
470456 } else {
471 - $n = ip2long( $ip );
472 - if ( $n == -1 || $n === false ) { # Return value on error depends on PHP version
473 - $n = false;
 457+ if ( $ip == '255.255.255.255' ) {
 458+ $n = -1;
 459+ } else {
 460+ $n = ip2long( $ip );
 461+ if ( $n == -1 || $n === false ) { # Return value on error depends on PHP version
 462+ $n = false;
 463+ }
474464 }
 465+ if ( $n < 0 ) {
 466+ $n += pow( 2, 32 );
 467+ }
475468 }
476 - if ( $n < 0 ) {
477 - $n += pow( 2, 32 );
478 - }
479469 return $n;
480470 }
481471
482472 /**
 473+ * Given an IPv6 address in octet notation, returns a pure hex string.
 474+ * @param string $ip octet ipv6 IP address.
 475+ * @return string hex (uppercase)
 476+ */
 477+ private static function IPv6ToRawHex( $ip ) {
 478+ $ip = self::sanitizeIP( $ip );
 479+ if ( !$ip ) {
 480+ return null;
 481+ }
 482+ $r_ip = '';
 483+ foreach ( explode( ':', $ip ) as $v ) {
 484+ $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
 485+ }
 486+ return $r_ip;
 487+ }
 488+
 489+ /**
483490 * Convert a dotted-quad IP to a signed integer
484491 * @param string $ip
485492 * @return mixed (string/false)
@@ -586,7 +593,6 @@
587594 * @return bool Whether or not the given address is in the given range.
588595 */
589596 public static function isInRange( $addr, $range ) {
590 - // Convert to IPv6 if needed
591597 $hexIP = self::toHex( $addr );
592598 list( $start, $end ) = self::parseRange( $range );
593599 return ( strcmp( $hexIP, $start ) >= 0 &&
Index: trunk/extensions/CheckUser/CheckUser_body.php
@@ -1151,7 +1151,7 @@
11521152 if ( $matches[1] < 96 || $matches[1] > 128 ) {
11531153 return false; // invalid
11541154 }
1155 - list( $start, $end ) = IP::parseRange6( $ip );
 1155+ list( $start, $end ) = IP::parseRange( $ip );
11561156 return array( 'cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end ) );
11571157 } elseif ( preg_match( '#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#', $ip ) ) {
11581158 // 32 bit IPv4

Follow-up revisions

RevisionCommit summaryAuthorDate
r76569Fix ranges formatting issue when testing for (g,z) in IPv6 address...hashar12:08, 12 November 2010
r76592* parseCIDR() should use parseCIDR6() for IPv6 (plus the later is private sin...aaron20:08, 12 November 2010
r78097Fixed r76560: one more caller of now-private toUnsigned6aaron22:18, 8 December 2010

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r20435*Run isIPv6() before trim/uppercasingaaron05:23, 14 March 2007
r76514* Followed-up r76267:...aaron12:18, 11 November 2010

Comments

#Comment by Hashar (talk | contribs)   12:09, 12 November 2010

Thanks Aaron !!!!!!!

#Comment by Brion VIBBER (talk | contribs)   22:55, 6 September 2011

The private ranges in IP::isPublic6() appear to be incorrect; per notes on bug 30739 it looks like it should look for private addresses in fc00::/7 rather than 00fc::/7.

Currently, publicly routable addresses get misinterpreted as private due to having a prefix anywhere between 00fc and fbff...

#Comment by Aaron Schulz (talk | contribs)   23:20, 6 September 2011

See r96386.

#Comment by Brion VIBBER (talk | contribs)   23:21, 6 September 2011

Confirmed that this fixes the test cases (and the equiv change was confirmed to fix the reported live problem). Thanks!

Status & tagging log