mirror of
https://github.com/danog/dns-over-https.git
synced 2025-01-22 21:31:24 +01:00
Make JSON parser a separate dependency
This commit is contained in:
parent
610d8af203
commit
aa227cf2d2
@ -44,6 +44,7 @@
|
||||
"php": ">=7.0",
|
||||
"amphp/cache": "^1.2",
|
||||
"amphp/parser": "^1",
|
||||
"danog/libdns-json": "^0.1",
|
||||
"daverandom/libdns": "^2.0.1",
|
||||
"amphp/amp": "^2",
|
||||
"amphp/artax": "dev-master",
|
||||
|
@ -5,14 +5,14 @@ namespace Amp\DoH\Internal;
|
||||
use Amp\Artax\Client;
|
||||
use Amp\Artax\Request;
|
||||
use Amp\DoH\DoHException;
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\DoH\Nameserver;
|
||||
use Amp\DoH\QueryEncoderFactory;
|
||||
use Amp\Promise;
|
||||
use LibDNS\Decoder\DecoderFactory;
|
||||
use LibDNS\Encoder\EncoderFactory;
|
||||
use LibDNS\Messages\Message;
|
||||
use function Amp\call;
|
||||
use danog\LibDNSJson\QueryEncoderFactory;
|
||||
use danog\LibDNSJson\JsonDecoderFactory;
|
||||
|
||||
/** @internal */
|
||||
final class HttpsSocket extends Socket
|
||||
|
@ -1,369 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH;
|
||||
|
||||
use Amp\Dns\DnsException;
|
||||
use LibDNS\Decoder\DecodingContextFactory;
|
||||
use LibDNS\Messages\Message;
|
||||
use LibDNS\Messages\MessageFactory;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
use LibDNS\Packets\Packet;
|
||||
use LibDNS\Packets\PacketFactory;
|
||||
use LibDNS\Records\Question;
|
||||
use LibDNS\Records\QuestionFactory;
|
||||
use LibDNS\Records\Resource;
|
||||
use LibDNS\Records\ResourceBuilder;
|
||||
use LibDNS\Records\Types\Anything;
|
||||
use LibDNS\Records\Types\BitMap;
|
||||
use LibDNS\Records\Types\Char;
|
||||
use LibDNS\Records\Types\CharacterString;
|
||||
use LibDNS\Records\Types\DomainName;
|
||||
use LibDNS\Records\Types\IPv4Address;
|
||||
use LibDNS\Records\Types\IPv6Address;
|
||||
use LibDNS\Records\Types\Long;
|
||||
use LibDNS\Records\Types\Short;
|
||||
use LibDNS\Records\Types\Type;
|
||||
use LibDNS\Records\Types\TypeBuilder;
|
||||
use LibDNS\Records\Types\Types;
|
||||
|
||||
/**
|
||||
* Decodes JSON DNS strings to Message objects.
|
||||
*
|
||||
* @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
|
||||
*/
|
||||
class JsonDecoder
|
||||
{
|
||||
/**
|
||||
* @var \LibDNS\Packets\PacketFactory
|
||||
*/
|
||||
private $packetFactory;
|
||||
|
||||
/**
|
||||
* @var \LibDNS\Messages\MessageFactory
|
||||
*/
|
||||
private $messageFactory;
|
||||
|
||||
/**
|
||||
* @var \LibDNS\Records\QuestionFactory
|
||||
*/
|
||||
private $questionFactory;
|
||||
|
||||
/**
|
||||
* @var \LibDNS\Records\ResourceBuilder
|
||||
*/
|
||||
private $resourceBuilder;
|
||||
|
||||
/**
|
||||
* @var \LibDNS\Records\Types\TypeBuilder
|
||||
*/
|
||||
private $typeBuilder;
|
||||
|
||||
/**
|
||||
* @var \LibDNS\Decoder\DecodingContextFactory
|
||||
*/
|
||||
private $decodingContextFactory;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \LibDNS\Packets\PacketFactory $packetFactory
|
||||
* @param \LibDNS\Messages\MessageFactory $messageFactory
|
||||
* @param \LibDNS\Records\QuestionFactory $questionFactory
|
||||
* @param \LibDNS\Records\ResourceBuilder $resourceBuilder
|
||||
* @param \LibDNS\Records\Types\TypeBuilder $typeBuilder
|
||||
* @param \LibDNS\Decoder\DecodingContextFactory $decodingContextFactory
|
||||
* @param bool $allowTrailingData
|
||||
*/
|
||||
public function __construct(
|
||||
PacketFactory $packetFactory,
|
||||
MessageFactory $messageFactory,
|
||||
QuestionFactory $questionFactory,
|
||||
ResourceBuilder $resourceBuilder,
|
||||
TypeBuilder $typeBuilder,
|
||||
DecodingContextFactory $decodingContextFactory
|
||||
) {
|
||||
$this->packetFactory = $packetFactory;
|
||||
$this->messageFactory = $messageFactory;
|
||||
$this->questionFactory = $questionFactory;
|
||||
$this->resourceBuilder = $resourceBuilder;
|
||||
$this->typeBuilder = $typeBuilder;
|
||||
$this->decodingContextFactory = $decodingContextFactory;
|
||||
}
|
||||
/**
|
||||
* Decode a question record.
|
||||
*
|
||||
*
|
||||
* @return \LibDNS\Records\Question
|
||||
* @throws \UnexpectedValueException When the record is invalid
|
||||
*/
|
||||
private function decodeQuestionRecord(array $record): Question
|
||||
{
|
||||
/** @var \LibDNS\Records\Types\DomainName $domainName */
|
||||
$domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
|
||||
$labels = \explode('.', $record['name']);
|
||||
if (!empty($last = \array_pop($labels))) {
|
||||
$labels[] = $last;
|
||||
}
|
||||
$domainName->setLabels($labels);
|
||||
|
||||
$question = $this->questionFactory->create($record['type']);
|
||||
$question->setName($domainName);
|
||||
//$question->setClass($meta['class']);
|
||||
return $question;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a resource record.
|
||||
*
|
||||
*
|
||||
* @return \LibDNS\Records\Resource
|
||||
* @throws \UnexpectedValueException When the record is invalid
|
||||
* @throws \InvalidArgumentException When a type subtype is unknown
|
||||
*/
|
||||
private function decodeResourceRecord(array $record): Resource
|
||||
{
|
||||
/** @var \LibDNS\Records\Types\DomainName $domainName */
|
||||
$domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
|
||||
$labels = \explode('.', $record['name']);
|
||||
if (!empty($last = \array_pop($labels))) {
|
||||
$labels[] = $last;
|
||||
}
|
||||
$domainName->setLabels($labels);
|
||||
$resource = $this->resourceBuilder->build($record['type']);
|
||||
$resource->setName($domainName);
|
||||
//$resource->setClass($meta['class']);
|
||||
$resource->setTTL($record['TTL']);
|
||||
|
||||
$data = $resource->getData();
|
||||
|
||||
$fieldDef = $index = null;
|
||||
foreach ($resource->getData()->getTypeDefinition() as $index => $fieldDef) {
|
||||
$field = $this->typeBuilder->build($fieldDef->getType());
|
||||
$this->decodeType($field, $record['data']);
|
||||
$data->setField($index, $field);
|
||||
break; // For now parse only one field
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
/**
|
||||
* Decode a Type field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\Type $type The object to populate with the result
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
* @throws \InvalidArgumentException When the Type subtype is unknown
|
||||
*/
|
||||
private function decodeType(Type $type, $data)
|
||||
{
|
||||
if ($type instanceof Anything) {
|
||||
$this->decodeAnything($type, $data);
|
||||
} elseif ($type instanceof BitMap) {
|
||||
$this->decodeBitMap($type, $data);
|
||||
} elseif ($type instanceof Char) {
|
||||
$this->decodeChar($type, $data);
|
||||
} elseif ($type instanceof CharacterString) {
|
||||
$this->decodeCharacterString($type, $data);
|
||||
} elseif ($type instanceof DomainName) {
|
||||
$this->decodeDomainName($type, $data);
|
||||
} elseif ($type instanceof IPv4Address) {
|
||||
$this->decodeIPv4Address($type, $data);
|
||||
} elseif ($type instanceof IPv6Address) {
|
||||
$this->decodeIPv6Address($type, $data);
|
||||
} elseif ($type instanceof Long) {
|
||||
$this->decodeLong($type, $data);
|
||||
} elseif ($type instanceof Short) {
|
||||
$this->decodeShort($type, $data);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Unknown Type '.\get_class($type));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Decode an Anything field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\Anything $anything The object to populate with the result
|
||||
* @param int $length
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeAnything(Anything $anything, $data)
|
||||
{
|
||||
$anything->setValue(\hex2bin($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a BitMap field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\BitMap $bitMap The object to populate with the result
|
||||
* @param int $length
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeBitMap(BitMap $bitMap, $data)
|
||||
{
|
||||
$bitMap->setValue(\hex2bin($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Char field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\Char $char The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeChar(Char $char, $result)
|
||||
{
|
||||
$value = \unpack('C', $result)[1];
|
||||
$char->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a CharacterString field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\CharacterString $characterString The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeCharacterString(CharacterString $characterString, $result)
|
||||
{
|
||||
$characterString->setValue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a DomainName field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\DomainName $domainName The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeDomainName(DomainName $domainName, $result)
|
||||
{
|
||||
$labels = \explode('.', $result);
|
||||
if (!empty($last = \array_pop($labels))) {
|
||||
$labels[] = $last;
|
||||
}
|
||||
|
||||
$domainName->setLabels($labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an IPv4Address field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\IPv4Address $ipv4Address The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeIPv4Address(IPv4Address $ipv4Address, $result)
|
||||
{
|
||||
$octets = \unpack('C4', \inet_pton($result));
|
||||
$ipv4Address->setOctets($octets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an IPv6Address field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\IPv6Address $ipv6Address The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeIPv6Address(IPv6Address $ipv6Address, $result)
|
||||
{
|
||||
$shorts = \unpack('n8', \inet_pton($result));
|
||||
$ipv6Address->setShorts($shorts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Long field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\Long $long The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeLong(Long $long, $result)
|
||||
{
|
||||
$long->setValue((int) $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Short field.
|
||||
*
|
||||
*
|
||||
* @param \LibDNS\Records\Types\Short $short The object to populate with the result
|
||||
* @return int The number of packet bytes consumed by the operation
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
*/
|
||||
private function decodeShort(Short $short, $result)
|
||||
{
|
||||
$short->setValue((int) $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Message from JSON-encoded string.
|
||||
*
|
||||
* @param string $data The data string to decode
|
||||
* @param int $requestId The message ID to set
|
||||
* @return \LibDNS\Messages\Message
|
||||
* @throws \UnexpectedValueException When the packet data is invalid
|
||||
* @throws \InvalidArgumentException When a type subtype is unknown
|
||||
*/
|
||||
public function decode(string $result, int $requestId): Message
|
||||
{
|
||||
$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());
|
||||
|
||||
//$message->isAuthoritative(true);
|
||||
$message->setType(MessageTypes::RESPONSE);
|
||||
$message->setID($requestId);
|
||||
$message->setResponseCode($result['Status']);
|
||||
$message->isTruncated($result['TC']);
|
||||
$message->isRecursionDesired($result['RD']);
|
||||
$message->isRecursionAvailable($result['RA']);
|
||||
|
||||
$decodingContext->setExpectedQuestionRecords(isset($result['Question']) ? \count($result['Question']) : 0);
|
||||
$decodingContext->setExpectedAnswerRecords(isset($result['Answer']) ? \count($result['Answer']) : 0);
|
||||
$decodingContext->setExpectedAuthorityRecords(0);
|
||||
$decodingContext->setExpectedAdditionalRecords(isset($result['Additional']) ? \count($result['Additional']) : 0);
|
||||
|
||||
$questionRecords = $message->getQuestionRecords();
|
||||
$expected = $decodingContext->getExpectedQuestionRecords();
|
||||
for ($i = 0; $i < $expected; $i++) {
|
||||
$questionRecords->add($this->decodeQuestionRecord($result['Question'][$i]));
|
||||
}
|
||||
|
||||
$answerRecords = $message->getAnswerRecords();
|
||||
$expected = $decodingContext->getExpectedAnswerRecords();
|
||||
for ($i = 0; $i < $expected; $i++) {
|
||||
$answerRecords->add($this->decodeResourceRecord($result['Answer'][$i]));
|
||||
}
|
||||
|
||||
$authorityRecords = $message->getAuthorityRecords();
|
||||
$expected = $decodingContext->getExpectedAuthorityRecords();
|
||||
for ($i = 0; $i < $expected; $i++) {
|
||||
$authorityRecords->add($this->decodeResourceRecord($result['Authority'][$i]));
|
||||
}
|
||||
|
||||
$additionalRecords = $message->getAdditionalRecords();
|
||||
$expected = $decodingContext->getExpectedAdditionalRecords();
|
||||
for ($i = 0; $i < $expected; $i++) {
|
||||
$additionalRecords->add($this->decodeResourceRecord($result['Additional'][$i]));
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Creates Decoder objects.
|
||||
*
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
* @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>,
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
*/
|
||||
|
||||
namespace Amp\DoH;
|
||||
|
||||
use \LibDNS\Messages\MessageFactory;
|
||||
use \LibDNS\Packets\PacketFactory;
|
||||
use \LibDNS\Records\QuestionFactory;
|
||||
use \LibDNS\Records\RDataBuilder;
|
||||
use \LibDNS\Records\RDataFactory;
|
||||
use \LibDNS\Records\RecordCollectionFactory;
|
||||
use \LibDNS\Records\ResourceBuilder;
|
||||
use \LibDNS\Records\ResourceFactory;
|
||||
use \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory;
|
||||
use \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory;
|
||||
use \LibDNS\Records\TypeDefinitions\TypeDefinitionManager;
|
||||
use \LibDNS\Records\Types\TypeBuilder;
|
||||
use \LibDNS\Records\Types\TypeFactory;
|
||||
use LibDNS\Decoder\DecodingContextFactory;
|
||||
|
||||
/**
|
||||
* Creates JsonDecoder objects.
|
||||
*
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
*/
|
||||
class JsonDecoderFactory
|
||||
{
|
||||
/**
|
||||
* Create a new JsonDecoder object.
|
||||
*
|
||||
* @param \LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager
|
||||
* @return JsonDecoder
|
||||
*/
|
||||
public function create(TypeDefinitionManager $typeDefinitionManager = null): JsonDecoder
|
||||
{
|
||||
$typeBuilder = new TypeBuilder(new TypeFactory);
|
||||
|
||||
return new JsonDecoder(
|
||||
new PacketFactory,
|
||||
new MessageFactory(new RecordCollectionFactory),
|
||||
new QuestionFactory,
|
||||
new ResourceBuilder(
|
||||
new ResourceFactory,
|
||||
new RDataBuilder(
|
||||
new RDataFactory,
|
||||
$typeBuilder
|
||||
),
|
||||
$typeDefinitionManager ?: new TypeDefinitionManager(
|
||||
new TypeDefinitionFactory,
|
||||
new FieldDefinitionFactory
|
||||
)
|
||||
),
|
||||
$typeBuilder,
|
||||
new DecodingContextFactory
|
||||
);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Encodes Message objects to raw network data.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category LibDNS
|
||||
* @package Encoder
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
* @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
namespace Amp\DoH;
|
||||
|
||||
use \LibDNS\Messages\Message;
|
||||
use Amp\Dns\DnsException;
|
||||
use LibDNS\Messages\MessageTypes;
|
||||
|
||||
/**
|
||||
* Encodes Message objects to raw network data.
|
||||
*
|
||||
* @category LibDNS
|
||||
* @package Encoder
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
*/
|
||||
class QueryEncoder
|
||||
{
|
||||
/**
|
||||
* Encode a Message to URL payload.
|
||||
*
|
||||
* @param \LibDNS\Messages\Message $message The Message to encode
|
||||
* @return string
|
||||
*/
|
||||
public function encode(Message $message): string
|
||||
{
|
||||
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,35 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Creates Encoder objects.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category LibDNS
|
||||
* @package Encoder
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
* @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
namespace Amp\DoH;
|
||||
|
||||
/**
|
||||
* Creates Encoder objects.
|
||||
*
|
||||
* @category LibDNS
|
||||
* @package Encoder
|
||||
* @author Chris Wright <https://github.com/DaveRandom>
|
||||
*/
|
||||
class QueryEncoderFactory
|
||||
{
|
||||
/**
|
||||
* Create a new Encoder object.
|
||||
*
|
||||
* @return \LibDNS\Encoder\Encoder
|
||||
*/
|
||||
public function create(): QueryEncoder
|
||||
{
|
||||
return new QueryEncoder();
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\Test;
|
||||
|
||||
use Amp\DoH\JsonDecoder;
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
|
||||
class JsonDecoderFactoryTest extends TestCase
|
||||
{
|
||||
public function testJsonDecoderFactoryWorks()
|
||||
{
|
||||
$this->assertInstanceOf(JsonDecoder::class, (new JsonDecoderFactory)->create());
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\Test;
|
||||
|
||||
use Amp\Dns\DnsException;
|
||||
use Amp\DoH\JsonDecoderFactory;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use LibDNS\Messages\Message;
|
||||
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
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\Test;
|
||||
|
||||
use Amp\DoH\QueryEncoder;
|
||||
use Amp\DoH\QueryEncoderFactory;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
|
||||
class QueryEncoderFactoryTest extends TestCase
|
||||
{
|
||||
public function testQueryEncoderFactoryWorks()
|
||||
{
|
||||
$this->assertInstanceOf(QueryEncoder::class, (new QueryEncoderFactory)->create());
|
||||
}
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\DoH\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('.', $response->getQuestionRecords()->getRecordByIndex(0)->getName()->getLabels()));
|
||||
}
|
||||
|
||||
public function provideValidQueryPayloads()
|
||||
{
|
||||
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
|
||||
)
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user