干就完了

This commit is contained in:
Enoch 2022-12-18 18:52:56 +08:00
commit feef31ca03
12 changed files with 971 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.history

13
composer.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "laysense/dns",
"type": "library",
"license": "MIT",
"description": "Webman plugin laysense/dns. You can making a DNS Server with Webman now",
"require": {
},
"autoload": {
"psr-4": {
"Laysense\\Dns\\": "src"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

103
readme.md Normal file
View File

@ -0,0 +1,103 @@
# Webman DNS
Webman的DNS服务器插件可以实现Webman启动时运行一个DNS服务器
> 注意默认为udp53端口需要ROOT权限
---
## 支持的DNS类型
* A
* AAAA
* CNAME
* SOA
* PTR
* MX
* TXT
最新版本已经增加了CNAME+A和CNAME+AAAA方式
---
## 安装
```shell
composer require laysense/dns
```
## 配置
> 配置文件位于 /config/plugin/laysense/dns/process.php
```php
<?php
return [
'Dns' => [
'handler' => process\DnsProcess::class,
'listen' => 'Dns://0.0.0.0:53', #使用的端口,53端口需要root权限
'transport' => 'udp',
'count' => cpu_count() * 4 #线程数量
],
];
```
## 使用
> 为了方便您的使用,本插件(不要脸地)导入了一个Controller
>
> 位于 /app/controller/DnsController.php
>
> 【如果这影响到了您的项目和您的开发习惯,请修改/process/DnsProcess.php 文件】
>
> 安装前请先保障文件不冲突
> 本DNS插件只提供了一个DNS请求和响应的接口其余的数据库、DNS查询、多级缓存、递归等需要您自行实现
该Controller名存实亡其实就是一个class
```php
<?php
namespace app\controller;
include_once base_path().'/vendor/laysense/dns/src/resource/ipv6.php';
use dnstools\ipv6; #用于IPv6相关
/**
* $ipv6=new IPv6;
* $ipv6->ip2bin($ip);
*/
class DnsController
{
public function DNS($type,$name,$rip,$id,$query)
{
#输出信息
#echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n";
#此处请根据业务需要,通过判断$name和$rip返回正确的数据
#详情请参见 https://github.com/ywnsya/workerman-dns 尤其是 https://github.com/ywnsya/Workerman-DNS/blob/master/start.php 中的用法
$send['detail']='dns.laysense.com';
$send['ttl']=30;
$send['type']='PTR';
#此处无需修改
$send['id']=$id;
$send['query']=$query;
$return=json_encode($send);
return $return;
}
}
```
具体的使用方式请参照 [Workerman-DNS](https://www.workerman.net/a/1439) ([Github](https://github.com/ywnsya/workerman-dns)) 下的start.php
## 赞助(我不要脸)
![1671360565549](image/readme/1671360565549.png)

80
src/Install.php Normal file
View File

@ -0,0 +1,80 @@
<?php
namespace Laysense\Dns;
class Install
{
const WEBMAN_PLUGIN = true;
/**
* @var array
*/
protected static $pathRelation = array (
'config/plugin/laysense/dns' => 'config/plugin/laysense/dns',
);
/**
* Install
* @return void
*/
public static function install()
{
static::installByRelation();
}
/**
* Uninstall
* @return void
*/
public static function uninstall()
{
self::uninstallByRelation();
}
/**
* installByRelation
* @return void
*/
public static function installByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
if ($pos = strrpos($dest, '/')) {
$parent_dir = base_path().'/'.substr($dest, 0, $pos);
if (!is_dir($parent_dir)) {
mkdir($parent_dir, 0777, true);
}
}
//symlink(__DIR__ . "/$source", base_path()."/$dest");
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
echo "Create $dest
";
}
copy(__DIR__ .'/resource/Dns.php',base_path().'/vendor/workerman/workerman/Protocols/Dns.php');
echo "Create DNS Protocol Successfully";
copy(__DIR__ .'/resource/DnsProcess.php',base_path().'/process/DnsProcess.php');
echo "Create DNS Process Successfully";
copy(__DIR__ .'/resource/DnsController.php',base_path().'/app/controller/DnsController.php');
echo "Create Dns Controller Successfully";
}
/**
* uninstallByRelation
* @return void
*/
public static function uninstallByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
$path = base_path()."/$dest";
if (!is_dir($path) && !is_file($path)) {
continue;
}
echo "Remove $dest
";
if (is_file($path) || is_link($path)) {
unlink($path);
continue;
}
remove_dir($path);
}
}
}

View File

@ -0,0 +1,4 @@
<?php
return [
'enable' => true,
];

View File

@ -0,0 +1,9 @@
<?php
return [
'Dns' => [
'handler' => process\DnsProcess::class,
'listen' => 'Dns://0.0.0.0:53',
'transport' => 'udp',
'count' => cpu_count() * 4
],
];

78
src/install.backup Normal file
View File

@ -0,0 +1,78 @@
<?php
namespace Laysense\Dns;
class Install
{
const WEBMAN_PLUGIN = true;
/**
* @var array
*/
protected static $pathRelation = array (
'config/plugin/laysense/dns' => 'config/plugin/laysense/dns',
);
/**
* Install
* @return void
*/
public static function install()
{
static::installByRelation();
}
/**
* Uninstall
* @return void
*/
public static function uninstall()
{
self::uninstallByRelation();
}
/**
* installByRelation
* @return void
*/
public static function installByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
if ($pos = strrpos($dest, '/')) {
$parent_dir = base_path().'/'.substr($dest, 0, $pos);
if (!is_dir($parent_dir)) {
mkdir($parent_dir, 0777, true);
}
}
//symlink(__DIR__ . "/$source", base_path()."/$dest");
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
echo "Create $dest
";
}
copy(__DIR__ .'/resource/Dns.php',base_path().'/vendor/workerman/workerman/Protocols/Dns.php');
echo "Create DNS Protocol Successfully";
copy(__DIR__ .'/resource/DnsProcess.php',base_path().'/process/DnsProcess.php');
echo "Create DNS Process Successfully";
}
/**
* uninstallByRelation
* @return void
*/
public static function uninstallByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
$path = base_path()."/$dest";
if (!is_dir($path) && !is_file($path)) {
continue;
}
echo "Remove $dest
";
if (is_file($path) || is_link($path)) {
unlink($path);
continue;
}
remove_dir($path);
}
}
}

419
src/resource/Dns.php Normal file
View File

@ -0,0 +1,419 @@
<?php
/**
* Workerman DNS Protocol
* @author Enoch EchoNoch Enoch@laysense.com
* @Repo http://git.laysense.com/enoch/workerman-dns
* @Github http://github.com/ywnsya/workerman-dns
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Workerman\Protocols;
class Dns
{
/**
* 检查包的完整性
* 如果能够得到包长则返回包的在buffer中的长度否则返回0继续等待数据
* 如果协议有问题则可以返回false当前客户端连接会因此断开
* @param string $buffer
* @return int
*/
public static function input($buffer)
{
return 200;
}
/**
* 打包,当向客户端发送数据的时候会自动调用
* @param string $buffer
* @return string
*/
public static function encode($buffer)
{
$buffer=json_decode($buffer);
$type=$buffer->type;
switch($type){
case 'A':
$type='0001';
#$lenth='0004';
$ip=$buffer->detail;
$n=0;
foreach($ip as $i){
$nss=explode('.',$i);
$detail[$n]='';
foreach($nss as $part){
$tpart=str_pad(dechex($part),2,"0",STR_PAD_LEFT);
$detail[$n]=$detail[$n].$tpart;
};
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
break;
case 'NS':
$type='0002';
#$lenth='0004';
$ns=$buffer->detail;
$n=0;
foreach($ns as $i){
$nss=explode('.',$i);
$detail[$n]='';
foreach($nss as $part){
#$len=strlen($part);
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[$n]=$detail[$n].$len.$tpart;
};
$detail[$n]=$detail[$n].'00';
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
break;
case 'PTR':
$type='000C';
$ns=$buffer->detail;
$nss=explode('.',$ns);
$detail[0]='';
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[0]=$detail[0].$len.$tpart;
};
$detail[0]=$detail[0].'00';
$lenth[0]=str_pad(dechex((strlen($detail[0])/2)),4,"0",STR_PAD_LEFT);
break;
case 'CNAME':
$type='0005';
$ns=$buffer->detail;
$n=0;
foreach($ns as $i){
$nss=explode('.',$i);
$detail[$n]='';
foreach($nss as $part){
#$len=strlen($part);
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[$n]=$detail[$n].$len.$tpart;
};
$detail[$n]=$detail[$n].'00';
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
break;
case 'CNAME+A':
$type='0005';
$ns=$buffer->detail;
$nss=explode('.',$ns);
$detail[0]='';
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[0]=$detail[0].$len.$tpart;
};
$detail[0]=$detail[0].'00';
$lenth[0]=str_pad(dechex((strlen($detail[0])/2)),4,"0",STR_PAD_LEFT);
$ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT);
$answer='';
$answer=$answer.'C00C'.$type.'0001'.$ttl.$lenth[0].$detail[0];
$ip=dns_get_record($ns,DNS_A);
$type='0001';
$n=0;
foreach($ip as $i){
$ttl=str_pad(dechex($i['ttl']),8,"0",STR_PAD_LEFT);
$i=$i['ip'];
$nss=explode('.',$i);
$detail[$n]='';
foreach($nss as $part){
$tpart=str_pad(dechex($part),2,"0",STR_PAD_LEFT);
$detail[$n]=$detail[$n].$tpart;
};
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
$n=0;
foreach($detail as $c){
$rlenth='';
$rlenth=$lenth[$n];
$n=$n+1;
$answer=$answer.'C02B'.$type.'0001'.$ttl.$rlenth.$c;
}
$status='8180';
$questions='0001';
$AuthorityRRs='0000';
$AdditionalRRs='0000';
$AnswerRRs=str_pad((count((array)$ip)+1),4,"0",STR_PAD_LEFT);
$response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer;
return hex2bin($response);
break;
case 'CNAME+AAAA':
$type='0005';
$ns=$buffer->detail;
$nss=explode('.',$ns);
$detail[0]='';
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[0]=$detail[0].$len.$tpart;
};
$detail[0]=$detail[0].'00';
$lenth[0]=str_pad(dechex((strlen($detail[0])/2)),4,"0",STR_PAD_LEFT);
$ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT);
$answer='';
$answer=$answer.'C00C'.$type.'0001'.$ttl.$lenth[0].$detail[0];
$ip=dns_get_record($ns,DNS_AAAA);
$type='001C';
$n=0;
foreach($ip as $i){
$ipv6=$i['ipv6'];
$hexstr = unpack("H*hex", inet_pton($ipv6));
$ipv6=substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hexstr['hex']), 0, -1);
$ipv6=str_replace(':','',$ipv6);
#$ipv6= bin2hex($ipv6);
$detail[$n]="$ipv6";
$lenth[$n]="0010";
$n=$n+1;
};
$n=0;
foreach($detail as $c){
$rlenth='';
$rlenth=$lenth[$n];
$n=$n+1;
$answer=$answer.'C02C'.$type.'0001'.$ttl.$rlenth.$c;
}
$status='8180';
$questions='0001';
$AuthorityRRs='0000';
$AdditionalRRs='0000';
$AnswerRRs=str_pad((count((array)$ip)+1),4,"0",STR_PAD_LEFT);
$response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer;
return hex2bin($response);
break;
case 'SOA':
$type='0006';
$ns=$buffer->detail;
$ns=json_decode( json_encode( $ns),true);
if($ns['type']=='none'){
$Rns=dns_get_record($ns['name'],DNS_SOA);
$Rns=$Rns[0];
$ns=$Rns;
$buffer->ttl=$Rns['ttl'];
}
$nss=explode('.',$ns['mname']);
$detail[0]='';
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[0]=$detail[0].$len.$tpart;
};
$detail[0]=$detail[0].'00';
unset($nss,$len,$tpart);
$nss=explode('.',$ns['rname']);
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[0]=$detail[0].$len.$tpart;
};
$detail[0]=$detail[0].'00'.str_pad(dechex($ns['serial']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['refresh']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['retry']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['expire']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['minimum-ttl']),8,"0",STR_PAD_LEFT);
$lenth[0]=str_pad(dechex((strlen($detail[0])/2)),4,"0",STR_PAD_LEFT);
break;
case 'AAAA':
$type='001C';
$ip=$buffer->detail;
$n=0;
foreach($ip as $i){
$detail[$n]="$i";
$lenth[$n]="0010";
$n=$n+1;
};
break;
case 'TEXT':
$type='0010';
$ns=$buffer->detail;
$n=0;
foreach($ns as $i){
$detail[$n]='';
$text=bin2hex($i);
$tlen=str_pad(dechex((strlen($text)/2)),2,"0",STR_PAD_LEFT);
$detail[$n]=$tlen.$text;
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
break;
case 'MX':
$type='000F';
$ns=$buffer->detail;
$n=0;
print_r($ns);
foreach($ns as $i){
$nss=explode('.',$i->name);
$detail[$n]='';
foreach($nss as $part){
#$len=strlen($part);
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail[$n]=$detail[$n].$len.$tpart;
};
$detail[$n]=$detail[$n].'00';
$lenth[$n]=str_pad(dechex((strlen($detail[$n])/2)+2),4,"0",STR_PAD_LEFT).str_pad(dechex($i->pre),4,"0",STR_PAD_LEFT);
$n=$n+1;
};
break;
case 'none':
$type='0006';
$ns=$buffer->detail;
$url=$ns;
while(true){
preg_match("#\.(.*)#i",$url,$match);//获取根域名
$domin = $match[1];
$soa=dns_get_record($domin,DNS_SOA);
if(array_key_exists('0',$soa)){
if(array_key_exists('mname',$soa[0])){
$qname=$domin;
$ns=$soa[0];
break;
}else{
$url=$domin;
}
}else{
$url=$domin;
}
}
$nss=explode('.',$ns['mname']);
$detail='';
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail=$detail.$len.$tpart;
};
$detail=$detail.'00';
unset($nss,$len,$tpart);
$nss=explode('.',$ns['rname']);
foreach($nss as $part){
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$detail=$detail.$len.$tpart;
};
$detail=$detail.'00'.str_pad(dechex($ns['serial']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['refresh']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['retry']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['expire']),8,"0",STR_PAD_LEFT).str_pad(dechex($ns['minimum-ttl']),8,"0",STR_PAD_LEFT);
$lenth=str_pad(dechex((strlen($detail)/2)),4,"0",STR_PAD_LEFT);
$ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT);
$status='8183';
$questions='0001';
$AnswerRRs='0000';
$AuthorityRRs='0001';
$AdditionalRRs='0000';
#$qname
$nss=explode('.',$qname);
$qname='';
foreach($nss as $part){
#$len=strlen($part);
$len=str_pad(dechex(strlen($part)),2,"0",STR_PAD_LEFT);
$tpart=bin2hex($part);
$qname=$qname.$len.$tpart;
};
$qname=$qname.'00';
$answer='';
$answer=$answer.$qname.$type.'0001'.$ttl.$lenth.$detail;
$response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer;
return hex2bin($response);
break;
}
$ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT);
$status='8180';
$questions='0001';
$AnswerRRs=str_pad(count((array)$buffer->detail),4,"0",STR_PAD_LEFT);
$AuthorityRRs='0000';
$AdditionalRRs='0000';
$answer='';
$n=0;
foreach($detail as $c){
$rlenth='';
$rlenth=$lenth[$n];
$n=$n+1;
$answer=$answer.'C00C'.$type.'0001'.$ttl.$rlenth.$c;
}
$response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer;
return hex2bin($response);
}
/**
* 解包当接收到的数据字节数等于input返回的值大于0的值自动调用
* 并传递给onMessage回调函数的$data参数
* @param string $buffer
* @return string
*/
public static function decode($buffer)
{
$data=bin2hex($buffer);
$id=substr($data,0,4);
$type=substr($data,-8,4);
switch($type){
case '0001':
$type='A';
break;
case '0002':
$type='NS';
break;
case '000c':
$type='PTR';
break;
case '0006':
$type='SOA';
break;
case '001c':
$type='AAAA';
break;
case '0005':
$type='CNAME';
break;
case '0010':
$type='TEXT';
break;
case '000f':
$type='MX';
break;
}
$name=substr($data,24,-8);
$namede=str_split($name,2);
$realname='';
foreach($namede as $cha){
$chat=hex2bin($cha);
if(!ctype_alnum($chat)){
$chat='.';
}
$realname=$realname.$chat;
}
$realname=substr($realname,1,-1);
$query=substr($data,24);
#$returndata="$type".'|||'."$realname";
$returndata= json_encode(array('type' => $type, 'name' => "$realname", 'id'=>"$id", 'query'=>"$query"));
return $returndata;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace app\controller;
include_once base_path().'/vendor/laysense/dns/src/resource/ipv6.php';
use dnstools\ipv6; #用于IPv6相关
/**
* $ipv6=new IPv6;
* $ipv6->ip2bin($ip);
*/
class DnsController
{
public function DNS($type,$name,$rip,$id,$query)
{
#输出信息
#echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n";
#此处请根据业务需要,通过判断$name和$rip返回正确的数据
#详情请参见 https://github.com/ywnsya/workerman-dns 尤其是 https://github.com/ywnsya/Workerman-DNS/blob/master/start.php 中的用法
$send['detail']='dns.laysense.com';
$send['ttl']=30;
$send['type']='PTR';
#此处无需修改
$send['id']=$id;
$send['query']=$query;
$return=json_encode($send);
return $return;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace process;
use Workerman\Connection\TcpConnection;
class DnsProcess
{
public function onConnect(TcpConnection $connection)
{
echo "onConnect\n";
}
public function onWebSocketConnect(TcpConnection $connection, $http_buffer)
{
echo "onWebSocketConnect\n";
}
public function onMessage(TcpConnection $connection, $data)
{
$connection->send($data);
}
public function onClose(TcpConnection $connection)
{
echo "onClose\n";
}
}

204
src/resource/ipv6.php Normal file
View File

@ -0,0 +1,204 @@
<?php
namespace dnstools;
/* 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';
}
/**
* realip()因为在Webman(Workerman)下无效,为防止冲突已删除
*/
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;
}
}
?>