Skip to content

Commit d2c8de5

Browse files
committed
Fix soa zone answer
1 parent 574327f commit d2c8de5

2 files changed

Lines changed: 67 additions & 0 deletions

File tree

src/DNS/Zone/Resolver.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ public static function lookup(Message $query, Zone $zone): Message
4242
$records = self::selectBestRecords($query, $zone);
4343

4444
if (empty($records)) {
45+
// SOA is stored separately; if querying SOA at the zone apex, return it
46+
if ($question->type === Record::TYPE_SOA && $question->name === $zone->name) {
47+
return Message::response(
48+
header: $query->header,
49+
responseCode: Message::RCODE_NOERROR,
50+
questions: $query->questions,
51+
answers: [$zone->soa],
52+
authoritative: true,
53+
recursionAvailable: false
54+
);
55+
}
56+
4557
return Message::response(
4658
header: $query->header,
4759
responseCode: Message::RCODE_NXDOMAIN,
@@ -157,6 +169,18 @@ private static function handleExactMatch(array $records, Message $query, Zone $z
157169
$isAuthoritative = $zone->isAuthoritative($question->name);
158170

159171
if ($isAuthoritative) {
172+
// SOA is stored separately in Zone; handle SOA queries at the zone apex
173+
if ($question->type === Record::TYPE_SOA && $question->name === $zone->name) {
174+
return Message::response(
175+
header: $query->header,
176+
responseCode: Message::RCODE_NOERROR,
177+
questions: $query->questions,
178+
answers: [$zone->soa],
179+
authoritative: true,
180+
recursionAvailable: false
181+
);
182+
}
183+
160184
// Path E1: Exact match of type
161185
$exactTypeRecords = array_filter(
162186
$records,

tests/unit/DNS/Zone/ResolverTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,49 @@ public function testLookupReturnsApexAAAARecord(): void
465465
$this->assertSame($aaaaRecord, $response->answers[0]);
466466
}
467467

468+
public function testLookupReturnsSoaAnswerForApexSoaQueryWithRecords(): void
469+
{
470+
$soa = new Record(
471+
'example.com',
472+
Record::TYPE_SOA,
473+
ttl: 300,
474+
rdata: 'ns1.appwrite.zone. team@appwrite.io. 1761705275 3600 600 86400 300'
475+
);
476+
$aRecord = new Record('example.com', Record::TYPE_A, ttl: 3600, rdata: '1.1.1.1');
477+
$zone = new Zone('example.com', [$aRecord], $soa);
478+
479+
$question = new Question('example.com', Record::TYPE_SOA);
480+
$query = Message::query($question);
481+
$response = Resolver::lookup($query, $zone);
482+
483+
$this->assertSame(Message::RCODE_NOERROR, $response->header->responseCode);
484+
$this->assertCount(1, $response->answers);
485+
$this->assertSame($soa, $response->answers[0]);
486+
$this->assertTrue($response->header->authoritative);
487+
$this->assertFalse($response->header->recursionAvailable);
488+
}
489+
490+
public function testLookupReturnsSoaAnswerForApexSoaQueryWithNoRecords(): void
491+
{
492+
$soa = new Record(
493+
'example.com',
494+
Record::TYPE_SOA,
495+
ttl: 300,
496+
rdata: 'ns1.appwrite.zone. team@appwrite.io. 1761705275 3600 600 86400 300'
497+
);
498+
$zone = new Zone('example.com', [], $soa);
499+
500+
$question = new Question('example.com', Record::TYPE_SOA);
501+
$query = Message::query($question);
502+
$response = Resolver::lookup($query, $zone);
503+
504+
$this->assertSame(Message::RCODE_NOERROR, $response->header->responseCode);
505+
$this->assertCount(1, $response->answers);
506+
$this->assertSame($soa, $response->answers[0]);
507+
$this->assertTrue($response->header->authoritative);
508+
$this->assertFalse($response->header->recursionAvailable);
509+
}
510+
468511
public function testLookupReturnsSoaInAuthorityForApexNonNSQuery(): void
469512
{
470513
$soa = new Record(

0 commit comments

Comments
 (0)