Project

General

Profile

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