Subversion Repositories vgwhois

Rev

Rev 2 | Rev 45 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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