summaryrefslogtreecommitdiffstats
path: root/vendor/react/dns/src/Resolver
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/react/dns/src/Resolver')
-rw-r--r--vendor/react/dns/src/Resolver/Factory.php214
-rw-r--r--vendor/react/dns/src/Resolver/Resolver.php147
-rw-r--r--vendor/react/dns/src/Resolver/ResolverInterface.php94
3 files changed, 455 insertions, 0 deletions
diff --git a/vendor/react/dns/src/Resolver/Factory.php b/vendor/react/dns/src/Resolver/Factory.php
new file mode 100644
index 0000000..5fe608c
--- /dev/null
+++ b/vendor/react/dns/src/Resolver/Factory.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace React\Dns\Resolver;
+
+use React\Cache\ArrayCache;
+use React\Cache\CacheInterface;
+use React\Dns\Config\Config;
+use React\Dns\Config\HostsFile;
+use React\Dns\Query\CachingExecutor;
+use React\Dns\Query\CoopExecutor;
+use React\Dns\Query\ExecutorInterface;
+use React\Dns\Query\FallbackExecutor;
+use React\Dns\Query\HostsFileExecutor;
+use React\Dns\Query\RetryExecutor;
+use React\Dns\Query\SelectiveTransportExecutor;
+use React\Dns\Query\TcpTransportExecutor;
+use React\Dns\Query\TimeoutExecutor;
+use React\Dns\Query\UdpTransportExecutor;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+
+final class Factory
+{
+ /**
+ * Creates a DNS resolver instance for the given DNS config
+ *
+ * As of v1.7.0 it's recommended to pass a `Config` object instead of a
+ * single nameserver address. If the given config contains more than one DNS
+ * nameserver, all DNS nameservers will be used in order. The primary DNS
+ * server will always be used first before falling back to the secondary or
+ * tertiary DNS server.
+ *
+ * @param Config|string $config DNS Config object (recommended) or single nameserver address
+ * @param ?LoopInterface $loop
+ * @return \React\Dns\Resolver\ResolverInterface
+ * @throws \InvalidArgumentException for invalid DNS server address
+ * @throws \UnderflowException when given DNS Config object has an empty list of nameservers
+ */
+ public function create($config, LoopInterface $loop = null)
+ {
+ $executor = $this->decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get()));
+
+ return new Resolver($executor);
+ }
+
+ /**
+ * Creates a cached DNS resolver instance for the given DNS config and cache
+ *
+ * As of v1.7.0 it's recommended to pass a `Config` object instead of a
+ * single nameserver address. If the given config contains more than one DNS
+ * nameserver, all DNS nameservers will be used in order. The primary DNS
+ * server will always be used first before falling back to the secondary or
+ * tertiary DNS server.
+ *
+ * @param Config|string $config DNS Config object (recommended) or single nameserver address
+ * @param ?LoopInterface $loop
+ * @param ?CacheInterface $cache
+ * @return \React\Dns\Resolver\ResolverInterface
+ * @throws \InvalidArgumentException for invalid DNS server address
+ * @throws \UnderflowException when given DNS Config object has an empty list of nameservers
+ */
+ public function createCached($config, LoopInterface $loop = null, CacheInterface $cache = null)
+ {
+ // default to keeping maximum of 256 responses in cache unless explicitly given
+ if (!($cache instanceof CacheInterface)) {
+ $cache = new ArrayCache(256);
+ }
+
+ $executor = $this->createExecutor($config, $loop ?: Loop::get());
+ $executor = new CachingExecutor($executor, $cache);
+ $executor = $this->decorateHostsFileExecutor($executor);
+
+ return new Resolver($executor);
+ }
+
+ /**
+ * Tries to load the hosts file and decorates the given executor on success
+ *
+ * @param ExecutorInterface $executor
+ * @return ExecutorInterface
+ * @codeCoverageIgnore
+ */
+ private function decorateHostsFileExecutor(ExecutorInterface $executor)
+ {
+ try {
+ $executor = new HostsFileExecutor(
+ HostsFile::loadFromPathBlocking(),
+ $executor
+ );
+ } catch (\RuntimeException $e) {
+ // ignore this file if it can not be loaded
+ }
+
+ // Windows does not store localhost in hosts file by default but handles this internally
+ // To compensate for this, we explicitly use hard-coded defaults for localhost
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $executor = new HostsFileExecutor(
+ new HostsFile("127.0.0.1 localhost\n::1 localhost"),
+ $executor
+ );
+ }
+
+ return $executor;
+ }
+
+ /**
+ * @param Config|string $nameserver
+ * @param LoopInterface $loop
+ * @return CoopExecutor
+ * @throws \InvalidArgumentException for invalid DNS server address
+ * @throws \UnderflowException when given DNS Config object has an empty list of nameservers
+ */
+ private function createExecutor($nameserver, LoopInterface $loop)
+ {
+ if ($nameserver instanceof Config) {
+ if (!$nameserver->nameservers) {
+ throw new \UnderflowException('Empty config with no DNS servers');
+ }
+
+ // Hard-coded to check up to 3 DNS servers to match default limits in place in most systems (see MAXNS config).
+ // Note to future self: Recursion isn't too hard, but how deep do we really want to go?
+ $primary = reset($nameserver->nameservers);
+ $secondary = next($nameserver->nameservers);
+ $tertiary = next($nameserver->nameservers);
+
+ if ($tertiary !== false) {
+ // 3 DNS servers given => nest first with fallback for second and third
+ return new CoopExecutor(
+ new RetryExecutor(
+ new FallbackExecutor(
+ $this->createSingleExecutor($primary, $loop),
+ new FallbackExecutor(
+ $this->createSingleExecutor($secondary, $loop),
+ $this->createSingleExecutor($tertiary, $loop)
+ )
+ )
+ )
+ );
+ } elseif ($secondary !== false) {
+ // 2 DNS servers given => fallback from first to second
+ return new CoopExecutor(
+ new RetryExecutor(
+ new FallbackExecutor(
+ $this->createSingleExecutor($primary, $loop),
+ $this->createSingleExecutor($secondary, $loop)
+ )
+ )
+ );
+ } else {
+ // 1 DNS server given => use single executor
+ $nameserver = $primary;
+ }
+ }
+
+ return new CoopExecutor(new RetryExecutor($this->createSingleExecutor($nameserver, $loop)));
+ }
+
+ /**
+ * @param string $nameserver
+ * @param LoopInterface $loop
+ * @return ExecutorInterface
+ * @throws \InvalidArgumentException for invalid DNS server address
+ */
+ private function createSingleExecutor($nameserver, LoopInterface $loop)
+ {
+ $parts = \parse_url($nameserver);
+
+ if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') {
+ $executor = $this->createTcpExecutor($nameserver, $loop);
+ } elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') {
+ $executor = $this->createUdpExecutor($nameserver, $loop);
+ } else {
+ $executor = new SelectiveTransportExecutor(
+ $this->createUdpExecutor($nameserver, $loop),
+ $this->createTcpExecutor($nameserver, $loop)
+ );
+ }
+
+ return $executor;
+ }
+
+ /**
+ * @param string $nameserver
+ * @param LoopInterface $loop
+ * @return TimeoutExecutor
+ * @throws \InvalidArgumentException for invalid DNS server address
+ */
+ private function createTcpExecutor($nameserver, LoopInterface $loop)
+ {
+ return new TimeoutExecutor(
+ new TcpTransportExecutor($nameserver, $loop),
+ 5.0,
+ $loop
+ );
+ }
+
+ /**
+ * @param string $nameserver
+ * @param LoopInterface $loop
+ * @return TimeoutExecutor
+ * @throws \InvalidArgumentException for invalid DNS server address
+ */
+ private function createUdpExecutor($nameserver, LoopInterface $loop)
+ {
+ return new TimeoutExecutor(
+ new UdpTransportExecutor(
+ $nameserver,
+ $loop
+ ),
+ 5.0,
+ $loop
+ );
+ }
+}
diff --git a/vendor/react/dns/src/Resolver/Resolver.php b/vendor/react/dns/src/Resolver/Resolver.php
new file mode 100644
index 0000000..92926f3
--- /dev/null
+++ b/vendor/react/dns/src/Resolver/Resolver.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace React\Dns\Resolver;
+
+use React\Dns\Model\Message;
+use React\Dns\Query\ExecutorInterface;
+use React\Dns\Query\Query;
+use React\Dns\RecordNotFoundException;
+
+/**
+ * @see ResolverInterface for the base interface
+ */
+final class Resolver implements ResolverInterface
+{
+ private $executor;
+
+ public function __construct(ExecutorInterface $executor)
+ {
+ $this->executor = $executor;
+ }
+
+ public function resolve($domain)
+ {
+ return $this->resolveAll($domain, Message::TYPE_A)->then(function (array $ips) {
+ return $ips[array_rand($ips)];
+ });
+ }
+
+ public function resolveAll($domain, $type)
+ {
+ $query = new Query($domain, $type, Message::CLASS_IN);
+ $that = $this;
+
+ return $this->executor->query(
+ $query
+ )->then(function (Message $response) use ($query, $that) {
+ return $that->extractValues($query, $response);
+ });
+ }
+
+ /**
+ * [Internal] extract all resource record values from response for this query
+ *
+ * @param Query $query
+ * @param Message $response
+ * @return array
+ * @throws RecordNotFoundException when response indicates an error or contains no data
+ * @internal
+ */
+ public function extractValues(Query $query, Message $response)
+ {
+ // reject if response code indicates this is an error response message
+ $code = $response->rcode;
+ if ($code !== Message::RCODE_OK) {
+ switch ($code) {
+ case Message::RCODE_FORMAT_ERROR:
+ $message = 'Format Error';
+ break;
+ case Message::RCODE_SERVER_FAILURE:
+ $message = 'Server Failure';
+ break;
+ case Message::RCODE_NAME_ERROR:
+ $message = 'Non-Existent Domain / NXDOMAIN';
+ break;
+ case Message::RCODE_NOT_IMPLEMENTED:
+ $message = 'Not Implemented';
+ break;
+ case Message::RCODE_REFUSED:
+ $message = 'Refused';
+ break;
+ default:
+ $message = 'Unknown error response code ' . $code;
+ }
+ throw new RecordNotFoundException(
+ 'DNS query for ' . $query->describe() . ' returned an error response (' . $message . ')',
+ $code
+ );
+ }
+
+ $answers = $response->answers;
+ $addresses = $this->valuesByNameAndType($answers, $query->name, $query->type);
+
+ // reject if we did not receive a valid answer (domain is valid, but no record for this type could be found)
+ if (0 === count($addresses)) {
+ throw new RecordNotFoundException(
+ 'DNS query for ' . $query->describe() . ' did not return a valid answer (NOERROR / NODATA)'
+ );
+ }
+
+ return array_values($addresses);
+ }
+
+ /**
+ * @param \React\Dns\Model\Record[] $answers
+ * @param string $name
+ * @param int $type
+ * @return array
+ */
+ private function valuesByNameAndType(array $answers, $name, $type)
+ {
+ // return all record values for this name and type (if any)
+ $named = $this->filterByName($answers, $name);
+ $records = $this->filterByType($named, $type);
+ if ($records) {
+ return $this->mapRecordData($records);
+ }
+
+ // no matching records found? check if there are any matching CNAMEs instead
+ $cnameRecords = $this->filterByType($named, Message::TYPE_CNAME);
+ if ($cnameRecords) {
+ $cnames = $this->mapRecordData($cnameRecords);
+ foreach ($cnames as $cname) {
+ $records = array_merge(
+ $records,
+ $this->valuesByNameAndType($answers, $cname, $type)
+ );
+ }
+ }
+
+ return $records;
+ }
+
+ private function filterByName(array $answers, $name)
+ {
+ return $this->filterByField($answers, 'name', $name);
+ }
+
+ private function filterByType(array $answers, $type)
+ {
+ return $this->filterByField($answers, 'type', $type);
+ }
+
+ private function filterByField(array $answers, $field, $value)
+ {
+ $value = strtolower($value);
+ return array_filter($answers, function ($answer) use ($field, $value) {
+ return $value === strtolower($answer->$field);
+ });
+ }
+
+ private function mapRecordData(array $records)
+ {
+ return array_map(function ($record) {
+ return $record->data;
+ }, $records);
+ }
+}
diff --git a/vendor/react/dns/src/Resolver/ResolverInterface.php b/vendor/react/dns/src/Resolver/ResolverInterface.php
new file mode 100644
index 0000000..fe937dc
--- /dev/null
+++ b/vendor/react/dns/src/Resolver/ResolverInterface.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace React\Dns\Resolver;
+
+interface ResolverInterface
+{
+ /**
+ * Resolves the given $domain name to a single IPv4 address (type `A` query).
+ *
+ * ```php
+ * $resolver->resolve('reactphp.org')->then(function ($ip) {
+ * echo 'IP for reactphp.org is ' . $ip . PHP_EOL;
+ * });
+ * ```
+ *
+ * This is one of the main methods in this package. It sends a DNS query
+ * for the given $domain name to your DNS server and returns a single IP
+ * address on success.
+ *
+ * If the DNS server sends a DNS response message that contains more than
+ * one IP address for this query, it will randomly pick one of the IP
+ * addresses from the response. If you want the full list of IP addresses
+ * or want to send a different type of query, you should use the
+ * [`resolveAll()`](#resolveall) method instead.
+ *
+ * If the DNS server sends a DNS response message that indicates an error
+ * code, this method will reject with a `RecordNotFoundException`. Its
+ * message and code can be used to check for the response code.
+ *
+ * If the DNS communication fails and the server does not respond with a
+ * valid response message, this message will reject with an `Exception`.
+ *
+ * Pending DNS queries can be cancelled by cancelling its pending promise like so:
+ *
+ * ```php
+ * $promise = $resolver->resolve('reactphp.org');
+ *
+ * $promise->cancel();
+ * ```
+ *
+ * @param string $domain
+ * @return \React\Promise\PromiseInterface<string,\Exception>
+ * resolves with a single IP address on success or rejects with an Exception on error.
+ */
+ public function resolve($domain);
+
+ /**
+ * Resolves all record values for the given $domain name and query $type.
+ *
+ * ```php
+ * $resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) {
+ * echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
+ * });
+ *
+ * $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) {
+ * echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
+ * });
+ * ```
+ *
+ * This is one of the main methods in this package. It sends a DNS query
+ * for the given $domain name to your DNS server and returns a list with all
+ * record values on success.
+ *
+ * If the DNS server sends a DNS response message that contains one or more
+ * records for this query, it will return a list with all record values
+ * from the response. You can use the `Message::TYPE_*` constants to control
+ * which type of query will be sent. Note that this method always returns a
+ * list of record values, but each record value type depends on the query
+ * type. For example, it returns the IPv4 addresses for type `A` queries,
+ * the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`,
+ * `CNAME` and `PTR` queries and structured data for other queries. See also
+ * the `Record` documentation for more details.
+ *
+ * If the DNS server sends a DNS response message that indicates an error
+ * code, this method will reject with a `RecordNotFoundException`. Its
+ * message and code can be used to check for the response code.
+ *
+ * If the DNS communication fails and the server does not respond with a
+ * valid response message, this message will reject with an `Exception`.
+ *
+ * Pending DNS queries can be cancelled by cancelling its pending promise like so:
+ *
+ * ```php
+ * $promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA);
+ *
+ * $promise->cancel();
+ * ```
+ *
+ * @param string $domain
+ * @return \React\Promise\PromiseInterface<array,\Exception>
+ * Resolves with all record values on success or rejects with an Exception on error.
+ */
+ public function resolveAll($domain, $type);
+}