一个用于抽象DNS查询的PHP库

5.0.1 2022-03-14 15:14 UTC

README

Build Status Total Downloads Coverage Status License Scrutinizer Code Quality PHP CI

PHP-DNS:PHP中的DNS抽象

用例

如果以下情况适用于您,那么这个库可能适合您:

  • 您希望能够在本地上或通过HTTPS查询DNS记录
  • 您想要了解您的DNS查找的可观察性
  • 您想要在实现中轻松测试/模拟
  • 您想要尝试不同的DNS真理来源
  • 您想要轻松扩展它或贡献以获取更多所需行为!

安装

composer require remotelyliving/php-dns

使用

基本解析器可以在 src/Resolvers 中找到

这些解析器至少实现了 Resolvers\Interfaces\DNSQuery 接口

  • GoogleDNS(使用GoogleDNS DNS over HTTPS API)
  • CloudFlare(使用CloudFlare DNS over HTTPS API)
  • LocalSystem(使用本地PHP dns查询函数)
  • Dig(每个实例可以使用特定的名称服务器,但需要主机操作系统安装dig)。基于 Spatie DNS
$resolver = new Resolvers\GoogleDNS();

// can query via convenience methods
$records = $resolver->getARecords('google.com'); // returns a collection of DNS A Records

// can also query by any RecordType.
$moreRecords = $resolver->getRecords($hostname, DNSRecordType::TYPE_AAAA);

// can query to see if any resolvers find a record or type.
$resolver->hasRecordType($hostname, $type) // true | false
$resolver->hasRecord($record) // true | false

// This becomes very powerful when used with the Chain Resolver

链式解析器

链式解析器可以用于遍历DNS解析器,直到找到答案。您首先传递的是调用序列中首先尝试的解析器。它实现了与其他解析器相同的 DNSQuery 接口,但还包含在 Chain 接口中找到的附加功能集。

所以就像这样

$chainResolver = new Chain($cloudFlareResolver, $googleDNSResolver, $localDNSResolver);

这将首先调用GoogleDNS解析器,如果没有找到答案,它将继续调用LocalSystem解析器。默认调用策略是“找到第一个”,即 Resolvers\Interfaces\Chain::withFirstResults(): Chain

您还可以通过 Resolvers\Interfaces\Chain::randomly(): Chain 随机选择链中首先尝试的解析器。示例

$foundRecord = $chainResolver->randomly()->getARecords('facebook.com')->pickFirst();

上面的代码将随机通过解析器,直到找到任何非空答案或耗尽链的顺序。

最后,成本最高的是 Resolvers\Interfaces\Chain::withAllResults(): ChainResolvers\Interfaces\Chain::withConsensusResults(): Chain。所有结果都是来自不同来源的合并,如果您想查看所有可用的内容很有用。共识结果将是来自来源到来源的公共结果。

src/Resolvers/Interfaces

// returns the first non empty result set
$chainResolver->withFirstResults()->getARecords('facebook.com'); 

// returns the first non empty result set from a randomly selected resolver
$chainResolver->randomly()->getARecords('facebook.com'); 

// returns only common results between resolvers
$chainResolver->withConsensusResults()->getARecords('facebook.com'); 

// returns all collective responses with duplicates filtered out
$chainResolver->withAllResults()->getARecords('facebook.com'); 

缓存解析器

如果您使用PSR6缓存实现,请随意将您想要使用的任何解析器包装在缓存解析器中。它将获取记录的最低TTL并将其用作缓存TTL。您可以通过在构造函数中设置缓存TTL来覆盖该行为。

$cachedResolver = new Resolvers\Cached($cache, $resolverOfChoice);
$cachedResolver->getRecords('facebook.com'); // get from cache if possible or falls back to the wrapped resolver and caches the returned records

如果您不希望缓存空结果答案,您可以通过这个附加选项调用

$cachedResolver->withEmptyResultCachingDisabled()->getARecords('facebook.com');

实体

请查看 src/Entities 以查看您可以通过什么来查询并接收。

对于具有额外类型数据的记录,例如SOA、TXT、MX、CNAME和NS,Entities\DNSRecord 上有一个数据属性,将设置正确的类型。

逆向查找

这通过一个单独的 ReverseDNSQuery 接口提供,因为它对于每种类型的DNS解析器都不常见或可用。只有 LocalSystem 解析器实现了它。

可观察性

所有提供的解析器都具备添加订阅者和监听器的功能。它们与symfony/event-dispatcher直接兼容。

所有事件都可以在这里找到:src/Observability/Events

关于订阅者可以用它们做什么,这里有很好的了解:src/Observability/Subscribers

你可以决定将事件流到哪里,是日志还是其他地方。所有事件都可以安全地使用json_encode()进行编码,无需额外解析。

如果你想看看如何轻松地配置所有这些,请查看repl引导

日志记录

所有提供的解析器都实现了Psr\Log\LoggerAwareInterface接口,并在运行时设置默认的NullLogger

篡改

查看Makefile,了解你可以做的一切!

有一个非常基本的REPL实现,它已经为你连接了一些解析器,并将事件管道传输到stderr和stdout

make repl

christians-mbp:php-dns chthomas$ make repl
Psy Shell v0.9.9 (PHP 7.2.8 — cli) by Justin Hileman
>>> ls
Variables: $cachedResolver, $chainResolver, $cloudFlareResolver, $googleDNSResolver, $IOSubscriber, $localSystemResolver, $stdErr, $stdOut
>>> $records = $chainResolver->getARecords('facebook.com')
{
    "dns.query.profiled": {
        "elapsedSeconds": 0.21915197372436523,
        "transactionName": "CloudFlare:facebook.com.:A",
        "peakMemoryUsage": 9517288
    }
}
{
    "dns.queried": {
        "resolver": "CloudFlare",
        "hostname": "facebook.com.",
        "type": "A",
        "records": [
            {
                "hostname": "facebook.com.",
                "type": "A",
                "TTL": 224,
                "class": "IN",
                "IPAddress": "31.13.71.36"
            }
        ],
        "empty": false
    }
}
=> RemotelyLiving\PHPDNS\Entities\DNSRecordCollection {#2370}
>>> $records->pickFirst()->toArray()
=> [
     "hostname" => "facebook.com.",
     "type" => "A",
     "TTL" => 224,
     "class" => "IN",
     "IPAddress" => "31.13.71.36",
   ]
>>> $records = $chainResolver->withConsensusResults()->getRecords('facebook.com', 'TXT')
{
    "dns.query.profiled": {
        "elapsedSeconds": 0.023031949996948242,
        "transactionName": "CloudFlare:facebook.com.:TXT",
        "peakMemoryUsage": 9615080
    }
}
{
    "dns.queried": {
        "resolver": "CloudFlare",
        "hostname": "facebook.com.",
        "type": "TXT",
        "records": [
            {
                "hostname": "facebook.com.",
                "type": "TXT",
                "TTL": 9136,
                "class": "IN",
                "data": "v=spf1 redirect=_spf.facebook.com"
            }
        ],
        "empty": false
    }
}
{
    "dns.query.profiled": {
        "elapsedSeconds": 0.23299598693847656,
        "transactionName": "GoogleDNS:facebook.com.:TXT",
        "peakMemoryUsage": 9615080
    }
}
{
    "dns.queried": {
        "resolver": "GoogleDNS",
        "hostname": "facebook.com.",
        "type": "TXT",
        "records": [
            {
                "hostname": "facebook.com.",
                "type": "TXT",
                "TTL": 21121,
                "class": "IN",
                "data": "v=spf1 redirect=_spf.facebook.com"
            }
        ],
        "empty": false
    }
}
{
    "dns.query.profiled": {
        "elapsedSeconds": 0.0018258094787597656,
        "transactionName": "LocalSystem:facebook.com.:TXT",
        "peakMemoryUsage": 9615080
    }
}
{
    "dns.queried": {
        "resolver": "LocalSystem",
        "hostname": "facebook.com.",
        "type": "TXT",
        "records": [
            {
                "hostname": "facebook.com.",
                "type": "TXT",
                "TTL": 25982,
                "class": "IN",
                "data": "v=spf1 redirect=_spf.facebook.com"
            }
        ],
        "empty": false
    }
}
=> RemotelyLiving\PHPDNS\Entities\DNSRecordCollection {#2413}
>>> $records->pickFirst()->getData()->getValue()
=> "v=spf1 redirect=_spf.facebook.com"
>>>