react/dns

ReactPHP 的异步 DNS 解析器

维护者

详细信息

github.com/reactphp/dns

源代码

问题

资助包维护!
开放集体

安装数: 42 704 969

依赖者: 81

建议者: 0

安全性: 0

星级: 484

关注者: 20

分支: 60

开放问题: 8

v1.13.0 2024-06-13 14:18 UTC

README

CI status installs on Packagist

ReactPHP 的异步 DNS 解析器。

开发版本:此分支包含即将推出的 v3 版本的代码。要查看当前稳定版 v1 的代码,请查看 1.x 分支

即将推出的 v3 版本将是此包的发展方向。然而,我们仍将积极支持 v1,以帮助那些尚未升级到最新版本的用户。有关更多详细信息,请参阅 安装说明

DNS 组件的主要目的是提供异步 DNS 解析。然而,它实际上是一个用于处理 DNS 消息的工具包,可以很容易地用于创建 DNS 服务器。

目录

基本用法

最基本的用法是只需通过解析器工厂创建一个解析器。您只需要提供一台域名服务器,然后您就可以开始解析名称了!

$config = React\Dns\Config\Config::loadSystemConfigBlocking();
if (!$config->nameservers) {
    $config->nameservers[] = '8.8.8.8';
}

$factory = new React\Dns\Resolver\Factory();
$dns = $factory->create($config);

$dns->resolve('igor.io')->then(function ($ip) {
    echo "Host: $ip\n";
});

请参阅 第一个示例

可以使用 Config 类来加载系统默认配置。这可能是一种会访问文件系统并阻塞的操作。因此,理想情况下,此方法应在循环开始之前仅执行一次,而不是在运行时重复执行。请注意,如果无法加载系统配置,则此类可能返回一个 空的 配置。因此,如果您找不到任何配置,您可能希望像上面那样应用默认域名服务器。

请注意,工厂在创建解析器实例时,一次从文件系统中加载 hosts 文件。因此,理想情况下,此方法应在循环开始之前仅执行一次,而不是在运行时重复执行。

但这不仅仅如此。

缓存

您可以通过配置解析器以使用 CachedExecutor 来缓存结果

$config = React\Dns\Config\Config::loadSystemConfigBlocking();
if (!$config->nameservers) {
    $config->nameservers[] = '8.8.8.8';
}

$factory = new React\Dns\Resolver\Factory();
$dns = $factory->createCached($config);

$dns->resolve('igor.io')->then(function ($ip) {
    echo "Host: $ip\n";
});

...

$dns->resolve('igor.io')->then(function ($ip) {
    echo "Host: $ip\n";
});

如果第一个调用在第二个之前返回,则只执行一个查询。第二个结果将从内存缓存中提供。这对于需要多次查找相同主机名的长时间运行脚本非常有用。

请参阅 第三个示例

自定义缓存适配器

默认情况下,以上将使用内存缓存。

您还可以指定一个自定义缓存,实现 CacheInterface 以处理记录缓存

$cache = new React\Cache\ArrayCache();
$factory = new React\Dns\Resolver\Factory();
$dns = $factory->createCached('8.8.8.8', null, $cache);

请参阅维基百科了解可能的 缓存实现

ResolverInterface

resolve()

可以使用 resolve(string $domain): PromiseInterface<string> 方法将给定的 $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

可以通过取消其挂起的Promise来取消挂起的DNS查询。

$promise = $resolver->resolve('reactphp.org');

$promise->cancel();

resolveAll()

resolveAll(string $host, int $type): PromiseInterface<array>方法可以用来解析给定域名和查询类型的所有记录值。

$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服务器发送DNS查询,并在成功时返回所有记录值列表。

如果DNS服务器发送的DNS响应消息包含针对此查询的一个或多个记录,它将返回一个包含所有记录值的列表。您可以使用Message::TYPE_*常量来控制发送哪种类型的查询。请注意,此方法始终返回记录值列表,但每种记录值类型取决于查询类型。例如,它返回类型为A的查询的IPv4地址,类型为AAAA的查询的IPv6地址,类型为NSCNAMEPTR的查询的域名,以及其他查询的结构化数据。有关更多详细信息,请参阅Record文档。

如果DNS服务器发送的DNS响应消息指示错误代码,此方法将拒绝并返回RecordNotFoundException。可以使用其消息和代码来检查响应代码。

如果DNS通信失败,服务器没有响应有效的响应消息,此消息将拒绝并返回Exception

可以通过取消其挂起的Promise来取消挂起的DNS查询。

$promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA);

$promise->cancel();

高级用法

UdpTransportExecutor

可以使用UdpTransportExecutor通过UDP传输发送DNS查询。

这是发送DNS查询到您的DNS服务器的主体类,并由Resolver内部用于实际消息传输。

对于更高级的用法,可以直接使用此类。以下示例查找igor.io的IPv6地址。

$executor = new UdpTransportExecutor('8.8.8.8:53');

$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');

另请参阅第四个示例

请注意,此执行器不实现超时,因此您可能希望将其与TimeoutExecutor一起使用,如下所示

$executor = new TimeoutExecutor(
    new UdpTransportExecutor($nameserver),
    3.0
);

此外,请注意,此执行器使用不可靠的UDP传输,并且没有实现任何重试逻辑,因此您可能希望将其与RetryExecutor一起使用,如下所示

$executor = new RetryExecutor(
    new TimeoutExecutor(
        new UdpTransportExecutor($nameserver),
        3.0
    )
);

请注意,此执行器完全异步,因此您可以使用它并发地执行任意数量的查询。您可能需要在您的应用程序中限制并发查询的数量,否则您可能会遇到速率限制和解析器端禁用。对于许多常见应用,您可能希望避免在第一个查询仍在挂起时发送相同的查询,因此您可能希望将其与CoopExecutor一起使用,如下所示

$executor = new CoopExecutor(
    new RetryExecutor(
        new TimeoutExecutor(
            new UdpTransportExecutor($nameserver),
            3.0
        )
    )
);

内部,此类使用PHP的UDP套接字,并且出于组织原因,没有利用react/datagram,以避免两个包之间的循环依赖。高级组件应利用数据报组件,而不是从头开始重新实现此套接字逻辑。

TcpTransportExecutor

可以使用TcpTransportExecutor类通过TCP/IP流传输发送DNS查询。

这是发送DNS查询到您的DNS服务器的主体类之一。

对于更高级的使用,可以直接使用此类。以下示例查找reactphp.orgIPv6地址。

$executor = new TcpTransportExecutor('8.8.8.8:53');

$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');

另请参阅示例#92

请注意,此执行器不实现超时,因此您可能希望将其与TimeoutExecutor一起使用,如下所示

$executor = new TimeoutExecutor(
    new TcpTransportExecutor($nameserver),
    3.0
);

UdpTransportExecutor不同,此类使用可靠的TCP/IP传输,因此您不一定必须实现任何重试逻辑。

请注意,此执行器完全是异步的,因此允许您并发执行查询。第一个查询将建立到DNS服务器的TCP/IP套接字连接,该连接将保持短暂开放。额外的查询将自动重用到DNS服务器的现有套接字连接,通过此单个连接管道多个请求,并保持空闲连接短暂开放。如果只发送偶尔的查询,初始TCP/IP连接开销可能会产生轻微的延迟 - 当通过现有连接发送更多并发查询时,它变得越来越高效,避免了创建像基于UDP的执行器那样的大量并发套接字。您可能仍然想要限制应用程序中(并发)查询的数量,或者您可能面临解析器端的速率限制和封禁。对于许多常见应用程序,您可能希望在第一个查询仍在挂起时避免发送相同的查询多次,因此您可能希望将其与CoopExecutor等结合使用

$executor = new CoopExecutor(
    new TimeoutExecutor(
        new TcpTransportExecutor($nameserver),
        3.0
    )
);

内部,此类使用PHP的TCP/IP套接字,并非出于对react/socket的利用,纯粹是为了组织原因,以避免两个包之间的循环依赖。高级组件应利用套接字组件而不是从头开始重新实现此套接字逻辑。

SelectiveTransportExecutor

SelectiveTransportExecutor类可用于通过UDP或TCP/IP流传输发送DNS查询。

此类将自动选择正确的传输协议向您的DNS服务器发送DNS查询。它始终尝试首先通过更高效的UDP传输发送。如果此查询产生与大小相关的问题(截断的消息),它将尝试通过流式TCP/IP传输重试。

对于更高级的使用,可以直接使用此类。以下示例查找reactphp.orgIPv6地址。

$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查询的正确传输逻辑。实现正确的传输逻辑、实现超时和任何重试逻辑留给给定的执行器,有关更多详细信息,请参阅UdpTransportExecutorTcpTransportExecutor

请注意,此执行器完全异步,因此您可以使用它并发地执行任意数量的查询。您可能需要在您的应用程序中限制并发查询的数量,否则您可能会遇到速率限制和解析器端禁用。对于许多常见应用,您可能希望避免在第一个查询仍在挂起时发送相同的查询,因此您可能希望将其与CoopExecutor一起使用,如下所示

$executor = new CoopExecutor(
    new SelectiveTransportExecutor(
        $datagramExecutor,
        $streamExecutor
    )
);

HostsFileExecutor

请注意,上述UdpTransportExecutor类始终执行实际的DNS查询。如果您还希望考虑主机文件中的条目,可以使用以下代码

$hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking();

$executor = new UdpTransportExecutor('8.8.8.8:53');
$executor = new HostsFileExecutor($hosts, $executor);

$executor->query(
    new Query('localhost', Message::TYPE_A, Message::CLASS_IN)
);

安装

安装此库的推荐方法是通过Composer初识Composer?

一旦发布,此项目将遵循SemVer。目前,这将安装最新开发版本

composer require react/dns:^3@dev

有关版本升级的详细信息,请参阅CHANGELOG

该项目旨在在任何平台上运行,因此不要求任何PHP扩展,并支持在PHP 7.1到当前PHP 8+上运行。强烈建议为此项目使用最新支持的PHP版本

测试

要运行测试套件,您首先需要克隆此仓库,然后通过Composer安装所有依赖项

composer install

要运行测试套件,请转到项目根目录并运行

vendor/bin/phpunit

测试套件还包含一些依赖于稳定互联网连接的功能集成测试。如果您不想运行这些测试,可以像这样简单地跳过

vendor/bin/phpunit --exclude-group internet

许可

麻省理工学院,请参阅许可证文件

参考