Project

General

Profile

Download (20.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2

    
3
/*
4
	pfSense_MODULE:	utils
5
*/
6

    
7
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
8

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

    
30
// {{{ constants
31

    
32
/**
33
 * Error message if netmask bits was not found
34
 * @see isInNetmask
35
 */
36
define("NET_IPV6_NO_NETMASK_MSG", "Netmask length not found");
37

    
38
/**
39
 * Error code if netmask bits was not found
40
 * @see isInNetmask
41
 */
42
define("NET_IPV6_NO_NETMASK", 10);
43

    
44
/**
45
 * Address Type: Unassigned (RFC 1884, Section 2.3)
46
 * @see getAddressType()
47
 */
48
define("NET_IPV6_UNASSIGNED", 1);
49

    
50
/**
51
 * Address Type: Reserved (RFC 1884, Section 2.3)
52
 * @see getAddressType()
53
 */
54
define("NET_IPV6_RESERVED", 11);
55

    
56
/**
57
 * Address Type: Reserved for NSAP Allocation (RFC 1884, Section 2.3)
58
 * @see getAddressType()
59
 */
60
define("NET_IPV6_RESERVED_NSAP", 12);
61

    
62
/**
63
 * Address Type: Reserved for IPX Allocation (RFC 1884, Section 2.3)
64
 * @see getAddressType()
65
 */
66
define("NET_IPV6_RESERVED_IPX", 13);
67

    
68
/**
69
 * Address Type: Reserved for Geographic-Based Unicast Addresses 
70
 * (RFC 1884, Section 2.3)
71
 * @see getAddressType()
72
 */
73
define("NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC", 14);
74

    
75
/**
76
 * Address Type: Provider-Based Unicast Address (RFC 1884, Section 2.3)
77
 * @see getAddressType()
78
 */
79
define("NET_IPV6_UNICAST_PROVIDER", 22);
80

    
81
/**
82
 * Address Type: Multicast Addresses (RFC 1884, Section 2.3)
83
 * @see getAddressType()
84
 */
85
define("NET_IPV6_MULTICAST", 31);
86

    
87
/**
88
 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3)
89
 * @see getAddressType()
90
 */
91
define("NET_IPV6_LOCAL_LINK", 42);
92

    
93
/**
94
 * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3)
95
 * @see getAddressType()
96
 */
97
define("NET_IPV6_LOCAL_SITE", 43);
98

    
99
/**
100
 * Address Type: address can not assigned to a specific type
101
 * @see getAddressType()
102
 */
103
define("NET_IPV6_UNKNOWN_TYPE", 1001);
104

    
105
// }}}
106
// {{{ Net_IPv6
107

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

    
123
    // {{{ removeNetmaskBits()
124

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

    
139
        if (false !== strpos($ip, '/')) {
140

    
141
            $elements = explode('/', $ip);
142

    
143
            if (2 == count($elements)) {
144

    
145
                $addr = $elements[0];
146

    
147
            }
148

    
149
        }
150

    
151
        return $addr;
152
    }
153

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

    
168
        if (false !== strpos($ip, '/')) {
169

    
170
            $elements = explode('/', $ip);
171

    
172
            if (2 == count($elements)) {
173

    
174
                $spec = $elements[1];
175

    
176
            }
177

    
178
        }
179

    
180
        return $spec;
181
    }
182

    
183
    // }}}
184
    // {{{ getNetmask()
185

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

    
202
            $elements = explode('/', $ip);
203

    
204
            if (2 == count($elements)) {
205

    
206
                $addr = $elements[0];
207
                $bits = $elements[1];
208

    
209
            } else {
210

    
211
                include_once 'PEAR.inc';
212

    
213
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
214
                                        NET_IPV6_NO_NETMASK);
215
            }
216

    
217
        } else {
218

    
219
            $addr = $ip;
220

    
221
        }
222

    
223
        $addr       = Net_IPv6::uncompress($addr);
224
        $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits);
225

    
226
        return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask);
227
    }
228

    
229
    // }}}
230
    // {{{ isInNetmask()
231

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

    
252
        if (null == $bits) {
253

    
254
            $elements = explode('/', $ip);
255

    
256
            if (2 == count($elements)) {
257

    
258
                $ip   = $elements[0];
259
                $bits = $elements[1];
260

    
261
            } else if (null == $bits) {
262

    
263
                $elements = explode('/', $netmask);
264

    
265
                if (2 == count($elements)) {
266

    
267
                     $netmask = $elements[0];
268
                     $bits   = $elements[1];
269

    
270
                }
271

    
272
                if (null == $bits) {
273

    
274
                    include_once 'PEAR.inc';
275
                    return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
276
                                            NET_IPV6_NO_NETMASK);
277

    
278
                }
279

    
280
            }
281

    
282
        }
283

    
284
        $binIp      = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip));
285
        $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask));
286

    
287
        if (null != $bits 
288
            && "" != $bits 
289
            && 0 == strncmp( $binNetmask, $binIp, $bits)) {
290

    
291
            return true;
292

    
293
        }
294

    
295
        return false;
296
    }
297

    
298
    // }}}
299
    // {{{ getAddressType()
300

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

    
333
        if (0 == strncmp('1111111010', $binip, 10)) {
334

    
335
            return NET_IPV6_LOCAL_LINK;
336

    
337
        } else if (0 == strncmp('1111111011', $binip, 10)) {
338

    
339
            return NET_IPV6_LOCAL_SITE;
340

    
341
        } else if (0 == strncmp('111111100', $binip, 9)) {
342

    
343
            return NET_IPV6_UNASSIGNED;
344

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

    
347
            return NET_IPV6_MULTICAST;
348

    
349
        }  else if (0 == strncmp('00000000', $binip, 8)) {
350

    
351
            return NET_IPV6_RESERVED;
352

    
353
        } else if (0 == strncmp('00000001', $binip, 8) 
354
                    || 0 == strncmp('1111110', $binip, 7)) {
355

    
356
            return NET_IPV6_UNASSIGNED;
357

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

    
360
            return NET_IPV6_RESERVED_NSAP;
361

    
362
        } else if (0 == strncmp('0000010', $binip, 7)) {
363

    
364
            return NET_IPV6_RESERVED_IPX;;
365

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

    
377
            return NET_IPV6_UNASSIGNED;
378

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

    
381
            return NET_IPV6_UNICAST_PROVIDER;
382

    
383
        }  else if (0 == strncmp('100', $binip, 3)) {
384

    
385
            return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC;
386

    
387
        }
388

    
389
        return NET_IPV6_UNKNOWN_TYPE;
390
    }
391

    
392
    // }}}
393
    // {{{ Uncompress()
394

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

    
414
        $prefix = Net_IPv6::getPrefixLength($ip);
415

    
416
        if (false === $prefix) {
417

    
418
            $prefix = '';
419

    
420
        } else {
421

    
422
            $ip     = Net_IPv6::removePrefixLength($ip);
423
            $prefix = '/'.$prefix;
424

    
425
        }
426

    
427
        $netmask = Net_IPv6::getNetmaskSpec($ip);
428
        $uip     = Net_IPv6::removeNetmaskSpec($ip);
429

    
430
        $c1 = -1;
431
        $c2 = -1;
432

    
433
        if (false !== strpos($uip, '::') ) {
434

    
435
            list($ip1, $ip2) = explode('::', $uip);
436

    
437
            if("" == $ip1) {
438

    
439
                $c1 = -1;
440

    
441
            } else {
442

    
443
                $pos = 0;
444

    
445
                if (0 < ($pos = substr_count($ip1, ':'))) {
446

    
447
                    $c1 = $pos;
448

    
449
                } else {
450

    
451
                    $c1 = 0;
452

    
453
                }
454
            }
455
            if ("" == $ip2) {
456

    
457
                $c2 = -1;
458

    
459
            } else {
460

    
461
                $pos = 0;
462

    
463
                if (0 < ($pos = substr_count($ip2, ':'))) {
464

    
465
                    $c2 = $pos;
466

    
467
                } else {
468

    
469
                    $c2 = 0;
470

    
471
                }
472

    
473
            }
474

    
475
            if (strstr($ip2, '.')) {
476

    
477
                $c2++;
478

    
479
            }
480
            if (-1 == $c1 && -1 == $c2) { // ::
481

    
482
                $uip = "0:0:0:0:0:0:0:0";
483

    
484
            } else if (-1 == $c1) {              // ::xxx
485

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

    
489
            } else if (-1 == $c2) {              // xxx::
490

    
491
                $fill = str_repeat(':0', 7-$c1);
492
                $uip  = str_replace('::', $fill, $uip);
493

    
494
            } else {                          // xxx::xxx
495

    
496
                $fill = str_repeat(':0:', 6-$c2-$c1);
497
                $uip  = str_replace('::', $fill, $uip);
498
                $uip  = str_replace('::', ':', $uip);
499

    
500
            }
501
        }
502
        if ('' != $netmask) {
503

    
504
                $uip = $uip.'/'.$netmask;
505

    
506
        }
507

    
508
        return $uip.$prefix;
509
    }
510

    
511
    // }}}
512
    // {{{ Compress()
513

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

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

    
537
            $prefix = '';
538

    
539
        } else {
540

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

    
544
        }
545

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

    
549
        if (!strstr($ip, '::')) {
550

    
551
            $ipp = explode(':',$ip);
552

    
553
            for ($i = 0; $i < count($ipp); $i++) {
554

    
555
                $ipp[$i] = dechex(hexdec($ipp[$i]));
556

    
557
            }
558

    
559
            $cip = ':' . join(':',$ipp) . ':';
560

    
561
            preg_match_all("/(:0)+/", $cip, $zeros);
562

    
563
            if (count($zeros[0]) > 0) {
564

    
565
                $match = '';
566

    
567
                foreach($zeros[0] as $zero) {
568

    
569
                    if (strlen($zero) > strlen($match)) {
570

    
571
                        $match = $zero;
572

    
573
                    }
574
                }
575

    
576
                $cip = preg_replace('/' . $match . '/', ':', $cip, 1);
577

    
578
            }
579

    
580
            $cip = preg_replace('/((^:)|(:$))/', '' ,$cip);
581
            $cip = preg_replace('/((^:)|(:$))/', '::' ,$cip);
582

    
583
         }
584
         if ('' != $netmask) {
585

    
586
                $cip = $cip.'/'.$netmask;
587

    
588
         }
589

    
590
         return $cip.$prefix;
591
    }
592

    
593
    // }}}
594
    // {{{ SplitV64()
595

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

    
616
        if ($uncompress) {
617

    
618
            $ip = Net_IPv6::Uncompress($ip);
619

    
620
        }
621

    
622
        if (strstr($ip, '.')) {
623

    
624
            $pos      = strrpos($ip, ':');
625
            $ip{$pos} = '_';
626
            $ipPart   = explode('_', $ip);
627

    
628
            return $ipPart;
629

    
630
        } else {
631

    
632
            return array($ip, "");
633

    
634
        }
635
    }
636

    
637
    // }}}
638
    // {{{ checkIPv6()
639

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

    
655
        $ipPart = Net_IPv6::SplitV64($ip);
656
        $count  = 0;
657

    
658
        if (!empty($ipPart[0]))
659
        {
660
            $ipv6 =explode(':', $ipPart[0]);
661

    
662
            for ($i = 0; $i < count($ipv6); $i++) {
663

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

    
669
                if ($ipv6[$i] >= 0 && $dec <= 65535
670
                    && $hex == strtoupper(dechex($dec))) {
671

    
672
                    $count++;
673

    
674
                }
675

    
676
            }
677

    
678
            if (8 == $count) {
679

    
680
                return true;
681

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

    
684
                $ipv4  = explode('.',$ipPart[1]);
685
                $count = 0;
686

    
687
                for ($i = 0; $i < count($ipv4); $i++) {
688

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

    
692
                        $count++;
693

    
694
                    }
695

    
696
                }
697

    
698
                if (4 == $count) {
699

    
700
                    return true;
701

    
702
                }
703

    
704
            } else {
705

    
706
                return false;
707

    
708
            }
709

    
710
        } else {
711

    
712
            return false;
713

    
714
        }
715

    
716
    }
717

    
718
    // }}}
719
    // {{{ getPrefixLength()
720

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

    
736
            return $matches[2];
737

    
738
        } else {
739

    
740
            return false;
741

    
742
        }
743

    
744
    }
745

    
746
    // }}}
747
    // {{{ removePrefixLength()
748

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

    
763
        if (false !== $pos) {
764

    
765
            return substr($ip, 0, $pos);
766

    
767
        }
768

    
769
        return $ip;
770
    }
771

    
772
    // }}}
773

    
774
    // {{{ _parseAddress()
775

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

    
796
    function parseAddress($ipToParse, $bits = null)
797
    {
798

    
799
        $ip      = null;
800
        $bitmask = null;
801

    
802
    	if( null == $bits )
803
    	{
804

    
805
    		$elements = explode('/', $ipToParse);
806

    
807
    		if( 2 == count($elements) ) {
808

    
809
    			$ip      = Net_IPv6::uncompress($elements[0]);
810
    			$bitmask = $elements[1];
811

    
812
    		} else {
813

    
814
    			include_once 'PEAR.inc';
815

    
816
    			return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
817
                                        NET_IPV6_NO_NETMASK);
818
    		}
819
    	}
820
    	else
821
    	{
822

    
823
    		$ip      = Net_IPv6::uncompress($ipToParse);
824
    		$bitmask = $bits;
825

    
826
    	}
827

    
828
    	$binNetmask = str_repeat('1', $bitmask).
829
                      str_repeat('0', 128 - $bitmask);
830
    	$maxNetmask = str_repeat('1', 128);
831
    	$netmask    = Net_IPv6::_bin2Ip($binNetmask);
832

    
833
    	$startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 
834
                        & $binNetmask);
835
    	$endAddress   = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
836
                        | ($binNetmask ^ $maxNetmask));
837

    
838
    	return array('start' => $startAddress, 'end' => $endAddress);
839
    }
840

    
841
    // }}}
842

    
843
    // {{{ _ip2Bin()
844

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

    
859
        $ip = Net_IPv6::removeNetmaskSpec($ip);
860
        $ip = Net_IPv6::Uncompress($ip);
861

    
862
        $parts = explode(':', $ip);
863

    
864
        foreach($parts as $v) {
865

    
866
            $str     = base_convert($v, 16, 2);
867
            $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
868

    
869
        }
870

    
871
        return $binstr;
872
    }
873

    
874
    // }}}
875
    // {{{ _bin2Ip()
876

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

    
890
        if (strlen($bin) < 128) {
891

    
892
            $bin = str_pad($str, 128, '0', STR_PAD_LEFT);
893

    
894
        }
895

    
896
        $parts = str_split($bin, "16");
897

    
898
        foreach($parts as $v) {
899

    
900
            $str = base_convert($v, 2, 16);
901
            $ip .= $str.":";
902

    
903
        }
904

    
905
        $ip = substr($ip, 0,-1);
906

    
907
        return $ip;
908
    }
909

    
910
    // }}}
911
}
912
// }}}
913

    
914
/*
915
 * Local variables:
916
 * tab-width: 4
917
 * c-basic-offset: 4
918
 * c-hanging-comment-ender-p: nil
919
 * End:
920
 */
921

    
922
?>
(1-1/61)