2016-12-30 04:16:14 +01:00
|
|
|
<?php
|
2015-07-28 03:53:44 +02:00
|
|
|
|
|
|
|
namespace Amp\Dns;
|
2016-08-24 06:14:10 +02:00
|
|
|
|
2017-06-15 22:06:50 +02:00
|
|
|
use Amp\Loop;
|
|
|
|
use Amp\Promise;
|
2016-08-02 23:57:40 +02:00
|
|
|
|
|
|
|
const LOOP_STATE_IDENTIFIER = Resolver::class;
|
2015-07-28 03:53:44 +02:00
|
|
|
|
2016-03-19 22:23:55 +01:00
|
|
|
/**
|
2017-06-17 10:49:54 +02:00
|
|
|
* Retrieve the application-wide dns resolver instance.
|
2016-03-19 22:23:55 +01:00
|
|
|
*
|
2016-08-02 23:57:40 +02:00
|
|
|
* @param \Amp\Dns\Resolver $resolver Optionally specify a new default dns resolver instance
|
2017-06-17 12:30:38 +02:00
|
|
|
*
|
2016-03-19 22:23:55 +01:00
|
|
|
* @return \Amp\Dns\Resolver Returns the application-wide dns resolver instance
|
|
|
|
*/
|
2016-08-24 06:14:10 +02:00
|
|
|
function resolver(Resolver $resolver = null): Resolver {
|
2016-08-02 23:57:40 +02:00
|
|
|
if ($resolver === null) {
|
2016-08-12 17:51:40 +02:00
|
|
|
$resolver = Loop::getState(LOOP_STATE_IDENTIFIER);
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2016-08-02 23:57:40 +02:00
|
|
|
if ($resolver) {
|
|
|
|
return $resolver;
|
|
|
|
}
|
|
|
|
|
|
|
|
$resolver = driver();
|
2016-03-19 22:23:55 +01:00
|
|
|
}
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2016-08-12 17:51:40 +02:00
|
|
|
Loop::setState(LOOP_STATE_IDENTIFIER, $resolver);
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2016-08-02 23:57:40 +02:00
|
|
|
return $resolver;
|
2016-03-19 22:23:55 +01:00
|
|
|
}
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2016-03-19 22:23:55 +01:00
|
|
|
/**
|
2017-06-17 10:49:54 +02:00
|
|
|
* Create a new dns resolver best-suited for the current environment.
|
2016-03-19 22:23:55 +01:00
|
|
|
*
|
|
|
|
* @return \Amp\Dns\Resolver
|
|
|
|
*/
|
2016-08-24 06:14:10 +02:00
|
|
|
function driver(): Resolver {
|
2016-03-20 11:40:08 +01:00
|
|
|
return new DefaultResolver;
|
2016-03-19 22:23:55 +01:00
|
|
|
}
|
2015-08-02 04:18:44 +02:00
|
|
|
|
|
|
|
/**
|
2016-02-25 12:36:51 +01:00
|
|
|
* Resolve a hostname name to an IP address
|
2017-06-17 10:49:54 +02:00
|
|
|
* [hostname as defined by RFC 3986].
|
2015-08-02 04:18:44 +02:00
|
|
|
*
|
|
|
|
* Upon success the returned promise resolves to an indexed array of the form:
|
|
|
|
*
|
2015-09-08 23:03:25 +02:00
|
|
|
* [string $recordValue, int $type, int $ttl]
|
2015-08-02 04:18:44 +02:00
|
|
|
*
|
|
|
|
* A null $ttl value indicates the DNS name was resolved from the cache or the
|
|
|
|
* local hosts file.
|
2015-08-30 16:39:04 +02:00
|
|
|
* $type being one constant from Amp\Dns\Record
|
2015-08-02 04:18:44 +02:00
|
|
|
*
|
|
|
|
* Options:
|
|
|
|
*
|
2015-09-08 23:03:25 +02:00
|
|
|
* - "server" | string Custom DNS server address in ip or ip:port format (Default: 8.8.8.8:53)
|
|
|
|
* - "timeout" | int DNS server query timeout (Default: 3000ms)
|
|
|
|
* - "hosts" | bool Use the hosts file (Default: true)
|
2015-08-30 16:39:04 +02:00
|
|
|
* - "reload_hosts" | bool Reload the hosts file (Default: false), only active when no_hosts not true
|
2015-09-08 23:03:25 +02:00
|
|
|
* - "cache" | bool Use local DNS cache when querying (Default: true)
|
2015-08-30 16:39:04 +02:00
|
|
|
* - "types" | array Default: [Record::A, Record::AAAA] (only for resolve())
|
2017-06-17 12:30:38 +02:00
|
|
|
* - "recurse" | bool Check for DNAME and CNAME records (always active for resolve(), Default: false for
|
|
|
|
* query())
|
2015-08-02 04:18:44 +02:00
|
|
|
*
|
|
|
|
* If the custom per-request "server" option is not present the resolver will
|
2015-09-18 15:09:28 +02:00
|
|
|
* use the first nameserver in /etc/resolv.conf or default to Google's public
|
|
|
|
* DNS servers on Windows or if /etc/resolv.conf is not found.
|
2015-08-02 04:18:44 +02:00
|
|
|
*
|
|
|
|
* @param string $name The hostname to resolve
|
|
|
|
* @param array $options
|
2017-06-17 12:30:38 +02:00
|
|
|
*
|
2017-03-17 05:01:58 +01:00
|
|
|
* @return \Amp\Promise
|
2015-08-02 04:18:44 +02:00
|
|
|
* @TODO add boolean "clear_cache" option flag
|
|
|
|
*/
|
2016-11-15 17:42:15 +01:00
|
|
|
function resolve(string $name, array $options = []): Promise {
|
2016-03-19 22:23:55 +01:00
|
|
|
return resolver()->resolve($name, $options);
|
2015-08-30 16:39:04 +02:00
|
|
|
}
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2015-09-18 15:09:28 +02:00
|
|
|
/**
|
|
|
|
* Query specific DNS records.
|
|
|
|
*
|
2017-06-17 12:30:38 +02:00
|
|
|
* @param string $name Unlike resolve(), query() allows for requesting _any_ name (as DNS RFC allows for arbitrary
|
|
|
|
* strings)
|
2015-09-18 15:09:28 +02:00
|
|
|
* @param int|int[] $type Use constants of Amp\Dns\Record
|
2017-06-17 12:30:38 +02:00
|
|
|
* @param array $options @see resolve documentation
|
|
|
|
*
|
2017-03-17 05:01:58 +01:00
|
|
|
* @return \Amp\Promise
|
2015-09-18 15:09:28 +02:00
|
|
|
*/
|
2016-11-15 17:42:15 +01:00
|
|
|
function query(string $name, $type, array $options = []): Promise {
|
2016-03-19 22:23:55 +01:00
|
|
|
return resolver()->query($name, $type, $options);
|
2017-01-25 16:36:19 +01:00
|
|
|
}
|
2017-06-15 22:06:50 +02:00
|
|
|
|
2017-06-17 12:30:38 +02:00
|
|
|
/**
|
|
|
|
* Checks whether a string is a valid DNS name.
|
|
|
|
*
|
|
|
|
* @param string $name DNS name to check.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
function isValidHostName(string $name): bool {
|
|
|
|
static $pattern = '/^(?<name>[a-z0-9]([a-z0-9-]*[a-z0-9])?)(\.(?&name))*$/i';
|
|
|
|
return !isset($name[253]) && \preg_match($pattern, $name);
|
|
|
|
}
|
|
|
|
|
2017-06-15 22:06:50 +02:00
|
|
|
if (\function_exists('idn_to_ascii')) {
|
|
|
|
/**
|
|
|
|
* Normalizes a DNS name.
|
|
|
|
*
|
|
|
|
* @param string $label DNS name.
|
|
|
|
*
|
|
|
|
* @return string Normalized DNS name.
|
|
|
|
*
|
|
|
|
* @throws \Error If an invalid name or an IDN name without ext/intl being installed has been passed.
|
|
|
|
*/
|
|
|
|
function normalizeName(string $label): string {
|
|
|
|
if (false === $result = \idn_to_ascii($label, 0, INTL_IDNA_VARIANT_UTS46)) {
|
2017-06-17 12:30:38 +02:00
|
|
|
throw new InvalidNameError("Label '{$label}' could not be processed for IDN");
|
2017-06-15 22:06:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
function normalizeName(string $label): string {
|
|
|
|
if (\preg_match('/[\x80-\xff]/', $label)) {
|
2017-06-17 12:30:38 +02:00
|
|
|
throw new InvalidNameError(
|
2017-06-15 22:06:50 +02:00
|
|
|
"Label '{$label}' contains non-ASCII characters and IDN support is not available."
|
|
|
|
. " Verify that ext/intl is installed for IDN support."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return \strtolower($label);
|
|
|
|
}
|
|
|
|
}
|