Subversion Repositories php_utils

Rev

Rev 2 | Rev 46 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2 Rev 5
1
<?php
1
<?php
2
 
2
 
3
/*
3
/*
4
 * IPv6 functions for PHP
4
 * IPv6 functions for PHP
5
 * Copyright 2012-2021 Daniel Marschall, ViaThinkSoft
5
 * Copyright 2012-2021 Daniel Marschall, ViaThinkSoft
6
 * Version 2021-05-21
6
 * Version 2021-05-21
7
 *
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
10
 * You may obtain a copy of the License at
11
 *
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
18
 * limitations under the License.
19
 */
19
 */
20
 
20
 
21
# This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present)
21
# This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present)
22
 
22
 
23
// TODO: oop, exceptions?
23
// TODO: oop, exceptions?
24
// TODO: variant without gmp ?
24
// TODO: variant without gmp ?
25
// TODO: IPv6 resolution 'ffff::192.168.69.1' -> 'ffff:0000:0000:0000:0000:0000:c0a8:4501' does not work!
25
// TODO: IPv6 resolution 'ffff::192.168.69.1' -> 'ffff:0000:0000:0000:0000:0000:c0a8:4501' does not work!
26
 
26
 
27
if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php';
27
if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php';
28
 
28
 
29
define('GMP_ONE', gmp_init('1'));
29
define('GMP_ONE', gmp_init('1'));
30
 
30
 
31
// Very small self-test:
31
// Very small self-test:
32
/*
32
/*
33
function ipv6_selftest() {
33
function ipv6_selftest() {
34
        $iv_b = 'c0ff:ee00::';
34
        $iv_b = 'c0ff:ee00::';
35
        $iv_m = 32;
35
        $iv_m = 32;
36
        $r = ipv6_cidr2range($iv_b, $iv_m);
36
        $r = ipv6_cidr2range($iv_b, $iv_m);
37
        echo "$iv_b/$iv_m => $r[0] - $r[1]\n";
37
        echo "$iv_b/$iv_m => $r[0] - $r[1]\n";
38
 
38
 
39
        $rev = ipv6_range2cidr($r[0], $r[1]);
39
        $rev = ipv6_range2cidr($r[0], $r[1]);
40
        $rev = implode("\n", $rev);
40
        $rev = implode("\n", $rev);
41
        echo "$r[0] - $r[1] => $rev [";
41
        echo "$r[0] - $r[1] => $rev [";
42
        $ok = $rev == "$iv_b/$iv_m";
42
        $ok = $rev == "$iv_b/$iv_m";
43
        echo $ok ? 'OK' : 'Mismatch';
43
        echo $ok ? 'OK' : 'Mismatch';
44
        echo "]\n";
44
        echo "]\n";
45
        echo "In-CIDR-Test: ";
45
        echo "In-CIDR-Test: ";
46
        echo ipv6_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail';
46
        echo ipv6_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail';
47
        echo "\n";
47
        echo "\n";
48
}
48
}
49
ipv6_selftest();
49
ipv6_selftest();
50
*/
50
*/
51
 
51
 
52
$cache_ipv6_cidr2range = array();
52
$cache_ipv6_cidr2range = array();
53
function ipv6_cidr2range($baseip_or_cidr, $subnet='') {
53
function ipv6_cidr2range($baseip_or_cidr, $subnet='') {
54
        # (C) 2012 ViaThinkSoft
54
        # (C) 2012 ViaThinkSoft
55
        # Version 1.1
55
        # Version 1.1
56
        # This function converts an CIDR notation <baseip>/<subnet> into an IPv6 address block array($low_ip, $high_ip)
56
        # This function converts an CIDR notation <baseip>/<subnet> into an IPv6 address block array($low_ip, $high_ip)
57
 
57
 
58
        global $cache_ipv6_cidr2range;
58
        global $cache_ipv6_cidr2range;
59
        $vvv = $baseip_or_cidr.'|'.$subnet;
59
        $vvv = $baseip_or_cidr.'|'.$subnet;
60
        if (isset($cache_ipv6_cidr2range[$vvv])) return $cache_ipv6_cidr2range[$vvv];
60
        if (isset($cache_ipv6_cidr2range[$vvv])) return $cache_ipv6_cidr2range[$vvv];
61
 
61
 
62
        if (strpos($baseip_or_cidr, '/') !== false) {
62
        if (strpos($baseip_or_cidr, '/') !== false) {
63
                $tmp = explode('/', $baseip_or_cidr, 2);
63
                $tmp = explode('/', $baseip_or_cidr, 2);
64
                $baseip_or_cidr = $tmp[0];
64
                $baseip_or_cidr = $tmp[0];
65
                $subnet = $tmp[1];
65
                $subnet = $tmp[1];
66
                unset($tmp);
66
                unset($tmp);
67
        }
67
        }
68
 
68
 
69
        if (($subnet < 0) || ($subnet > 128)) {
69
        if (($subnet < 0) || ($subnet > 128)) {
70
                $cache_ipv6_cidr2range[$vvv] = false;
70
                $cache_ipv6_cidr2range[$vvv] = false;
71
                return false;
71
                return false;
72
        }
72
        }
73
 
73
 
74
        $maxint128 = gmp_sub(gmp_pow('2', 128), GMP_ONE); # TODO: GMP_TWO ?
74
        $maxint128 = gmp_sub(gmp_pow('2', 128), GMP_ONE); # TODO: GMP_TWO ?
75
        $netmask = gmp_shiftl($maxint128, 128-$subnet);
75
        $netmask = gmp_shiftl($maxint128, 128-$subnet);
76
        $netmask = gmp_and($netmask, $maxint128); // crop to 128 bit
76
        $netmask = gmp_and($netmask, $maxint128); // crop to 128 bit
77
        $wildcard = gmp_xor($maxint128, $netmask);
77
        $wildcard = gmp_xor($maxint128, $netmask);
78
 
78
 
79
        $x = gmp_and(ip2long6($baseip_or_cidr), $netmask);
79
        $x = gmp_and(ip2long6($baseip_or_cidr), $netmask);
80
        $nums = $wildcard;
80
        $nums = $wildcard;
81
        $low = long2ip6($x);
81
        $low = long2ip6($x);
82
        $high = long2ip6(gmp_add($x, $nums));
82
        $high = long2ip6(gmp_add($x, $nums));
83
 
83
 
84
        $out = array($low, $high);
84
        $out = array($low, $high);
85
        $cache_ipv6_cidr2range[$vvv] = $out;
85
        $cache_ipv6_cidr2range[$vvv] = $out;
86
        return $out;
86
        return $out;
87
}
87
}
88
 
88
 
89
$cache_ipv6_range2cidr = array();
89
$cache_ipv6_range2cidr = array();
90
function ipv6_range2cidr($baseip, $topip) {
90
function ipv6_range2cidr($baseip, $topip) {
91
        # (C) 2012 ViaThinkSoft
91
        # (C) 2012 ViaThinkSoft
92
        # Version 1.0
92
        # Version 1.0
93
        # This function converts an IPv6 address block into valid CIDR blocks (There may be multiple blocks!)
93
        # This function converts an IPv6 address block into valid CIDR blocks (There may be multiple blocks!)
94
 
94
 
95
        global $cache_ipv6_range2cidr;
95
        global $cache_ipv6_range2cidr;
96
        $vvv = $baseip.'|'.$topip;
96
        $vvv = $baseip.'|'.$topip;
97
        if (isset($cache_ipv6_range2cidr[$vvv])) return $cache_ipv6_range2cidr[$vvv];
97
        if (isset($cache_ipv6_range2cidr[$vvv])) return $cache_ipv6_range2cidr[$vvv];
98
 
98
 
99
        $out = array();
99
        $out = array();
100
        if (ipv6_cmp($baseip, $topip) > 0) {
100
        if (ipv6_cmp($baseip, $topip) > 0) {
101
                $cache_ipv6_range2cidr[$vvv] = false;
101
                $cache_ipv6_range2cidr[$vvv] = false;
102
                return false;
102
                return false;
103
        }
103
        }
104
        while (gmp_cmp(gmp_sub(ip2long6($baseip), GMP_ONE), ip2long6($topip)) != 0) {
104
        while (gmp_cmp(gmp_sub(ip2long6($baseip), GMP_ONE), ip2long6($topip)) != 0) {
105
                $i = -1;
105
                $i = -1;
106
                do {
106
                do {
107
                        $i++;
107
                        $i++;
108
                        $range = ipv6_cidr2range($baseip, $i);
108
                        $range = ipv6_cidr2range($baseip, $i);
109
                        $l = $range[0];
109
                        $l = $range[0];
110
                        $t = $range[1];
110
                        $t = $range[1];
111
                } while ((ipv6_cmp($l, $baseip) != 0) || (ipv6_cmp($t, $topip) > 0));
111
                } while ((ipv6_cmp($l, $baseip) != 0) || (ipv6_cmp($t, $topip) > 0));
112
 
112
 
113
                $out[] = "$baseip/$i";
113
                $out[] = "$baseip/$i";
114
                $baseip = ipv6_add($t, GMP_ONE);
114
                $baseip = ipv6_add($t, GMP_ONE);
115
        }
115
        }
116
 
116
 
117
        $cache_ipv6_range2cidr[$vvv] = $out;
117
        $cache_ipv6_range2cidr[$vvv] = $out;
118
        return $out;
118
        return $out;
119
}
119
}
120
 
120
 
121
function ipv6_add($baseip, $num) {
121
function ipv6_add($baseip, $num) {
122
        # (C) 2012 ViaThinkSoft
122
        # (C) 2012 ViaThinkSoft
123
        # Version 1.0
123
        # Version 1.0
124
 
124
 
125
        return long2ip6(gmp_add(ip2long6($baseip), $num));
125
        return long2ip6(gmp_add(ip2long6($baseip), $num));
126
}
126
}
127
 
127
 
128
function ipv6_sub($baseip, $num) {
128
function ipv6_sub($baseip, $num) {
129
        # (C) 2012 ViaThinkSoft
129
        # (C) 2012 ViaThinkSoft
130
        # Version 1.0
130
        # Version 1.0
131
 
131
 
132
        return long2ip6(gmp_sub(ip2long6($baseip), $num));
132
        return long2ip6(gmp_sub(ip2long6($baseip), $num));
133
}
133
}
134
 
134
 
135
function ipv6_cmp($a, $b) {
135
function ipv6_cmp($a, $b) {
136
        # (C) 2012 ViaThinkSoft
136
        # (C) 2012 ViaThinkSoft
137
        # Version 1.0
137
        # Version 1.0
138
 
138
 
139
        return gmp_cmp(ip2long6($a), ip2long6($b));
139
        return gmp_cmp(ip2long6($a), ip2long6($b));
140
}
140
}
141
 
141
 
142
$cache_ipv6_in_cidr = array();
142
$cache_ipv6_in_cidr = array();
143
function ipv6_in_cidr($haystack, $needle) {
143
function ipv6_in_cidr($haystack, $needle) {
144
        # (C) 2012 ViaThinkSoft
144
        # (C) 2012 ViaThinkSoft
145
        # Version 1.1
145
        # Version 1.1
146
 
146
 
147
        global $cache_ipv6_in_cidr;
147
        global $cache_ipv6_in_cidr;
148
        $vvv = $haystack.'|'.$needle;
148
        $vvv = $haystack.'|'.$needle;
149
        if (isset($cache_ipv6_in_cidr[$vvv])) return $cache_ipv6_in_cidr[$vvv];
149
        if (isset($cache_ipv6_in_cidr[$vvv])) return $cache_ipv6_in_cidr[$vvv];
150
 
150
 
151
        $x = explode('/', $haystack);
151
        $x = explode('/', $haystack);
152
        $ha = ipv6_cidr2range($x[0], $x[1]);
152
        $ha = ipv6_cidr2range($x[0], $x[1]);
153
 
153
 
154
        $x = explode('/', $needle);
154
        $x = explode('/', $needle);
155
        if (!isset($x[1])) $x[1] = 128; // single IP
155
        if (!isset($x[1])) $x[1] = 128; // single IP
156
        $ne = ipv6_cidr2range($x[0], $x[1]);
156
        $ne = ipv6_cidr2range($x[0], $x[1]);
157
 
157
 
158
        $ha_low = ip2long6($ha[0]);
158
        $ha_low = ip2long6($ha[0]);
159
        $ha_hig = ip2long6($ha[1]);
159
        $ha_hig = ip2long6($ha[1]);
160
        $ne_low = ip2long6($ne[0]);
160
        $ne_low = ip2long6($ne[0]);
161
        $ne_hig = ip2long6($ne[1]);
161
        $ne_hig = ip2long6($ne[1]);
162
 
162
 
163
        # HA:    low[                               ]high
163
        # HA:    low[                               ]high
164
        # NE:            low[             ]high
164
        # NE:            low[             ]high
165
 
165
 
166
        $out = (gmp_cmp($ne_low, $ha_low) >= 0) && (gmp_cmp($ne_hig, $ha_hig) <= 0);
166
        $out = (gmp_cmp($ne_low, $ha_low) >= 0) && (gmp_cmp($ne_hig, $ha_hig) <= 0);
167
        $cache_ipv6_in_cidr[$vvv] = $out;
167
        $cache_ipv6_in_cidr[$vvv] = $out;
168
        return $out;
168
        return $out;
169
}
169
}
170
 
170
 
171
// IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y !
171
// IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y !
172
function ipv6_merge_address_blocks($data, $debug = false) {
172
function ipv6_merge_address_blocks($data, $debug = false) {
173
        # (C) 2012-2013 ViaThinkSoft
173
        # (C) 2012-2013 ViaThinkSoft
174
        # Version 2.2
174
        # Version 2.2
175
 
175
 
176
        if ($debug !== false) $STARTZEIT = time();
176
        if ($debug !== false) $STARTZEIT = time();
177
 
177
 
178
        // 1. Convert IPs to numbers
178
        // 1. Convert IPs to numbers
179
 
179
 
180
        $cmp_ary = array();
180
        $cmp_ary = array();
181
        foreach ($data as $a => &$b) {
181
        foreach ($data as $a => &$b) {
182
                $a = ip2long6($a);
182
                $a = ip2long6($a);
183
                $b = ip2long6($b);
183
                $b = ip2long6($b);
184
 
184
 
185
                $cmp_ary[gmp_strval($a)] = gmp_strval($b);
185
                $cmp_ary[gmp_strval($a)] = gmp_strval($b);
186
                unset($a);
186
                unset($a);
187
                unset($b);
187
                unset($b);
188
        }
188
        }
189
 
189
 
190
        // 2. Sort array
190
        // 2. Sort array
191
 
191
 
192
        ksort($cmp_ary);
192
        ksort($cmp_ary);
193
 
193
 
194
        // 3. Merge the blocks in an intelligent way (and remove redundant blocks)
194
        // 3. Merge the blocks in an intelligent way (and remove redundant blocks)
195
 
195
 
196
        # Merge overlapping blocks
196
        # Merge overlapping blocks
197
        #   [          ]
197
        #   [          ]
198
        #           [            ]   ->   [                    ]
198
        #           [            ]   ->   [                    ]
199
 
199
 
200
        # Merge neighbor blocks
200
        # Merge neighbor blocks
201
        #   [   ][   ]   ->   [        ]
201
        #   [   ][   ]   ->   [        ]
202
 
202
 
203
        # Remove redundant blocks
203
        # Remove redundant blocks
204
        #  [          ]   ->   [          ]
204
        #  [          ]   ->   [          ]
205
        #      [  ]
205
        #      [  ]
206
 
206
 
207
        $merge_count = 0;
207
        $merge_count = 0;
208
        $redundant_deleted_count = 0;
208
        $redundant_deleted_count = 0;
209
        $round_count = 0;
209
        $round_count = 0;
210
        do {
210
        do {
211
                if ($debug !== false) {
211
                if ($debug !== false) {
212
                        $LAUFZEIT = time() - $STARTZEIT;
212
                        $LAUFZEIT = time() - $STARTZEIT;
213
                        echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r";
213
                        echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r";
214
                }
214
                }
215
 
215
 
216
                $round_count++;
216
                $round_count++;
217
 
217
 
218
                $clean = true;
218
                $clean = true;
219
 
219
 
220
                foreach ($cmp_ary as $a => &$b) {
220
                foreach ($cmp_ary as $a => &$b) {
221
                        foreach ($cmp_ary as $x => &$y) {
221
                        foreach ($cmp_ary as $x => &$y) {
222
                                // x in range [a+1..b+1] ?
222
                                // x in range [a+1..b+1] ?
223
                                if (gmp_cmp(gmp_init($x), gmp_init($a)) <= 0) continue;
223
                                if (gmp_cmp(gmp_init($x), gmp_init($a)) <= 0) continue;
224
                                if (gmp_cmp(gmp_init($x), gmp_add(gmp_init($b), GMP_ONE)) > 0) break;
224
                                if (gmp_cmp(gmp_init($x), gmp_add(gmp_init($b), GMP_ONE)) > 0) break;
225
 
225
 
226
                                // Merge
226
                                // Merge
227
                                $clean = false;
227
                                $clean = false;
228
                                if (gmp_cmp(gmp_init($y), gmp_init($b)) > 0) {
228
                                if (gmp_cmp(gmp_init($y), gmp_init($b)) > 0) {
229
                                        $merge_count++;
229
                                        $merge_count++;
230
                                        $b = $y;
230
                                        $b = $y;
231
                                        unset($cmp_ary[$x]);
231
                                        unset($cmp_ary[$x]);
232
                                } else {
232
                                } else {
233
                                        $redundant_deleted_count++;
233
                                        $redundant_deleted_count++;
234
                                        unset($cmp_ary[$x]);
234
                                        unset($cmp_ary[$x]);
235
                                }
235
                                }
236
                        }
236
                        }
237
                }
237
                }
238
        } while (!$clean);
238
        } while (!$clean);
239
 
239
 
240
        if ($debug !== false) {
240
        if ($debug !== false) {
241
                $LAUFZEIT = time() - $STARTZEIT;
241
                $LAUFZEIT = time() - $STARTZEIT;
242
                echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n";
242
                echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n";
243
        }
243
        }
244
 
244
 
245
        // 4. Convert back to IPs
245
        // 4. Convert back to IPs
246
 
246
 
247
        $out_ary = array();
247
        $out_ary = array();
248
        foreach ($cmp_ary as $a => &$b) {
248
        foreach ($cmp_ary as $a => &$b) {
249
                $a = long2ip6(gmp_init($a));
249
                $a = long2ip6(gmp_init($a));
250
                $b = long2ip6(gmp_init($b));
250
                $b = long2ip6(gmp_init($b));
251
                $out_ary[$a] = $b;
251
                $out_ary[$a] = $b;
252
        }
252
        }
253
 
253
 
254
        return $out_ary;
254
        return $out_ary;
255
}
255
}
256
 
256
 
257
function ipv6_merge_arrays($data_a, $data_b) {
257
function ipv6_merge_arrays($data_a, $data_b) {
258
        # (C) 2012 ViaThinkSoft
258
        # (C) 2012 ViaThinkSoft
259
        # Version 1.2
259
        # Version 1.2
260
 
260
 
261
        $normalized_data_a = array();
261
        $normalized_data_a = array();
262
        foreach ($data_a as $from => &$to) {
262
        foreach ($data_a as $from => &$to) {
263
                $normalized_data_a[ipv6_normalize($from)] = ipv6_normalize($to);
263
                $normalized_data_a[ipv6_normalize($from)] = ipv6_normalize($to);
264
        }
264
        }
265
 
265
 
266
        $normalized_data_b = array();
266
        $normalized_data_b = array();
267
        foreach ($data_b as $from => &$to) {
267
        foreach ($data_b as $from => &$to) {
268
                $normalized_data_b[ipv6_normalize($from)] = ipv6_normalize($to);
268
                $normalized_data_b[ipv6_normalize($from)] = ipv6_normalize($to);
269
        }
269
        }
270
 
270
 
271
        $data = array();
271
        $data = array();
272
 
272
 
273
        foreach ($normalized_data_a as $from => &$to) {
273
        foreach ($normalized_data_a as $from => &$to) {
274
                if (isset($normalized_data_b[$from])) {
274
                if (isset($normalized_data_b[$from])) {
275
                        $data[$from] = ipv6_max($to, $normalized_data_b[$from]);
275
                        $data[$from] = ipv6_max($to, $normalized_data_b[$from]);
276
                } else {
276
                } else {
277
                        $data[$from] = $to;
277
                        $data[$from] = $to;
278
                }
278
                }
279
        }
279
        }
280
 
280
 
281
        foreach ($normalized_data_b as $from => &$to) {
281
        foreach ($normalized_data_b as $from => &$to) {
282
                if (!isset($normalized_data_a[$from])) {
282
                if (!isset($normalized_data_a[$from])) {
283
                        $data[$from] = $to;
283
                        $data[$from] = $to;
284
                }
284
                }
285
        }
285
        }
286
 
286
 
287
        return $data;
287
        return $data;
288
}
288
}
289
 
289
 
290
function ipv6_valid($ip) {
290
function ipv6_valid($ip) {
291
        # (C) 2012 ViaThinkSoft
291
        # (C) 2012 ViaThinkSoft
292
        # Version 1.0
292
        # Version 1.0
293
 
293
 
294
        return ip2long6($ip) !== false;
294
        return ip2long6($ip) !== false;
295
}
295
}
296
 
296
 
297
function ipv6_normalize($ip) {
297
function ipv6_normalize($ip) {
298
        # (C) 2012 ViaThinkSoft
298
        # (C) 2012 ViaThinkSoft
299
        # Version 1.0
299
        # Version 1.0
300
 
300
 
301
        # Example:
301
        # Example:
302
        # 2001:0000:0000::1 -> 2001::1
302
        # 2001:0000:0000::1 -> 2001::1
303
 
303
 
304
        $long = ip2long6($ip);
304
        $long = ip2long6($ip);
305
        if ($long == -1 || $long === FALSE) return false;
305
        if ($long == -1 || $long === FALSE) return false;
306
        return long2ip6($long);
306
        return long2ip6($long);
307
}
307
}
308
 
308
 
309
function ipv6_expand($ip) {
309
function ipv6_expand($ip) {
310
        # (C) 2012 ViaThinkSoft
310
        # (C) 2012 ViaThinkSoft
311
        # Version 1.0
311
        # Version 1.0
312
 
312
 
313
        # Example:
313
        # Example:
314
        # 2001::1 -> 2001:0000:0000:0000:0000:0000:0000:0000
314
        # 2001::1 -> 2001:0000:0000:0000:0000:0000:0000:0000
315
 
315
 
316
        $long = ip2long6($ip);
316
        $long = ip2long6($ip);
317
        if ($long == -1 || $long === FALSE) return false;
317
        if ($long == -1 || $long === FALSE) return false;
318
        return long2ip6($long, false);
318
        return long2ip6($long, false);
319
}
319
}
320
 
320
 
321
function ipv6_min($ip_a, $ip_b) {
321
function ipv6_min($ip_a, $ip_b) {
322
        # (C) 2012 ViaThinkSoft
322
        # (C) 2012 ViaThinkSoft
323
        # Version 1.0
323
        # Version 1.0
324
 
324
 
325
        if (ipv6_cmp($ip_a, $ip_b) == -1) {
325
        if (ipv6_cmp($ip_a, $ip_b) == -1) {
326
                return $ip_a;
326
                return $ip_a;
327
        } else {
327
        } else {
328
                return $ip_b;
328
                return $ip_b;
329
        }
329
        }
330
}
330
}
331
 
331
 
332
function ipv6_max($ip_a, $ip_b) {
332
function ipv6_max($ip_a, $ip_b) {
333
        # (C) 2012 ViaThinkSoft
333
        # (C) 2012 ViaThinkSoft
334
        # Version 1.0
334
        # Version 1.0
335
 
335
 
336
        if (ipv6_cmp($ip_a, $ip_b) == 1) {
336
        if (ipv6_cmp($ip_a, $ip_b) == 1) {
337
                return $ip_a;
337
                return $ip_a;
338
        } else {
338
        } else {
339
                return $ip_b;
339
                return $ip_b;
340
        }
340
        }
341
}
341
}
342
 
342
 
343
function ipv6_ipcount($data) {
343
function ipv6_ipcount($data) {
344
        # (C) 2012 ViaThinkSoft
344
        # (C) 2012 ViaThinkSoft
345
        # Version 1.0
345
        # Version 1.0
346
 
346
 
347
        $cnt = gmp_init('0');
347
        $cnt = gmp_init('0');
348
 
348
 
349
        foreach ($data as $from => &$to) {
349
        foreach ($data as $from => &$to) {
350
                $cnt = gmp_add($cnt, gmp_sub(ip2long6($to), ip2long6($from)));
350
                $cnt = gmp_add($cnt, gmp_sub(ip2long6($to), ip2long6($from)));
351
        }
351
        }
352
 
352
 
353
        return gmp_strval($cnt, 10);
353
        return gmp_strval($cnt, 10);
354
}
354
}
355
 
355
 
356
function ipv6_read_file($file) {
356
function ipv6_read_file($file) {
357
        # (C) 2012 ViaThinkSoft
357
        # (C) 2012 ViaThinkSoft
358
        # Version 1.0
358
        # Version 1.0
359
 
359
 
360
        $data = array();
360
        $data = array();
361
 
361
 
362
        $lines = file($file);
362
        $lines = file($file);
363
        foreach ($lines as &$line) {
363
        foreach ($lines as &$line) {
364
                $rng = ipv6_line2range($line);
364
                $rng = ipv6_line2range($line);
365
                $data[$rng[0]] = $rng[1];
365
                $data[$rng[0]] = $rng[1];
366
        }
366
        }
367
 
367
 
368
        return $data;
368
        return $data;
369
}
369
}
370
 
370
 
371
function ipv6_line2range($line) {
371
function ipv6_line2range($line) {
372
        # (C) 2012 ViaThinkSoft
372
        # (C) 2012 ViaThinkSoft
373
        # Version 1.0
373
        # Version 1.0
374
 
374
 
375
        $line = trim($line);
375
        $line = trim($line);
376
 
376
 
377
        if (strpos($line, '/') !== false) {
377
        if (strpos($line, '/') !== false) {
378
                $rng = ipv6_cidr2range($line);
378
                $rng = ipv6_cidr2range($line);
379
        } else {
379
        } else {
380
                $rng = explode('-', $line);
380
                $rng = explode('-', $line);
381
                $rng[0] = trim($rng[0]);
381
                $rng[0] = trim($rng[0]);
382
                $rng[1] = trim($rng[1]);
382
                $rng[1] = trim($rng[1]);
383
                $rng[0] = ipv6_normalize($rng[0]);
383
                $rng[0] = ipv6_normalize($rng[0]);
384
                if (!isset($rng[1])) $rng[1] = $rng[0];
384
                if (!isset($rng[1])) $rng[1] = $rng[0];
385
                $rng[1] = ipv6_normalize($rng[1]);
385
                $rng[1] = ipv6_normalize($rng[1]);
386
        }
386
        }
387
 
387
 
388
        return $rng;
388
        return $rng;
389
}
389
}
390
 
390
 
391
# ---
391
# ---
392
 
392
 
393
if (!function_exists('gmp_shiftl')) {
393
if (!function_exists('gmp_shiftl')) {
394
        function gmp_shiftl($x, $n) { // shift left
394
        function gmp_shiftl($x, $n) { // shift left
395
                // http://www.php.net/manual/en/ref.gmp.php#99788
395
                // http://www.php.net/manual/en/ref.gmp.php#99788
396
                return gmp_mul($x, gmp_pow('2', $n));
396
                return gmp_mul($x, gmp_pow('2', $n));
397
        }
397
        }
398
}
398
}
399
 
399
 
400
if (!function_exists('gmp_shiftr')) {
400
if (!function_exists('gmp_shiftr')) {
401
        function gmp_shiftr($x, $n) { // shift right
401
        function gmp_shiftr($x, $n) { // shift right
402
                // http://www.php.net/manual/en/ref.gmp.php#99788
402
                // http://www.php.net/manual/en/ref.gmp.php#99788
403
                return gmp_div($x, gmp_pow('2', $n));
403
                return gmp_div($x, gmp_pow('2', $n));
404
        }
404
        }
405
}
405
}
406
 
406
 
407
$cache_ip2long6 = array();
407
$cache_ip2long6 = array();
408
function ip2long6($ipv6) {
408
function ip2long6($ipv6) {
409
        // Source:
409
        // Source:
410
        // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
410
        // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
411
        // Slightly modified
411
        // Slightly modified
412
 
412
 
413
        global $cache_ip2long6;
413
        global $cache_ip2long6;
414
        if (isset($cache_ip2long6[$ipv6])) return $cache_ip2long6[$ipv6];
414
        if (isset($cache_ip2long6[$ipv6])) return $cache_ip2long6[$ipv6];
415
 
415
 
416
        if ($ipv6 == '') $ipv6 = '::';
416
        if ($ipv6 == '') $ipv6 = '::';
417
 
417
 
418
        if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
418
        if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
419
                $cache_ip2long6[$ipv6] = false;
419
                $cache_ip2long6[$ipv6] = false;
420
                return false;
420
                return false;
421
        }
421
        }
422
 
422
 
423
        $ip_n = @inet_pton($ipv6);
423
        $ip_n = @inet_pton($ipv6);
424
        if ($ip_n === false) {
424
        if ($ip_n === false) {
425
                $cache_ip2long6[$ipv6] = false;
425
                $cache_ip2long6[$ipv6] = false;
426
                return false; // modified
426
                return false; // modified
427
        }
427
        }
428
        $bytes = 16; // 16 bytes x 8 bit/byte = 128bit
428
        $bytes = 16; // 16 bytes x 8 bit/byte = 128bit
429
        $ipv6long = '';
429
        $ipv6long = '';
430
 
430
 
431
        while ($bytes > 0) {
431
        while ($bytes > 0) {
432
                $bin = sprintf('%08b',(ord($ip_n[$bytes-1])));
432
                $bin = sprintf('%08b',(ord($ip_n[$bytes-1])));
433
                $ipv6long = $bin.$ipv6long;
433
                $ipv6long = $bin.$ipv6long;
434
                $bytes--;
434
                $bytes--;
435
        }
435
        }
436
 
436
 
437
        // $out = gmp_strval(gmp_init($ipv6long, 2), 10);
437
        // $out = gmp_strval(gmp_init($ipv6long, 2), 10);
438
        $out = gmp_init($ipv6long, 2);
438
        $out = gmp_init($ipv6long, 2);
439
        $cache_ip2long6[$ipv6] = $out;
439
        $cache_ip2long6[$ipv6] = $out;
440
        return $out;
440
        return $out;
441
}
441
}
442
 
442
 
443
$cache_long2ip6 = array();
443
$cache_long2ip6 = array();
444
function long2ip6($ipv6long, $compress=true) {
444
function long2ip6($ipv6long, $compress=true) {
445
        // Source:
445
        // Source:
446
        // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
446
        // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
447
        // Slightly modified
447
        // Slightly modified
448
 
448
 
449
        global $cache_long2ip6;
449
        global $cache_long2ip6;
450
        $vvv = ($compress ? 'T' : 'F').$ipv6long;
450
        $vvv = ($compress ? 'T' : 'F').$ipv6long;
451
        if (isset($cache_long2ip6[$vvv])) return $cache_long2ip6[$vvv];
451
        if (isset($cache_long2ip6[$vvv])) return $cache_long2ip6[$vvv];
452
 
452
 
453
        // $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
453
        // $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
454
        $bin = gmp_strval($ipv6long, 2);
454
        $bin = gmp_strval($ipv6long, 2);
455
        if (strlen($bin) < 128) {
455
        if (strlen($bin) < 128) {
456
                $pad = 128 - strlen($bin);
456
                $pad = 128 - strlen($bin);
457
                for ($i = 1; $i <= $pad; $i++) {
457
                for ($i = 1; $i <= $pad; $i++) {
458
                        $bin = '0'.$bin;
458
                        $bin = '0'.$bin;
459
                }
459
                }
460
        }
460
        }
461
 
461
 
462
        $bytes = 0;
462
        $bytes = 0;
463
        $ipv6 = '';
463
        $ipv6 = '';
464
        while ($bytes < 8) { // 16 bytes x 8 bit/byte = 128bit
464
        while ($bytes < 8) { // 16 bytes x 8 bit/byte = 128bit
465
                $bin_part = substr($bin,($bytes*16),16);
465
                $bin_part = substr($bin,($bytes*16),16);
466
                $part = dechex(bindec($bin_part));
466
                $part = dechex(bindec($bin_part));
467
                if (!$compress) {
467
                if (!$compress) {
468
                        $part = str_pad($part, 4, '0', STR_PAD_LEFT);
468
                        $part = str_pad($part, 4, '0', STR_PAD_LEFT);
469
                }
469
                }
470
                $ipv6 .= $part.':';
470
                $ipv6 .= $part.':';
471
                $bytes++;
471
                $bytes++;
472
        }
472
        }
473
 
473
 
474
        if ($compress) {
474
        if ($compress) {
475
                $out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
475
                $out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
476
        } else {
476
        } else {
477
                $out = substr($ipv6, 0, strlen($ipv6)-1);
477
                $out = substr($ipv6, 0, strlen($ipv6)-1);
478
        }
478
        }
479
        $cache_long2ip6[$vvv] = $out;
479
        $cache_long2ip6[$vvv] = $out;
480
        return $out;
480
        return $out;
481
}
481
}
482
 
482
 
483
# --- New 16,12,12
483
# --- New 16,12,12
484
 
484
 
485
define('IPV6_BITS', 128);
485
define('IPV6_BITS', 128);
486
 
486
 
487
$global_ipv6_distance = array();
487
$global_ipv6_distance = array();
488
function ipv6_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) {
488
function ipv6_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) {
489
        global $global_ipv6_distance;
489
        global $global_ipv6_distance;
490
        $vvv = $ipOrCIDR_Searchterm.'|'.$ipOrCIDR_Candidate;
490
        $vvv = $ipOrCIDR_Searchterm.'|'.$ipOrCIDR_Candidate;
491
        if (isset($global_ipv6_distance[$vvv])) return $global_ipv6_distance[$vvv];
491
        if (isset($global_ipv6_distance[$vvv])) return $global_ipv6_distance[$vvv];
492
 
492
 
493
        $ary = ipv6_cidr_split($ipOrCIDR_Searchterm);
493
        $ary = ipv6_cidr_split($ipOrCIDR_Searchterm);
494
        $ip = $ary[0];
494
        $ip = $ary[0];
495
 
495
 
496
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
496
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
497
                $global_ipv6_distance[$vvv] = false;
497
                $global_ipv6_distance[$vvv] = false;
498
                return false;
498
                return false;
499
        }
499
        }
500
 
500
 
501
        $ary = ipv6_cidr_split($ipOrCIDR_Candidate);
501
        $ary = ipv6_cidr_split($ipOrCIDR_Candidate);
502
        $ip = $ary[0];
502
        $ip = $ary[0];
503
        $cidr_bits = $ary[1];
503
        $cidr_bits = $ary[1];
504
        if ($cidr_bits > IPV6_BITS) {
504
        if ($cidr_bits > IPV6_BITS) {
505
                $global_ipv6_distance[$vvv] = false;
505
                $global_ipv6_distance[$vvv] = false;
506
                return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
506
                return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
507
        }
507
        }
508
        if (!is_numeric($cidr_bits)) return false;
508
        if (!is_numeric($cidr_bits)) return false;
509
 
509
 
510
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
510
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
511
                $global_ipv6_distance[$vvv] = false;
511
                $global_ipv6_distance[$vvv] = false;
512
                return false;
512
                return false;
513
        }
513
        }
514
 
514
 
515
        $x = ipv6_trackdown($ipOrCIDR_Searchterm);
515
        $x = ipv6_trackdown($ipOrCIDR_Searchterm);
516
 
516
 
517
        if (ipv6_in_cidr($x[0], $ip.'/'.$cidr_bits)) {
517
        if (ipv6_in_cidr($x[0], $ip.'/'.$cidr_bits)) {
518
                $ary = ipv6_cidr_split($x[0]);
518
                $ary = ipv6_cidr_split($x[0]);
519
                $cidr_bits2 = $ary[1];
519
                $cidr_bits2 = $ary[1];
520
                if ($cidr_bits2 > IPV6_BITS) {
520
                if ($cidr_bits2 > IPV6_BITS) {
521
                        $global_ipv6_distance[$vvv] = false;
521
                        $global_ipv6_distance[$vvv] = false;
522
                        return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
522
                        return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
523
                }
523
                }
524
                $out = $cidr_bits2-$cidr_bits;
524
                $out = $cidr_bits2-$cidr_bits;
525
                $global_ipv6_distance[$vvv] = $out;
525
                $global_ipv6_distance[$vvv] = $out;
526
                return $out;
526
                return $out;
527
        }
527
        }
528
 
528
 
529
        $i = 0;
529
        $i = 0;
530
        $max = false;
530
        $max = false;
531
        foreach ($x as &$y) {
531
        foreach ($x as &$y) {
532
                if (ipv6_in_cidr($ip.'/'.$cidr_bits, $y)) {
532
                if (ipv6_in_cidr($ip.'/'.$cidr_bits, $y)) {
533
                        $max = $i;
533
                        $max = $i;
534
                }
534
                }
535
                $i++;
535
                $i++;
536
        }
536
        }
537
 
537
 
538
        $global_ipv6_distance[$vvv] = $max;
538
        $global_ipv6_distance[$vvv] = $max;
539
        return $max;
539
        return $max;
540
}
540
}
541
 
541
 
542
function ipv6_cidr_split($ipOrCIDR) {
542
function ipv6_cidr_split($ipOrCIDR) {
543
        $ary = explode('/', $ipOrCIDR, 2);
543
        $ary = explode('/', $ipOrCIDR, 2);
544
        $cidr_bits = isset($ary[1]) ? $ary[1] : IPV6_BITS;
544
        $cidr_bits = isset($ary[1]) ? $ary[1] : IPV6_BITS;
545
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
545
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
546
        if (!is_numeric($cidr_bits)) return false;
546
        if (!is_numeric($cidr_bits)) return false;
547
        $ip = $ary[0];
547
        $ip = $ary[0];
548
        return array($ip, $cidr_bits);
548
        return array($ip, $cidr_bits);
549
}
549
}
550
 
550
 
551
function ipv6_equals($ipOrCIDRA, $ipOrCIDRB) {
551
function ipv6_equals($ipOrCIDRA, $ipOrCIDRB) {
552
        return ipv6_normalize_range($ipOrCIDRA) == ipv6_normalize_range($ipOrCIDRB);
552
        return ipv6_normalize_range($ipOrCIDRA) == ipv6_normalize_range($ipOrCIDRB);
553
}
553
}
554
 
554
 
555
function ipv6_cidr_min_ip($ipOrCIDR) {
555
function ipv6_cidr_min_ip($ipOrCIDR) {
556
        $ary = ipv6_cidr_split($ipOrCIDR);
556
        $ary = ipv6_cidr_split($ipOrCIDR);
557
        $ipOrCIDR  = $ary[0];
557
        $ipOrCIDR  = $ary[0];
558
        $cidr_bits = $ary[1];
558
        $cidr_bits = $ary[1];
559
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
559
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
560
        if (!is_numeric($cidr_bits)) return false;
560
        if (!is_numeric($cidr_bits)) return false;
561
 
561
 
562
        $m = ip2bin($ipOrCIDR);
562
        $m = ip2bin($ipOrCIDR);
563
        $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
563
        $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
564
 
564
 
565
        return bin2ip($m);
565
        return bin2ip($m);
566
}
566
}
567
 
567
 
568
function ipv6_cidr_max_ip($ipOrCIDR) {
568
function ipv6_cidr_max_ip($ipOrCIDR) {
569
        $ary = ipv6_cidr_split($ipOrCIDR);
569
        $ary = ipv6_cidr_split($ipOrCIDR);
570
        $ipOrCIDR  = $ary[0];
570
        $ipOrCIDR  = $ary[0];
571
        $cidr_bits = $ary[1];
571
        $cidr_bits = $ary[1];
572
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
572
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
573
        if (!is_numeric($cidr_bits)) return false;
573
        if (!is_numeric($cidr_bits)) return false;
574
 
574
 
575
        $m = ip2bin($ipOrCIDR);
575
        $m = ip2bin($ipOrCIDR);
576
        $m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV6_BITS-$cidr_bits);
576
        $m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV6_BITS-$cidr_bits);
577
 
577
 
578
        return bin2ip($m);
578
        return bin2ip($m);
579
}
579
}
580
 
580
 
581
function ipv6_normalize_range($ipOrCIDR) {
581
function ipv6_normalize_range($ipOrCIDR) {
582
        #     2001:1800::1/21
582
        #     2001:1800::1/21
583
        # --> 2001:1800::/21
583
        # --> 2001:1800::/21
584
 
584
 
585
        #     2001:1af8:4100:a061:0001::1337
585
        #     2001:1af8:4100:a061:0001::1337
586
        # --> 2001:1af8:4100:a061:1::1337/128
586
        # --> 2001:1af8:4100:a061:1::1337/128
587
 
587
 
588
        $ary = ipv6_cidr_split($ipOrCIDR);
588
        $ary = ipv6_cidr_split($ipOrCIDR);
589
        $ipOrCIDR  = $ary[0];
589
        $ipOrCIDR  = $ary[0];
590
        $cidr_bits = $ary[1];
590
        $cidr_bits = $ary[1];
591
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
591
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
592
        if (!is_numeric($cidr_bits)) return false;
592
        if (!is_numeric($cidr_bits)) return false;
593
 
593
 
594
        $m = ip2bin($ipOrCIDR);
594
        $m = ip2bin($ipOrCIDR);
595
        $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
595
        $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
596
 
596
 
597
        return bin2ip($m) . '/' . $cidr_bits;
597
        return bin2ip($m) . '/' . $cidr_bits;
598
}
598
}
599
 
599
 
600
function ipv6_trackdown($ipOrCIDR) {
600
function ipv6_trackdown($ipOrCIDR) {
601
        $ary = ipv6_cidr_split($ipOrCIDR);
601
        $ary = ipv6_cidr_split($ipOrCIDR);
602
        $ipOrCIDR  = $ary[0];
602
        $ipOrCIDR  = $ary[0];
603
        $cidr_bits = $ary[1];
603
        $cidr_bits = $ary[1];
604
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
604
        if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
605
        if (!is_numeric($cidr_bits)) return false;
605
        if (!is_numeric($cidr_bits)) return false;
606
 
606
 
607
        $out = array();
607
        $out = array();
608
        $m = ip2bin($ipOrCIDR);
608
        $m = ip2bin($ipOrCIDR);
609
        for ($i=$cidr_bits; $i>=0; $i--) {
609
        for ($i=$cidr_bits; $i>=0; $i--) {
610
                $m = substr($m, 0, $i) . str_repeat('0', IPV6_BITS-$i);
610
                $m = substr($m, 0, $i) . str_repeat('0', IPV6_BITS-$i);
611
                $out[] = bin2ip($m) . '/' . $i;
611
                $out[] = bin2ip($m) . '/' . $i;
612
        }
612
        }
613
 
613
 
614
        return $out;
614
        return $out;
615
}
615
}
616
 
616
 
617
function ipv6_sort($ary) {
617
function ipv6_sort($ary) {
618
        $f = array();
618
        $f = array();
619
        foreach ($ary as $c) {
619
        foreach ($ary as $c) {
620
                $a = explode('/', $c);
620
                $a = explode('/', $c);
621
                $ip = $a[0];
621
                $ip = $a[0];
622
                $bits = isset($a[1]) ? $a[1] : 128;
622
                $bits = isset($a[1]) ? $a[1] : 128;
623
 
623
 
624
                $d = ip2bin($ip);
624
                $d = ip2bin($ip);
625
 
625
 
626
                # ord('*') must be smaller than ord('0')
626
                # ord('*') must be smaller than ord('0')
627
                $d = substr($d, 0, $bits).str_repeat('*', 128-$bits);
627
                $d = substr($d, 0, $bits).str_repeat('*', 128-$bits);
628
 
628
 
629
                $f[$d] = $c;
629
                $f[$d] = $c;
630
        }
630
        }
631
 
631
 
632
        return $f;
632
        return $f;
633
}
633
}
634
 
634
 
635
function ipv6_make_tree($ary) {
635
function ipv6_make_tree($ary) {
636
        $ary = ipv6_sort($ary);
636
        $ary = ipv6_sort($ary);
637
 
637
 
638
        if (count($ary) == 0) return array();
638
        if (count($ary) == 0) return array();
639
 
639
 
640
        $sub_begin = '';
640
        $sub_begin = '';
641
        $sub_begin_ip = '';
641
        $sub_begin_ip = '';
642
        foreach ($ary as $n => $d) {
642
        foreach ($ary as $n => $d) {
643
                $sub_begin = substr($n, 0, strpos($n, '*'));
643
                $sub_begin = substr($n, 0, strpos($n, '*'));
644
                $sub_begin_ip = $d;
644
                $sub_begin_ip = $d;
645
                unset($ary[$n]);
645
                unset($ary[$n]);
646
                break;
646
                break;
647
        }
647
        }
648
 
648
 
649
        $sub = array();
649
        $sub = array();
650
        $nonsub = array();
650
        $nonsub = array();
651
        foreach ($ary as $n => $d) {
651
        foreach ($ary as $n => $d) {
652
                if (substr($n, 0, strlen($sub_begin)) == $sub_begin) {
652
                if (substr($n, 0, strlen($sub_begin)) == $sub_begin) {
653
                        $sub[$n] = $d;
653
                        $sub[$n] = $d;
654
                } else {
654
                } else {
655
                        $nonsub[$n] = $d;
655
                        $nonsub[$n] = $d;
656
                }
656
                }
657
        }
657
        }
658
 
658
 
659
        $out = array();
659
        $out = array();
660
        $out[$sub_begin_ip] = ipv6_make_tree($sub);
660
        $out[$sub_begin_ip] = ipv6_make_tree($sub);
661
 
661
 
662
        $a = ipv6_make_tree($nonsub);
662
        $a = ipv6_make_tree($nonsub);
663
 
663
 
664
        $out = array_merge($out, $a);
664
        $out = array_merge($out, $a);
665
 
665
 
666
        return $out;
666
        return $out;
667
}
667
}
668
 
668
 
669
# ---
669
# ---
670
 
670
 
671
if (!function_exists('ip2bin')) {
671
if (!function_exists('ip2bin')) {
672
        $cache_ip2bin = array();
672
        $cache_ip2bin = array();
673
        function ip2bin($ip) {
673
        function ip2bin($ip) {
674
                # Source: http://php.net/manual/en/function.ip2long.php#104163
674
                # Source: http://php.net/manual/en/function.ip2long.php#104163
675
                # modified by VTS
675
                # modified by VTS
676
 
676
 
677
                global $cache_ip2bin;
677
                global $cache_ip2bin;
678
                if (isset($cache_ip2bin[$ip])) return $cache_ip2bin[$ip];
678
                if (isset($cache_ip2bin[$ip])) return $cache_ip2bin[$ip];
679
 
679
 
680
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
680
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
681
                        $iplong = ip2long($ip);
681
                        $iplong = ip2long($ip);
682
                        assert($iplong !== false);
682
                        assert($iplong !== false);
683
                        $out = base_convert((string)$iplong, 10, 2);
683
                        $out = base_convert((string)$iplong, 10, 2);
684
                        $cache_ip2bin[$ip] = $out;
684
                        $cache_ip2bin[$ip] = $out;
685
                        return $out;
685
                        return $out;
686
                }
686
                }
687
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
687
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
688
                        $cache_ip2bin[$ip] = false;
688
                        $cache_ip2bin[$ip] = false;
689
                        return false;
689
                        return false;
690
                }
690
                }
691
                if (($ip_n = inet_pton($ip)) === false) {
691
                if (($ip_n = inet_pton($ip)) === false) {
692
                        $cache_ip2bin[$ip] = false;
692
                        $cache_ip2bin[$ip] = false;
693
                        return false;
693
                        return false;
694
                }
694
                }
695
                $bits = 15; // 16 x 8 bit = 128bit (ipv6)
695
                $bits = 15; // 16 x 8 bit = 128bit (ipv6)
696
                $ipbin = ''; # added by vts to avoid warning
696
                $ipbin = ''; # added by vts to avoid warning
697
                while ($bits >= 0) {
697
                while ($bits >= 0) {
698
                        $bin = sprintf('%08b', (ord($ip_n[$bits])));
698
                        $bin = sprintf('%08b', (ord($ip_n[$bits])));
699
                        $ipbin = $bin.$ipbin;
699
                        $ipbin = $bin.$ipbin;
700
                        $bits--;
700
                        $bits--;
701
                }
701
                }
702
 
702
 
703
                $cache_ip2bin[$ip] = $ipbin;
703
                $cache_ip2bin[$ip] = $ipbin;
704
                return $ipbin;
704
                return $ipbin;
705
        }
705
        }
706
}
706
}
707
 
707
 
708
if (!function_exists('bin2ip')) {
708
if (!function_exists('bin2ip')) {
709
        $cache_bin2ip = array();
709
        $cache_bin2ip = array();
710
        function bin2ip($bin) {
710
        function bin2ip($bin) {
711
                # Source: http://php.net/manual/en/function.ip2long.php#104163
711
                # Source: http://php.net/manual/en/function.ip2long.php#104163
712
                # modified by VTS
712
                # modified by VTS
713
 
713
 
714
                global $cache_bin2ip;
714
                global $cache_bin2ip;
715
                if (isset($cache_bin2ip[$bin])) return $cache_bin2ip[$bin];
715
                if (isset($cache_bin2ip[$bin])) return $cache_bin2ip[$bin];
716
 
716
 
717
                if (strlen($bin) <= 32) { // 32bits (ipv4)
717
                if (strlen($bin) <= 32) { // 32bits (ipv4)
718
                        $iplong = base_convert($bin, 2, 10);
718
                        $iplong = base_convert($bin, 2, 10);
719
                        $out = long2ip(intval($iplong));
719
                        $out = long2ip(intval($iplong));
720
                        $cache_bin2ip[$bin] = $out;
720
                        $cache_bin2ip[$bin] = $out;
721
                        return $out;
721
                        return $out;
722
                }
722
                }
723
                if (strlen($bin) != 128) {
723
                if (strlen($bin) != 128) {
724
                        $cache_bin2ip[$bin] = false;
724
                        $cache_bin2ip[$bin] = false;
725
                        return false;
725
                        return false;
726
                }
726
                }
727
                $pad = 128 - strlen($bin);
727
                $pad = 128 - strlen($bin);
728
                for ($i = 1; $i <= $pad; $i++) {
728
                for ($i = 1; $i <= $pad; $i++) {
729
                        $bin = '0'.$bin;
729
                        $bin = '0'.$bin;
730
                }
730
                }
731
                $bits = 0;
731
                $bits = 0;
732
                $ipv6 = ''; # added by vts to avoid warning
732
                $ipv6 = ''; # added by vts to avoid warning
733
                while ($bits <= 7) {
733
                while ($bits <= 7) {
734
                        $bin_part = substr($bin,($bits*16),16);
734
                        $bin_part = substr($bin,($bits*16),16);
735
                        $ipv6 .= dechex(bindec($bin_part)) . ':';
735
                        $ipv6 .= dechex(bindec($bin_part)) . ':';
736
                        $bits++;
736
                        $bits++;
737
                }
737
                }
738
 
738
 
739
                $out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
739
                $out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
740
                $cache_bin2ip[$bin] = $out;
740
                $cache_bin2ip[$bin] = $out;
741
                return $out;
741
                return $out;
742
        }
742
        }
743
}
743
}
744
 
744
 
745
# --- TEST
745
# --- TEST
746
 
746
 
747
/*
747
/*
748
assert(ipv6_normalize('2001:0000:0000::1') == '2001::1');
748
assert(ipv6_normalize('2001:0000:0000::1') == '2001::1');
749
 
749
 
750
assert(ipv6_distance('2001:1ae0::/27', '2001:1af8::/29') == -2);
750
assert(ipv6_distance('2001:1ae0::/27', '2001:1af8::/29') == -2);
751
assert(ipv6_distance('2001:1af0::/28', '2001:1af8::/29') == -1);
751
assert(ipv6_distance('2001:1af0::/28', '2001:1af8::/29') == -1);
752
assert(ipv6_distance('2001:1af8::/29', '2001:1af8::/29') == 0);
752
assert(ipv6_distance('2001:1af8::/29', '2001:1af8::/29') == 0);
753
assert(ipv6_distance('2001:1af8::/30', '2001:1af8::/29') == 1);
753
assert(ipv6_distance('2001:1af8::/30', '2001:1af8::/29') == 1);
754
assert(ipv6_distance('2001:1af8::/31', '2001:1af8::/29') == 2);
754
assert(ipv6_distance('2001:1af8::/31', '2001:1af8::/29') == 2);
755
 
755
 
756
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/127', '2001:1af8:4100:a061:0001::1335/127') === false);
756
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/127', '2001:1af8:4100:a061:0001::1335/127') === false);
757
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/128', '2001:1af8:4100:a061:0001::1337/128') === false);
757
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/128', '2001:1af8:4100:a061:0001::1337/128') === false);
758
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336',     '2001:1af8:4100:a061:0001::1337')     === false);
758
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336',     '2001:1af8:4100:a061:0001::1337')     === false);
759
*/
759
*/
760
 
760
 
761
/*
761
/*
762
$test = '2001:1af8:4100:a061:0001::1337';
762
$test = '2001:1af8:4100:a061:0001::1337';
763
$x = ipv6_trackdown($test);
763
$x = ipv6_trackdown($test);
764
foreach ($x as &$cidr) {
764
foreach ($x as &$cidr) {
765
        $min = ipv6_cidr_min_ip($cidr);
765
        $min = ipv6_cidr_min_ip($cidr);
766
        $max = ipv6_cidr_max_ip($cidr);
766
        $max = ipv6_cidr_max_ip($cidr);
767
        echo "$cidr ($min - $max)\n";
767
        echo "$cidr ($min - $max)\n";
768
}
768
}
769
*/
769
*/
770
 
770