Wordpress 4.6
[autoinstalls/wordpress.git] / wp-includes / Requests / IPv6.php
1 <?php
2 /**
3  * Class to validate and to work with IPv6 addresses
4  *
5  * @package Requests
6  * @subpackage Utilities
7  */
8
9 /**
10  * Class to validate and to work with IPv6 addresses
11  *
12  * This was originally based on the PEAR class of the same name, but has been
13  * entirely rewritten.
14  *
15  * @package Requests
16  * @subpackage Utilities
17  */
18 class Requests_IPv6 {
19         /**
20          * Uncompresses an IPv6 address
21          *
22          * RFC 4291 allows you to compress consecutive zero pieces in an address to
23          * '::'. This method expects a valid IPv6 address and expands the '::' to
24          * the required number of zero pieces.
25          *
26          * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
27          *           ::1         ->  0:0:0:0:0:0:0:1
28          *
29          * @author Alexander Merz <alexander.merz@web.de>
30          * @author elfrink at introweb dot nl
31          * @author Josh Peck <jmp at joshpeck dot org>
32          * @copyright 2003-2005 The PHP Group
33          * @license http://www.opensource.org/licenses/bsd-license.php
34          * @param string $ip An IPv6 address
35          * @return string The uncompressed IPv6 address
36          */
37         public static function uncompress($ip) {
38                 if (substr_count($ip, '::') !== 1) {
39                         return $ip;
40                 }
41
42                 list($ip1, $ip2) = explode('::', $ip);
43                 $c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':');
44                 $c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':');
45
46                 if (strpos($ip2, '.') !== false) {
47                         $c2++;
48                 }
49                 // ::
50                 if ($c1 === -1 && $c2 === -1) {
51                         $ip = '0:0:0:0:0:0:0:0';
52                 }
53                 // ::xxx
54                 else if ($c1 === -1) {
55                         $fill = str_repeat('0:', 7 - $c2);
56                         $ip = str_replace('::', $fill, $ip);
57                 }
58                 // xxx::
59                 else if ($c2 === -1) {
60                         $fill = str_repeat(':0', 7 - $c1);
61                         $ip = str_replace('::', $fill, $ip);
62                 }
63                 // xxx::xxx
64                 else {
65                         $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
66                         $ip = str_replace('::', $fill, $ip);
67                 }
68                 return $ip;
69         }
70
71         /**
72          * Compresses an IPv6 address
73          *
74          * RFC 4291 allows you to compress consecutive zero pieces in an address to
75          * '::'. This method expects a valid IPv6 address and compresses consecutive
76          * zero pieces to '::'.
77          *
78          * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
79          *           0:0:0:0:0:0:0:1        ->  ::1
80          *
81          * @see uncompress()
82          * @param string $ip An IPv6 address
83          * @return string The compressed IPv6 address
84          */
85         public static function compress($ip) {
86                 // Prepare the IP to be compressed
87                 $ip = self::uncompress($ip);
88                 $ip_parts = self::split_v6_v4($ip);
89
90                 // Replace all leading zeros
91                 $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
92
93                 // Find bunches of zeros
94                 if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
95                         $max = 0;
96                         $pos = null;
97                         foreach ($matches[0] as $match) {
98                                 if (strlen($match[0]) > $max) {
99                                         $max = strlen($match[0]);
100                                         $pos = $match[1];
101                                 }
102                         }
103
104                         $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
105                 }
106
107                 if ($ip_parts[1] !== '') {
108                         return implode(':', $ip_parts);
109                 }
110                 else {
111                         return $ip_parts[0];
112                 }
113         }
114
115         /**
116          * Splits an IPv6 address into the IPv6 and IPv4 representation parts
117          *
118          * RFC 4291 allows you to represent the last two parts of an IPv6 address
119          * using the standard IPv4 representation
120          *
121          * Example:  0:0:0:0:0:0:13.1.68.3
122          *           0:0:0:0:0:FFFF:129.144.52.38
123          *
124          * @param string $ip An IPv6 address
125          * @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
126          */
127         protected static function split_v6_v4($ip) {
128                 if (strpos($ip, '.') !== false) {
129                         $pos = strrpos($ip, ':');
130                         $ipv6_part = substr($ip, 0, $pos);
131                         $ipv4_part = substr($ip, $pos + 1);
132                         return array($ipv6_part, $ipv4_part);
133                 }
134                 else {
135                         return array($ip, '');
136                 }
137         }
138
139         /**
140          * Checks an IPv6 address
141          *
142          * Checks if the given IP is a valid IPv6 address
143          *
144          * @param string $ip An IPv6 address
145          * @return bool true if $ip is a valid IPv6 address
146          */
147         public static function check_ipv6($ip) {
148                 $ip = self::uncompress($ip);
149                 list($ipv6, $ipv4) = self::split_v6_v4($ip);
150                 $ipv6 = explode(':', $ipv6);
151                 $ipv4 = explode('.', $ipv4);
152                 if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
153                         foreach ($ipv6 as $ipv6_part) {
154                                 // The section can't be empty
155                                 if ($ipv6_part === '') {
156                                         return false;
157                                 }
158
159                                 // Nor can it be over four characters
160                                 if (strlen($ipv6_part) > 4) {
161                                         return false;
162                                 }
163
164                                 // Remove leading zeros (this is safe because of the above)
165                                 $ipv6_part = ltrim($ipv6_part, '0');
166                                 if ($ipv6_part === '') {
167                                         $ipv6_part = '0';
168                                 }
169
170                                 // Check the value is valid
171                                 $value = hexdec($ipv6_part);
172                                 if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
173                                         return false;
174                                 }
175                         }
176                         if (count($ipv4) === 4) {
177                                 foreach ($ipv4 as $ipv4_part) {
178                                         $value = (int) $ipv4_part;
179                                         if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
180                                                 return false;
181                                         }
182                                 }
183                         }
184                         return true;
185                 }
186                 else {
187                         return false;
188                 }
189         }
190 }