Project

General

Profile

Download (26.3 KB) Statistics
| Branch: | Tag: | Revision:
1 56c100ab Erik Fonnesbeck
<?php
2
3
/*
4 03ab9b30 Ermal
       pfSense_MODULE: utils
5 56c100ab Erik Fonnesbeck
*/
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 03ab9b30 Ermal
 * @license   BSD License http://www.opensource.org/licenses/bsd-license.php
26 bf416e49 Ermal
 * @version   CVS: $Id$
27 56c100ab Erik Fonnesbeck
 * @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 03ab9b30 Ermal
/**
100
 * Address Type: Address range to embedded IPv4 ip in an IPv6 address (RFC 4291, Section 2.5.5)
101
 * @see getAddressType()
102
 */
103
define("NET_IPV6_IPV4MAPPING", 51);
104
105
/**
106
 * Address Type: Unspecified (RFC 4291, Section 2.5.2)
107
 * @see getAddressType()
108
 */
109
define("NET_IPV6_UNSPECIFIED", 52);
110
111
/**
112
 * Address Type: Unspecified (RFC 4291, Section 2.5.3)
113
 * @see getAddressType()
114
 */
115
define("NET_IPV6_LOOPBACK", 53);
116
117 56c100ab Erik Fonnesbeck
/**
118
 * Address Type: address can not assigned to a specific type
119
 * @see getAddressType()
120
 */
121
define("NET_IPV6_UNKNOWN_TYPE", 1001);
122
123
// }}}
124
// {{{ Net_IPv6
125
126
/**
127
 * Class to validate and to work with IPv6 addresses.
128
 *
129
 * @category  Net
130
 * @package   Net_IPv6
131
 * @author    Alexander Merz <alexander.merz@web.de>
132
 * @author    <elfrink at introweb dot nl>
133
 * @author    Josh Peck <jmp at joshpeck dot org>
134 03ab9b30 Ermal
 * @copyright 2003-2010 The PHP Group
135
 * @license   BSD License http://www.opensource.org/licenses/bsd-license.php
136
 * @version   Release: 1.1.0RC5
137
 * @link      http://pear.php.net/package/Net_IPv6
138 56c100ab Erik Fonnesbeck
 */
139 03ab9b30 Ermal
class Net_IPv6
140
{
141 56c100ab Erik Fonnesbeck
142 03ab9b30 Ermal
    // {{{ separate()
143
    /**
144
     * Separates an IPv6 address into the address and a prefix length part
145
     *
146
     * @param String $ip the (compressed) IP as Hex representation
147
     *
148
     * @return Array the first element is the IP, the second the prefix length
149
     * @since  1.2.0
150
     * @access public
151
     * @static     
152
     */
153 b85d9c61 Ermal LUÇI
    static function separate($ip) 
154 03ab9b30 Ermal
    {
155
        
156
        $addr = $ip;
157
        $spec = '';
158
159
        if(false === strrpos($ip, '/')) {
160
161
            return array($addr, $spec);
162
163
        }
164
165
        $elements = explode('/', $ip);
166
167
        if(2 == count($elements)) {
168
169
            $addr = $elements[0];
170
            $spec = $elements[1];
171
172
        }
173
174
        return array($addr, $spec);
175
176
    }
177
    // }}}
178
179
    // {{{ removeNetmaskSpec()
180 56c100ab Erik Fonnesbeck
181
    /**
182 bf416e49 Ermal
     * Removes a possible existing prefix length/ netmask specification at an IP address.
183 56c100ab Erik Fonnesbeck
     *
184 03ab9b30 Ermal
     * @param String $ip the (compressed) IP as Hex representation
185 56c100ab Erik Fonnesbeck
     *
186
     * @return String the IP without netmask length
187
     * @since  1.1.0
188
     * @access public
189
     * @static
190
     */
191 b85d9c61 Ermal LUÇI
    static function removeNetmaskSpec($ip)
192 56c100ab Erik Fonnesbeck
    {
193
194 03ab9b30 Ermal
        $elements = Net_IPv6::separate($ip);
195 56c100ab Erik Fonnesbeck
196 03ab9b30 Ermal
        return $elements[0];
197 56c100ab Erik Fonnesbeck
198 03ab9b30 Ermal
    }
199
    // }}}
200
    // {{{ removePrefixLength()
201 56c100ab Erik Fonnesbeck
202 03ab9b30 Ermal
    /**
203
     * Tests for a prefix length specification in the address
204
     * and removes the prefix length, if exists
205
     *
206
     * The method is technically identical to removeNetmaskSpec() and 
207
     * will be dropped in a future release.
208
     *
209
     * @param String $ip a valid ipv6 address
210
     *
211
     * @return String the address without a prefix length
212
     * @access public
213
     * @static
214
     * @see removeNetmaskSpec()
215
     * @deprecated
216
     */
217 b85d9c61 Ermal LUÇI
    static function removePrefixLength($ip)
218 03ab9b30 Ermal
    {
219
        $pos = strrpos($ip, '/');
220 56c100ab Erik Fonnesbeck
221 03ab9b30 Ermal
        if (false !== $pos) {
222
223
            return substr($ip, 0, $pos);
224 56c100ab Erik Fonnesbeck
225
        }
226
227 03ab9b30 Ermal
        return $ip;
228 56c100ab Erik Fonnesbeck
    }
229
230 03ab9b30 Ermal
    // }}}
231
    // {{{ getNetmaskSpec()
232
233 56c100ab Erik Fonnesbeck
    /**
234 bf416e49 Ermal
     * Returns a possible existing prefix length/netmask specification on an IP address.
235 56c100ab Erik Fonnesbeck
     *
236 03ab9b30 Ermal
     * @param String $ip the (compressed) IP as Hex representation
237 56c100ab Erik Fonnesbeck
     *
238
     * @return String the netmask spec
239
     * @since  1.1.0
240
     * @access public
241
     * @static
242
     */
243 b85d9c61 Ermal LUÇI
    static function getNetmaskSpec($ip) 
244 56c100ab Erik Fonnesbeck
    {
245
246 03ab9b30 Ermal
        $elements = Net_IPv6::separate($ip);
247 56c100ab Erik Fonnesbeck
248 03ab9b30 Ermal
        return $elements[1];
249 56c100ab Erik Fonnesbeck
250 03ab9b30 Ermal
    }
251 56c100ab Erik Fonnesbeck
252 03ab9b30 Ermal
    // }}}
253
    // {{{ getPrefixLength()
254 56c100ab Erik Fonnesbeck
255 03ab9b30 Ermal
    /**
256
     * Tests for a prefix length specification in the address
257
     * and returns the prefix length, if exists
258
     *
259
     * The method is technically identical to getNetmaskSpec() and 
260
     * will be dropped in a future release.
261
     *
262
     * @param String $ip a valid ipv6 address
263
     *
264
     * @return Mixed the prefix as String or false, if no prefix was found
265
     * @access public
266
     * @static
267
     * @deprecated
268
     */
269 b85d9c61 Ermal LUÇI
    static function getPrefixLength($ip) 
270 03ab9b30 Ermal
    {
271
        if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", 
272
                        $ip, $matches)) {
273
274
            return $matches[2];
275
276
        } else {
277
278
            return false;
279 56c100ab Erik Fonnesbeck
280
        }
281
282
    }
283
284
    // }}}
285
    // {{{ getNetmask()
286
287
    /**
288
     * Calculates the network prefix based on the netmask bits.
289
     *
290 03ab9b30 Ermal
     * @param String $ip   the (compressed) IP in Hex format
291
     * @param int    $bits if the number of netmask bits is not part of the IP
292
     *                     you must provide the number of bits
293 56c100ab Erik Fonnesbeck
     *
294
     * @return String the network prefix
295
     * @since  1.1.0
296
     * @access public
297
     * @static
298
     */
299 b85d9c61 Ermal LUÇI
    static function getNetmask($ip, $bits = null)
300 56c100ab Erik Fonnesbeck
    {
301
        if (null==$bits) {
302
303
            $elements = explode('/', $ip);
304
305
            if (2 == count($elements)) {
306
307
                $addr = $elements[0];
308
                $bits = $elements[1];
309
310
            } else {
311
312 77148acc Ermal
                include_once 'PEAR.inc';
313 56c100ab Erik Fonnesbeck
314
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
315
                                        NET_IPV6_NO_NETMASK);
316
            }
317
318
        } else {
319
320
            $addr = $ip;
321
322
        }
323
324
        $addr       = Net_IPv6::uncompress($addr);
325
        $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits);
326
327
        return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask);
328
    }
329
330
    // }}}
331
    // {{{ isInNetmask()
332
333
    /**
334
     * Checks if an (compressed) IP is in a specific address space.
335
     *
336 bf416e49 Ermal
     * If the IP does not contain the number of netmask bits (F8000::FFFF/16)
337 56c100ab Erik Fonnesbeck
     * then you have to use the $bits parameter.
338
     *
339 03ab9b30 Ermal
     * @param String $ip      the IP to check (eg. F800::FFFF)
340
     * @param String $netmask the netmask (eg F800::)
341
     * @param int    $bits    the number of netmask bits to compare,
342
     *                        if not given in $ip
343 56c100ab Erik Fonnesbeck
     *
344
     * @return boolean true if $ip is in the netmask
345
     * @since  1.1.0
346
     * @access public
347
     * @static
348
     */
349 b85d9c61 Ermal LUÇI
    static function isInNetmask($ip, $netmask, $bits=null)
350 56c100ab Erik Fonnesbeck
    {
351
        // try to get the bit count
352
353
        if (null == $bits) {
354
355
            $elements = explode('/', $ip);
356
357
            if (2 == count($elements)) {
358
359
                $ip   = $elements[0];
360
                $bits = $elements[1];
361
362
            } else if (null == $bits) {
363
364
                $elements = explode('/', $netmask);
365
366
                if (2 == count($elements)) {
367
368
                     $netmask = $elements[0];
369 03ab9b30 Ermal
                     $bits    = $elements[1];
370 56c100ab Erik Fonnesbeck
371
                }
372
373
                if (null == $bits) {
374
375 77148acc Ermal
                    include_once 'PEAR.inc';
376 56c100ab Erik Fonnesbeck
                    return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
377
                                            NET_IPV6_NO_NETMASK);
378
379
                }
380
381
            }
382
383
        }
384
385
        $binIp      = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip));
386
        $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask));
387
388 03ab9b30 Ermal
        if (null != $bits
389
            && "" != $bits
390
            && 0 == strncmp($binNetmask, $binIp, $bits)) {
391 56c100ab Erik Fonnesbeck
392
            return true;
393
394
        }
395
396
        return false;
397
    }
398
399
    // }}}
400
    // {{{ getAddressType()
401
402
    /**
403
     * Returns the type of an IPv6 address.
404
     *
405 03ab9b30 Ermal
     * RFC 2373, Section 2.3 describes several types of addresses in
406 bf416e49 Ermal
     * the IPv6 address space.
407
     * Several address types are markers for reserved spaces and as
408
     * a consequence are subject to change.
409 56c100ab Erik Fonnesbeck
     *
410 03ab9b30 Ermal
     * @param String $ip the IP address in Hex format,
411 56c100ab Erik Fonnesbeck
     *                    compressed IPs are allowed
412
     *
413 bf416e49 Ermal
     * @return int one of the address type constants
414 56c100ab Erik Fonnesbeck
     * @access public
415
     * @since  1.1.0
416
     * @static
417
     *
418
     * @see    NET_IPV6_UNASSIGNED
419
     * @see    NET_IPV6_RESERVED
420
     * @see    NET_IPV6_RESERVED_NSAP
421
     * @see    NET_IPV6_RESERVED_IPX
422
     * @see    NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC
423
     * @see    NET_IPV6_UNICAST_PROVIDER
424
     * @see    NET_IPV6_MULTICAST
425
     * @see    NET_IPV6_LOCAL_LINK
426
     * @see    NET_IPV6_LOCAL_SITE
427 03ab9b30 Ermal
     * @see    NET_IPV6_IPV4MAPPING  
428
     * @see    NET_IPV6_UNSPECIFIED  
429
     * @see    NET_IPV6_LOOPBACK  
430 56c100ab Erik Fonnesbeck
     * @see    NET_IPV6_UNKNOWN_TYPE
431
     */
432 b85d9c61 Ermal LUÇI
    static function getAddressType($ip) 
433 56c100ab Erik Fonnesbeck
    {
434
        $ip    = Net_IPv6::removeNetmaskSpec($ip);
435
        $binip = Net_IPv6::_ip2Bin($ip);
436
437 03ab9b30 Ermal
        if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128
438
439
            return NET_IPV6_UNSPECIFIED;
440
441
        } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128
442
443
            return NET_IPV6_LOOPBACK;
444
445
        } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96
446
447
            return NET_IPV6_IPV4MAPPING; 
448
449
        } else if (0 == strncmp('1111111010', $binip, 10)) {
450 56c100ab Erik Fonnesbeck
451
            return NET_IPV6_LOCAL_LINK;
452
453
        } else if (0 == strncmp('1111111011', $binip, 10)) {
454
455
            return NET_IPV6_LOCAL_SITE;
456
457
        } else if (0 == strncmp('111111100', $binip, 9)) {
458
459
            return NET_IPV6_UNASSIGNED;
460
461
        } else if (0 == strncmp('11111111', $binip, 8)) {
462
463
            return NET_IPV6_MULTICAST;
464
465 03ab9b30 Ermal
        } else if (0 == strncmp('00000000', $binip, 8)) { 
466 56c100ab Erik Fonnesbeck
467
            return NET_IPV6_RESERVED;
468
469 03ab9b30 Ermal
        } else if (0 == strncmp('00000001', $binip, 8)
470 56c100ab Erik Fonnesbeck
                    || 0 == strncmp('1111110', $binip, 7)) {
471
472
            return NET_IPV6_UNASSIGNED;
473
474
        } else if (0 == strncmp('0000001', $binip, 7)) {
475
476
            return NET_IPV6_RESERVED_NSAP;
477
478
        } else if (0 == strncmp('0000010', $binip, 7)) {
479
480 91f026b0 ayvis
            return NET_IPV6_RESERVED_IPX;
481 56c100ab Erik Fonnesbeck
482
        } else if (0 == strncmp('0000011', $binip, 7) ||
483
                    0 == strncmp('111110', $binip, 6) ||
484
                    0 == strncmp('11110', $binip, 5) ||
485
                    0 == strncmp('00001', $binip, 5) ||
486
                    0 == strncmp('1110', $binip, 4) ||
487
                    0 == strncmp('0001', $binip, 4) ||
488
                    0 == strncmp('001', $binip, 3) ||
489
                    0 == strncmp('011', $binip, 3) ||
490
                    0 == strncmp('101', $binip, 3) ||
491
                    0 == strncmp('110', $binip, 3)) {
492
493
            return NET_IPV6_UNASSIGNED;
494
495
        } else if (0 == strncmp('010', $binip, 3)) {
496
497
            return NET_IPV6_UNICAST_PROVIDER;
498
499 03ab9b30 Ermal
        } else if (0 == strncmp('100', $binip, 3)) {
500 56c100ab Erik Fonnesbeck
501
            return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC;
502
503
        }
504
505
        return NET_IPV6_UNKNOWN_TYPE;
506
    }
507
508
    // }}}
509
    // {{{ Uncompress()
510
511
    /**
512 bf416e49 Ermal
     * Uncompresses an IPv6 address
513 56c100ab Erik Fonnesbeck
     *
514 bf416e49 Ermal
     * RFC 2373 allows you to compress zeros in an address to '::'. This
515
     * function expects a valid IPv6 address and expands the '::' to
516 56c100ab Erik Fonnesbeck
     * the required zeros.
517
     *
518
     * Example:  FF01::101  ->  FF01:0:0:0:0:0:0:101
519
     *           ::1        ->  0:0:0:0:0:0:0:1
520
     *
521 bf416e49 Ermal
     * Note: You can also pass an invalid IPv6 address (usually as part of the process
522
     * of validation by checkIPv6, which will validate the return string).
523
     * This function will insert the "0" between "::" even if this results in too many
524
     * numbers in total. It is NOT the purpose of this function to validate your input.
525
     *
526
     * Example of calling with invalid input: 1::2:3:4:5:6:7:8:9 -> 1:0:2:3:4:5:6:7:8:9
527
     *
528
     * @param String $ip a (possibly) valid IPv6-address (hex format)
529 03ab9b30 Ermal
     * @param Boolean $leadingZeros if true, leading zeros are added to each 
530
     *                              block of the address 
531
     *                              (FF01::101  ->  
532
     *                               FF01:0000:0000:0000:0000:0000:0000:0101) 
533
     *
534 bf416e49 Ermal
     * @return String the uncompressed IPv6-address (hex format)
535 56c100ab Erik Fonnesbeck
     * @access public
536
     * @see Compress()
537
     * @static
538 03ab9b30 Ermal
     * @author Pascal Uhlmann
539 56c100ab Erik Fonnesbeck
     */
540 b85d9c61 Ermal LUÇI
    static function uncompress($ip, $leadingZeros = false)
541 56c100ab Erik Fonnesbeck
    {
542
543
        $prefix = Net_IPv6::getPrefixLength($ip);
544
545
        if (false === $prefix) {
546
547
            $prefix = '';
548
549
        } else {
550
551
            $ip     = Net_IPv6::removePrefixLength($ip);
552
            $prefix = '/'.$prefix;
553
554
        }
555
556
        $netmask = Net_IPv6::getNetmaskSpec($ip);
557
        $uip     = Net_IPv6::removeNetmaskSpec($ip);
558
559
        $c1 = -1;
560
        $c2 = -1;
561
562
        if (false !== strpos($uip, '::') ) {
563
564
            list($ip1, $ip2) = explode('::', $uip);
565
566 03ab9b30 Ermal
            if ("" == $ip1) {
567 56c100ab Erik Fonnesbeck
568
                $c1 = -1;
569
570
            } else {
571
572
                $pos = 0;
573
574
                if (0 < ($pos = substr_count($ip1, ':'))) {
575
576
                    $c1 = $pos;
577
578
                } else {
579
580
                    $c1 = 0;
581
582
                }
583
            }
584
            if ("" == $ip2) {
585
586
                $c2 = -1;
587
588
            } else {
589
590
                $pos = 0;
591
592
                if (0 < ($pos = substr_count($ip2, ':'))) {
593
594
                    $c2 = $pos;
595
596
                } else {
597
598
                    $c2 = 0;
599
600
                }
601
602
            }
603
604
            if (strstr($ip2, '.')) {
605
606
                $c2++;
607
608
            }
609
            if (-1 == $c1 && -1 == $c2) { // ::
610
611
                $uip = "0:0:0:0:0:0:0:0";
612
613
            } else if (-1 == $c1) {              // ::xxx
614
615
                $fill = str_repeat('0:', 7-$c2);
616
                $uip  = str_replace('::', $fill, $uip);
617
618
            } else if (-1 == $c2) {              // xxx::
619
620
                $fill = str_repeat(':0', 7-$c1);
621
                $uip  = str_replace('::', $fill, $uip);
622
623
            } else {                          // xxx::xxx
624
625 bf416e49 Ermal
                $fill = str_repeat(':0:', max(1, 6-$c2-$c1));
626 56c100ab Erik Fonnesbeck
                $uip  = str_replace('::', $fill, $uip);
627
                $uip  = str_replace('::', ':', $uip);
628
629
            }
630
        }
631 03ab9b30 Ermal
632
        if(true == $leadingZeros) {
633
            
634
            $uipT    = array();
635
            $uiparts = explode(':', $uip);
636
637
            foreach($uiparts as $p) {
638
639
                $uipT[] = sprintf('%04s', $p);
640
            
641
            }
642
643
            $uip = implode(':', $uipT);
644
        }
645
646 56c100ab Erik Fonnesbeck
        if ('' != $netmask) {
647
648
                $uip = $uip.'/'.$netmask;
649
650
        }
651
652
        return $uip.$prefix;
653
    }
654
655
    // }}}
656
    // {{{ Compress()
657
658
    /**
659 bf416e49 Ermal
     * Compresses an IPv6 address
660 56c100ab Erik Fonnesbeck
     *
661 bf416e49 Ermal
     * RFC 2373 allows you to compress zeros in an address to '::'. This
662
     * function expects a valid IPv6 address and compresses successive zeros
663 56c100ab Erik Fonnesbeck
     * to '::'
664
     *
665
     * Example:  FF01:0:0:0:0:0:0:101   -> FF01::101
666
     *           0:0:0:0:0:0:0:1        -> ::1
667
     *
668 bf416e49 Ermal
     * When $ip is an already compressed address and $force is false, the method returns 
669
     * the value as is, even if the address can be compressed further.
670 03ab9b30 Ermal
     *
671
     * Example: FF01::0:1 -> FF01::0:1
672
     *
673
     * To enforce maximum compression, you can set the second argument $force to true.
674
     *
675
     * Example: FF01::0:1 -> FF01::1 
676
     *
677 bf416e49 Ermal
     * @param String  $ip    a valid IPv6-address (hex format)
678
     * @param boolean $force if true the address will be compressed as best as possible (since 1.2.0)
679 03ab9b30 Ermal
     *
680 bf416e49 Ermal
     * @return String the compressed IPv6-address (hex format)
681 56c100ab Erik Fonnesbeck
     * @access public
682 03ab9b30 Ermal
     * @see    Uncompress()
683 56c100ab Erik Fonnesbeck
     * @static
684
     * @author elfrink at introweb dot nl
685
     */
686 b85d9c61 Ermal LUÇI
    static function compress($ip, $force = false)  
687 56c100ab Erik Fonnesbeck
    {
688 03ab9b30 Ermal
        
689
        if(false !== strpos($ip, '::')) { // its already compressed
690
691
            if(true == $force) {
692
693
                $ip = Net_IPv6::uncompress($ip); 
694
695
            } else {
696
697
                return $ip;
698
699
            }
700
701
        }
702
703 56c100ab Erik Fonnesbeck
        $prefix = Net_IPv6::getPrefixLength($ip);
704
705
        if (false === $prefix) {
706
707
            $prefix = '';
708
709
        } else {
710
711
            $ip     = Net_IPv6::removePrefixLength($ip);
712
            $prefix = '/'.$prefix;
713
714
        }
715
716
        $netmask = Net_IPv6::getNetmaskSpec($ip);
717
        $ip      = Net_IPv6::removeNetmaskSpec($ip);
718
719 03ab9b30 Ermal
        $ipp = explode(':', $ip);
720 56c100ab Erik Fonnesbeck
721 03ab9b30 Ermal
        for ($i = 0; $i < count($ipp); $i++) {
722 56c100ab Erik Fonnesbeck
723 03ab9b30 Ermal
            $ipp[$i] = dechex(hexdec($ipp[$i]));
724 56c100ab Erik Fonnesbeck
725 03ab9b30 Ermal
        }
726 56c100ab Erik Fonnesbeck
727 03ab9b30 Ermal
        $cip = ':' . join(':', $ipp) . ':';
728 56c100ab Erik Fonnesbeck
729 bf416e49 Ermal
        preg_match_all("/(:0)(:0)+/", $cip, $zeros);
730 56c100ab Erik Fonnesbeck
731 03ab9b30 Ermal
        if (count($zeros[0]) > 0) {
732 56c100ab Erik Fonnesbeck
733 03ab9b30 Ermal
            $match = '';
734 56c100ab Erik Fonnesbeck
735 03ab9b30 Ermal
            foreach ($zeros[0] as $zero) {
736 56c100ab Erik Fonnesbeck
737 03ab9b30 Ermal
                if (strlen($zero) > strlen($match)) {
738 56c100ab Erik Fonnesbeck
739 03ab9b30 Ermal
                    $match = $zero;
740 56c100ab Erik Fonnesbeck
741
                }
742 03ab9b30 Ermal
            }
743 56c100ab Erik Fonnesbeck
744 03ab9b30 Ermal
            $cip = preg_replace('/' . $match . '/', ':', $cip, 1);
745 56c100ab Erik Fonnesbeck
746 03ab9b30 Ermal
        }
747 56c100ab Erik Fonnesbeck
748 03ab9b30 Ermal
        $cip = preg_replace('/((^:)|(:$))/', '', $cip);
749
        $cip = preg_replace('/((^:)|(:$))/', '::', $cip);
750 56c100ab Erik Fonnesbeck
751 ce0e5d38 Daniel Becker
        if (empty($cip)) {
752
753
            $cip = "::";
754
755
        }
756
757 03ab9b30 Ermal
        if ('' != $netmask) {
758 56c100ab Erik Fonnesbeck
759 03ab9b30 Ermal
            $cip = $cip.'/'.$netmask;
760 56c100ab Erik Fonnesbeck
761 03ab9b30 Ermal
        }
762
763
        return $cip.$prefix;
764 56c100ab Erik Fonnesbeck
765
    }
766
767 03ab9b30 Ermal
    // }}}
768 bf416e49 Ermal
    // {{{ recommendedFormat()
769
    /**
770
     * Represent IPv6 address in RFC5952 format.
771
     *
772
     * @param String  $ip a valid IPv6-address (hex format)
773
     *
774
     * @return String the recommended representation of IPv6-address (hex format)
775
     * @access public
776
     * @see    compress()
777
     * @static
778
     * @author koyama at hoge dot org
779
     * @todo This method may become a part of compress() in a further releases
780
     */
781 b85d9c61 Ermal LUÇI
    static function recommendedFormat($ip)
782 bf416e49 Ermal
    {
783
        $compressed = self::compress($ip, true);
784
        // RFC5952 4.2.2
785
        // The symbol "::" MUST NOT be used to shorten just one
786
        // 16-bit 0 field.
787
        if ((substr_count($compressed, ':') == 7) &&
788
            (strpos($compressed, '::') !== false)) {
789
            $compressed = str_replace('::', ':0:', $compressed);
790
        }
791
        return $compressed;
792
    }
793
    // }}}
794
795 03ab9b30 Ermal
    // {{{ isCompressible()
796
797
    /**
798 bf416e49 Ermal
     * Checks, if an IPv6 address can be compressed
799 03ab9b30 Ermal
     *
800 bf416e49 Ermal
     * @param String $ip a valid IPv6 address
801 03ab9b30 Ermal
     * 
802 bf416e49 Ermal
     * @return Boolean true, if address can be compressed
803 03ab9b30 Ermal
     * 
804
     * @access public
805
     * @since 1.2.0b
806
     * @static
807
     * @author Manuel Schmitt
808
     */
809 b85d9c61 Ermal LUÇI
    static function isCompressible($ip) 
810 03ab9b30 Ermal
    {
811
812
        return (bool)($ip != Net_IPv6::compress($address));
813
814
    }    
815
816 56c100ab Erik Fonnesbeck
    // }}}
817
    // {{{ SplitV64()
818
819
    /**
820 bf416e49 Ermal
     * Splits an IPv6 address into the IPv6 and a possible IPv4 part
821 56c100ab Erik Fonnesbeck
     *
822 bf416e49 Ermal
     * RFC 2373 allows you to note the last two parts of an IPv6 address as
823
     * an IPv4 compatible address
824 56c100ab Erik Fonnesbeck
     *
825
     * Example:  0:0:0:0:0:0:13.1.68.3
826
     *           0:0:0:0:0:FFFF:129.144.52.38
827
     *
828 bf416e49 Ermal
     * @param String  $ip         a valid IPv6-address (hex format)
829 03ab9b30 Ermal
     * @param Boolean $uncompress if true, the address will be uncompressed 
830
     *                            before processing
831 56c100ab Erik Fonnesbeck
     *
832 03ab9b30 Ermal
     * @return Array  [0] contains the IPv6 part,
833 56c100ab Erik Fonnesbeck
     *                [1] the IPv4 part (hex format)
834
     * @access public
835
     * @static
836
     */
837 b85d9c61 Ermal LUÇI
    static function SplitV64($ip, $uncompress = true)
838 56c100ab Erik Fonnesbeck
    {
839
        $ip = Net_IPv6::removeNetmaskSpec($ip);
840
841
        if ($uncompress) {
842
843
            $ip = Net_IPv6::Uncompress($ip);
844
845
        }
846
847
        if (strstr($ip, '.')) {
848
849
            $pos      = strrpos($ip, ':');
850
            $ip{$pos} = '_';
851
            $ipPart   = explode('_', $ip);
852
853
            return $ipPart;
854
855
        } else {
856
857
            return array($ip, "");
858
859
        }
860
    }
861
862
    // }}}
863
    // {{{ checkIPv6()
864
865
    /**
866 bf416e49 Ermal
     * Checks an IPv6 address
867 56c100ab Erik Fonnesbeck
     *
868
     * Checks if the given IP is IPv6-compatible
869
     *
870 bf416e49 Ermal
     * @param String $ip a valid IPv6-address
871 03ab9b30 Ermal
     *
872 bf416e49 Ermal
     * @return Boolean true if $ip is an IPv6 address
873 56c100ab Erik Fonnesbeck
     * @access public
874
     * @static
875
     */
876 b85d9c61 Ermal LUÇI
    static function checkIPv6($ip)
877 56c100ab Erik Fonnesbeck
    {
878 bf416e49 Ermal
879
        $elements = Net_IPv6::separate($ip);
880
    
881
        $ip = $elements[0];
882
883 8b731067 Ermal
        if('' != $elements[1] && ( !is_numeric($elements[1]) || 0 > $elements[1] || 128 < $elements[1])) {
884 bf416e49 Ermal
885
            return false;
886
887
        } 
888 56c100ab Erik Fonnesbeck
889
        $ipPart = Net_IPv6::SplitV64($ip);
890
        $count  = 0;
891
892 03ab9b30 Ermal
        if (!empty($ipPart[0])) {
893 bf416e49 Ermal
            $ipv6 = explode(':', $ipPart[0]);
894
895
            foreach($ipv6 as $element) { // made a validate precheck
896
                if(!preg_match('/[0-9a-fA-F]*/', $element)) {
897
                    return false;
898
                }
899
            }
900 56c100ab Erik Fonnesbeck
901
            for ($i = 0; $i < count($ipv6); $i++) {
902
903 03ab9b30 Ermal
                if(4 < strlen($ipv6[$i])) {
904
                    
905
                    return false;
906
907
                }
908
909 56c100ab Erik Fonnesbeck
                $dec = hexdec($ipv6[$i]);
910
                $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/",
911
                                                "\\1", 
912
                                                $ipv6[$i]));
913
914
                if ($ipv6[$i] >= 0 && $dec <= 65535
915
                    && $hex == strtoupper(dechex($dec))) {
916
917
                    $count++;
918
919
                }
920
921
            }
922
923
            if (8 == $count) {
924
925
                return true;
926
927
            } else if (6 == $count and !empty($ipPart[1])) {
928
929 03ab9b30 Ermal
                $ipv4  = explode('.', $ipPart[1]);
930 56c100ab Erik Fonnesbeck
                $count = 0;
931
932
                for ($i = 0; $i < count($ipv4); $i++) {
933
934
                    if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255
935
                        && preg_match("/^\d{1,3}$/", $ipv4[$i])) {
936
937
                        $count++;
938
939
                    }
940
941
                }
942
943
                if (4 == $count) {
944
945
                    return true;
946
947
                }
948
949
            } else {
950
951
                return false;
952
953
            }
954
955
        } else {
956
957
            return false;
958
959
        }
960
961
    }
962
963
    // }}}
964
965
    // {{{ _parseAddress()
966
967
    /**
968
     * Returns the lowest and highest IPv6 address
969
     * for a given IP and netmask specification
970
     * 
971
     * The netmask may be a part of the $ip or 
972 bf416e49 Ermal
     * the number of netmask bits is provided via $bits
973 56c100ab Erik Fonnesbeck
     *
974
     * The result is an indexed array. The key 'start'
975 bf416e49 Ermal
     * contains the lowest possible IP address. The key
976 56c100ab Erik Fonnesbeck
     * 'end' the highest address.
977
     *
978 03ab9b30 Ermal
     * @param String $ipToParse the IPv6 address
979
     * @param String $bits      the optional count of netmask bits
980 56c100ab Erik Fonnesbeck
     *
981
     * @return Array ['start', 'end'] the lowest and highest IPv6 address
982
     * @access public
983
     * @static
984
     * @author Nicholas Williams
985
     */
986
987 b85d9c61 Ermal LUÇI
    static function parseAddress($ipToParse, $bits = null)
988 56c100ab Erik Fonnesbeck
    {
989
990
        $ip      = null;
991
        $bitmask = null;
992
993 03ab9b30 Ermal
        if ( null == $bits ) {  
994 56c100ab Erik Fonnesbeck
995 03ab9b30 Ermal
            $elements = explode('/', $ipToParse);
996 56c100ab Erik Fonnesbeck
997 03ab9b30 Ermal
            if ( 2 == count($elements) ) {
998 56c100ab Erik Fonnesbeck
999 03ab9b30 Ermal
                $ip      = Net_IPv6::uncompress($elements[0]);
1000
                $bitmask = $elements[1];
1001 56c100ab Erik Fonnesbeck
1002 03ab9b30 Ermal
            } else {
1003 56c100ab Erik Fonnesbeck
1004 77148acc Ermal
                include_once 'PEAR.inc';
1005 56c100ab Erik Fonnesbeck
1006 03ab9b30 Ermal
                return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG,
1007 56c100ab Erik Fonnesbeck
                                        NET_IPV6_NO_NETMASK);
1008 03ab9b30 Ermal
            }
1009
        } else {
1010 56c100ab Erik Fonnesbeck
1011 03ab9b30 Ermal
            $ip      = Net_IPv6::uncompress($ipToParse);
1012
            $bitmask = $bits;
1013 56c100ab Erik Fonnesbeck
1014 03ab9b30 Ermal
        }
1015 56c100ab Erik Fonnesbeck
1016 03ab9b30 Ermal
        $binNetmask = str_repeat('1', $bitmask).
1017 56c100ab Erik Fonnesbeck
                      str_repeat('0', 128 - $bitmask);
1018 03ab9b30 Ermal
        $maxNetmask = str_repeat('1', 128);
1019
        $netmask    = Net_IPv6::_bin2Ip($binNetmask);
1020 56c100ab Erik Fonnesbeck
1021 03ab9b30 Ermal
        $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
1022
                                          & $binNetmask);
1023
        $endAddress   = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip)
1024
                                          | ($binNetmask ^ $maxNetmask));
1025 56c100ab Erik Fonnesbeck
1026 03ab9b30 Ermal
        return array('start' => $startAddress, 'end' => $endAddress);
1027 56c100ab Erik Fonnesbeck
    }
1028
1029
    // }}}
1030
1031
    // {{{ _ip2Bin()
1032
1033
    /**
1034
     * Converts an IPv6 address from Hex into Binary representation.
1035
     *
1036
     * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), 
1037
     *                   compressed IPs are allowed
1038
     *
1039
     * @return String the binary representation
1040
     * @access private
1041
     @ @since 1.1.0
1042
     */
1043 b85d9c61 Ermal LUÇI
    static function _ip2Bin($ip) 
1044 56c100ab Erik Fonnesbeck
    {
1045
        $binstr = '';
1046
1047
        $ip = Net_IPv6::removeNetmaskSpec($ip);
1048
        $ip = Net_IPv6::Uncompress($ip);
1049
1050
        $parts = explode(':', $ip);
1051
1052 03ab9b30 Ermal
        foreach ( $parts as $v ) {
1053 56c100ab Erik Fonnesbeck
1054
            $str     = base_convert($v, 16, 2);
1055
            $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT);
1056
1057
        }
1058
1059
        return $binstr;
1060
    }
1061
1062
    // }}}
1063
    // {{{ _bin2Ip()
1064
1065
    /**
1066
     * Converts an IPv6 address from Binary into Hex representation.
1067
     *
1068 03ab9b30 Ermal
     * @param String $bin the IP address as binary
1069 56c100ab Erik Fonnesbeck
     *
1070
     * @return String the uncompressed Hex representation
1071
     * @access private
1072
     @ @since 1.1.0
1073
     */
1074 b85d9c61 Ermal LUÇI
    static function _bin2Ip($bin)
1075 56c100ab Erik Fonnesbeck
    {
1076
        $ip = "";
1077
1078
        if (strlen($bin) < 128) {
1079
1080 03ab9b30 Ermal
            $bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
1081 56c100ab Erik Fonnesbeck
1082
        }
1083
1084
        $parts = str_split($bin, "16");
1085
1086 03ab9b30 Ermal
        foreach ( $parts as $v ) {
1087 56c100ab Erik Fonnesbeck
1088
            $str = base_convert($v, 2, 16);
1089
            $ip .= $str.":";
1090
1091
        }
1092
1093 03ab9b30 Ermal
        $ip = substr($ip, 0, -1);
1094 56c100ab Erik Fonnesbeck
1095
        return $ip;
1096
    }
1097
1098
    // }}}
1099
}
1100
// }}}
1101
1102
/*
1103
 * Local variables:
1104
 * tab-width: 4
1105
 * c-basic-offset: 4
1106
 * c-hanging-comment-ender-p: nil
1107
 * End:
1108
 */
1109
1110 7405845f Phil Davis
?>