From 245a2c50937735019b2dda7baa8edd627f05afc3 Mon Sep 17 00:00:00 2001 From: vaidas-lungis Date: Tue, 27 Dec 2016 18:03:03 +0200 Subject: [PATCH 1/5] Add .gitignore file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c422267..0607f18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ composer.phar /vendor/ +.DS_Store +/.idea +composer.lock # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file From 635157441e47380f60d70ec84b99d3b0bcd7a1f3 Mon Sep 17 00:00:00 2001 From: vaidas-lungis Date: Tue, 27 Dec 2016 18:03:55 +0200 Subject: [PATCH 2/5] Create composer.json file --- composer.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 composer.json diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6ecc289 --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "hostinger/php-dig", + "description": "Use servers CLI command dig over php internal dns_get_record() function", + "keywords": [ + "dig", + "dns", + "dns_get_record" + ], + "license": "MIT", + "require": {}, + "require-dev": { + "phpunit/phpunit": "^5.6" + }, + "autoload": { + "psr-4": { + "Hostinger\\": "src/" + } + }, + "scripts": { + "test": "./vendor/bin/phpunit" + } +} From 9d7d71eed83f7870bea392e911c74c71e0c305a8 Mon Sep 17 00:00:00 2001 From: vaidas-lungis Date: Tue, 3 Jan 2017 01:52:35 +0200 Subject: [PATCH 3/5] Create PHP-dig with package Supported record types: - CNAME - MX --- composer.json | 10 +++- phpunit.xml.dist | 21 +++++++++ src/DigClient.php | 84 +++++++++++++++++++++++++++++++++ src/ExecuteDigCommand.php | 27 +++++++++++ src/RecordType/Cname.php | 38 +++++++++++++++ src/RecordType/Mx.php | 41 ++++++++++++++++ src/RecordType/RecordType.php | 18 +++++++ src/RecordTypeFactory.php | 21 +++++++++ tests/ClientTest.php | 34 +++++++++++++ tests/RecordTypeFactoryTest.php | 12 +++++ 10 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 phpunit.xml.dist create mode 100644 src/DigClient.php create mode 100644 src/ExecuteDigCommand.php create mode 100644 src/RecordType/Cname.php create mode 100644 src/RecordType/Mx.php create mode 100644 src/RecordType/RecordType.php create mode 100644 src/RecordTypeFactory.php create mode 100644 tests/ClientTest.php create mode 100644 tests/RecordTypeFactoryTest.php diff --git a/composer.json b/composer.json index 6ecc289..660d14f 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,10 @@ "dns_get_record" ], "license": "MIT", - "require": {}, + "require": { + "php": ">=5.6", + "psr/log": "^1.0" + }, "require-dev": { "phpunit/phpunit": "^5.6" }, @@ -16,6 +19,11 @@ "Hostinger\\": "src/" } }, + "autoload-dev": { + "classmap": [ + "tests/" + ] + }, "scripts": { "test": "./vendor/bin/phpunit" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..fe72ff0 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + ./tests + + + + + ./src + + + diff --git a/src/DigClient.php b/src/DigClient.php new file mode 100644 index 0000000..c2fa8ad --- /dev/null +++ b/src/DigClient.php @@ -0,0 +1,84 @@ +logger = new NullLogger(); + } + + public function getRecord($domain, $type) + { + $recordType = (new RecordTypeFactory())->make($type); + if (is_null($recordType)) { + $this->logger->warning('Unsupported DNS type', ['domain' => $domain, 'type' => $type]); + return $this->fallback($domain, $type); + } + + if ($errorCode = $this->execEnabled() !== true) { + $this->logger->warning('EXEC disabled', ['domain' => $domain, 'type' => $type, 'error' => $errorCode]); + return $this->fallback($domain, $type); + } + + $this->logger->debug('execute dig', ['domain' => $domain, 'type' => $type]); + return (new ExecuteDigCommand())->execute($domain, $recordType); + } + + /** + * Use custom error handler to convert dns_get_record() errors to exceptions. + * It throws a warning when the DNS query fails. + * @see http://stackoverflow.com/a/1241751/2728507 + * @see http://php.net/manual/en/function.restore-error-handler.php + * @param $domain + * @param $type + * @return array + */ + public function fallback($domain, $type) + { + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + // error was suppressed with the @-operator + if (0 === error_reporting()) { + return false; + } + + throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + }); + + $output = []; + try { + $output = dns_get_record($domain, $type); + } catch (\ErrorException $errorException) { + $this->logger->critical('dns_get_record() query failed', + ['domain' => $domain, 'type' => $type, 'error' => $errorException->getMessage()]); + } + + restore_error_handler(); + return $output; + } + + /** + * Check if system allowed to execute commands. Return true or string with error message + * @return bool|string + */ + public function execEnabled() + { + if (!function_exists('exec')) { + return 'missing_function_exec'; + } + + $disabled = explode(',', ini_get('disable_functions')); + if (in_array('exec', $disabled)) { + return 'disabled_function_exec'; + } + + return true; + } +} diff --git a/src/ExecuteDigCommand.php b/src/ExecuteDigCommand.php new file mode 100644 index 0000000..7ca355c --- /dev/null +++ b/src/ExecuteDigCommand.php @@ -0,0 +1,27 @@ +timeout = $value; + } + + public function execute($domain, RecordType $recordType) + { + $lines = []; + $dnsType = strtoupper($recordType->getType()); + $command = 'dig +noall +answer +time=' . escapeshellarg($this->timeout) . ' ' . escapeshellarg($dnsType) . ' ' . escapeshellarg($domain); + exec($command, $lines); + if (empty($lines)) { + return []; + } + return $recordType->transform($lines); + } +} diff --git a/src/RecordType/Cname.php b/src/RecordType/Cname.php new file mode 100644 index 0000000..e165a76 --- /dev/null +++ b/src/RecordType/Cname.php @@ -0,0 +1,38 @@ +getType()) { + continue; + } + + $output[] = [ + 'host' => $recordProp[0], + 'ttl' => $recordProp[1], + 'class' => $recordProp[2], + 'type' => $recordProp[3], + ]; + } + return $output; + } + + public function getType() + { + return 'CNAME'; + } + +} diff --git a/src/RecordType/Mx.php b/src/RecordType/Mx.php new file mode 100644 index 0000000..8740356 --- /dev/null +++ b/src/RecordType/Mx.php @@ -0,0 +1,41 @@ +getType()) { + continue; + } + + $output[] = [ + 'host' => $recordProp[0], + 'ttl' => $recordProp[1], + 'class' => $recordProp[2], + 'type' => $recordProp[3], + 'pri' => $recordProp[4], + 'target' => $recordProp[5], + ]; + } + return $output; + } + + public function getType() + { + return 'MX'; + } + +} diff --git a/src/RecordType/RecordType.php b/src/RecordType/RecordType.php new file mode 100644 index 0000000..ef013ec --- /dev/null +++ b/src/RecordType/RecordType.php @@ -0,0 +1,18 @@ + 'MX', + DNS_CNAME => 'CNAME', + ]; + + public function make($dns_type) + { + $class = '\\Hostinger\\RecordType\\' . ucfirst(strtolower($this->dns_types[$dns_type])); + if (!class_exists($class)) { + return null; + } + return new $class(); + } + +} diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..1714d91 --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,34 @@ +execEnabled(); + $this->assertTrue($result, $result); + } + + public function domainsAndTypesProvider() + { + return [ + ['hostingermail.com', DNS_MX], + ['ghs.google.com', DNS_CNAME], + ]; + } + + /** + * @dataProvider domainsAndTypesProvider + */ + public function testgetRecords($domain, $type) + { + $client = new Hostinger\DigClient(); + $result = $client->getRecord($domain, $type); + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey(0, $result); + + $expected = dns_get_record($domain, $type); + + $this->assertEmpty(array_diff_key($result, $expected)); + } +} diff --git a/tests/RecordTypeFactoryTest.php b/tests/RecordTypeFactoryTest.php new file mode 100644 index 0000000..640564d --- /dev/null +++ b/tests/RecordTypeFactoryTest.php @@ -0,0 +1,12 @@ +make(DNS_MX); + $this->assertNotNull($result); + $this->assertInstanceOf(\Hostinger\RecordType\RecordType::class, $result); + } +} From cc262b9c59dfbbbdee09208e62c77cd51aac0d06 Mon Sep 17 00:00:00 2001 From: vaidas-lungis Date: Tue, 3 Jan 2017 09:57:37 +0200 Subject: [PATCH 4/5] move files to src/Hostinger --- composer.json | 5 ++++- src/{ => Hostinger}/DigClient.php | 0 src/{ => Hostinger}/ExecuteDigCommand.php | 0 src/{ => Hostinger}/RecordType/Cname.php | 0 src/{ => Hostinger}/RecordType/Mx.php | 0 src/{ => Hostinger}/RecordType/RecordType.php | 0 src/{ => Hostinger}/RecordTypeFactory.php | 0 7 files changed, 4 insertions(+), 1 deletion(-) rename src/{ => Hostinger}/DigClient.php (100%) rename src/{ => Hostinger}/ExecuteDigCommand.php (100%) rename src/{ => Hostinger}/RecordType/Cname.php (100%) rename src/{ => Hostinger}/RecordType/Mx.php (100%) rename src/{ => Hostinger}/RecordType/RecordType.php (100%) rename src/{ => Hostinger}/RecordTypeFactory.php (100%) diff --git a/composer.json b/composer.json index 660d14f..da1345e 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,13 @@ }, "autoload": { "psr-4": { - "Hostinger\\": "src/" + "Hostinger\\": "src/Hostinger" } }, "autoload-dev": { + "psr-4": { + "Hostinger\\": "src/Hostinger" + }, "classmap": [ "tests/" ] diff --git a/src/DigClient.php b/src/Hostinger/DigClient.php similarity index 100% rename from src/DigClient.php rename to src/Hostinger/DigClient.php diff --git a/src/ExecuteDigCommand.php b/src/Hostinger/ExecuteDigCommand.php similarity index 100% rename from src/ExecuteDigCommand.php rename to src/Hostinger/ExecuteDigCommand.php diff --git a/src/RecordType/Cname.php b/src/Hostinger/RecordType/Cname.php similarity index 100% rename from src/RecordType/Cname.php rename to src/Hostinger/RecordType/Cname.php diff --git a/src/RecordType/Mx.php b/src/Hostinger/RecordType/Mx.php similarity index 100% rename from src/RecordType/Mx.php rename to src/Hostinger/RecordType/Mx.php diff --git a/src/RecordType/RecordType.php b/src/Hostinger/RecordType/RecordType.php similarity index 100% rename from src/RecordType/RecordType.php rename to src/Hostinger/RecordType/RecordType.php diff --git a/src/RecordTypeFactory.php b/src/Hostinger/RecordTypeFactory.php similarity index 100% rename from src/RecordTypeFactory.php rename to src/Hostinger/RecordTypeFactory.php From 0a0e2a62bb0b77cc4682ef118f1c7ccd15106c09 Mon Sep 17 00:00:00 2001 From: vaidas-lungis Date: Tue, 3 Jan 2017 09:58:04 +0200 Subject: [PATCH 5/5] update README.md --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b1701b..11a5830 100644 --- a/README.md +++ b/README.md @@ -1 +1,51 @@ -# php-dig \ No newline at end of file +# php-dig +## Introduction + +[PHP DNS functions](http://php.net/manual/en/ref.network.php) don't have a timeout while the default timeout for dig is 5 seconds (with several (3) tries) + +It should drastically decrease time to get dns records, and lower failure errors like `dns_get_record(): A temporary server error occurred.` + +## Installation +Install the latest version with + +```console +$ composer require hostinger/php-dig +``` + +## Usage + +```php +$client = new Hostinger\DigClient(); +$result = $client->getRecord('hostinger.com', DNS_MX); +``` + +This is equal to +```php +dns_get_record('hostinger.com', DNS_MX); +``` + +Package checks if it can run `exec` in server environment, otherwise it will fallback to dns_get_record(). + +### DigClient implements LoggerAwareInterface +You can set [logger](https://github.com/Seldaek/monolog/) to debug / log package activity +```php +$client = new Hostinger\DigClient(); +$logger = new \Monolog\Logger\Logger('App'); +$logger->pushHandler(new StreamHandler('path/to/your.log')); +$client->setLogger($logger); +``` + +## About + +### Requirements + +- php-dig client works with PHP 5.6 or above. + +### Submitting bugs and feature requests + +Bugs and feature request are tracked on [GitHub](https://github.com/hostinger/php-dig/issues) + + +## Sources +- [Stack overflow question What would cause checkdnsrr() or dns_get_record() to take too long?](http://stackoverflow.com/questions/14065946/what-would-cause-checkdnsrr-or-dns-get-record-to-take-too-long) +- [Reddit thread: dns_get_record suddenly running very slowly](https://www.reddit.com/r/PHP/comments/2k3ns7/dns_get_record_suddenly_running_very_slowly/) \ No newline at end of file