2017-06-17 12:30:38 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Amp\Dns;
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
final class Config
|
|
|
|
{
|
2019-07-30 18:09:33 +02:00
|
|
|
/** @var array */
|
2017-06-17 12:30:38 +02:00
|
|
|
private $nameservers;
|
2019-07-30 18:09:33 +02:00
|
|
|
/** @var array */
|
2017-06-17 12:30:38 +02:00
|
|
|
private $knownHosts;
|
2019-07-30 18:09:33 +02:00
|
|
|
/** @var int */
|
2017-06-17 12:30:38 +02:00
|
|
|
private $timeout;
|
2019-07-30 18:09:33 +02:00
|
|
|
/** @var int */
|
2017-06-17 12:30:38 +02:00
|
|
|
private $attempts;
|
2019-07-30 18:09:33 +02:00
|
|
|
/** @var array */
|
|
|
|
private $searchList = [];
|
|
|
|
/** @var int */
|
|
|
|
private $ndots = 1;
|
|
|
|
/** @var bool */
|
|
|
|
private $rotation = false;
|
2017-06-17 12:30:38 +02:00
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
public function __construct(array $nameservers, array $knownHosts = [], int $timeout = 3000, int $attempts = 2)
|
|
|
|
{
|
2017-06-17 12:30:38 +02:00
|
|
|
if (\count($nameservers) < 1) {
|
|
|
|
throw new ConfigException("At least one nameserver is required for a valid config");
|
|
|
|
}
|
|
|
|
|
2017-06-22 23:25:21 +02:00
|
|
|
foreach ($nameservers as $nameserver) {
|
|
|
|
$this->validateNameserver($nameserver);
|
|
|
|
}
|
|
|
|
|
2017-06-17 12:30:38 +02:00
|
|
|
if ($timeout < 0) {
|
|
|
|
throw new ConfigException("Invalid timeout ({$timeout}), must be 0 or greater");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($attempts < 1) {
|
|
|
|
throw new ConfigException("Invalid attempt count ({$attempts}), must be 1 or greater");
|
|
|
|
}
|
|
|
|
|
2018-12-24 12:49:01 +01:00
|
|
|
// Windows does not include localhost in its host file. Fetch it from the system instead
|
|
|
|
if (!isset($knownHosts[Record::A]["localhost"]) && !isset($knownHosts[Record::AAAA]["localhost"])) {
|
|
|
|
// PHP currently provides no way to **resolve** IPv6 hostnames (not even with fallback)
|
|
|
|
$local = \gethostbyname("localhost");
|
|
|
|
if ($local !== "localhost") {
|
|
|
|
$knownHosts[Record::A]["localhost"] = $local;
|
|
|
|
} else {
|
|
|
|
$knownHosts[Record::AAAA]["localhost"] = '::1';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-17 12:30:38 +02:00
|
|
|
$this->nameservers = $nameservers;
|
|
|
|
$this->knownHosts = $knownHosts;
|
|
|
|
$this->timeout = $timeout;
|
|
|
|
$this->attempts = $attempts;
|
|
|
|
}
|
|
|
|
|
2019-07-30 18:09:33 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
private function validateNameserver($nameserver)
|
|
|
|
{
|
2017-06-22 23:39:13 +02:00
|
|
|
if (!$nameserver || !\is_string($nameserver)) {
|
2017-06-22 23:25:21 +02:00
|
|
|
throw new ConfigException("Invalid nameserver: {$nameserver}");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($nameserver[0] === "[") { // IPv6
|
|
|
|
$addr = \strstr(\substr($nameserver, 1), "]", true);
|
|
|
|
$port = \substr($nameserver, \strrpos($nameserver, "]") + 1);
|
|
|
|
|
|
|
|
if ($port !== "" && !\preg_match("(^:(\\d+)$)", $port, $match)) {
|
|
|
|
throw new ConfigException("Invalid nameserver: {$nameserver}");
|
|
|
|
}
|
|
|
|
|
|
|
|
$port = $port === "" ? 53 : \substr($port, 1);
|
|
|
|
} else { // IPv4
|
|
|
|
$arr = \explode(":", $nameserver, 2);
|
|
|
|
|
|
|
|
if (\count($arr) === 2) {
|
|
|
|
list($addr, $port) = $arr;
|
|
|
|
} else {
|
|
|
|
$addr = $arr[0];
|
|
|
|
$port = 53;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$addr = \trim($addr, "[]");
|
|
|
|
$port = (int) $port;
|
|
|
|
|
|
|
|
if (!$inAddr = @\inet_pton($addr)) {
|
|
|
|
throw new ConfigException("Invalid server IP: {$addr}");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($port < 1 || $port > 65535) {
|
|
|
|
throw new ConfigException("Invalid server port: {$port}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
public function getNameservers(): array
|
|
|
|
{
|
2017-06-17 12:30:38 +02:00
|
|
|
return $this->nameservers;
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
public function getKnownHosts(): array
|
|
|
|
{
|
2017-06-17 12:30:38 +02:00
|
|
|
return $this->knownHosts;
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
public function getTimeout(): int
|
|
|
|
{
|
2017-06-17 12:30:38 +02:00
|
|
|
return $this->timeout;
|
|
|
|
}
|
|
|
|
|
2019-01-25 02:27:47 +01:00
|
|
|
public function getAttempts(): int
|
|
|
|
{
|
2017-06-17 12:30:38 +02:00
|
|
|
return $this->attempts;
|
|
|
|
}
|
2019-07-30 18:09:33 +02:00
|
|
|
|
|
|
|
public function getSearchList(): array
|
|
|
|
{
|
|
|
|
return $this->searchList;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getNdots(): int
|
|
|
|
{
|
|
|
|
return $this->ndots;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function isRotationEnabled(): bool
|
|
|
|
{
|
|
|
|
return $this->rotation;
|
|
|
|
}
|
2017-06-17 12:30:38 +02:00
|
|
|
}
|