Project

General

Profile

Download (21 KB) Statistics
| Branch: | Tag: | Revision:
1 56c100ab Erik Fonnesbeck
<?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 7405845f Phil Davis
     * Removes a possible existing netmask specification at an IP address.
127 56c100ab Erik Fonnesbeck
     *
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 7405845f Phil Davis
     * Returns a possible existing netmask specification at an IP address.
156 56c100ab Erik Fonnesbeck
     *
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 7405845f Phil Davis
     * IF the IP does not contain the number of netmask bits (F8000::FFFF/16)
236 56c100ab Erik Fonnesbeck
     * 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 7405845f Phil Davis
     * the IPv6 address space.
306
     * Several address types are markers for reserved spaces and as 
307
     * a consequence are subject to change.
308 56c100ab Erik Fonnesbeck
     *
309
     * @param  String $ip the IP address in Hex format, 
310
     *                    compressed IPs are allowed
311
     *
312 7405845f Phil Davis
     * @return int one of the address type constants
313 56c100ab Erik Fonnesbeck
     * @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 7405845f Phil Davis
     * Uncompresses an IPv6 address
397 56c100ab Erik Fonnesbeck
     *
398 7405845f Phil Davis
     * RFC 2373 allows you to compress zeros in an address to '::'. This
399
     * function expects a valid IPv6 address and expands the '::' to
400 56c100ab Erik Fonnesbeck
     * 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 7405845f Phil Davis
     * @param string $ip    a valid IPv6-address (hex format)
409
     * @return string   the uncompressed IPv6-address (hex format)
410 56c100ab Erik Fonnesbeck
     */
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 7405845f Phil Davis
                $fill = str_repeat(':0:', max(1, 6-$c2-$c1));
497 56c100ab Erik Fonnesbeck
                $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 7405845f Phil Davis
     * Compresses an IPv6 address
516 56c100ab Erik Fonnesbeck
     *
517 7405845f Phil Davis
     * RFC 2373 allows you to compress zeros in an address to '::'. This
518
     * function expects a valid IPv6 address and compresses successive zeros
519 56c100ab Erik Fonnesbeck
     * 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 7405845f Phil Davis
     * @param string $ip    a valid IPv6-address (hex format)
528
     * @return string   the compressed IPv6-address (hex format)
529 56c100ab Erik Fonnesbeck
     * @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 b4924d86 Phil Davis
         } else {
584
            // The input IP appears to be compressed already, give it back as-is to the caller.
585
            $cip = $ip;
586 56c100ab Erik Fonnesbeck
         }
587
         if ('' != $netmask) {
588
589
                $cip = $cip.'/'.$netmask;
590
591
         }
592
593
         return $cip.$prefix;
594
    }
595
596
    // }}}
597
    // {{{ SplitV64()
598
599
    /**
600 7405845f Phil Davis
     * Splits an IPv6 address into the IPv6 and a possible IPv4 part
601 56c100ab Erik Fonnesbeck
     *
602 7405845f Phil Davis
     * RFC 2373 allows you to note the last two parts of an IPv6 address as
603
     * an IPv4 compatible address
604 56c100ab Erik Fonnesbeck
     *
605
     * Example:  0:0:0:0:0:0:13.1.68.3
606
     *           0:0:0:0:0:FFFF:129.144.52.38
607
     *
608 7405845f Phil Davis
     * @param  string $ip  a valid IPv6-address (hex format)
609 56c100ab Erik Fonnesbeck
     *
610
     * @return array  [0] contains the IPv6 part, 
611
     *                [1] the IPv4 part (hex format)
612
     * @access public
613
     * @static
614
     */
615
    function SplitV64($ip, $uncompress = true) 
616
    {
617
        $ip = Net_IPv6::removeNetmaskSpec($ip);
618
619
        if ($uncompress) {
620
621
            $ip = Net_IPv6::Uncompress($ip);
622
623
        }
624
625
        if (strstr($ip, '.')) {
626
627
            $pos      = strrpos($ip, ':');
628
            $ip{$pos} = '_';
629
            $ipPart   = explode('_', $ip);
630
631
            return $ipPart;
632
633
        } else {
634
635
            return array($ip, "");
636
637
        }
638
    }
639
640
    // }}}
641
    // {{{ checkIPv6()
642
643
    /**
644 7405845f Phil Davis
     * Checks an IPv6 address
645 56c100ab Erik Fonnesbeck
     *
646
     * Checks if the given IP is IPv6-compatible
647
     *
648
     * @access public
649
     * @static
650 7405845f Phil Davis
     * @param string $ip    a valid IPv6-address
651
     * @return boolean  true if $ip is an IPv6 address
652 56c100ab Erik Fonnesbeck
     */
653
    function checkIPv6($ip) 
654
    {
655
        $ip = Net_IPv6::removePrefixLength($ip);
656
        $ip = Net_IPv6::removeNetmaskSpec($ip);
657
658
        $ipPart = Net_IPv6::SplitV64($ip);
659
        $count  = 0;
660
661
        if (!empty($ipPart[0]))
662
        {
663
            $ipv6 =explode(':', $ipPart[0]);
664
665
            for ($i = 0; $i < count($ipv6); $i++) {
666
667
                $dec = hexdec($ipv6[$i]);
668
                $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/",
669
                                                "\\1", 
670
                                                $ipv6[$i]));
671
672
                if ($ipv6[$i] >= 0 && $dec <= 65535
673
                    && $hex == strtoupper(dechex($dec))) {
674
675
                    $count++;
676
677
                }
678
679
            }
680
681
            if (8 == $count) {
682
683
                return true;
684
685
            } else if (6 == $count and !empty($ipPart[1])) {
686
687
                $ipv4  = explode('.',$ipPart[1]);
688
                $count = 0;
689
690
                for ($i = 0; $i < count($ipv4); $i++) {
691
692
                    if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255
693
                        && preg_match("/^\d{1,3}$/", $ipv4[$i])) {
694
695
                        $count++;
696
697
                    }
698
699
                }
700
701
                if (4 == $count) {
702
703
                    return true;
704
705
                }
706
707
            } else {
708
709
                return false;
710
711
            }
712
713
        } else {
714
715
            return false;
716
717
        }
718
719
    }
720
721
    // }}}
722
    // {{{ getPrefixLength()
723
724
    /**
725
     * Tests for a prefix length specification in the address
726
     * and returns the prefix length, if exists
727
     *
728
     * @param  String $ip a valid ipv6 address
729
     *
730
     * @return Mixed the prefix as String or false, if no prefix was found
731
     * @access public
732
     * @static
733
     */
734
    function getPrefixLength($ip) 
735
    {
736
        if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", 
737
                        $ip, $matches)) {
738
739
            return $matches[2];
740
741
        } else {
742
743
            return false;
744
745
        }
746
747
    }
748
749
    // }}}
750
    // {{{ removePrefixLength()
751
752
    /**
753
     * Tests for a prefix length specification in the address
754
     * and removes the prefix length, if exists
755
     *
756
     * @param  String $ip a valid ipv6 address
757
     *
758
     * @return String the address without a prefix length
759
     * @access public
760
     * @static
761
     */
762
    function removePrefixLength($ip) 
763
    {
764
        $pos = strrpos($ip, '/');
765
766
        if (false !== $pos) {
767
768
            return substr($ip, 0, $pos);
769
770
        }
771
772
        return $ip;
773
    }
774
775
    // }}}
776
777
    // {{{ _parseAddress()
778
779
    /**
780
     * Returns the lowest and highest IPv6 address
781
     * for a given IP and netmask specification
782
     * 
783
     * The netmask may be a part of the $ip or 
784 7405845f Phil Davis
     * the number of netmask bits is provided via $bits
785 56c100ab Erik Fonnesbeck
     *
786
     * The result is an indexed array. The key 'start'
787 7405845f Phil Davis
     * contains the lowest possible IP address. The key
788 56c100ab Erik Fonnesbeck
     * 'end' the highest address.
789
     *
790
     * @param  String $ip the IPv6 address
791
     * @param  String $bits the optional count of netmask bits
792
     *
793
     * @return Array ['start', 'end'] the lowest and highest IPv6 address
794
     * @access public
795
     * @static
796
     * @author Nicholas Williams
797
     */
798
799
    function parseAddress($ipToParse, $bits = null)
800
    {
801
802
        $ip      = null;
803
        $bitmask = null;
804
805
    	if( null == $bits )
806
    	{
807
808
    		$elements = explode('/', $ipToParse);
809
810
    		if( 2 == count($elements) ) {
811
812
    			$ip      = Net_IPv6::uncompress($elements[0]);
813
    			$bitmask = $elements[1];
814
815
    		} else {
816
817
    			include_once 'PEAR.inc';
818
819
    			return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
820
                                        NET_IPV6_NO_NETMASK);
821
    		}
822
    	}
823
    	else
824
    	{
825
826
    		$ip      = Net_IPv6::uncompress($ipToParse);
827
    		$bitmask = $bits;
828
829
    	}
830
831
    	$binNetmask = str_repeat('1', $bitmask).
832
                      str_repeat('0', 128 - $bitmask);
833
    	$maxNetmask = str_repeat('1', 128);
834
    	$netmask    = Net_IPv6::_bin2Ip($binNetmask);
835
836
    	$startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) 
837
                        & $binNetmask);
838
    	$endAddress   = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
839
                        | ($binNetmask ^ $maxNetmask));
840
841
    	return array('start' => $startAddress, 'end' => $endAddress);
842
    }
843
844
    // }}}
845
846
    // {{{ _ip2Bin()
847
848
    /**
849
     * Converts an IPv6 address from Hex into Binary representation.
850
     *
851
     * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 
852
     *                   compressed IPs are allowed
853
     *
854
     * @return String the binary representation
855
     * @access private
856
     @ @since 1.1.0
857
     */
858
    function _ip2Bin($ip) 
859
    {
860
        $binstr = '';
861
862
        $ip = Net_IPv6::removeNetmaskSpec($ip);
863
        $ip = Net_IPv6::Uncompress($ip);
864
865
        $parts = explode(':', $ip);
866
867
        foreach($parts as $v) {
868
869
            $str     = base_convert($v, 16, 2);
870
            $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
871
872
        }
873
874
        return $binstr;
875
    }
876
877
    // }}}
878
    // {{{ _bin2Ip()
879
880
    /**
881
     * Converts an IPv6 address from Binary into Hex representation.
882
     *
883
     * @param String $ip the IP as binary
884
     *
885
     * @return String the uncompressed Hex representation
886
     * @access private
887
     @ @since 1.1.0
888
     */
889
    function _bin2Ip($bin)
890
    {
891
        $ip = "";
892
893
        if (strlen($bin) < 128) {
894
895
            $bin = str_pad($str, 128, '0', STR_PAD_LEFT);
896
897
        }
898
899
        $parts = str_split($bin, "16");
900
901
        foreach($parts as $v) {
902
903
            $str = base_convert($v, 2, 16);
904
            $ip .= $str.":";
905
906
        }
907
908
        $ip = substr($ip, 0,-1);
909
910
        return $ip;
911
    }
912
913
    // }}}
914
}
915
// }}}
916
917
/*
918
 * Local variables:
919
 * tab-width: 4
920
 * c-basic-offset: 4
921
 * c-hanging-comment-ender-p: nil
922
 * End:
923
 */
924
925 7405845f Phil Davis
?>