Make JSON parser a separate dependency

This commit is contained in:
Daniil Gentili 2019-06-12 14:47:23 +02:00
parent 610d8af203
commit aa227cf2d2
10 changed files with 3 additions and 948 deletions

View File

@ -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",

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
);
}
}

View File

@ -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
]
);
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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
],
];
}
}

View File

@ -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());
}
}

View File

@ -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
)
],
];
}
}