mirror of
https://github.com/danog/dns.git
synced 2024-11-30 04:29:06 +01:00
Merge
This commit is contained in:
commit
acf6e83e3c
@ -4,10 +4,20 @@ namespace Amp\Dns;
|
|||||||
|
|
||||||
final class Config
|
final class Config
|
||||||
{
|
{
|
||||||
|
/** @var array */
|
||||||
private $nameservers;
|
private $nameservers;
|
||||||
|
/** @var array */
|
||||||
private $knownHosts;
|
private $knownHosts;
|
||||||
|
/** @var int */
|
||||||
private $timeout;
|
private $timeout;
|
||||||
|
/** @var int */
|
||||||
private $attempts;
|
private $attempts;
|
||||||
|
/** @var array */
|
||||||
|
private $searchList = [];
|
||||||
|
/** @var int */
|
||||||
|
private $ndots = 1;
|
||||||
|
/** @var bool */
|
||||||
|
private $rotation = false;
|
||||||
|
|
||||||
public function __construct(array $nameservers, array $knownHosts = [], int $timeout = 3000, int $attempts = 2)
|
public function __construct(array $nameservers, array $knownHosts = [], int $timeout = 3000, int $attempts = 2)
|
||||||
{
|
{
|
||||||
@ -44,6 +54,39 @@ final class Config
|
|||||||
$this->attempts = $attempts;
|
$this->attempts = $attempts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withSearchList(array $searchList): self
|
||||||
|
{
|
||||||
|
$self = clone $this;
|
||||||
|
$self->searchList = $searchList;
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ConfigException
|
||||||
|
*/
|
||||||
|
public function withNdots(int $ndots): self
|
||||||
|
{
|
||||||
|
if ($ndots < 0) {
|
||||||
|
throw new ConfigException("Invalid ndots ({$ndots}), must be greater or equal to 0");
|
||||||
|
}
|
||||||
|
if ($ndots > 15) {
|
||||||
|
$ndots = 15;
|
||||||
|
}
|
||||||
|
$self = clone $this;
|
||||||
|
$self->ndots = $ndots;
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withRotationEnabled(bool $enabled = true): self
|
||||||
|
{
|
||||||
|
$self = clone $this;
|
||||||
|
$self->rotation = $enabled;
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
private function validateNameserver($nameserver)
|
private function validateNameserver($nameserver)
|
||||||
{
|
{
|
||||||
if (!$nameserver || !\is_string($nameserver)) {
|
if (!$nameserver || !\is_string($nameserver)) {
|
||||||
@ -101,4 +144,19 @@ final class Config
|
|||||||
{
|
{
|
||||||
return $this->attempts;
|
return $this->attempts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSearchList(): array
|
||||||
|
{
|
||||||
|
return $this->searchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNdots(): int
|
||||||
|
{
|
||||||
|
return $this->ndots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRotationEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->rotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
|
|
||||||
/** @var BlockingFallbackResolver */
|
/** @var BlockingFallbackResolver */
|
||||||
private $blockingFallbackResolver;
|
private $blockingFallbackResolver;
|
||||||
|
/** @var int */
|
||||||
|
private $nextNameserver = 0;
|
||||||
|
|
||||||
public function __construct(Cache $cache = null, ConfigLoader $configLoader = null)
|
public function __construct(Cache $cache = null, ConfigLoader $configLoader = null)
|
||||||
{
|
{
|
||||||
@ -135,7 +137,9 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$dots = \substr_count($name, ".");
|
||||||
|
// Should be replaced with $name[-1] from 7.1
|
||||||
|
$trailingDot = \substr($name, -1, 1) === ".";
|
||||||
$name = normalizeName($name);
|
$name = normalizeName($name);
|
||||||
|
|
||||||
if ($records = $this->queryHosts($name, $typeRestriction)) {
|
if ($records = $this->queryHosts($name, $typeRestriction)) {
|
||||||
@ -150,52 +154,69 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
: [new Record('127.0.0.1', Record::A, null)];
|
: [new Record('127.0.0.1', Record::A, null)];
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($redirects = 0; $redirects < 5; $redirects++) {
|
if (!$dots && \count($this->config->getSearchList()) === 0) {
|
||||||
try {
|
throw new DnsException("Giving up resolution of '{$name}', unknown host");
|
||||||
if ($typeRestriction) {
|
}
|
||||||
return yield $this->query($name, $typeRestriction);
|
|
||||||
|
$searchList = [null];
|
||||||
|
if (!$trailingDot && $dots < $this->config->getNdots()) {
|
||||||
|
$searchList = \array_merge($this->config->getSearchList(), $searchList);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($searchList as $search) {
|
||||||
|
for ($redirects = 0; $redirects < 5; $redirects++) {
|
||||||
|
$searchName = $name;
|
||||||
|
|
||||||
|
if ($search !== null) {
|
||||||
|
$searchName = $name . "." . $search;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
list(, $records) = yield Promise\some([
|
if ($typeRestriction) {
|
||||||
$this->query($name, Record::A),
|
return yield $this->query($searchName, $typeRestriction);
|
||||||
$this->query($name, Record::AAAA),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return \array_merge(...$records);
|
|
||||||
} catch (MultiReasonException $e) {
|
|
||||||
$errors = [];
|
|
||||||
|
|
||||||
foreach ($e->getReasons() as $reason) {
|
|
||||||
if ($reason instanceof NoRecordException) {
|
|
||||||
throw $reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
$errors[] = $reason->getMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new DnsException(
|
try {
|
||||||
"All query attempts failed for {$name}: " . \implode(", ", $errors),
|
list(, $records) = yield Promise\some([
|
||||||
0,
|
$this->query($searchName, Record::A),
|
||||||
$e
|
$this->query($searchName, Record::AAAA),
|
||||||
);
|
]);
|
||||||
}
|
|
||||||
} catch (NoRecordException $e) {
|
return \array_merge(...$records);
|
||||||
try {
|
} catch (MultiReasonException $e) {
|
||||||
/** @var Record[] $cnameRecords */
|
$errors = [];
|
||||||
$cnameRecords = yield $this->query($name, Record::CNAME);
|
|
||||||
$name = $cnameRecords[0]->getValue();
|
foreach ($e->getReasons() as $reason) {
|
||||||
continue;
|
if ($reason instanceof NoRecordException) {
|
||||||
|
throw $reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors[] = $reason->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DnsException(
|
||||||
|
"All query attempts failed for {$searchName}: " . \implode(", ", $errors),
|
||||||
|
0,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (NoRecordException $e) {
|
} catch (NoRecordException $e) {
|
||||||
/** @var Record[] $dnameRecords */
|
try {
|
||||||
$dnameRecords = yield $this->query($name, Record::DNAME);
|
/** @var Record[] $cnameRecords */
|
||||||
$name = $dnameRecords[0]->getValue();
|
$cnameRecords = yield $this->query($searchName, Record::CNAME);
|
||||||
continue;
|
$name = $cnameRecords[0]->getValue();
|
||||||
|
continue;
|
||||||
|
} catch (NoRecordException $e) {
|
||||||
|
/** @var Record[] $dnameRecords */
|
||||||
|
$dnameRecords = yield $this->query($searchName, Record::DNAME);
|
||||||
|
$name = $dnameRecords[0]->getValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new DnsException("Giving up resolution of '{$name}', too many redirects");
|
throw new DnsException("Giving up resolution of '{$searchName}', too many redirects");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +289,8 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
return $this->decodeCachedResult($name, $type, $cachedValue);
|
return $this->decodeCachedResult($name, $type, $cachedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$nameservers = $this->config->getNameservers();
|
$nameservers = $this->selectNameservers();
|
||||||
|
$nameserversCount = \count($nameservers);
|
||||||
$attempts = $this->config->getAttempts();
|
$attempts = $this->config->getAttempts();
|
||||||
$protocol = "udp";
|
$protocol = "udp";
|
||||||
$attempt = 0;
|
$attempt = 0;
|
||||||
@ -285,9 +307,7 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
unset($this->sockets[$uri]);
|
unset($this->sockets[$uri]);
|
||||||
$socket->close();
|
$socket->close();
|
||||||
|
|
||||||
/** @var Socket $server */
|
$uri = $protocol . "://" . $nameservers[$attempt % $nameserversCount];
|
||||||
$i = $attempt % \count($nameservers);
|
|
||||||
$uri = $protocol . "://" . $nameservers[$i];
|
|
||||||
$socket = yield $this->getSocket($uri);
|
$socket = yield $this->getSocket($uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,8 +329,7 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
if ($protocol !== "tcp") {
|
if ($protocol !== "tcp") {
|
||||||
// Retry with TCP, don't count attempt
|
// Retry with TCP, don't count attempt
|
||||||
$protocol = "tcp";
|
$protocol = "tcp";
|
||||||
$i = $attempt % \count($nameservers);
|
$uri = $protocol . "://" . $nameservers[$attempt % $nameserversCount];
|
||||||
$uri = $protocol . "://" . $nameservers[$i];
|
|
||||||
$socket = yield $this->getSocket($uri);
|
$socket = yield $this->getSocket($uri);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -356,8 +375,7 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
$socket->close();
|
$socket->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
$i = ++$attempt % \count($nameservers);
|
$uri = $protocol . "://" . $nameservers[++$attempt % $nameserversCount];
|
||||||
$uri = $protocol . "://" . $nameservers[$i];
|
|
||||||
$socket = yield $this->getSocket($uri);
|
$socket = yield $this->getSocket($uri);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -488,10 +506,28 @@ final class Rfc1035StubResolver implements Resolver
|
|||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws DnsException
|
||||||
|
*/
|
||||||
private function assertAcceptableResponse(Message $response)
|
private function assertAcceptableResponse(Message $response)
|
||||||
{
|
{
|
||||||
if ($response->getResponseCode() !== 0) {
|
if ($response->getResponseCode() !== 0) {
|
||||||
throw new DnsException(\sprintf("Server returned error code: %d", $response->getResponseCode()));
|
throw new DnsException(\sprintf("Server returned error code: %d", $response->getResponseCode()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function selectNameservers(): array
|
||||||
|
{
|
||||||
|
$nameservers = $this->config->getNameservers();
|
||||||
|
|
||||||
|
if ($this->config->isRotationEnabled() && ($nameserversCount = \count($nameservers)) > 1) {
|
||||||
|
$nameservers = \array_merge(
|
||||||
|
\array_slice($nameservers, $this->nextNameserver),
|
||||||
|
\array_slice($nameservers, 0, $this->nextNameserver)
|
||||||
|
);
|
||||||
|
$this->nextNameserver = ++$this->nextNameserver % $nameserversCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nameservers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,23 @@ use function Amp\call;
|
|||||||
|
|
||||||
class UnixConfigLoader implements ConfigLoader
|
class UnixConfigLoader implements ConfigLoader
|
||||||
{
|
{
|
||||||
|
const MAX_NAMESERVERS = 3;
|
||||||
|
const MAX_DNS_SEARCH = 6;
|
||||||
|
|
||||||
|
const MAX_TIMEOUT = 30 * 1000;
|
||||||
|
const MAX_ATTEMPTS = 5;
|
||||||
|
const MAX_NDOTS = 15;
|
||||||
|
|
||||||
|
const DEFAULT_TIMEOUT = 5 * 1000;
|
||||||
|
const DEFAULT_ATTEMPTS = 2;
|
||||||
|
const DEFAULT_NDOTS = 1;
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = [
|
||||||
|
"timeout" => self::DEFAULT_TIMEOUT,
|
||||||
|
"attempts" => self::DEFAULT_ATTEMPTS,
|
||||||
|
"ndots" => self::DEFAULT_NDOTS,
|
||||||
|
"rotate" => false,
|
||||||
|
];
|
||||||
private $path;
|
private $path;
|
||||||
private $hostLoader;
|
private $hostLoader;
|
||||||
|
|
||||||
@ -40,8 +57,20 @@ class UnixConfigLoader implements ConfigLoader
|
|||||||
{
|
{
|
||||||
return call(function () {
|
return call(function () {
|
||||||
$nameservers = [];
|
$nameservers = [];
|
||||||
$timeout = 3000;
|
$searchList = [];
|
||||||
$attempts = 2;
|
$options = self::DEFAULT_OPTIONS;
|
||||||
|
$haveLocaldomainEnv = false;
|
||||||
|
|
||||||
|
/* Allow user to override the local domain definition. */
|
||||||
|
if ($localdomain = \getenv("LOCALDOMAIN")) {
|
||||||
|
/* Set search list to be blank-separated strings from rest of
|
||||||
|
env value. Permits users of LOCALDOMAIN to still have a
|
||||||
|
search list, and anyone to set the one that they want to use
|
||||||
|
as an individual (even more important now that the rfc1535
|
||||||
|
stuff restricts searches). */
|
||||||
|
$searchList = $this->splitOnWhitespace($localdomain);
|
||||||
|
$haveLocaldomainEnv = true;
|
||||||
|
}
|
||||||
|
|
||||||
$fileContent = yield $this->readFile($this->path);
|
$fileContent = yield $this->readFile($this->path);
|
||||||
|
|
||||||
@ -57,6 +86,9 @@ class UnixConfigLoader implements ConfigLoader
|
|||||||
list($type, $value) = $line;
|
list($type, $value) = $line;
|
||||||
|
|
||||||
if ($type === "nameserver") {
|
if ($type === "nameserver") {
|
||||||
|
if (\count($nameservers) === self::MAX_NAMESERVERS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$value = \trim($value);
|
$value = \trim($value);
|
||||||
$ip = @\inet_pton($value);
|
$ip = @\inet_pton($value);
|
||||||
|
|
||||||
@ -69,29 +101,90 @@ class UnixConfigLoader implements ConfigLoader
|
|||||||
} else { // IPv4
|
} else { // IPv4
|
||||||
$nameservers[] = $value . ":53";
|
$nameservers[] = $value . ":53";
|
||||||
}
|
}
|
||||||
|
} elseif ($type === "domain" && !$haveLocaldomainEnv) { // LOCALDOMAIN env overrides config
|
||||||
|
$searchList = $this->splitOnWhitespace($value);
|
||||||
|
} elseif ($type === "search" && !$haveLocaldomainEnv) { // LOCALDOMAIN env overrides config
|
||||||
|
$searchList = $this->splitOnWhitespace($value);
|
||||||
} elseif ($type === "options") {
|
} elseif ($type === "options") {
|
||||||
$optline = \preg_split('#\s+#', $value, 2);
|
$option = $this->parseOption($value);
|
||||||
|
if (\count($option) === 2) {
|
||||||
if (\count($optline) !== 2) {
|
$options[$option[0]] = $option[1];
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list($option, $value) = $optline;
|
|
||||||
|
|
||||||
switch ($option) {
|
|
||||||
case "timeout":
|
|
||||||
$timeout = (int) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "attempts":
|
|
||||||
$attempts = (int) $value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$hosts = yield $this->hostLoader->loadHosts();
|
$hosts = yield $this->hostLoader->loadHosts();
|
||||||
|
|
||||||
return new Config($nameservers, $hosts, $timeout, $attempts);
|
if (\count($searchList) === 0) {
|
||||||
|
$hostname = \gethostname();
|
||||||
|
$dot = \strpos(".", $hostname);
|
||||||
|
if ($dot !== false && $dot < \strlen($hostname)) {
|
||||||
|
$searchList = [
|
||||||
|
\substr($hostname, $dot),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (\count($searchList) > self::MAX_DNS_SEARCH) {
|
||||||
|
$searchList = \array_slice($searchList, 0, self::MAX_DNS_SEARCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
$resOptions = \getenv("RES_OPTIONS");
|
||||||
|
if ($resOptions) {
|
||||||
|
foreach ($this->splitOnWhitespace($resOptions) as $option) {
|
||||||
|
$option = $this->parseOption($option);
|
||||||
|
if (\count($option) === 2) {
|
||||||
|
$options[$option[0]] = $option[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = new Config($nameservers, $hosts, $options["timeout"], $options["attempts"]);
|
||||||
|
|
||||||
|
return $config->withSearchList($searchList)
|
||||||
|
->withNdots($options["ndots"])
|
||||||
|
->withRotationEnabled($options["rotate"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function splitOnWhitespace(string $names): array
|
||||||
|
{
|
||||||
|
return \preg_split("#\s+#", \trim($names));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseOption(string $option): array
|
||||||
|
{
|
||||||
|
$optline = \explode(':', $option, 2);
|
||||||
|
list($name, $value) = $optline + [1 => null];
|
||||||
|
|
||||||
|
switch ($name) {
|
||||||
|
case "timeout":
|
||||||
|
$value = (int) $value;
|
||||||
|
if ($value < 0) {
|
||||||
|
return []; // don't overwrite option value
|
||||||
|
}
|
||||||
|
// The value for this option is silently capped to 30s
|
||||||
|
return ["timeout", (int) \min($value * 1000, self::MAX_TIMEOUT)];
|
||||||
|
|
||||||
|
case "attempts":
|
||||||
|
$value = (int) $value;
|
||||||
|
if ($value < 0) {
|
||||||
|
return []; // don't overwrite option value
|
||||||
|
}
|
||||||
|
// The value for this option is silently capped to 5
|
||||||
|
return ["attempts", (int) \min($value, self::MAX_ATTEMPTS)];
|
||||||
|
|
||||||
|
case "ndots":
|
||||||
|
$value = (int) $value;
|
||||||
|
if ($value < 0) {
|
||||||
|
return []; // don't overwrite option value
|
||||||
|
}
|
||||||
|
// The value for this option is silently capped to 15
|
||||||
|
return ["ndots", (int) \min($value, self::MAX_NDOTS)];
|
||||||
|
|
||||||
|
case "rotate":
|
||||||
|
return ["rotate", true];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,4 +74,38 @@ class ConfigTest extends TestCase
|
|||||||
$this->expectException(ConfigException::class);
|
$this->expectException(ConfigException::class);
|
||||||
new Config(["127.0.0.1"], [], 500, 0);
|
new Config(["127.0.0.1"], [], 500, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testInvalidNtods()
|
||||||
|
{
|
||||||
|
$this->expectException(ConfigException::class);
|
||||||
|
$config = new Config(["127.0.0.1"]);
|
||||||
|
$config->withNdots(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNdots()
|
||||||
|
{
|
||||||
|
$config = new Config(["127.0.0.1"]);
|
||||||
|
$config = $config->withNdots(1);
|
||||||
|
$this->assertSame(1, $config->getNdots());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSearchList()
|
||||||
|
{
|
||||||
|
$config = new Config(["127.0.0.1"]);
|
||||||
|
$config = $config->withSearchList(['local']);
|
||||||
|
$this->assertSame(['local'], $config->getSearchList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRotationEnabled()
|
||||||
|
{
|
||||||
|
$config = new Config(["127.0.0.1"]);
|
||||||
|
$config = $config->withRotationEnabled(true);
|
||||||
|
$this->assertTrue($config->isRotationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRotationDisabled()
|
||||||
|
{
|
||||||
|
$config = new Config(["127.0.0.1"]);
|
||||||
|
$this->assertFalse($config->isRotationEnabled());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,17 @@
|
|||||||
|
|
||||||
namespace Amp\Dns\Test;
|
namespace Amp\Dns\Test;
|
||||||
|
|
||||||
|
use Amp\Cache\NullCache;
|
||||||
use Amp\Dns;
|
use Amp\Dns;
|
||||||
use Amp\Dns\BlockingFallbackResolver;
|
use Amp\Dns\BlockingFallbackResolver;
|
||||||
|
use Amp\Dns\DnsException;
|
||||||
use Amp\Dns\Record;
|
use Amp\Dns\Record;
|
||||||
|
use Amp\Dns\UnixConfigLoader;
|
||||||
|
use Amp\Dns\WindowsConfigLoader;
|
||||||
use Amp\Loop;
|
use Amp\Loop;
|
||||||
use Amp\PHPUnit\TestCase;
|
use Amp\PHPUnit\TestCase;
|
||||||
|
use Amp\Success;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
||||||
class IntegrationTest extends TestCase
|
class IntegrationTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -25,7 +31,7 @@ class IntegrationTest extends TestCase
|
|||||||
$inAddr = @\inet_pton($record->getValue());
|
$inAddr = @\inet_pton($record->getValue());
|
||||||
$this->assertNotFalse(
|
$this->assertNotFalse(
|
||||||
$inAddr,
|
$inAddr,
|
||||||
"Server name $hostname did not resolve to a valid IP address"
|
"Server name {$hostname} did not resolve to a valid IP address"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -95,6 +101,87 @@ class IntegrationTest extends TestCase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testResolveUsingSearchList()
|
||||||
|
{
|
||||||
|
Loop::run(function () {
|
||||||
|
$configLoader = \stripos(PHP_OS, "win") === 0
|
||||||
|
? new WindowsConfigLoader()
|
||||||
|
: new UnixConfigLoader();
|
||||||
|
/** @var Dns\Config $config */
|
||||||
|
$config = yield $configLoader->loadConfig();
|
||||||
|
$config = $config->withSearchList(['kelunik.com']);
|
||||||
|
$config = $config->withNdots(1);
|
||||||
|
/** @var Dns\ConfigLoader|MockObject $configLoader */
|
||||||
|
$configLoader = $this->createMock(Dns\ConfigLoader::class);
|
||||||
|
$configLoader->expects($this->once())
|
||||||
|
->method('loadConfig')
|
||||||
|
->willReturn(new Success($config));
|
||||||
|
|
||||||
|
Dns\resolver(new Dns\Rfc1035StubResolver(null, $configLoader));
|
||||||
|
$result = yield Dns\resolve('blog');
|
||||||
|
|
||||||
|
/** @var Record $record */
|
||||||
|
$record = $result[0];
|
||||||
|
$inAddr = @\inet_pton($record->getValue());
|
||||||
|
$this->assertNotFalse(
|
||||||
|
$inAddr,
|
||||||
|
"Server name blog.kelunik.com did not resolve to a valid IP address"
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = yield Dns\query('blog.kelunik.com', Dns\Record::A);
|
||||||
|
/** @var Record $record */
|
||||||
|
$record = $result[0];
|
||||||
|
$this->assertSame($inAddr, @\inet_pton($record->getValue()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailResolveRootedDomainWhenSearchListDefined()
|
||||||
|
{
|
||||||
|
Loop::run(function () {
|
||||||
|
$configLoader = \stripos(PHP_OS, "win") === 0
|
||||||
|
? new WindowsConfigLoader()
|
||||||
|
: new UnixConfigLoader();
|
||||||
|
/** @var Dns\Config $config */
|
||||||
|
$config = yield $configLoader->loadConfig();
|
||||||
|
$config = $config->withSearchList(['kelunik.com']);
|
||||||
|
$config = $config->withNdots(1);
|
||||||
|
/** @var Dns\ConfigLoader|MockObject $configLoader */
|
||||||
|
$configLoader = $this->createMock(Dns\ConfigLoader::class);
|
||||||
|
$configLoader->expects($this->once())
|
||||||
|
->method('loadConfig')
|
||||||
|
->willReturn(new Success($config));
|
||||||
|
|
||||||
|
Dns\resolver(new Dns\Rfc1035StubResolver(null, $configLoader));
|
||||||
|
$this->expectException(DnsException::class);
|
||||||
|
yield Dns\resolve('blog.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResolveWithRotateList()
|
||||||
|
{
|
||||||
|
Loop::run(function () {
|
||||||
|
/** @var Dns\ConfigLoader|MockObject $configLoader */
|
||||||
|
$configLoader = $this->createMock(Dns\ConfigLoader::class);
|
||||||
|
$config = new Dns\Config([
|
||||||
|
'208.67.222.220:53', // Opendns, US
|
||||||
|
'195.243.214.4:53', // Deutche Telecom AG, DE
|
||||||
|
]);
|
||||||
|
$config = $config->withRotationEnabled(true);
|
||||||
|
$configLoader->expects($this->once())
|
||||||
|
->method('loadConfig')
|
||||||
|
->willReturn(new Success($config));
|
||||||
|
|
||||||
|
$resolver = new Dns\Rfc1035StubResolver(new NullCache(), $configLoader);
|
||||||
|
|
||||||
|
/** @var Record $record1 */
|
||||||
|
list($record1) = yield $resolver->query('facebook.com', Dns\Record::A);
|
||||||
|
/** @var Record $record2 */
|
||||||
|
list($record2) = yield $resolver->query('facebook.com', Dns\Record::A);
|
||||||
|
|
||||||
|
$this->assertNotSame($record1->getValue(), $record2->getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function testPtrLookup()
|
public function testPtrLookup()
|
||||||
{
|
{
|
||||||
Loop::run(function () {
|
Loop::run(function () {
|
||||||
@ -179,6 +266,7 @@ class IntegrationTest extends TestCase
|
|||||||
["localhost"],
|
["localhost"],
|
||||||
["192.168.0.1"],
|
["192.168.0.1"],
|
||||||
["::1"],
|
["::1"],
|
||||||
|
["dns.google."], /* that's rooted domain name - cannot use searchList */
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,87 @@ class UnixConfigLoaderTest extends TestCase
|
|||||||
"[2001:4860:4860::8888]:53",
|
"[2001:4860:4860::8888]:53",
|
||||||
], $result->getNameservers());
|
], $result->getNameservers());
|
||||||
|
|
||||||
$this->assertSame(5000, $result->getTimeout());
|
$this->assertSame(30000, $result->getTimeout());
|
||||||
$this->assertSame(3, $result->getAttempts());
|
$this->assertSame(3, $result->getAttempts());
|
||||||
|
$this->assertEmpty($result->getSearchList());
|
||||||
|
$this->assertSame(1, $result->getNdots());
|
||||||
|
$this->assertFalse($result->isRotationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithSearchList()
|
||||||
|
{
|
||||||
|
$loader = new UnixConfigLoader(__DIR__ . "/data/resolv-search.conf");
|
||||||
|
|
||||||
|
/** @var Config $result */
|
||||||
|
$result = wait($loader->loadConfig());
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
"127.0.0.1:53",
|
||||||
|
"[2001:4860:4860::8888]:53",
|
||||||
|
], $result->getNameservers());
|
||||||
|
|
||||||
|
$this->assertSame(30000, $result->getTimeout());
|
||||||
|
$this->assertSame(3, $result->getAttempts());
|
||||||
|
$this->assertSame(['local', 'local1', 'local2', 'local3', 'local4', 'local5'], $result->getSearchList());
|
||||||
|
$this->assertSame(15, $result->getNdots());
|
||||||
|
$this->assertFalse($result->isRotationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithRotateOption()
|
||||||
|
{
|
||||||
|
$loader = new UnixConfigLoader(__DIR__ . "/data/resolv-rotate.conf");
|
||||||
|
|
||||||
|
/** @var Config $result */
|
||||||
|
$result = wait($loader->loadConfig());
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
"127.0.0.1:53",
|
||||||
|
"[2001:4860:4860::8888]:53",
|
||||||
|
], $result->getNameservers());
|
||||||
|
|
||||||
|
$this->assertSame(5000, $result->getTimeout());
|
||||||
|
$this->assertSame(2, $result->getAttempts());
|
||||||
|
$this->assertTrue($result->isRotationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithNegativeOption()
|
||||||
|
{
|
||||||
|
$loader = new UnixConfigLoader(__DIR__ . "/data/resolv-negative-option-values.conf");
|
||||||
|
|
||||||
|
/** @var Config $result */
|
||||||
|
$result = wait($loader->loadConfig());
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
"127.0.0.1:53",
|
||||||
|
"[2001:4860:4860::8888]:53",
|
||||||
|
], $result->getNameservers());
|
||||||
|
|
||||||
|
$this->assertSame(5000, $result->getTimeout());
|
||||||
|
$this->assertSame(2, $result->getAttempts());
|
||||||
|
$this->assertSame(1, $result->getNdots());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithEnvironmentOverride()
|
||||||
|
{
|
||||||
|
\putenv("LOCALDOMAIN=local");
|
||||||
|
\putenv("RES_OPTIONS=timeout:1 attempts:10 ndots:10 rotate");
|
||||||
|
|
||||||
|
$loader = new UnixConfigLoader(__DIR__ . "/data/resolv.conf");
|
||||||
|
|
||||||
|
/** @var Config $result */
|
||||||
|
$result = wait($loader->loadConfig());
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
"127.0.0.1:53",
|
||||||
|
"[2001:4860:4860::8888]:53",
|
||||||
|
], $result->getNameservers());
|
||||||
|
|
||||||
|
$this->assertSame(['local'], $result->getSearchList());
|
||||||
|
|
||||||
|
$this->assertSame(1000, $result->getTimeout());
|
||||||
|
$this->assertSame(5, $result->getAttempts());
|
||||||
|
$this->assertSame(10, $result->getNdots());
|
||||||
|
$this->assertTrue($result->isRotationEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNoDefaultsOnConfNotFound()
|
public function testNoDefaultsOnConfNotFound()
|
||||||
|
9
test/data/resolv-negative-option-values.conf
Normal file
9
test/data/resolv-negative-option-values.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Default
|
||||||
|
nameserver 127.0.0.1
|
||||||
|
|
||||||
|
# Google Fallback
|
||||||
|
nameserver 2001:4860:4860::8888
|
||||||
|
|
||||||
|
options timeout:-1
|
||||||
|
options attempts:-1
|
||||||
|
options ndots:-1
|
10
test/data/resolv-rotate.conf
Normal file
10
test/data/resolv-rotate.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Default
|
||||||
|
nameserver 127.0.0.1
|
||||||
|
|
||||||
|
# Google Fallback
|
||||||
|
nameserver 2001:4860:4860::8888
|
||||||
|
|
||||||
|
# Invalid server gets ignored
|
||||||
|
nameserver foobar
|
||||||
|
|
||||||
|
options rotate
|
17
test/data/resolv-search.conf
Normal file
17
test/data/resolv-search.conf
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Default
|
||||||
|
nameserver 127.0.0.1
|
||||||
|
|
||||||
|
# Google Fallback
|
||||||
|
nameserver 2001:4860:4860::8888
|
||||||
|
|
||||||
|
# Invalid server gets ignored
|
||||||
|
nameserver foobar
|
||||||
|
|
||||||
|
search local local1 local2 local3 local4 local5 local6
|
||||||
|
|
||||||
|
options timeout:5000
|
||||||
|
options attempts:3
|
||||||
|
options ndots:20
|
||||||
|
|
||||||
|
# Unknown option gets ignored
|
||||||
|
options foo
|
@ -7,8 +7,8 @@ nameserver 2001:4860:4860::8888
|
|||||||
# Invalid server gets ignored
|
# Invalid server gets ignored
|
||||||
nameserver foobar
|
nameserver foobar
|
||||||
|
|
||||||
options timeout 5000
|
options timeout:5000
|
||||||
options attempts 3
|
options attempts:3
|
||||||
|
|
||||||
# Unknown option gets ignored
|
# Unknown option gets ignored
|
||||||
options foo
|
options foo
|
Loading…
Reference in New Issue
Block a user