228 lines
8.0 KiB
PHP
228 lines
8.0 KiB
PHP
|
||
<?php
|
||
/* PHP-IPv6 V1.0.
|
||
* Copyright (c) 2010 Mr.Bin <bin_jly@163.com>
|
||
*/
|
||
class ipv6 {
|
||
function addr($addr=null) {
|
||
// 常规获取IPv6地址或格式化IP地址为IPv6格式
|
||
!$addr && ($addr = $_SERVER['REMOTE_ADDR']);
|
||
$type = self::type($addr);
|
||
if ( $type === 6 && self::ipv6_check($addr) ) return $addr;
|
||
elseif ( $type === 4 ) return self::ip426($addr);
|
||
else return 'Unknown';
|
||
}
|
||
|
||
function realip() {
|
||
/* 穿过代理获取真实IP地址
|
||
* 返回值为数组,array[0]为真实IP,array[1]为代理IP(可能为空)
|
||
* 若array[0]和array[1]相等,则实际真实IP可能无法获取(高度匿名?)
|
||
*/
|
||
$is_proxy = false;
|
||
if ( $_SERVER['HTTP_X_FORWARDED_FOR'] ) {
|
||
$is_proxy = true;
|
||
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||
foreach($ips as $ip){
|
||
if ( !self::wan_ip($ip) ) $ip = false;
|
||
else break;
|
||
}
|
||
}
|
||
if ( !$ip && $_SERVER['HTTP_CLIENT_IP'] ) {
|
||
$is_proxy = true;
|
||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||
if ( !self::wan_ip($ip) ) $ip = false;
|
||
}
|
||
if ( $is_proxy ) {
|
||
$proxy = $_SERVER['REMOTE_ADDR'];
|
||
if (!$ip) $ip = $proxy;
|
||
} else {
|
||
$ip = $_SERVER['REMOTE_ADDR'];
|
||
}
|
||
return explode(',', $ip.','.$proxy);
|
||
}
|
||
|
||
function cut($addr) {
|
||
// 压缩IPv6地址
|
||
if (!self::ipv6_check($addr)) return $addr;
|
||
$addr = self::fill($addr);
|
||
$arr = explode(':',$addr);
|
||
foreach ($arr as $a) {
|
||
$arr2[] = preg_replace('/^0{1,3}(\w+)/','\1',$a);
|
||
}
|
||
$addr = join(':',$arr2);
|
||
$olen = strlen($addr);
|
||
for($i=6;$i>0;$i--){
|
||
// 初步压缩
|
||
$addr = preg_replace('/:(0\:){'.$i.'}/','::',$addr,1);
|
||
if (strlen($addr) < $olen ) break;
|
||
}
|
||
$addr = preg_replace('/^0\:\:/','::',$addr);
|
||
$addr = preg_replace('/\:\:0$/','::',$addr);
|
||
return $addr;
|
||
}
|
||
|
||
function fill($addr) {
|
||
// 标准IPv6格式
|
||
if (!self::ipv6_check($addr)) return $addr;
|
||
$addr = self::_fix_v4($addr);
|
||
$arr = explode(':',$addr);
|
||
foreach ($arr as $a) {
|
||
$l = strlen($a);
|
||
if ( $l > 0 && $l < 4 )
|
||
$arr2[] = str_repeat('0', 4-$l).$a;
|
||
else $arr2[] = $a;
|
||
}
|
||
$addr = join(':',$arr2);
|
||
$fil = ':'.str_repeat('0000:', 9-count($arr));
|
||
$addr = str_replace('::',$fil,$addr);
|
||
$addr = preg_replace('/^\:/','0000:',$addr);
|
||
$addr = preg_replace('/\:$/',':0000',$addr);
|
||
return $addr;
|
||
}
|
||
|
||
function ip2bin($addr) {
|
||
$type = self::type($addr);
|
||
if ( $type === 0 ) return false;
|
||
elseif ( $type === 4 ) $addr = self::ip426($addr);
|
||
else $addr = self::fill($addr);
|
||
$hexstr = str_replace(':','',$addr);
|
||
return pack('H*', $hexstr);
|
||
}
|
||
|
||
function bin2ip($bin) {
|
||
if ( strlen($bin) !== 16 ) return false;
|
||
$arr = str_split(join('',unpack('H*', $bin)), 4);
|
||
$addr = join(':',$arr);
|
||
return $addr;
|
||
}
|
||
|
||
function ip426($addr) {
|
||
// IPv4 to IPv6
|
||
if (!self::ipv4_check($addr)) return $addr;
|
||
$hex = dechex(self::ip2long($addr));
|
||
$hex = str_repeat('0', 8-strlen($hex)).$hex;
|
||
$ipv6 = '0000:0000:0000:0000:0000:0000:';
|
||
$ipv6 .= substr($hex,0,4) . ':' . substr($hex,4,4);
|
||
return $ipv6;
|
||
}
|
||
|
||
function type($addr) {
|
||
if ( self::ipv6_check($addr) ) return 6;
|
||
elseif ( self::ipv4_check($addr) ) return 4;
|
||
else return 0;
|
||
}
|
||
|
||
function ipv4_check($addr) {
|
||
$arr = explode('.', $addr);
|
||
$l = count($arr);
|
||
for ( $i=0;$i<$l;$i++ ) {
|
||
if ( strlen($arr[$i]) > 3 ) return false;
|
||
if ( !is_numeric($arr[$i]) ) return false;
|
||
$a = intval($arr[$i], 10);
|
||
if ($a > 255 || $a <0) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function ipv6_check($addr) {
|
||
$addr = self::_fix_v4($addr);
|
||
if ( strpos($addr, '.') ) return false;
|
||
$l1 = count(explode('::',$addr));
|
||
if ( $l1 > 2 ) return false;
|
||
$l2 = count(explode(':',$addr));
|
||
if ( $l2 < 3 || $l2 > 8 ) return false;
|
||
if ( $l2 < 8 && $l1 !== 2 ) return false;
|
||
preg_match('/^([0-9a-f]{0,4}\:)+[0-9a-f]{0,4}$/i',$addr,$arr);
|
||
if ( !$arr[0] ) return false;
|
||
return true;
|
||
}
|
||
|
||
function ip2long($addr) {
|
||
$arr = explode('.', $addr);
|
||
$l = count($arr);
|
||
$long = 0;
|
||
for ( $i=0;$i<$l;$i++ ) {
|
||
if ( strlen($arr[$i]) > 3 ) return false;
|
||
if ( !is_numeric($arr[$i]) ) return false;
|
||
$a = intval($arr[$i], 10);
|
||
if ($a > 255 || $a <0) return false;
|
||
$long += $a * pow(2, 24-$i*8);
|
||
}
|
||
return $long;
|
||
}
|
||
|
||
function wan_ip($addr) {
|
||
// 检查外网可用地址
|
||
if ( self::ipv6_check($addr) ) {
|
||
$addr = self::fill($addr);
|
||
// IPv4类地址处理
|
||
$v4p = substr($addr,0,29);
|
||
if ( $v4p == '0000:0000:0000:0000:0000:0000'
|
||
|| strtolower($v4p) == 'ffff:0000:0000:0000:0000:0000' ) {
|
||
$t = str_replace($v4p,'',$addr);
|
||
$t = str_replace(':','',$t);
|
||
$ipv4 = long2ip(hexdec($t));
|
||
return self::_wan_ipv4($ipv4);
|
||
}
|
||
// 取前16位进行比较
|
||
$v6p = substr($addr,0,4);
|
||
$bin = decbin(hexdec($v6p));
|
||
$p = str_repeat(0, 16-strlen($bin)).$bin;
|
||
if ( (($p&'1110000000000000')=='0010000000000000') //2000::/3
|
||
|| (($p&'1111111000000000')=='1111110000000000') //FC00::/7
|
||
|| (($p&'1111111111000000')=='1111111010000000') //FE80::/10
|
||
|| (($p&'1111111100000000')=='1111111100000000') //FF00::/8
|
||
) return false;
|
||
return true;
|
||
} else {
|
||
return self::_wan_ipv4($addr);
|
||
}
|
||
}
|
||
|
||
private function _wan_ipv4($addr){
|
||
if ( !self::ipv4_check($addr) ) return false;
|
||
$arr = explode('.',$addr);
|
||
$bin = decbin($arr[0]*256+$arr[1]);
|
||
$p = str_repeat(0, 16-strlen($bin)).$bin;
|
||
$p8 = $p & '1111111100000000';
|
||
$p16 = &$p;
|
||
if ( ($p8 == '0000000000000000') // 0/8
|
||
|| ($p8 == '0000010100000000') // 5/8
|
||
|| ($p8 == '0000101000000000') // 10/8
|
||
|| ($p8 == '0001011100000000') // 23/8
|
||
|| ($p8 == '0010010000000000') // 36/8
|
||
|| ($p8 == '0010010100000000') // 37/8
|
||
|| ($p8 == '0010011100000000') // 39/8
|
||
|| ($p8 == '0010101000000000') // 42/8
|
||
|| ($p8 == '0110010000000000') // 100/8
|
||
|| ($p8 == '0110011000000000') // 102/8
|
||
|| ($p8 == '0110011100000000') // 103/8
|
||
|| ($p8 == '0110100000000000') // 104/8
|
||
|| ($p8 == '0110100100000000') // 105/8
|
||
|| ($p8 == '0110101000000000') // 106/8
|
||
|| ($p8 == '0111111100000000') // 127/8
|
||
|| ($p16 == '1010100111111110') // 169.254/16
|
||
|| (($p&'1111111111110000')=='1010110000010000') // 172.16/12
|
||
|| ($p8 == '1011001100000000') // 179/8
|
||
|| ($p8 == '1011100100000000') // 185/8
|
||
|| ($p16 == '1100000010101000') // 192.168/16
|
||
|| (($p&'1110000000000000')=='1110000000000000') // 224/8-255/8
|
||
) return false;
|
||
return true;
|
||
}
|
||
|
||
private function _fix_v4($addr) {
|
||
// 修正IPv4位址类IPv6格式为标准IPv6格式,不验证合法性
|
||
if ( !strpos($addr, '.') ) return $addr;
|
||
preg_match('/(\d+\.){3}\d+$/',$addr,$arr);
|
||
if ( !self::ipv4_check($arr[0]) ) return $addr;
|
||
$hex = dechex(self::ip2long($arr[0]));
|
||
$hex = str_repeat('0', 8-strlen($hex)).$hex;
|
||
$v4p = substr($hex,0,4) . ':' . substr($hex,4,4);
|
||
$p1 = str_replace($arr[0],'',$addr);
|
||
strtolower($p1) === 'ffff:' && $p1 = '::'.$p1;
|
||
$addr = $p1 . $v4p;
|
||
return $addr;
|
||
}
|
||
}
|
||
?>
|