Project

General

Profile

Download (21.7 KB) Statistics
| Branch: | Tag: | Revision:
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   http://www.opensource.org/licenses/bsd-license.php
22
 * @version   CVS: $Id: IPv6.inc,v 1.2 2008/11/26 03:54:43 sumacob Exp $
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 can not assigned to a specific type
97
 * @see getAddressType()
98
 */
99
define("NET_IPV6_UNKNOWN_TYPE", 1001);
100

    
101
// }}}
102
// {{{ Net_IPv6
103

    
104
/**
105
 * Class to validate and to work with IPv6 addresses.
106
 *
107
 * @category  Net
108
 * @package   Net_IPv6
109
 * @copyright 2003-2005 The PHP Group
110
 * @license   http://www.opensource.org/licenses/bsd-license.php
111
 * @version   $Release$
112
 * @link      http://pear.php.net/package/Net_IPv6
113
 * @author    Alexander Merz <alexander.merz@web.de>
114
 * @author    <elfrink at introweb dot nl>
115
 * @author    Josh Peck <jmp at joshpeck dot org>
116
 */
117
class Net_IPv6 {
118

    
119
    // {{{ removeNetmaskBits()
120

    
121
    /**
122
     * Removes a possible existing netmask specification at an IP addresse.
123
     *
124
     * @param  String $ip the (compressed) IP as Hex representation
125
     *
126
     * @return String the IP without netmask length
127
     * @since  1.1.0
128
     * @access public
129
     * @static
130
     */
131
    function removeNetmaskSpec($ip) 
132
    {
133
        $addr = $ip;
134

    
135
        if (false !== strpos($ip, '/')) {
136

    
137
            $elements = explode('/', $ip);
138

    
139
            if (2 == count($elements)) {
140

    
141
                $addr = $elements[0];
142

    
143
            }
144

    
145
        }
146

    
147
        return $addr;
148
    }
149

    
150
    /**
151
     * Returns a possible existing netmask specification at an IP addresse.
152
     *
153
     * @param  String $ip the (compressed) IP as Hex representation
154
     *
155
     * @return String the netmask spec
156
     * @since  1.1.0
157
     * @access public
158
     * @static
159
     */
160
    function getNetmaskSpec($ip) 
161
    {
162
        $spec = '';
163

    
164
        if (false !== strpos($ip, '/')) {
165

    
166
            $elements = explode('/', $ip);
167

    
168
            if (2 == count($elements)) {
169

    
170
                $spec = $elements[1];
171

    
172
            }
173

    
174
        }
175

    
176
        return $spec;
177
    }
178

    
179
    // }}}
180
    // {{{ getNetmask()
181

    
182
    /**
183
     * Calculates the network prefix based on the netmask bits.
184
     *
185
     * @param  String $ip the (compressed) IP in Hex format
186
     * @param  int $bits if the number of netmask bits is not part of the IP
187
     *                  you must provide the number of bits
188
     *
189
     * @return String the network prefix
190
     * @since  1.1.0
191
     * @access public
192
     * @static
193
     */
194
    function getNetmask($ip, $bits = null)
195
    {
196
        if (null==$bits) {
197

    
198
            $elements = explode('/', $ip);
199

    
200
            if (2 == count($elements)) {
201

    
202
                $addr = $elements[0];
203
                $bits = $elements[1];
204

    
205
            } else {
206

    
207
                include_once 'PEAR.inc';
208

    
209
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
210
                                        NET_IPV6_NO_NETMASK);
211
            }
212

    
213
        } else {
214

    
215
            $addr = $ip;
216

    
217
        }
218

    
219
        $addr       = Net_IPv6::uncompress($addr);
220
        $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits);
221

    
222
        return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask);
223
    }
224

    
225
    // }}}
226
    // {{{ isInNetmask()
227

    
228
    /**
229
     * Checks if an (compressed) IP is in a specific address space.
230
     *
231
     * IF the IP does not contains the number of netmask bits (F8000::FFFF/16)
232
     * then you have to use the $bits parameter.
233
     *
234
     * @param  String $ip the IP to check (eg. F800::FFFF)
235
     * @param  String $netmask the netmask (eg F800::)
236
     * @param  int $bits the number of netmask bits to compare, 
237
     *                   if not given in $ip
238
     *
239
     * @return boolean true if $ip is in the netmask
240
     * @since  1.1.0
241
     * @access public
242
     * @static
243
     */
244
    function isInNetmask($ip, $netmask, $bits=null) 
245
    {
246
        // try to get the bit count
247

    
248
        if (null == $bits) {
249

    
250
            $elements = explode('/', $ip);
251

    
252
            if (2 == count($elements)) {
253

    
254
                $ip   = $elements[0];
255
                $bits = $elements[1];
256

    
257
            } else if (null == $bits) {
258

    
259
                $elements = explode('/', $netmask);
260

    
261
                if (2 == count($elements)) {
262

    
263
                     $netmask = $elements[0];
264
                     $bits   = $elements[1];
265

    
266
                }
267

    
268
                if (null == $bits) {
269

    
270
                    include_once 'PEAR.inc';
271
                    return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
272
                                            NET_IPV6_NO_NETMASK);
273

    
274
                }
275

    
276
            }
277

    
278
        }
279

    
280
        $binIp      = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip));
281
        $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask));
282

    
283
        if (null != $bits 
284
            && "" != $bits 
285
            && 0 == strncmp( $binNetmask, $binIp, $bits)) {
286

    
287
            return true;
288

    
289
        }
290

    
291
        return false;
292
    }
293

    
294
    // }}}
295
    // {{{ getAddressType()
296

    
297
    /**
298
     * Returns the type of an IPv6 address.
299
     *
300
     * RFC 1883, Section 2.3 describes several types of addresses in
301
     * the IPv6 addresse space.
302
     * Several addresse types are markers for reserved spaces and as 
303
     * consequence a subject to change.
304
     *
305
     * @param  String $ip the IP address in Hex format, 
306
     *                    compressed IPs are allowed
307
     *
308
     * @return int one of the addresse type constants
309
     * @access public
310
     * @since  1.1.0
311
     * @static
312
     *
313
     * @see    NET_IPV6_UNASSIGNED
314
     * @see    NET_IPV6_RESERVED
315
     * @see    NET_IPV6_RESERVED_NSAP
316
     * @see    NET_IPV6_RESERVED_IPX
317
     * @see    NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC
318
     * @see    NET_IPV6_UNICAST_PROVIDER
319
     * @see    NET_IPV6_MULTICAST
320
     * @see    NET_IPV6_LOCAL_LINK
321
     * @see    NET_IPV6_LOCAL_SITE
322
     * @see    NET_IPV6_UNKNOWN_TYPE
323
     */
324
    function getAddressType($ip) 
325
    {
326
        $ip    = Net_IPv6::removeNetmaskSpec($ip);
327
        $binip = Net_IPv6::_ip2Bin($ip);
328

    
329
        if (0 == strncmp('1111111010', $binip, 10)) {
330

    
331
            return NET_IPV6_LOCAL_LINK;
332

    
333
        } else if (0 == strncmp('1111111011', $binip, 10)) {
334

    
335
            return NET_IPV6_LOCAL_SITE;
336

    
337
        } else if (0 == strncmp('111111100', $binip, 9)) {
338

    
339
            return NET_IPV6_UNASSIGNED;
340

    
341
        } else if (0 == strncmp('11111111', $binip, 8)) {
342

    
343
            return NET_IPV6_MULTICAST;
344

    
345
        }  else if (0 == strncmp('00000000', $binip, 8)) {
346

    
347
            return NET_IPV6_RESERVED;
348

    
349
        } else if (0 == strncmp('00000001', $binip, 8) 
350
                    || 0 == strncmp('1111110', $binip, 7)) {
351

    
352
            return NET_IPV6_UNASSIGNED;
353

    
354
        } else if (0 == strncmp('0000001', $binip, 7)) {
355

    
356
            return NET_IPV6_RESERVED_NSAP;
357

    
358
        } else if (0 == strncmp('0000010', $binip, 7)) {
359

    
360
            return NET_IPV6_RESERVED_IPX;;
361

    
362
        } else if (0 == strncmp('0000011', $binip, 7) ||
363
                    0 == strncmp('111110', $binip, 6) ||
364
                    0 == strncmp('11110', $binip, 5) ||
365
                    0 == strncmp('00001', $binip, 5) ||
366
                    0 == strncmp('1110', $binip, 4) ||
367
                    0 == strncmp('0001', $binip, 4) ||
368
                    0 == strncmp('001', $binip, 3) ||
369
                    0 == strncmp('011', $binip, 3) ||
370
                    0 == strncmp('101', $binip, 3) ||
371
                    0 == strncmp('110', $binip, 3)) {
372

    
373
            return NET_IPV6_UNASSIGNED;
374

    
375
        } else if (0 == strncmp('010', $binip, 3)) {
376

    
377
            return NET_IPV6_UNICAST_PROVIDER;
378

    
379
        }  else if (0 == strncmp('100', $binip, 3)) {
380

    
381
            return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC;
382

    
383
        }
384

    
385
        return NET_IPV6_UNKNOWN_TYPE;
386
    }
387

    
388
    // }}}
389
    // {{{ Uncompress()
390

    
391
    /**
392
     * Uncompresses an IPv6 adress
393
     *
394
     * RFC 2373 allows you to compress zeros in an adress to '::'. This
395
     * function expects an valid IPv6 adress and expands the '::' to
396
     * the required zeros.
397
     *
398
     * Example:  FF01::101  ->  FF01:0:0:0:0:0:0:101
399
     *           ::1        ->  0:0:0:0:0:0:0:1
400
     *
401
     * @access public
402
     * @see Compress()
403
     * @static
404
     * @param string $ip    a valid IPv6-adress (hex format)
405
     * @return string   the uncompressed IPv6-adress (hex format)
406
     */
407
    function uncompress($ip) 
408
    {
409

    
410
        $prefix = Net_IPv6::getPrefixLength($ip);
411

    
412
        if (false === $prefix) {
413

    
414
            $prefix = '';
415

    
416
        } else {
417

    
418
            $ip     = Net_IPv6::removePrefixLength($ip);
419
            $prefix = '/'.$prefix;
420

    
421
        }
422

    
423
        $netmask = Net_IPv6::getNetmaskSpec($ip);
424
        $uip     = Net_IPv6::removeNetmaskSpec($ip);
425

    
426
        $c1 = -1;
427
        $c2 = -1;
428

    
429
        if (false !== strpos($uip, '::') ) {
430

    
431
            list($ip1, $ip2) = explode('::', $uip);
432

    
433
            if("" == $ip1) {
434

    
435
                $c1 = -1;
436

    
437
            } else {
438

    
439
                $pos = 0;
440

    
441
                if (0 < ($pos = substr_count($ip1, ':'))) {
442

    
443
                    $c1 = $pos;
444

    
445
                } else {
446

    
447
                    $c1 = 0;
448

    
449
                }
450
            }
451
            if ("" == $ip2) {
452

    
453
                $c2 = -1;
454

    
455
            } else {
456

    
457
                $pos = 0;
458

    
459
                if (0 < ($pos = substr_count($ip2, ':'))) {
460

    
461
                    $c2 = $pos;
462

    
463
                } else {
464

    
465
                    $c2 = 0;
466

    
467
                }
468

    
469
            }
470

    
471
            if (strstr($ip2, '.')) {
472

    
473
                $c2++;
474

    
475
            }
476
            if (-1 == $c1 && -1 == $c2) { // ::
477

    
478
                $uip = "0:0:0:0:0:0:0:0";
479

    
480
            } else if (-1 == $c1) {              // ::xxx
481

    
482
                $fill = str_repeat('0:', 7-$c2);
483
                $uip  = str_replace('::', $fill, $uip);
484

    
485
            } else if (-1 == $c2) {              // xxx::
486

    
487
                $fill = str_repeat(':0', 7-$c1);
488
                $uip  = str_replace('::', $fill, $uip);
489

    
490
            } else {                          // xxx::xxx
491

    
492
                $fill = str_repeat(':0:', 6-$c2-$c1);
493
                $uip  = str_replace('::', $fill, $uip);
494
                $uip  = str_replace('::', ':', $uip);
495

    
496
            }
497
        }
498
        if ('' != $netmask) {
499

    
500
                $uip = $uip.'/'.$netmask;
501

    
502
        }
503

    
504
        return $uip.$prefix;
505
    }
506

    
507
    // }}}
508
    // {{{ Compress()
509

    
510
    /**
511
     * Compresses an IPv6 adress
512
     *
513
     * RFC 2373 allows you to compress zeros in an adress to '::'. This
514
     * function expects an valid IPv6 adress and compresses successive zeros
515
     * to '::'
516
     *
517
     * Example:  FF01:0:0:0:0:0:0:101   -> FF01::101
518
     *           0:0:0:0:0:0:0:1        -> ::1
519
     *
520
     * @access public
521
     * @see Uncompress()
522
     * @static
523
     * @param string $ip    a valid IPv6-adress (hex format)
524
     * @return string   the compressed IPv6-adress (hex format)
525
     * @author elfrink at introweb dot nl
526
     */
527
    function compress($ip)  
528
    {
529
        $prefix = Net_IPv6::getPrefixLength($ip);
530

    
531
        if (false === $prefix) {
532

    
533
            $prefix = '';
534

    
535
        } else {
536

    
537
            $ip     = Net_IPv6::removePrefixLength($ip);
538
            $prefix = '/'.$prefix;
539

    
540
        }
541

    
542
        $netmask = Net_IPv6::getNetmaskSpec($ip);
543
        $ip      = Net_IPv6::removeNetmaskSpec($ip);
544

    
545
        if (!strstr($ip, '::')) {
546

    
547
            $ipp = explode(':',$ip);
548

    
549
            for ($i = 0; $i < count($ipp); $i++) {
550

    
551
                $ipp[$i] = dechex(hexdec($ipp[$i]));
552

    
553
            }
554

    
555
            $cip = ':' . join(':',$ipp) . ':';
556

    
557
            preg_match_all("/(:0)+/", $cip, $zeros);
558

    
559
            if (count($zeros[0]) > 0) {
560

    
561
                $match = '';
562

    
563
                foreach($zeros[0] as $zero) {
564

    
565
                    if (strlen($zero) > strlen($match)) {
566

    
567
                        $match = $zero;
568

    
569
                    }
570
                }
571

    
572
                $cip = preg_replace('/' . $match . '/', ':', $cip, 1);
573

    
574
            }
575

    
576
            $cip = preg_replace('/((^:)|(:$))/', '' ,$cip);
577
            $cip = preg_replace('/((^:)|(:$))/', '::' ,$cip);
578

    
579
         }
580
         if ('' != $netmask) {
581

    
582
                $cip = $cip.'/'.$netmask;
583

    
584
         }
585

    
586
         return $cip.$prefix;
587
    }
588

    
589
    // }}}
590
    // {{{ SplitV64()
591

    
592
    /**
593
     * Splits an IPv6 adress into the IPv6 and a possible IPv4 part
594
     *
595
     * RFC 2373 allows you to note the last two parts of an IPv6 adress as
596
     * an IPv4 compatible adress
597
     *
598
     * Example:  0:0:0:0:0:0:13.1.68.3
599
     *           0:0:0:0:0:FFFF:129.144.52.38
600
     *
601
     * @param  string $ip  a valid IPv6-adress (hex format)
602
     *
603
     * @return array  [0] contains the IPv6 part, 
604
     *                [1] the IPv4 part (hex format)
605
     * @access public
606
     * @static
607
     */
608
    function SplitV64($ip, $uncompress = true) 
609
    {
610
        $ip = Net_IPv6::removeNetmaskSpec($ip);
611

    
612
        if ($uncompress) {
613

    
614
            $ip = Net_IPv6::Uncompress($ip);
615

    
616
        }
617

    
618
        if (strstr($ip, '.')) {
619

    
620
            $pos      = strrpos($ip, ':');
621
            $ip{$pos} = '_';
622
            $ipPart   = explode('_', $ip);
623

    
624
            return $ipPart;
625

    
626
        } else {
627

    
628
            return array($ip, "");
629

    
630
        }
631
    }
632

    
633
    // }}}
634
    // {{{ checkIPv6()
635

    
636
    /**
637
     * Checks an IPv6 adress
638
     *
639
     * Checks if the given IP is IPv6-compatible
640
     *
641
     * @access public
642
     * @static
643
     * @param string $ip    a valid IPv6-adress
644
     * @return boolean  true if $ip is an IPv6 adress
645
     */
646
    function checkIPv6($ip) 
647
    {
648
        $ip = Net_IPv6::removePrefixLength($ip);
649
        $ip = Net_IPv6::removeNetmaskSpec($ip);
650

    
651
        $ipPart = Net_IPv6::SplitV64($ip);
652
        $count  = 0;
653

    
654
        if (!empty($ipPart[0]))
655
        {
656
            $ipv6 =explode(':', $ipPart[0]);
657

    
658
            for ($i = 0; $i < count($ipv6); $i++) {
659

    
660
                $dec = hexdec($ipv6[$i]);
661
                $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/",
662
                                                "\\1", 
663
                                                $ipv6[$i]));
664

    
665
                if ($ipv6[$i] >= 0 && $dec <= 65535
666
                    && $hex == strtoupper(dechex($dec))) {
667

    
668
                    $count++;
669

    
670
                }
671

    
672
            }
673

    
674
            if (8 == $count) {
675

    
676
                return true;
677

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

    
680
                $ipv4  = explode('.',$ipPart[1]);
681
                $count = 0;
682

    
683
                for ($i = 0; $i < count($ipv4); $i++) {
684

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

    
688
                        $count++;
689

    
690
                    }
691

    
692
                }
693

    
694
                if (4 == $count) {
695

    
696
                    return true;
697

    
698
                }
699

    
700
            } else {
701

    
702
                return false;
703

    
704
            }
705

    
706
        } else {
707

    
708
            return false;
709

    
710
        }
711

    
712
    }
713

    
714
    // }}}
715
    // {{{ getPrefixLength()
716

    
717
    /**
718
     * Tests for a prefix length specification in the address
719
     * and returns the prefix length, if exists
720
     *
721
     * @param  String $ip a valid ipv6 address
722
     *
723
     * @return Mixed the prefix as String or false, if no prefix was found
724
     * @access public
725
     * @static
726
     */
727
    function getPrefixLength($ip) 
728
    {
729
        if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", 
730
                        $ip, $matches)) {
731

    
732
            return $matches[2];
733

    
734
        } else {
735

    
736
            return false;
737

    
738
        }
739

    
740
    }
741

    
742
    // }}}
743
    // {{{ removePrefixLength()
744

    
745
    /**
746
     * Tests for a prefix length specification in the address
747
     * and removes the prefix length, if exists
748
     *
749
     * @param  String $ip a valid ipv6 address
750
     *
751
     * @return String the address without a prefix length
752
     * @access public
753
     * @static
754
     */
755
    function removePrefixLength($ip) 
756
    {
757
        $pos = strrpos($ip, '/');
758

    
759
        if (false !== $pos) {
760

    
761
            return substr($ip, 0, $pos);
762

    
763
        }
764

    
765
        return $ip;
766
    }
767

    
768
    // }}}
769

    
770
    // {{{ _parseAddress()
771

    
772
    /**
773
     * Returns the lowest and highest IPv6 address
774
     * for a given IP and netmask specification
775
     * 
776
     * The netmask may be a part of the $ip or 
777
     * the number of netwask bits is provided via $bits
778
     *
779
     * The result is an indexed array. The key 'start'
780
     * contains the lowest possible IP adress. The key
781
     * 'end' the highest address.
782
     *
783
     * @param  String $ip the IPv6 address
784
     * @param  String $bits the optional count of netmask bits
785
     *
786
     * @return Array ['start', 'end'] the lowest and highest IPv6 address
787
     * @access public
788
     * @static
789
     * @author Nicholas Williams
790
     */
791

    
792
    function parseAddress($ipToParse, $bits = null)
793
    {
794

    
795
        $ip      = null;
796
        $bitmask = null;
797

    
798
    	if( null == $bits )
799
    	{
800

    
801
    		$elements = explode('/', $ipToParse);
802

    
803
    		if( 2 == count($elements) ) {
804

    
805
    			$ip      = Net_IPv6::uncompress($elements[0]);
806
    			$bitmask = $elements[1];
807

    
808
    		} else {
809

    
810
    			include_once 'PEAR.inc';
811

    
812
    			return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
813
                                        NET_IPV6_NO_NETMASK);
814
    		}
815
    	}
816
    	else
817
    	{
818

    
819
    		$ip      = Net_IPv6::uncompress($ipToParse);
820
    		$bitmask = $bits;
821

    
822
    	}
823

    
824
    	$binNetmask = str_repeat('1', $bitmask).
825
                      str_repeat('0', 128 - $bitmask);
826
    	$maxNetmask = str_repeat('1', 128);
827
    	$netmask    = Net_IPv6::_bin2Ip($binNetmask);
828

    
829
    	$startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 
830
                        & $binNetmask);
831
    	$endAddress   = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
832
                        | ($binNetmask ^ $maxNetmask));
833

    
834
    	return array('start' => $startAddress, 'end' => $endAddress);
835
    }
836

    
837
    // }}}
838

    
839
    // {{{ _ip2Bin()
840

    
841
    /**
842
     * Converts an IPv6 address from Hex into Binary representation.
843
     *
844
     * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 
845
     *                   compressed IPs are allowed
846
     *
847
     * @return String the binary representation
848
     * @access private
849
     @ @since 1.1.0
850
     */
851
    function _ip2Bin($ip) 
852
    {
853
        $binstr = '';
854

    
855
        $ip = Net_IPv6::removeNetmaskSpec($ip);
856
        $ip = Net_IPv6::Uncompress($ip);
857

    
858
        $parts = explode(':', $ip);
859

    
860
        foreach($parts as $v) {
861

    
862
            $str     = base_convert($v, 16, 2);
863
            $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
864

    
865
        }
866

    
867
        return $binstr;
868
    }
869

    
870
    // }}}
871
    // {{{ _bin2Ip()
872

    
873
    /**
874
     * Converts an IPv6 address from Binary into Hex representation.
875
     *
876
     * @param String $ip the IP as binary
877
     *
878
     * @return String the uncompressed Hex representation
879
     * @access private
880
     @ @since 1.1.0
881
     */
882
    function _bin2Ip($bin)
883
    {
884
        $ip = "";
885

    
886
        if (strlen($bin) < 128) {
887

    
888
            $bin = str_pad($str, 128, '0', STR_PAD_LEFT);
889

    
890
        }
891

    
892
        $parts = str_split($bin, "16");
893

    
894
        foreach($parts as $v) {
895

    
896
            $str = base_convert($v, 2, 16);
897
            $ip .= $str.":";
898

    
899
        }
900

    
901
        $ip = substr($ip, 0,-1);
902

    
903
        return $ip;
904
    }
905

    
906
    // }}}
907
}
908
// }}}
909

    
910
/*
911
 * Local variables:
912
 * tab-width: 4
913
 * c-basic-offset: 4
914
 * c-hanging-comment-ender-p: nil
915
 * End:
916
 */
917

    
918
?>
(1-1/44)