Workerman-DNS/php-ipv6.php
2022-12-16 20:36:45 +08:00

228 lines
8.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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]为真实IParray[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;
}
}
?>