raimx/dns

ReactPHP 的异步 DNS 解析器

维护者

详细信息

github.com/RaiMX/dns

源代码

v1.1.0 2019-07-18 09:47 UTC

README

Build Status

异步 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拒绝。

可以通过取消其挂起的Promise来取消挂起的DNS查询,如下所示:

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

$promise->cancel();

resolveAll()

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

$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地址,类型为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.ioIPv6地址。

$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,纯粹是为了避免两个包之间的循环依赖。高级组件应利用数据报组件而不是从头实现此套接字逻辑。

HostsFileExecutor

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

$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.1

有关版本升级的详细信息,请参阅变更日志

此项目旨在在任何平台上运行,因此不需要任何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文件

参考