My changes in r96548 were too radical, stripping of edit commits (oops!) and rollback...
[lhc/web/wiklou.git] / includes / IP.php
index 9b55ad2..152236f 100644 (file)
@@ -185,6 +185,81 @@ class IP {
                return $ip;
        }
 
+       /**
+        * Given a host/port string, like one might find in the host part of a URL
+        * per RFC 2732, split the hostname part and the port part and return an
+        * array with an element for each. If there is no port part, the array will
+        * have false in place of the port. If the string was invalid in some way,
+        * false is returned.
+        *
+        * This was easy with IPv4 and was generally done in an ad-hoc way, but
+        * with IPv6 it's somewhat more complicated due to the need to parse the
+        * square brackets and colons.
+        *
+        * A bare IPv6 address is accepted despite the lack of square brackets.
+        *
+        * @param $both The string with the host and port
+        * @return array
+        */
+       public static function splitHostAndPort( $both ) {
+               if ( substr( $both, 0, 1 ) === '[' ) {
+                       if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P<port>\d+))?$/', $both, $m ) ) {
+                               if ( isset( $m['port'] ) ) {
+                                       return array( $m[1], intval( $m['port'] ) );
+                               } else {
+                                       return array( $m[1], false );
+                               }
+                       } else {
+                               // Square bracket found but no IPv6
+                               return false;
+                       }
+               }
+               $numColons = substr_count( $both, ':' );
+               if ( $numColons >= 2 ) {
+                       // Is it a bare IPv6 address?
+                       if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) {
+                               return array( $both, false );
+                       } else {
+                               // Not valid IPv6, but too many colons for anything else
+                               return false;
+                       }
+               }
+               if ( $numColons >= 1 ) {
+                       // Host:port?
+                       $bits = explode( ':', $both );
+                       if ( preg_match( '/^\d+/', $bits[1] ) ) {
+                               return array( $bits[0], intval( $bits[1] ) );
+                       } else {
+                               // Not a valid port
+                               return false;
+                       }
+               }
+               // Plain hostname
+               return array( $both, false );
+       }
+
+       /**
+        * Given a host name and a port, combine them into host/port string like
+        * you might find in a URL. If the host contains a colon, wrap it in square
+        * brackets like in RFC 2732. If the port matches the default port, omit
+        * the port specification
+        *
+        * @param $host string
+        * @param $port int
+        * @param $defaultPort bool|int
+        * @return string
+        */
+       public static function combineHostAndPort( $host, $port, $defaultPort = false ) {
+               if ( strpos( $host, ':' ) !== false ) {
+                       $host = "[$host]";
+               }
+               if ( $defaultPort !== false && $port == $defaultPort ) {
+                       return $host;
+               } else {
+                       return "$host:$port";
+               }
+       }
+
        /**
         * Given an unsigned integer, returns an IPv6 address in octet notation
         *
@@ -303,7 +378,7 @@ class IP {
                static $privateRanges = false;
                if ( !$privateRanges ) {
                        $privateRanges = array(
-                               array( 'fc::', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ), # RFC 4193 (local)
+                               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
                        );
                }
@@ -379,6 +454,10 @@ class IP {
                return $n;
        }
 
+       /**
+        * @param $ip
+        * @return String
+        */
        private static function toUnsigned6( $ip ) {
                return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
        }
@@ -478,6 +557,8 @@ class IP {
         * Convert a network specification in IPv6 CIDR notation to an
         * integer network and a number of bits
         *
+        * @param $range
+        *
         * @return array(string, int)
         */
        private static function parseCIDR6( $range ) {
@@ -515,6 +596,9 @@ class IP {
         *     2001:0db8:85a3::7344/96                                   CIDR
         *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
         *     2001:0db8:85a3::7344/96                                   Single IP
+        *
+        * @param $range
+        *
         * @return array(string, string)
         */
        private static function parseRange6( $range ) {