diff --git a/Dns.php b/Dns.php index 134c880..9b8f49e 100644 --- a/Dns.php +++ b/Dns.php @@ -10,6 +10,15 @@ namespace Workerman\Protocols; class Dns { + public static function send($response,$query,$info){ + $response=hex2bin($response); + $traffic=strlen($response); + $info=json_decode($info); + #var_dump($info); + #出流量统计 + #您也可以在此处保存$response,下一次通过raw类型实现快速缓存相应. + return $response; + } /** * 检查包的完整性 * 如果能够得到包长,则返回包的在buffer中的长度,否则返回0继续等待数据 @@ -19,8 +28,7 @@ class Dns */ public static function input($buffer) { - - return 200; + return 512; } /** @@ -34,6 +42,39 @@ class Dns $buffer=json_decode($buffer); $type=$buffer->type; switch($type){ + case 'raw': + return Dns::send($buffer->detail,$buffer->query,$buffer->info); + break; + case 'flag': + if($buffer->flag=='NXDOMAIN'){ + $status='8183'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0001'; + /** + * NXDOMAIN应当返回SOA记录,主要内容是TTL,LDNS会在SOA的TTL到期前缓存该FLAG,否则会被LDNS递归时拒绝 + */ + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + }elseif($buffer->flag=='SERVFAIL'){ + $status='8182'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0000'; + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + }elseif($buffer->flag=='REFUSE'){ + $status='8185'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0000'; + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + } + break; case 'A': $type='0001'; #$lenth='0004'; @@ -150,7 +191,7 @@ class Dns $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); + return Dns::send($response,$buffer->query,$buffer->info); break; case 'CNAME+AAAA': @@ -201,14 +242,14 @@ class Dns $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); + return Dns::send($response,$buffer->query,$buffer->info); break; case 'SOA': $type='0006'; $ns=$buffer->detail; $ns=json_decode( json_encode( $ns),true); - if($ns['type']=='none'){ + if($ns['type']=='auto'){ $Rns=dns_get_record($ns['name'],DNS_SOA); $Rns=$Rns[0]; $ns=$Rns; @@ -282,24 +323,31 @@ class Dns 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; + $ns=json_decode( json_encode( $ns),true); + var_dump($ns); + + if($ns['type']=='auto'){ + $ns=$ns['name']; + $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; + } + } }else{ - $url=$domin; + $qname=$ns['qname']; } - }else{ - $url=$domin; - } - } - $nss=explode('.',$ns['mname']); $detail=''; foreach($nss as $part){ @@ -340,7 +388,7 @@ class Dns $answer=''; $answer=$answer.$qname.$type.'0001'.$ttl.$lenth.$detail; $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer; - return hex2bin($response); + return Dns::send($response,$buffer->query,$buffer->info); break; } $ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT); @@ -358,7 +406,8 @@ class Dns $answer=$answer.'C00C'.$type.'0001'.$ttl.$rlenth.$c; } $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer; - return hex2bin($response); + $traffic=strlen(hex2bin($response)); + return Dns::send($response,$buffer->query,$buffer->info); } /** @@ -369,12 +418,27 @@ class Dns */ public static function decode($buffer) { - /** - $data=bin2hex($buffer); - echo $data; - $id=substr($data,0,4); - $flag=substr($data,5,4); - $type=substr($data,-8,4); + $traffic=strlen($buffer);#接收流量 + $data=bin2hex($buffer); + $id=substr($data,0,4); + $flag=substr($data,4,4); + $questions=substr($data,8,4); + $answerRRs=substr($data,12,4); + $authorityRRs=substr($data,16,4); + $additionalRRs=substr($data,20,4); + $startbyte=24; + $dlen=substr($data,$startbyte,2); + $startbyte=26; + $i=1; + while($dlen!='00'){ + $domain[$i]=hex2bin(substr($data,$startbyte,hexdec($dlen)*2)); + $startbyte=$startbyte+(hexdec($dlen)*2); + $dlen=substr($data,$startbyte,2); + $startbyte=$startbyte+2; + $i++; + } + $realname=join(".",$domain); + $type=substr($data,$startbyte,4); switch($type){ case '0001': $type='A'; @@ -401,71 +465,10 @@ class Dns $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"; - $data=bin2hex($buffer); - $id=substr($data,0,4); -$flag=substr($data,4,4); -$questions=substr($data,8,4); -$answerRRs=substr($data,12,4); -$authorityRRs=substr($data,16,4); -$additionalRRs=substr($data,20,4); -$startbyte=24; -$dlen=substr($data,$startbyte,2); -$startbyte=26; -$i=1; -while($dlen!='00'){ -$domain[$i]=hex2bin(substr($data,$startbyte,hexdec($dlen)*2)); -$startbyte=$startbyte+(hexdec($dlen)*2); -$dlen=substr($data,$startbyte,2); -$startbyte=$startbyte+2; -$i++; -} -$realname=join(".",$domain); -$type=substr($data,$startbyte,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; -} -$query=substr($data,24,$startbyte-16); + $query=substr($data,24,$startbyte-16); - $returndata= json_encode(array('type' => $type, 'name' => "$realname", 'id'=>"$id", 'query'=>"$query")); + $returndata= json_encode(array('type' => $type, 'name' => "$realname", 'id'=>"$id", 'query'=>"$query",'traffic'=>$traffic)); return $returndata; } diff --git a/license b/license new file mode 100644 index 0000000..543eff5 --- /dev/null +++ b/license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2024] [Enoch] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/readme.md b/readme.md index 4241030..1336fe3 100644 --- a/readme.md +++ b/readme.md @@ -1,30 +1,118 @@ -# Workerman-DNS +# WorkermanDNS DOC -[Workerman](https://www.workerman.net/)的DNS协议,实现了简单的DNS协议解析和响应,通过本协议支持,您可以利用[Workerman](https://www.workerman.net/)实现基于PHP的Dns服务器 +# 有什么用 -目前支持以下DNS类型: +让您能够使用Workerman编写DNS服务器。 -* A -* AAAA -* CNAME -* SOA -* PTR -* MX -* TXT +该库仅提供DNS协议的响应方式和DNS请求的解析。记录的查询、储存、缓存都需要您自行编写完成。 -> 本仓库内vendor文件夹为[Workerman](https://www.workerman.net/)您可以删除. -> -> 直接将本仓库根目录下的 Dns.php 放置到您的Workerman项目中的 /vendor/workerman/workerman/Protocols 目录下即可使用 +# 支持特性 ---- +- 支持的协议: + + A AAAA CNAME MX PTR NS TXT SOA + +- 支持的Flag标签: + - 8180:OK≈HTTP200 :正常 + - 8182:SERVFAIL≈HTTP503 :服务器错误 + - 8183:NXDOMAIN≈HTTP404 :记录不存在 + - 8185:REFUSE≈HTTP403:拒绝请求 -## 使用方式: +# 安装 -详见start.php 文件 +### PHP环境 -> 注意:使用53端口需要root权限 +WorkermanDNS基于Workerman PHP框架。是纯PHP编写的DNS服务器。使用php-cli环境运行。 -#### 1.监听端口 +安装环境请参照Workerman要求: + +[环境要求-workerman手册](https://www.workerman.net/doc/workerman/install/requirement.html) + +### Workerman框架 + +通过Composer安装Workerman框架 + +`composer require workerman/workerman` + +### 下载Dns.php + +可通过 + +[https://git.laysense.com/enoch/Workerman-DNS/raw/branch/master/Dns.php](https://git.laysense.com/enoch/Workerman-DNS/raw/branch/master/Dns.php) + +或 + +[raw.githubusercontent.com/ywnsya/Workerman-DNS/master/Dns.php](https://raw.githubusercontent.com/ywnsya/Workerman-DNS/master/Dns.php) + +或从Releases中: + +[Workerman-DNS](https://git.laysense.com/enoch/Workerman-DNS/releases) + +下载Dns.php文件 + +### 下载IPv6支持库 + +如您需要使用到IPv6功能(您也可以用其他库代替,不影响DNS协议的使用),请下载IPv6支持库 + +地址为: + +[git.laysense.com/enoch/Workerman-DNS/raw/branch/master/php-ipv6.php](https://git.laysense.com/enoch/Workerman-DNS/raw/branch/master/php-ipv6.php) + +或 + +[raw.githubusercontent.com/ywnsya/Workerman-DNS/master/php-ipv6.php](https://raw.githubusercontent.com/ywnsya/Workerman-DNS/master/php-ipv6.php) + +### 放置Dns.php文件 + +Dns.php文件应当放置在./vendor/workerman/workerman/Protocols下 + +### 放置php-ipv6.php文件 + +该文件放置在项目目录./下 + +### 创建start.php文件 + +在项目目录./下新建空白的start.php文件即可 + +### 以上步骤完成后,您的文件夹结构应该如下: + +``` +. +├── composer.json +├── composer.lock +├── ***php-ipv6.php*** +├── ***start.php*** +└── vendor + ├── autoload.php + ├── composer + │ ├── ……此处省略…… + └── workerman + ├── workerman + │ ├── ……此处省略…… + │ ├── Protocols + │ │ ├── ***Dns.php*** + │ │ ├── Frame.php + │ │ ├── Http + │ │ │ ├── Chunk.php + │ │ │ ├── Request.php + │ │ │ ├── Response.php + │ │ │ ├── ServerSentEvents.php + │ │ │ ├── Session + │ │ │ ├── Session.php + │ │ │ └── mime.types + │ │ ├── Http.php + │ │ ├── ProtocolInterface.php + │ │ ├── Text.php + │ │ ├── Websocket.php + │ │ └── Ws.php + │ ├── ……此处省略…… +``` + +# 基础框架: + +以下内容均在start.php中编写 + +请在start.php中写入: ```php transport = 'udp'; - - -``` - -#### 2.获取查询内容 - -```php +$worker->transport = 'udp'; $worker->onMessage = function($connection, $data){ $data=json_decode($data); $type=$data->type; #查询类型 $name=$data->name; #查询内容(一般是域名,PTR时为倒序IP) $rip=$connection->getRemoteIp(); #客户端IP -#输出信息 -echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n"; +# 请在下方编写您的DNS响应 +#—————————————————————— -} -``` - -#### 3.响应A记录 - -```php -$worker->onMessage = function($connection, $data){ - -$send['type']='A'; -$send['detail'][1]='119.29.29.29'; #第一条记录 -$send['detail'][2]='8.8.8.8'; #第二条记录 -$send['ttl']=30; - - - -#id和query一般情况下直接返回输出即可 +#—————————————————————— +#下方内容无需更改 $send['id']=$data->id; $send['query']=$data->query; - - - +if(!isset($send['ttl'])){ + $send['ttl']=0; +} +$send['info']=json_encode(['domain'=>$data->name,'querytype'=>$data->type,'answertype'=>$send['type'],'ip'=>$rip,'ttl'=>$send['ttl'],'detail'=>$send['detail']]); $send=json_encode($send); $connection->send($send); - - }; Worker::runAll(); ``` -#### 4.响应其他记录 +### 请求变量 -见start.php 内有所有记录类型的响应方式 +| 变量名称 | 意义 | 示例 | +| --- | --- | --- | +| $type | 请求类型 | A | +| $name | 请求域名 | dnsservertest.com | +| $rip | 客户端IP | 192.168.1.1 | +| $data->id | 请求ID | 0001 | +| $data->traffic | 请求包大小(Byte) | 88 | -#### 5.说明 +# 编写响应: -您应当通过获取query的 `$name`通过查询数据库等方式返回数据,对于不存在的记录应当返回SOA记录 +### 响应参数 -您需要的时候可以通过 `dns_get_record()`向上级DNS递归查找并缓存 +您最终需要回复一个$send的数组。定义如下: -这一系列操作,本协议不提供,您可以自行通过Redis等并利用workerman实现 +| 名称 | 意义 | 类型 | 示例 | 说明 | +| --- | --- | --- | --- | --- | +| $send['type'] | 响应类型 | 字符串 | $send['type']=A +$send['type']=CNAME | 指定响应的类型 | +| $send['detail'] | 响应内容 | 字符串 +数组 +多维数组 | 119.29.29.29 +$send['detail'][1]='119.29.29.29' +$send['detail']= array(); | 根据不同的响应类型,返回不同的响应。具体请往下阅读各种记录的响应方式 | +| $send['ttl'] | TTL缓存时间 | 数字 | $send['ttl']=600 | 并非所有记录都需要TTL,未传入时默认0 | +| $send['flag'] | Flag类型 | 字符串 | $send['flag']=‘REFUSE’ | 使用flag类型时需指定的flag | +| $send['id'] | ID | 数字 | 0001 | 无需更改无需编写。 | +| $send['query'] | 请求体 | 16进制 | | 无需更改无需编写。 | +| $send['info'] | 请求和响应信息 | json | | 无需编写,基础框架内已经完成。 | -不建议作为根域名的NS服务器使用。 +您应当根据$name变量,进行响应。 -## 已知问题 +以下内容请在start.php结构中间的空行内编写: -本协议最早写于鄙人刚学习php的阶段,现在翻出来无疑是屎山一坨,代码写的和xxs一样,性能不敢测试,还请各位大佬包容 +### A记录: -目前已知问题是: +响应一个或多个IPv4地址 -域名不存在时可能出现BUG +$send['detail']应为一个数组。 + +```php +$send['type']='A'; +$send['detail'][1]='119.29.29.29'; +$send['detail'][2]='8.8.8.8'; +$send['ttl']=30; +``` + +以上意味着响应IP为119.29.29.29和8.8.8.8,指定TTL为30秒。 + +- 示例 + +```php +transport = 'udp'; +$worker->onMessage = function($connection, $data){ +$data=json_decode($data); +$type=$data->type; #查询类型 +$name=$data->name; #查询内容(一般是域名,PTR时为倒序IP) +$rip=$connection->getRemoteIp(); #客户端IP + +if($type=='A' && $name=='dnsservertest.com'){ + $send['type']='A'; + $send['detail'][1]='192.168.2.1'; + $send['detail'][2]='192.168.2.2'; + $send['detail'][3]='192.168.2.3'; + $send['ttl']=60; +} + +$send['id']=$data->id; +$send['query']=$data->query; +if(!isset($send['ttl'])){ + $send['ttl']=0; +} +$send['info']=json_encode(['domain'=>$data->name,'querytype'=>$data->type,'answertype'=>$send['type'],'ip'=>$rip,'ttl'=>$send['ttl'],'detail'=>$send['detail']]); +$send=json_encode($send); +$connection->send($send); +}; +Worker::runAll(); +``` + +以上代码意味着对于dnsservertest.com域名的A记录请求,响应192.168.2.1、2.2、2.3三个IP,指定TTL为60秒。 + +以下内容将不再给出完整代码示例。 + +### AAAA记录 + +AAAA记录响应一个或多个IPv6地址。 + +```php +$ipv6=new IPv6; +$send['type']='AAAA'; +$send['detail'][1]=bin2hex($ipv6->ip2bin("fe80::2c5f")); #此操作可以还原被简化的IPv6地址 协议内不再对IPv6地址进行处理,请按照本方式传递16进制无":"的完整16位IPv6 +$send['detail'][2]=bin2hex($ipv6->ip2bin("2001:0:2851:b9d0:2c5f:f0d9:21be:4b96")); +$send['ttl']=30; +``` + +$send['detail']应当为数组,且值为完整的、没有‘:’的,连续16位16进制IPv6地址。 + +IPv6库可以如上方示例所述,将IPv6还原完整并去除‘:’,再通过bin2hex的php函数转为16进制 + +### CNAME + +CNAME即别名,将映射到另一个或多个DNS域名,客户端将向另一个域名发送请求。 + +```php + $send['type']='CNAME' + $send['detail'][1]='hk.dnsservertest.com'; + $send['detail'][2]='jp.dnsservertest.com'; + $send['ttl']=600; +``` + +$send['detail']一样为数组。 + +一般情况下,为了提升DNS解析效率,DNS服务器将同时直接返回一个A或AAAA记录,要实现这个请使用CNAME+A或CNAME+AAAA类型 + +### CNAME+A/AAAA + +```php +#CNAME+A +$send['type']='CNAME+A'; +$send['detail']='ipv4.dnsservertest.com'; +$send['ttl']=30; + +#CNAME+AAAA +$send['type']='CNAME+AAAA'; +$send['detail']='ipv6.dnsservertest.com'; +$send['ttl']=30; +``` + + CNAME+A和CNAME+AAAA的情况下,均只支持返回一条CNAME. + +因此,此时的$send['detail']应当为字符串 + +如多条CNAME的均衡负载请通过您的代码在服务端实现。 + +WorkermanDNS将自动获取对应的A记录并返回。 + +### TEXT(TXT) + +TEXT记录将返回一条文本记录。由于默认的最大包大小为512B,因此应当控制TEXT记录内容在256B之内 + +```php +$send['type']='TEXT'; +$send['detail'][1]='text1-test'; +$send['detail'][2]='text2-test'; +$send['ttl']=600; +``` + +$send['detail']为数组。值为TEXT记录内容(字符串) + +### MX + +MX邮件交换记录,建设邮箱时常用。 + +```php + $send['type']='MX'; + $send['detail'][1]['name']='mx.zoho.com'; + $send['detail'][1]['pre']='20'; #权重 + $send['detail'][2]['name']='mx2.zoho.com'; + $send['detail'][2]['pre']='30'; #权重 + $send['detail'][3]['name']='mx3.zoho.com'; + $send['detail'][3]['pre']='50'; #权重 + $send['ttl']=600; +``` + +$send['detail']为数组,其中的每个元素也是多维数组,分别有name和pre两个参数,分别为MX的记录值和对应权重。 + +### NS + +将子域名转交给其他DNS服务器解析。 + +```php +$send['type']='NS'; +$send['detail'][1]='coco.bunny.net'; +$send['detail'][2]='kiki.bunny.net'; +$send['ttl']=600; +``` + +$send['detail']为数组,值为DNS服务器名称。 + +### SOA + +SOA有自动和手动两种 + +- 自动 + +```php + $send['type']='SOA'; + $send['detail']= array(); + $send['detail']['type']='auto';#自动获取上级的SOA + $send['detail']['name']="$name"; +``` + +自动则指定 $send['detail']['type']='auto' ,将自行从上级DNS服务器获取SOA信息。以上代码无需改动,直接使用即可。 + +- 手动 + +```php +$send['type']='SOA'; +$send['detail']= array(); +$send['detail']['type']='self'; +$send['detail']['mname']='dns31.hichina.com'; #主DNS服务器名 +$send['detail']['rname']='hostmaster.hichina.com'; #DNS管理员邮箱 +$send['detail']['serial']='2022052002'; #序列号 序列号必须递增 类似于dns记录的版本号 序列号变大时递归dns将更新记录 +$send['detail']['refresh']='3600'; #区域应当被刷新前的时间间隔 +$send['detail']['retry']='1200'; #刷新失败重试的时间间隔 +$send['detail']['expire']='86400'; #规定在区域不再是权威的之前可以等待的时间间隔的上限 +$send['detail']['minimum-ttl']='60'; #最小TTL +$send['ttl']='180'; #当前SOA记录的TTL +``` + +示例和说明如上。不推荐使用,因为SOA记录的错误可能对递归DNS造成极大影响,除非是您使用WorkermanDNS作为权威DNS时(请尽可能不要这么做) + +### None + +none是WorkermanDNS自定义的一种方式。将返回NXDOMAIN状态码以及SOA记录,一般用于域名不存在时。同样,SOA部分也可以使用自动和手动两种。 + +```php +$send['type']='none'; +$send['detail']= array(); +$send['detail']['type']='auto'; +$send['detail']['name']="$name"; +``` + +```php + $send['type']='none'; + $send['detail']= array(); + $send['detail']['type']='self'; + $send['detail']['qname']='phpisthebestlanguage.com';#根域名 + $send['detail']['mname']='dns31.hichina.com'; #主DNS服务器名 + $send['detail']['rname']='hostmaster.hichina.com'; #DNS管理员邮箱 + $send['detail']['serial']='2022052002'; #序列号 序列号必须递增 类似于dns记录的版本号 序列号变大时递归dns将更新记录 + $send['detail']['refresh']='3600'; #区域应当被刷新前的时间间隔 + $send['detail']['retry']='1200'; #刷新失败重试的时间间隔 + $send['detail']['expire']='86400'; #规定在区域不再是权威的之前可以等待的时间间隔的上限 + $send['detail']['minimum-ttl']='600'; #最小TTL + $send['ttl']='180'; #当前TTL +``` + +### Flag + +Flag类型将返回DNS状态码。 + +- NXDOMAIN + + ```php + $send['type']='flag'; + $send['flag']='NXDOMAIN'; + ``` + + NXDOMAIN表示域名不存在,使用flag类型返回NXDOMAIN时不会附带SOA,当作为权威DNS时可能造成LDNS发生错误,不建议使用,建议使用none类型替代 + +- SERVFAIL + + ```php + $send['type']='flag'; + $send['flag']='SERVFAIL'; + ``` + + SERVFAIL表示解析遇到错误,类似于HTTP的503 + +- REFUSE + + ```php + $send['type']='flag'; + $send['flag']='REFUSE'; + ``` + + REFUSE表示服务器拒绝此请求,可用于限定客户端的IP范围 + + +Flag类型无需返回$send['detail'] + +### Raw + +Raw是WorkermanDNS自定义的一种类型,将不再处理,直接以16进制形式响应,从而无需进行16进制拼合,以加快响应速度。一般是在第一次响应完毕后通过截取记录和请求,写入缓存(如Redis或共享变量等),后续遇到相同请求且在TTL内直接返回。 + +```php +$send['type']='raw'; +$status='8180'; +$questions='0001'; +$AnswerRRs='0001'; +$AuthorityRRs='0000'; +$AdditionalRRs='0000'; +$answer='c00c000c00010000001e001203646e73086c617973656e736503636f6d00';#此处示例为返回记录值为dns.laysense.com的PTR记录的16进制 +$response=$data->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$data->query.$answer; +$send['detail']=$response; +``` + +# 流量统计与日志 + +### 响应(出站) + +Dns.php文件中send函数下,将会返回提供$query和$info两个变量,以及$response。 + +`strlen($response)`将直接可以获取该次响应出站流量(为byte) + +$query是请求包体的16进制响应内容 + +$info为一个json对象,var_dump结构类似如下 + +```php +object(stdClass)#18 (6) { + ["domain"]=> + string(26) "95.188.24.172.in-addr.arpa" + ["querytype"]=> + string(3) "PTR" + ["answertype"]=> + string(3) "PTR" + ["ip"]=> + string(12) "172.24.176.1" + ["ttl"]=> + int(30) + ["detail"]=> + string(16) "dns.laysense.com" +} +``` + +以上是一个对172.24.188.95的IP地址提供值为dns.laysense.com的PTR记录的$info示例 + +包含了请求域名、请求类型、响应类型、客户端IP、ttl等,可供您进行日志统计 + +### 请求(入站) + +请参看上方的请求变量章节。 + +其中对于流量将传递$data->traffic变量,其为请求包体的大小(Byte) + +# 运行 + +使用 + +`php start.php start` 运行 + +`php start.php start -d` 后台运行,自带守护进程 + +`php start.php stop` 停止运行 + +`php start.php status` 查看运行状况 + +请注意: + +DNS服务使用53端口,为特权端口,绝大多数情况下必须使用root权限运行。 + +### 版本 + +0.1.0@2024/01/31 + +### About + +作者 Enoch[enoch@laysense.com] + +- Repo + +[Workerman-DNS](https://git.laysense.com/enoch/Workerman-DNS) + +- Github + +[https://github.com/ywnsya/Workerman-DNS](https://github.com/ywnsya/Workerman-DNS) + +### License + +**Apache License 2.0** \ No newline at end of file diff --git a/start.php b/start.php index 2930fc7..e0b917b 100644 --- a/start.php +++ b/start.php @@ -21,10 +21,6 @@ $type=$data->type; #查询类型 $name=$data->name; #查询内容(一般是域名,PTR时为倒序IP) $rip=$connection->getRemoteIp(); #客户端IP -#输出信息 -echo "\n Type:$type \n Domain: $name\n Client IP: $rip \n"; - - if($type=='A'){ $send['type']='A'; $send['detail'][1]='119.29.29.29'; @@ -69,13 +65,6 @@ if($type=='CNAME'){ $send['ttl']=600; } -if($type=='CNAME'){ - $send['type']='CNAME'; - $send['detail'][1]='baidu.cn'; - $send['detail'][2]='baidu.com'; - $send['ttl']=600; -} - if($type=='AAAA'){ $ipv6=new IPv6; $send['type']='AAAA'; @@ -114,7 +103,7 @@ if($type=='SOA'){ $send['type']='SOA'; $send['detail']= array(); - $send['detail']['type']='none'; + $send['detail']['type']='auto';#自动获取上级的SOA $send['detail']['name']=$name; /** @@ -138,18 +127,78 @@ if($type=='SOA'){ } #返回域名不存在(自动获取SOA) -if($name=='phpisthebestlanguage.com'){ +if($name=='1.phpisthebestlanguage.com'){ $send['type']='none'; - $send['detail']="$name"; + $send['detail']= array(); + $send['detail']['type']='auto'; + $send['detail']['name']="$name"; + /** + * none类型将返回NXDOMAIN,同时通过自动获取SOA返回SOA记录。 + */ + /** + * + * 手动自行返回SOA记录(如果为权威服务器时) + * $send['detail']['type']='self'; + * $send['detail']['mname']='dns31.hichina.com'; #主DNS服务器名 + * $send['detail']['rname']='hostmaster.hichina.com'; #DNS管理员邮箱 + * $send['detail']['serial']='2022052002'; #序列号 序列号必须递增 类似于dns记录的版本号 序列号变大时递归dns将更新记录 + * $send['detail']['refresh']='3600'; #区域应当被刷新前的时间间隔 + * $send['detail']['retry']='1200'; #刷新失败重试的时间间隔 + * $send['detail']['expire']='86400'; #规定在区域不再是权威的之前可以等待的时间间隔的上限 + * $send['detail']['minimum-ttl']='600'; #最小TTL + * + * $send['ttl']='180'; #当前TTL + **/ +} +if($name=='2.phpisthebestlanguage.com'){ + $send['type']='none'; + $send['detail']= array(); + $send['detail']['type']='self'; + #手动自行返回SOA记录与NXDOMAIN时的示例 + $send['detail']['qname']='phpisthebestlanguage.com';#根域名 + $send['detail']['mname']='dns31.hichina.com'; #主DNS服务器名 + $send['detail']['rname']='hostmaster.hichina.com'; #DNS管理员邮箱 + $send['detail']['serial']='2022052002'; #序列号 序列号必须递增 类似于dns记录的版本号 序列号变大时递归dns将更新记录 + $send['detail']['refresh']='3600'; #区域应当被刷新前的时间间隔 + $send['detail']['retry']='1200'; #刷新失败重试的时间间隔 + $send['detail']['expire']='86400'; #规定在区域不再是权威的之前可以等待的时间间隔的上限 + $send['detail']['minimum-ttl']='600'; #最小TTL + $send['ttl']='180'; #当前TTL +} +if($name=='404.testdnsserver.com'){ + $send['type']='flag'; + $send['flag']='NXDOMAIN'; + #使用flag类型返回NXDOMAIN时不会附带SOA,当作为权威DNS时可能造成LDNS发生错误,不建议使用,建议改为none类型 +} +if($name=='503.testdnsserver.com'){ + $send['type']='flag'; + $send['flag']='SERVFAIL'; + #SERVFAIL表示解析遇到错误,类似于HTTP的503 +} +if($name=='403.testdnsserver.com'){ + $send['type']='flag'; + $send['flag']='REFUSE'; + #REFUSE表示服务器拒绝此请求,可用于限定客户端的IP范围 +} +if($name=='raw.testdnsserver.com'){ + $send['type']='raw'; + $status='8180'; + $questions='0001'; + $AnswerRRs='0001'; + $AuthorityRRs='0000'; + $AdditionalRRs='0000'; + $answer='c00c000c00010000001e001203646e73086c617973656e736503636f6d00';#此处示例为返回记录值为dns.laysense.com的PTR记录的16进制 + $response=$data->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$data->query.$answer; + $send['detail']=$response; } - - - #id和query一般情况下直接返回输出即可 $send['id']=$data->id; $send['query']=$data->query; - +if(!isset($send['ttl'])){ + $send['ttl']=0; +} +$send['info']=json_encode(['domain'=>$data->name,'querytype'=>$data->type,'answertype'=>$send['type'],'ip'=>$rip,'ttl'=>$send['ttl'],'detail'=>$send['detail']]); $send=json_encode($send); diff --git a/vendor/workerman/workerman.log b/vendor/workerman/workerman.log index 9067637..a8d3104 100644 --- a/vendor/workerman/workerman.log +++ b/vendor/workerman/workerman.log @@ -811,3 +811,247 @@ Stack trace: 2024-01-29 08:29:51 pid:7583 Workerman[start.php] start in DEBUG mode 2024-01-29 08:31:33 pid:7583 Workerman[start.php] stopping ... 2024-01-29 08:31:33 pid:7583 Workerman[start.php] has been stopped +2024-01-30 05:45:51 pid:3591 Workerman[start.php] start in DEBUG mode +2024-01-30 05:56:47 pid:3591 Workerman[start.php] stopping ... +2024-01-30 05:56:47 pid:3591 Workerman[start.php] has been stopped +2024-01-30 05:56:47 pid:4659 Workerman[start.php] start in DEBUG mode +2024-01-30 06:09:39 pid:4659 Workerman[start.php] stopping ... +2024-01-30 06:09:39 pid:4659 Workerman[start.php] has been stopped +2024-01-30 13:46:59 pid:14283 Workerman[start.php] start in DEBUG mode +2024-01-30 13:47:51 pid:14284 Error: Cannot use object of type stdClass as array in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:300 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Connection/UdpConnection.php(73): Workerman\Protocols\Dns::encode() +#1 /www/Workerman-DNS/start.php(180): Workerman\Connection\UdpConnection->send() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2569): {closure}() +#3 [internal function]: Workerman\Worker->acceptUdpConnection() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(557): Workerman\Worker::forkWorkers() +#10 /www/Workerman-DNS/start.php(184): Workerman\Worker::runAll() +#11 {main} +2024-01-30 13:47:51 pid:14283 worker[none:14284] exit with status 64000 +2024-01-30 13:47:53 pid:14283 Workerman[start.php] stopping ... +2024-01-30 13:47:53 pid:14283 Workerman[start.php] has been stopped +2024-01-30 13:49:09 pid:14608 Workerman[start.php] start in DEBUG mode +2024-01-30 13:49:12 pid:14609 Error: Cannot use object of type stdClass as array in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:301 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Connection/UdpConnection.php(73): Workerman\Protocols\Dns::encode() +#1 /www/Workerman-DNS/start.php(180): Workerman\Connection\UdpConnection->send() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2569): {closure}() +#3 [internal function]: Workerman\Worker->acceptUdpConnection() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(557): Workerman\Worker::forkWorkers() +#10 /www/Workerman-DNS/start.php(184): Workerman\Worker::runAll() +#11 {main} +2024-01-30 13:49:12 pid:14608 worker[none:14609] exit with status 64000 +2024-01-30 13:49:53 pid:14608 Workerman[start.php] stopping ... +2024-01-30 13:49:53 pid:14608 Workerman[start.php] has been stopped +2024-01-30 13:49:53 pid:14716 Workerman[start.php] start in DEBUG mode +2024-01-30 13:52:30 pid:14716 Workerman[start.php] stopping ... +2024-01-30 13:52:30 pid:14716 Workerman[start.php] has been stopped +2024-01-30 13:52:31 pid:15036 Workerman[start.php] start in DEBUG mode +2024-01-30 13:57:14 pid:15036 Workerman[start.php] stopping ... +2024-01-30 13:57:14 pid:15036 Workerman[start.php] has been stopped +2024-01-30 13:57:14 pid:15609 Workerman[start.php] start in DEBUG mode +2024-01-30 14:04:37 pid:15609 Workerman[start.php] stopping ... +2024-01-30 14:04:37 pid:15609 Workerman[start.php] has been stopped +2024-01-30 14:04:40 pid:16331 Workerman[start.php] start in DEBUG mode +2024-01-30 14:05:39 pid:16331 Workerman[start.php] stopping ... +2024-01-30 14:05:39 pid:16331 Workerman[start.php] has been stopped +2024-01-30 14:05:40 pid:16528 Workerman[start.php] start in DEBUG mode +2024-01-30 14:06:02 pid:16528 Workerman[start.php] stopping ... +2024-01-30 14:06:02 pid:16528 Workerman[start.php] has been stopped +2024-01-30 14:06:03 pid:16617 Workerman[start.php] start in DEBUG mode +2024-01-30 14:12:35 pid:16617 Workerman[start.php] stopping ... +2024-01-30 14:12:35 pid:16617 Workerman[start.php] has been stopped +2024-01-30 14:12:35 pid:17252 Workerman[start.php] start in DEBUG mode +2024-01-30 14:12:53 pid:17252 Workerman[start.php] stopping ... +2024-01-30 14:12:53 pid:17252 Workerman[start.php] has been stopped +2024-01-30 14:12:53 pid:17320 Workerman[start.php] start in DEBUG mode +2024-01-30 14:13:28 pid:17320 Workerman[start.php] stopping ... +2024-01-30 14:13:28 pid:17320 Workerman[start.php] has been stopped +2024-01-30 14:13:29 pid:17415 Workerman[start.php] start in DEBUG mode +2024-01-30 14:13:54 pid:17415 Workerman[start.php] stopping ... +2024-01-30 14:13:54 pid:17415 Workerman[start.php] has been stopped +2024-01-30 14:13:54 pid:17489 Workerman[start.php] start in DEBUG mode +2024-01-30 14:15:00 pid:17489 Workerman[start.php] stopping ... +2024-01-30 14:15:00 pid:17489 Workerman[start.php] has been stopped +2024-01-30 14:15:01 pid:17626 Workerman[start.php] start in DEBUG mode +2024-01-30 14:17:41 pid:17626 Workerman[start.php] stopping ... +2024-01-30 14:17:41 pid:17626 Workerman[start.php] has been stopped +2024-01-30 14:17:41 pid:17913 Workerman[start.php] start in DEBUG mode +2024-01-30 14:18:25 pid:17913 Workerman[start.php] stopping ... +2024-01-30 14:18:25 pid:17913 Workerman[start.php] has been stopped +2024-01-30 14:18:26 pid:18020 Workerman[start.php] start in DEBUG mode +2024-01-30 14:19:30 pid:18020 Workerman[start.php] stopping ... +2024-01-30 14:19:30 pid:18020 Workerman[start.php] has been stopped +2024-01-30 14:19:30 pid:18154 Workerman[start.php] start in DEBUG mode +2024-01-30 14:20:59 pid:18154 Workerman[start.php] stopping ... +2024-01-30 14:20:59 pid:18154 Workerman[start.php] has been stopped +2024-01-30 14:21:01 pid:18337 Workerman[start.php] start in DEBUG mode +2024-01-30 14:21:22 pid:18337 Workerman[start.php] stopping ... +2024-01-30 14:21:22 pid:18337 Workerman[start.php] has been stopped +2024-01-30 14:21:22 pid:18429 Workerman[start.php] start in DEBUG mode +2024-01-30 14:22:16 pid:18429 Workerman[start.php] stopping ... +2024-01-30 14:22:16 pid:18429 Workerman[start.php] has been stopped +2024-01-30 14:22:17 pid:18587 Workerman[start.php] start in DEBUG mode +2024-01-30 14:22:58 pid:18587 Workerman[start.php] stopping ... +2024-01-30 14:22:58 pid:18587 Workerman[start.php] has been stopped +2024-01-30 14:22:58 pid:18693 Workerman[start.php] start in DEBUG mode +2024-01-30 14:23:11 pid:18693 Workerman[start.php] stopping ... +2024-01-30 14:23:11 pid:18693 Workerman[start.php] has been stopped +2024-01-30 14:23:12 pid:18755 Workerman[start.php] start in DEBUG mode +2024-01-30 14:23:29 pid:18755 Workerman[start.php] stopping ... +2024-01-30 14:23:29 pid:18755 Workerman[start.php] has been stopped +2024-01-30 14:23:29 pid:18817 Workerman[start.php] start in DEBUG mode +2024-01-30 14:23:56 pid:18817 Workerman[start.php] stopping ... +2024-01-30 14:23:56 pid:18817 Workerman[start.php] has been stopped +2024-01-30 14:23:57 pid:18912 Workerman[start.php] start in DEBUG mode +2024-01-30 14:31:03 pid:18912 Workerman[start.php] stopping ... +2024-01-30 14:31:03 pid:18912 Workerman[start.php] has been stopped +2024-01-30 14:31:14 pid:19625 Workerman[start.php] start in DEBUG mode +2024-01-30 14:31:33 pid:19625 Workerman[start.php] stopping ... +2024-01-30 14:31:33 pid:19625 Workerman[start.php] has been stopped +2024-01-30 14:31:34 pid:19693 Workerman[start.php] start in DEBUG mode +2024-01-30 14:35:02 pid:19693 Workerman[start.php] stopping ... +2024-01-30 14:35:02 pid:19693 Workerman[start.php] has been stopped +2024-01-30 14:35:02 pid:20047 Workerman[start.php] start in DEBUG mode +2024-01-30 14:35:05 pid:20048 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:25 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(557): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#9 {main} +2024-01-30 14:35:05 pid:20047 worker[none:20048] exit with status 64000 +2024-01-30 14:35:07 pid:20067 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:25 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1720): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1660): Workerman\Worker::monitorWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(559): Workerman\Worker::monitorWorkers() +#10 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#11 {main} +2024-01-30 14:35:07 pid:20047 worker[none:20067] exit with status 64000 +2024-01-30 14:35:09 pid:20074 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:25 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1720): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1660): Workerman\Worker::monitorWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(559): Workerman\Worker::monitorWorkers() +#10 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#11 {main} +2024-01-30 14:35:09 pid:20047 worker[none:20074] exit with status 64000 +2024-01-30 14:35:11 pid:20093 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:25 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1720): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1660): Workerman\Worker::monitorWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(559): Workerman\Worker::monitorWorkers() +#10 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#11 {main} +2024-01-30 14:35:11 pid:20047 worker[none:20093] exit with status 64000 +2024-01-30 14:35:13 pid:20097 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:25 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1720): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1660): Workerman\Worker::monitorWorkersForLinux() +#9 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(559): Workerman\Worker::monitorWorkers() +#10 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#11 {main} +2024-01-30 14:35:13 pid:20047 worker[none:20097] exit with status 64000 +2024-01-30 14:35:16 pid:20047 Workerman[start.php] stopping ... +2024-01-30 14:35:16 pid:20047 Workerman[start.php] has been stopped +2024-01-30 14:35:17 pid:20128 Workerman[start.php] start in DEBUG mode +2024-01-30 14:35:18 pid:20129 Worker[20129] process terminated with ERROR: E_ERROR "Cannot redeclare Workerman\Protocols\test() (previously declared in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:24) in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php on line 23" +2024-01-30 14:35:18 pid:20128 worker[none:20129] exit with status 65280 +2024-01-30 14:35:52 pid:20128 Workerman[start.php] stopping ... +2024-01-30 14:35:52 pid:20128 Workerman[start.php] has been stopped +2024-01-30 14:35:52 pid:20231 Workerman[start.php] start in DEBUG mode +2024-01-30 14:35:57 pid:20232 Error: Call to undefined function Workerman\Protocols\test() in /www/Workerman-DNS/vendor/workerman/workerman/Protocols/Dns.php:26 +Stack trace: +#0 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2561): Workerman\Protocols\Dns::input() +#1 [internal function]: Workerman\Worker->acceptUdpConnection() +#2 /www/Workerman-DNS/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop() +#3 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(2463): Workerman\Events\Event->loop() +#4 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1574): Workerman\Worker->run() +#5 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1416): Workerman\Worker::forkOneWorkerForLinux() +#6 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(1390): Workerman\Worker::forkWorkersForLinux() +#7 /www/Workerman-DNS/vendor/workerman/workerman/Worker.php(557): Workerman\Worker::forkWorkers() +#8 /www/Workerman-DNS/start.php(208): Workerman\Worker::runAll() +#9 {main} +2024-01-30 14:35:57 pid:20231 worker[none:20232] exit with status 64000 +2024-01-30 14:36:40 pid:20231 Workerman[start.php] stopping ... +2024-01-30 14:36:40 pid:20231 Workerman[start.php] has been stopped +2024-01-30 14:36:45 pid:20379 Workerman[start.php] start in DEBUG mode +2024-01-30 14:39:24 pid:20379 Workerman[start.php] stopping ... +2024-01-30 14:39:24 pid:20379 Workerman[start.php] has been stopped +2024-01-30 14:39:24 pid:20664 Workerman[start.php] start in DEBUG mode +2024-01-30 14:40:04 pid:20664 Workerman[start.php] stopping ... +2024-01-30 14:40:04 pid:20664 Workerman[start.php] has been stopped +2024-01-30 14:40:05 pid:20769 Workerman[start.php] start in DEBUG mode +2024-01-30 14:40:10 pid:20769 Workerman[start.php] stopping ... +2024-01-30 14:40:10 pid:20769 Workerman[start.php] has been stopped +2024-01-30 14:40:52 pid:20903 Workerman[start.php] start in DEBUG mode +2024-01-30 14:57:37 pid:20903 Workerman[start.php] stopping ... +2024-01-30 14:57:37 pid:20903 Workerman[start.php] has been stopped +2024-01-30 14:57:38 pid:22573 Workerman[start.php] start in DEBUG mode +2024-01-30 14:58:21 pid:22573 Workerman[start.php] stopping ... +2024-01-30 14:58:21 pid:22573 Workerman[start.php] has been stopped +2024-01-30 14:58:22 pid:22698 Workerman[start.php] start in DEBUG mode +2024-01-30 15:05:04 pid:22698 Workerman[start.php] stopping ... +2024-01-30 15:05:04 pid:22698 Workerman[start.php] has been stopped +2024-01-30 15:05:05 pid:23387 Workerman[start.php] start in DEBUG mode +2024-01-30 15:05:49 pid:23387 Workerman[start.php] stopping ... +2024-01-30 15:05:49 pid:23387 Workerman[start.php] has been stopped +2024-01-30 15:05:50 pid:23494 Workerman[start.php] start in DEBUG mode +2024-01-30 15:07:52 pid:23494 Workerman[start.php] stopping ... +2024-01-30 15:07:52 pid:23494 Workerman[start.php] has been stopped +2024-01-30 15:07:53 pid:23741 Workerman[start.php] start in DEBUG mode +2024-01-30 15:07:58 pid:23741 Workerman[start.php] stopping ... +2024-01-30 15:07:58 pid:23741 Workerman[start.php] has been stopped +2024-01-30 15:07:58 pid:23769 Workerman[start.php] start in DEBUG mode +2024-01-30 15:08:05 pid:23769 Workerman[start.php] stopping ... +2024-01-30 15:08:05 pid:23769 Workerman[start.php] has been stopped +2024-01-30 15:08:06 pid:23825 Workerman[start.php] start in DEBUG mode +2024-01-30 15:16:03 pid:23825 Workerman[start.php] stopping ... +2024-01-30 15:16:03 pid:23825 Workerman[start.php] has been stopped +2024-01-30 16:25:24 pid:30921 Workerman[start.php] start in DEBUG mode +2024-01-30 16:25:31 pid:30921 Workerman[start.php] stopping ... +2024-01-30 16:25:31 pid:30921 Workerman[start.php] has been stopped diff --git a/vendor/workerman/workerman/Protocols/Dns.php b/vendor/workerman/workerman/Protocols/Dns.php index 134c880..9b8f49e 100644 --- a/vendor/workerman/workerman/Protocols/Dns.php +++ b/vendor/workerman/workerman/Protocols/Dns.php @@ -10,6 +10,15 @@ namespace Workerman\Protocols; class Dns { + public static function send($response,$query,$info){ + $response=hex2bin($response); + $traffic=strlen($response); + $info=json_decode($info); + #var_dump($info); + #出流量统计 + #您也可以在此处保存$response,下一次通过raw类型实现快速缓存相应. + return $response; + } /** * 检查包的完整性 * 如果能够得到包长,则返回包的在buffer中的长度,否则返回0继续等待数据 @@ -19,8 +28,7 @@ class Dns */ public static function input($buffer) { - - return 200; + return 512; } /** @@ -34,6 +42,39 @@ class Dns $buffer=json_decode($buffer); $type=$buffer->type; switch($type){ + case 'raw': + return Dns::send($buffer->detail,$buffer->query,$buffer->info); + break; + case 'flag': + if($buffer->flag=='NXDOMAIN'){ + $status='8183'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0001'; + /** + * NXDOMAIN应当返回SOA记录,主要内容是TTL,LDNS会在SOA的TTL到期前缓存该FLAG,否则会被LDNS递归时拒绝 + */ + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + }elseif($buffer->flag=='SERVFAIL'){ + $status='8182'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0000'; + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + }elseif($buffer->flag=='REFUSE'){ + $status='8185'; + $questions='0001'; + $AnswerRRs='0000'; + $AuthorityRRs='0000'; + $AdditionalRRs='0000'; + $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query; + return Dns::send($response,$buffer->query,$buffer->info); + } + break; case 'A': $type='0001'; #$lenth='0004'; @@ -150,7 +191,7 @@ class Dns $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); + return Dns::send($response,$buffer->query,$buffer->info); break; case 'CNAME+AAAA': @@ -201,14 +242,14 @@ class Dns $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); + return Dns::send($response,$buffer->query,$buffer->info); break; case 'SOA': $type='0006'; $ns=$buffer->detail; $ns=json_decode( json_encode( $ns),true); - if($ns['type']=='none'){ + if($ns['type']=='auto'){ $Rns=dns_get_record($ns['name'],DNS_SOA); $Rns=$Rns[0]; $ns=$Rns; @@ -282,24 +323,31 @@ class Dns 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; + $ns=json_decode( json_encode( $ns),true); + var_dump($ns); + + if($ns['type']=='auto'){ + $ns=$ns['name']; + $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; + } + } }else{ - $url=$domin; + $qname=$ns['qname']; } - }else{ - $url=$domin; - } - } - $nss=explode('.',$ns['mname']); $detail=''; foreach($nss as $part){ @@ -340,7 +388,7 @@ class Dns $answer=''; $answer=$answer.$qname.$type.'0001'.$ttl.$lenth.$detail; $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer; - return hex2bin($response); + return Dns::send($response,$buffer->query,$buffer->info); break; } $ttl=str_pad(dechex($buffer->ttl),8,"0",STR_PAD_LEFT); @@ -358,7 +406,8 @@ class Dns $answer=$answer.'C00C'.$type.'0001'.$ttl.$rlenth.$c; } $response=$buffer->id.$status.$questions.$AnswerRRs.$AuthorityRRs.$AdditionalRRs.$buffer->query.$answer; - return hex2bin($response); + $traffic=strlen(hex2bin($response)); + return Dns::send($response,$buffer->query,$buffer->info); } /** @@ -369,12 +418,27 @@ class Dns */ public static function decode($buffer) { - /** - $data=bin2hex($buffer); - echo $data; - $id=substr($data,0,4); - $flag=substr($data,5,4); - $type=substr($data,-8,4); + $traffic=strlen($buffer);#接收流量 + $data=bin2hex($buffer); + $id=substr($data,0,4); + $flag=substr($data,4,4); + $questions=substr($data,8,4); + $answerRRs=substr($data,12,4); + $authorityRRs=substr($data,16,4); + $additionalRRs=substr($data,20,4); + $startbyte=24; + $dlen=substr($data,$startbyte,2); + $startbyte=26; + $i=1; + while($dlen!='00'){ + $domain[$i]=hex2bin(substr($data,$startbyte,hexdec($dlen)*2)); + $startbyte=$startbyte+(hexdec($dlen)*2); + $dlen=substr($data,$startbyte,2); + $startbyte=$startbyte+2; + $i++; + } + $realname=join(".",$domain); + $type=substr($data,$startbyte,4); switch($type){ case '0001': $type='A'; @@ -401,71 +465,10 @@ class Dns $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"; - $data=bin2hex($buffer); - $id=substr($data,0,4); -$flag=substr($data,4,4); -$questions=substr($data,8,4); -$answerRRs=substr($data,12,4); -$authorityRRs=substr($data,16,4); -$additionalRRs=substr($data,20,4); -$startbyte=24; -$dlen=substr($data,$startbyte,2); -$startbyte=26; -$i=1; -while($dlen!='00'){ -$domain[$i]=hex2bin(substr($data,$startbyte,hexdec($dlen)*2)); -$startbyte=$startbyte+(hexdec($dlen)*2); -$dlen=substr($data,$startbyte,2); -$startbyte=$startbyte+2; -$i++; -} -$realname=join(".",$domain); -$type=substr($data,$startbyte,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; -} -$query=substr($data,24,$startbyte-16); + $query=substr($data,24,$startbyte-16); - $returndata= json_encode(array('type' => $type, 'name' => "$realname", 'id'=>"$id", 'query'=>"$query")); + $returndata= json_encode(array('type' => $type, 'name' => "$realname", 'id'=>"$id", 'query'=>"$query",'traffic'=>$traffic)); return $returndata; }