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