29define(
'RE_IP_BYTE',
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])' );
32define(
'RE_IP_PREFIX',
'(3[0-2]|[12]?\d)' );
37define(
'RE_IPV6_WORD',
'([0-9A-Fa-f]{1,4})' );
38define(
'RE_IPV6_PREFIX',
'(12[0-8]|1[01][0-9]|[1-9]?\d)' );
53define(
'RE_IPV6_GAP',
':(?:0+:)*(?::(?:0+:)*)?' );
54define(
'RE_IPV6_V4_PREFIX',
'0*' .
RE_IPV6_GAP .
'(?:ffff:)?' );
57define(
'IP_ADDRESS_STRING',
79 public static function isIPAddress( $ip ) {
90 public static function isIPv6( $ip ) {
101 public static function isIPv4( $ip ) {
113 public static function isValid( $ip ) {
114 return ( preg_match(
'/^' .
RE_IP_ADD .
'$/', $ip )
128 return self::isValidRange( $ipRange );
142 || preg_match(
'/^' .
RE_IP_RANGE .
'$/', $ipRange ) );
162 if ( !self::isIPAddress( $ip ) ) {
165 if ( self::isIPv4( $ip ) ) {
167 $ip = preg_replace(
'/(?:^|(?<=\.))0+(?=[1-9]|0\.|0$)/',
'', $ip );
171 $ip = strtoupper( $ip );
173 $abbrevPos = strpos( $ip,
'::' );
174 if ( $abbrevPos !==
false ) {
177 $CIDRStart = strpos( $ip,
"/" );
178 $addressEnd = ( $CIDRStart !==
false )
182 if ( $abbrevPos == 0 ) {
184 $extra = ( $ip ==
'::' ) ?
'0' :
'';
187 } elseif ( $abbrevPos == ( $addressEnd - 1 ) ) {
197 $ip = str_replace(
'::',
198 str_repeat( $repeat, $pad - substr_count( $ip,
':' ) ) . $extra,
203 $ip = preg_replace(
'/(^|:)0+(' .
RE_IPV6_WORD .
')/',
'$1$2', $ip );
216 $ip = self::sanitizeIP( $ip );
217 if ( self::isIPv6( $ip ) ) {
219 if ( strpos( $ip,
'/' ) !==
false ) {
220 list( $ip, $cidr ) = explode(
'/', $ip, 2 );
222 list( $ip, $cidr ) = [ $ip,
'' ];
226 $longest = $longestPos =
false;
228 '!(?:^|:)0(?::0)+(?:$|:)!', $ip, $m, PREG_OFFSET_CAPTURE, $offset
230 list( $match, $pos ) = $m[0];
231 if ( strlen( $match ) > strlen( $longest ) ) {
235 $offset = ( $pos + strlen( $match ) );
237 if ( $longest !==
false ) {
239 $ip = substr_replace( $ip,
'::', $longestPos, strlen( $longest ) );
242 if ( $cidr !==
'' ) {
243 $ip =
"{$ip}/{$cidr}";
246 $ip = strtolower( $ip );
269 if ( substr( $both, 0, 1 ) ===
'[' ) {
270 if ( preg_match(
'/^\[(' .
RE_IPV6_ADD .
')\](?::(?P<port>\d+))?$/', $both, $m ) ) {
271 if ( isset( $m[
'port'] ) ) {
272 return [ $m[1], intval( $m[
'port'] ) ];
274 return [ $m[1],
false ];
281 $numColons = substr_count( $both,
':' );
282 if ( $numColons >= 2 ) {
284 if ( preg_match(
'/^' .
RE_IPV6_ADD .
'$/', $both ) ) {
285 return [ $both,
false ];
291 if ( $numColons >= 1 ) {
293 $bits = explode(
':', $both );
294 if ( preg_match(
'/^\d+/', $bits[1] ) ) {
295 return [ $bits[0], intval( $bits[1] ) ];
303 return [ $both,
false ];
318 if ( strpos( $host,
':' ) !==
false ) {
321 if ( $defaultPort !==
false && $port == $defaultPort ) {
324 return "$host:$port";
334 public static function formatHex( $hex ) {
335 if ( substr( $hex, 0, 3 ) ==
'v6-' ) {
336 return self::hexToOctet( substr( $hex, 3 ) );
338 return self::hexToQuad( $hex );
348 public static function hexToOctet( $ip_hex ) {
350 $ip_hex = str_pad( strtoupper( $ip_hex ), 32,
'0', STR_PAD_LEFT );
352 $ip_oct = substr( $ip_hex, 0, 4 );
353 for ( $n = 1; $n < 8; $n++ ) {
354 $ip_oct .=
':' . substr( $ip_hex, 4 * $n, 4 );
357 $ip_oct = preg_replace(
'/(^|:)0+(' .
RE_IPV6_WORD .
')/',
'$1$2', $ip_oct );
368 public static function hexToQuad( $ip_hex ) {
370 $ip_hex = str_pad( strtoupper( $ip_hex ), 8,
'0', STR_PAD_LEFT );
373 for ( $i = 0; $i < 4; $i++ ) {
377 $s .= base_convert( substr( $ip_hex, $i * 2, 2 ), 16, 10 );
390 public static function isPublic( $ip ) {
391 static $privateSet =
null;
392 if ( !$privateSet ) {
393 $privateSet =
new IPSet( [
394 '10.0.0.0/8', # RFC 1918 (
private)
395 '172.16.0.0/12', # RFC 1918 (
private)
396 '192.168.0.0/16', # RFC 1918 (
private)
398 '127.0.0.0/8', # loopback
399 'fc00::/7', # RFC 4193 (local)
400 '0:0:0:0:0:0:0:1', # loopback
401 '169.254.0.0/16', # link-local
402 'fe80::/10', # link-local
405 return !$privateSet->match( $ip );
419 public static function toHex( $ip ) {
420 if ( self::isIPv6( $ip ) ) {
421 $n =
'v6-' . self::IPv6ToRawHex( $ip );
422 } elseif ( self::isIPv4( $ip ) ) {
425 $ip = self::sanitizeIP( $ip );
429 # On 32-bit platforms (and on Windows), 2^32 does not fit into an int,
430 # so $n becomes a float. We convert it to string instead.
431 if ( is_float( $n ) ) {
435 if ( $n !==
false ) {
436 # Floating points can handle the conversion; faster than Wikimedia\base_convert()
437 $n = strtoupper( str_pad( base_convert( $n, 10, 16 ), 8,
'0', STR_PAD_LEFT ) );
453 $ip = self::sanitizeIP( $ip );
458 foreach ( explode(
':', $ip ) as $v ) {
459 $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
472 public static function parseCIDR( $range ) {
473 if ( self::isIPv6( $range ) ) {
474 return self::parseCIDR6( $range );
476 $parts = explode(
'/', $range, 2 );
477 if ( count( $parts ) != 2 ) {
478 return [
false,
false ];
480 list( $network, $bits ) = $parts;
481 $network = ip2long( $network );
482 if ( $network !==
false && is_numeric( $bits ) && $bits >= 0 && $bits <= 32 ) {
486 $network &= ~( ( 1 << ( 32 - $bits ) ) - 1 );
488 # Convert to unsigned
489 if ( $network < 0 ) {
490 $network += pow( 2, 32 );
497 return [ $network, $bits ];
517 if ( strpos( $range,
'/' ) !==
false ) {
518 if ( self::isIPv6( $range ) ) {
519 return self::parseRange6( $range );
521 list( $network, $bits ) = self::parseCIDR( $range );
522 if ( $network ===
false ) {
523 $start = $end =
false;
525 $start = sprintf(
'%08X', $network );
526 $end = sprintf(
'%08X', $network + pow( 2, ( 32 - $bits ) ) - 1 );
529 } elseif ( strpos( $range,
'-' ) !==
false ) {
530 list( $start, $end ) = array_map(
'trim', explode(
'-', $range, 2 ) );
531 if ( self::isIPv6( $start ) && self::isIPv6( $end ) ) {
532 return self::parseRange6( $range );
534 if ( self::isIPv4( $start ) && self::isIPv4( $end ) ) {
535 $start = self::toHex( $start );
536 $end = self::toHex( $end );
537 if ( $start > $end ) {
538 $start = $end =
false;
541 $start = $end =
false;
545 $start = $end = self::toHex( $range );
547 if ( $start ===
false || $end ===
false ) {
548 return [
false,
false ];
550 return [ $start, $end ];
562 private static function parseCIDR6( $range ) {
563 # Explode into <expanded IP,range>
564 $parts = explode(
'/', self::sanitizeIP( $range ), 2 );
565 if ( count( $parts ) != 2 ) {
566 return [
false,
false ];
568 list( $network, $bits ) = $parts;
569 $network = self::IPv6ToRawHex( $network );
570 if ( $network !==
false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) {
574 # Native 32 bit functions WONT work here!!!
575 # Convert to a padded binary number
576 $network = Wikimedia\base_convert( $network, 16, 2, 128 );
577 # Truncate the last (128-$bits) bits and replace them with zeros
578 $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
579 # Convert back to an integer
580 $network = Wikimedia\base_convert( $network, 2, 10 );
587 return [ $network, (int)$bits ];
605 $range = self::sanitizeIP( $range );
607 if ( strpos( $range,
'/' ) !==
false ) {
608 list( $network, $bits ) = self::parseCIDR6( $range );
609 if ( $network ===
false ) {
610 $start = $end =
false;
612 $start = Wikimedia\base_convert( $network, 10, 16, 32,
false );
613 # Turn network to binary (again)
614 $end = Wikimedia\base_convert( $network, 10, 2, 128 );
615 # Truncate the last (128-$bits) bits and replace them with ones
616 $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT );
618 $end = Wikimedia\base_convert( $end, 2, 16, 32,
false );
619 # see toHex() comment
620 $start =
"v6-$start";
624 } elseif ( strpos( $range,
'-' ) !==
false ) {
625 list( $start, $end ) = array_map(
'trim', explode(
'-', $range, 2 ) );
626 $start = self::toHex( $start );
627 $end = self::toHex( $end );
628 if ( $start > $end ) {
629 $start = $end =
false;
633 $start = $end = self::toHex( $range );
635 if ( $start ===
false || $end ===
false ) {
636 return [
false,
false ];
638 return [ $start, $end ];
652 public static function isInRange( $addr, $range ) {
653 $hexIP = self::toHex( $addr );
654 list( $start, $end ) = self::parseRange( $range );
656 return ( strcmp( $hexIP, $start ) >= 0 &&
657 strcmp( $hexIP, $end ) <= 0 );
670 public static function isInRanges( $ip, $ranges ) {
671 foreach ( $ranges as $range ) {
672 if ( self::isInRange( $ip, $range ) ) {
691 $addr = preg_replace(
'/\%.*/',
'', $addr );
693 if ( self::isValid( $addr ) ) {
697 if ( strpos( $addr,
':' ) !==
false && strpos( $addr,
'.' ) !==
false ) {
698 $addr = substr( $addr, strrpos( $addr,
':' ) + 1 );
699 if ( self::isIPv4( $addr ) ) {
705 if ( preg_match(
'/^0*' .
RE_IPV6_GAP .
'1$/', $addr, $m ) ) {
715 return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
728 list( , $bits ) = self::parseCIDR( $range );
729 list( $start, ) = self::parseRange( $range );
730 $start = self::formatHex( $start );
731 if ( $bits ===
false ) {
735 return "$start/$bits";
744 public static function getSubnet( $ip ) {
747 if ( self::isIPv6( $ip ) ) {
748 $parts = self::parseRange(
"$ip/64" );
750 } elseif ( preg_match(
'/^(\d+\.\d+\.\d+)\.\d+$/', $ip,
$matches ) ) {
A collection of public static functions to play with IP address and IP ranges.
static combineHostAndPort( $host, $port, $defaultPort=false)
Given a host name and a port, combine them into host/port string like you might find in a URL.
static isValid( $ip)
Validate an IP address.
static prettifyIP( $ip)
Prettify an IP for display to end users.
static isIPv4( $ip)
Given a string, determine if it as valid IP in IPv4 only.
static parseRange( $range)
Given a string range in a number of formats, return the start and end of the range in hexadecimal.
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
static formatHex( $hex)
Convert an IPv4 or IPv6 hexadecimal representation back to readable format.
static parseCIDR6( $range)
Convert a network specification in IPv6 CIDR notation to an integer network and a number of bits.
static IPv6ToRawHex( $ip)
Given an IPv6 address in octet notation, returns a pure hex string.
static canonicalize( $addr)
Convert some unusual representations of IPv4 addresses to their canonical dotted quad representation.
static getSubnet( $ip)
Returns the subnet of a given IP.
static isValidBlock( $ipRange)
Validate an IP range (valid address with a valid CIDR prefix).
static parseRange6( $range)
Given a string range in a number of formats, return the start and end of the range in hexadecimal.
static sanitizeRange( $range)
Gets rid of unneeded numbers in quad-dotted/octet IP strings For example, 127.111....
static hexToQuad( $ip_hex)
Converts a hexadecimal number to an IPv4 address in quad-dotted notation.
static toHex( $ip)
Return a zero-padded upper case hexadecimal representation of an IP address.
static isPublic( $ip)
Determine if an IP address really is an IP address, and if it is public, i.e.
static hexToOctet( $ip_hex)
Converts a hexadecimal number to an IPv6 address in octet notation.
static isInRange( $addr, $range)
Determine if a given IPv4/IPv6 address is in a given CIDR network.
static isValidRange( $ipRange)
Validate an IP range (valid address with a valid CIDR prefix).
static isIPv6( $ip)
Given a string, determine if it as valid IP in IPv6 only.
static isInRanges( $ip, $ranges)
Determines if an IP address is a list of CIDR a.b.c.d/n ranges.
static parseCIDR( $range)
Convert a network specification in CIDR notation to an integer network and a number of bits.
static splitHostAndPort( $both)
Given a host/port string, like one might find in the host part of a URL per RFC 2732,...
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
processing should stop and the error should be shown to the user * false
MediaWiki has optional support for a high distributed memory object caching system For general information on but for a larger site with heavy like it should help lighten the load on the database servers by caching data and objects in Ubuntu and probably other Linux distributions If there s no package available for your you can compile it from epoll rt patch for Linux is current Memcached and libevent are under BSD style licenses The server should run on Linux and other Unix like systems you can run multiple servers on one machine or on multiple machines on a network