stardothosting / dns
ReactPHP 的异步 DNS 解析器
Requires
- php: >=5.3.0
- react/cache: ^1.0 || ^0.6 || ^0.5
- react/event-loop: ^1.0 || ^0.5
- react/promise: ^2.7 || ^1.2.1
- react/promise-timer: ^1.2
Requires (Dev)
- clue/block-react: ^1.2
- phpunit/phpunit: ^7.0 || ^6.4 || ^5.7 || ^4.8.35
This package is auto-updated.
Last update: 2024-09-05 22:32:27 UTC
README
ReactPHP 的异步 DNS 解析器 ReactPHP。
DNS 组件的主要目的是提供异步 DNS 解析。然而,它实际上是一个用于处理 DNS 消息的工具包,可以很容易地用来创建 DNS 服务器。
目录
基本用法
最基本的用法是通过解析器工厂创建一个解析器。你需要提供的只是域名服务器,然后你就可以开始解析名称了,宝贝!
$loop = React\EventLoop\Factory::create(); $config = React\Dns\Config\Config::loadSystemConfigBlocking(); $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8'; $factory = new React\Dns\Resolver\Factory(); $dns = $factory->create($server, $loop); $dns->resolve('igor.io')->then(function ($ip) { echo "Host: $ip\n"; }); $loop->run();
请参阅第一个示例。
可以使用 Config
类加载系统默认配置。这是一个可能访问文件系统并阻塞的操作。因此,理想情况下,应该在循环开始之前只执行一次,而不是在运行时重复执行。请注意,如果无法加载系统配置,该类可能会返回一个 空 配置。因此,如果你找不到任何配置,你可能需要像上面那样应用默认的域名服务器。
请注意,工厂在创建解析器实例时仅从文件系统中加载一次主机文件。因此,理想情况下,应该在循环开始之前只执行一次,而不是在运行时重复执行。
但还有更多。
缓存
你可以通过将解析器配置为使用 CachedExecutor
来缓存结果
$loop = React\EventLoop\Factory::create(); $config = React\Dns\Config\Config::loadSystemConfigBlocking(); $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8'; $factory = new React\Dns\Resolver\Factory(); $dns = $factory->createCached($server, $loop); $dns->resolve('igor.io')->then(function ($ip) { echo "Host: $ip\n"; }); ... $dns->resolve('igor.io')->then(function ($ip) { echo "Host: $ip\n"; }); $loop->run();
如果第一次调用在第二次调用之前返回,则只会执行一个查询。第二个结果将从一个内存缓存中提供。这对于长时间运行的脚本非常有用,其中必须多次查找相同的域名。
请参阅第三个示例。
自定义缓存适配器
默认情况下,上述将使用内存缓存。
你还可以指定一个自定义的 CacheInterface
实现来处理记录缓存
$cache = new React\Cache\ArrayCache(); $loop = React\EventLoop\Factory::create(); $factory = new React\Dns\Resolver\Factory(); $dns = $factory->createCached('8.8.8.8', $loop, $cache);
请参阅维基百科中可能的缓存实现。
ResolverInterface
resolve()
可以使用 resolve(string $domain): PromiseInterface<string,Exception>
方法将给定的 $domain 名称解析为单个 IPv4 地址(类型 A
查询)。
$resolver->resolve('reactphp.org')->then(function ($ip) { echo 'IP for reactphp.org is ' . $ip . PHP_EOL; });
这是本包中的主要方法之一。它向你的 DNS 服务器发送一个针对给定 $domain 名称的 DNS 查询,并在成功时返回单个 IP 地址。
如果 DNS 服务器发送的 DNS 响应消息包含此查询的多个 IP 地址,它将随机从响应中选取一个 IP 地址。如果你想获取完整的 IP 地址列表或想发送不同类型的查询,你应该使用 resolveAll()
方法。
如果 DNS 服务器发送的 DNS 响应消息指示错误代码,此方法将拒绝并抛出 RecordNotFoundException
。你可以使用其消息和代码来检查响应代码。
如果 DNS 通信失败并且服务器没有返回有效的响应消息,则此消息将拒绝并抛出 Exception
。
可以通过取消其挂起的承诺来取消待处理的 DNS 查询,如下所示
$promise = $resolver->resolve('reactphp.org'); $promise->cancel();
resolveAll()
可以使用 resolveAll(string $host, int $type): PromiseInterface<array,Exception>
方法来解析给定 $domain 名称和查询 $type 的所有记录值。
$resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) { echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; }); $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; });
这是本包中的主要方法之一。它向您的 DNS 服务器发送针对给定 $domain 名称的 DNS 查询,并在成功时返回一个包含所有记录值的列表。
如果 DNS 服务器发送包含一个或多个该查询记录的 DNS 响应消息,则将返回一个包含所有记录值的列表。您可以使用 Message::TYPE_*
常量来控制要发送哪种类型的查询。请注意,此方法始终返回记录值列表,但每种记录值类型取决于查询类型。例如,它返回类型 A
查询的 IPv4 地址,类型 AAAA
查询的 IPv6 地址,类型 NS
、CNAME
和 PTR
查询的主机名以及其他查询的结构化数据。有关更多详细信息,请参阅 Record
文档。
如果 DNS 服务器发送的 DNS 响应消息指示错误代码,此方法将拒绝并抛出 RecordNotFoundException
。你可以使用其消息和代码来检查响应代码。
如果 DNS 通信失败并且服务器没有返回有效的响应消息,则此消息将拒绝并抛出 Exception
。
可以通过取消其挂起的承诺来取消待处理的 DNS 查询,如下所示
$promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA); $promise->cancel();
高级用法
UdpTransportExecutor
可以使用 UdpTransportExecutor
通过 UDP 传输发送 DNS 查询。
这是主要发送 DNS 查询到您的 DNS 服务器并由 Resolver
内部用于实际消息传输的类。
对于更高级的用法,可以直接使用此类。以下示例查找 igor.io
的 IPv6
地址。
$loop = Factory::create(); $executor = new UdpTransportExecutor('8.8.8.8:53', $loop); $executor->query( new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) )->then(function (Message $message) { foreach ($message->answers as $answer) { echo 'IPv6: ' . $answer->data . PHP_EOL; } }, 'printf'); $loop->run();
另请参阅第四个示例。
请注意,此执行器不实现超时,因此您很可能会将其与 TimeoutExecutor
结合使用,如下所示
$executor = new TimeoutExecutor( new UdpTransportExecutor($nameserver, $loop), 3.0, $loop );
还请注意,此执行器使用不可靠的 UDP 传输,并且不实现任何重试逻辑,因此您很可能会将其与 RetryExecutor
结合使用,如下所示
$executor = new RetryExecutor( new TimeoutExecutor( new UdpTransportExecutor($nameserver, $loop), 3.0, $loop ) );
请注意,此执行器完全异步,因此允许您并发执行任意数量的查询。您可能需要限制应用程序中的并发查询数量,否则您可能会在解析器端遇到速率限制和封禁。对于许多常见应用程序,当第一个查询仍在挂起时,您可能希望避免发送相同的查询,因此您可能需要将其与 CoopExecutor
结合使用,如下所示
$executor = new CoopExecutor( new RetryExecutor( new TimeoutExecutor( new UdpTransportExecutor($nameserver, $loop), 3.0, $loop ) ) );
内部,此类使用 PHP 的 UDP 套接字,并且出于组织原因没有利用 react/datagram,以避免两个包之间的循环依赖。高级组件应利用数据报组件,而不是从头实现此套接字逻辑。
TcpTransportExecutor
可以使用 TcpTransportExecutor
类通过 TCP/IP 流传输发送 DNS 查询。
这是发送 DNS 查询到您的 DNS 服务器的几个主要类之一。
对于更高级的用法,可以直接使用此类。以下示例查找 reactphp.org
的 IPv6
地址。
$loop = Factory::create(); $executor = new TcpTransportExecutor('8.8.8.8:53', $loop); $executor->query( new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) )->then(function (Message $message) { foreach ($message->answers as $answer) { echo 'IPv6: ' . $answer->data . PHP_EOL; } }, 'printf'); $loop->run();
另请参阅示例 #92。
请注意,此执行器不实现超时,因此您很可能会将其与 TimeoutExecutor
结合使用,如下所示
$executor = new TimeoutExecutor( new TcpTransportExecutor($nameserver, $loop), 3.0, $loop );
与 UdpTransportExecutor
不同,此类使用可靠的 TCP/IP 传输,因此您不必实现任何重试逻辑。
请注意,此执行器完全异步,因此允许您并发执行查询。第一个查询将建立与DNS服务器的TCP/IP套接字连接,该连接将保持开放一段时间。额外的查询将自动重新使用到DNS服务器的现有套接字连接,将在单个连接上管道多个请求,并将空闲连接保持开放一段时间。如果您只偶尔发送查询,初始TCP/IP连接的开销可能会造成轻微的延迟 - 当您通过现有连接发送更多的并发查询时,它会变得越来越高效,并避免像基于UDP的执行器那样创建许多并发套接字。您可能仍然想限制您的应用程序中(并发)查询的数量,或者您可能面临解析器端的速度限制和禁令。对于许多常见应用,您可能希望在第一个查询仍然挂起时避免发送相同的查询多次,因此您可能会想结合使用像这样的CoopExecutor
$executor = new CoopExecutor( new TimeoutExecutor( new TcpTransportExecutor($nameserver, $loop), 3.0, $loop ) );
内部,此类使用PHP的TCP/IP套接字,并且出于组织原因不利用react/socket,以避免这两个包之间的循环依赖。高级组件应该利用套接字组件,而不是从头开始重新实现此套接字逻辑。
SelectiveTransportExecutor
SelectiveTransportExecutor
类可用于通过UDP或TCP/IP流传输发送DNS查询。
此类将自动选择正确的传输协议来向您的DNS服务器发送DNS查询。它将始终尝试通过更高效的UDP传输发送。如果此查询产生与大小相关的问题(消息截断),它将尝试通过流式TCP/IP传输重新发送。
对于更高级的用法,可以直接使用此类。以下示例查找 reactphp.org
的 IPv6
地址。
$executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor); $executor->query( new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) )->then(function (Message $message) { foreach ($message->answers as $answer) { echo 'IPv6: ' . $answer->data . PHP_EOL; } }, 'printf');
请注意,此执行器仅实现了为给定DNS查询选择正确传输的逻辑。实现正确的传输逻辑、实现超时和任何重试逻辑由给定的执行器负责,有关更多详细信息,请参阅UdpTransportExecutor
和TcpTransportExecutor
。
请注意,此执行器完全异步,因此允许您并发执行任意数量的查询。您可能需要限制应用程序中的并发查询数量,否则您可能会在解析器端遇到速率限制和封禁。对于许多常见应用程序,当第一个查询仍在挂起时,您可能希望避免发送相同的查询,因此您可能需要将其与 CoopExecutor
结合使用,如下所示
$executor = new CoopExecutor( new SelectiveTransportExecutor( $datagramExecutor, $streamExecutor ) );
HostsFileExecutor
请注意,上述UdpTransportExecutor
类始终执行实际的DNS查询。如果您还想考虑hosts文件中的条目,您可以使用此代码
$hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking(); $executor = new UdpTransportExecutor('8.8.8.8:53', $loop); $executor = new HostsFileExecutor($hosts, $executor); $executor->query( new Query('localhost', Message::TYPE_A, Message::CLASS_IN) );
安装
推荐通过Composer安装此库。 Composer新手?
此项目遵循SemVer。这将安装最新的支持版本
$ composer require react/dns:^1.2
有关版本升级的详细信息,请参阅CHANGELOG。
此项目旨在在任意平台上运行,因此不要求任何PHP扩展,并支持在旧的PHP 5.3通过当前的PHP 7+和HHVM上运行。强烈建议使用PHP 7+来运行此项目。
测试
要运行测试套件,您首先需要克隆此仓库,然后通过Composer安装所有依赖项
$ composer install
要运行测试套件,请转到项目根目录并运行
$ php vendor/bin/phpunit
测试套件还包括一些依赖于稳定互联网连接的功能集成测试。如果您不想运行这些测试,可以简单地像这样跳过
$ php vendor/bin/phpunit --exclude-group internet
许可证
MIT,请参阅LICENSE文件。