From 5a3a88d018c3e5a24d059abddfbf209c981d2454 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 15 Jul 2019 14:41:26 +0200 Subject: [PATCH] First commit --- .gitignore | 7 + .php_cs.dist | 13 ++ .travis.yml | 37 ++++ LICENSE | 21 +++ README.md | 47 +++++ appveyor.yml | 38 ++++ composer.json | 49 ++++++ examples/.gitkeep | 0 examples/simple.php | 26 +++ lib/NativeDecoder.php | 282 ++++++++++++++++++++++++++++++ lib/NativeDecoderFactory.php | 55 ++++++ lib/NativeEncoder.php | 59 +++++++ lib/NativeEncoderFactory.php | 35 ++++ phpunit.xml.dist | 25 +++ test/NativeDecoderFactoryTest.php | 15 ++ test/NativeDecoderTest.php | 155 ++++++++++++++++ test/NativeEncoderFactoryTest.php | 15 ++ test/NativeEncoderTest.php | 50 ++++++ 18 files changed, 929 insertions(+) create mode 100644 .gitignore create mode 100644 .php_cs.dist create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 composer.json create mode 100644 examples/.gitkeep create mode 100644 examples/simple.php create mode 100644 lib/NativeDecoder.php create mode 100644 lib/NativeDecoderFactory.php create mode 100644 lib/NativeEncoder.php create mode 100644 lib/NativeEncoderFactory.php create mode 100644 phpunit.xml.dist create mode 100644 test/NativeDecoderFactoryTest.php create mode 100644 test/NativeDecoderTest.php create mode 100644 test/NativeEncoderFactoryTest.php create mode 100644 test/NativeEncoderTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bef310b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vscode +build +composer.lock +phpunit.xml +vendor +.php_cs.cache +coverage diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..8d02bce --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,13 @@ +getFinder() + ->in(__DIR__ . '/examples') + ->in(__DIR__ . '/lib') + ->in(__DIR__ . '/test'); + +$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__; + +$config->setCacheFile($cacheDir . '/.php_cs.cache'); + +return $config; diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ae930a6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,37 @@ +sudo: false + +language: php + +php: + - 7.0 + - 7.1 + - 7.2 + - 7.3 + - nightly + +matrix: + allow_failures: + - php: nightly + fast_finish: true + + +before_install: + - phpenv config-rm xdebug.ini || echo "No xdebug config." + +install: + - composer update -n --prefer-dist + - wget https://github.com/php-coveralls/php-coveralls/releases/download/v1.0.2/coveralls.phar + - chmod +x coveralls.phar + +script: + - vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml + - PHP_CS_FIXER_IGNORE_ENV=1 php vendor/bin/php-cs-fixer --diff --dry-run -v fix + +after_script: + - ./coveralls.phar -v + +cache: + directories: + - $HOME/.composer/cache + - $HOME/.php-cs-fixer + - $HOME/.local diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..88c2300 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2017 amphp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..52fe0a0 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# LibDNSNative + +[![Build Status](https://img.shields.io/travis/danog/libdnsnative/master.svg?style=flat-square)](https://travis-ci.org/danog/libdnsnative) +![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square) + + +Encoder/decoder for the raw format of [PHP's dns_get_record function](https://www.php.net/manual/en/function.dns-get-record.php) based on [libdns](https://github.com/DaveRandom/LibDNS/): allows usage of the function to fetch **all kinds of DNS records**, not just the ones supported by the `DNS_` constants. + +The API consists of a `NativeEncoderFactory` that creates `NativeEncoder` objects, that can encode libdns `Message` objects to a list of parameters that that must be passed to the `dns_get_record` function. + +The `NativeDecoderFactory` creates `NativeDecoder` objects, that accept the results of the `dns_get_record` function and decode them back to `Message` objects. + +## Installation + +``` +composer require danog/libdns-native +``` + +## Usage + +```php +create(ResourceQTypes::DNSKEY); +$question->setName('daniil.it'); + +$message = (new MessageFactory)->create(MessageTypes::QUERY); +$records = $message->getQuestionRecords(); +$records->add($question); + +$encoder = (new NativeEncoderFactory)->create(); +$question = $encoder->encode($message); + +$result = dns_get_record(...$question); + +$decoder = (new NativeDecoderFactory)->create(); +$result = $decoder->decode($result, ...$question); +``` \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..9e38621 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,38 @@ +build: false +shallow_clone: false + +platform: + - x86 + - x64 + +clone_folder: c:\projects\libdns + +cache: + - c:\tools\php73 -> appveyor.yml + +init: + - SET PATH=C:\Program Files\OpenSSL;c:\tools\php73;%PATH% + - SET COMPOSER_NO_INTERACTION=1 + - SET PHP=1 + - SET ANSICON=121x90 (121x90) + +install: + - IF EXIST c:\tools\php73 (SET PHP=0) + - IF %PHP%==1 sc config wuauserv start= auto + - IF %PHP%==1 net start wuauserv + - IF %PHP%==1 cinst -y OpenSSL.Light + - IF %PHP%==1 cinst -y php + - cd c:\tools\php73 + - IF %PHP%==1 copy php.ini-production php.ini /Y + - IF %PHP%==1 echo date.timezone="UTC" >> php.ini + - IF %PHP%==1 echo extension_dir=ext >> php.ini + - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini + - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini + - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini + - cd c:\projects\libdns + - appveyor DownloadFile https://getcomposer.org/composer.phar + - php composer.phar install --prefer-dist --no-progress + +test_script: + - cd c:\projects\libdns + - vendor/bin/phpunit --colors=always diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0a70c34 --- /dev/null +++ b/composer.json @@ -0,0 +1,49 @@ +{ + "name": "danog/libdns-native", + "homepage": "https://github.com/danog/libdns-natie", + "description": "Encoder/decoder for PHP's dns_get_record raw format based on libdns", + "keywords": [ + "dns", + "message", + "dns_get_record", + "php", + "native function" + ], + "license": "MIT", + "authors": [{ + "name": "Daniil Gentili", + "email": "daniil@daniil.it" + }, + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + } + ], + "require": { + "php": ">=7.0", + "daverandom/libdns": "^2.0.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^6" + }, + "autoload": { + "psr-4": { + "danog\\LibDNSNative\\": "lib" + } + }, + "autoload-dev": { + "psr-4": { + "danog\\LibDNSNative\\Test\\": "test" + } + }, + "scripts": { + "check": [ + "@cs", + "@test" + ], + "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run", + "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff", + "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text" + } +} diff --git a/examples/.gitkeep b/examples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple.php b/examples/simple.php new file mode 100644 index 0000000..58f545c --- /dev/null +++ b/examples/simple.php @@ -0,0 +1,26 @@ +create(ResourceQTypes::DNSKEY); +$question->setName('daniil.it'); + +$message = (new MessageFactory)->create(MessageTypes::QUERY); +$records = $message->getQuestionRecords(); +$records->add($question); + +$encoder = (new NativeEncoderFactory)->create(); +$question = $encoder->encode($message); + +$result = dns_get_record(...$question); + +$decoder = (new NativeDecoderFactory)->create(); +$result = $decoder->decode($result, ...$question); + +var_dump($result); diff --git a/lib/NativeDecoder.php b/lib/NativeDecoder.php new file mode 100644 index 0000000..0a5b65d --- /dev/null +++ b/lib/NativeDecoder.php @@ -0,0 +1,282 @@ +, Chris Wright + */ +class NativeDecoder +{ + /** + * @var \LibDNS\Packets\PacketFactory + */ + private $packetFactory; + + /** + * @var \LibDNS\Messages\MessageFactory + */ + private $messageFactory; + + /** + * @var \LibDNS\Records\QuestionFactory + */ + private $questionFactory; + + /** + * @var \LibDNS\Records\Types\TypeBuilder + */ + private $typeBuilder; + /** + * @var \LibDNS\Decoder\DecoderFactory + */ + private $decoderFactory; + + /** + * Map class names to IDs + * + * @var array + */ + private $classMap = []; + /** + * Constructor. + * + * @param \LibDNS\Packets\PacketFactory $packetFactory + * @param \LibDNS\Messages\MessageFactory $messageFactory + * @param \LibDNS\Records\QuestionFactory $questionFactory + * @param \LibDNS\Records\Types\TypeBuilder $typeBuilder + * @param \LibDNS\Encoder\EncodingContextFactory $encodingContextFactory + * @param \LibDNS\Decoder\DecoderFactory $decoderFactory + * @param bool $allowTrailingData + */ + public function __construct( + PacketFactory $packetFactory, + MessageFactory $messageFactory, + QuestionFactory $questionFactory, + TypeBuilder $typeBuilder, + EncodingContextFactory $encodingContextFactory, + DecoderFactory $decoderFactory + ) { + $this->packetFactory = $packetFactory; + $this->messageFactory = $messageFactory; + $this->questionFactory = $questionFactory; + $this->typeBuilder = $typeBuilder; + $this->encodingContextFactory = $encodingContextFactory; + $this->decoderFactory = $decoderFactory; + + $classes = new \ReflectionClass('\\LibDNS\\Records\\ResourceClasses'); + foreach ($classes->getConstants() as $name => $value) { + $this->classMap[$name] = $value; + } + } + /** + * Decode a question record. + * + * + * @return \LibDNS\Records\Question + * @throws \UnexpectedValueException When the record is invalid + */ + private function decodeQuestionRecord(string $name, int $type): Question + { + /** @var \LibDNS\Records\Types\DomainName $domainName */ + $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME); + $labels = \explode('.', $name); + if (!empty($last = \array_pop($labels))) { + $labels[] = $last; + } + $domainName->setLabels($labels); + + $question = $this->questionFactory->create($type); + $question->setName($domainName); + //$question->setClass($meta['class']); + return $question; + } + + /** + * Encode a question record + * + * @param \LibDNS\Encoder\EncodingContext $encodingContext + * @param \LibDNS\Records\Question $record + */ + private function encodeQuestionRecord(EncodingContext $encodingContext, string $name, int $type) + { + if (!$encodingContext->isTruncated()) { + $packet = $encodingContext->getPacket(); + $record = $this->decodeQuestionRecord($name, $type); + $name = $this->encodeDomainName($record->getName(), $encodingContext); + $meta = \pack('n*', $record->getType(), $record->getClass()); + + if (12 + $packet->getLength()+\strlen($name) + 4 > 512) { + $encodingContext->isTruncated(true); + } else { + $packet->write($name); + $packet->write($meta); + } + } + } + /** + * Encode a DomainName field + * + * @param \LibDNS\Records\Types\DomainName $domainName + * @param \LibDNS\Encoder\EncodingContext $encodingContext + * @return string + */ + private function encodeDomainName(DomainName $domainName, EncodingContext $encodingContext): string + { + $packetIndex = $encodingContext->getPacket()->getLength() + 12; + $labelRegistry = $encodingContext->getLabelRegistry(); + + $result = ''; + $labels = $domainName->getLabels(); + + if ($encodingContext->useCompression()) { + do { + $part = \implode('.', $labels); + $index = $labelRegistry->lookupIndex($part); + + if ($index === null) { + $labelRegistry->register($part, $packetIndex); + + $label = \array_shift($labels); + $length = \strlen($label); + + $result .= \chr($length).$label; + $packetIndex += $length + 1; + } else { + $result .= \pack('n', 0b1100000000000000 | $index); + break; + } + } while ($labels); + + if (!$labels) { + $result .= "\x00"; + } + } else { + foreach ($labels as $label) { + $result .= \chr(\strlen($label)).$label; + } + + $result .= "\x00"; + } + + return $result; + } + + /** + * Encode a resource record + * + * @param \LibDNS\Encoder\EncodingContext $encodingContext + * @param array $record + */ + private function encodeResourceRecord(EncodingContext $encodingContext, array $record) + { + if (!$encodingContext->isTruncated()) { + /** @var \LibDNS\Records\Types\DomainName $domainName */ + $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME); + $labels = \explode('.', $record['host']); + if (!empty($last = \array_pop($labels))) { + $labels[] = $last; + } + $domainName->setLabels($labels); + + $packet = $encodingContext->getPacket(); + $name = $this->encodeDomainName($domainName, $encodingContext); + + $data = $record['data']; + $meta = \pack('n2Nn', $record['type'], $this->classMap[$record['class']], $record['ttl'], \strlen($data)); + + if (12 + $packet->getLength()+\strlen($name) + 10+\strlen($data) > 512) { + $encodingContext->isTruncated(true); + } else { + $packet->write($name); + $packet->write($meta); + $packet->write($data); + } + } + } + + /** + * Decode a Message from JSON-encoded string. + * + * @param array $result The actual response + * @param string $domain The domain name that was queried + * @param int $type The record type that was queried + * @param array $authoritative Authoritative NS results + * @param array $additional Additional results + * @return \LibDNS\Messages\Message + * @throws \UnexpectedValueException When the packet data is invalid + * @throws \InvalidArgumentException When a type subtype is unknown + */ + public function decode(array $result, string $domain, int $type, array $authoritative = null, array $additional = null): Message + { + $additional = $additional ?? []; + $authoritative = $additional ?? []; + + $packet = $this->packetFactory->create(); + $encodingContext = $this->encodingContextFactory->create($packet, false); + $message = $this->messageFactory->create(); + + //$message->isAuthoritative(true); + $message->setType(MessageTypes::RESPONSE); + //$message->setID($requestId); + $message->setResponseCode(0); + $message->isTruncated(false); + $message->isRecursionDesired(false); + $message->isRecursionAvailable(false); + + $this->encodeQuestionRecord($encodingContext, $domain, $type); + + $expectedAnswers = count($result); + for ($i = 0; $i < $expectedAnswers; $i++) { + $this->encodeResourceRecord($encodingContext, $result[$i]); + } + + $expectedAuth = count($authoritative); + for ($i = 0; $i < $expectedAuth; $i++) { + $this->encodeResourceRecord($encodingContext, $authoritative[$i]); + } + + $expectedAdditional = count($additional); + for ($i = 0; $i < $expectedAdditional; $i++) { + $this->encodeResourceRecord($encodingContext, $additional[$i]); + } + + $header = [ + 'id' => $message->getID(), + 'meta' => 0, + 'qd' => 1, + 'an' => $expectedAnswers, + 'ns' => $expectedAuth, + 'ar' => $expectedAdditional, + ]; + + $header['meta'] |= $message->getType() << 15; + $header['meta'] |= $message->getOpCode() << 11; + $header['meta'] |= ((int) $message->isAuthoritative()) << 10; + $header['meta'] |= ((int) $encodingContext->isTruncated()) << 9; + $header['meta'] |= ((int) $message->isRecursionDesired()) << 8; + $header['meta'] |= ((int) $message->isRecursionAvailable()) << 7; + $header['meta'] |= $message->getResponseCode(); + + $data = \pack('n*', $header['id'], $header['meta'], $header['qd'], $header['an'], $header['ns'], $header['ar']).$packet->read($packet->getLength()); + + return $this->decoderFactory->create()->decode($data); + } +} diff --git a/lib/NativeDecoderFactory.php b/lib/NativeDecoderFactory.php new file mode 100644 index 0000000..f50a2b8 --- /dev/null +++ b/lib/NativeDecoderFactory.php @@ -0,0 +1,55 @@ +, Chris Wright + * @copyright Copyright (c) Chris Wright , + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace danog\LibDNSNative; + +use LibDNS\Decoder\DecodingContextFactory; +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\DecoderFactory; +use LibDNS\Encoder\EncodingContextFactory; + +/** + * Creates NativeDecoder objects. + * + * @author Daniil Gentili , Chris Wright + */ +class NativeDecoderFactory +{ + /** + * Create a new NativeDecoder object. + * + * @param \LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager + * @return NativeDecoder + */ + public function create(TypeDefinitionManager $typeDefinitionManager = null): NativeDecoder + { + $typeBuilder = new TypeBuilder(new TypeFactory); + + return new NativeDecoder( + new PacketFactory, + new MessageFactory(new RecordCollectionFactory), + new QuestionFactory, + $typeBuilder, + new EncodingContextFactory, + new DecoderFactory + ); + } +} diff --git a/lib/NativeEncoder.php b/lib/NativeEncoder.php new file mode 100644 index 0000000..c4d0606 --- /dev/null +++ b/lib/NativeEncoder.php @@ -0,0 +1,59 @@ +, Chris Wright + * @copyright Copyright (c) Chris Wright + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 2.0.0 + */ + +namespace danog\LibDNSNative; + +use LibDNS\Messages\MessageTypes; +use \LibDNS\Messages\Message; +use LibDNS\Records\ResourceQTypes; + +/** + * Encodes Message objects to query strings. + * + * @category LibDNS + * @package Encoder + * @author Daniil Gentili , Chris Wright + */ +class NativeEncoder +{ + /** + * Encode a Message to URL payload. + * + * @param \LibDNS\Messages\Message $message The Message to encode + * @return array Array of parameters to pass to the \dns_get_record function + */ + public function encode(Message $message): array + { + if ($message->getType() !== MessageTypes::QUERY) { + throw new \InvalidArgumentException('Invalid question: is not a question record'); + } + $questions = $message->getQuestionRecords(); + if ($questions->count() === 0) { + throw new \InvalidArgumentException('Invalid question: 0 question records provided'); + } + if ($questions->count() !== 1) { + throw new \InvalidArgumentException('Invalid question: only one question record can be provided at a time'); + } + + $question = $questions->getRecordByIndex(0); + + return [ + \implode('.', $question->getName()->getLabels()), // Name + $question->getType(), // Type + null, // Authority records + null, // Additional records + true, // Raw results + ]; + } +} diff --git a/lib/NativeEncoderFactory.php b/lib/NativeEncoderFactory.php new file mode 100644 index 0000000..47f2183 --- /dev/null +++ b/lib/NativeEncoderFactory.php @@ -0,0 +1,35 @@ +, Chris Wright + * @copyright Copyright (c) Chris Wright + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 2.0.0 + */ + +namespace danog\LibDNSNative; + +/** + * Creates NativeEncoder objects. + * + * @category LibDNS + * @package Encoder + * @author Daniil Gentili , Chris Wright + */ +class NativeEncoderFactory +{ + /** + * Create a new Encoder object. + * + * @return \LibDNS\Encoder\Encoder + */ + public function create(): NativeEncoder + { + return new NativeEncoder(); + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..4b81b12 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + test + + + + + lib + + + diff --git a/test/NativeDecoderFactoryTest.php b/test/NativeDecoderFactoryTest.php new file mode 100644 index 0000000..8c19b0c --- /dev/null +++ b/test/NativeDecoderFactoryTest.php @@ -0,0 +1,15 @@ +assertInstanceOf(NativeDecoder::class, (new NativeDecoderFactory)->create()); + } +} diff --git a/test/NativeDecoderTest.php b/test/NativeDecoderTest.php new file mode 100644 index 0000000..067b1a2 --- /dev/null +++ b/test/NativeDecoderTest.php @@ -0,0 +1,155 @@ +create(); + $response = $decoder->decode($result, $name, $type, $auth, $additional); + + $this->assertInstanceOf(Message::class, $response); + $this->assertEquals(MessageTypes::RESPONSE, $response->getType()); + } + + public function provideValidNativePayloads() + { + return [ + [ + [ + [ + 'host' => 'daniil.it', + 'class' => 'IN', + 'ttl' => 3600, + 'type' => 48, + 'data' => base64_decode('AQADDaCTEREs+ROIGM0v6ulw671NajD2CIwlsyWjmrvFzRGXqgmCg+Wq9CEXfCql1xSZKplX0bzBj5jNcfHxgGtl4Ug='), + ], + + [ + 'host' => 'daniil.it', + 'class' => 'IN', + 'ttl' => 3600, + 'type' => 48, + 'data' => base64_decode('AQEDDZnbLMFMq9wz1td9pjovFfcRElhPI06NHcQo456KSpfhqicaVV3JBwHhfipMS28SC3wy1E9KwCvYlM8tS+d3ihk='), + ], + ], + 'daniil.it', + ResourceQTypes::DNSKEY, + [ + ], + null, + ], + + [ + + [ + + [ + 'host' => 'apple.com', + 'class' => 'IN', + 'ttl' => 2898, + 'type' => 1, + 'data' => base64_decode('EazgLw=='), + ], + + [ + 'host' => 'apple.com', + 'class' => 'IN', + 'ttl' => 2898, + 'type' => 1, + 'data' => base64_decode('EY6gOw=='), + ], + + [ + 'host' => 'apple.com', + 'class' => 'IN', + 'ttl' => 2898, + 'type' => 1, + 'data' => base64_decode('EbJgOw=='), + ], + ], + 'apple.com', + ResourceQTypes::A, + [ + ], + null, + ], + + [ + + [ + + [ + 'host' => 'amphp.org', + 'class' => 'IN', + 'ttl' => 166, + 'type' => 1, + 'data' => base64_decode('aBgUIg=='), + ], + + [ + 'host' => 'amphp.org', + 'class' => 'IN', + 'ttl' => 166, + 'type' => 1, + 'data' => base64_decode('aBgVIg=='), + ], + ], + 'amphp.org', + ResourceQTypes::A, + + [ + ], + null, + ], + + [ + [ + [ + 'host' => 'tssthacks.daniil.it', + 'class' => 'IN', + 'ttl' => 282, + 'type' => 5, + 'data' => base64_decode('A2docwxnb29nbGVob3N0ZWQDY29tAA=='), + ], + ], + 'tssthacks.daniil.it', + ResourceQTypes::CNAME, + null, + null, + ], + [ + [ + [ + 'host' => 'daniil.it', + 'class' => 'IN', + 'ttl' => 289, + 'type' => 15, + 'data' => base64_decode('AAoCbXgGeWFuZGV4A25ldAA='), + ], + ], + 'daniil.it', + ResourceQTypes::MX, + [ + ], + null, + ], + ]; + } +} diff --git a/test/NativeEncoderFactoryTest.php b/test/NativeEncoderFactoryTest.php new file mode 100644 index 0000000..1fb6af8 --- /dev/null +++ b/test/NativeEncoderFactoryTest.php @@ -0,0 +1,15 @@ +assertInstanceOf(NativeEncoder::class, (new NativeEncoderFactory)->create()); + } +} diff --git a/test/NativeEncoderTest.php b/test/NativeEncoderTest.php new file mode 100644 index 0000000..b35ead2 --- /dev/null +++ b/test/NativeEncoderTest.php @@ -0,0 +1,50 @@ +create(); + $response = $decoder->decode([], $name, $type, [], [], [], 0); + $response->setType(MessageTypes::QUERY); + + $encoder = (new NativeEncoderFactory)->create(); + $request = $encoder->encode($response); + + $this->assertInternalType('array', $request, "Got a ".\gettype($request)." instead of an array"); + $this->assertCount(5, $request); + $this->assertInternalType('string', $request[0], "Got a ".\gettype($request[0])." instead of a string"); + $this->assertInternalType('int', $request[1], "Got a ".\gettype($request[1])." instead of an int"); + $this->assertEquals($name, $request[0]); + $this->assertEquals($type, $request[1]); + $this->assertEquals($request[1], $response->getQuestionRecords()->getRecordByIndex(0)->getType()); + $this->assertEquals($request[0], \implode('.', $response->getQuestionRecords()->getRecordByIndex(0)->getName()->getLabels())); + } + + public function provideValidNativePayloads() + { + return [ + ['apple.com', ResourceQTypes::A], + ['amphp.org', ResourceQTypes::A], + ['tssthacks.daniil.it', ResourceQTypes::CNAME], + ['daniil.it', ResourceQTypes::MX], + ['daniil.it', ResourceQTypes::DNSKEY], + ]; + } +}