Unverified Commit ff126e8b authored by Sam Williams's avatar Sam Williams Committed by GitHub
Browse files

Merge pull request #41 from Badcow/PTR

Ensure reverse records parse correctly.
parents 73f8f948 a2906357
......@@ -62,7 +62,11 @@ class Parser
*/
public function __construct(array $rdataHandlers = [])
{
$this->rdataHandlers = array_merge(RdataHandlers::getHandlers(), $rdataHandlers);
$this->rdataHandlers = array_merge(
RdataHandlers::getHandlers(),
['PTR' => __CLASS__.'::ptrHandler'],
$rdataHandlers
);
}
/**
......@@ -127,7 +131,6 @@ class Parser
{
if ($this->isTTL($iterator)) {
$this->currentResourceRecord->setTtl((int) $iterator->current());
$this->lastStatedTtl = (int) $iterator->current();
$iterator->next();
$this->processEntry($iterator);
......@@ -136,7 +139,6 @@ class Parser
if ($this->isClass($iterator)) {
$this->currentResourceRecord->setClass(strtoupper($iterator->current()));
$this->lastStatedClass = strtoupper($iterator->current());
$iterator->next();
$this->processEntry($iterator);
......@@ -145,7 +147,6 @@ class Parser
if ($this->isResourceName($iterator) && null === $this->currentResourceRecord->getName()) {
$this->currentResourceRecord->setName($iterator->current());
$this->lastStatedDomain = $iterator->current();
$iterator->next();
$this->processEntry($iterator);
......@@ -171,14 +172,20 @@ class Parser
{
if (null === $this->currentResourceRecord->getName()) {
$this->currentResourceRecord->setName($this->lastStatedDomain);
} else {
$this->lastStatedDomain = $this->currentResourceRecord->getName();
}
if (null === $this->currentResourceRecord->getTtl()) {
$this->currentResourceRecord->setTtl($this->lastStatedTtl);
} else {
$this->lastStatedTtl = $this->currentResourceRecord->getTtl();
}
if (null === $this->currentResourceRecord->getClass()) {
$this->currentResourceRecord->setClass($this->lastStatedClass);
} else {
$this->lastStatedClass = $this->currentResourceRecord->getClass();
}
}
......@@ -211,21 +218,25 @@ class Parser
}
$isName = $this->isTTL($iterator) ||
$this->isClass($iterator) ||
$this->isClass($iterator, 'DOMAIN') ||
$this->isType($iterator);
$iterator->prev();
return $isName;
}
private function isClass(ResourceRecordIterator $iterator): bool
private function isClass(ResourceRecordIterator $iterator, $origin = null): bool
{
if (!Classes::isValid($iterator->current())) {
return false;
}
$iterator->next();
$isClass = $this->isTTL($iterator) || $this->isType($iterator);
if ('TTL' === $origin) {
$isClass = $this->isType($iterator);
} else {
$isClass = $this->isTTL($iterator, 'CLASS') || $this->isType($iterator);
}
$iterator->prev();
return $isClass;
......@@ -252,17 +263,22 @@ class Parser
* Determine if the iterant is a TTL (i.e. it is an integer).
*
* @param ResourceRecordIterator $iterator
* @param string $origin
*
* @return bool
*/
private function isTTL(ResourceRecordIterator $iterator): bool
private function isTTL(ResourceRecordIterator $iterator, $origin = null): bool
{
if (1 !== preg_match('/^\d+$/', $iterator->current())) {
return false;
}
$iterator->next();
$isTtl = $this->isClass($iterator) || $this->isType($iterator);
if ('CLASS' === $origin) {
$isTtl = $this->isType($iterator);
} else {
$isTtl = $this->isClass($iterator, 'TTL') || $this->isType($iterator);
}
$iterator->prev();
return $isTtl;
......@@ -290,4 +306,31 @@ class Parser
return RdataHandlers::catchAll($type, $iterator);
}
/**
* This handler addresses the special case where an integer resource name could be confused for a TTL, for instance:
* 50 IN PTR mx1.acme.com.
*
* In the above, if the integer is below 256 then it is assumed to represent an octet of an IPv4 address.
*
* @param ResourceRecordIterator $iterator
*
* @return Rdata\PTR
*/
private function ptrHandler(ResourceRecordIterator $iterator): Rdata\PTR
{
if (null === $this->currentResourceRecord->getName() && null !== $this->currentResourceRecord->getTtl()) {
if ($this->currentResourceRecord->getTtl() < 256) {
$this->currentResourceRecord->setName((string) $this->currentResourceRecord->getTtl());
$this->currentResourceRecord->setTtl(null);
}
}
$ptr = RdataHandlers::catchAll(Rdata\PTR::TYPE, $iterator);
if (!$ptr instanceof Rdata\PTR) {
throw new \UnexpectedValueException();
}
return $ptr;
}
}
......@@ -137,7 +137,7 @@ class ParserTest extends TestCase
$zone = Parser::parse('example.com.', $file);
$this->assertEquals('example.com.', $zone->getName());
$this->assertEquals('::1', $this->findRecord('ipv6.domain', $zone)[0]->getRdata()->getAddress());
$this->assertEquals('::1', self::findRecord('ipv6.domain', $zone)[0]->getRdata()->getAddress());
$this->assertEquals(1337, $zone->getDefaultTtl());
}
......@@ -164,10 +164,10 @@ class ParserTest extends TestCase
$txt2 = 'Some text another Some text';
$this->assertEquals($txt, $this->findRecord($txt->getName(), $zone)[0]);
$this->assertEquals($txt2, $this->findRecord('test', $zone)[0]->getRdata()->getText());
$this->assertCount(1, $this->findRecord('xn----7sbfndkfpirgcajeli2a4pnc.xn----7sbbfcqfo2cfcagacemif0ap5q', $zone));
$this->assertCount(4, $this->findRecord('testmx', $zone));
$this->assertEquals($txt, self::findRecord($txt->getName(), $zone)[0]);
$this->assertEquals($txt2, self::findRecord('test', $zone)[0]->getRdata()->getText());
$this->assertCount(1, self::findRecord('xn----7sbfndkfpirgcajeli2a4pnc.xn----7sbbfcqfo2cfcagacemif0ap5q', $zone));
$this->assertCount(4, self::findRecord('testmx', $zone));
}
/**
......@@ -200,7 +200,7 @@ class ParserTest extends TestCase
$zone = Parser::parse('example.com.', $file);
/** @var APL $apl */
$apl = $this->findRecord('multicast', $zone)[0]->getRdata();
$apl = self::findRecord('multicast', $zone)[0]->getRdata();
$this->assertCount(2, $apl->getIncludedAddressRanges());
$this->assertCount(2, $apl->getExcludedAddressRanges());
......@@ -276,8 +276,8 @@ TXT;
{
$file = NormaliserTest::readFile(__DIR__.'/Resources/ambiguous.acme.org.txt');
$zone = Parser::parse('ambiguous.acme.org.', $file);
$mxRecords = $this->findRecord('mx', $zone);
$a4Records = $this->findRecord('aaaa', $zone);
$mxRecords = self::findRecord('mx', $zone);
$a4Records = self::findRecord('aaaa', $zone);
$this->assertCount(3, $mxRecords);
$this->assertCount(2, $a4Records);
......@@ -334,7 +334,7 @@ TXT;
*
* @return ResourceRecord[]
*/
private function findRecord(?string $name, Zone $zone): array
public static function findRecord(?string $name, Zone $zone): array
{
$records = [];
......
$ORIGIN 50.100.200.in-addr.arpa.
$TTL 3600
@ IN SOA (
ns.acme.com. ; MNAME
noc.acme.com. ; RNAME
2014110501 ; SERIAL
3600 ; REFRESH
14400 ; RETRY
604800 ; EXPIRE
3600 ; MINIMUM
)
; NS RECORDS
@ NS ns1.acme.com.
@ NS ns2.acme.com.
1 IN 1080 PTR gw01.core.acme.com.
1 IN 1080 PTR gw02.core.acme.com.
50 IN PTR mx1.acme.com.
52 IN PTR mx2.acme.com.
70 7200 IN PTR ns1.acme.com.
72 7200 IN PTR ns2.acme.com.
150 200 PTR smtp.example.com.
170 150 IN PTR netscape.com.
<?php
/*
* This file is part of Badcow DNS Library.
*
* (c) Samuel Williams <sam@badcow.co>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Badcow\DNS\Tests\Parser;
use Badcow\DNS\Classes;
use Badcow\DNS\Parser\Parser;
use Badcow\DNS\Rdata\PTR;
use PHPUnit\Framework\TestCase;
class ReverseRecordTest extends TestCase
{
/**
* @throws \Badcow\DNS\Parser\ParseException
*/
public function testReverseRecord()
{
$ptr = '1 1080 IN PTR gw01.core.acme.com.';
$zone = Parser::parse('50.100.200.in-addr.arpa.', $ptr);
$rr = $zone->getResourceRecords()[0];
$this->assertEquals('1', $rr->getName());
$this->assertEquals(Classes::INTERNET, $rr->getClass());
$this->assertEquals(PTR::TYPE, $rr->getType());
$this->assertEquals('gw01.core.acme.com.', $rr->getRdata()->getTarget());
}
/**
* @throws \Badcow\DNS\Parser\ParseException|\Exception
*/
public function testParseReverseRecordFile()
{
$file = NormaliserTest::readFile(__DIR__.'/Resources/50.100.200.in-addr.arpa.db');
$zone = Parser::parse('50.100.200.in-addr.arpa.', $file);
$parentRecords = ParserTest::findRecord('@', $zone);
$_1Records = ParserTest::findRecord('1', $zone);
$_50Records = ParserTest::findRecord('50', $zone);
$_150Records = ParserTest::findRecord('150', $zone);
$_170Records = ParserTest::findRecord('170', $zone);
$this->assertCount(11, $zone);
$this->assertCount(3, $parentRecords);
$this->assertCount(2, $_1Records);
$this->assertCount(1, $_50Records);
$this->assertCount(1, $_150Records);
$_1 = $_1Records[0];
$_50 = $_50Records[0];
$_150 = $_150Records[0];
$_170 = $_170Records[0];
$this->assertEquals('1', $_1->getName());
$this->assertEquals(1080, $_1->getTtl());
$this->assertEquals(Classes::INTERNET, $_1->getClass());
$this->assertEquals(PTR::TYPE, $_1->getType());
$this->assertEquals('gw01.core.acme.com.', $_1->getRdata()->getTarget());
$this->assertEquals('50', $_50->getName());
$this->assertEquals(1080, $_50->getTtl());
$this->assertEquals(Classes::INTERNET, $_50->getClass());
$this->assertEquals(PTR::TYPE, $_50->getType());
$this->assertEquals('mx1.acme.com.', $_50->getRdata()->getTarget());
$this->assertEquals('150', $_150->getName());
$this->assertEquals(200, $_150->getTtl());
$this->assertEquals(Classes::INTERNET, $_150->getClass());
$this->assertEquals(PTR::TYPE, $_150->getType());
$this->assertEquals('smtp.example.com.', $_150->getRdata()->getTarget());
$this->assertEquals('170', $_170->getName());
$this->assertEquals(150, $_170->getTtl());
$this->assertEquals(Classes::INTERNET, $_170->getClass());
$this->assertEquals(PTR::TYPE, $_170->getType());
$this->assertEquals('netscape.com.', $_170->getRdata()->getTarget());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment