Project

General

Profile

Bug #11117 » IPv6.php

Current problematic referenced file - Andrew Appel, 11/30/2020 11:39 AM

 
1
<?php
2

    
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4

    
5
/**
6
 * This file contains the implementation of the Net_IPv6 class
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * LICENSE: This source file is subject to the New BSD license, that is
11
 * available through the world-wide-web at 
12
 * http://www.opensource.org/licenses/bsd-license.php
13
 * If you did not receive a copy of the new BSDlicense and are unable
14
 * to obtain it through the world-wide-web, please send a note to
15
 * license@php.net so we can mail you a copy immediately
16
 *
17
 * @category  Net
18
 * @package   Net_IPv6
19
 * @author    Alexander Merz <alexander.merz@web.de>
20
 * @copyright 2003-2005 The PHP Group
21
 * @license   BSD License http://www.opensource.org/licenses/bsd-license.php
22
 * @version   CVS: $Id: IPv6.php 338818 2016-03-25 12:15:02Z alexmerz $
23
 * @link      http://pear.php.net/package/Net_IPv6
24
 */
25

    
26
// {{{ constants
27

    
28
/**
29
 * Error message if netmask bits was not found
30
 * @see isInNetmask
31
 */
32
define("NET_IPV6_NO_NETMASK_MSG", "Netmask length not found");
33

    
34
/**
35
 * Error code if netmask bits was not found
36
 * @see isInNetmask
37
 */
38
define("NET_IPV6_NO_NETMASK", 10);
39

    
40
/**
41
 * Address Type: Unassigned (RFC 1884, Section 2.3)
42
 * @see getAddressType()
43
 */
44
define("NET_IPV6_UNASSIGNED", 1);
45

    
46
/**
47
 * Address Type: Reserved (RFC 1884, Section 2.3)
48
 * @see getAddressType()
49
 */
50
define("NET_IPV6_RESERVED", 11);
51

    
52
/**
53
 * Address Type: Reserved for NSAP Allocation (RFC 1884, Section 2.3)
54
 * @see getAddressType()
55
 */
56
define("NET_IPV6_RESERVED_NSAP", 12);
57

    
58
/**
59
 * Address Type: Reserved for IPX Allocation (RFC 1884, Section 2.3)
60
 * @see getAddressType()
61
 */
62
define("NET_IPV6_RESERVED_IPX", 13);
63

    
64
/**
65
 * Address Type: Reserved for Geographic-Based Unicast Addresses 
66
 * (RFC 1884, Section 2.3)
67
 * @see getAddressType()
68
 */
69
define("NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC", 14);
70

    
71
/**
72
 * Address Type: Provider-Based Unicast Address (RFC 1884, Section 2.3)
73
 * @see getAddressType()
74
 */
75
define("NET_IPV6_UNICAST_PROVIDER", 22);
76

    
77
/**
78
 * Address Type: Multicast Addresses (RFC 1884, Section 2.3)
79
 * @see getAddressType()
80
 */
81
define("NET_IPV6_MULTICAST", 31);
82

    
83
/**
84
 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3)
85
 * @see getAddressType()
86
 */
87
define("NET_IPV6_LOCAL_LINK", 42);
88

    
89
/**
90
 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3)
91
 * @see getAddressType()
92
 */
93
define("NET_IPV6_LOCAL_SITE", 43);
94

    
95
/**
96
 * Address Type: Address range to embedded IPv4 ip in an IPv6 address (RFC 4291, Section 2.5.5)
97
 * @see getAddressType()
98
 */
99
define("NET_IPV6_IPV4MAPPING", 51);
100

    
101
/**
102
 * Address Type: Unspecified (RFC 4291, Section 2.5.2)
103
 * @see getAddressType()
104
 */
105
define("NET_IPV6_UNSPECIFIED", 52);
106

    
107
/**
108
 * Address Type: Unspecified (RFC 4291, Section 2.5.3)
109
 * @see getAddressType()
110
 */
111
define("NET_IPV6_LOOPBACK", 53);
112

    
113
/**
114
 * Address Type: address can not assigned to a specific type
115
 * @see getAddressType()
116
 */
117
define("NET_IPV6_UNKNOWN_TYPE", 1001);
118

    
119
// }}}
120
// {{{ Net_IPv6
121

    
122
/**
123
 * Class to validate and to work with IPv6 addresses.
124
 *
125
 * @category  Net
126
 * @package   Net_IPv6
127
 * @author    Alexander Merz <alexander.merz@web.de>
128
 * @author    <elfrink at introweb dot nl>
129
 * @author    Josh Peck <jmp at joshpeck dot org>
130
 * @copyright 2003-2010 The PHP Group
131
 * @license   BSD License http://www.opensource.org/licenses/bsd-license.php
132
 * @version   Release: 1.1.0RC5
133
 * @link      http://pear.php.net/package/Net_IPv6
134
 */
135
class Net_IPv6
136
{
137

    
138
    // {{{ separate()
139
    /**
140
     * Separates an IPv6 address into the address and a prefix length part
141
     *
142
     * @param String $ip the (compressed) IP as Hex representation
143
     *
144
     * @return Array the first element is the IP, the second the prefix length
145
     * @since  1.2.0
146
     * @access public
147
     * @static     
148
     */
149
    public static function separate($ip) 
150
    {
151
        
152
        $addr = $ip;
153
        $spec = '';
154

    
155
        if(false === strrpos($ip, '/')) {
156

    
157
            return array($addr, $spec);
158

    
159
        }
160

    
161
        $elements = explode('/', $ip);
162

    
163
        if(2 == count($elements)) {
164

    
165
            $addr = $elements[0];
166
            $spec = $elements[1];
167

    
168
        }
169

    
170
        return array($addr, $spec);
171

    
172
    }
173
    // }}}
174

    
175
    // {{{ removeNetmaskSpec()
176

    
177
    /**
178
     * Removes a possible existing prefix length/ netmask specification at an IP addresse.
179
     *
180
     * @param String $ip the (compressed) IP as Hex representation
181
     *
182
     * @return String the IP without netmask length
183
     * @since  1.1.0
184
     * @access public
185
     * @static
186
     */
187
    public static function removeNetmaskSpec($ip)
188
    {
189

    
190
        $elements = Net_IPv6::separate($ip);
191

    
192
        return $elements[0];
193

    
194
    }
195
    // }}}
196
    // {{{ removePrefixLength()
197

    
198
    /**
199
     * Tests for a prefix length specification in the address
200
     * and removes the prefix length, if exists
201
     *
202
     * The method is technically identical to removeNetmaskSpec() and 
203
     * will be dropped in a future release.
204
     *
205
     * @param String $ip a valid ipv6 address
206
     *
207
     * @return String the address without a prefix length
208
     * @access public
209
     * @static
210
     * @see removeNetmaskSpec()
211
     * @deprecated
212
     */
213
    public static function removePrefixLength($ip)
214
    {
215
        $pos = strrpos($ip, '/');
216

    
217
        if (false !== $pos) {
218

    
219
            return substr($ip, 0, $pos);
220

    
221
        }
222

    
223
        return $ip;
224
    }
225

    
226
    // }}}
227
    // {{{ getNetmaskSpec()
228

    
229
    /**
230
     * Returns a possible existing prefix length/netmask specification on an IP addresse.
231
     *
232
     * @param String $ip the (compressed) IP as Hex representation
233
     *
234
     * @return String the netmask spec
235
     * @since  1.1.0
236
     * @access public
237
     * @static
238
     */
239
    public static function getNetmaskSpec($ip) 
240
    {
241

    
242
        $elements = Net_IPv6::separate($ip);
243

    
244
        return $elements[1];
245

    
246
    }
247

    
248
    // }}}
249
    // {{{ getPrefixLength()
250

    
251
    /**
252
     * Tests for a prefix length specification in the address
253
     * and returns the prefix length, if exists
254
     *
255
     * The method is technically identical to getNetmaskSpec() and 
256
     * will be dropped in a future release.
257
     *
258
     * @param String $ip a valid ipv6 address
259
     *
260
     * @return Mixed the prefix as String or false, if no prefix was found
261
     * @access public
262
     * @static
263
     * @deprecated
264
     */
265
    public static function getPrefixLength($ip) 
266
    {
267
        if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", 
268
                        $ip, $matches)) {
269

    
270
            return $matches[2];
271

    
272
        } else {
273

    
274
            return false;
275

    
276
        }
277

    
278
    }
279

    
280
    // }}}
281
    // {{{ getNetmask()
282

    
283
    /**
284
     * Calculates the network prefix based on the netmask bits.
285
     *
286
     * @param String $ip   the (compressed) IP in Hex format
287
     * @param int    $bits if the number of netmask bits is not part of the IP
288
     *                     you must provide the number of bits
289
     *
290
     * @return String the network prefix
291
     * @since  1.1.0
292
     * @access public
293
     * @static
294
     */
295
    public static function getNetmask($ip, $bits = null)
296
    {
297
        if (null==$bits) {
298

    
299
            $elements = explode('/', $ip);
300

    
301
            if (2 == count($elements)) {
302

    
303
                $addr = $elements[0];
304
                $bits = $elements[1];
305

    
306
            } else {
307

    
308
                include_once 'PEAR.php';
309

    
310
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
311
                                        NET_IPV6_NO_NETMASK);
312
            }
313

    
314
        } else {
315

    
316
            $addr = $ip;
317

    
318
        }
319

    
320
        $addr       = Net_IPv6::uncompress($addr);
321
        $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits);
322

    
323
        return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask);
324
    }
325

    
326
    // }}}
327
    // {{{ isInNetmask()
328

    
329
    /**
330
     * Checks if an (compressed) IP is in a specific address space.
331
     *
332
     * IF the IP does not contains the number of netmask bits (F8000::FFFF/16)
333
     * then you have to use the $bits parameter.
334
     *
335
     * @param String $ip      the IP to check (eg. F800::FFFF)
336
     * @param String $netmask the netmask (eg F800::)
337
     * @param int    $bits    the number of netmask bits to compare,
338
     *                        if not given in $ip
339
     *
340
     * @return boolean true if $ip is in the netmask
341
     * @since  1.1.0
342
     * @access public
343
     * @static
344
     */
345
    public static function isInNetmask($ip, $netmask, $bits=null)
346
    {
347
        // try to get the bit count
348

    
349
        if (null == $bits) {
350

    
351
            $elements = explode('/', $ip);
352

    
353
            if (2 == count($elements)) {
354

    
355
                $ip   = $elements[0];
356
                $bits = $elements[1];
357

    
358
            } else if (null == $bits) {
359

    
360
                $elements = explode('/', $netmask);
361

    
362
                if (2 == count($elements)) {
363

    
364
                     $netmask = $elements[0];
365
                     $bits    = $elements[1];
366

    
367
                }
368

    
369
                if (null == $bits) {
370

    
371
                    include_once 'PEAR.php';
372
                    return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
373
                                            NET_IPV6_NO_NETMASK);
374

    
375
                }
376

    
377
            }
378

    
379
        }
380

    
381
        $binIp      = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip));
382
        $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask));
383

    
384
        if (null != $bits
385
            && "" != $bits
386
            && 0 == strncmp($binNetmask, $binIp, $bits)) {
387

    
388
            return true;
389

    
390
        }
391

    
392
        return false;
393
    }
394

    
395
    // }}}
396
    // {{{ getAddressType()
397

    
398
    /**
399
     * Returns the type of an IPv6 address.
400
     *
401
     * RFC 2373, Section 2.3 describes several types of addresses in
402
     * the IPv6 addresse space.
403
     * Several addresse types are markers for reserved spaces and as
404
     * consequence a subject to change.
405
     *
406
     * @param String $ip the IP address in Hex format,
407
     *                    compressed IPs are allowed
408
     *
409
     * @return int one of the addresse type constants
410
     * @access public
411
     * @since  1.1.0
412
     * @static
413
     *
414
     * @see    NET_IPV6_UNASSIGNED
415
     * @see    NET_IPV6_RESERVED
416
     * @see    NET_IPV6_RESERVED_NSAP
417
     * @see    NET_IPV6_RESERVED_IPX
418
     * @see    NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC
419
     * @see    NET_IPV6_UNICAST_PROVIDER
420
     * @see    NET_IPV6_MULTICAST
421
     * @see    NET_IPV6_LOCAL_LINK
422
     * @see    NET_IPV6_LOCAL_SITE
423
     * @see    NET_IPV6_IPV4MAPPING  
424
     * @see    NET_IPV6_UNSPECIFIED  
425
     * @see    NET_IPV6_LOOPBACK  
426
     * @see    NET_IPV6_UNKNOWN_TYPE
427
     */
428
    public static function getAddressType($ip) 
429
    {
430
        $ip    = Net_IPv6::removeNetmaskSpec($ip);
431
        $binip = Net_IPv6::_ip2Bin($ip);
432

    
433
        if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128
434

    
435
            return NET_IPV6_UNSPECIFIED;
436

    
437
        } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128
438

    
439
            return NET_IPV6_LOOPBACK;
440

    
441
        } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96
442

    
443
            return NET_IPV6_IPV4MAPPING; 
444

    
445
        } else if (0 == strncmp('1111111010', $binip, 10)) {
446

    
447
            return NET_IPV6_LOCAL_LINK;
448

    
449
        } else if (0 == strncmp('1111111011', $binip, 10)) {
450

    
451
            return NET_IPV6_LOCAL_SITE;
452

    
453
        } else if (0 == strncmp('111111100', $binip, 9)) {
454

    
455
            return NET_IPV6_UNASSIGNED;
456

    
457
        } else if (0 == strncmp('11111111', $binip, 8)) {
458

    
459
            return NET_IPV6_MULTICAST;
460

    
461
        } else if (0 == strncmp('00000000', $binip, 8)) { 
462

    
463
            return NET_IPV6_RESERVED;
464

    
465
        } else if (0 == strncmp('00000001', $binip, 8)
466
                    || 0 == strncmp('1111110', $binip, 7)) {
467

    
468
            return NET_IPV6_UNASSIGNED;
469

    
470
        } else if (0 == strncmp('0000001', $binip, 7)) {
471

    
472
            return NET_IPV6_RESERVED_NSAP;
473

    
474
        } else if (0 == strncmp('0000010', $binip, 7)) {
475

    
476
            return NET_IPV6_RESERVED_IPX;;
477

    
478
        } else if (0 == strncmp('0000011', $binip, 7) ||
479
                    0 == strncmp('111110', $binip, 6) ||
480
                    0 == strncmp('11110', $binip, 5) ||
481
                    0 == strncmp('00001', $binip, 5) ||
482
                    0 == strncmp('1110', $binip, 4) ||
483
                    0 == strncmp('0001', $binip, 4) ||
484
                    0 == strncmp('001', $binip, 3) ||
485
                    0 == strncmp('011', $binip, 3) ||
486
                    0 == strncmp('101', $binip, 3) ||
487
                    0 == strncmp('110', $binip, 3)) {
488

    
489
            return NET_IPV6_UNASSIGNED;
490

    
491
        } else if (0 == strncmp('010', $binip, 3)) {
492

    
493
            return NET_IPV6_UNICAST_PROVIDER;
494

    
495
        } else if (0 == strncmp('100', $binip, 3)) {
496

    
497
            return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC;
498

    
499
        }
500

    
501
        return NET_IPV6_UNKNOWN_TYPE;
502
    }
503

    
504
    // }}}
505
    // {{{ Uncompress()
506

    
507
    /**
508
     * Uncompresses an IPv6 adress
509
     *
510
     * RFC 2373 allows you to compress zeros in an adress to '::'. This
511
     * function expects an valid IPv6 adress and expands the '::' to
512
     * the required zeros.
513
     *
514
     * Example:  FF01::101  ->  FF01:0:0:0:0:0:0:101
515
     *           ::1        ->  0:0:0:0:0:0:0:1
516
     *
517
     * @param String $ip a valid IPv6-adress (hex format)
518
     * @param Boolean $leadingZeros if true, leading zeros are added to each 
519
     *                              block of the address 
520
     *                              (FF01::101  ->  
521
     *                               FF01:0000:0000:0000:0000:0000:0000:0101) 
522
     *
523
     * @return String the uncompressed IPv6-adress (hex format)
524
     * @access public
525
     * @see Compress()
526
     * @static
527
     * @author Pascal Uhlmann
528
     */
529
    public static function uncompress($ip, $leadingZeros = false)
530
    {
531

    
532
        $prefix = Net_IPv6::getPrefixLength($ip);
533

    
534
        if (false === $prefix) {
535

    
536
            $prefix = '';
537

    
538
        } else {
539

    
540
            $ip     = Net_IPv6::removePrefixLength($ip);
541
            $prefix = '/'.$prefix;
542

    
543
        }
544

    
545
        $netmask = Net_IPv6::getNetmaskSpec($ip);
546
        $uip     = Net_IPv6::removeNetmaskSpec($ip);
547

    
548
        $c1 = -1;
549
        $c2 = -1;
550

    
551
        if (false !== strpos($uip, '::') ) {
552

    
553
            list($ip1, $ip2) = explode('::', $uip, 2);
554

    
555
            if ("" == $ip1) {
556

    
557
                $c1 = -1;
558

    
559
            } else {
560

    
561
                $pos = 0;
562

    
563
                if (0 < ($pos = substr_count($ip1, ':'))) {
564

    
565
                    $c1 = $pos;
566

    
567
                } else {
568

    
569
                    $c1 = 0;
570

    
571
                }
572
            }
573
            if ("" == $ip2) {
574

    
575
                $c2 = -1;
576

    
577
            } else {
578

    
579
                $pos = 0;
580

    
581
                if (0 < ($pos = substr_count($ip2, ':'))) {
582

    
583
                    $c2 = $pos;
584

    
585
                } else {
586

    
587
                    $c2 = 0;
588

    
589
                }
590

    
591
            }
592

    
593
            if (strstr($ip2, '.')) {
594

    
595
                $c2++;
596

    
597
            }
598
            if (-1 == $c1 && -1 == $c2) { // ::
599

    
600
                $uip = "0:0:0:0:0:0:0:0";
601

    
602
            } else if (-1 == $c1) {              // ::xxx
603

    
604
                $fill = str_repeat('0:', max(1, 7-$c2));
605
                $uip  = str_replace('::', $fill, $uip);
606

    
607
            } else if (-1 == $c2) {              // xxx::
608

    
609
                $fill = str_repeat(':0', max(1, 7-$c1));
610
                $uip  = str_replace('::', $fill, $uip);
611

    
612
            } else {                          // xxx::xxx
613

    
614
                $fill = str_repeat(':0:', 6-$c2-$c1);
615
                $uip  = str_replace('::', $fill, $uip);
616
                $uip  = str_replace('::', ':', $uip);
617

    
618
            }
619
        }
620

    
621
        if(true == $leadingZeros) {
622
            
623
            $uipT    = array();
624
            $uiparts = explode(':', $uip);
625

    
626
            foreach($uiparts as $p) {
627

    
628
                $uipT[] = sprintf('%04s', $p);
629
            
630
            }
631

    
632
            $uip = implode(':', $uipT);
633
        }
634

    
635
        if ('' != $netmask) {
636

    
637
                $uip = $uip.'/'.$netmask;
638

    
639
        }
640

    
641
        return $uip.$prefix;
642
    }
643

    
644
    // }}}
645
    // {{{ Compress()
646

    
647
    /**
648
     * Compresses an IPv6 adress
649
     *
650
     * RFC 2373 allows you to compress zeros in an adress to '::'. This
651
     * function expects an valid IPv6 adress and compresses successive zeros
652
     * to '::'
653
     *
654
     * Example:  FF01:0:0:0:0:0:0:101   -> FF01::101
655
     *           0:0:0:0:0:0:0:1        -> ::1
656
     *
657
     * Whe $ip is an already compressed adress the methode returns the value as is,
658
     * also if the adress can be compressed further.
659
     *
660
     * Example: FF01::0:1 -> FF01::0:1
661
     *
662
     * To enforce maximum compression, you can set the second argument $force to true.
663
     *
664
     * Example: FF01::0:1 -> FF01::1 
665
     *
666
     * @param String  $ip    a valid IPv6-adress (hex format)
667
     * @param boolean $force if true the adress will be compresses as best as possible (since 1.2.0)
668
     *
669
     * @return tring the compressed IPv6-adress (hex format)
670
     * @access public
671
     * @see    Uncompress()
672
     * @static
673
     * @author elfrink at introweb dot nl
674
     */
675
    public static function compress($ip, $force = false)  
676
    {
677
        
678
        if(false !== strpos($ip, '::')) { // its already compressed
679

    
680
            if(true == $force) {
681

    
682
                $ip = Net_IPv6::uncompress($ip); 
683

    
684
            } else {
685

    
686
                return $ip;
687

    
688
            }
689

    
690
        }
691

    
692
        $prefix = Net_IPv6::getPrefixLength($ip);
693

    
694
        if (false === $prefix) {
695

    
696
            $prefix = '';
697

    
698
        } else {
699

    
700
            $ip     = Net_IPv6::removePrefixLength($ip);
701
            $prefix = '/'.$prefix;
702

    
703
        }
704

    
705
        $netmask = Net_IPv6::getNetmaskSpec($ip);
706
        $ip      = Net_IPv6::removeNetmaskSpec($ip);
707

    
708
        $ipp = explode(':', $ip);
709

    
710
        for ($i = 0; $i < count($ipp); $i++) {
711

    
712
            $ipp[$i] = dechex(hexdec($ipp[$i]));
713

    
714
        }
715

    
716
        $cip = ':' . join(':', $ipp) . ':';
717

    
718
        preg_match_all("/(:0)(:0)+/", $cip, $zeros);
719

    
720
        if (count($zeros[0]) > 0) {
721

    
722
            $match = '';
723

    
724
            foreach ($zeros[0] as $zero) {
725

    
726
                if (strlen($zero) > strlen($match)) {
727

    
728
                    $match = $zero;
729

    
730
                }
731
            }
732

    
733
            $cip = preg_replace('/' . $match . '/', ':', $cip, 1);
734

    
735
        }
736

    
737
        if ($cip != "::") {
738
            $cip = preg_replace('/((^:)|(:$))/', '', $cip);
739
            $cip = preg_replace('/((^:)|(:$))/', '::', $cip);
740
        }
741

    
742
        if ('' != $netmask) {
743

    
744
            $cip = $cip.'/'.$netmask;
745

    
746
        }
747

    
748
        return $cip.$prefix;
749

    
750
    }
751

    
752
    // }}}
753
    // {{{ recommendedFormat()
754
    /**
755
     * Represent IPv6 address in RFC5952 format.
756
     *
757
     * @param String  $ip a valid IPv6-adress (hex format)
758
     *
759
     * @return String the recommended representation of IPv6-adress (hex format)
760
     * @access public
761
     * @see    compress()
762
     * @static
763
     * @author koyama at hoge dot org
764
     * @todo This method may become a part of compress() in a further releases
765
     */
766
    public static function recommendedFormat($ip)
767
    {
768
        $compressed = self::compress($ip, true);
769
        // RFC5952 4.2.2
770
        // The symbol "::" MUST NOT be used to shorten just one
771
        // 16-bit 0 field.
772
        if ((substr_count($compressed, ':') == 7) &&
773
            (strpos($compressed, '::') !== false)) {
774
            $compressed = str_replace('::', ':0:', $compressed);
775
        }
776
        return $compressed;
777
    }
778
    // }}}
779

    
780
    // {{{ isCompressible()
781

    
782
    /**
783
     * Checks, if an IPv6 adress can be compressed
784
     *
785
     * @param String $ip a valid IPv6 adress
786
     * 
787
     * @return Boolean true, if adress can be compressed
788
     * 
789
     * @access public
790
     * @since 1.2.0b
791
     * @static
792
     * @author Manuel Schmitt
793
     */
794
    public static function isCompressible($ip) 
795
    {
796

    
797
        return (bool)($ip != Net_IPv6::compress($address));
798

    
799
    }    
800

    
801
    // }}}
802
    // {{{ SplitV64()
803

    
804
    /**
805
     * Splits an IPv6 adress into the IPv6 and a possible IPv4 part
806
     *
807
     * RFC 2373 allows you to note the last two parts of an IPv6 adress as
808
     * an IPv4 compatible adress
809
     *
810
     * Example:  0:0:0:0:0:0:13.1.68.3
811
     *           0:0:0:0:0:FFFF:129.144.52.38
812
     *
813
     * @param String  $ip         a valid IPv6-adress (hex format)
814
     * @param Boolean $uncompress if true, the address will be uncompressed 
815
     *                            before processing
816
     *
817
     * @return Array  [0] contains the IPv6 part,
818
     *                [1] the IPv4 part (hex format)
819
     * @access public
820
     * @static
821
     */
822
    public static function SplitV64($ip, $uncompress = true)
823
    {
824
        $ip = Net_IPv6::removeNetmaskSpec($ip);
825

    
826
        if ($uncompress) {
827

    
828
            $ip = Net_IPv6::Uncompress($ip);
829

    
830
        }
831

    
832
        if (strstr($ip, '.')) {
833

    
834
            $pos      = strrpos($ip, ':');
835
            
836
            if(false === $pos) {
837
            	return array("", $ip);
838
            }
839
            
840
            $ip{$pos} = '_';
841
            $ipPart   = explode('_', $ip);
842

    
843
            return $ipPart;
844

    
845
        } else {
846

    
847
            return array($ip, "");
848

    
849
        }
850
    }
851

    
852
    // }}}
853
    // {{{ checkIPv6()
854

    
855
    /**
856
     * Checks an IPv6 adress
857
     *
858
     * Checks if the given IP is IPv6-compatible
859
     *
860
     * @param String $ip a valid IPv6-adress
861
     *
862
     * @return Boolean true if $ip is an IPv6 adress
863
     * @access public
864
     * @static
865
     */
866
    public static function checkIPv6($ip)
867
    {
868

    
869
        $elements = Net_IPv6::separate($ip);
870
    
871
        $ip = $elements[0];
872

    
873
        if('' != $elements[1] && ( !is_numeric($elements[1]) || 0 > $elements || 128 < $elements[1])) {
874

    
875
            return false;
876

    
877
        } 
878

    
879
        $ipPart = Net_IPv6::SplitV64($ip);
880
        $count  = 0;
881

    
882
        if (!empty($ipPart[0])) {
883
            $ipv6 = explode(':', $ipPart[0]);
884

    
885
			if(8 < count($ipv6)) {
886
				return false;
887
			}
888

    
889
            foreach($ipv6 as $element) { // made a validate precheck
890
                if(!preg_match('/[0-9a-fA-F]*/', $element)) {
891
                    return false;
892
                }
893
            }
894

    
895
            for ($i = 0; $i < count($ipv6); $i++) {
896

    
897
                if(4 < strlen($ipv6[$i])) {
898
                    
899
                    return false;
900

    
901
                }
902

    
903
                $dec = hexdec($ipv6[$i]);
904
                $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/",
905
                                                "\\1", 
906
                                                $ipv6[$i]));
907

    
908
                if ($ipv6[$i] >= 0 && $dec <= 65535
909
                    && $hex == strtoupper(dechex($dec))) {
910

    
911
                    $count++;
912

    
913
                }
914

    
915
            }
916

    
917
            if (8 == $count and empty($ipPart[1])) {
918

    
919
                return true;
920

    
921
            } else if (6 == $count and !empty($ipPart[1])) {
922

    
923
                $ipv4  = explode('.', $ipPart[1]);
924
                $count = 0;
925

    
926
                for ($i = 0; $i < count($ipv4); $i++) {
927

    
928
                    if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255
929
                        && preg_match("/^\d{1,3}$/", $ipv4[$i])) {
930

    
931
                        $count++;
932

    
933
                    }
934

    
935
                }
936

    
937
                if (4 == $count) {
938

    
939
                    return true;
940

    
941
                }
942

    
943
            } else {
944

    
945
                return false;
946

    
947
            }
948

    
949
        } else {
950

    
951
            return false;
952

    
953
        }
954

    
955
    }
956

    
957
    // }}}
958

    
959
    // {{{ _parseAddress()
960

    
961
    /**
962
     * Returns the lowest and highest IPv6 address
963
     * for a given IP and netmask specification
964
     * 
965
     * The netmask may be a part of the $ip or 
966
     * the number of netwask bits is provided via $bits
967
     *
968
     * The result is an indexed array. The key 'start'
969
     * contains the lowest possible IP adress. The key
970
     * 'end' the highest address.
971
     *
972
     * @param String $ipToParse the IPv6 address
973
     * @param String $bits      the optional count of netmask bits
974
     *
975
     * @return Array ['start', 'end'] the lowest and highest IPv6 address
976
     * @access public
977
     * @static
978
     * @author Nicholas Williams
979
     */
980

    
981
    public static function parseAddress($ipToParse, $bits = null)
982
    {
983

    
984
        $ip      = null;
985
        $bitmask = null;
986

    
987
        if ( null == $bits ) {  
988

    
989
            $elements = explode('/', $ipToParse);
990

    
991
            if ( 2 == count($elements) ) {
992

    
993
                $ip      = Net_IPv6::uncompress($elements[0]);
994
                $bitmask = $elements[1];
995

    
996
            } else {
997

    
998
                include_once 'PEAR.php';
999

    
1000
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
1001
                                        NET_IPV6_NO_NETMASK);
1002
            }
1003
        } else {
1004

    
1005
            $ip      = Net_IPv6::uncompress($ipToParse);
1006
            $bitmask = $bits;
1007

    
1008
        }
1009

    
1010
        $binNetmask = str_repeat('1', $bitmask).
1011
                      str_repeat('0', 128 - $bitmask);
1012
        $maxNetmask = str_repeat('1', 128);
1013
        $netmask    = Net_IPv6::_bin2Ip($binNetmask);
1014

    
1015
        $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
1016
                                          & $binNetmask);
1017
        $endAddress   = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
1018
                                          | ($binNetmask ^ $maxNetmask));
1019

    
1020
        return array('start' => $startAddress, 'end' => $endAddress);
1021
    }
1022

    
1023
    // }}}
1024

    
1025
    // {{{ _ip2Bin()
1026

    
1027
    /**
1028
     * Converts an IPv6 address from Hex into Binary representation.
1029
     *
1030
     * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 
1031
     *                   compressed IPs are allowed
1032
     *
1033
     * @return String the binary representation
1034
     * @access private
1035
     @ @since 1.1.0
1036
     */
1037
    protected static function _ip2Bin($ip) 
1038
    {
1039
        $binstr = '';
1040

    
1041
        $ip = Net_IPv6::removeNetmaskSpec($ip);
1042
        $ip = Net_IPv6::Uncompress($ip);
1043

    
1044
        $parts = explode(':', $ip);
1045

    
1046
        foreach ( $parts as $v ) {
1047

    
1048
            $str     = base_convert($v, 16, 2);
1049
            $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
1050

    
1051
        }
1052

    
1053
        return $binstr;
1054
    }
1055

    
1056
    // }}}
1057
    // {{{ _bin2Ip()
1058

    
1059
    /**
1060
     * Converts an IPv6 address from Binary into Hex representation.
1061
     *
1062
     * @param String $bin the IP address as binary
1063
     *
1064
     * @return String the uncompressed Hex representation
1065
     * @access private
1066
     @ @since 1.1.0
1067
     */
1068
    protected static function _bin2Ip($bin)
1069
    {
1070
        $ip = "";
1071

    
1072
        if (strlen($bin) < 128) {
1073

    
1074
            $bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
1075

    
1076
        }
1077

    
1078
        $parts = str_split($bin, "16");
1079

    
1080
        foreach ( $parts as $v ) {
1081

    
1082
            $str = base_convert($v, 2, 16);
1083
            $ip .= $str.":";
1084

    
1085
        }
1086

    
1087
        $ip = substr($ip, 0, -1);
1088

    
1089
        return $ip;
1090
    }
1091

    
1092
    // }}}
1093
}
1094
// }}}
1095

    
1096
/*
1097
 * Local variables:
1098
 * tab-width: 4
1099
 * c-basic-offset: 4
1100
 * c-hanging-comment-ender-p: nil
1101
 * End:
1102
 */
1103

    
1104
?>
(2-2/3)