Subversion Repositories vgwhois

Rev

Rev 5 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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