fix(DnsPinning): Ensure to always lookup based on FQDN

Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
This commit is contained in:
David Dreschner 2026-03-21 19:32:53 +01:00
parent 753e6ee442
commit 5bc0ba6a51
No known key found for this signature in database
2 changed files with 31 additions and 20 deletions

View file

@ -25,6 +25,17 @@ class DnsPinMiddleware {
) {
}
/**
* DNS lookups must end with a dot to be marked as
* FQDN. Otherwise, a record without answer may trigger
* a lookup on the local domain name. See GitHub
* issue #56489 for details.
*/
private function enforceFqdn(string $hostname): string {
$trimmedHostname = rtrim($hostname, '.');
return "$trimmedHostname.";
}
/**
* Fetch soa record for a target
*/
@ -35,7 +46,7 @@ class DnsPinMiddleware {
$second = array_pop($labels);
$hostname = $second . '.' . $top;
$responses = $this->dnsGetRecord($hostname, DNS_SOA);
$responses = $this->dnsGetRecord($this->enforceFqdn($hostname), DNS_SOA);
if ($responses === false || count($responses) === 0) {
return null;
@ -68,7 +79,7 @@ class DnsPinMiddleware {
continue;
}
$dnsResponses = $this->dnsGetRecord($target, $dnsType);
$dnsResponses = $this->dnsGetRecord($this->enforceFqdn($target), $dnsType);
if ($dnsResponses !== false && count($dnsResponses) > 0) {
foreach ($dnsResponses as $dnsResponse) {
if (isset($dnsResponse['ip'])) {

View file

@ -61,7 +61,7 @@ class DnsPinMiddlewareTest extends TestCase {
->method('dnsGetRecord')
->willReturnCallback(function (string $hostname, int $type) {
// example.com SOA
if ($hostname === 'example.com') {
if ($hostname === 'example.com.') {
return match ($type) {
DNS_SOA => [
[
@ -76,7 +76,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.com A, AAAA, CNAME
if ($hostname === 'www.example.com') {
if ($hostname === 'www.example.com.') {
return match ($type) {
DNS_A => [],
DNS_AAAA => [],
@ -93,7 +93,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net SOA
if ($hostname === 'example.net') {
if ($hostname === 'example.net.') {
return match ($type) {
DNS_SOA => [
[
@ -108,7 +108,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net A, AAAA, CNAME
if ($hostname === 'www.example.net') {
if ($hostname === 'www.example.net.') {
return match ($type) {
DNS_A => [
[
@ -154,7 +154,7 @@ class DnsPinMiddlewareTest extends TestCase {
->method('dnsGetRecord')
->willReturnCallback(function (string $hostname, int $type) {
// example.com SOA
if ($hostname === 'example.com') {
if ($hostname === 'example.com.') {
return match ($type) {
DNS_SOA => [
[
@ -169,7 +169,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.com A, AAAA, CNAME
if ($hostname === 'www.example.com') {
if ($hostname === 'www.example.com.') {
return match ($type) {
DNS_A => [],
DNS_AAAA => [],
@ -186,7 +186,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net SOA
if ($hostname === 'example.net') {
if ($hostname === 'example.net.') {
return match ($type) {
DNS_SOA => [
[
@ -201,7 +201,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net A, AAAA, CNAME
if ($hostname === 'www.example.net') {
if ($hostname === 'www.example.net.') {
return match ($type) {
DNS_A => [
[
@ -378,7 +378,7 @@ class DnsPinMiddlewareTest extends TestCase {
->method('dnsGetRecord')
->willReturnCallback(function (string $hostname, int $type) {
// example.com SOA
if ($hostname === 'example.com') {
if ($hostname === 'example.com.') {
return match ($type) {
DNS_SOA => [
[
@ -393,7 +393,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.com A, AAAA, CNAME
if ($hostname === 'www.example.com') {
if ($hostname === 'www.example.com.') {
return match ($type) {
DNS_A => [],
DNS_AAAA => [],
@ -410,7 +410,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net SOA
if ($hostname === 'example.net') {
if ($hostname === 'example.net.') {
return match ($type) {
DNS_SOA => [
[
@ -425,7 +425,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net A, AAAA, CNAME
if ($hostname === 'www.example.net') {
if ($hostname === 'www.example.net.') {
return match ($type) {
DNS_A => [
[
@ -496,7 +496,7 @@ class DnsPinMiddlewareTest extends TestCase {
$dnsQueries[] = $hostname . $type;
// example.com SOA
if ($hostname === 'example.com') {
if ($hostname === 'example.com.') {
return match ($type) {
DNS_SOA => [
[
@ -511,7 +511,7 @@ class DnsPinMiddlewareTest extends TestCase {
}
// example.net A, AAAA, CNAME
if ($hostname === 'subsubdomain.subdomain.example.com') {
if ($hostname === 'subsubdomain.subdomain.example.com.') {
return match ($type) {
DNS_A => [
[
@ -540,10 +540,10 @@ class DnsPinMiddlewareTest extends TestCase {
);
$this->assertCount(3, $dnsQueries);
$this->assertContains('example.com' . DNS_SOA, $dnsQueries);
$this->assertContains('subsubdomain.subdomain.example.com' . DNS_A, $dnsQueries);
$this->assertContains('subsubdomain.subdomain.example.com' . DNS_AAAA, $dnsQueries);
$this->assertContains('example.com.' . DNS_SOA, $dnsQueries);
$this->assertContains('subsubdomain.subdomain.example.com.' . DNS_A, $dnsQueries);
$this->assertContains('subsubdomain.subdomain.example.com.' . DNS_AAAA, $dnsQueries);
// CNAME should not be queried if A or AAAA succeeded already
$this->assertNotContains('subsubdomain.subdomain.example.com' . DNS_CNAME, $dnsQueries);
$this->assertNotContains('subsubdomain.subdomain.example.com.' . DNS_CNAME, $dnsQueries);
}
}