mirror of
https://github.com/danog/dns-over-https.git
synced 2024-11-30 04:19:11 +01:00
Add tests
This commit is contained in:
parent
f5cbb3a9f5
commit
e431e1014e
@ -17,7 +17,7 @@ $domains = \array_map(function ($line) {
|
||||
\array_shift($domains);
|
||||
|
||||
// Set default resolver to DNS-over-HTTPS resolver
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://cloudflare-dns.com/dns-query')]);
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]);
|
||||
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig));
|
||||
|
||||
Loop::run(function () use ($domains) {
|
||||
|
@ -24,7 +24,7 @@ $customConfigLoader = new class implements Dns\ConfigLoader {
|
||||
};
|
||||
|
||||
// Set default resolver to DNS-over-https resolver
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://cloudflare-dns.com/dns-query')], null, null, $customConfigLoader);
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')], null, null, $customConfigLoader);
|
||||
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig));
|
||||
|
||||
Loop::run(function () {
|
||||
|
23
examples/domain-fronting.php
Normal file
23
examples/domain-fronting.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . "/_bootstrap.php";
|
||||
|
||||
use Amp\Dns;
|
||||
use Amp\DoH;
|
||||
use Amp\Loop;
|
||||
use Amp\DoH\Nameserver;
|
||||
|
||||
// Set default resolver to DNS-over-https resolver
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"])]);
|
||||
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig));
|
||||
|
||||
Loop::run(function () {
|
||||
$hostname = "amphp.org";
|
||||
|
||||
try {
|
||||
pretty_print_records($hostname, yield Dns\resolve($hostname));
|
||||
} catch (Dns\DnsException $e) {
|
||||
pretty_print_error($hostname, $e);
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ use Amp\DoH;
|
||||
use Amp\Loop;
|
||||
|
||||
// Set default resolver to DNS-over-HTTPS resolver
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://cloudflare-dns.com/dns-query')]);
|
||||
$DohConfig = new DoH\DoHConfig([new DoH\Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]);
|
||||
Dns\resolver(new DoH\Rfc8484StubResolver($DohConfig));
|
||||
|
||||
Loop::run(function () {
|
||||
|
@ -11,6 +11,7 @@ use Amp\Dns\Resolver;
|
||||
use Amp\Dns\UnixConfigLoader;
|
||||
use Amp\Dns\WindowsConfigLoader;
|
||||
use Amp\Dns\Rfc1035StubResolver;
|
||||
use Amp\Dns\ConfigException;
|
||||
|
||||
final class DoHConfig
|
||||
{
|
||||
@ -29,10 +30,12 @@ final class DoHConfig
|
||||
foreach ($nameservers as $nameserver) {
|
||||
$this->validateNameserver($nameserver);
|
||||
}
|
||||
if ($resolver instanceof Rfc8484StubResolver) {
|
||||
throw new ConfigException("Can't use Rfc8484StubResolver as subresolver for Rfc8484StubResolver");
|
||||
}
|
||||
|
||||
$this->nameservers = $nameservers;
|
||||
$this->artax = $artax ?? new DefaultClient();
|
||||
|
||||
$this->cache = $cache ?? new ArrayCache(5000/* default gc interval */, 256/* size */);
|
||||
$this->configLoader = $configLoader ?? (\stripos(PHP_OS, "win") === 0
|
||||
? new WindowsConfigLoader
|
||||
|
@ -62,7 +62,7 @@ final class HttpsSocket extends Socket
|
||||
switch ($this->nameserver->getType()) {
|
||||
case Nameserver::RFC8484_GET:
|
||||
$data = $this->encoder->encode($message);
|
||||
$request = (new Request($this->nameserver->getUri().'?'.http_build_query(['dns' => base64_encode($data), 'ct' => 'application/dns-message']), "GET"))
|
||||
$request = (new Request($this->nameserver->getUri().'?'.\http_build_query(['dns' => \base64_encode($data), 'ct' => 'application/dns-message']), "GET"))
|
||||
->withHeader('accept', 'application/dns-message')
|
||||
->withHeaders($this->nameserver->getHeaders());
|
||||
break;
|
||||
|
@ -23,6 +23,7 @@ use LibDNS\Records\Types\Type;
|
||||
use LibDNS\Records\Types\TypeBuilder;
|
||||
use LibDNS\Records\Types\Types;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
use Amp\Dns\DnsException;
|
||||
|
||||
/**
|
||||
* Decodes JSON DNS strings to Message objects
|
||||
@ -314,7 +315,15 @@ class JsonDecoder
|
||||
*/
|
||||
public function decode(string $result, int $requestId): Message
|
||||
{
|
||||
$result = json_decode($result, true);
|
||||
$result = \json_decode($result, true);
|
||||
if ($result === false) {
|
||||
$error = \json_last_error_msg();
|
||||
throw new DnsException("Could not decode JSON DNS payload ($error)");
|
||||
}
|
||||
if (!isset($result['Status'], $result['TC'], $result['RD'], $result['RA'])) {
|
||||
throw new DnsException('Wrong reply from server, missing required fields');
|
||||
}
|
||||
|
||||
$message = $this->messageFactory->create();
|
||||
$decodingContext = $this->decodingContextFactory->create($this->packetFactory->create());
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace Amp\DoH;
|
||||
|
||||
use Amp\Uri\InvalidDnsNameException;
|
||||
use Amp\Dns\ConfigException;
|
||||
|
||||
final class Nameserver
|
||||
{
|
||||
const RFC8484_GET = 0;
|
||||
@ -15,6 +18,12 @@ final class Nameserver
|
||||
|
||||
public function __construct(string $uri, int $type = self::RFC8484_POST, array $headers = [])
|
||||
{
|
||||
if (parse_url($uri, PHP_URL_SCHEME) !== 'https') {
|
||||
throw new ConfigException('Did not provide a valid HTTPS url!');
|
||||
}
|
||||
if (!in_array($type, [self::RFC8484_GET, self::RFC8484_POST, self::GOOGLE_JSON], true)) {
|
||||
throw new ConfigException('Invalid nameserver type provided!');
|
||||
}
|
||||
$this->uri = $uri;
|
||||
$this->type = $type;
|
||||
$this->headers = $headers;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php declare (strict_types = 1);
|
||||
/**
|
||||
* Encodes Message objects to raw network data
|
||||
*
|
||||
@ -13,7 +13,9 @@
|
||||
*/
|
||||
namespace Amp\DoH;
|
||||
|
||||
use Amp\Dns\DnsException;
|
||||
use \LibDNS\Messages\Message;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
|
||||
/**
|
||||
* Encodes Message objects to raw network data
|
||||
@ -32,12 +34,22 @@ class QueryEncoder
|
||||
*/
|
||||
public function encode(Message $message): string
|
||||
{
|
||||
return http_build_query([
|
||||
'cd' => 0, // Do not disable result validation
|
||||
'do' => 0, // Do not send me DNSSEC data
|
||||
'type' => $message->getQuestionRecords()->getRecordByIndex(0)->getType(), // Record type being requested
|
||||
'name' => implode('.', $message->getQuestionRecords()->getRecordByIndex(0)->getName()->getLabels()), // Record name being requested
|
||||
'ct' => 'application/dns-json', // Content-type of request
|
||||
]);
|
||||
if ($message->getType() !== MessageTypes::QUERY) {
|
||||
throw new DnsException('Invalid question: is not a question record');
|
||||
}
|
||||
$questions = $message->getQuestionRecords();
|
||||
if ($questions->count() === 0) {
|
||||
throw new DnsException('Invalid question: 0 question records provided');
|
||||
}
|
||||
$question = $questions->getRecordByIndex(0);
|
||||
return \http_build_query(
|
||||
[
|
||||
'cd' => 0, // Do not disable result validation
|
||||
'do' => 0, // Do not send me DNSSEC data
|
||||
'type' => $question->getType(), // Record type being requested
|
||||
'name' => implode('.', $question->getName()->getLabels()), // Record name being requested
|
||||
'ct' => 'application/dns-json', // Content-type of request
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use LibDNS\Decoder\DecoderFactory;
|
||||
use LibDNS\Messages\Message;
|
||||
|
||||
class DecodeTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Regression test for https://github.com/amphp/dns/issues/53 and other reported issues.
|
||||
*/
|
||||
public function testDecodesEmptyDomains()
|
||||
{
|
||||
$message = \hex2bin("37ed818000010005000d000005676d61696c03636f6d00000f0001c00c000f000100000dff0020000a04616c74310d676d61696c2d736d74702d696e016c06676f6f676c65c012c00c000f000100000dff0009001404616c7432c02ec00c000f000100000dff0009002804616c7434c02ec00c000f000100000dff0009001e04616c7433c02ec00c000f000100000dff00040005c02e0000020001000026b50014016c0c726f6f742d73657276657273036e6574000000020001000026b500040163c0a30000020001000026b500040164c0a30000020001000026b50004016ac0a30000020001000026b500040162c0a30000020001000026b500040161c0a30000020001000026b500040167c0a30000020001000026b50004016bc0a30000020001000026b500040165c0a30000020001000026b50004016dc0a30000020001000026b500040169c0a30000020001000026b500040166c0a30000020001000026b500040168c0a3");
|
||||
|
||||
$decoder = (new DecoderFactory)->create();
|
||||
$response = $decoder->decode($message);
|
||||
|
||||
$this->assertInstanceOf(Message::class, $response);
|
||||
}
|
||||
}
|
164
test/DoHConfigTest.php
Normal file
164
test/DoHConfigTest.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\Test;
|
||||
|
||||
use Amp\Dns\Config;
|
||||
use Amp\Dns\ConfigException;
|
||||
use Amp\DoH\DoHConfig;
|
||||
use Amp\DoH\Nameserver;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use Amp\Artax\DefaultClient;
|
||||
use Amp\Dns\Rfc1035StubResolver;
|
||||
use Amp\DoH\Rfc8484StubResolver;
|
||||
use Amp\Cache\ArrayCache;
|
||||
use Amp\Dns\WindowsConfigLoader;
|
||||
use Amp\Dns\UnixConfigLoader;
|
||||
|
||||
class DoHConfigTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param string[] $nameservers Valid server array.
|
||||
*
|
||||
* @dataProvider provideValidServers
|
||||
*/
|
||||
public function testAcceptsValidServers(array $nameservers)
|
||||
{
|
||||
$this->assertInstanceOf(DoHConfig::class, new DoHConfig($nameservers));
|
||||
}
|
||||
|
||||
public function provideValidServers()
|
||||
{
|
||||
return [
|
||||
[[new Nameserver('https://cloudflare-dns.com/dns-query')]],
|
||||
[[new Nameserver('https://cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST)]],
|
||||
[[new Nameserver('https://cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET)]],
|
||||
[[new Nameserver('https://cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON)]],
|
||||
[[new Nameserver('https://google.com/resolve', Nameserver::GOOGLE_JSON, ["host" => "dns.google.com"])]],
|
||||
[[new Nameserver('https://cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON), new Nameserver('https://google.com/resolve', Nameserver::GOOGLE_JSON, ["host" => "dns.google.com"])]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $nameservers Invalid server array.
|
||||
*
|
||||
* @dataProvider provideInvalidServers
|
||||
*/
|
||||
public function testRejectsInvalidServers(array $nameservers)
|
||||
{
|
||||
$this->expectException(ConfigException::class);
|
||||
new DoHConfig($nameservers);
|
||||
}
|
||||
|
||||
public function provideInvalidServers()
|
||||
{
|
||||
return [
|
||||
[[]],
|
||||
[[42]],
|
||||
[[null]],
|
||||
[[true]],
|
||||
[["foobar"]],
|
||||
[["foobar.com"]],
|
||||
[["127.1.1:53"]],
|
||||
[["127.1.1"]],
|
||||
[["127.1.1.1.1"]],
|
||||
[["126.0.0.5", "foobar"]],
|
||||
[["42"]],
|
||||
[["::1"]],
|
||||
[["::1:53"]],
|
||||
[["[::1]:53"]],
|
||||
[["[::1]:"]],
|
||||
[["[::1]:76235"]],
|
||||
[["[::1]:0"]],
|
||||
[["[::1]:-1"]],
|
||||
[["[::1:51"]],
|
||||
[["[::1]:abc"]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Amp\Artax\Client $client Valid artax instance
|
||||
*
|
||||
* @dataProvider provideValidArtax
|
||||
*/
|
||||
public function testAcceptsValidArtax($client)
|
||||
{
|
||||
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], $client));
|
||||
}
|
||||
|
||||
public function provideValidArtax()
|
||||
{
|
||||
return [
|
||||
[new DefaultClient()]
|
||||
[null]
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @param \Amp\Dns\Resolver $resolver Valid resolver instance
|
||||
*
|
||||
* @dataProvider provideValidResolver
|
||||
*/
|
||||
public function testAcceptsValidResolver($resolver)
|
||||
{
|
||||
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, $resolver));
|
||||
}
|
||||
|
||||
public function provideValidResolver()
|
||||
{
|
||||
return [
|
||||
[new Rfc1035StubResolver()]
|
||||
[null]
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @param $resolver any invalid resolver instance
|
||||
*
|
||||
* @dataProvider provideInvalidResolver
|
||||
*/
|
||||
public function testRejectsInvalidResolver($resolver)
|
||||
{
|
||||
$this->expectException(ConfigException::class);
|
||||
new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, $resolver);
|
||||
}
|
||||
|
||||
public function provideInvalidResolver()
|
||||
{
|
||||
return [
|
||||
[new Rfc8484StubResolver(new DoHConfig([new Nameserver('https://mozilla.cloudflare-dns.com/dns-query')]))]
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @param $configLoader Valid ConfigLoader instance
|
||||
*
|
||||
* @dataProvider provideValidConfigLoader
|
||||
*/
|
||||
public function testAcceptsValidConfigLoader($configLoader)
|
||||
{
|
||||
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, $configLoader));
|
||||
}
|
||||
|
||||
public function provideValidConfigLoader()
|
||||
{
|
||||
return [
|
||||
[new WindowsConfigLoader()]
|
||||
[new UnixConfigLoader()]
|
||||
[null]
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @param \Amp\Cache\Cache Valid cache instance
|
||||
*
|
||||
* @dataProvider provideValidCache
|
||||
*/
|
||||
public function testAcceptsValidCache($cache)
|
||||
{
|
||||
$this->assertInstanceOf(DoHConfig::class, new DoHConfig([new Nameserver('https://cloudflare-dns.com/dns-query')], null, null, null, $cache));
|
||||
}
|
||||
|
||||
public function provideValidCache()
|
||||
{
|
||||
return [
|
||||
[new ArrayCache(5000/* default gc interval */, 256/* size */)]
|
||||
[null]
|
||||
];
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\Dns\HostLoader;
|
||||
use Amp\Dns\Record;
|
||||
use Amp\Loop;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
|
||||
class HostLoaderTest extends TestCase
|
||||
{
|
||||
public function testIgnoresCommentsAndParsesBasicEntry()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$loader = new HostLoader(__DIR__ . "/data/hosts");
|
||||
$this->assertSame([
|
||||
Record::A => [
|
||||
"localhost" => "127.0.0.1",
|
||||
],
|
||||
], yield $loader->loadHosts());
|
||||
});
|
||||
}
|
||||
|
||||
public function testReturnsEmptyErrorOnFileNotFound()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$loader = new HostLoader(__DIR__ . "/data/hosts.not.found");
|
||||
$this->assertSame([], yield $loader->loadHosts());
|
||||
});
|
||||
}
|
||||
|
||||
public function testIgnoresInvalidNames()
|
||||
{
|
||||
Loop::run(function () {
|
||||
$loader = new HostLoader(__DIR__ . "/data/hosts.invalid.name");
|
||||
$this->assertSame([
|
||||
Record::A => [
|
||||
"localhost" => "127.0.0.1",
|
||||
],
|
||||
], yield $loader->loadHosts());
|
||||
});
|
||||
}
|
||||
}
|
15
test/JsonDecoderFactoryTest.php
Normal file
15
test/JsonDecoderFactoryTest.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\DoH\JsonDecoder;
|
||||
|
||||
class JsonDecoderFactoryTest extends TestCase
|
||||
{
|
||||
public function testJsonDecoderFactoryWorks()
|
||||
{
|
||||
$this->assertInstanceOf(JsonDecoder::class, (new JsonDecoderFactory)->create());
|
||||
}
|
||||
}
|
201
test/JsonDecoderTest.php
Normal file
201
test/JsonDecoderTest.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use LibDNS\Messages\Message;
|
||||
use Amp\Dns\DnsException;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
|
||||
class JsonDecoderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test decoding of valid JSON DNS payloads
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $requestId
|
||||
* @return void
|
||||
*
|
||||
* @dataProvider provideValidJsonPayloads
|
||||
*/
|
||||
public function testDecodesValidJsonPayloads(string $message, int $requestId)
|
||||
{
|
||||
$decoder = (new JsonDecoderFactory)->create();
|
||||
$response = $decoder->decode($message, $requestId);
|
||||
|
||||
$this->assertInstanceOf(Message::class, $response);
|
||||
$this->assertEquals(MessageTypes::RESPONSE, $response->getType());
|
||||
}
|
||||
|
||||
public function provideValidJsonPayloads()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'{
|
||||
"Status": 0,
|
||||
"TC": false,
|
||||
"RD": true,
|
||||
"RA": true,
|
||||
"AD": false,
|
||||
"CD": false,
|
||||
"Question":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"Answer":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.178.96.59"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.172.224.47"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.142.160.59"
|
||||
}
|
||||
],
|
||||
"Additional": [ ],
|
||||
"edns_client_subnet": "12.34.56.78/0"
|
||||
}',
|
||||
2,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test decoding of invalid JSON DNS payloads
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $requestId
|
||||
* @return void
|
||||
*
|
||||
* @dataProvider provideInvalidJsonPayloads
|
||||
*/
|
||||
public function testDecodesInvalidJsonPayloads($message, $requestId)
|
||||
{
|
||||
$decoder = (new JsonDecoderFactory)->create();
|
||||
$this->expectException(DnsException::class);
|
||||
$decoder->decode($message, $requestId);
|
||||
}
|
||||
|
||||
public function provideInvalidJsonPayloads()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'{lmfao
|
||||
"Status": 0,
|
||||
"TC": false,
|
||||
"RD": true,
|
||||
"RA": true,
|
||||
"AD": false,
|
||||
"CD": false,
|
||||
"Question":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"Answer":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.178.96.59"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.172.224.47"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.142.160.59"
|
||||
}
|
||||
],
|
||||
"Additional": [ ],
|
||||
"edns_client_subnet": "12.34.56.78/0"
|
||||
}',
|
||||
2,
|
||||
],
|
||||
[
|
||||
'xd{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'whaaa{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'xdxdxxxxx{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'xd',
|
||||
0
|
||||
],
|
||||
[
|
||||
null,
|
||||
0
|
||||
],
|
||||
[
|
||||
[],
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
new class {},
|
||||
0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
86
test/NameserverTest.php
Normal file
86
test/NameserverTest.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\Test;
|
||||
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use Amp\DoH\Nameserver;
|
||||
|
||||
class NameserverTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param string[] $nameservers Valid server array.
|
||||
*
|
||||
* @dataProvider provideValidServers
|
||||
*/
|
||||
public function testAcceptsValidServers($nameserver, $type = Nameserver::RFC8484_POST, $headers = [])
|
||||
{
|
||||
$this->assertInstanceOf(Nameserver::class, new Nameserver($nameserver, $type, $headers));
|
||||
}
|
||||
|
||||
public function provideValidServers()
|
||||
{
|
||||
return [
|
||||
['https://mozilla.cloudflare-dns.com/dns-query'],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON],
|
||||
['https://google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $nameservers Invalid server array.
|
||||
*
|
||||
* @dataProvider provideInvalidServers
|
||||
*/
|
||||
public function testRejectsInvalidServers($nameserver, $type = Nameserver::RFC8484_POST, $headers = [])
|
||||
{
|
||||
$this->expectException(ConfigException::class);
|
||||
new Nameserver($nameserver, $type, $headers);
|
||||
}
|
||||
|
||||
public function provideInvalidServers()
|
||||
{
|
||||
return [
|
||||
[''],
|
||||
[42],
|
||||
[null],
|
||||
[true],
|
||||
["foobar"],
|
||||
["foobar.com"],
|
||||
["127.1.1"],
|
||||
["127.1.1.1.1"],
|
||||
["126.0.0.5"],
|
||||
["42"],
|
||||
["::1"],
|
||||
["::1:53"],
|
||||
["[::1]:"],
|
||||
["[::1]:76235"],
|
||||
["[::1]:0"],
|
||||
["[::1]:-1"],
|
||||
["[::1:51"],
|
||||
["[::1]:abc"],
|
||||
['http://mozilla.cloudflare-dns.com/dns-query'],
|
||||
['http://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST],
|
||||
['http://mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET],
|
||||
['http://mozilla.cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON],
|
||||
['http://google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"]],
|
||||
|
||||
['mozilla.cloudflare-dns.com/dns-query'],
|
||||
['mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_POST],
|
||||
['mozilla.cloudflare-dns.com/dns-query', Nameserver::RFC8484_GET],
|
||||
['mozilla.cloudflare-dns.com/dns-query', Nameserver::GOOGLE_JSON],
|
||||
['google.com/resolve', Nameserver::GOOGLE_JSON, ["Host" => "dns.google.com"]],
|
||||
|
||||
['https://mozilla.cloudflare-dns.com/dns-query'],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', 100],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', "2"],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', -1],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', null],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', "hi"],
|
||||
['https://mozilla.cloudflare-dns.com/dns-query', "foobar"],
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
}
|
15
test/QueryEncoderFactoryTest.php
Normal file
15
test/QueryEncoderFactoryTest.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use Amp\DoH\QueryEncoderFactory;
|
||||
use Amp\DoH\QueryEncoder;
|
||||
|
||||
class QueryEncoderFactoryTest extends TestCase
|
||||
{
|
||||
public function testQueryEncoderFactoryWorks()
|
||||
{
|
||||
$this->assertInstanceOf(QueryEncoder::class, (new QueryEncoderFactory)->create());
|
||||
}
|
||||
}
|
208
test/QueryEncoderTest.php
Normal file
208
test/QueryEncoderTest.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\Dns\DnsException;
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\DoH\QueryEncoderFactory;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use LibDNS\Messages\Message;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
|
||||
class QueryEncoderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test encoding of valid DNS message payloads
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*
|
||||
* @dataProvider provideValidQueryPayloads
|
||||
*/
|
||||
public function testEncodesValidQueryPayloads(string $message)
|
||||
{
|
||||
$decoder = (new JsonDecoderFactory)->create();
|
||||
$response = $decoder->decode($message, 0);
|
||||
$response->setType(MessageTypes::QUERY);
|
||||
|
||||
$encoder = (new QueryEncoderFactory)->create();
|
||||
$request = $encoder->encode($response);
|
||||
|
||||
$this->assertInternalType('string', $request, "Got a ".gettype($request)." instead of a string");
|
||||
parse_str($request, $output);
|
||||
$this->assertNotEmpty($output);
|
||||
$this->assertArrayHasKey('cd', $output);
|
||||
$this->assertArrayHasKey('do', $output);
|
||||
$this->assertArrayHasKey('ct', $output);
|
||||
$this->assertArrayHasKey('type', $output);
|
||||
$this->assertArrayHasKey('name', $output);
|
||||
$this->assertEquals($output['cd'], 0);
|
||||
$this->assertEquals($output['do'], 0);
|
||||
$this->assertEquals($output['ct'], 'application/dns-json');
|
||||
$this->assertEquals($output['type'], $response->getQuestionRecords()->getRecordByIndex(0)->getType());
|
||||
$this->assertEquals($output['name'], implode('.', $message->getQuestionRecords()->getRecordByIndex(0)->getName()->getLabels()));
|
||||
}
|
||||
|
||||
public function provideValidJsonPayloads()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'{
|
||||
"Status": 0,
|
||||
"TC": false,
|
||||
"RD": true,
|
||||
"RA": true,
|
||||
"AD": false,
|
||||
"CD": false,
|
||||
"Question":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"Answer":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.178.96.59"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.172.224.47"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.142.160.59"
|
||||
}
|
||||
],
|
||||
"Additional": [ ],
|
||||
"edns_client_subnet": "12.34.56.78/0"
|
||||
}',
|
||||
2,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
|
||||
3,
|
||||
],
|
||||
[
|
||||
'{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
|
||||
3,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test query encoding of invalid DNS payloads
|
||||
*
|
||||
* @param $request
|
||||
* @return void
|
||||
*
|
||||
* @dataProvider provideInvalidQueryPayloads
|
||||
*/
|
||||
public function testEncodesInvalidQueryPayloads($request)
|
||||
{
|
||||
$encoder = (new QueryEncoderFactory)->create();
|
||||
$this->expectException(DnsException::class);
|
||||
$encoder->encode($request);
|
||||
}
|
||||
|
||||
public function provideInvalidQueryPayloads()
|
||||
{
|
||||
$decoder = (new JsonDecoderFactory)->create();
|
||||
return [
|
||||
[
|
||||
$decoder->decode(
|
||||
'{
|
||||
"Status": 0,
|
||||
"TC": false,
|
||||
"RD": true,
|
||||
"RA": true,
|
||||
"AD": false,
|
||||
"CD": false,
|
||||
"Question":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"Answer":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.178.96.59"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.172.224.47"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.142.160.59"
|
||||
}
|
||||
],
|
||||
"Additional": [ ],
|
||||
"edns_client_subnet": "12.34.56.78/0"
|
||||
}',
|
||||
2,
|
||||
),
|
||||
],
|
||||
[
|
||||
$decoder->decode(
|
||||
'{
|
||||
"Status": 0,
|
||||
"TC": false,
|
||||
"RD": true,
|
||||
"RA": true,
|
||||
"AD": false,
|
||||
"CD": false,
|
||||
"Question":
|
||||
[
|
||||
],
|
||||
"Answer":
|
||||
[
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.178.96.59"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.172.224.47"
|
||||
},
|
||||
{
|
||||
"name": "apple.com.",
|
||||
"type": 1,
|
||||
"TTL": 3599,
|
||||
"data": "17.142.160.59"
|
||||
}
|
||||
],
|
||||
"Additional": [ ],
|
||||
"edns_client_subnet": "12.34.56.78/0"
|
||||
}',
|
||||
2,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\Dns\Record;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
|
||||
class RecordTest extends TestCase
|
||||
{
|
||||
public function testGetName()
|
||||
{
|
||||
$this->assertSame("A", Record::getName(Record::A));
|
||||
}
|
||||
|
||||
public function testGetNameOnInvalidRecordType()
|
||||
{
|
||||
$this->expectException(\Error::class);
|
||||
$this->expectExceptionMessage("65536 does not correspond to a valid record type (must be between 0 and 65535).");
|
||||
|
||||
Record::getName(65536);
|
||||
}
|
||||
|
||||
public function testGetNameOnUnknownRecordType()
|
||||
{
|
||||
$this->assertSame("unknown (1000)", Record::getName(1000));
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Dns\Test;
|
||||
|
||||
use Amp\Dns\Config;
|
||||
use Amp\Dns\ConfigException;
|
||||
use Amp\Dns\UnixConfigLoader;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use function Amp\Promise\wait;
|
||||
|
||||
class UnixConfigLoaderTest extends TestCase
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
$loader = new UnixConfigLoader(__DIR__ . "/data/resolv.conf");
|
||||
|
||||
/** @var Config $result */
|
||||
$result = wait($loader->loadConfig());
|
||||
|
||||
$this->assertSame([
|
||||
"127.0.0.1:53",
|
||||
"[2001:4860:4860::8888]:53",
|
||||
], $result->getNameservers());
|
||||
|
||||
$this->assertSame(5000, $result->getTimeout());
|
||||
$this->assertSame(3, $result->getAttempts());
|
||||
}
|
||||
|
||||
public function testNoDefaultsOnConfNotFound()
|
||||
{
|
||||
$this->expectException(ConfigException::class);
|
||||
wait((new UnixConfigLoader(__DIR__ . "/data/non-existent.conf"))->loadConfig());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user